Commit f89c6c32 authored by Sinclair Yeh's avatar Sinclair Yeh Committed by Thomas Hellstrom
Browse files

drm/vmwgfx: Replace SurfaceDMA usage with SurfaceCopy in 2D VMs



This patch address the following underlying issues with SurfaceDMA

* SurfaceDMA command does not work in a 2D VM, but we can wrap a
  proxy surface around the same DMA buffer and use the SurfaceCopy
  command which does work in a 2D VM.

* Wrapping a DMA buffer with a proxy surface also gives us an
  added optimization path for the case when the DMA buf
  dimensions match the mode.  In this case, the DMA buf can
  be pinned as the display surface, saving an extra copy.
  This only works in a 2D VM because we won't be doing any
  rendering operations directly to the display surface.

v2
* Moved is_dmabuf_proxy field to vmw_framebuffer_surface
* Undone coding style changes
* Addressed other issues from review

Signed-off-by: default avatarSinclair Yeh <syeh@vmware.com>
Reviewed-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
parent 35c05125
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -342,7 +342,8 @@ enum vmw_display_unit_type {
};


#define VMW_QUIRK_SCREENTARGET (1U << 0)
#define VMW_QUIRK_DST_SID_OK (1U << 0)
#define VMW_QUIRK_SRC_SID_OK (1U << 1)

struct vmw_sw_context{
	struct drm_open_hash res_ht;
+13 −7
Original line number Diff line number Diff line
@@ -674,13 +674,16 @@ static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv,
	int ret;

	cmd = container_of(header, struct vmw_sid_cmd, header);

	if (!(sw_context->quirks & VMW_QUIRK_SRC_SID_OK)) {
		ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
					user_surface_converter,
					&cmd->body.src.sid, NULL);
	if (unlikely(ret != 0))
		if (ret != 0)
			return ret;
	}

	if (sw_context->quirks & VMW_QUIRK_SCREENTARGET)
	if (sw_context->quirks & VMW_QUIRK_DST_SID_OK)
		return 0;

	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
@@ -1264,7 +1267,7 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
	if (unlikely(suffix->maximumOffset > bo_size))
		suffix->maximumOffset = bo_size;

	if (sw_context->quirks & VMW_QUIRK_SCREENTARGET)
	if (sw_context->quirks & VMW_QUIRK_DST_SID_OK)
		goto out_no_surface;

	ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
@@ -1505,6 +1508,9 @@ static int vmw_cmd_update_gb_image(struct vmw_private *dev_priv,

	cmd = container_of(header, struct vmw_gb_surface_cmd, header);

	if (sw_context->quirks & VMW_QUIRK_SRC_SID_OK)
		return 0;

	return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
				 user_surface_converter,
				 &cmd->body.image.sid, NULL);
+101 −6
Original line number Diff line number Diff line
@@ -487,7 +487,8 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_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;
@@ -562,6 +563,7 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
	vfbs->surface = surface;
	vfbs->base.user_handle = mode_cmd->handle;
	vfbs->master = drm_master_get(file_priv->master);
	vfbs->is_dmabuf_proxy = is_dmabuf_proxy;

	mutex_lock(&vmaster->fb_surf_mutex);
	list_add_tail(&vfbs->head, &vmaster->fb_surf);
@@ -699,6 +701,82 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
	return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, 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,
				   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;
	int ret;


	switch (mode_cmd->depth) {
	case 32:
	case 24:
		format = SVGA3D_X8R8G8B8;
		break;

	case 16:
	case 15:
		format = SVGA3D_R5G6B5;
		break;

	case 8:
		format = SVGA3D_P8;
		break;

	default:
		DRM_ERROR("Invalid framebuffer format %d\n", mode_cmd->depth);
		return -EINVAL;
	}

	content_base_size.width  = mode_cmd->width;
	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,
			content_base_size,
			srf_out);
	if (ret) {
		DRM_ERROR("Failed to allocate proxy content buffer\n");
		return ret;
	}

	/* Use the same MOB backing for surface */
	vmw_dmabuf_reference(dmabuf_mob);

	(*srf_out)->res.backup = dmabuf_mob;

	/* FIXME:  Waiting for fbdev rework to do a proper reserve/pin */
	ret = vmw_resource_validate(&(*srf_out)->res);

	return ret;
}



static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
					  struct vmw_dma_buffer *dmabuf,
					  struct vmw_framebuffer **out,
@@ -801,6 +879,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
	struct vmw_dma_buffer *bo = NULL;
	struct ttm_base_object *user_obj;
	struct drm_mode_fb_cmd mode_cmd;
	bool is_dmabuf_proxy = false;
	int ret;

	mode_cmd.width = mode_cmd2->width;
@@ -849,13 +928,29 @@ 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)
	/*
	 * 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 (bo && !(dev_priv->capabilities & SVGA_CAP_3D) &&
	    dev_priv->active_display_unit == vmw_du_screen_target) {
		ret = vmw_create_dmabuf_proxy(dev_priv->dev, &mode_cmd, bo,
			&surface);
		if (ret)
			goto err_out;

		is_dmabuf_proxy = true;
	}

	/* Create the new framebuffer depending one what we have */
	if (surface)
		ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv,
						      surface, &vfb, &mode_cmd,
						      is_dmabuf_proxy);
	else 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();

+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ struct vmw_framebuffer_surface {
	struct vmw_dma_buffer *buffer;
	struct list_head head;
	struct drm_master *master;
	bool is_dmabuf_proxy;  /* true if this is proxy surface for DMA buf */
};


+1 −2
Original line number Diff line number Diff line
@@ -31,8 +31,7 @@
 * If we set up the screen target otable, screen objects stop working.
 */

#define VMW_OTABLE_SETUP_SUB ((VMWGFX_ENABLE_SCREEN_TARGET_OTABLE &&	\
			       (dev_priv->capabilities & SVGA_CAP_3D)) ? 0 : 1)
#define VMW_OTABLE_SETUP_SUB ((VMWGFX_ENABLE_SCREEN_TARGET_OTABLE ? 0 : 1))

#ifdef CONFIG_64BIT
#define VMW_PPN_SIZE 8
Loading