These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / vmwgfx / vmwgfx_kms.c
index 07cda8c..7c2e118 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
  *
- * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright © 2009-2015 VMware, Inc., Palo Alto, CA., USA
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
 /* Might need a hrtimer here? */
 #define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
 
-
-struct vmw_clip_rect {
-       int x1, x2, y1, y2;
-};
-
-/**
- * Clip @num_rects number of @rects against @clip storing the
- * results in @out_rects and the number of passed rects in @out_num.
- */
-static void vmw_clip_cliprects(struct drm_clip_rect *rects,
-                       int num_rects,
-                       struct vmw_clip_rect clip,
-                       SVGASignedRect *out_rects,
-                       int *out_num)
-{
-       int i, k;
-
-       for (i = 0, k = 0; i < num_rects; i++) {
-               int x1 = max_t(int, clip.x1, rects[i].x1);
-               int y1 = max_t(int, clip.y1, rects[i].y1);
-               int x2 = min_t(int, clip.x2, rects[i].x2);
-               int y2 = min_t(int, clip.y2, rects[i].y2);
-
-               if (x1 >= x2)
-                       continue;
-               if (y1 >= y2)
-                       continue;
-
-               out_rects[k].left   = x1;
-               out_rects[k].top    = y1;
-               out_rects[k].right  = x2;
-               out_rects[k].bottom = y2;
-               k++;
-       }
-
-       *out_num = k;
-}
-
-void vmw_display_unit_cleanup(struct vmw_display_unit *du)
+void vmw_du_cleanup(struct vmw_display_unit *du)
 {
        if (du->cursor_surface)
                vmw_surface_unreference(&du->cursor_surface);
@@ -109,14 +71,14 @@ int vmw_cursor_update_image(struct vmw_private *dev_priv,
 
        memcpy(&cmd[1], image, image_size);
 
-       cmd->cmd = cpu_to_le32(SVGA_CMD_DEFINE_ALPHA_CURSOR);
-       cmd->cursor.id = cpu_to_le32(0);
-       cmd->cursor.width = cpu_to_le32(width);
-       cmd->cursor.height = cpu_to_le32(height);
-       cmd->cursor.hotspotX = cpu_to_le32(hotspotX);
-       cmd->cursor.hotspotY = cpu_to_le32(hotspotY);
+       cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR;
+       cmd->cursor.id = 0;
+       cmd->cursor.width = width;
+       cmd->cursor.height = height;
+       cmd->cursor.hotspotX = hotspotX;
+       cmd->cursor.hotspotY = hotspotY;
 
-       vmw_fifo_commit(dev_priv, cmd_size);
+       vmw_fifo_commit_flush(dev_priv, cmd_size);
 
        return 0;
 }
@@ -161,23 +123,29 @@ err_unreserve:
 void vmw_cursor_update_position(struct vmw_private *dev_priv,
                                bool show, int x, int y)
 {
-       __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
+       u32 *fifo_mem = dev_priv->mmio_virt;
        uint32_t count;
 
-       iowrite32(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
-       iowrite32(x, fifo_mem + SVGA_FIFO_CURSOR_X);
-       iowrite32(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
-       count = ioread32(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
-       iowrite32(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
+       vmw_mmio_write(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
+       vmw_mmio_write(x, fifo_mem + SVGA_FIFO_CURSOR_X);
+       vmw_mmio_write(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
+       count = vmw_mmio_read(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
+       vmw_mmio_write(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
 }
 
-int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
-                          uint32_t handle, uint32_t width, uint32_t height)
+
+/*
+ * vmw_du_crtc_cursor_set2 - Driver cursor_set2 callback.
+ */
+int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
+                           uint32_t handle, uint32_t width, uint32_t height,
+                           int32_t hot_x, int32_t hot_y)
 {
        struct vmw_private *dev_priv = vmw_priv(crtc->dev);
        struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
        struct vmw_surface *surface = NULL;
        struct vmw_dma_buffer *dmabuf = NULL;
+       s32 hotspot_x, hotspot_y;
        int ret;
 
        /*
@@ -189,6 +157,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
         */
        drm_modeset_unlock_crtc(crtc);
        drm_modeset_lock_all(dev_priv->dev);
+       hotspot_x = hot_x + du->hotspot_x;
+       hotspot_y = hot_y + du->hotspot_y;
 
        /* A lot of the code assumes this */
        if (handle && (width != 64 || height != 64)) {
@@ -225,31 +195,34 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
                vmw_dmabuf_unreference(&du->cursor_dmabuf);
 
        /* setup new image */
+       ret = 0;
        if (surface) {
                /* vmw_user_surface_lookup takes one reference */
                du->cursor_surface = surface;
 
                du->cursor_surface->snooper.crtc = crtc;
                du->cursor_age = du->cursor_surface->snooper.age;
-               vmw_cursor_update_image(dev_priv, surface->snooper.image,
-                                       64, 64, du->hotspot_x, du->hotspot_y);
+               ret = vmw_cursor_update_image(dev_priv, surface->snooper.image,
+                                             64, 64, hotspot_x, hotspot_y);
        } else if (dmabuf) {
                /* vmw_user_surface_lookup takes one reference */
                du->cursor_dmabuf = dmabuf;
 
                ret = vmw_cursor_update_dmabuf(dev_priv, dmabuf, width, height,
-                                              du->hotspot_x, du->hotspot_y);
+                                              hotspot_x, hotspot_y);
        } else {
                vmw_cursor_update_position(dev_priv, false, 0, 0);
-               ret = 0;
                goto out;
        }
 
-       vmw_cursor_update_position(dev_priv, true,
-                                  du->cursor_x + du->hotspot_x,
-                                  du->cursor_y + du->hotspot_y);
+       if (!ret) {
+               vmw_cursor_update_position(dev_priv, true,
+                                          du->cursor_x + hotspot_x,
+                                          du->cursor_y + hotspot_y);
+               du->core_hotspot_x = hot_x;
+               du->core_hotspot_y = hot_y;
+       }
 
-       ret = 0;
 out:
        drm_modeset_unlock_all(dev_priv->dev);
        drm_modeset_lock_crtc(crtc, crtc->cursor);
@@ -277,8 +250,10 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        drm_modeset_lock_all(dev_priv->dev);
 
        vmw_cursor_update_position(dev_priv, shown,
-                                  du->cursor_x + du->hotspot_x,
-                                  du->cursor_y + du->hotspot_y);
+                                  du->cursor_x + du->hotspot_x +
+                                  du->core_hotspot_x,
+                                  du->cursor_y + du->hotspot_y +
+                                  du->core_hotspot_y);
 
        drm_modeset_unlock_all(dev_priv->dev);
        drm_modeset_lock_crtc(crtc, crtc->cursor);
@@ -367,20 +342,34 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
 
        srf->snooper.age++;
 
-       /* we can't call this function from this function since execbuf has
-        * reserved fifo space.
-        *
-        * if (srf->snooper.crtc)
-        *      vmw_ldu_crtc_cursor_update_image(dev_priv,
-        *                                       srf->snooper.image, 64, 64,
-        *                                       du->hotspot_x, du->hotspot_y);
-        */
-
        ttm_bo_kunmap(&map);
 err_unreserve:
        ttm_bo_unreserve(bo);
 }
 
+/**
+ * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots
+ *
+ * @dev_priv: Pointer to the device private struct.
+ *
+ * Clears all legacy hotspots.
+ */
+void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv)
+{
+       struct drm_device *dev = dev_priv->dev;
+       struct vmw_display_unit *du;
+       struct drm_crtc *crtc;
+
+       drm_modeset_lock_all(dev);
+       drm_for_each_crtc(crtc, dev) {
+               du = vmw_crtc_to_du(crtc);
+
+               du->hotspot_x = 0;
+               du->hotspot_y = 0;
+       }
+       drm_modeset_unlock_all(dev);
+}
+
 void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
 {
        struct drm_device *dev = dev_priv->dev;
@@ -398,7 +387,9 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
                du->cursor_age = du->cursor_surface->snooper.age;
                vmw_cursor_update_image(dev_priv,
                                        du->cursor_surface->snooper.image,
-                                       64, 64, du->hotspot_x, du->hotspot_y);
+                                       64, 64,
+                                       du->hotspot_x + du->core_hotspot_x,
+                                       du->hotspot_y + du->core_hotspot_y);
        }
 
        mutex_unlock(&dev->mode_config.mutex);
@@ -412,183 +403,19 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
  * Surface framebuffer code
  */
 
-#define vmw_framebuffer_to_vfbs(x) \
-       container_of(x, struct vmw_framebuffer_surface, base.base)
-
-struct vmw_framebuffer_surface {
-       struct vmw_framebuffer base;
-       struct vmw_surface *surface;
-       struct vmw_dma_buffer *buffer;
-       struct list_head head;
-       struct drm_master *master;
-};
-
 static void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
 {
        struct vmw_framebuffer_surface *vfbs =
                vmw_framebuffer_to_vfbs(framebuffer);
-       struct vmw_master *vmaster = vmw_master(vfbs->master);
 
-
-       mutex_lock(&vmaster->fb_surf_mutex);
-       list_del(&vfbs->head);
-       mutex_unlock(&vmaster->fb_surf_mutex);
-
-       drm_master_put(&vfbs->master);
        drm_framebuffer_cleanup(framebuffer);
        vmw_surface_unreference(&vfbs->surface);
-       ttm_base_object_unref(&vfbs->base.user_obj);
+       if (vfbs->base.user_obj)
+               ttm_base_object_unref(&vfbs->base.user_obj);
 
        kfree(vfbs);
 }
 
-static int do_surface_dirty_sou(struct vmw_private *dev_priv,
-                               struct drm_file *file_priv,
-                               struct vmw_framebuffer *framebuffer,
-                               unsigned flags, unsigned color,
-                               struct drm_clip_rect *clips,
-                               unsigned num_clips, int inc,
-                               struct vmw_fence_obj **out_fence)
-{
-       struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
-       struct drm_clip_rect *clips_ptr;
-       struct drm_clip_rect *tmp;
-       struct drm_crtc *crtc;
-       size_t fifo_size;
-       int i, num_units;
-       int ret = 0; /* silence warning */
-       int left, right, top, bottom;
-
-       struct {
-               SVGA3dCmdHeader header;
-               SVGA3dCmdBlitSurfaceToScreen body;
-       } *cmd;
-       SVGASignedRect *blits;
-
-       num_units = 0;
-       list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list,
-                           head) {
-               if (crtc->primary->fb != &framebuffer->base)
-                       continue;
-               units[num_units++] = vmw_crtc_to_du(crtc);
-       }
-
-       BUG_ON(!clips || !num_clips);
-
-       tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL);
-       if (unlikely(tmp == NULL)) {
-               DRM_ERROR("Temporary cliprect memory alloc failed.\n");
-               return -ENOMEM;
-       }
-
-       fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
-       cmd = kzalloc(fifo_size, GFP_KERNEL);
-       if (unlikely(cmd == NULL)) {
-               DRM_ERROR("Temporary fifo memory alloc failed.\n");
-               ret = -ENOMEM;
-               goto out_free_tmp;
-       }
-
-       /* setup blits pointer */
-       blits = (SVGASignedRect *)&cmd[1];
-
-       /* initial clip region */
-       left = clips->x1;
-       right = clips->x2;
-       top = clips->y1;
-       bottom = clips->y2;
-
-       /* skip the first clip rect */
-       for (i = 1, clips_ptr = clips + inc;
-            i < num_clips; i++, clips_ptr += inc) {
-               left = min_t(int, left, (int)clips_ptr->x1);
-               right = max_t(int, right, (int)clips_ptr->x2);
-               top = min_t(int, top, (int)clips_ptr->y1);
-               bottom = max_t(int, bottom, (int)clips_ptr->y2);
-       }
-
-       /* only need to do this once */
-       cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
-       cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
-
-       cmd->body.srcRect.left = left;
-       cmd->body.srcRect.right = right;
-       cmd->body.srcRect.top = top;
-       cmd->body.srcRect.bottom = bottom;
-
-       clips_ptr = clips;
-       for (i = 0; i < num_clips; i++, clips_ptr += inc) {
-               tmp[i].x1 = clips_ptr->x1 - left;
-               tmp[i].x2 = clips_ptr->x2 - left;
-               tmp[i].y1 = clips_ptr->y1 - top;
-               tmp[i].y2 = clips_ptr->y2 - top;
-       }
-
-       /* do per unit writing, reuse fifo for each */
-       for (i = 0; i < num_units; i++) {
-               struct vmw_display_unit *unit = units[i];
-               struct vmw_clip_rect clip;
-               int num;
-
-               clip.x1 = left - unit->crtc.x;
-               clip.y1 = top - unit->crtc.y;
-               clip.x2 = right - unit->crtc.x;
-               clip.y2 = bottom - unit->crtc.y;
-
-               /* skip any crtcs that misses the clip region */
-               if (clip.x1 >= unit->crtc.mode.hdisplay ||
-                   clip.y1 >= unit->crtc.mode.vdisplay ||
-                   clip.x2 <= 0 || clip.y2 <= 0)
-                       continue;
-
-               /*
-                * In order for the clip rects to be correctly scaled
-                * the src and dest rects needs to be the same size.
-                */
-               cmd->body.destRect.left = clip.x1;
-               cmd->body.destRect.right = clip.x2;
-               cmd->body.destRect.top = clip.y1;
-               cmd->body.destRect.bottom = clip.y2;
-
-               /* create a clip rect of the crtc in dest coords */
-               clip.x2 = unit->crtc.mode.hdisplay - clip.x1;
-               clip.y2 = unit->crtc.mode.vdisplay - clip.y1;
-               clip.x1 = 0 - clip.x1;
-               clip.y1 = 0 - clip.y1;
-
-               /* need to reset sid as it is changed by execbuf */
-               cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle);
-               cmd->body.destScreenId = unit->unit;
-
-               /* clip and write blits to cmd stream */
-               vmw_clip_cliprects(tmp, num_clips, clip, blits, &num);
-
-               /* if no cliprects hit skip this */
-               if (num == 0)
-                       continue;
-
-               /* only return the last fence */
-               if (out_fence && *out_fence)
-                       vmw_fence_obj_unreference(out_fence);
-
-               /* recalculate package length */
-               fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num;
-               cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
-               ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
-                                         fifo_size, 0, NULL, out_fence);
-
-               if (unlikely(ret != 0))
-                       break;
-       }
-
-
-       kfree(cmd);
-out_free_tmp:
-       kfree(tmp);
-
-       return ret;
-}
-
 static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
                                  struct drm_file *file_priv,
                                  unsigned flags, unsigned color,
@@ -601,11 +428,8 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
        struct drm_clip_rect norect;
        int ret, inc = 1;
 
-       if (unlikely(vfbs->master != file_priv->master))
-               return -EINVAL;
-
-       /* Require ScreenObject support for 3D */
-       if (!dev_priv->sou_priv)
+       /* Legacy Display Unit does not support 3D */
+       if (dev_priv->active_display_unit == vmw_du_legacy)
                return -EINVAL;
 
        drm_modeset_lock_all(dev_priv->dev);
@@ -627,10 +451,16 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
                inc = 2; /* skip source rects */
        }
 
-       ret = do_surface_dirty_sou(dev_priv, file_priv, &vfbs->base,
-                                  flags, color,
-                                  clips, num_clips, inc, NULL);
+       if (dev_priv->active_display_unit == vmw_du_screen_object)
+               ret = vmw_kms_sou_do_surface_dirty(dev_priv, &vfbs->base,
+                                                  clips, NULL, NULL, 0, 0,
+                                                  num_clips, inc, NULL);
+       else
+               ret = vmw_kms_stdu_surface_dirty(dev_priv, &vfbs->base,
+                                                clips, NULL, NULL, 0, 0,
+                                                num_clips, inc, NULL);
 
+       vmw_fifo_flush(dev_priv, false);
        ttm_read_unlock(&dev_priv->reservation_sem);
 
        drm_modeset_unlock_all(dev_priv->dev);
@@ -638,27 +468,66 @@ static int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
        return 0;
 }
 
+/**
+ * vmw_kms_readback - Perform a readback from the screen system to
+ * a dma-buffer backed framebuffer.
+ *
+ * @dev_priv: Pointer to the device private structure.
+ * @file_priv: Pointer to a struct drm_file identifying the caller.
+ * Must be set to NULL if @user_fence_rep is NULL.
+ * @vfb: Pointer to the dma-buffer backed framebuffer.
+ * @user_fence_rep: User-space provided structure for fence information.
+ * Must be set to non-NULL if @file_priv is non-NULL.
+ * @vclips: Array of clip rects.
+ * @num_clips: Number of clip rects in @vclips.
+ *
+ * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
+ * interrupted.
+ */
+int vmw_kms_readback(struct vmw_private *dev_priv,
+                    struct drm_file *file_priv,
+                    struct vmw_framebuffer *vfb,
+                    struct drm_vmw_fence_rep __user *user_fence_rep,
+                    struct drm_vmw_rect *vclips,
+                    uint32_t num_clips)
+{
+       switch (dev_priv->active_display_unit) {
+       case vmw_du_screen_object:
+               return vmw_kms_sou_readback(dev_priv, file_priv, vfb,
+                                           user_fence_rep, vclips, num_clips);
+       case vmw_du_screen_target:
+               return vmw_kms_stdu_dma(dev_priv, file_priv, vfb,
+                                       user_fence_rep, NULL, vclips, num_clips,
+                                       1, false, true);
+       default:
+               WARN_ONCE(true,
+                         "Readback called with invalid display system.\n");
+}
+
+       return -ENOSYS;
+}
+
+
 static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
        .destroy = vmw_framebuffer_surface_destroy,
        .dirty = vmw_framebuffer_surface_dirty,
 };
 
 static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
-                                          struct drm_file *file_priv,
                                           struct vmw_surface *surface,
                                           struct vmw_framebuffer **out,
                                           const struct drm_mode_fb_cmd
-                                          *mode_cmd)
+                                          *mode_cmd,
+                                          bool is_dmabuf_proxy)
 
 {
        struct drm_device *dev = dev_priv->dev;
        struct vmw_framebuffer_surface *vfbs;
        enum SVGA3dSurfaceFormat format;
-       struct vmw_master *vmaster = vmw_master(file_priv->master);
        int ret;
 
-       /* 3D is only supported on HWv8 hosts which supports screen objects */
-       if (!dev_priv->sou_priv)
+       /* 3D is only supported on HWv8 and newer hosts */
+       if (dev_priv->active_display_unit == vmw_du_legacy)
                return -ENOSYS;
 
        /*
@@ -692,15 +561,16 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
        case 15:
                format = SVGA3D_A1R5G5B5;
                break;
-       case 8:
-               format = SVGA3D_LUMINANCE8;
-               break;
        default:
                DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth);
                return -EINVAL;
        }
 
-       if (unlikely(format != surface->format)) {
+       /*
+        * For DX, surface format validation is done when surface->scanout
+        * is set.
+        */
+       if (!dev_priv->has_dx && format != surface->format) {
                DRM_ERROR("Invalid surface format for requested mode.\n");
                return -EINVAL;
        }
@@ -711,38 +581,27 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
                goto out_err1;
        }
 
-       if (!vmw_surface_reference(surface)) {
-               DRM_ERROR("failed to reference surface %p\n", surface);
-               ret = -EINVAL;
-               goto out_err2;
-       }
-
        /* XXX get the first 3 from the surface info */
        vfbs->base.base.bits_per_pixel = mode_cmd->bpp;
        vfbs->base.base.pitches[0] = mode_cmd->pitch;
        vfbs->base.base.depth = mode_cmd->depth;
        vfbs->base.base.width = mode_cmd->width;
        vfbs->base.base.height = mode_cmd->height;
-       vfbs->surface = surface;
+       vfbs->surface = vmw_surface_reference(surface);
        vfbs->base.user_handle = mode_cmd->handle;
-       vfbs->master = drm_master_get(file_priv->master);
-
-       mutex_lock(&vmaster->fb_surf_mutex);
-       list_add_tail(&vfbs->head, &vmaster->fb_surf);
-       mutex_unlock(&vmaster->fb_surf_mutex);
+       vfbs->is_dmabuf_proxy = is_dmabuf_proxy;
 
        *out = &vfbs->base;
 
        ret = drm_framebuffer_init(dev, &vfbs->base.base,
                                   &vmw_framebuffer_surface_funcs);
        if (ret)
-               goto out_err3;
+               goto out_err2;
 
        return 0;
 
-out_err3:
-       vmw_surface_unreference(&surface);
 out_err2:
+       vmw_surface_unreference(&surface);
        kfree(vfbs);
 out_err1:
        return ret;
@@ -752,14 +611,6 @@ out_err1:
  * Dmabuf framebuffer code
  */
 
-#define vmw_framebuffer_to_vfbd(x) \
-       container_of(x, struct vmw_framebuffer_dmabuf, base.base)
-
-struct vmw_framebuffer_dmabuf {
-       struct vmw_framebuffer base;
-       struct vmw_dma_buffer *buffer;
-};
-
 static void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
 {
        struct vmw_framebuffer_dmabuf *vfbd =
@@ -767,185 +618,12 @@ static void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
 
        drm_framebuffer_cleanup(framebuffer);
        vmw_dmabuf_unreference(&vfbd->buffer);
-       ttm_base_object_unref(&vfbd->base.user_obj);
+       if (vfbd->base.user_obj)
+               ttm_base_object_unref(&vfbd->base.user_obj);
 
        kfree(vfbd);
 }
 
-static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv,
-                              struct vmw_framebuffer *framebuffer,
-                              unsigned flags, unsigned color,
-                              struct drm_clip_rect *clips,
-                              unsigned num_clips, int increment)
-{
-       size_t fifo_size;
-       int i;
-
-       struct {
-               uint32_t header;
-               SVGAFifoCmdUpdate body;
-       } *cmd;
-
-       fifo_size = sizeof(*cmd) * num_clips;
-       cmd = vmw_fifo_reserve(dev_priv, fifo_size);
-       if (unlikely(cmd == NULL)) {
-               DRM_ERROR("Fifo reserve failed.\n");
-               return -ENOMEM;
-       }
-
-       memset(cmd, 0, fifo_size);
-       for (i = 0; i < num_clips; i++, clips += increment) {
-               cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE);
-               cmd[i].body.x = cpu_to_le32(clips->x1);
-               cmd[i].body.y = cpu_to_le32(clips->y1);
-               cmd[i].body.width = cpu_to_le32(clips->x2 - clips->x1);
-               cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1);
-       }
-
-       vmw_fifo_commit(dev_priv, fifo_size);
-       return 0;
-}
-
-static int do_dmabuf_define_gmrfb(struct drm_file *file_priv,
-                                 struct vmw_private *dev_priv,
-                                 struct vmw_framebuffer *framebuffer)
-{
-       int depth = framebuffer->base.depth;
-       size_t fifo_size;
-       int ret;
-
-       struct {
-               uint32_t header;
-               SVGAFifoCmdDefineGMRFB body;
-       } *cmd;
-
-       /* Emulate RGBA support, contrary to svga_reg.h this is not
-        * supported by hosts. This is only a problem if we are reading
-        * this value later and expecting what we uploaded back.
-        */
-       if (depth == 32)
-               depth = 24;
-
-       fifo_size = sizeof(*cmd);
-       cmd = kmalloc(fifo_size, GFP_KERNEL);
-       if (unlikely(cmd == NULL)) {
-               DRM_ERROR("Failed to allocate temporary cmd buffer.\n");
-               return -ENOMEM;
-       }
-
-       memset(cmd, 0, fifo_size);
-       cmd->header = SVGA_CMD_DEFINE_GMRFB;
-       cmd->body.format.bitsPerPixel = framebuffer->base.bits_per_pixel;
-       cmd->body.format.colorDepth = depth;
-       cmd->body.format.reserved = 0;
-       cmd->body.bytesPerLine = framebuffer->base.pitches[0];
-       cmd->body.ptr.gmrId = framebuffer->user_handle;
-       cmd->body.ptr.offset = 0;
-
-       ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
-                                 fifo_size, 0, NULL, NULL);
-
-       kfree(cmd);
-
-       return ret;
-}
-
-static int do_dmabuf_dirty_sou(struct drm_file *file_priv,
-                              struct vmw_private *dev_priv,
-                              struct vmw_framebuffer *framebuffer,
-                              unsigned flags, unsigned color,
-                              struct drm_clip_rect *clips,
-                              unsigned num_clips, int increment,
-                              struct vmw_fence_obj **out_fence)
-{
-       struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
-       struct drm_clip_rect *clips_ptr;
-       int i, k, num_units, ret;
-       struct drm_crtc *crtc;
-       size_t fifo_size;
-
-       struct {
-               uint32_t header;
-               SVGAFifoCmdBlitGMRFBToScreen body;
-       } *blits;
-
-       ret = do_dmabuf_define_gmrfb(file_priv, dev_priv, framebuffer);
-       if (unlikely(ret != 0))
-               return ret; /* define_gmrfb prints warnings */
-
-       fifo_size = sizeof(*blits) * num_clips;
-       blits = kmalloc(fifo_size, GFP_KERNEL);
-       if (unlikely(blits == NULL)) {
-               DRM_ERROR("Failed to allocate temporary cmd buffer.\n");
-               return -ENOMEM;
-       }
-
-       num_units = 0;
-       list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
-               if (crtc->primary->fb != &framebuffer->base)
-                       continue;
-               units[num_units++] = vmw_crtc_to_du(crtc);
-       }
-
-       for (k = 0; k < num_units; k++) {
-               struct vmw_display_unit *unit = units[k];
-               int hit_num = 0;
-
-               clips_ptr = clips;
-               for (i = 0; i < num_clips; i++, clips_ptr += increment) {
-                       int clip_x1 = clips_ptr->x1 - unit->crtc.x;
-                       int clip_y1 = clips_ptr->y1 - unit->crtc.y;
-                       int clip_x2 = clips_ptr->x2 - unit->crtc.x;
-                       int clip_y2 = clips_ptr->y2 - unit->crtc.y;
-                       int move_x, move_y;
-
-                       /* skip any crtcs that misses the clip region */
-                       if (clip_x1 >= unit->crtc.mode.hdisplay ||
-                           clip_y1 >= unit->crtc.mode.vdisplay ||
-                           clip_x2 <= 0 || clip_y2 <= 0)
-                               continue;
-
-                       /* clip size to crtc size */
-                       clip_x2 = min_t(int, clip_x2, unit->crtc.mode.hdisplay);
-                       clip_y2 = min_t(int, clip_y2, unit->crtc.mode.vdisplay);
-
-                       /* translate both src and dest to bring clip into screen */
-                       move_x = min_t(int, clip_x1, 0);
-                       move_y = min_t(int, clip_y1, 0);
-
-                       /* actual translate done here */
-                       blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN;
-                       blits[hit_num].body.destScreenId = unit->unit;
-                       blits[hit_num].body.srcOrigin.x = clips_ptr->x1 - move_x;
-                       blits[hit_num].body.srcOrigin.y = clips_ptr->y1 - move_y;
-                       blits[hit_num].body.destRect.left = clip_x1 - move_x;
-                       blits[hit_num].body.destRect.top = clip_y1 - move_y;
-                       blits[hit_num].body.destRect.right = clip_x2;
-                       blits[hit_num].body.destRect.bottom = clip_y2;
-                       hit_num++;
-               }
-
-               /* no clips hit the crtc */
-               if (hit_num == 0)
-                       continue;
-
-               /* only return the last fence */
-               if (out_fence && *out_fence)
-                       vmw_fence_obj_unreference(out_fence);
-
-               fifo_size = sizeof(*blits) * hit_num;
-               ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits,
-                                         fifo_size, 0, NULL, out_fence);
-
-               if (unlikely(ret != 0))
-                       break;
-       }
-
-       kfree(blits);
-
-       return ret;
-}
-
 static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
                                 struct drm_file *file_priv,
                                 unsigned flags, unsigned color,
@@ -977,16 +655,29 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
                increment = 2;
        }
 
-       if (dev_priv->ldu_priv) {
-               ret = do_dmabuf_dirty_ldu(dev_priv, &vfbd->base,
-                                         flags, color,
-                                         clips, num_clips, increment);
-       } else {
-               ret = do_dmabuf_dirty_sou(file_priv, dev_priv, &vfbd->base,
-                                         flags, color,
-                                         clips, num_clips, increment, NULL);
+       switch (dev_priv->active_display_unit) {
+       case vmw_du_screen_target:
+               ret = vmw_kms_stdu_dma(dev_priv, NULL, &vfbd->base, NULL,
+                                      clips, NULL, num_clips, increment,
+                                      true, true);
+               break;
+       case vmw_du_screen_object:
+               ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base,
+                                                 clips, num_clips, increment,
+                                                 true,
+                                                 NULL);
+               break;
+       case vmw_du_legacy:
+               ret = vmw_kms_ldu_do_dmabuf_dirty(dev_priv, &vfbd->base, 0, 0,
+                                                 clips, num_clips, increment);
+               break;
+       default:
+               ret = -EINVAL;
+               WARN_ONCE(true, "Dirty called with invalid display system.\n");
+               break;
        }
 
+       vmw_fifo_flush(dev_priv, false);
        ttm_read_unlock(&dev_priv->reservation_sem);
 
        drm_modeset_unlock_all(dev_priv->dev);
@@ -1002,41 +693,137 @@ static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
 /**
  * Pin the dmabuffer to the start of vram.
  */
-static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
+static int vmw_framebuffer_pin(struct vmw_framebuffer *vfb)
 {
        struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
-       struct vmw_framebuffer_dmabuf *vfbd =
-               vmw_framebuffer_to_vfbd(&vfb->base);
+       struct vmw_dma_buffer *buf;
        int ret;
 
-       /* This code should not be used with screen objects */
-       BUG_ON(dev_priv->sou_priv);
-
-       vmw_overlay_pause_all(dev_priv);
+       buf = vfb->dmabuf ?  vmw_framebuffer_to_vfbd(&vfb->base)->buffer :
+               vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup;
 
-       ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer, true, false);
-
-       vmw_overlay_resume_all(dev_priv);
+       if (!buf)
+               return 0;
 
-       WARN_ON(ret != 0);
+       switch (dev_priv->active_display_unit) {
+       case vmw_du_legacy:
+               vmw_overlay_pause_all(dev_priv);
+               ret = vmw_dmabuf_pin_in_start_of_vram(dev_priv, buf, false);
+               vmw_overlay_resume_all(dev_priv);
+               break;
+       case vmw_du_screen_object:
+       case vmw_du_screen_target:
+               if (vfb->dmabuf)
+                       return vmw_dmabuf_pin_in_vram_or_gmr(dev_priv, buf,
+                                                            false);
+
+               return vmw_dmabuf_pin_in_placement(dev_priv, buf,
+                                                  &vmw_mob_placement, false);
+       default:
+               return -EINVAL;
+       }
 
-       return 0;
+       return ret;
 }
 
-static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
+static int vmw_framebuffer_unpin(struct vmw_framebuffer *vfb)
 {
        struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
-       struct vmw_framebuffer_dmabuf *vfbd =
-               vmw_framebuffer_to_vfbd(&vfb->base);
+       struct vmw_dma_buffer *buf;
+
+       buf = vfb->dmabuf ?  vmw_framebuffer_to_vfbd(&vfb->base)->buffer :
+               vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.backup;
 
-       if (!vfbd->buffer) {
-               WARN_ON(!vfbd->buffer);
+       if (WARN_ON(!buf))
                return 0;
+
+       return vmw_dmabuf_unpin(dev_priv, buf, false);
+}
+
+/**
+ * vmw_create_dmabuf_proxy - create a proxy surface for the DMA buf
+ *
+ * @dev: DRM device
+ * @mode_cmd: parameters for the new surface
+ * @dmabuf_mob: MOB backing the DMA buf
+ * @srf_out: newly created surface
+ *
+ * When the content FB is a DMA buf, we create a surface as a proxy to the
+ * same buffer.  This way we can do a surface copy rather than a surface DMA.
+ * This is a more efficient approach
+ *
+ * RETURNS:
+ * 0 on success, error code otherwise
+ */
+static int vmw_create_dmabuf_proxy(struct drm_device *dev,
+                                  const struct drm_mode_fb_cmd *mode_cmd,
+                                  struct vmw_dma_buffer *dmabuf_mob,
+                                  struct vmw_surface **srf_out)
+{
+       uint32_t format;
+       struct drm_vmw_size content_base_size;
+       struct vmw_resource *res;
+       unsigned int bytes_pp;
+       int ret;
+
+       switch (mode_cmd->depth) {
+       case 32:
+       case 24:
+               format = SVGA3D_X8R8G8B8;
+               bytes_pp = 4;
+               break;
+
+       case 16:
+       case 15:
+               format = SVGA3D_R5G6B5;
+               bytes_pp = 2;
+               break;
+
+       case 8:
+               format = SVGA3D_P8;
+               bytes_pp = 1;
+               break;
+
+       default:
+               DRM_ERROR("Invalid framebuffer format %d\n", mode_cmd->depth);
+               return -EINVAL;
+       }
+
+       content_base_size.width  = mode_cmd->pitch / bytes_pp;
+       content_base_size.height = mode_cmd->height;
+       content_base_size.depth  = 1;
+
+       ret = vmw_surface_gb_priv_define(dev,
+                       0, /* kernel visible only */
+                       0, /* flags */
+                       format,
+                       true, /* can be a scanout buffer */
+                       1, /* num of mip levels */
+                       0,
+                       0,
+                       content_base_size,
+                       srf_out);
+       if (ret) {
+               DRM_ERROR("Failed to allocate proxy content buffer\n");
+               return ret;
        }
 
-       return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, false);
+       res = &(*srf_out)->res;
+
+       /* Reserve and switch the backing mob. */
+       mutex_lock(&res->dev_priv->cmdbuf_mutex);
+       (void) vmw_resource_reserve(res, false, true);
+       vmw_dmabuf_unreference(&res->backup);
+       res->backup = vmw_dmabuf_reference(dmabuf_mob);
+       res->backup_offset = 0;
+       vmw_resource_unreserve(res, false, NULL, 0);
+       mutex_unlock(&res->dev_priv->cmdbuf_mutex);
+
+       return 0;
 }
 
+
+
 static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
                                          struct vmw_dma_buffer *dmabuf,
                                          struct vmw_framebuffer **out,
@@ -1057,7 +844,7 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
        }
 
        /* Limited framebuffer color depth support for screen objects */
-       if (dev_priv->sou_priv) {
+       if (dev_priv->active_display_unit == vmw_du_screen_object) {
                switch (mode_cmd->depth) {
                case 32:
                case 24:
@@ -1089,46 +876,101 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
                goto out_err1;
        }
 
-       if (!vmw_dmabuf_reference(dmabuf)) {
-               DRM_ERROR("failed to reference dmabuf %p\n", dmabuf);
-               ret = -EINVAL;
-               goto out_err2;
-       }
-
        vfbd->base.base.bits_per_pixel = mode_cmd->bpp;
        vfbd->base.base.pitches[0] = mode_cmd->pitch;
        vfbd->base.base.depth = mode_cmd->depth;
        vfbd->base.base.width = mode_cmd->width;
        vfbd->base.base.height = mode_cmd->height;
-       if (!dev_priv->sou_priv) {
-               vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
-               vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
-       }
        vfbd->base.dmabuf = true;
-       vfbd->buffer = dmabuf;
+       vfbd->buffer = vmw_dmabuf_reference(dmabuf);
        vfbd->base.user_handle = mode_cmd->handle;
        *out = &vfbd->base;
 
        ret = drm_framebuffer_init(dev, &vfbd->base.base,
                                   &vmw_framebuffer_dmabuf_funcs);
        if (ret)
-               goto out_err3;
+               goto out_err2;
 
        return 0;
 
-out_err3:
-       vmw_dmabuf_unreference(&dmabuf);
 out_err2:
+       vmw_dmabuf_unreference(&dmabuf);
        kfree(vfbd);
 out_err1:
        return ret;
 }
 
-/*
- * Generic Kernel modesetting functions
+/**
+ * vmw_kms_new_framebuffer - Create a new framebuffer.
+ *
+ * @dev_priv: Pointer to device private struct.
+ * @dmabuf: Pointer to dma buffer to wrap the kms framebuffer around.
+ * Either @dmabuf or @surface must be NULL.
+ * @surface: Pointer to a surface to wrap the kms framebuffer around.
+ * Either @dmabuf or @surface must be NULL.
+ * @only_2d: No presents will occur to this dma buffer based framebuffer. This
+ * Helps the code to do some important optimizations.
+ * @mode_cmd: Frame-buffer metadata.
  */
-
-static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
+struct vmw_framebuffer *
+vmw_kms_new_framebuffer(struct vmw_private *dev_priv,
+                       struct vmw_dma_buffer *dmabuf,
+                       struct vmw_surface *surface,
+                       bool only_2d,
+                       const struct drm_mode_fb_cmd *mode_cmd)
+{
+       struct vmw_framebuffer *vfb = NULL;
+       bool is_dmabuf_proxy = false;
+       int ret;
+
+       /*
+        * We cannot use the SurfaceDMA command in an non-accelerated VM,
+        * therefore, wrap the DMA buf in a surface so we can use the
+        * SurfaceCopy command.
+        */
+       if (dmabuf && only_2d &&
+           dev_priv->active_display_unit == vmw_du_screen_target) {
+               ret = vmw_create_dmabuf_proxy(dev_priv->dev, mode_cmd,
+                                             dmabuf, &surface);
+               if (ret)
+                       return ERR_PTR(ret);
+
+               is_dmabuf_proxy = true;
+       }
+
+       /* Create the new framebuffer depending one what we have */
+       if (surface) {
+               ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
+                                                     mode_cmd,
+                                                     is_dmabuf_proxy);
+
+               /*
+                * vmw_create_dmabuf_proxy() adds a reference that is no longer
+                * needed
+                */
+               if (is_dmabuf_proxy)
+                       vmw_surface_unreference(&surface);
+       } else if (dmabuf) {
+               ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, dmabuf, &vfb,
+                                                    mode_cmd);
+       } else {
+               BUG();
+       }
+
+       if (ret)
+               return ERR_PTR(ret);
+
+       vfb->pin = vmw_framebuffer_pin;
+       vfb->unpin = vmw_framebuffer_unpin;
+
+       return vfb;
+}
+
+/*
+ * Generic Kernel modesetting functions
+ */
+
+static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
                                                 struct drm_file *file_priv,
                                                 struct drm_mode_fb_cmd2 *mode_cmd2)
 {
@@ -1157,7 +999,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
        if (!vmw_kms_validate_mode_vram(dev_priv,
                                        mode_cmd.pitch,
                                        mode_cmd.height)) {
-               DRM_ERROR("VRAM size is too small for requested mode.\n");
+               DRM_ERROR("Requested mode exceed bounding box limit.\n");
                return ERR_PTR(-ENOMEM);
        }
 
@@ -1187,15 +1029,13 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
        if (ret)
                goto err_out;
 
-       /* Create the new framebuffer depending one what we got back */
-       if (bo)
-               ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
-                                                    &mode_cmd);
-       else if (surface)
-               ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv,
-                                                     surface, &vfb, &mode_cmd);
-       else
-               BUG();
+       vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface,
+                                     !(dev_priv->capabilities & SVGA_CAP_3D),
+                                     &mode_cmd);
+       if (IS_ERR(vfb)) {
+               ret = PTR_ERR(vfb);
+               goto err_out;
+       }
 
 err_out:
        /* vmw_user_lookup_handle takes one ref so does new_fb */
@@ -1218,6 +1058,21 @@ static const struct drm_mode_config_funcs vmw_kms_funcs = {
        .fb_create = vmw_kms_fb_create,
 };
 
+static int vmw_kms_generic_present(struct vmw_private *dev_priv,
+                                  struct drm_file *file_priv,
+                                  struct vmw_framebuffer *vfb,
+                                  struct vmw_surface *surface,
+                                  uint32_t sid,
+                                  int32_t destX, int32_t destY,
+                                  struct drm_vmw_rect *clips,
+                                  uint32_t num_clips)
+{
+       return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips,
+                                           &surface->res, destX, destY,
+                                           num_clips, 1, NULL);
+}
+
+
 int vmw_kms_present(struct vmw_private *dev_priv,
                    struct drm_file *file_priv,
                    struct vmw_framebuffer *vfb,
@@ -1227,238 +1082,31 @@ int vmw_kms_present(struct vmw_private *dev_priv,
                    struct drm_vmw_rect *clips,
                    uint32_t num_clips)
 {
-       struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
-       struct drm_clip_rect *tmp;
-       struct drm_crtc *crtc;
-       size_t fifo_size;
-       int i, k, num_units;
-       int ret = 0; /* silence warning */
-       int left, right, top, bottom;
-
-       struct {
-               SVGA3dCmdHeader header;
-               SVGA3dCmdBlitSurfaceToScreen body;
-       } *cmd;
-       SVGASignedRect *blits;
-
-       num_units = 0;
-       list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
-               if (crtc->primary->fb != &vfb->base)
-                       continue;
-               units[num_units++] = vmw_crtc_to_du(crtc);
-       }
-
-       BUG_ON(surface == NULL);
-       BUG_ON(!clips || !num_clips);
-
-       tmp = kzalloc(sizeof(*tmp) * num_clips, GFP_KERNEL);
-       if (unlikely(tmp == NULL)) {
-               DRM_ERROR("Temporary cliprect memory alloc failed.\n");
-               return -ENOMEM;
-       }
-
-       fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips;
-       cmd = kmalloc(fifo_size, GFP_KERNEL);
-       if (unlikely(cmd == NULL)) {
-               DRM_ERROR("Failed to allocate temporary fifo memory.\n");
-               ret = -ENOMEM;
-               goto out_free_tmp;
-       }
-
-       left = clips->x;
-       right = clips->x + clips->w;
-       top = clips->y;
-       bottom = clips->y + clips->h;
-
-       for (i = 1; i < num_clips; i++) {
-               left = min_t(int, left, (int)clips[i].x);
-               right = max_t(int, right, (int)clips[i].x + clips[i].w);
-               top = min_t(int, top, (int)clips[i].y);
-               bottom = max_t(int, bottom, (int)clips[i].y + clips[i].h);
-       }
-
-       /* only need to do this once */
-       memset(cmd, 0, fifo_size);
-       cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN);
-
-       blits = (SVGASignedRect *)&cmd[1];
-
-       cmd->body.srcRect.left = left;
-       cmd->body.srcRect.right = right;
-       cmd->body.srcRect.top = top;
-       cmd->body.srcRect.bottom = bottom;
-
-       for (i = 0; i < num_clips; i++) {
-               tmp[i].x1 = clips[i].x - left;
-               tmp[i].x2 = clips[i].x + clips[i].w - left;
-               tmp[i].y1 = clips[i].y - top;
-               tmp[i].y2 = clips[i].y + clips[i].h - top;
-       }
-
-       for (k = 0; k < num_units; k++) {
-               struct vmw_display_unit *unit = units[k];
-               struct vmw_clip_rect clip;
-               int num;
-
-               clip.x1 = left + destX - unit->crtc.x;
-               clip.y1 = top + destY - unit->crtc.y;
-               clip.x2 = right + destX - unit->crtc.x;
-               clip.y2 = bottom + destY - unit->crtc.y;
-
-               /* skip any crtcs that misses the clip region */
-               if (clip.x1 >= unit->crtc.mode.hdisplay ||
-                   clip.y1 >= unit->crtc.mode.vdisplay ||
-                   clip.x2 <= 0 || clip.y2 <= 0)
-                       continue;
-
-               /*
-                * In order for the clip rects to be correctly scaled
-                * the src and dest rects needs to be the same size.
-                */
-               cmd->body.destRect.left = clip.x1;
-               cmd->body.destRect.right = clip.x2;
-               cmd->body.destRect.top = clip.y1;
-               cmd->body.destRect.bottom = clip.y2;
-
-               /* create a clip rect of the crtc in dest coords */
-               clip.x2 = unit->crtc.mode.hdisplay - clip.x1;
-               clip.y2 = unit->crtc.mode.vdisplay - clip.y1;
-               clip.x1 = 0 - clip.x1;
-               clip.y1 = 0 - clip.y1;
-
-               /* need to reset sid as it is changed by execbuf */
-               cmd->body.srcImage.sid = sid;
-               cmd->body.destScreenId = unit->unit;
-
-               /* clip and write blits to cmd stream */
-               vmw_clip_cliprects(tmp, num_clips, clip, blits, &num);
-
-               /* if no cliprects hit skip this */
-               if (num == 0)
-                       continue;
-
-               /* recalculate package length */
-               fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num;
-               cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
-               ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
-                                         fifo_size, 0, NULL, NULL);
-
-               if (unlikely(ret != 0))
-                       break;
-       }
-
-       kfree(cmd);
-out_free_tmp:
-       kfree(tmp);
-
-       return ret;
-}
-
-int vmw_kms_readback(struct vmw_private *dev_priv,
-                    struct drm_file *file_priv,
-                    struct vmw_framebuffer *vfb,
-                    struct drm_vmw_fence_rep __user *user_fence_rep,
-                    struct drm_vmw_rect *clips,
-                    uint32_t num_clips)
-{
-       struct vmw_framebuffer_dmabuf *vfbd =
-               vmw_framebuffer_to_vfbd(&vfb->base);
-       struct vmw_dma_buffer *dmabuf = vfbd->buffer;
-       struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
-       struct drm_crtc *crtc;
-       size_t fifo_size;
-       int i, k, ret, num_units, blits_pos;
-
-       struct {
-               uint32_t header;
-               SVGAFifoCmdDefineGMRFB body;
-       } *cmd;
-       struct {
-               uint32_t header;
-               SVGAFifoCmdBlitScreenToGMRFB body;
-       } *blits;
-
-       num_units = 0;
-       list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
-               if (crtc->primary->fb != &vfb->base)
-                       continue;
-               units[num_units++] = vmw_crtc_to_du(crtc);
-       }
-
-       BUG_ON(dmabuf == NULL);
-       BUG_ON(!clips || !num_clips);
-
-       /* take a safe guess at fifo size */
-       fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips * num_units;
-       cmd = kmalloc(fifo_size, GFP_KERNEL);
-       if (unlikely(cmd == NULL)) {
-               DRM_ERROR("Failed to allocate temporary fifo memory.\n");
-               return -ENOMEM;
-       }
-
-       memset(cmd, 0, fifo_size);
-       cmd->header = SVGA_CMD_DEFINE_GMRFB;
-       cmd->body.format.bitsPerPixel = vfb->base.bits_per_pixel;
-       cmd->body.format.colorDepth = vfb->base.depth;
-       cmd->body.format.reserved = 0;
-       cmd->body.bytesPerLine = vfb->base.pitches[0];
-       cmd->body.ptr.gmrId = vfb->user_handle;
-       cmd->body.ptr.offset = 0;
-
-       blits = (void *)&cmd[1];
-       blits_pos = 0;
-       for (i = 0; i < num_units; i++) {
-               struct drm_vmw_rect *c = clips;
-               for (k = 0; k < num_clips; k++, c++) {
-                       /* transform clip coords to crtc origin based coords */
-                       int clip_x1 = c->x - units[i]->crtc.x;
-                       int clip_x2 = c->x - units[i]->crtc.x + c->w;
-                       int clip_y1 = c->y - units[i]->crtc.y;
-                       int clip_y2 = c->y - units[i]->crtc.y + c->h;
-                       int dest_x = c->x;
-                       int dest_y = c->y;
-
-                       /* compensate for clipping, we negate
-                        * a negative number and add that.
-                        */
-                       if (clip_x1 < 0)
-                               dest_x += -clip_x1;
-                       if (clip_y1 < 0)
-                               dest_y += -clip_y1;
-
-                       /* clip */
-                       clip_x1 = max(clip_x1, 0);
-                       clip_y1 = max(clip_y1, 0);
-                       clip_x2 = min(clip_x2, units[i]->crtc.mode.hdisplay);
-                       clip_y2 = min(clip_y2, units[i]->crtc.mode.vdisplay);
-
-                       /* and cull any rects that misses the crtc */
-                       if (clip_x1 >= units[i]->crtc.mode.hdisplay ||
-                           clip_y1 >= units[i]->crtc.mode.vdisplay ||
-                           clip_x2 <= 0 || clip_y2 <= 0)
-                               continue;
-
-                       blits[blits_pos].header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB;
-                       blits[blits_pos].body.srcScreenId = units[i]->unit;
-                       blits[blits_pos].body.destOrigin.x = dest_x;
-                       blits[blits_pos].body.destOrigin.y = dest_y;
+       int ret;
 
-                       blits[blits_pos].body.srcRect.left = clip_x1;
-                       blits[blits_pos].body.srcRect.top = clip_y1;
-                       blits[blits_pos].body.srcRect.right = clip_x2;
-                       blits[blits_pos].body.srcRect.bottom = clip_y2;
-                       blits_pos++;
-               }
+       switch (dev_priv->active_display_unit) {
+       case vmw_du_screen_target:
+               ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips,
+                                                &surface->res, destX, destY,
+                                                num_clips, 1, NULL);
+               break;
+       case vmw_du_screen_object:
+               ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface,
+                                             sid, destX, destY, clips,
+                                             num_clips);
+               break;
+       default:
+               WARN_ONCE(true,
+                         "Present called with invalid display system.\n");
+               ret = -ENOSYS;
+               break;
        }
-       /* reset size here and use calculated exact size from loops */
-       fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos;
-
-       ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size,
-                                 0, user_fence_rep, NULL);
+       if (ret)
+               return ret;
 
-       kfree(cmd);
+       vmw_fifo_flush(dev_priv, false);
 
-       return ret;
+       return 0;
 }
 
 int vmw_kms_init(struct vmw_private *dev_priv)
@@ -1470,30 +1118,37 @@ int vmw_kms_init(struct vmw_private *dev_priv)
        dev->mode_config.funcs = &vmw_kms_funcs;
        dev->mode_config.min_width = 1;
        dev->mode_config.min_height = 1;
-       /* assumed largest fb size */
-       dev->mode_config.max_width = 8192;
-       dev->mode_config.max_height = 8192;
+       dev->mode_config.max_width = dev_priv->texture_max_width;
+       dev->mode_config.max_height = dev_priv->texture_max_height;
 
-       ret = vmw_kms_init_screen_object_display(dev_priv);
-       if (ret) /* Fallback */
-               (void)vmw_kms_init_legacy_display_system(dev_priv);
+       ret = vmw_kms_stdu_init_display(dev_priv);
+       if (ret) {
+               ret = vmw_kms_sou_init_display(dev_priv);
+               if (ret) /* Fallback */
+                       ret = vmw_kms_ldu_init_display(dev_priv);
+       }
 
-       return 0;
+       return ret;
 }
 
 int vmw_kms_close(struct vmw_private *dev_priv)
 {
+       int ret;
+
        /*
         * Docs says we should take the lock before calling this function
         * but since it destroys encoders and our destructor calls
         * drm_encoder_cleanup which takes the lock we deadlock.
         */
        drm_mode_config_cleanup(dev_priv->dev);
-       if (dev_priv->sou_priv)
-               vmw_kms_close_screen_object_display(dev_priv);
+       if (dev_priv->active_display_unit == vmw_du_screen_object)
+               ret = vmw_kms_sou_close_display(dev_priv);
+       else if (dev_priv->active_display_unit == vmw_du_screen_target)
+               ret = vmw_kms_stdu_close_display(dev_priv);
        else
-               vmw_kms_close_legacy_display_system(dev_priv);
-       return 0;
+               ret = vmw_kms_ldu_close_display(dev_priv);
+
+       return ret;
 }
 
 int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
@@ -1542,7 +1197,8 @@ int vmw_kms_write_svga(struct vmw_private *vmw_priv,
        if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
                vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch);
        else if (vmw_fifo_have_pitchlock(vmw_priv))
-               iowrite32(pitch, vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
+               vmw_mmio_write(pitch, vmw_priv->mmio_virt +
+                              SVGA_FIFO_PITCHLOCK);
        vmw_write(vmw_priv, SVGA_REG_WIDTH, width);
        vmw_write(vmw_priv, SVGA_REG_HEIGHT, height);
        vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp);
@@ -1568,8 +1224,8 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv)
                vmw_priv->vga_pitchlock =
                  vmw_read(vmw_priv, SVGA_REG_PITCHLOCK);
        else if (vmw_fifo_have_pitchlock(vmw_priv))
-               vmw_priv->vga_pitchlock = ioread32(vmw_priv->mmio_virt +
-                                                      SVGA_FIFO_PITCHLOCK);
+               vmw_priv->vga_pitchlock = vmw_mmio_read(vmw_priv->mmio_virt +
+                                                       SVGA_FIFO_PITCHLOCK);
 
        if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY))
                return 0;
@@ -1617,8 +1273,8 @@ int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
                vmw_write(vmw_priv, SVGA_REG_PITCHLOCK,
                          vmw_priv->vga_pitchlock);
        else if (vmw_fifo_have_pitchlock(vmw_priv))
-               iowrite32(vmw_priv->vga_pitchlock,
-                         vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
+               vmw_mmio_write(vmw_priv->vga_pitchlock,
+                              vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
 
        if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY))
                return 0;
@@ -1641,14 +1297,16 @@ bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
                                uint32_t pitch,
                                uint32_t height)
 {
-       return ((u64) pitch * (u64) height) < (u64) dev_priv->prim_bb_mem;
+       return ((u64) pitch * (u64) height) < (u64)
+               ((dev_priv->active_display_unit == vmw_du_screen_target) ?
+                dev_priv->prim_bb_mem : dev_priv->vram_size);
 }
 
 
 /**
  * Function called by DRM code called with vbl_lock held.
  */
-u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc)
+u32 vmw_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
 {
        return 0;
 }
@@ -1656,7 +1314,7 @@ u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc)
 /**
  * Function called by DRM code called with vbl_lock held.
  */
-int vmw_enable_vblank(struct drm_device *dev, int crtc)
+int vmw_enable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        return -ENOSYS;
 }
@@ -1664,7 +1322,7 @@ int vmw_enable_vblank(struct drm_device *dev, int crtc)
 /**
  * Function called by DRM code called with vbl_lock held.
  */
-void vmw_disable_vblank(struct drm_device *dev, int crtc)
+void vmw_disable_vblank(struct drm_device *dev, unsigned int pipe)
 {
 }
 
@@ -1715,75 +1373,6 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
        return 0;
 }
 
-int vmw_du_page_flip(struct drm_crtc *crtc,
-                    struct drm_framebuffer *fb,
-                    struct drm_pending_vblank_event *event,
-                    uint32_t page_flip_flags)
-{
-       struct vmw_private *dev_priv = vmw_priv(crtc->dev);
-       struct drm_framebuffer *old_fb = crtc->primary->fb;
-       struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb);
-       struct drm_file *file_priv ;
-       struct vmw_fence_obj *fence = NULL;
-       struct drm_clip_rect clips;
-       int ret;
-
-       if (event == NULL)
-               return -EINVAL;
-
-       /* require ScreenObject support for page flipping */
-       if (!dev_priv->sou_priv)
-               return -ENOSYS;
-
-       file_priv = event->base.file_priv;
-       if (!vmw_kms_screen_object_flippable(dev_priv, crtc))
-               return -EINVAL;
-
-       crtc->primary->fb = fb;
-
-       /* do a full screen dirty update */
-       clips.x1 = clips.y1 = 0;
-       clips.x2 = fb->width;
-       clips.y2 = fb->height;
-
-       if (vfb->dmabuf)
-               ret = do_dmabuf_dirty_sou(file_priv, dev_priv, vfb,
-                                         0, 0, &clips, 1, 1, &fence);
-       else
-               ret = do_surface_dirty_sou(dev_priv, file_priv, vfb,
-                                          0, 0, &clips, 1, 1, &fence);
-
-
-       if (ret != 0)
-               goto out_no_fence;
-       if (!fence) {
-               ret = -EINVAL;
-               goto out_no_fence;
-       }
-
-       ret = vmw_event_fence_action_queue(file_priv, fence,
-                                          &event->base,
-                                          &event->event.tv_sec,
-                                          &event->event.tv_usec,
-                                          true);
-
-       /*
-        * No need to hold on to this now. The only cleanup
-        * we need to do if we fail is unref the fence.
-        */
-       vmw_fence_obj_unreference(&fence);
-
-       if (vmw_crtc_to_du(crtc)->is_implicit)
-               vmw_kms_screen_object_update_implicit_fb(dev_priv, crtc);
-
-       return ret;
-
-out_no_fence:
-       crtc->primary->fb = old_fb;
-       return ret;
-}
-
-
 void vmw_du_crtc_save(struct drm_crtc *crtc)
 {
 }
@@ -1808,8 +1397,9 @@ void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
        }
 }
 
-void vmw_du_connector_dpms(struct drm_connector *connector, int mode)
+int vmw_du_connector_dpms(struct drm_connector *connector, int mode)
 {
+       return 0;
 }
 
 void vmw_du_connector_save(struct drm_connector *connector)
@@ -1919,7 +1509,7 @@ static struct drm_display_mode vmw_kms_connector_builtin[] = {
  * @mode - Pointer to a struct drm_display_mode with hdisplay and vdisplay
  * members filled in.
  */
-static void vmw_guess_mode_timing(struct drm_display_mode *mode)
+void vmw_guess_mode_timing(struct drm_display_mode *mode)
 {
        mode->hsync_start = mode->hdisplay + 50;
        mode->hsync_end = mode->hsync_start + 50;
@@ -1954,36 +1544,39 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
         * If using screen objects, then assume 32-bpp because that's what the
         * SVGA device is assuming
         */
-       if (dev_priv->sou_priv)
+       if (dev_priv->active_display_unit == vmw_du_screen_object)
                assumed_bpp = 4;
 
+       if (dev_priv->active_display_unit == vmw_du_screen_target) {
+               max_width  = min(max_width,  dev_priv->stdu_max_width);
+               max_height = min(max_height, dev_priv->stdu_max_height);
+       }
+
        /* Add preferred mode */
-       {
-               mode = drm_mode_duplicate(dev, &prefmode);
-               if (!mode)
-                       return 0;
-               mode->hdisplay = du->pref_width;
-               mode->vdisplay = du->pref_height;
-               vmw_guess_mode_timing(mode);
-
-               if (vmw_kms_validate_mode_vram(dev_priv,
-                                               mode->hdisplay * assumed_bpp,
-                                               mode->vdisplay)) {
-                       drm_mode_probed_add(connector, mode);
-               } else {
-                       drm_mode_destroy(dev, mode);
-                       mode = NULL;
-               }
+       mode = drm_mode_duplicate(dev, &prefmode);
+       if (!mode)
+               return 0;
+       mode->hdisplay = du->pref_width;
+       mode->vdisplay = du->pref_height;
+       vmw_guess_mode_timing(mode);
 
-               if (du->pref_mode) {
-                       list_del_init(&du->pref_mode->head);
-                       drm_mode_destroy(dev, du->pref_mode);
-               }
+       if (vmw_kms_validate_mode_vram(dev_priv,
+                                       mode->hdisplay * assumed_bpp,
+                                       mode->vdisplay)) {
+               drm_mode_probed_add(connector, mode);
+       } else {
+               drm_mode_destroy(dev, mode);
+               mode = NULL;
+       }
 
-               /* mode might be null here, this is intended */
-               du->pref_mode = mode;
+       if (du->pref_mode) {
+               list_del_init(&du->pref_mode->head);
+               drm_mode_destroy(dev, du->pref_mode);
        }
 
+       /* mode might be null here, this is intended */
+       du->pref_mode = mode;
+
        for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) {
                bmode = &vmw_kms_connector_builtin[i];
                if (bmode->hdisplay > max_width ||
@@ -2003,11 +1596,9 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
                drm_mode_probed_add(connector, mode);
        }
 
-       /* Move the prefered mode first, help apps pick the right mode. */
-       if (du->pref_mode)
-               list_move(&du->pref_mode->head, &connector->probed_modes);
-
        drm_mode_connector_list_update(connector, true);
+       /* Move the prefered mode first, help apps pick the right mode. */
+       drm_mode_sort(&connector->modes);
 
        return 1;
 }
@@ -2031,7 +1622,9 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
        unsigned rects_size;
        int ret;
        int i;
+       u64 total_pixels = 0;
        struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_vmw_rect bounding_box = {0};
 
        if (!arg->num_outputs) {
                struct drm_vmw_rect def_rect = {0, 0, 800, 600};
@@ -2062,6 +1655,40 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
                        ret = -EINVAL;
                        goto out_free;
                }
+
+               /*
+                * bounding_box.w and bunding_box.h are used as
+                * lower-right coordinates
+                */
+               if (rects[i].x + rects[i].w > bounding_box.w)
+                       bounding_box.w = rects[i].x + rects[i].w;
+
+               if (rects[i].y + rects[i].h > bounding_box.h)
+                       bounding_box.h = rects[i].y + rects[i].h;
+
+               total_pixels += (u64) rects[i].w * (u64) rects[i].h;
+       }
+
+       if (dev_priv->active_display_unit == vmw_du_screen_target) {
+               /*
+                * For Screen Targets, the limits for a toplogy are:
+                *      1. Bounding box (assuming 32bpp) must be < prim_bb_mem
+                *      2. Total pixels (assuming 32bpp) must be < prim_bb_mem
+                */
+               u64 bb_mem    = bounding_box.w * bounding_box.h * 4;
+               u64 pixel_mem = total_pixels * 4;
+
+               if (bb_mem > dev_priv->prim_bb_mem) {
+                       DRM_ERROR("Topology is beyond supported limits.\n");
+                       ret = -EINVAL;
+                       goto out_free;
+               }
+
+               if (pixel_mem > dev_priv->prim_bb_mem) {
+                       DRM_ERROR("Combined output size too large\n");
+                       ret = -EINVAL;
+                       goto out_free;
+               }
        }
 
        vmw_du_update_layout(dev_priv, arg->num_outputs, rects);
@@ -2070,3 +1697,418 @@ out_free:
        kfree(rects);
        return ret;
 }
+
+/**
+ * vmw_kms_helper_dirty - Helper to build commands and perform actions based
+ * on a set of cliprects and a set of display units.
+ *
+ * @dev_priv: Pointer to a device private structure.
+ * @framebuffer: Pointer to the framebuffer on which to perform the actions.
+ * @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL.
+ * Cliprects are given in framebuffer coordinates.
+ * @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must
+ * be NULL. Cliprects are given in source coordinates.
+ * @dest_x: X coordinate offset for the crtc / destination clip rects.
+ * @dest_y: Y coordinate offset for the crtc / destination clip rects.
+ * @num_clips: Number of cliprects in the @clips or @vclips array.
+ * @increment: Integer with which to increment the clip counter when looping.
+ * Used to skip a predetermined number of clip rects.
+ * @dirty: Closure structure. See the description of struct vmw_kms_dirty.
+ */
+int vmw_kms_helper_dirty(struct vmw_private *dev_priv,
+                        struct vmw_framebuffer *framebuffer,
+                        const struct drm_clip_rect *clips,
+                        const struct drm_vmw_rect *vclips,
+                        s32 dest_x, s32 dest_y,
+                        int num_clips,
+                        int increment,
+                        struct vmw_kms_dirty *dirty)
+{
+       struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
+       struct drm_crtc *crtc;
+       u32 num_units = 0;
+       u32 i, k;
+
+       dirty->dev_priv = dev_priv;
+
+       list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) {
+               if (crtc->primary->fb != &framebuffer->base)
+                       continue;
+               units[num_units++] = vmw_crtc_to_du(crtc);
+       }
+
+       for (k = 0; k < num_units; k++) {
+               struct vmw_display_unit *unit = units[k];
+               s32 crtc_x = unit->crtc.x;
+               s32 crtc_y = unit->crtc.y;
+               s32 crtc_width = unit->crtc.mode.hdisplay;
+               s32 crtc_height = unit->crtc.mode.vdisplay;
+               const struct drm_clip_rect *clips_ptr = clips;
+               const struct drm_vmw_rect *vclips_ptr = vclips;
+
+               dirty->unit = unit;
+               if (dirty->fifo_reserve_size > 0) {
+                       dirty->cmd = vmw_fifo_reserve(dev_priv,
+                                                     dirty->fifo_reserve_size);
+                       if (!dirty->cmd) {
+                               DRM_ERROR("Couldn't reserve fifo space "
+                                         "for dirty blits.\n");
+                               return -ENOMEM;
+                       }
+                       memset(dirty->cmd, 0, dirty->fifo_reserve_size);
+               }
+               dirty->num_hits = 0;
+               for (i = 0; i < num_clips; i++, clips_ptr += increment,
+                      vclips_ptr += increment) {
+                       s32 clip_left;
+                       s32 clip_top;
+
+                       /*
+                        * Select clip array type. Note that integer type
+                        * in @clips is unsigned short, whereas in @vclips
+                        * it's 32-bit.
+                        */
+                       if (clips) {
+                               dirty->fb_x = (s32) clips_ptr->x1;
+                               dirty->fb_y = (s32) clips_ptr->y1;
+                               dirty->unit_x2 = (s32) clips_ptr->x2 + dest_x -
+                                       crtc_x;
+                               dirty->unit_y2 = (s32) clips_ptr->y2 + dest_y -
+                                       crtc_y;
+                       } else {
+                               dirty->fb_x = vclips_ptr->x;
+                               dirty->fb_y = vclips_ptr->y;
+                               dirty->unit_x2 = dirty->fb_x + vclips_ptr->w +
+                                       dest_x - crtc_x;
+                               dirty->unit_y2 = dirty->fb_y + vclips_ptr->h +
+                                       dest_y - crtc_y;
+                       }
+
+                       dirty->unit_x1 = dirty->fb_x + dest_x - crtc_x;
+                       dirty->unit_y1 = dirty->fb_y + dest_y - crtc_y;
+
+                       /* Skip this clip if it's outside the crtc region */
+                       if (dirty->unit_x1 >= crtc_width ||
+                           dirty->unit_y1 >= crtc_height ||
+                           dirty->unit_x2 <= 0 || dirty->unit_y2 <= 0)
+                               continue;
+
+                       /* Clip right and bottom to crtc limits */
+                       dirty->unit_x2 = min_t(s32, dirty->unit_x2,
+                                              crtc_width);
+                       dirty->unit_y2 = min_t(s32, dirty->unit_y2,
+                                              crtc_height);
+
+                       /* Clip left and top to crtc limits */
+                       clip_left = min_t(s32, dirty->unit_x1, 0);
+                       clip_top = min_t(s32, dirty->unit_y1, 0);
+                       dirty->unit_x1 -= clip_left;
+                       dirty->unit_y1 -= clip_top;
+                       dirty->fb_x -= clip_left;
+                       dirty->fb_y -= clip_top;
+
+                       dirty->clip(dirty);
+               }
+
+               dirty->fifo_commit(dirty);
+       }
+
+       return 0;
+}
+
+/**
+ * vmw_kms_helper_buffer_prepare - Reserve and validate a buffer object before
+ * command submission.
+ *
+ * @dev_priv. Pointer to a device private structure.
+ * @buf: The buffer object
+ * @interruptible: Whether to perform waits as interruptible.
+ * @validate_as_mob: Whether the buffer should be validated as a MOB. If false,
+ * The buffer will be validated as a GMR. Already pinned buffers will not be
+ * validated.
+ *
+ * Returns 0 on success, negative error code on failure, -ERESTARTSYS if
+ * interrupted by a signal.
+ */
+int vmw_kms_helper_buffer_prepare(struct vmw_private *dev_priv,
+                                 struct vmw_dma_buffer *buf,
+                                 bool interruptible,
+                                 bool validate_as_mob)
+{
+       struct ttm_buffer_object *bo = &buf->base;
+       int ret;
+
+       ttm_bo_reserve(bo, false, false, interruptible, NULL);
+       ret = vmw_validate_single_buffer(dev_priv, bo, interruptible,
+                                        validate_as_mob);
+       if (ret)
+               ttm_bo_unreserve(bo);
+
+       return ret;
+}
+
+/**
+ * vmw_kms_helper_buffer_revert - Undo the actions of
+ * vmw_kms_helper_buffer_prepare.
+ *
+ * @res: Pointer to the buffer object.
+ *
+ * Helper to be used if an error forces the caller to undo the actions of
+ * vmw_kms_helper_buffer_prepare.
+ */
+void vmw_kms_helper_buffer_revert(struct vmw_dma_buffer *buf)
+{
+       if (buf)
+               ttm_bo_unreserve(&buf->base);
+}
+
+/**
+ * vmw_kms_helper_buffer_finish - Unreserve and fence a buffer object after
+ * kms command submission.
+ *
+ * @dev_priv: Pointer to a device private structure.
+ * @file_priv: Pointer to a struct drm_file representing the caller's
+ * connection. Must be set to NULL if @user_fence_rep is NULL, and conversely
+ * if non-NULL, @user_fence_rep must be non-NULL.
+ * @buf: The buffer object.
+ * @out_fence:  Optional pointer to a fence pointer. If non-NULL, a
+ * ref-counted fence pointer is returned here.
+ * @user_fence_rep: Optional pointer to a user-space provided struct
+ * drm_vmw_fence_rep. If provided, @file_priv must also be provided and the
+ * function copies fence data to user-space in a fail-safe manner.
+ */
+void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv,
+                                 struct drm_file *file_priv,
+                                 struct vmw_dma_buffer *buf,
+                                 struct vmw_fence_obj **out_fence,
+                                 struct drm_vmw_fence_rep __user *
+                                 user_fence_rep)
+{
+       struct vmw_fence_obj *fence;
+       uint32_t handle;
+       int ret;
+
+       ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence,
+                                        file_priv ? &handle : NULL);
+       if (buf)
+               vmw_fence_single_bo(&buf->base, fence);
+       if (file_priv)
+               vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv),
+                                           ret, user_fence_rep, fence,
+                                           handle);
+       if (out_fence)
+               *out_fence = fence;
+       else
+               vmw_fence_obj_unreference(&fence);
+
+       vmw_kms_helper_buffer_revert(buf);
+}
+
+
+/**
+ * vmw_kms_helper_resource_revert - Undo the actions of
+ * vmw_kms_helper_resource_prepare.
+ *
+ * @res: Pointer to the resource. Typically a surface.
+ *
+ * Helper to be used if an error forces the caller to undo the actions of
+ * vmw_kms_helper_resource_prepare.
+ */
+void vmw_kms_helper_resource_revert(struct vmw_resource *res)
+{
+       vmw_kms_helper_buffer_revert(res->backup);
+       vmw_resource_unreserve(res, false, NULL, 0);
+       mutex_unlock(&res->dev_priv->cmdbuf_mutex);
+}
+
+/**
+ * vmw_kms_helper_resource_prepare - Reserve and validate a resource before
+ * command submission.
+ *
+ * @res: Pointer to the resource. Typically a surface.
+ * @interruptible: Whether to perform waits as interruptible.
+ *
+ * Reserves and validates also the backup buffer if a guest-backed resource.
+ * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
+ * interrupted by a signal.
+ */
+int vmw_kms_helper_resource_prepare(struct vmw_resource *res,
+                                   bool interruptible)
+{
+       int ret = 0;
+
+       if (interruptible)
+               ret = mutex_lock_interruptible(&res->dev_priv->cmdbuf_mutex);
+       else
+               mutex_lock(&res->dev_priv->cmdbuf_mutex);
+
+       if (unlikely(ret != 0))
+               return -ERESTARTSYS;
+
+       ret = vmw_resource_reserve(res, interruptible, false);
+       if (ret)
+               goto out_unlock;
+
+       if (res->backup) {
+               ret = vmw_kms_helper_buffer_prepare(res->dev_priv, res->backup,
+                                                   interruptible,
+                                                   res->dev_priv->has_mob);
+               if (ret)
+                       goto out_unreserve;
+       }
+       ret = vmw_resource_validate(res);
+       if (ret)
+               goto out_revert;
+       return 0;
+
+out_revert:
+       vmw_kms_helper_buffer_revert(res->backup);
+out_unreserve:
+       vmw_resource_unreserve(res, false, NULL, 0);
+out_unlock:
+       mutex_unlock(&res->dev_priv->cmdbuf_mutex);
+       return ret;
+}
+
+/**
+ * vmw_kms_helper_resource_finish - Unreserve and fence a resource after
+ * kms command submission.
+ *
+ * @res: Pointer to the resource. Typically a surface.
+ * @out_fence: Optional pointer to a fence pointer. If non-NULL, a
+ * ref-counted fence pointer is returned here.
+ */
+void vmw_kms_helper_resource_finish(struct vmw_resource *res,
+                            struct vmw_fence_obj **out_fence)
+{
+       if (res->backup || out_fence)
+               vmw_kms_helper_buffer_finish(res->dev_priv, NULL, res->backup,
+                                            out_fence, NULL);
+
+       vmw_resource_unreserve(res, false, NULL, 0);
+       mutex_unlock(&res->dev_priv->cmdbuf_mutex);
+}
+
+/**
+ * vmw_kms_update_proxy - Helper function to update a proxy surface from
+ * its backing MOB.
+ *
+ * @res: Pointer to the surface resource
+ * @clips: Clip rects in framebuffer (surface) space.
+ * @num_clips: Number of clips in @clips.
+ * @increment: Integer with which to increment the clip counter when looping.
+ * Used to skip a predetermined number of clip rects.
+ *
+ * This function makes sure the proxy surface is updated from its backing MOB
+ * using the region given by @clips. The surface resource @res and its backing
+ * MOB needs to be reserved and validated on call.
+ */
+int vmw_kms_update_proxy(struct vmw_resource *res,
+                        const struct drm_clip_rect *clips,
+                        unsigned num_clips,
+                        int increment)
+{
+       struct vmw_private *dev_priv = res->dev_priv;
+       struct drm_vmw_size *size = &vmw_res_to_srf(res)->base_size;
+       struct {
+               SVGA3dCmdHeader header;
+               SVGA3dCmdUpdateGBImage body;
+       } *cmd;
+       SVGA3dBox *box;
+       size_t copy_size = 0;
+       int i;
+
+       if (!clips)
+               return 0;
+
+       cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
+       if (!cmd) {
+               DRM_ERROR("Couldn't reserve fifo space for proxy surface "
+                         "update.\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) {
+               box = &cmd->body.box;
+
+               cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE;
+               cmd->header.size = sizeof(cmd->body);
+               cmd->body.image.sid = res->id;
+               cmd->body.image.face = 0;
+               cmd->body.image.mipmap = 0;
+
+               if (clips->x1 > size->width || clips->x2 > size->width ||
+                   clips->y1 > size->height || clips->y2 > size->height) {
+                       DRM_ERROR("Invalid clips outsize of framebuffer.\n");
+                       return -EINVAL;
+               }
+
+               box->x = clips->x1;
+               box->y = clips->y1;
+               box->z = 0;
+               box->w = clips->x2 - clips->x1;
+               box->h = clips->y2 - clips->y1;
+               box->d = 1;
+
+               copy_size += sizeof(*cmd);
+       }
+
+       vmw_fifo_commit(dev_priv, copy_size);
+
+       return 0;
+}
+
+int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv,
+                           unsigned unit,
+                           u32 max_width,
+                           u32 max_height,
+                           struct drm_connector **p_con,
+                           struct drm_crtc **p_crtc,
+                           struct drm_display_mode **p_mode)
+{
+       struct drm_connector *con;
+       struct vmw_display_unit *du;
+       struct drm_display_mode *mode;
+       int i = 0;
+
+       list_for_each_entry(con, &dev_priv->dev->mode_config.connector_list,
+                           head) {
+               if (i == unit)
+                       break;
+
+               ++i;
+       }
+
+       if (i != unit) {
+               DRM_ERROR("Could not find initial display unit.\n");
+               return -EINVAL;
+       }
+
+       if (list_empty(&con->modes))
+               (void) vmw_du_connector_fill_modes(con, max_width, max_height);
+
+       if (list_empty(&con->modes)) {
+               DRM_ERROR("Could not find initial display mode.\n");
+               return -EINVAL;
+       }
+
+       du = vmw_connector_to_du(con);
+       *p_con = con;
+       *p_crtc = &du->crtc;
+
+       list_for_each_entry(mode, &con->modes, head) {
+               if (mode->type & DRM_MODE_TYPE_PREFERRED)
+                       break;
+       }
+
+       if (mode->type & DRM_MODE_TYPE_PREFERRED)
+               *p_mode = mode;
+       else {
+               WARN_ONCE(true, "Could not find initial preferred mode.\n");
+               *p_mode = list_first_entry(&con->modes,
+                                          struct drm_display_mode,
+                                          head);
+       }
+
+       return 0;
+}