drm/vmwgfx: Add and connect plane helper functions
authorSinclair Yeh <syeh@vmware.com>
Thu, 23 Mar 2017 21:18:32 +0000 (14:18 -0700)
committerSinclair Yeh <syeh@vmware.com>
Fri, 31 Mar 2017 20:37:03 +0000 (13:37 -0700)
Refactor previous FB and cursor plane update code into their
atomic counterparts: check, update, prepare, cleanup, and disable.

These helpers won't be called until we flip on the atomic support
flag or set drm_crtc_funcs->set_config to using the atomic
helper.

v2:
* Removed unnecessary pinning of cursor surface
* Added a few function headers

v3:
* Set clip region equal to the destination region
* Fixed surface pinning policy
* Enable SVGA mode in vmw_sou_primary_plane_prepare_fb

Signed-off-by: Sinclair Yeh <syeh@vmware.com>
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c

index 40224e39af13e58a7a63cacd9e2f2af308b9ff8e..0644b716f1dce454619e40bf8075e5dc666865f2 100644 (file)
  **************************************************************************/
 
 #include "vmwgfx_kms.h"
+#include <drm/drm_plane_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_rect.h>
 
 
 /* Might need a hrtimer here? */
@@ -390,6 +392,260 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane)
 }
 
 
+/**
+ * vmw_du_vps_unpin_surf - unpins resource associated with a framebuffer surface
+ *
+ * @vps: plane state associated with the display surface
+ * @unreference: true if we also want to unreference the display.
+ */
+void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
+                            bool unreference)
+{
+       if (vps->surf) {
+               if (vps->pinned) {
+                       vmw_resource_unpin(&vps->surf->res);
+                       vps->pinned--;
+               }
+
+               if (unreference) {
+                       if (vps->pinned)
+                               DRM_ERROR("Surface still pinned\n");
+                       vmw_surface_unreference(&vps->surf);
+               }
+       }
+}
+
+
+/**
+ * vmw_du_plane_cleanup_fb - Unpins the cursor
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the framebuffer surface
+ *
+ * Returns 0 on success
+ */
+void
+vmw_du_plane_cleanup_fb(struct drm_plane *plane,
+                       struct drm_plane_state *old_state)
+{
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
+
+       vmw_du_plane_unpin_surf(vps, false);
+}
+
+
+/**
+ * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * Returns 0 on success
+ */
+int
+vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
+                              struct drm_plane_state *new_state)
+{
+       struct drm_framebuffer *fb = new_state->fb;
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+
+
+       if (vps->surf)
+               vmw_surface_unreference(&vps->surf);
+
+       if (vps->dmabuf)
+               vmw_dmabuf_unreference(&vps->dmabuf);
+
+       if (fb) {
+               if (vmw_framebuffer_to_vfb(fb)->dmabuf) {
+                       vps->dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer;
+                       vmw_dmabuf_reference(vps->dmabuf);
+               } else {
+                       vps->surf = vmw_framebuffer_to_vfbs(fb)->surface;
+                       vmw_surface_reference(vps->surf);
+               }
+       }
+
+       return 0;
+}
+
+
+void
+vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
+                                  struct drm_plane_state *old_state)
+{
+       struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
+       struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+
+       drm_atomic_set_fb_for_plane(plane->state, NULL);
+       vmw_cursor_update_position(dev_priv, false, 0, 0);
+}
+
+
+void
+vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
+                                 struct drm_plane_state *old_state)
+{
+       struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
+       struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+       struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state);
+       s32 hotspot_x, hotspot_y;
+       int ret = 0;
+
+
+       hotspot_x = du->hotspot_x;
+       hotspot_y = du->hotspot_y;
+       du->cursor_surface = vps->surf;
+       du->cursor_dmabuf = vps->dmabuf;
+
+       /* setup new image */
+       if (vps->surf) {
+               du->cursor_age = du->cursor_surface->snooper.age;
+
+               ret = vmw_cursor_update_image(dev_priv,
+                                             vps->surf->snooper.image,
+                                             64, 64, hotspot_x, hotspot_y);
+       } else if (vps->dmabuf) {
+               ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf,
+                                              plane->state->crtc_w,
+                                              plane->state->crtc_h,
+                                              hotspot_x, hotspot_y);
+       } else {
+               vmw_cursor_update_position(dev_priv, false, 0, 0);
+               return;
+       }
+
+       if (!ret) {
+               du->cursor_x = plane->state->crtc_x + du->set_gui_x;
+               du->cursor_y = plane->state->crtc_y + du->set_gui_y;
+
+               vmw_cursor_update_position(dev_priv, true,
+                                          du->cursor_x + hotspot_x,
+                                          du->cursor_y + hotspot_y);
+       } else {
+               DRM_ERROR("Failed to update cursor image\n");
+       }
+}
+
+
+/**
+ * vmw_du_primary_plane_atomic_check - check if the new state is okay
+ *
+ * @plane: display plane
+ * @state: info on the new plane state, including the FB
+ *
+ * Check if the new state is settable given the current state.  Other
+ * than what the atomic helper checks, we care about crtc fitting
+ * the FB and maintaining one active framebuffer.
+ *
+ * Returns 0 on success
+ */
+int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
+                                     struct drm_plane_state *state)
+{
+       struct drm_framebuffer *new_fb = state->fb;
+       bool visible;
+
+       struct drm_rect src = {
+               .x1 = state->src_x,
+               .y1 = state->src_y,
+               .x2 = state->src_x + state->src_w,
+               .y2 = state->src_y + state->src_h,
+       };
+       struct drm_rect dest = {
+               .x1 = state->crtc_x,
+               .y1 = state->crtc_y,
+               .x2 = state->crtc_x + state->crtc_w,
+               .y2 = state->crtc_y + state->crtc_h,
+       };
+       struct drm_rect clip = dest;
+       int ret;
+
+       ret = drm_plane_helper_check_update(plane, state->crtc, new_fb,
+                                           &src, &dest, &clip,
+                                           DRM_ROTATE_0,
+                                           DRM_PLANE_HELPER_NO_SCALING,
+                                           DRM_PLANE_HELPER_NO_SCALING,
+                                           false, true, &visible);
+
+
+       if (!ret && new_fb) {
+               struct drm_crtc *crtc = state->crtc;
+               struct vmw_connector_state *vcs;
+               struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+               struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+               struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);
+
+               vcs = vmw_connector_state_to_vcs(du->connector.state);
+
+               if ((dest.x2 > new_fb->width ||
+                    dest.y2 > new_fb->height)) {
+                       DRM_ERROR("CRTC area outside of framebuffer\n");
+                       return -EINVAL;
+               }
+
+               /* Only one active implicit framebuffer at a time. */
+               mutex_lock(&dev_priv->global_kms_state_mutex);
+               if (vcs->is_implicit && dev_priv->implicit_fb &&
+                   !(dev_priv->num_implicit == 1 && du->active_implicit)
+                   && dev_priv->implicit_fb != vfb) {
+                       DRM_ERROR("Multiple implicit framebuffers "
+                                 "not supported.\n");
+                       ret = -EINVAL;
+               }
+               mutex_unlock(&dev_priv->global_kms_state_mutex);
+       }
+
+
+       return ret;
+}
+
+
+/**
+ * vmw_du_cursor_plane_atomic_check - check if the new state is okay
+ *
+ * @plane: cursor plane
+ * @state: info on the new plane state
+ *
+ * This is a chance to fail if the new cursor state does not fit
+ * our requirements.
+ *
+ * Returns 0 on success
+ */
+int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
+                                    struct drm_plane_state *new_state)
+{
+       int ret = 0;
+       struct vmw_surface *surface = NULL;
+       struct drm_framebuffer *fb = new_state->fb;
+
+
+       /* Turning off */
+       if (!fb)
+               return ret;
+
+       /* A lot of the code assumes this */
+       if (new_state->crtc_w != 64 || new_state->crtc_h != 64) {
+               DRM_ERROR("Invalid cursor dimensions (%d, %d)\n",
+                         new_state->crtc_w, new_state->crtc_h);
+               ret = -EINVAL;
+       }
+
+       if (!vmw_framebuffer_to_vfb(fb)->dmabuf)
+               surface = vmw_framebuffer_to_vfbs(fb)->surface;
+
+       if (surface && !surface->snooper.image) {
+               DRM_ERROR("surface not suitable for cursor\n");
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+
 int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
                             struct drm_crtc_state *new_state)
 {
index f711b5da73e5d184bd99923e01326431e2223966..de6a0b64bb4bea6a671428d944e658bf743e90fb 100644 (file)
@@ -345,10 +345,25 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane,
                               uint32_t src_w, uint32_t src_h);
 
 /* Atomic Helpers */
+int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
+                                     struct drm_plane_state *state);
+int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
+                                    struct drm_plane_state *state);
+void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
+                                      struct drm_plane_state *old_state);
+void vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
+                                       struct drm_plane_state *old_state);
+int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
+                                  struct drm_plane_state *new_state);
+void vmw_du_plane_cleanup_fb(struct drm_plane *plane,
+                            struct drm_plane_state *old_state);
 void vmw_du_plane_reset(struct drm_plane *plane);
 struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane);
 void vmw_du_plane_destroy_state(struct drm_plane *plane,
                                struct drm_plane_state *state);
+void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
+                            bool unreference);
+
 int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
                             struct drm_crtc_state *state);
 void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
index d547e8061f6a62d286cf64c60b4c73adcf73e553..1d734de1e6e1dd96afd9ce4d8f5e4af1eddf6a79 100644 (file)
@@ -234,7 +234,7 @@ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
 
        ldu = vmw_crtc_to_ldu(crtc);
        dev_priv = vmw_priv(crtc->dev);
-       fb       = crtc->primary->fb;
+       fb       = crtc->primary->state->fb;
 
        vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
 
@@ -242,6 +242,8 @@ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
                vmw_ldu_add_active(dev_priv, ldu, vfb);
        else
                vmw_ldu_del_active(dev_priv, ldu);
+
+       vmw_ldu_commit_list(dev_priv);
 }
 
 /**
@@ -391,6 +393,46 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = {
  * Legacy Display Plane Functions
  */
 
+/**
+ * vmw_ldu_primary_plane_cleanup_fb - Unpin fb
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the display surface
+ *
+ * Returns 0 on success
+ */
+static void
+vmw_ldu_primary_plane_cleanup_fb(struct drm_plane *plane,
+                                struct drm_plane_state *old_state)
+{
+}
+
+
+/**
+ * vmw_ldu_primary_plane_prepare_fb -
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * Returns 0 on success
+ */
+static int
+vmw_ldu_primary_plane_prepare_fb(struct drm_plane *plane,
+                                struct drm_plane_state *new_state)
+{
+       return 0;
+}
+
+
+static void
+vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane,
+                                   struct drm_plane_state *old_state)
+{
+}
+
+
 static const struct drm_plane_funcs vmw_ldu_plane_funcs = {
        .update_plane = drm_primary_helper_update,
        .disable_plane = drm_primary_helper_disable,
@@ -412,6 +454,22 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = {
 /*
  * Atomic Helpers
  */
+static const struct
+drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = {
+       .atomic_check = vmw_du_cursor_plane_atomic_check,
+       .atomic_update = vmw_du_cursor_plane_atomic_update,
+       .prepare_fb = vmw_du_cursor_plane_prepare_fb,
+       .cleanup_fb = vmw_du_plane_cleanup_fb,
+};
+
+static const struct
+drm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = {
+       .atomic_check = vmw_du_primary_plane_atomic_check,
+       .atomic_update = vmw_ldu_primary_plane_atomic_update,
+       .prepare_fb = vmw_ldu_primary_plane_prepare_fb,
+       .cleanup_fb = vmw_ldu_primary_plane_cleanup_fb,
+};
+
 static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = {
        .prepare = vmw_ldu_crtc_helper_prepare,
        .commit = vmw_ldu_crtc_helper_commit,
@@ -471,6 +529,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
                goto err_free;
        }
 
+       drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs);
+
        /* Initialize cursor plane */
        vmw_du_plane_reset(cursor);
 
@@ -485,6 +545,10 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
                goto err_free;
        }
 
+       drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs);
+
+
+       vmw_du_connector_reset(connector);
        ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
                                 DRM_MODE_CONNECTOR_VIRTUAL);
        if (ret) {
index 662024c9f35183deaeeebede4296560553588670..eca055ec27c8b3d42ce84a0c6717cfc71ff2b390 100644 (file)
@@ -612,6 +612,100 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = {
  * Screen Object Display Plane Functions
  */
 
+/**
+ * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the display surface
+ *
+ * Returns 0 on success
+ */
+static void
+vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane,
+                                struct drm_plane_state *old_state)
+{
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
+
+       vmw_dmabuf_unreference(&vps->dmabuf);
+       vps->dmabuf_size = 0;
+
+       vmw_du_plane_cleanup_fb(plane, old_state);
+}
+
+
+/**
+ * vmw_sou_primary_plane_prepare_fb - allocate backing buffer
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * The SOU backing buffer is our equivalent of the display plane.
+ *
+ * Returns 0 on success
+ */
+static int
+vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
+                                struct drm_plane_state *new_state)
+{
+       struct drm_framebuffer *new_fb = new_state->fb;
+       struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc;
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+       struct vmw_private *dev_priv;
+       size_t size;
+       int ret;
+
+
+       if (!new_fb) {
+               vmw_dmabuf_unreference(&vps->dmabuf);
+               vps->dmabuf_size = 0;
+
+               return 0;
+       }
+
+       size = new_state->crtc_w * new_state->crtc_h * 4;
+
+       if (vps->dmabuf) {
+               if (vps->dmabuf_size == size)
+                       return 0;
+
+               vmw_dmabuf_unreference(&vps->dmabuf);
+               vps->dmabuf_size = 0;
+       }
+
+       vps->dmabuf = kzalloc(sizeof(*vps->dmabuf), GFP_KERNEL);
+       if (!vps->dmabuf)
+               return -ENOMEM;
+
+       dev_priv = vmw_priv(crtc->dev);
+       vmw_svga_enable(dev_priv);
+
+       /* After we have alloced the backing store might not be able to
+        * resume the overlays, this is preferred to failing to alloc.
+        */
+       vmw_overlay_pause_all(dev_priv);
+       ret = vmw_dmabuf_init(dev_priv, vps->dmabuf, size,
+                             &vmw_vram_ne_placement,
+                             false, &vmw_dmabuf_bo_free);
+       vmw_overlay_resume_all(dev_priv);
+
+       if (ret != 0)
+               vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */
+       else
+               vps->dmabuf_size = size;
+
+       return ret;
+}
+
+
+static void
+vmw_sou_primary_plane_atomic_update(struct drm_plane *plane,
+                                   struct drm_plane_state *old_state)
+{
+}
+
+
 static const struct drm_plane_funcs vmw_sou_plane_funcs = {
        .update_plane = drm_primary_helper_update,
        .disable_plane = drm_primary_helper_disable,
@@ -633,6 +727,22 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = {
 /*
  * Atomic Helpers
  */
+static const struct
+drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = {
+       .atomic_check = vmw_du_cursor_plane_atomic_check,
+       .atomic_update = vmw_du_cursor_plane_atomic_update,
+       .prepare_fb = vmw_du_cursor_plane_prepare_fb,
+       .cleanup_fb = vmw_du_plane_cleanup_fb,
+};
+
+static const struct
+drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = {
+       .atomic_check = vmw_du_primary_plane_atomic_check,
+       .atomic_update = vmw_sou_primary_plane_atomic_update,
+       .prepare_fb = vmw_sou_primary_plane_prepare_fb,
+       .cleanup_fb = vmw_sou_primary_plane_cleanup_fb,
+};
+
 static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {
        .prepare = vmw_sou_crtc_helper_prepare,
        .commit = vmw_sou_crtc_helper_commit,
@@ -691,6 +801,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
                goto err_free;
        }
 
+       drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs);
+
        /* Initialize cursor plane */
        vmw_du_plane_reset(cursor);
 
@@ -705,6 +817,9 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
                goto err_free;
        }
 
+       drm_plane_helper_add(cursor, &vmw_sou_cursor_plane_helper_funcs);
+
+       vmw_du_connector_reset(connector);
        ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs,
                                 DRM_MODE_CONNECTOR_VIRTUAL);
        if (ret) {
index 6e3cfad336b0a72c56d22709aa895973c2fdab20..cce5e5b02e201d236476043dd46849378447006c 100644 (file)
@@ -1194,6 +1194,230 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = {
  * Screen Target Display Plane Functions
  *****************************************************************************/
 
+
+
+/**
+ * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the display surface
+ *
+ * Returns 0 on success
+ */
+static void
+vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,
+                                 struct drm_plane_state *old_state)
+{
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
+
+       if (vps->surf)
+               WARN_ON(!vps->pinned);
+
+       vmw_du_plane_cleanup_fb(plane, old_state);
+
+       vps->content_fb_type = SAME_AS_DISPLAY;
+}
+
+
+
+/**
+ * vmw_stdu_primary_plane_prepare_fb - Readies the display surface
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * This function allocates a new display surface if the content is
+ * backed by a DMA.  The display surface is pinned here, and it'll
+ * be unpinned in .cleanup_fb()
+ *
+ * Returns 0 on success
+ */
+static int
+vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
+                                 struct drm_plane_state *new_state)
+{
+       struct drm_framebuffer *new_fb = new_state->fb;
+       struct vmw_framebuffer *vfb;
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+       enum stdu_content_type new_content_type;
+       struct vmw_framebuffer_surface *new_vfbs;
+       struct drm_crtc *crtc = new_state->crtc;
+       uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h;
+       int ret;
+
+       /* No FB to prepare */
+       if (!new_fb) {
+               if (vps->surf) {
+                       WARN_ON(vps->pinned != 0);
+                       vmw_surface_unreference(&vps->surf);
+               }
+
+               return 0;
+       }
+
+       vfb = vmw_framebuffer_to_vfb(new_fb);
+       new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
+
+       if (new_vfbs && new_vfbs->surface->base_size.width == hdisplay &&
+           new_vfbs->surface->base_size.height == vdisplay)
+               new_content_type = SAME_AS_DISPLAY;
+       else if (vfb->dmabuf)
+               new_content_type = SEPARATE_DMA;
+       else
+               new_content_type = SEPARATE_SURFACE;
+
+       if (new_content_type != SAME_AS_DISPLAY) {
+               struct vmw_surface content_srf;
+               struct drm_vmw_size display_base_size = {0};
+
+               display_base_size.width  = hdisplay;
+               display_base_size.height = vdisplay;
+               display_base_size.depth  = 1;
+
+               /*
+                * If content buffer is a DMA buf, then we have to construct
+                * surface info
+                */
+               if (new_content_type == SEPARATE_DMA) {
+
+                       switch (new_fb->format->cpp[0]*8) {
+                       case 32:
+                               content_srf.format = SVGA3D_X8R8G8B8;
+                               break;
+
+                       case 16:
+                               content_srf.format = SVGA3D_R5G6B5;
+                               break;
+
+                       case 8:
+                               content_srf.format = SVGA3D_P8;
+                               break;
+
+                       default:
+                               DRM_ERROR("Invalid format\n");
+                               return -EINVAL;
+                       }
+
+                       content_srf.flags             = 0;
+                       content_srf.mip_levels[0]     = 1;
+                       content_srf.multisample_count = 0;
+               } else {
+                       content_srf = *new_vfbs->surface;
+               }
+
+               if (vps->surf) {
+                       struct drm_vmw_size cur_base_size = vps->surf->base_size;
+
+                       if (cur_base_size.width != display_base_size.width ||
+                           cur_base_size.height != display_base_size.height ||
+                           vps->surf->format != content_srf.format) {
+                               WARN_ON(vps->pinned != 0);
+                               vmw_surface_unreference(&vps->surf);
+                       }
+
+               }
+
+               if (!vps->surf) {
+                       ret = vmw_surface_gb_priv_define
+                               (crtc->dev,
+                                /* Kernel visible only */
+                                0,
+                                content_srf.flags,
+                                content_srf.format,
+                                true,  /* a scanout buffer */
+                                content_srf.mip_levels[0],
+                                content_srf.multisample_count,
+                                0,
+                                display_base_size,
+                                &vps->surf);
+                       if (ret != 0) {
+                               DRM_ERROR("Couldn't allocate STDU surface.\n");
+                               return ret;
+                       }
+               }
+       } else {
+               /*
+                * prepare_fb and clean_fb should only take care of pinning
+                * and unpinning.  References are tracked by state objects.
+                * The only time we add a reference in prepare_fb is if the
+                * state object doesn't have a reference to begin with
+                */
+               if (vps->surf) {
+                       WARN_ON(vps->pinned != 0);
+                       vmw_surface_unreference(&vps->surf);
+               }
+
+               vps->surf = vmw_surface_reference(new_vfbs->surface);
+       }
+
+       if (vps->surf) {
+
+               /* Pin new surface before flipping */
+               ret = vmw_resource_pin(&vps->surf->res, false);
+               if (ret)
+                       goto out_srf_unref;
+
+               vps->pinned++;
+       }
+
+       vps->content_fb_type = new_content_type;
+       return 0;
+
+out_srf_unref:
+       vmw_surface_unreference(&vps->surf);
+       return ret;
+}
+
+
+
+/**
+ * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane
+ *
+ * @plane: display plane
+ * @old_state: Only used to get crtc info
+ *
+ * Formally update stdu->display_srf to the new plane, and bind the new
+ * plane STDU.  This function is called during the commit phase when
+ * all the preparation have been done and all the configurations have
+ * been checked.
+ */
+static void
+vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
+                                    struct drm_plane_state *old_state)
+{
+       struct vmw_private *dev_priv;
+       struct vmw_screen_target_display_unit *stdu;
+       struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state);
+       struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
+       int ret;
+
+       stdu     = vmw_crtc_to_stdu(crtc);
+       dev_priv = vmw_priv(crtc->dev);
+
+       stdu->display_srf = vps->surf;
+       stdu->content_fb_type = vps->content_fb_type;
+
+       if (!stdu->defined)
+               return;
+
+       if (plane->state->fb)
+               ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res);
+       else
+               ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
+
+       /*
+        * We cannot really fail this function, so if we do, then output an
+        * error and quit
+        */
+       if (ret)
+               DRM_ERROR("Failed to bind surface to STDU.\n");
+       else
+               crtc->primary->fb = plane->state->fb;
+}
+
+
 static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
        .update_plane = drm_primary_helper_update,
        .disable_plane = drm_primary_helper_disable,
@@ -1216,6 +1440,22 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {
 /*
  * Atomic Helpers
  */
+static const struct
+drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = {
+       .atomic_check = vmw_du_cursor_plane_atomic_check,
+       .atomic_update = vmw_du_cursor_plane_atomic_update,
+       .prepare_fb = vmw_du_cursor_plane_prepare_fb,
+       .cleanup_fb = vmw_du_plane_cleanup_fb,
+};
+
+static const struct
+drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = {
+       .atomic_check = vmw_du_primary_plane_atomic_check,
+       .atomic_update = vmw_stdu_primary_plane_atomic_update,
+       .prepare_fb = vmw_stdu_primary_plane_prepare_fb,
+       .cleanup_fb = vmw_stdu_primary_plane_cleanup_fb,
+};
+
 static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
        .prepare = vmw_stdu_crtc_helper_prepare,
        .commit = vmw_stdu_crtc_helper_commit,
@@ -1283,6 +1523,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
                goto err_free;
        }
 
+       drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs);
+
        /* Initialize cursor plane */
        vmw_du_plane_reset(cursor);
 
@@ -1297,6 +1539,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
                goto err_free;
        }
 
+       drm_plane_helper_add(cursor, &vmw_stdu_cursor_plane_helper_funcs);
+
        vmw_du_connector_reset(connector);
 
        ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs,