|
@@ -109,6 +109,10 @@ struct vmw_screen_target_display_unit {
|
|
|
s32 display_width, display_height;
|
|
|
|
|
|
bool defined;
|
|
|
+
|
|
|
+ /* For CPU Blit */
|
|
|
+ struct ttm_bo_kmap_obj host_map, guest_map;
|
|
|
+ unsigned int cpp;
|
|
|
};
|
|
|
|
|
|
|
|
@@ -636,6 +640,129 @@ static void vmw_stdu_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty)
|
|
|
ddirty->right = ddirty->bottom = S32_MIN;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * vmw_stdu_dmabuf_cpu_clip - Callback to encode a CPU blit
|
|
|
+ *
|
|
|
+ * @dirty: The closure structure.
|
|
|
+ *
|
|
|
+ * This function calculates the bounding box for all the incoming clips
|
|
|
+ */
|
|
|
+static void vmw_stdu_dmabuf_cpu_clip(struct vmw_kms_dirty *dirty)
|
|
|
+{
|
|
|
+ struct vmw_stdu_dirty *ddirty =
|
|
|
+ container_of(dirty, struct vmw_stdu_dirty, base);
|
|
|
+
|
|
|
+ dirty->num_hits = 1;
|
|
|
+
|
|
|
+ /* Calculate 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_cpu_commit - Callback to do a CPU blit from DMAbuf
|
|
|
+ *
|
|
|
+ * @dirty: The closure structure.
|
|
|
+ *
|
|
|
+ * For the special case when we cannot create a proxy surface in a
|
|
|
+ * 2D VM, we have to do a CPU blit ourselves.
|
|
|
+ */
|
|
|
+static void vmw_stdu_dmabuf_cpu_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);
|
|
|
+ s32 width, height;
|
|
|
+ s32 src_pitch, dst_pitch;
|
|
|
+ u8 *src, *dst;
|
|
|
+ bool not_used;
|
|
|
+
|
|
|
+
|
|
|
+ if (!dirty->num_hits)
|
|
|
+ return;
|
|
|
+
|
|
|
+ width = ddirty->right - ddirty->left;
|
|
|
+ height = ddirty->bottom - ddirty->top;
|
|
|
+
|
|
|
+ if (width == 0 || height == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+
|
|
|
+ /* Assume we are blitting from Host (display_srf) to Guest (dmabuf) */
|
|
|
+ src_pitch = stdu->display_srf->base_size.width * stdu->cpp;
|
|
|
+ src = ttm_kmap_obj_virtual(&stdu->host_map, ¬_used);
|
|
|
+ src += dirty->unit_y1 * src_pitch + dirty->unit_x1 * stdu->cpp;
|
|
|
+
|
|
|
+ dst_pitch = ddirty->pitch;
|
|
|
+ dst = ttm_kmap_obj_virtual(&stdu->guest_map, ¬_used);
|
|
|
+ dst += dirty->fb_y * dst_pitch + dirty->fb_x * stdu->cpp;
|
|
|
+
|
|
|
+
|
|
|
+ /* Figure out the real direction */
|
|
|
+ if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) {
|
|
|
+ u8 *tmp;
|
|
|
+ s32 tmp_pitch;
|
|
|
+
|
|
|
+ tmp = src;
|
|
|
+ tmp_pitch = src_pitch;
|
|
|
+
|
|
|
+ src = dst;
|
|
|
+ src_pitch = dst_pitch;
|
|
|
+
|
|
|
+ dst = tmp;
|
|
|
+ dst_pitch = tmp_pitch;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* CPU Blit */
|
|
|
+ while (height-- > 0) {
|
|
|
+ memcpy(dst, src, width * stdu->cpp);
|
|
|
+ dst += dst_pitch;
|
|
|
+ src += src_pitch;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ddirty->transfer == SVGA3D_WRITE_HOST_VRAM) {
|
|
|
+ struct vmw_private *dev_priv;
|
|
|
+ struct vmw_stdu_update *cmd;
|
|
|
+ struct drm_clip_rect region;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* We are updating the actual surface, not a proxy */
|
|
|
+ region.x1 = ddirty->left;
|
|
|
+ region.x2 = ddirty->right;
|
|
|
+ region.y1 = ddirty->top;
|
|
|
+ region.y2 = ddirty->bottom;
|
|
|
+ ret = vmw_kms_update_proxy(
|
|
|
+ (struct vmw_resource *) &stdu->display_srf->res,
|
|
|
+ (const struct drm_clip_rect *) ®ion, 1, 1);
|
|
|
+ if (ret)
|
|
|
+ goto out_cleanup;
|
|
|
+
|
|
|
+
|
|
|
+ dev_priv = vmw_priv(stdu->base.crtc.dev);
|
|
|
+ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
|
|
|
+
|
|
|
+ if (!cmd) {
|
|
|
+ DRM_ERROR("Cannot reserve FIFO space to update STDU");
|
|
|
+ goto out_cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ vmw_stdu_populate_update(cmd, stdu->base.unit,
|
|
|
+ ddirty->left, ddirty->right,
|
|
|
+ ddirty->top, ddirty->bottom);
|
|
|
+
|
|
|
+ vmw_fifo_commit(dev_priv, sizeof(*cmd));
|
|
|
+ }
|
|
|
+
|
|
|
+out_cleanup:
|
|
|
+ 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.
|
|
@@ -694,6 +821,13 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
|
|
|
if (to_surface)
|
|
|
ddirty.base.fifo_reserve_size += sizeof(struct vmw_stdu_update);
|
|
|
|
|
|
+ /* 2D VMs cannot use SVGA_3D_CMD_SURFACE_DMA so do CPU blit instead */
|
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_3D)) {
|
|
|
+ ddirty.base.fifo_commit = vmw_stdu_dmabuf_cpu_commit;
|
|
|
+ ddirty.base.clip = vmw_stdu_dmabuf_cpu_clip;
|
|
|
+ ddirty.base.fifo_reserve_size = 0;
|
|
|
+ }
|
|
|
+
|
|
|
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,
|
|
@@ -960,12 +1094,19 @@ vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,
|
|
|
{
|
|
|
struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
|
|
|
|
|
|
+ if (vps->guest_map.virtual)
|
|
|
+ ttm_bo_kunmap(&vps->guest_map);
|
|
|
+
|
|
|
+ if (vps->host_map.virtual)
|
|
|
+ ttm_bo_kunmap(&vps->host_map);
|
|
|
+
|
|
|
if (vps->surf)
|
|
|
WARN_ON(!vps->pinned);
|
|
|
|
|
|
vmw_du_plane_cleanup_fb(plane, old_state);
|
|
|
|
|
|
vps->content_fb_type = SAME_AS_DISPLAY;
|
|
|
+ vps->cpp = 0;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -986,6 +1127,7 @@ static int
|
|
|
vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
|
|
|
struct drm_plane_state *new_state)
|
|
|
{
|
|
|
+ struct vmw_private *dev_priv = vmw_priv(plane->dev);
|
|
|
struct drm_framebuffer *new_fb = new_state->fb;
|
|
|
struct vmw_framebuffer *vfb;
|
|
|
struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
|
|
@@ -1111,8 +1253,54 @@ vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
|
|
|
}
|
|
|
|
|
|
vps->content_fb_type = new_content_type;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This should only happen if the DMA buf is too large to create a
|
|
|
+ * proxy surface for.
|
|
|
+ * If we are a 2D VM with a DMA buffer then we have to use CPU blit
|
|
|
+ * so cache these mappings
|
|
|
+ */
|
|
|
+ if (vps->content_fb_type == SEPARATE_DMA &&
|
|
|
+ !(dev_priv->capabilities & SVGA_CAP_3D)) {
|
|
|
+
|
|
|
+ struct vmw_framebuffer_dmabuf *new_vfbd;
|
|
|
+
|
|
|
+ new_vfbd = vmw_framebuffer_to_vfbd(new_fb);
|
|
|
+
|
|
|
+ ret = ttm_bo_reserve(&new_vfbd->buffer->base, false, false,
|
|
|
+ NULL);
|
|
|
+ if (ret)
|
|
|
+ goto out_srf_unpin;
|
|
|
+
|
|
|
+ ret = ttm_bo_kmap(&new_vfbd->buffer->base, 0,
|
|
|
+ new_vfbd->buffer->base.num_pages,
|
|
|
+ &vps->guest_map);
|
|
|
+
|
|
|
+ ttm_bo_unreserve(&new_vfbd->buffer->base);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ DRM_ERROR("Failed to map content buffer to CPU\n");
|
|
|
+ goto out_srf_unpin;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ttm_bo_kmap(&vps->surf->res.backup->base, 0,
|
|
|
+ vps->surf->res.backup->base.num_pages,
|
|
|
+ &vps->host_map);
|
|
|
+ if (ret) {
|
|
|
+ DRM_ERROR("Failed to map display buffer to CPU\n");
|
|
|
+ ttm_bo_kunmap(&vps->guest_map);
|
|
|
+ goto out_srf_unpin;
|
|
|
+ }
|
|
|
+
|
|
|
+ vps->cpp = new_fb->pitches[0] / new_fb->width;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
+out_srf_unpin:
|
|
|
+ vmw_resource_unpin(&vps->surf->res);
|
|
|
+ vps->pinned--;
|
|
|
+
|
|
|
out_srf_unref:
|
|
|
vmw_surface_unreference(&vps->surf);
|
|
|
return ret;
|
|
@@ -1146,6 +1334,9 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
|
|
|
|
|
|
stdu->display_srf = vps->surf;
|
|
|
stdu->content_fb_type = vps->content_fb_type;
|
|
|
+ stdu->cpp = vps->cpp;
|
|
|
+ memcpy(&stdu->guest_map, &vps->guest_map, sizeof(vps->guest_map));
|
|
|
+ memcpy(&stdu->host_map, &vps->host_map, sizeof(vps->host_map));
|
|
|
|
|
|
if (!stdu->defined)
|
|
|
return;
|
|
@@ -1410,6 +1601,17 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv)
|
|
|
|
|
|
dev_priv->active_display_unit = vmw_du_screen_target;
|
|
|
|
|
|
+ if (!(dev_priv->capabilities & SVGA_CAP_3D)) {
|
|
|
+ /*
|
|
|
+ * Given various display aspect ratios, there's no way to
|
|
|
+ * estimate these using prim_bb_mem. So just set these to
|
|
|
+ * something arbitrarily large and we will reject any layout
|
|
|
+ * that doesn't fit prim_bb_mem later
|
|
|
+ */
|
|
|
+ dev->mode_config.max_width = 16384;
|
|
|
+ dev->mode_config.max_height = 16384;
|
|
|
+ }
|
|
|
+
|
|
|
vmw_kms_create_implicit_placement_property(dev_priv, false);
|
|
|
|
|
|
for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) {
|