diff mbox

[06/11] drm/vmwgfx: Add and connect plane helper functions

Message ID 1490652064-44817-7-git-send-email-syeh@vmware.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sinclair Yeh March 27, 2017, 10 p.m. UTC
Refactor previous FB and cursor plane update code into their
atomic counterparts: check, update, prepare, cleanup, and disable.

These helpers won't be called until we flip on the atomic support
flag or set drm_crtc_funcs->set_config to using the atomic
helper.

v2:
* Removed unnecessary pinning of cursor surface
* Added a few function headers

v3:
* Set clip region equal to the destination region
* Fixed surface pinning policy
* Enable SVGA mode in vmw_sou_primary_plane_prepare_fb

Signed-off-by: Sinclair Yeh <syeh@vmware.com>
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  | 256 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h  |  15 ++
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c  |  66 ++++++++-
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 115 ++++++++++++++++
 drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 244 +++++++++++++++++++++++++++++++++
 5 files changed, 695 insertions(+), 1 deletion(-)

Comments

Daniel Vetter March 28, 2017, 7:40 a.m. UTC | #1
On Mon, Mar 27, 2017 at 03:00:59PM -0700, Sinclair Yeh wrote:
> Refactor previous FB and cursor plane update code into their
> atomic counterparts: check, update, prepare, cleanup, and disable.
> 
> These helpers won't be called until we flip on the atomic support
> flag or set drm_crtc_funcs->set_config to using the atomic
> helper.
> 
> v2:
> * Removed unnecessary pinning of cursor surface
> * Added a few function headers
> 
> v3:
> * Set clip region equal to the destination region
> * Fixed surface pinning policy
> * Enable SVGA mode in vmw_sou_primary_plane_prepare_fb
> 
> Signed-off-by: Sinclair Yeh <syeh@vmware.com>
> Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
> Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
> ---
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  | 256 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.h  |  15 ++
>  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c  |  66 ++++++++-
>  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 115 ++++++++++++++++
>  drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 244 +++++++++++++++++++++++++++++++++
>  5 files changed, 695 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index 6f0f160..d00ff21 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -26,8 +26,10 @@
>   **************************************************************************/
>  
>  #include "vmwgfx_kms.h"
> +#include <drm/drm_plane_helper.h>
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_rect.h>
>  
>  
>  /* Might need a hrtimer here? */
> @@ -399,6 +401,260 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane)
>  }
>  
>  
> +/**
> + * vmw_du_vps_unpin_surf - unpins resource associated with a framebuffer surface
> + *
> + * @vps: plane state associated with the display surface
> + * @unreference: true if we also want to unreference the display.
> + */
> +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
> +			     bool unreference)
> +{
> +	if (vps->surf) {
> +		if (vps->pinned) {
> +			vmw_resource_unpin(&vps->surf->res);
> +			vps->pinned--;
> +		}
> +
> +		if (unreference) {
> +			if (vps->pinned)
> +				DRM_ERROR("Surface still pinned\n");
> +			vmw_surface_unreference(&vps->surf);
> +		}
> +	}
> +}
> +
> +
> +/**
> + * vmw_du_plane_cleanup_fb - Unpins the cursor
> + *
> + * @plane:  display plane
> + * @old_state: Contains the FB to clean up
> + *
> + * Unpins the framebuffer surface
> + *
> + * Returns 0 on success
> + */
> +void
> +vmw_du_plane_cleanup_fb(struct drm_plane *plane,
> +			struct drm_plane_state *old_state)
> +{
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
> +
> +	vmw_du_plane_unpin_surf(vps, false);
> +}
> +
> +
> +/**
> + * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it
> + *
> + * @plane:  display plane
> + * @new_state: info on the new plane state, including the FB
> + *
> + * Returns 0 on success
> + */
> +int
> +vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
> +			       struct drm_plane_state *new_state)
> +{
> +	struct drm_framebuffer *fb = new_state->fb;
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
> +
> +
> +	if (vps->surf)
> +		vmw_surface_unreference(&vps->surf);
> +
> +	if (vps->dmabuf)
> +		vmw_dmabuf_unreference(&vps->dmabuf);
> +
> +	if (fb) {
> +		if (vmw_framebuffer_to_vfb(fb)->dmabuf) {
> +			vps->dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer;
> +			vmw_dmabuf_reference(vps->dmabuf);
> +		} else {
> +			vps->surf = vmw_framebuffer_to_vfbs(fb)->surface;
> +			vmw_surface_reference(vps->surf);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +
> +void
> +vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
> +				   struct drm_plane_state *old_state)
> +{
> +	struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
> +	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
> +
> +	drm_atomic_set_fb_for_plane(plane->state, NULL);
> +	vmw_cursor_update_position(dev_priv, false, 0, 0);
> +}
> +
> +
> +void
> +vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
> +				  struct drm_plane_state *old_state)
> +{
> +	struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
> +	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
> +	struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state);
> +	s32 hotspot_x, hotspot_y;
> +	int ret = 0;
> +
> +
> +	hotspot_x = du->hotspot_x;
> +	hotspot_y = du->hotspot_y;
> +	du->cursor_surface = vps->surf;
> +	du->cursor_dmabuf = vps->dmabuf;
> +
> +	/* setup new image */
> +	if (vps->surf) {
> +		du->cursor_age = du->cursor_surface->snooper.age;
> +
> +		ret = vmw_cursor_update_image(dev_priv,
> +					      vps->surf->snooper.image,
> +					      64, 64, hotspot_x, hotspot_y);
> +	} else if (vps->dmabuf) {
> +		ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf,
> +					       plane->state->crtc_w,
> +					       plane->state->crtc_h,
> +					       hotspot_x, hotspot_y);
> +	} else {
> +		vmw_cursor_update_position(dev_priv, false, 0, 0);
> +		return;
> +	}
> +
> +	if (!ret) {
> +		du->cursor_x = plane->state->crtc_x + du->set_gui_x;
> +		du->cursor_y = plane->state->crtc_y + du->set_gui_y;
> +
> +		vmw_cursor_update_position(dev_priv, true,
> +					   du->cursor_x + hotspot_x,
> +					   du->cursor_y + hotspot_y);
> +	} else {
> +		DRM_ERROR("Failed to update cursor image\n");
> +	}
> +}
> +
> +
> +/**
> + * vmw_du_primary_plane_atomic_check - check if the new state is okay
> + *
> + * @plane: display plane
> + * @state: info on the new plane state, including the FB
> + *
> + * Check if the new state is settable given the current state.  Other
> + * than what the atomic helper checks, we care about crtc fitting
> + * the FB and maintaining one active framebuffer.
> + *
> + * Returns 0 on success
> + */
> +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
> +				      struct drm_plane_state *state)
> +{
> +	struct drm_framebuffer *new_fb = state->fb;
> +	bool visible;
> +
> +	struct drm_rect src = {
> +		.x1 = state->src_x,
> +		.y1 = state->src_y,
> +		.x2 = state->src_x + state->src_w,
> +		.y2 = state->src_y + state->src_h,
> +	};
> +	struct drm_rect dest = {
> +		.x1 = state->crtc_x,
> +		.y1 = state->crtc_y,
> +		.x2 = state->crtc_x + state->crtc_w,
> +		.y2 = state->crtc_y + state->crtc_h,
> +	};
> +	struct drm_rect clip = dest;
> +	int ret;
> +
> +	ret = drm_plane_helper_check_update(plane, state->crtc, new_fb,
> +					    &src, &dest, &clip,
> +					    DRM_ROTATE_0,
> +					    DRM_PLANE_HELPER_NO_SCALING,
> +					    DRM_PLANE_HELPER_NO_SCALING,
> +					    false, true, &visible);

There's a new atomic version of this, drm_plane_helper_check_state(), for
less boilerplate.

> +
> +
> +	if (!ret && new_fb) {
> +		struct drm_crtc *crtc = state->crtc;
> +		struct vmw_connector_state *vcs;
> +		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
> +		struct vmw_private *dev_priv = vmw_priv(crtc->dev);
> +		struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);
> +
> +		vcs = vmw_connector_state_to_vcs(du->connector.state);
> +
> +		if ((dest.x2 > new_fb->width ||
> +		     dest.y2 > new_fb->height)) {
> +			DRM_ERROR("CRTC area outside of framebuffer\n");
> +			return -EINVAL;
> +		}
> +
> +		/* Only one active implicit framebuffer at a time. */
> +		mutex_lock(&dev_priv->global_kms_state_mutex);

It hasn't (yet) merged, but my recommendation for global kms state is to
mirror the main atomic framework, i.e.
- state struct, with duplicate/commit semantics
- separate ww_mutex
We should also be merging a new helper for driver private state objects
rsn, to make this pattern a lot easier/direct to implement.

You can make this work too, but since it's a different way of atomic
commit it means more work trying to understand it.
-Daniel

> +		if (vcs->is_implicit && dev_priv->implicit_fb &&
> +		    !(dev_priv->num_implicit == 1 && du->active_implicit)
> +		    && dev_priv->implicit_fb != vfb) {
> +			DRM_ERROR("Multiple implicit framebuffers "
> +				  "not supported.\n");
> +			ret = -EINVAL;
> +		}
> +		mutex_unlock(&dev_priv->global_kms_state_mutex);
> +	}
> +
> +
> +	return ret;
> +}
> +
> +
> +/**
> + * vmw_du_cursor_plane_atomic_check - check if the new state is okay
> + *
> + * @plane: cursor plane
> + * @state: info on the new plane state
> + *
> + * This is a chance to fail if the new cursor state does not fit
> + * our requirements.
> + *
> + * Returns 0 on success
> + */
> +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
> +				     struct drm_plane_state *new_state)
> +{
> +	int ret = 0;
> +	struct vmw_surface *surface = NULL;
> +	struct drm_framebuffer *fb = new_state->fb;
> +
> +
> +	/* Turning off */
> +	if (!fb)
> +		return ret;
> +
> +	/* A lot of the code assumes this */
> +	if (new_state->crtc_w != 64 || new_state->crtc_h != 64) {
> +		DRM_ERROR("Invalid cursor dimensions (%d, %d)\n",
> +			  new_state->crtc_w, new_state->crtc_h);
> +		ret = -EINVAL;
> +	}
> +
> +	if (!vmw_framebuffer_to_vfb(fb)->dmabuf)
> +		surface = vmw_framebuffer_to_vfbs(fb)->surface;
> +
> +	if (surface && !surface->snooper.image) {
> +		DRM_ERROR("surface not suitable for cursor\n");
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +
>  int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
>  			     struct drm_crtc_state *new_state)
>  {
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> index f711b5d..de6a0b6 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> @@ -345,10 +345,25 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane,
>  			       uint32_t src_w, uint32_t src_h);
>  
>  /* Atomic Helpers */
> +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
> +				      struct drm_plane_state *state);
> +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
> +				     struct drm_plane_state *state);
> +void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
> +				       struct drm_plane_state *old_state);
> +void vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
> +					struct drm_plane_state *old_state);
> +int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
> +				   struct drm_plane_state *new_state);
> +void vmw_du_plane_cleanup_fb(struct drm_plane *plane,
> +			     struct drm_plane_state *old_state);
>  void vmw_du_plane_reset(struct drm_plane *plane);
>  struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane);
>  void vmw_du_plane_destroy_state(struct drm_plane *plane,
>  				struct drm_plane_state *state);
> +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
> +			     bool unreference);
> +
>  int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
>  			     struct drm_crtc_state *state);
>  void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> index d547e80..1d734de 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> @@ -234,7 +234,7 @@ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
>  
>  	ldu = vmw_crtc_to_ldu(crtc);
>  	dev_priv = vmw_priv(crtc->dev);
> -	fb       = crtc->primary->fb;
> +	fb       = crtc->primary->state->fb;
>  
>  	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
>  
> @@ -242,6 +242,8 @@ static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
>  		vmw_ldu_add_active(dev_priv, ldu, vfb);
>  	else
>  		vmw_ldu_del_active(dev_priv, ldu);
> +
> +	vmw_ldu_commit_list(dev_priv);
>  }
>  
>  /**
> @@ -391,6 +393,46 @@ static const struct drm_connector_funcs vmw_legacy_connector_funcs = {
>   * Legacy Display Plane Functions
>   */
>  
> +/**
> + * vmw_ldu_primary_plane_cleanup_fb - Unpin fb
> + *
> + * @plane:  display plane
> + * @old_state: Contains the FB to clean up
> + *
> + * Unpins the display surface
> + *
> + * Returns 0 on success
> + */
> +static void
> +vmw_ldu_primary_plane_cleanup_fb(struct drm_plane *plane,
> +				 struct drm_plane_state *old_state)
> +{
> +}
> +
> +
> +/**
> + * vmw_ldu_primary_plane_prepare_fb -
> + *
> + * @plane:  display plane
> + * @new_state: info on the new plane state, including the FB
> + *
> + * Returns 0 on success
> + */
> +static int
> +vmw_ldu_primary_plane_prepare_fb(struct drm_plane *plane,
> +				 struct drm_plane_state *new_state)
> +{
> +	return 0;
> +}
> +
> +
> +static void
> +vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +}
> +
> +
>  static const struct drm_plane_funcs vmw_ldu_plane_funcs = {
>  	.update_plane = drm_primary_helper_update,
>  	.disable_plane = drm_primary_helper_disable,
> @@ -412,6 +454,22 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = {
>  /*
>   * Atomic Helpers
>   */
> +static const struct
> +drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = {
> +	.atomic_check = vmw_du_cursor_plane_atomic_check,
> +	.atomic_update = vmw_du_cursor_plane_atomic_update,
> +	.prepare_fb = vmw_du_cursor_plane_prepare_fb,
> +	.cleanup_fb = vmw_du_plane_cleanup_fb,
> +};
> +
> +static const struct
> +drm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = {
> +	.atomic_check = vmw_du_primary_plane_atomic_check,
> +	.atomic_update = vmw_ldu_primary_plane_atomic_update,
> +	.prepare_fb = vmw_ldu_primary_plane_prepare_fb,
> +	.cleanup_fb = vmw_ldu_primary_plane_cleanup_fb,
> +};
> +
>  static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = {
>  	.prepare = vmw_ldu_crtc_helper_prepare,
>  	.commit = vmw_ldu_crtc_helper_commit,
> @@ -471,6 +529,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free;
>  	}
>  
> +	drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs);
> +
>  	/* Initialize cursor plane */
>  	vmw_du_plane_reset(cursor);
>  
> @@ -485,6 +545,10 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free;
>  	}
>  
> +	drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs);
> +
> +
> +	vmw_du_connector_reset(connector);
>  	ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
>  				 DRM_MODE_CONNECTOR_VIRTUAL);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> index 662024c..eca055e 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> @@ -612,6 +612,100 @@ static const struct drm_connector_funcs vmw_sou_connector_funcs = {
>   * Screen Object Display Plane Functions
>   */
>  
> +/**
> + * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer
> + *
> + * @plane:  display plane
> + * @old_state: Contains the FB to clean up
> + *
> + * Unpins the display surface
> + *
> + * Returns 0 on success
> + */
> +static void
> +vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane,
> +				 struct drm_plane_state *old_state)
> +{
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
> +
> +	vmw_dmabuf_unreference(&vps->dmabuf);
> +	vps->dmabuf_size = 0;
> +
> +	vmw_du_plane_cleanup_fb(plane, old_state);
> +}
> +
> +
> +/**
> + * vmw_sou_primary_plane_prepare_fb - allocate backing buffer
> + *
> + * @plane:  display plane
> + * @new_state: info on the new plane state, including the FB
> + *
> + * The SOU backing buffer is our equivalent of the display plane.
> + *
> + * Returns 0 on success
> + */
> +static int
> +vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
> +				 struct drm_plane_state *new_state)
> +{
> +	struct drm_framebuffer *new_fb = new_state->fb;
> +	struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc;
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
> +	struct vmw_private *dev_priv;
> +	size_t size;
> +	int ret;
> +
> +
> +	if (!new_fb) {
> +		vmw_dmabuf_unreference(&vps->dmabuf);
> +		vps->dmabuf_size = 0;
> +
> +		return 0;
> +	}
> +
> +	size = new_state->crtc_w * new_state->crtc_h * 4;
> +
> +	if (vps->dmabuf) {
> +		if (vps->dmabuf_size == size)
> +			return 0;
> +
> +		vmw_dmabuf_unreference(&vps->dmabuf);
> +		vps->dmabuf_size = 0;
> +	}
> +
> +	vps->dmabuf = kzalloc(sizeof(*vps->dmabuf), GFP_KERNEL);
> +	if (!vps->dmabuf)
> +		return -ENOMEM;
> +
> +	dev_priv = vmw_priv(crtc->dev);
> +	vmw_svga_enable(dev_priv);
> +
> +	/* After we have alloced the backing store might not be able to
> +	 * resume the overlays, this is preferred to failing to alloc.
> +	 */
> +	vmw_overlay_pause_all(dev_priv);
> +	ret = vmw_dmabuf_init(dev_priv, vps->dmabuf, size,
> +			      &vmw_vram_ne_placement,
> +			      false, &vmw_dmabuf_bo_free);
> +	vmw_overlay_resume_all(dev_priv);
> +
> +	if (ret != 0)
> +		vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */
> +	else
> +		vps->dmabuf_size = size;
> +
> +	return ret;
> +}
> +
> +
> +static void
> +vmw_sou_primary_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +}
> +
> +
>  static const struct drm_plane_funcs vmw_sou_plane_funcs = {
>  	.update_plane = drm_primary_helper_update,
>  	.disable_plane = drm_primary_helper_disable,
> @@ -633,6 +727,22 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = {
>  /*
>   * Atomic Helpers
>   */
> +static const struct
> +drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = {
> +	.atomic_check = vmw_du_cursor_plane_atomic_check,
> +	.atomic_update = vmw_du_cursor_plane_atomic_update,
> +	.prepare_fb = vmw_du_cursor_plane_prepare_fb,
> +	.cleanup_fb = vmw_du_plane_cleanup_fb,
> +};
> +
> +static const struct
> +drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = {
> +	.atomic_check = vmw_du_primary_plane_atomic_check,
> +	.atomic_update = vmw_sou_primary_plane_atomic_update,
> +	.prepare_fb = vmw_sou_primary_plane_prepare_fb,
> +	.cleanup_fb = vmw_sou_primary_plane_cleanup_fb,
> +};
> +
>  static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {
>  	.prepare = vmw_sou_crtc_helper_prepare,
>  	.commit = vmw_sou_crtc_helper_commit,
> @@ -691,6 +801,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free;
>  	}
>  
> +	drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs);
> +
>  	/* Initialize cursor plane */
>  	vmw_du_plane_reset(cursor);
>  
> @@ -705,6 +817,9 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free;
>  	}
>  
> +	drm_plane_helper_add(cursor, &vmw_sou_cursor_plane_helper_funcs);
> +
> +	vmw_du_connector_reset(connector);
>  	ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs,
>  				 DRM_MODE_CONNECTOR_VIRTUAL);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> index 6e3cfad..cce5e5b 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> @@ -1194,6 +1194,230 @@ static const struct drm_connector_funcs vmw_stdu_connector_funcs = {
>   * Screen Target Display Plane Functions
>   *****************************************************************************/
>  
> +
> +
> +/**
> + * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface
> + *
> + * @plane:  display plane
> + * @old_state: Contains the FB to clean up
> + *
> + * Unpins the display surface
> + *
> + * Returns 0 on success
> + */
> +static void
> +vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,
> +				  struct drm_plane_state *old_state)
> +{
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
> +
> +	if (vps->surf)
> +		WARN_ON(!vps->pinned);
> +
> +	vmw_du_plane_cleanup_fb(plane, old_state);
> +
> +	vps->content_fb_type = SAME_AS_DISPLAY;
> +}
> +
> +
> +
> +/**
> + * vmw_stdu_primary_plane_prepare_fb - Readies the display surface
> + *
> + * @plane:  display plane
> + * @new_state: info on the new plane state, including the FB
> + *
> + * This function allocates a new display surface if the content is
> + * backed by a DMA.  The display surface is pinned here, and it'll
> + * be unpinned in .cleanup_fb()
> + *
> + * Returns 0 on success
> + */
> +static int
> +vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
> +				  struct drm_plane_state *new_state)
> +{
> +	struct drm_framebuffer *new_fb = new_state->fb;
> +	struct vmw_framebuffer *vfb;
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
> +	enum stdu_content_type new_content_type;
> +	struct vmw_framebuffer_surface *new_vfbs;
> +	struct drm_crtc *crtc = new_state->crtc;
> +	uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h;
> +	int ret;
> +
> +	/* No FB to prepare */
> +	if (!new_fb) {
> +		if (vps->surf) {
> +			WARN_ON(vps->pinned != 0);
> +			vmw_surface_unreference(&vps->surf);
> +		}
> +
> +		return 0;
> +	}
> +
> +	vfb = vmw_framebuffer_to_vfb(new_fb);
> +	new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
> +
> +	if (new_vfbs && new_vfbs->surface->base_size.width == hdisplay &&
> +	    new_vfbs->surface->base_size.height == 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) {
> +		struct vmw_surface content_srf;
> +		struct drm_vmw_size display_base_size = {0};
> +
> +		display_base_size.width  = hdisplay;
> +		display_base_size.height = 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;
> +		}
> +
> +		if (vps->surf) {
> +			struct drm_vmw_size cur_base_size = vps->surf->base_size;
> +
> +			if (cur_base_size.width != display_base_size.width ||
> +			    cur_base_size.height != display_base_size.height ||
> +			    vps->surf->format != content_srf.format) {
> +				WARN_ON(vps->pinned != 0);
> +				vmw_surface_unreference(&vps->surf);
> +			}
> +
> +		}
> +
> +		if (!vps->surf) {
> +			ret = vmw_surface_gb_priv_define
> +				(crtc->dev,
> +				 /* Kernel visible only */
> +				 0,
> +				 content_srf.flags,
> +				 content_srf.format,
> +				 true,  /* a scanout buffer */
> +				 content_srf.mip_levels[0],
> +				 content_srf.multisample_count,
> +				 0,
> +				 display_base_size,
> +				 &vps->surf);
> +			if (ret != 0) {
> +				DRM_ERROR("Couldn't allocate STDU surface.\n");
> +				return ret;
> +			}
> +		}
> +	} else {
> +		/*
> +		 * prepare_fb and clean_fb should only take care of pinning
> +		 * and unpinning.  References are tracked by state objects.
> +		 * The only time we add a reference in prepare_fb is if the
> +		 * state object doesn't have a reference to begin with
> +		 */
> +		if (vps->surf) {
> +			WARN_ON(vps->pinned != 0);
> +			vmw_surface_unreference(&vps->surf);
> +		}
> +
> +		vps->surf = vmw_surface_reference(new_vfbs->surface);
> +	}
> +
> +	if (vps->surf) {
> +
> +		/* Pin new surface before flipping */
> +		ret = vmw_resource_pin(&vps->surf->res, false);
> +		if (ret)
> +			goto out_srf_unref;
> +
> +		vps->pinned++;
> +	}
> +
> +	vps->content_fb_type = new_content_type;
> +	return 0;
> +
> +out_srf_unref:
> +	vmw_surface_unreference(&vps->surf);
> +	return ret;
> +}
> +
> +
> +
> +/**
> + * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane
> + *
> + * @plane: display plane
> + * @old_state: Only used to get crtc info
> + *
> + * Formally update stdu->display_srf to the new plane, and bind the new
> + * plane STDU.  This function is called during the commit phase when
> + * all the preparation have been done and all the configurations have
> + * been checked.
> + */
> +static void
> +vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
> +				     struct drm_plane_state *old_state)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_screen_target_display_unit *stdu;
> +	struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state);
> +	struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
> +	int ret;
> +
> +	stdu     = vmw_crtc_to_stdu(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +
> +	stdu->display_srf = vps->surf;
> +	stdu->content_fb_type = vps->content_fb_type;
> +
> +	if (!stdu->defined)
> +		return;
> +
> +	if (plane->state->fb)
> +		ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res);
> +	else
> +		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
> +
> +	/*
> +	 * We cannot really fail this function, so if we do, then output an
> +	 * error and quit
> +	 */
> +	if (ret)
> +		DRM_ERROR("Failed to bind surface to STDU.\n");
> +	else
> +		crtc->primary->fb = plane->state->fb;
> +}
> +
> +
>  static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
>  	.update_plane = drm_primary_helper_update,
>  	.disable_plane = drm_primary_helper_disable,
> @@ -1216,6 +1440,22 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {
>  /*
>   * Atomic Helpers
>   */
> +static const struct
> +drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = {
> +	.atomic_check = vmw_du_cursor_plane_atomic_check,
> +	.atomic_update = vmw_du_cursor_plane_atomic_update,
> +	.prepare_fb = vmw_du_cursor_plane_prepare_fb,
> +	.cleanup_fb = vmw_du_plane_cleanup_fb,
> +};
> +
> +static const struct
> +drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = {
> +	.atomic_check = vmw_du_primary_plane_atomic_check,
> +	.atomic_update = vmw_stdu_primary_plane_atomic_update,
> +	.prepare_fb = vmw_stdu_primary_plane_prepare_fb,
> +	.cleanup_fb = vmw_stdu_primary_plane_cleanup_fb,
> +};
> +
>  static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
>  	.prepare = vmw_stdu_crtc_helper_prepare,
>  	.commit = vmw_stdu_crtc_helper_commit,
> @@ -1283,6 +1523,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free;
>  	}
>  
> +	drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs);
> +
>  	/* Initialize cursor plane */
>  	vmw_du_plane_reset(cursor);
>  
> @@ -1297,6 +1539,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free;
>  	}
>  
> +	drm_plane_helper_add(cursor, &vmw_stdu_cursor_plane_helper_funcs);
> +
>  	vmw_du_connector_reset(connector);
>  
>  	ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs,
> -- 
> 2.7.4
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 6f0f160..d00ff21 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -26,8 +26,10 @@ 
  **************************************************************************/
 
 #include "vmwgfx_kms.h"
+#include <drm/drm_plane_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_rect.h>
 
 
 /* Might need a hrtimer here? */
@@ -399,6 +401,260 @@  void vmw_du_primary_plane_destroy(struct drm_plane *plane)
 }
 
 
+/**
+ * vmw_du_vps_unpin_surf - unpins resource associated with a framebuffer surface
+ *
+ * @vps: plane state associated with the display surface
+ * @unreference: true if we also want to unreference the display.
+ */
+void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
+			     bool unreference)
+{
+	if (vps->surf) {
+		if (vps->pinned) {
+			vmw_resource_unpin(&vps->surf->res);
+			vps->pinned--;
+		}
+
+		if (unreference) {
+			if (vps->pinned)
+				DRM_ERROR("Surface still pinned\n");
+			vmw_surface_unreference(&vps->surf);
+		}
+	}
+}
+
+
+/**
+ * vmw_du_plane_cleanup_fb - Unpins the cursor
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the framebuffer surface
+ *
+ * Returns 0 on success
+ */
+void
+vmw_du_plane_cleanup_fb(struct drm_plane *plane,
+			struct drm_plane_state *old_state)
+{
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
+
+	vmw_du_plane_unpin_surf(vps, false);
+}
+
+
+/**
+ * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * Returns 0 on success
+ */
+int
+vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
+			       struct drm_plane_state *new_state)
+{
+	struct drm_framebuffer *fb = new_state->fb;
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+
+
+	if (vps->surf)
+		vmw_surface_unreference(&vps->surf);
+
+	if (vps->dmabuf)
+		vmw_dmabuf_unreference(&vps->dmabuf);
+
+	if (fb) {
+		if (vmw_framebuffer_to_vfb(fb)->dmabuf) {
+			vps->dmabuf = vmw_framebuffer_to_vfbd(fb)->buffer;
+			vmw_dmabuf_reference(vps->dmabuf);
+		} else {
+			vps->surf = vmw_framebuffer_to_vfbs(fb)->surface;
+			vmw_surface_reference(vps->surf);
+		}
+	}
+
+	return 0;
+}
+
+
+void
+vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
+				   struct drm_plane_state *old_state)
+{
+	struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
+	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+
+	drm_atomic_set_fb_for_plane(plane->state, NULL);
+	vmw_cursor_update_position(dev_priv, false, 0, 0);
+}
+
+
+void
+vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
+				  struct drm_plane_state *old_state)
+{
+	struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
+	struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+	struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state);
+	s32 hotspot_x, hotspot_y;
+	int ret = 0;
+
+
+	hotspot_x = du->hotspot_x;
+	hotspot_y = du->hotspot_y;
+	du->cursor_surface = vps->surf;
+	du->cursor_dmabuf = vps->dmabuf;
+
+	/* setup new image */
+	if (vps->surf) {
+		du->cursor_age = du->cursor_surface->snooper.age;
+
+		ret = vmw_cursor_update_image(dev_priv,
+					      vps->surf->snooper.image,
+					      64, 64, hotspot_x, hotspot_y);
+	} else if (vps->dmabuf) {
+		ret = vmw_cursor_update_dmabuf(dev_priv, vps->dmabuf,
+					       plane->state->crtc_w,
+					       plane->state->crtc_h,
+					       hotspot_x, hotspot_y);
+	} else {
+		vmw_cursor_update_position(dev_priv, false, 0, 0);
+		return;
+	}
+
+	if (!ret) {
+		du->cursor_x = plane->state->crtc_x + du->set_gui_x;
+		du->cursor_y = plane->state->crtc_y + du->set_gui_y;
+
+		vmw_cursor_update_position(dev_priv, true,
+					   du->cursor_x + hotspot_x,
+					   du->cursor_y + hotspot_y);
+	} else {
+		DRM_ERROR("Failed to update cursor image\n");
+	}
+}
+
+
+/**
+ * vmw_du_primary_plane_atomic_check - check if the new state is okay
+ *
+ * @plane: display plane
+ * @state: info on the new plane state, including the FB
+ *
+ * Check if the new state is settable given the current state.  Other
+ * than what the atomic helper checks, we care about crtc fitting
+ * the FB and maintaining one active framebuffer.
+ *
+ * Returns 0 on success
+ */
+int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
+				      struct drm_plane_state *state)
+{
+	struct drm_framebuffer *new_fb = state->fb;
+	bool visible;
+
+	struct drm_rect src = {
+		.x1 = state->src_x,
+		.y1 = state->src_y,
+		.x2 = state->src_x + state->src_w,
+		.y2 = state->src_y + state->src_h,
+	};
+	struct drm_rect dest = {
+		.x1 = state->crtc_x,
+		.y1 = state->crtc_y,
+		.x2 = state->crtc_x + state->crtc_w,
+		.y2 = state->crtc_y + state->crtc_h,
+	};
+	struct drm_rect clip = dest;
+	int ret;
+
+	ret = drm_plane_helper_check_update(plane, state->crtc, new_fb,
+					    &src, &dest, &clip,
+					    DRM_ROTATE_0,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    DRM_PLANE_HELPER_NO_SCALING,
+					    false, true, &visible);
+
+
+	if (!ret && new_fb) {
+		struct drm_crtc *crtc = state->crtc;
+		struct vmw_connector_state *vcs;
+		struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+		struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+		struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb);
+
+		vcs = vmw_connector_state_to_vcs(du->connector.state);
+
+		if ((dest.x2 > new_fb->width ||
+		     dest.y2 > new_fb->height)) {
+			DRM_ERROR("CRTC area outside of framebuffer\n");
+			return -EINVAL;
+		}
+
+		/* Only one active implicit framebuffer at a time. */
+		mutex_lock(&dev_priv->global_kms_state_mutex);
+		if (vcs->is_implicit && dev_priv->implicit_fb &&
+		    !(dev_priv->num_implicit == 1 && du->active_implicit)
+		    && dev_priv->implicit_fb != vfb) {
+			DRM_ERROR("Multiple implicit framebuffers "
+				  "not supported.\n");
+			ret = -EINVAL;
+		}
+		mutex_unlock(&dev_priv->global_kms_state_mutex);
+	}
+
+
+	return ret;
+}
+
+
+/**
+ * vmw_du_cursor_plane_atomic_check - check if the new state is okay
+ *
+ * @plane: cursor plane
+ * @state: info on the new plane state
+ *
+ * This is a chance to fail if the new cursor state does not fit
+ * our requirements.
+ *
+ * Returns 0 on success
+ */
+int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
+				     struct drm_plane_state *new_state)
+{
+	int ret = 0;
+	struct vmw_surface *surface = NULL;
+	struct drm_framebuffer *fb = new_state->fb;
+
+
+	/* Turning off */
+	if (!fb)
+		return ret;
+
+	/* A lot of the code assumes this */
+	if (new_state->crtc_w != 64 || new_state->crtc_h != 64) {
+		DRM_ERROR("Invalid cursor dimensions (%d, %d)\n",
+			  new_state->crtc_w, new_state->crtc_h);
+		ret = -EINVAL;
+	}
+
+	if (!vmw_framebuffer_to_vfb(fb)->dmabuf)
+		surface = vmw_framebuffer_to_vfbs(fb)->surface;
+
+	if (surface && !surface->snooper.image) {
+		DRM_ERROR("surface not suitable for cursor\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+
 int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
 			     struct drm_crtc_state *new_state)
 {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index f711b5d..de6a0b6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -345,10 +345,25 @@  int vmw_du_cursor_plane_update(struct drm_plane *plane,
 			       uint32_t src_w, uint32_t src_h);
 
 /* Atomic Helpers */
+int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
+				      struct drm_plane_state *state);
+int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane,
+				     struct drm_plane_state *state);
+void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane,
+				       struct drm_plane_state *old_state);
+void vmw_du_cursor_plane_atomic_disable(struct drm_plane *plane,
+					struct drm_plane_state *old_state);
+int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane,
+				   struct drm_plane_state *new_state);
+void vmw_du_plane_cleanup_fb(struct drm_plane *plane,
+			     struct drm_plane_state *old_state);
 void vmw_du_plane_reset(struct drm_plane *plane);
 struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane);
 void vmw_du_plane_destroy_state(struct drm_plane *plane,
 				struct drm_plane_state *state);
+void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps,
+			     bool unreference);
+
 int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
 			     struct drm_crtc_state *state);
 void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index d547e80..1d734de 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -234,7 +234,7 @@  static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
 
 	ldu = vmw_crtc_to_ldu(crtc);
 	dev_priv = vmw_priv(crtc->dev);
-	fb       = crtc->primary->fb;
+	fb       = crtc->primary->state->fb;
 
 	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
 
@@ -242,6 +242,8 @@  static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
 		vmw_ldu_add_active(dev_priv, ldu, vfb);
 	else
 		vmw_ldu_del_active(dev_priv, ldu);
+
+	vmw_ldu_commit_list(dev_priv);
 }
 
 /**
@@ -391,6 +393,46 @@  static const struct drm_connector_funcs vmw_legacy_connector_funcs = {
  * Legacy Display Plane Functions
  */
 
+/**
+ * vmw_ldu_primary_plane_cleanup_fb - Unpin fb
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the display surface
+ *
+ * Returns 0 on success
+ */
+static void
+vmw_ldu_primary_plane_cleanup_fb(struct drm_plane *plane,
+				 struct drm_plane_state *old_state)
+{
+}
+
+
+/**
+ * vmw_ldu_primary_plane_prepare_fb -
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * Returns 0 on success
+ */
+static int
+vmw_ldu_primary_plane_prepare_fb(struct drm_plane *plane,
+				 struct drm_plane_state *new_state)
+{
+	return 0;
+}
+
+
+static void
+vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+}
+
+
 static const struct drm_plane_funcs vmw_ldu_plane_funcs = {
 	.update_plane = drm_primary_helper_update,
 	.disable_plane = drm_primary_helper_disable,
@@ -412,6 +454,22 @@  static const struct drm_plane_funcs vmw_ldu_cursor_funcs = {
 /*
  * Atomic Helpers
  */
+static const struct
+drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = {
+	.atomic_check = vmw_du_cursor_plane_atomic_check,
+	.atomic_update = vmw_du_cursor_plane_atomic_update,
+	.prepare_fb = vmw_du_cursor_plane_prepare_fb,
+	.cleanup_fb = vmw_du_plane_cleanup_fb,
+};
+
+static const struct
+drm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = {
+	.atomic_check = vmw_du_primary_plane_atomic_check,
+	.atomic_update = vmw_ldu_primary_plane_atomic_update,
+	.prepare_fb = vmw_ldu_primary_plane_prepare_fb,
+	.cleanup_fb = vmw_ldu_primary_plane_cleanup_fb,
+};
+
 static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = {
 	.prepare = vmw_ldu_crtc_helper_prepare,
 	.commit = vmw_ldu_crtc_helper_commit,
@@ -471,6 +529,8 @@  static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free;
 	}
 
+	drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs);
+
 	/* Initialize cursor plane */
 	vmw_du_plane_reset(cursor);
 
@@ -485,6 +545,10 @@  static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free;
 	}
 
+	drm_plane_helper_add(cursor, &vmw_ldu_cursor_plane_helper_funcs);
+
+
+	vmw_du_connector_reset(connector);
 	ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
 				 DRM_MODE_CONNECTOR_VIRTUAL);
 	if (ret) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index 662024c..eca055e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -612,6 +612,100 @@  static const struct drm_connector_funcs vmw_sou_connector_funcs = {
  * Screen Object Display Plane Functions
  */
 
+/**
+ * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the display surface
+ *
+ * Returns 0 on success
+ */
+static void
+vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane,
+				 struct drm_plane_state *old_state)
+{
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
+
+	vmw_dmabuf_unreference(&vps->dmabuf);
+	vps->dmabuf_size = 0;
+
+	vmw_du_plane_cleanup_fb(plane, old_state);
+}
+
+
+/**
+ * vmw_sou_primary_plane_prepare_fb - allocate backing buffer
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * The SOU backing buffer is our equivalent of the display plane.
+ *
+ * Returns 0 on success
+ */
+static int
+vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane,
+				 struct drm_plane_state *new_state)
+{
+	struct drm_framebuffer *new_fb = new_state->fb;
+	struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc;
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+	struct vmw_private *dev_priv;
+	size_t size;
+	int ret;
+
+
+	if (!new_fb) {
+		vmw_dmabuf_unreference(&vps->dmabuf);
+		vps->dmabuf_size = 0;
+
+		return 0;
+	}
+
+	size = new_state->crtc_w * new_state->crtc_h * 4;
+
+	if (vps->dmabuf) {
+		if (vps->dmabuf_size == size)
+			return 0;
+
+		vmw_dmabuf_unreference(&vps->dmabuf);
+		vps->dmabuf_size = 0;
+	}
+
+	vps->dmabuf = kzalloc(sizeof(*vps->dmabuf), GFP_KERNEL);
+	if (!vps->dmabuf)
+		return -ENOMEM;
+
+	dev_priv = vmw_priv(crtc->dev);
+	vmw_svga_enable(dev_priv);
+
+	/* After we have alloced the backing store might not be able to
+	 * resume the overlays, this is preferred to failing to alloc.
+	 */
+	vmw_overlay_pause_all(dev_priv);
+	ret = vmw_dmabuf_init(dev_priv, vps->dmabuf, size,
+			      &vmw_vram_ne_placement,
+			      false, &vmw_dmabuf_bo_free);
+	vmw_overlay_resume_all(dev_priv);
+
+	if (ret != 0)
+		vps->dmabuf = NULL; /* vmw_dmabuf_init frees on error */
+	else
+		vps->dmabuf_size = size;
+
+	return ret;
+}
+
+
+static void
+vmw_sou_primary_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+}
+
+
 static const struct drm_plane_funcs vmw_sou_plane_funcs = {
 	.update_plane = drm_primary_helper_update,
 	.disable_plane = drm_primary_helper_disable,
@@ -633,6 +727,22 @@  static const struct drm_plane_funcs vmw_sou_cursor_funcs = {
 /*
  * Atomic Helpers
  */
+static const struct
+drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = {
+	.atomic_check = vmw_du_cursor_plane_atomic_check,
+	.atomic_update = vmw_du_cursor_plane_atomic_update,
+	.prepare_fb = vmw_du_cursor_plane_prepare_fb,
+	.cleanup_fb = vmw_du_plane_cleanup_fb,
+};
+
+static const struct
+drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = {
+	.atomic_check = vmw_du_primary_plane_atomic_check,
+	.atomic_update = vmw_sou_primary_plane_atomic_update,
+	.prepare_fb = vmw_sou_primary_plane_prepare_fb,
+	.cleanup_fb = vmw_sou_primary_plane_cleanup_fb,
+};
+
 static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {
 	.prepare = vmw_sou_crtc_helper_prepare,
 	.commit = vmw_sou_crtc_helper_commit,
@@ -691,6 +801,8 @@  static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free;
 	}
 
+	drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs);
+
 	/* Initialize cursor plane */
 	vmw_du_plane_reset(cursor);
 
@@ -705,6 +817,9 @@  static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free;
 	}
 
+	drm_plane_helper_add(cursor, &vmw_sou_cursor_plane_helper_funcs);
+
+	vmw_du_connector_reset(connector);
 	ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs,
 				 DRM_MODE_CONNECTOR_VIRTUAL);
 	if (ret) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index 6e3cfad..cce5e5b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -1194,6 +1194,230 @@  static const struct drm_connector_funcs vmw_stdu_connector_funcs = {
  * Screen Target Display Plane Functions
  *****************************************************************************/
 
+
+
+/**
+ * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface
+ *
+ * @plane:  display plane
+ * @old_state: Contains the FB to clean up
+ *
+ * Unpins the display surface
+ *
+ * Returns 0 on success
+ */
+static void
+vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane,
+				  struct drm_plane_state *old_state)
+{
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state);
+
+	if (vps->surf)
+		WARN_ON(!vps->pinned);
+
+	vmw_du_plane_cleanup_fb(plane, old_state);
+
+	vps->content_fb_type = SAME_AS_DISPLAY;
+}
+
+
+
+/**
+ * vmw_stdu_primary_plane_prepare_fb - Readies the display surface
+ *
+ * @plane:  display plane
+ * @new_state: info on the new plane state, including the FB
+ *
+ * This function allocates a new display surface if the content is
+ * backed by a DMA.  The display surface is pinned here, and it'll
+ * be unpinned in .cleanup_fb()
+ *
+ * Returns 0 on success
+ */
+static int
+vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
+				  struct drm_plane_state *new_state)
+{
+	struct drm_framebuffer *new_fb = new_state->fb;
+	struct vmw_framebuffer *vfb;
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+	enum stdu_content_type new_content_type;
+	struct vmw_framebuffer_surface *new_vfbs;
+	struct drm_crtc *crtc = new_state->crtc;
+	uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h;
+	int ret;
+
+	/* No FB to prepare */
+	if (!new_fb) {
+		if (vps->surf) {
+			WARN_ON(vps->pinned != 0);
+			vmw_surface_unreference(&vps->surf);
+		}
+
+		return 0;
+	}
+
+	vfb = vmw_framebuffer_to_vfb(new_fb);
+	new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
+
+	if (new_vfbs && new_vfbs->surface->base_size.width == hdisplay &&
+	    new_vfbs->surface->base_size.height == 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) {
+		struct vmw_surface content_srf;
+		struct drm_vmw_size display_base_size = {0};
+
+		display_base_size.width  = hdisplay;
+		display_base_size.height = 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;
+		}
+
+		if (vps->surf) {
+			struct drm_vmw_size cur_base_size = vps->surf->base_size;
+
+			if (cur_base_size.width != display_base_size.width ||
+			    cur_base_size.height != display_base_size.height ||
+			    vps->surf->format != content_srf.format) {
+				WARN_ON(vps->pinned != 0);
+				vmw_surface_unreference(&vps->surf);
+			}
+
+		}
+
+		if (!vps->surf) {
+			ret = vmw_surface_gb_priv_define
+				(crtc->dev,
+				 /* Kernel visible only */
+				 0,
+				 content_srf.flags,
+				 content_srf.format,
+				 true,  /* a scanout buffer */
+				 content_srf.mip_levels[0],
+				 content_srf.multisample_count,
+				 0,
+				 display_base_size,
+				 &vps->surf);
+			if (ret != 0) {
+				DRM_ERROR("Couldn't allocate STDU surface.\n");
+				return ret;
+			}
+		}
+	} else {
+		/*
+		 * prepare_fb and clean_fb should only take care of pinning
+		 * and unpinning.  References are tracked by state objects.
+		 * The only time we add a reference in prepare_fb is if the
+		 * state object doesn't have a reference to begin with
+		 */
+		if (vps->surf) {
+			WARN_ON(vps->pinned != 0);
+			vmw_surface_unreference(&vps->surf);
+		}
+
+		vps->surf = vmw_surface_reference(new_vfbs->surface);
+	}
+
+	if (vps->surf) {
+
+		/* Pin new surface before flipping */
+		ret = vmw_resource_pin(&vps->surf->res, false);
+		if (ret)
+			goto out_srf_unref;
+
+		vps->pinned++;
+	}
+
+	vps->content_fb_type = new_content_type;
+	return 0;
+
+out_srf_unref:
+	vmw_surface_unreference(&vps->surf);
+	return ret;
+}
+
+
+
+/**
+ * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane
+ *
+ * @plane: display plane
+ * @old_state: Only used to get crtc info
+ *
+ * Formally update stdu->display_srf to the new plane, and bind the new
+ * plane STDU.  This function is called during the commit phase when
+ * all the preparation have been done and all the configurations have
+ * been checked.
+ */
+static void
+vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
+				     struct drm_plane_state *old_state)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_screen_target_display_unit *stdu;
+	struct vmw_plane_state *vps = vmw_plane_state_to_vps(plane->state);
+	struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
+	int ret;
+
+	stdu     = vmw_crtc_to_stdu(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+
+	stdu->display_srf = vps->surf;
+	stdu->content_fb_type = vps->content_fb_type;
+
+	if (!stdu->defined)
+		return;
+
+	if (plane->state->fb)
+		ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res);
+	else
+		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
+
+	/*
+	 * We cannot really fail this function, so if we do, then output an
+	 * error and quit
+	 */
+	if (ret)
+		DRM_ERROR("Failed to bind surface to STDU.\n");
+	else
+		crtc->primary->fb = plane->state->fb;
+}
+
+
 static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
 	.update_plane = drm_primary_helper_update,
 	.disable_plane = drm_primary_helper_disable,
@@ -1216,6 +1440,22 @@  static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {
 /*
  * Atomic Helpers
  */
+static const struct
+drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = {
+	.atomic_check = vmw_du_cursor_plane_atomic_check,
+	.atomic_update = vmw_du_cursor_plane_atomic_update,
+	.prepare_fb = vmw_du_cursor_plane_prepare_fb,
+	.cleanup_fb = vmw_du_plane_cleanup_fb,
+};
+
+static const struct
+drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = {
+	.atomic_check = vmw_du_primary_plane_atomic_check,
+	.atomic_update = vmw_stdu_primary_plane_atomic_update,
+	.prepare_fb = vmw_stdu_primary_plane_prepare_fb,
+	.cleanup_fb = vmw_stdu_primary_plane_cleanup_fb,
+};
+
 static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
 	.prepare = vmw_stdu_crtc_helper_prepare,
 	.commit = vmw_stdu_crtc_helper_commit,
@@ -1283,6 +1523,8 @@  static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free;
 	}
 
+	drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs);
+
 	/* Initialize cursor plane */
 	vmw_du_plane_reset(cursor);
 
@@ -1297,6 +1539,8 @@  static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free;
 	}
 
+	drm_plane_helper_add(cursor, &vmw_stdu_cursor_plane_helper_funcs);
+
 	vmw_du_connector_reset(connector);
 
 	ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs,