Commit 060e2ad5 authored by Sinclair Yeh's avatar Sinclair Yeh
Browse files

drm/vmwgfx: Add and connect plane helper functions



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: default avatarSinclair Yeh <syeh@vmware.com>
Signed-off-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
parent 06ec4190
Loading
Loading
Loading
Loading
+256 −0
Original line number Diff line number Diff line
@@ -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? */
@@ -390,6 +392,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)
{
+15 −0
Original line number Diff line number Diff line
@@ -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,
+65 −1
Original line number Diff line number Diff line
@@ -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) {
+115 −0
Original line number Diff line number Diff line
@@ -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) {
+244 −0
Original line number Diff line number Diff line
@@ -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,