[PATCH 10/11] drm/vmwgfx: Switch over to internal atomic API for STDU

Daniel Vetter daniel at ffwll.ch
Tue Mar 28 07:49:38 UTC 2017


On Mon, Mar 27, 2017 at 03:01:03PM -0700, Sinclair Yeh wrote:
> Switch over to using internal atomic API for mode set.
> 
> This removes the legacy set_config API, replacing it with
> drm_atomic_helper_set_config().  The DRM helper will use various
> vmwgfx-specific atomic functions to set a mode.
> 
> DRIVER_ATOMIC capability flag is not yet set, so the user mode
> will still use the legacy mode set IOCTL.
> 
> v2:
> * Avoid a clash between page-flip pinning and setcrtc pinning, modify
> the page-flip code to use the page-flip helper and the atomic callbacks.
> To enable this, we will need to add a wrapper around atomic_commit.
> 
> * Add vmw_kms_set_config() to work around vmwgfx xorg driver bug
> 
> Signed-off-by: Sinclair Yeh <syeh at vmware.com>
> Signed-off-by: Thomas Hellstrom <thellstrom at vmware.com>
> Reviewed-by: Thomas Hellstrom <thellstrom at vmware.com>
> ---
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  |  20 +++
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.h  |   1 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 325 ++++-------------------------------
>  3 files changed, 51 insertions(+), 295 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index 6b593aaf..7104796 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -2923,3 +2923,23 @@ vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv,
>  					  "implicit_placement", 0, 1);
>  
>  }
> +
> +
> +/**
> + * vmw_kms_set_config - Wrapper around drm_atomic_helper_set_config
> + *
> + * @set: The configuration to set.
> + *
> + * The vmwgfx Xorg driver doesn't assign the mode::type member, which
> + * when drm_mode_set_crtcinfo is called as part of the configuration setting
> + * causes it to return incorrect crtc dimensions causing severe problems in
> + * the vmwgfx modesetting. So explicitly clear that member before calling
> + * into drm_atomic_helper_set_config.
> + */
> +int vmw_kms_set_config(struct drm_mode_set *set)
> +{
> +	if (set && set->mode)
> +		set->mode->type = 0;

ugh :( Looking at set_crtcinfo the only thing I can see it look at ->type
is to check for built-in modes. Not a single driver is using that afaics,
we might as well remove this and and void the vmw special case here too.
-Daniel

> +
> +	return drm_atomic_helper_set_config(set);
> +}
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> index 3251562..0016f07 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> @@ -451,5 +451,6 @@ int vmw_kms_stdu_dma(struct vmw_private *dev_priv,
>  		     bool to_surface,
>  		     bool interruptible);
>  
> +int vmw_kms_set_config(struct drm_mode_set *set);
>  
>  #endif
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> index 708d063..ff00817 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> @@ -104,8 +104,7 @@ struct vmw_stdu_surface_copy {
>   */
>  struct vmw_screen_target_display_unit {
>  	struct vmw_display_unit base;
> -
> -	struct vmw_surface     *display_srf;
> +	const struct vmw_surface *display_srf;
>  	enum stdu_content_type content_fb_type;
>  
>  	bool defined;
> @@ -118,32 +117,6 @@ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu);
>  
>  
>  /******************************************************************************
> - * Screen Target Display Unit helper Functions
> - *****************************************************************************/
> -
> -/**
> - * vmw_stdu_unpin_display - unpins the resource associated with display surface
> - *
> - * @stdu: contains the display surface
> - *
> - * If the display surface was privatedly allocated by
> - * vmw_surface_gb_priv_define() and not registered as a framebuffer, then it
> - * won't be automatically cleaned up when all the framebuffers are freed.  As
> - * such, we have to explicitly call vmw_resource_unreference() to get it freed.
> - */
> -static void vmw_stdu_unpin_display(struct vmw_screen_target_display_unit *stdu)
> -{
> -	if (stdu->display_srf) {
> -		struct vmw_resource *res = &stdu->display_srf->res;
> -
> -		vmw_resource_unpin(res);
> -		vmw_surface_unreference(&stdu->display_srf);
> -	}
> -}
> -
> -
> -
> -/******************************************************************************
>   * Screen Target Display Unit CRTC Functions
>   *****************************************************************************/
>  
> @@ -228,7 +201,7 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv,
>   */
>  static int vmw_stdu_bind_st(struct vmw_private *dev_priv,
>  			    struct vmw_screen_target_display_unit *stdu,
> -			    struct vmw_resource *res)
> +			    const struct vmw_resource *res)
>  {
>  	SVGA3dSurfaceImageId image;
>  
> @@ -377,129 +350,6 @@ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv,
>  	return ret;
>  }
>  
> -/**
> - * vmw_stdu_bind_fb - Bind an fb to a defined screen target
> - *
> - * @dev_priv: Pointer to a device private struct.
> - * @crtc: The crtc holding the screen target.
> - * @mode: The mode currently used by the screen target. Must be non-NULL.
> - * @new_fb: The new framebuffer to bind. Must be non-NULL.
> - *
> - * RETURNS:
> - * 0 on success, error code on failure.
> - */
> -static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
> -			    struct drm_crtc *crtc,
> -			    struct drm_display_mode *mode,
> -			    struct drm_framebuffer *new_fb)
> -{
> -	struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc);
> -	struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);
> -	struct vmw_surface *new_display_srf = NULL;
> -	enum stdu_content_type new_content_type;
> -	struct vmw_framebuffer_surface *new_vfbs;
> -	int ret;
> -
> -	WARN_ON_ONCE(!stdu->defined);
> -
> -	new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
> -
> -	if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay &&
> -	    new_vfbs->surface->base_size.height == mode->vdisplay)
> -		new_content_type = SAME_AS_DISPLAY;
> -	else if (vfb->dmabuf)
> -		new_content_type = SEPARATE_DMA;
> -	else
> -		new_content_type = SEPARATE_SURFACE;
> -
> -	if (new_content_type != SAME_AS_DISPLAY &&
> -	    !stdu->display_srf) {
> -		struct vmw_surface content_srf;
> -		struct drm_vmw_size display_base_size = {0};
> -
> -		display_base_size.width  = mode->hdisplay;
> -		display_base_size.height = mode->vdisplay;
> -		display_base_size.depth  = 1;
> -
> -		/*
> -		 * If content buffer is a DMA buf, then we have to construct
> -		 * surface info
> -		 */
> -		if (new_content_type == SEPARATE_DMA) {
> -
> -			switch (new_fb->format->cpp[0] * 8) {
> -			case 32:
> -				content_srf.format = SVGA3D_X8R8G8B8;
> -				break;
> -
> -			case 16:
> -				content_srf.format = SVGA3D_R5G6B5;
> -				break;
> -
> -			case 8:
> -				content_srf.format = SVGA3D_P8;
> -				break;
> -
> -			default:
> -				DRM_ERROR("Invalid format\n");
> -				return -EINVAL;
> -			}
> -
> -			content_srf.flags             = 0;
> -			content_srf.mip_levels[0]     = 1;
> -			content_srf.multisample_count = 0;
> -		} else {
> -			content_srf = *new_vfbs->surface;
> -		}
> -
> -
> -		ret = vmw_surface_gb_priv_define(crtc->dev,
> -				0, /* because kernel visible only */
> -				content_srf.flags,
> -				content_srf.format,
> -				true, /* a scanout buffer */
> -				content_srf.mip_levels[0],
> -				content_srf.multisample_count,
> -				0,
> -				display_base_size,
> -				&new_display_srf);
> -		if (unlikely(ret != 0)) {
> -			DRM_ERROR("Could not allocate screen target surface.\n");
> -			return ret;
> -		}
> -	} else if (new_content_type == SAME_AS_DISPLAY) {
> -		new_display_srf = vmw_surface_reference(new_vfbs->surface);
> -	}
> -
> -	if (new_display_srf) {
> -		/* Pin new surface before flipping */
> -		ret = vmw_resource_pin(&new_display_srf->res, false);
> -		if (ret)
> -			goto out_srf_unref;
> -
> -		ret = vmw_stdu_bind_st(dev_priv, stdu, &new_display_srf->res);
> -		if (ret)
> -			goto out_srf_unpin;
> -
> -		/* Unpin and unreference old surface */
> -		vmw_stdu_unpin_display(stdu);
> -
> -		/* Transfer the reference */
> -		stdu->display_srf = new_display_srf;
> -		new_display_srf = NULL;
> -	}
> -
> -	crtc->primary->fb = new_fb;
> -	stdu->content_fb_type = new_content_type;
> -	return 0;
> -
> -out_srf_unpin:
> -	vmw_resource_unpin(&new_display_srf->res);
> -out_srf_unref:
> -	vmw_surface_unreference(&new_display_srf);
> -	return ret;
> -}
> -
>  
>  /**
>   * vmw_stdu_crtc_mode_set_nofb - Updates screen target size
> @@ -601,136 +451,6 @@ static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc)
>  }
>  
>  /**
> - * vmw_stdu_crtc_set_config - Sets a mode
> - *
> - * @set:  mode parameters
> - *
> - * This function is the device-specific portion of the DRM CRTC mode set.
> - * For the SVGA device, we do this by defining a Screen Target, binding a
> - * GB Surface to that target, and finally update the screen target.
> - *
> - * RETURNS:
> - * 0 on success, error code otherwise
> - */
> -static int vmw_stdu_crtc_set_config(struct drm_mode_set *set)
> -{
> -	struct vmw_private *dev_priv;
> -	struct vmw_framebuffer *vfb;
> -	struct vmw_screen_target_display_unit *stdu;
> -	struct drm_display_mode *mode;
> -	struct drm_framebuffer  *new_fb;
> -	struct drm_crtc      *crtc;
> -	struct drm_encoder   *encoder;
> -	struct drm_connector *connector;
> -	bool turning_off;
> -	int    ret;
> -
> -
> -	if (!set || !set->crtc)
> -		return -EINVAL;
> -
> -	crtc     = set->crtc;
> -	stdu     = vmw_crtc_to_stdu(crtc);
> -	mode     = set->mode;
> -	new_fb   = set->fb;
> -	dev_priv = vmw_priv(crtc->dev);
> -	turning_off = set->num_connectors == 0 || !mode || !new_fb;
> -	vfb = (new_fb) ? vmw_framebuffer_to_vfb(new_fb) : NULL;
> -
> -	if (set->num_connectors > 1) {
> -		DRM_ERROR("Too many connectors\n");
> -		return -EINVAL;
> -	}
> -
> -	if (set->num_connectors == 1 &&
> -	    set->connectors[0] != &stdu->base.connector) {
> -		DRM_ERROR("Connectors don't match %p %p\n",
> -			set->connectors[0], &stdu->base.connector);
> -		return -EINVAL;
> -	}
> -
> -	if (!turning_off && (set->x + mode->hdisplay > new_fb->width ||
> -			     set->y + mode->vdisplay > new_fb->height)) {
> -		DRM_ERROR("Set outside of framebuffer\n");
> -		return -EINVAL;
> -	}
> -
> -	/* Only one active implicit frame-buffer at a time. */
> -	mutex_lock(&dev_priv->global_kms_state_mutex);
> -	if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb &&
> -	    !(dev_priv->num_implicit == 1 && stdu->base.active_implicit)
> -	    && dev_priv->implicit_fb != vfb) {
> -		mutex_unlock(&dev_priv->global_kms_state_mutex);
> -		DRM_ERROR("Multiple implicit framebuffers not supported.\n");
> -		return -EINVAL;
> -	}
> -	mutex_unlock(&dev_priv->global_kms_state_mutex);
> -
> -	/* Since they always map one to one these are safe */
> -	connector = &stdu->base.connector;
> -	encoder   = &stdu->base.encoder;
> -
> -	if (stdu->defined) {
> -		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
> -		if (ret)
> -			return ret;
> -
> -		vmw_stdu_unpin_display(stdu);
> -		(void) vmw_stdu_update_st(dev_priv, stdu);
> -		vmw_kms_del_active(dev_priv, &stdu->base);
> -
> -		ret = vmw_stdu_destroy_st(dev_priv, stdu);
> -		if (ret)
> -			return ret;
> -
> -		crtc->primary->fb = NULL;
> -		crtc->enabled = false;
> -		encoder->crtc = NULL;
> -		connector->encoder = NULL;
> -		stdu->content_fb_type = SAME_AS_DISPLAY;
> -		crtc->x = set->x;
> -		crtc->y = set->y;
> -	}
> -
> -	if (turning_off)
> -		return 0;
> -
> -	/*
> -	 * Steps to displaying a surface, assume surface is already
> -	 * bound:
> -	 *   1.  define a screen target
> -	 *   2.  bind a fb to the screen target
> -	 *   3.  update that screen target (this is done later by
> -	 *       vmw_kms_stdu_do_surface_dirty_or_present)
> -	 */
> -	/*
> -	 * Note on error handling: We can't really restore the crtc to
> -	 * it's original state on error, but we at least update the
> -	 * current state to what's submitted to hardware to enable
> -	 * future recovery.
> -	 */
> -	vmw_svga_enable(dev_priv);
> -	ret = vmw_stdu_define_st(dev_priv, stdu, mode, set->x, set->y);
> -	if (ret)
> -		return ret;
> -
> -	crtc->x = set->x;
> -	crtc->y = set->y;
> -	crtc->mode = *mode;
> -
> -	ret = vmw_stdu_bind_fb(dev_priv, crtc, mode, new_fb);
> -	if (ret)
> -		return ret;
> -
> -	vmw_kms_add_active(dev_priv, &stdu->base, vfb);
> -	crtc->enabled = true;
> -	connector->encoder = encoder;
> -	encoder->crtc      = crtc;
> -
> -	return 0;
> -}
> -
> -/**
>   * vmw_stdu_crtc_page_flip - Binds a buffer to a screen target
>   *
>   * @crtc: CRTC to attach FB to
> @@ -756,9 +476,9 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc,
>  
>  {
>  	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
> -	struct vmw_screen_target_display_unit *stdu;
> -	struct drm_vmw_rect vclips;
> +	struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc);
>  	struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);
> +	struct drm_vmw_rect vclips;
>  	int ret;
>  
>  	dev_priv          = vmw_priv(crtc->dev);
> @@ -767,25 +487,42 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc,
>  	if (!stdu->defined || !vmw_kms_crtc_flippable(dev_priv, crtc))
>  		return -EINVAL;
>  
> -	ret = vmw_stdu_bind_fb(dev_priv, crtc, &crtc->mode, new_fb);
> -	if (ret)
> +	/*
> +	 * We're always async, but the helper doesn't know how to set async
> +	 * so lie to the helper. Also, the helper expects someone
> +	 * to pick the event up from the crtc state, and if nobody does,
> +	 * it will free it. Since we handle the event in this function,
> +	 * don't hand it to the helper.
> +	 */
> +	flags &= ~DRM_MODE_PAGE_FLIP_ASYNC;
> +	ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags);
> +	if (ret) {
> +		DRM_ERROR("Page flip error %d.\n", ret);
>  		return ret;
> +	}
>  
>  	if (stdu->base.is_implicit)
>  		vmw_kms_update_implicit_fb(dev_priv, crtc);
>  
> +	/*
> +	 * Now that we've bound a new surface to the screen target,
> +	 * update the contents.
> +	 */
>  	vclips.x = crtc->x;
>  	vclips.y = crtc->y;
>  	vclips.w = crtc->mode.hdisplay;
>  	vclips.h = crtc->mode.vdisplay;
> +
>  	if (vfb->dmabuf)
>  		ret = vmw_kms_stdu_dma(dev_priv, NULL, vfb, NULL, NULL, &vclips,
>  				       1, 1, true, false);
>  	else
>  		ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, &vclips,
>  						 NULL, 0, 0, 1, 1, NULL);
> -	if (ret)
> +	if (ret) {
> +		DRM_ERROR("Page flip update error %d.\n", ret);
>  		return ret;
> +	}
>  
>  	if (event) {
>  		struct vmw_fence_obj *fence = NULL;
> @@ -802,7 +539,7 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc,
>  						   true);
>  		vmw_fence_obj_unreference(&fence);
>  	} else {
> -		vmw_fifo_flush(dev_priv, false);
> +		(void) vmw_fifo_flush(dev_priv, false);
>  	}
>  
>  	return 0;
> @@ -1123,7 +860,7 @@ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
>  	.reset = vmw_du_crtc_reset,
>  	.atomic_duplicate_state = vmw_du_crtc_duplicate_state,
>  	.atomic_destroy_state = vmw_du_crtc_destroy_state,
> -	.set_config = vmw_stdu_crtc_set_config,
> +	.set_config = vmw_kms_set_config,
>  	.page_flip = vmw_stdu_crtc_page_flip,
>  };
>  
> @@ -1425,8 +1162,8 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
>  
>  
>  static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
> -	.update_plane = drm_primary_helper_update,
> -	.disable_plane = drm_primary_helper_disable,
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
>  	.destroy = vmw_du_primary_plane_destroy,
>  	.reset = vmw_du_plane_reset,
>  	.atomic_duplicate_state = vmw_du_plane_duplicate_state,
> @@ -1434,8 +1171,8 @@ static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
>  };
>  
>  static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {
> -	.update_plane = vmw_du_cursor_plane_update,
> -	.disable_plane = vmw_du_cursor_plane_disable,
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
>  	.destroy = vmw_du_cursor_plane_destroy,
>  	.reset = vmw_du_plane_reset,
>  	.atomic_duplicate_state = vmw_du_plane_duplicate_state,
> @@ -1625,8 +1362,6 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
>   */
>  static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu)
>  {
> -	vmw_stdu_unpin_display(stdu);
> -
>  	vmw_du_cleanup(&stdu->base);
>  	kfree(stdu);
>  }
> -- 
> 2.7.4
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list