+/**
+ * vmw_stdu_dmabuf_clip - Callback to encode a suface DMA command cliprect
+ *
+ * @dirty: The closure structure.
+ *
+ * Encodes a surface DMA command cliprect and updates the bounding box
+ * for the DMA.
+ */
+static void vmw_stdu_dmabuf_clip(struct vmw_kms_dirty *dirty)
+{
+ struct vmw_stdu_dirty *ddirty =
+ container_of(dirty, struct vmw_stdu_dirty, base);
+ struct vmw_stdu_dma *cmd = dirty->cmd;
+ struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
+
+ blit += dirty->num_hits;
+ blit->srcx = dirty->fb_x;
+ blit->srcy = dirty->fb_y;
+ blit->x = dirty->unit_x1;
+ blit->y = dirty->unit_y1;
+ blit->d = 1;
+ blit->w = dirty->unit_x2 - dirty->unit_x1;
+ blit->h = dirty->unit_y2 - dirty->unit_y1;
+ dirty->num_hits++;
+
+ if (ddirty->transfer != SVGA3D_WRITE_HOST_VRAM)
+ return;
+
+ /* Destination bounding box */
+ ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1);
+ ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1);
+ ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2);
+ ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2);
+}
+
+/**
+ * vmw_stdu_dmabuf_fifo_commit - Callback to fill in and submit a DMA command.
+ *
+ * @dirty: The closure structure.
+ *
+ * Fills in the missing fields in a DMA command, and optionally encodes
+ * a screen target update command, depending on transfer direction.
+ */
+static void vmw_stdu_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty)
+{
+ struct vmw_stdu_dirty *ddirty =
+ container_of(dirty, struct vmw_stdu_dirty, base);
+ struct vmw_screen_target_display_unit *stdu =
+ container_of(dirty->unit, typeof(*stdu), base);
+ struct vmw_stdu_dma *cmd = dirty->cmd;
+ struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
+ SVGA3dCmdSurfaceDMASuffix *suffix =
+ (SVGA3dCmdSurfaceDMASuffix *) &blit[dirty->num_hits];
+ size_t blit_size = sizeof(*blit) * dirty->num_hits + sizeof(*suffix);
+
+ if (!dirty->num_hits) {
+ vmw_fifo_commit(dirty->dev_priv, 0);
+ return;
+ }
+
+ cmd->header.id = SVGA_3D_CMD_SURFACE_DMA;
+ cmd->header.size = sizeof(cmd->body) + blit_size;
+ vmw_bo_get_guest_ptr(&ddirty->buf->base, &cmd->body.guest.ptr);
+ cmd->body.guest.pitch = ddirty->pitch;
+ cmd->body.host.sid = stdu->display_srf->res.id;
+ cmd->body.host.face = 0;
+ cmd->body.host.mipmap = 0;
+ cmd->body.transfer = ddirty->transfer;
+ suffix->suffixSize = sizeof(*suffix);
+ suffix->maximumOffset = ddirty->buf->base.num_pages * PAGE_SIZE;
+
+ if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) {
+ blit_size += sizeof(struct vmw_stdu_update);
+
+ vmw_stdu_populate_update(&suffix[1], stdu->base.unit,
+ ddirty->left, ddirty->right,
+ ddirty->top, ddirty->bottom);
+ }
+
+ vmw_fifo_commit(dirty->dev_priv, sizeof(*cmd) + blit_size);
+
+ ddirty->left = ddirty->top = S32_MAX;
+ ddirty->right = ddirty->bottom = S32_MIN;
+}
+
+/**
+ * vmw_kms_stdu_dma - Perform a DMA transfer between a dma-buffer backed
+ * framebuffer and the screen target system.
+ *
+ * @dev_priv: Pointer to the device private structure.
+ * @file_priv: Pointer to a struct drm-file identifying the caller. May be
+ * set to NULL, but then @user_fence_rep must also be set to NULL.
+ * @vfb: Pointer to the dma-buffer backed framebuffer.
+ * @clips: Array of clip rects. Either @clips or @vclips must be NULL.
+ * @vclips: Alternate array of clip rects. Either @clips or @vclips must
+ * be NULL.
+ * @num_clips: Number of clip rects in @clips or @vclips.
+ * @increment: Increment to use when looping over @clips or @vclips.
+ * @to_surface: Whether to DMA to the screen target system as opposed to
+ * from the screen target system.
+ * @interruptible: Whether to perform waits interruptible if possible.
+ *
+ * If DMA-ing till the screen target system, the function will also notify
+ * the screen target system that a bounding box of the cliprects has been
+ * updated.
+ * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
+ * interrupted.
+ */
+int vmw_kms_stdu_dma(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_clip_rect *clips,
+ struct drm_vmw_rect *vclips,
+ uint32_t num_clips,
+ int increment,
+ bool to_surface,
+ bool interruptible)
+{
+ struct vmw_dma_buffer *buf =
+ container_of(vfb, struct vmw_framebuffer_dmabuf, base)->buffer;
+ struct vmw_stdu_dirty ddirty;
+ int ret;
+
+ ret = vmw_kms_helper_buffer_prepare(dev_priv, buf, interruptible,
+ false);
+ if (ret)
+ return ret;
+
+ ddirty.transfer = (to_surface) ? SVGA3D_WRITE_HOST_VRAM :
+ SVGA3D_READ_HOST_VRAM;
+ ddirty.left = ddirty.top = S32_MAX;
+ ddirty.right = ddirty.bottom = S32_MIN;
+ ddirty.pitch = vfb->base.pitches[0];
+ ddirty.buf = buf;
+ ddirty.base.fifo_commit = vmw_stdu_dmabuf_fifo_commit;
+ ddirty.base.clip = vmw_stdu_dmabuf_clip;
+ ddirty.base.fifo_reserve_size = sizeof(struct vmw_stdu_dma) +
+ num_clips * sizeof(SVGA3dCopyBox) +
+ sizeof(SVGA3dCmdSurfaceDMASuffix);
+ if (to_surface)
+ ddirty.base.fifo_reserve_size += sizeof(struct vmw_stdu_update);
+
+ ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips,
+ 0, 0, num_clips, increment, &ddirty.base);
+ vmw_kms_helper_buffer_finish(dev_priv, file_priv, buf, NULL,
+ user_fence_rep);
+
+ return ret;
+}
+
+/**
+ * vmw_stdu_surface_clip - Callback to encode a surface copy command cliprect
+ *
+ * @dirty: The closure structure.
+ *
+ * Encodes a surface copy command cliprect and updates the bounding box
+ * for the copy.
+ */
+static void vmw_kms_stdu_surface_clip(struct vmw_kms_dirty *dirty)
+{
+ struct vmw_stdu_dirty *sdirty =
+ container_of(dirty, struct vmw_stdu_dirty, base);
+ struct vmw_stdu_surface_copy *cmd = dirty->cmd;
+ struct vmw_screen_target_display_unit *stdu =
+ container_of(dirty->unit, typeof(*stdu), base);
+
+ if (sdirty->sid != stdu->display_srf->res.id) {
+ struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
+
+ blit += dirty->num_hits;
+ blit->srcx = dirty->fb_x;
+ blit->srcy = dirty->fb_y;
+ blit->x = dirty->unit_x1;
+ blit->y = dirty->unit_y1;
+ blit->d = 1;
+ blit->w = dirty->unit_x2 - dirty->unit_x1;
+ blit->h = dirty->unit_y2 - dirty->unit_y1;
+ }
+
+ dirty->num_hits++;
+
+ /* Destination bounding box */
+ sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1);
+ sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1);
+ sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2);
+ sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2);
+}
+
+/**
+ * vmw_stdu_surface_fifo_commit - Callback to fill in and submit a surface
+ * copy command.
+ *
+ * @dirty: The closure structure.
+ *
+ * Fills in the missing fields in a surface copy command, and encodes a screen
+ * target update command.
+ */
+static void vmw_kms_stdu_surface_fifo_commit(struct vmw_kms_dirty *dirty)
+{
+ struct vmw_stdu_dirty *sdirty =
+ container_of(dirty, struct vmw_stdu_dirty, base);
+ struct vmw_screen_target_display_unit *stdu =
+ container_of(dirty->unit, typeof(*stdu), base);
+ struct vmw_stdu_surface_copy *cmd = dirty->cmd;
+ struct vmw_stdu_update *update;
+ size_t blit_size = sizeof(SVGA3dCopyBox) * dirty->num_hits;
+ size_t commit_size;
+
+ if (!dirty->num_hits) {
+ vmw_fifo_commit(dirty->dev_priv, 0);
+ return;
+ }
+
+ if (sdirty->sid != stdu->display_srf->res.id) {
+ struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1];
+
+ cmd->header.id = SVGA_3D_CMD_SURFACE_COPY;
+ cmd->header.size = sizeof(cmd->body) + blit_size;
+ cmd->body.src.sid = sdirty->sid;
+ cmd->body.dest.sid = stdu->display_srf->res.id;
+ update = (struct vmw_stdu_update *) &blit[dirty->num_hits];
+ commit_size = sizeof(*cmd) + blit_size + sizeof(*update);
+ } else {
+ update = dirty->cmd;
+ commit_size = sizeof(*update);
+ }
+
+ vmw_stdu_populate_update(update, stdu->base.unit, sdirty->left,
+ sdirty->right, sdirty->top, sdirty->bottom);
+
+ vmw_fifo_commit(dirty->dev_priv, commit_size);
+
+ sdirty->left = sdirty->top = S32_MAX;
+ sdirty->right = sdirty->bottom = S32_MIN;
+}
+
+/**
+ * vmw_kms_stdu_surface_dirty - Dirty part of a surface backed framebuffer
+ *
+ * @dev_priv: Pointer to the device private structure.
+ * @framebuffer: Pointer to the surface-buffer backed framebuffer.
+ * @clips: Array of clip rects. Either @clips or @vclips must be NULL.
+ * @vclips: Alternate array of clip rects. Either @clips or @vclips must
+ * be NULL.
+ * @srf: Pointer to surface to blit from. If NULL, the surface attached
+ * to @framebuffer will be used.
+ * @dest_x: X coordinate offset to align @srf with framebuffer coordinates.
+ * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates.
+ * @num_clips: Number of clip rects in @clips.
+ * @inc: Increment to use when looping over @clips.
+ * @out_fence: If non-NULL, will return a ref-counted pointer to a
+ * struct vmw_fence_obj. The returned fence pointer may be NULL in which
+ * case the device has already synchronized.
+ *
+ * Returns 0 on success, negative error code on failure. -ERESTARTSYS if
+ * interrupted.
+ */
+int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv,
+ struct vmw_framebuffer *framebuffer,
+ struct drm_clip_rect *clips,
+ struct drm_vmw_rect *vclips,
+ struct vmw_resource *srf,
+ s32 dest_x,
+ s32 dest_y,
+ unsigned num_clips, int inc,
+ struct vmw_fence_obj **out_fence)
+{
+ struct vmw_framebuffer_surface *vfbs =
+ container_of(framebuffer, typeof(*vfbs), base);
+ struct vmw_stdu_dirty sdirty;
+ int ret;
+
+ if (!srf)
+ srf = &vfbs->surface->res;
+
+ ret = vmw_kms_helper_resource_prepare(srf, true);
+ if (ret)
+ return ret;
+
+ if (vfbs->is_dmabuf_proxy) {
+ ret = vmw_kms_update_proxy(srf, clips, num_clips, inc);
+ if (ret)
+ goto out_finish;
+ }
+
+ sdirty.base.fifo_commit = vmw_kms_stdu_surface_fifo_commit;
+ sdirty.base.clip = vmw_kms_stdu_surface_clip;
+ sdirty.base.fifo_reserve_size = sizeof(struct vmw_stdu_surface_copy) +
+ sizeof(SVGA3dCopyBox) * num_clips +
+ sizeof(struct vmw_stdu_update);
+ sdirty.sid = srf->id;
+ sdirty.left = sdirty.top = S32_MAX;
+ sdirty.right = sdirty.bottom = S32_MIN;
+
+ ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips,
+ dest_x, dest_y, num_clips, inc,
+ &sdirty.base);
+out_finish:
+ vmw_kms_helper_resource_finish(srf, out_fence);
+
+ return ret;
+}
+