diff mbox

[05/11] drm/vmwgfx: Add and connect CRTC helper functions

Message ID 1490652064-44817-6-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
Atomic mode set requires us to refactor existing vmw_stdu_crtc_set_config
code into sections that check the validity of the new mode, and sections
that actually program the hardware state.

vmw_du_crtc_atomic_check() takes CRTC-related checking code.  In a later
patch, vmw_du_primary_plane_atomic_check() will take framebuffer-related
checking code.

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:
* The state->num_connector is actually the total number of potential
  connectors, not just the one associated with the display unit.
  The proper one to check is ->connector_mask.

* Add the check to only allow plane state to be the same as crtc state
  (Thanks to mlankhorst)

* Make sure to turn on SVGA mode before using VRAM.  SVGA mode is
  disabled in master_drop if dbdev is not running.

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  |  48 ++++++++++++++
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h  |   9 +++
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c  |  79 +++++++++++++++++++++++
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 119 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 117 ++++++++++++++++++++++++++++++++++
 5 files changed, 372 insertions(+)

Comments

Daniel Vetter March 28, 2017, 7:56 a.m. UTC | #1
On Mon, Mar 27, 2017 at 03:00:58PM -0700, Sinclair Yeh wrote:
> Atomic mode set requires us to refactor existing vmw_stdu_crtc_set_config
> code into sections that check the validity of the new mode, and sections
> that actually program the hardware state.
> 
> vmw_du_crtc_atomic_check() takes CRTC-related checking code.  In a later
> patch, vmw_du_primary_plane_atomic_check() will take framebuffer-related
> checking code.
> 
> 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:
> * The state->num_connector is actually the total number of potential
>   connectors, not just the one associated with the display unit.
>   The proper one to check is ->connector_mask.
> 
> * Add the check to only allow plane state to be the same as crtc state
>   (Thanks to mlankhorst)
> 
> * Make sure to turn on SVGA mode before using VRAM.  SVGA mode is
>   disabled in master_drop if dbdev is not running.
> 
> 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  |  48 ++++++++++++++
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.h  |   9 +++
>  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c  |  79 +++++++++++++++++++++++
>  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 119 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 117 ++++++++++++++++++++++++++++++++++
>  5 files changed, 372 insertions(+)
> 
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index a8e0909..6f0f160 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -399,6 +399,54 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane)
>  }
>  
>  
> +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
> +			     struct drm_crtc_state *new_state)
> +{
> +	struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc);
> +	int connector_mask = 1 << drm_connector_index(&du->connector);
> +	bool has_primary = new_state->plane_mask &
> +			   BIT(drm_plane_index(crtc->primary));
> +
> +	/* We always want to have an active plane with an active CRTC */
> +	if (has_primary != new_state->enable)
> +		return -EINVAL;
> +
> +
> +	if (new_state->connector_mask != connector_mask &&
> +	    new_state->connector_mask != 0) {
> +		DRM_ERROR("Invalid connectors configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +
> +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
> +			      struct drm_crtc_state *old_crtc_state)
> +{
> +}
> +
> +
> +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
> +			      struct drm_crtc_state *old_crtc_state)
> +{
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +
> +	if (event) {
> +		crtc->state->event = NULL;
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		if (drm_crtc_vblank_get(crtc) == 0)
> +			drm_crtc_arm_vblank_event(crtc, event);
> +		else
> +			drm_crtc_send_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +	}
> +
> +}
> +
> +
>  /**
>   * vmw_du_crtc_duplicate_state - duplicate crtc state
>   * @crtc: DRM crtc
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> index cc50bf3..f711b5d 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> @@ -161,6 +161,7 @@ struct vmw_crtc_state {
>   * @surf Display surface for STDU
>   * @dmabuf display dmabuf for SOU
>   * @content_fb_type Used by STDU.
> + * @dmabuf_size Size of the dmabuf, used by Screen Object Display Unit
>   * @pinned pin count for STDU display surface
>   */
>  struct vmw_plane_state {
> @@ -169,6 +170,7 @@ struct vmw_plane_state {
>  	struct vmw_dma_buffer *dmabuf;
>  
>  	int content_fb_type;
> +	unsigned long dmabuf_size;
>  
>  	int pinned;
>  };
> @@ -342,10 +344,17 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane,
>  			       uint32_t src_x, uint32_t src_y,
>  			       uint32_t src_w, uint32_t src_h);
>  
> +/* Atomic Helpers */
>  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);
> +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,
> +			      struct drm_crtc_state *old_crtc_state);
> +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
> +			      struct drm_crtc_state *old_crtc_state);
>  void vmw_du_crtc_reset(struct drm_crtc *crtc);
>  struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc);
>  void vmw_du_crtc_destroy_state(struct drm_crtc *crtc,
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> index 276c744..d547e80 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> @@ -167,6 +167,7 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
>  	if (vfb != ld->fb) {
>  		if (ld->fb && ld->fb->unpin)
>  			ld->fb->unpin(ld->fb);
> +		vmw_svga_enable(vmw_priv);
>  		if (vfb->pin)
>  			vfb->pin(vfb);
>  		ld->fb = vfb;
> @@ -190,6 +191,68 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
>  	return 0;
>  }
>  
> +/**
> + * vmw_ldu_crtc_mode_set_nofb - Enable svga
> + *
> + * @crtc: CRTC associated with the new screen
> + *
> + * For LDU, just enable the svga
> + */
> +static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +}
> +
> +/**
> + * vmw_ldu_crtc_helper_prepare - Noop
> + *
> + * @crtc: CRTC associated with the new screen
> + *
> + * Prepares the CRTC for a mode set, but we don't need to do anything here.
> + *
> + */
> +static void vmw_ldu_crtc_helper_prepare(struct drm_crtc *crtc)
> +{
> +}
> +
> +/**
> + * vmw_ldu_crtc_helper_commit - Noop
> + *
> + * @crtc: CRTC associated with the new screen
> + *
> + * This is called after a mode set has been completed.  Here's
> + * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active
> + * but since for LDU the display plane is closely tied to the
> + * CRTC, it makes more sense to do those at plane update time.
> + */
> +static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_legacy_display_unit *ldu;
> +	struct vmw_framebuffer *vfb;
> +	struct drm_framebuffer *fb;
> +
> +
> +	ldu = vmw_crtc_to_ldu(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +	fb       = crtc->primary->fb;
> +
> +	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
> +
> +	if (vfb)
> +		vmw_ldu_add_active(dev_priv, ldu, vfb);
> +	else
> +		vmw_ldu_del_active(dev_priv, ldu);
> +}
> +
> +/**
> + * vmw_ldu_crtc_helper_disable - Turns off CRTC
> + *
> + * @crtc: CRTC to be turned off
> + */
> +static void vmw_ldu_crtc_helper_disable(struct drm_crtc *crtc)
> +{
> +}
> +
>  static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
>  {
>  	struct vmw_private *dev_priv;
> @@ -346,6 +409,20 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = {
>  	.atomic_destroy_state = vmw_du_plane_destroy_state,
>  };
>  
> +/*
> + * Atomic Helpers
> + */
> +static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = {
> +	.prepare = vmw_ldu_crtc_helper_prepare,
> +	.commit = vmw_ldu_crtc_helper_commit,
> +	.disable = vmw_ldu_crtc_helper_disable,
> +	.mode_set = drm_helper_crtc_mode_set,

Note this is only needed for the transitional helpers. You can/should
remove the .mode_set hook once you're fully atomic. I didn't see that
anywhere later on, might have missed it.
-Daniel

> +	.mode_set_nofb = vmw_ldu_crtc_mode_set_nofb,
> +	.atomic_check = vmw_du_crtc_atomic_check,
> +	.atomic_begin = vmw_du_crtc_atomic_begin,
> +	.atomic_flush = vmw_du_crtc_atomic_flush,
> +};
> +
>  
>  static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
>  {
> @@ -445,6 +522,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free_unregister;
>  	}
>  
> +	drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs);
> +
>  	drm_mode_crtc_set_gamma_size(crtc, 256);
>  
>  	drm_object_attach_property(&connector->base,
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> index 36d42f5..662024c 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> @@ -250,6 +250,109 @@ static int vmw_sou_backing_alloc(struct vmw_private *dev_priv,
>  	return ret;
>  }
>  
> +/**
> + * vmw_sou_crtc_mode_set_nofb - Create new screen
> + *
> + * @crtc: CRTC associated with the new screen
> + *
> + * This function creates/destroys a screen.  This function cannot fail, so if
> + * somehow we run into a failure, just do the best we can to get out.
> + */
> +static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_screen_object_unit *sou;
> +	struct vmw_framebuffer *vfb;
> +	struct drm_framebuffer *fb;
> +	struct drm_plane_state *ps;
> +	struct vmw_plane_state *vps;
> +	int ret;
> +
> +
> +	sou      = vmw_crtc_to_sou(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +	ps       = crtc->primary->state;
> +	fb       = ps->fb;
> +	vps      = vmw_plane_state_to_vps(ps);
> +
> +	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
> +
> +	if (sou->defined) {
> +		ret = vmw_sou_fifo_destroy(dev_priv, sou);
> +		if (ret) {
> +			DRM_ERROR("Failed to destroy Screen Object\n");
> +			return;
> +		}
> +	}
> +
> +	if (vfb) {
> +		sou->buffer = vps->dmabuf;
> +		sou->buffer_size = vps->dmabuf_size;
> +
> +		ret = vmw_sou_fifo_create(dev_priv, sou, crtc->x, crtc->y,
> +					  &crtc->mode);
> +		if (ret)
> +			DRM_ERROR("Failed to define Screen Object %dx%d\n",
> +				  crtc->x, crtc->y);
> +
> +		vmw_kms_add_active(dev_priv, &sou->base, vfb);
> +	} else {
> +		sou->buffer = NULL;
> +		sou->buffer_size = 0;
> +
> +		vmw_kms_del_active(dev_priv, &sou->base);
> +	}
> +}
> +
> +/**
> + * vmw_sou_crtc_helper_prepare - Noop
> + *
> + * @crtc: CRTC associated with the new screen
> + *
> + * Prepares the CRTC for a mode set, but we don't need to do anything here.
> + */
> +static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc)
> +{
> +}
> +
> +/**
> + * vmw_sou_crtc_helper_commit - Noop
> + *
> + * @crtc: CRTC associated with the new screen
> + *
> + * This is called after a mode set has been completed.
> + */
> +static void vmw_sou_crtc_helper_commit(struct drm_crtc *crtc)
> +{
> +}
> +
> +/**
> + * vmw_sou_crtc_helper_disable - Turns off CRTC
> + *
> + * @crtc: CRTC to be turned off
> + */
> +static void vmw_sou_crtc_helper_disable(struct drm_crtc *crtc)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_screen_object_unit *sou;
> +	int ret;
> +
> +
> +	if (!crtc) {
> +		DRM_ERROR("CRTC is NULL\n");
> +		return;
> +	}
> +
> +	sou = vmw_crtc_to_sou(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +
> +	if (sou->defined) {
> +		ret = vmw_sou_fifo_destroy(dev_priv, sou);
> +		if (ret)
> +			DRM_ERROR("Failed to destroy Screen Object\n");
> +	}
> +}
> +
>  static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
>  {
>  	struct vmw_private *dev_priv;
> @@ -527,6 +630,20 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = {
>  	.atomic_destroy_state = vmw_du_plane_destroy_state,
>  };
>  
> +/*
> + * Atomic Helpers
> + */
> +static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {
> +	.prepare = vmw_sou_crtc_helper_prepare,
> +	.commit = vmw_sou_crtc_helper_commit,
> +	.disable = vmw_sou_crtc_helper_disable,
> +	.mode_set = drm_helper_crtc_mode_set,
> +	.mode_set_nofb = vmw_sou_crtc_mode_set_nofb,
> +	.atomic_check = vmw_du_crtc_atomic_check,
> +	.atomic_begin = vmw_du_crtc_atomic_begin,
> +	.atomic_flush = vmw_du_crtc_atomic_flush,
> +};
> +
>  
>  static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
>  {
> @@ -626,6 +743,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free_unregister;
>  	}
>  
> +	drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs);
> +
>  	drm_mode_crtc_set_gamma_size(crtc, 256);
>  
>  	drm_object_attach_property(&connector->base,
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> index b1fae49..6e3cfad 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
> @@ -500,6 +500,106 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
>  	return ret;
>  }
>  
> +
> +/**
> + * vmw_stdu_crtc_mode_set_nofb - Updates screen target size
> + *
> + * @crtc: CRTC associated with the screen target
> + *
> + * This function defines/destroys a screen target
> + *
> + */
> +static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_screen_target_display_unit *stdu;
> +	int ret;
> +
> +
> +	stdu     = vmw_crtc_to_stdu(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +
> +	if (stdu->defined) {
> +		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
> +		if (ret)
> +			DRM_ERROR("Failed to blank CRTC\n");
> +
> +		(void) vmw_stdu_update_st(dev_priv, stdu);
> +
> +		ret = vmw_stdu_destroy_st(dev_priv, stdu);
> +		if (ret)
> +			DRM_ERROR("Failed to destroy Screen Target\n");
> +
> +		stdu->content_fb_type = SAME_AS_DISPLAY;
> +	}
> +
> +	if (!crtc->state->enable)
> +		return;
> +
> +	vmw_svga_enable(dev_priv);
> +	ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, crtc->x, crtc->y);
> +
> +	if (ret)
> +		DRM_ERROR("Failed to define Screen Target of size %dx%d\n",
> +			  crtc->x, crtc->y);
> +}
> +
> +
> +static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc)
> +{
> +}
> +
> +
> +static void vmw_stdu_crtc_helper_commit(struct drm_crtc *crtc)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_screen_target_display_unit *stdu;
> +	struct vmw_framebuffer *vfb;
> +	struct drm_framebuffer *fb;
> +
> +
> +	stdu     = vmw_crtc_to_stdu(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +	fb       = crtc->primary->fb;
> +
> +	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
> +
> +	if (vfb)
> +		vmw_kms_add_active(dev_priv, &stdu->base, vfb);
> +	else
> +		vmw_kms_del_active(dev_priv, &stdu->base);
> +}
> +
> +static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc)
> +{
> +	struct vmw_private *dev_priv;
> +	struct vmw_screen_target_display_unit *stdu;
> +	int ret;
> +
> +
> +	if (!crtc) {
> +		DRM_ERROR("CRTC is NULL\n");
> +		return;
> +	}
> +
> +	stdu     = vmw_crtc_to_stdu(crtc);
> +	dev_priv = vmw_priv(crtc->dev);
> +
> +	if (stdu->defined) {
> +		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
> +		if (ret)
> +			DRM_ERROR("Failed to blank CRTC\n");
> +
> +		(void) vmw_stdu_update_st(dev_priv, stdu);
> +
> +		ret = vmw_stdu_destroy_st(dev_priv, stdu);
> +		if (ret)
> +			DRM_ERROR("Failed to destroy Screen Target\n");
> +
> +		stdu->content_fb_type = SAME_AS_DISPLAY;
> +	}
> +}
> +
>  /**
>   * vmw_stdu_crtc_set_config - Sets a mode
>   *
> @@ -1113,6 +1213,21 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {
>  };
>  
>  
> +/*
> + * Atomic Helpers
> + */
> +static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
> +	.prepare = vmw_stdu_crtc_helper_prepare,
> +	.commit = vmw_stdu_crtc_helper_commit,
> +	.disable = vmw_stdu_crtc_helper_disable,
> +	.mode_set = drm_helper_crtc_mode_set,
> +	.mode_set_nofb = vmw_stdu_crtc_mode_set_nofb,
> +	.atomic_check = vmw_du_crtc_atomic_check,
> +	.atomic_begin = vmw_du_crtc_atomic_begin,
> +	.atomic_flush = vmw_du_crtc_atomic_flush,
> +};
> +
> +
>  /**
>   * vmw_stdu_init - Sets up a Screen Target Display Unit
>   *
> @@ -1219,6 +1334,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
>  		goto err_free_unregister;
>  	}
>  
> +	drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs);
> +
>  	drm_mode_crtc_set_gamma_size(crtc, 256);
>  
>  	drm_object_attach_property(&connector->base,
> -- 
> 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 a8e0909..6f0f160 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -399,6 +399,54 @@  void vmw_du_primary_plane_destroy(struct drm_plane *plane)
 }
 
 
+int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
+			     struct drm_crtc_state *new_state)
+{
+	struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc);
+	int connector_mask = 1 << drm_connector_index(&du->connector);
+	bool has_primary = new_state->plane_mask &
+			   BIT(drm_plane_index(crtc->primary));
+
+	/* We always want to have an active plane with an active CRTC */
+	if (has_primary != new_state->enable)
+		return -EINVAL;
+
+
+	if (new_state->connector_mask != connector_mask &&
+	    new_state->connector_mask != 0) {
+		DRM_ERROR("Invalid connectors configuration\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
+			      struct drm_crtc_state *old_crtc_state)
+{
+}
+
+
+void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
+			      struct drm_crtc_state *old_crtc_state)
+{
+	struct drm_pending_vblank_event *event = crtc->state->event;
+
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		if (drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+
+}
+
+
 /**
  * vmw_du_crtc_duplicate_state - duplicate crtc state
  * @crtc: DRM crtc
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index cc50bf3..f711b5d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -161,6 +161,7 @@  struct vmw_crtc_state {
  * @surf Display surface for STDU
  * @dmabuf display dmabuf for SOU
  * @content_fb_type Used by STDU.
+ * @dmabuf_size Size of the dmabuf, used by Screen Object Display Unit
  * @pinned pin count for STDU display surface
  */
 struct vmw_plane_state {
@@ -169,6 +170,7 @@  struct vmw_plane_state {
 	struct vmw_dma_buffer *dmabuf;
 
 	int content_fb_type;
+	unsigned long dmabuf_size;
 
 	int pinned;
 };
@@ -342,10 +344,17 @@  int vmw_du_cursor_plane_update(struct drm_plane *plane,
 			       uint32_t src_x, uint32_t src_y,
 			       uint32_t src_w, uint32_t src_h);
 
+/* Atomic Helpers */
 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);
+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,
+			      struct drm_crtc_state *old_crtc_state);
+void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc,
+			      struct drm_crtc_state *old_crtc_state);
 void vmw_du_crtc_reset(struct drm_crtc *crtc);
 struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc);
 void vmw_du_crtc_destroy_state(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index 276c744..d547e80 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -167,6 +167,7 @@  static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
 	if (vfb != ld->fb) {
 		if (ld->fb && ld->fb->unpin)
 			ld->fb->unpin(ld->fb);
+		vmw_svga_enable(vmw_priv);
 		if (vfb->pin)
 			vfb->pin(vfb);
 		ld->fb = vfb;
@@ -190,6 +191,68 @@  static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
 	return 0;
 }
 
+/**
+ * vmw_ldu_crtc_mode_set_nofb - Enable svga
+ *
+ * @crtc: CRTC associated with the new screen
+ *
+ * For LDU, just enable the svga
+ */
+static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+}
+
+/**
+ * vmw_ldu_crtc_helper_prepare - Noop
+ *
+ * @crtc: CRTC associated with the new screen
+ *
+ * Prepares the CRTC for a mode set, but we don't need to do anything here.
+ *
+ */
+static void vmw_ldu_crtc_helper_prepare(struct drm_crtc *crtc)
+{
+}
+
+/**
+ * vmw_ldu_crtc_helper_commit - Noop
+ *
+ * @crtc: CRTC associated with the new screen
+ *
+ * This is called after a mode set has been completed.  Here's
+ * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active
+ * but since for LDU the display plane is closely tied to the
+ * CRTC, it makes more sense to do those at plane update time.
+ */
+static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_legacy_display_unit *ldu;
+	struct vmw_framebuffer *vfb;
+	struct drm_framebuffer *fb;
+
+
+	ldu = vmw_crtc_to_ldu(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+	fb       = crtc->primary->fb;
+
+	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
+
+	if (vfb)
+		vmw_ldu_add_active(dev_priv, ldu, vfb);
+	else
+		vmw_ldu_del_active(dev_priv, ldu);
+}
+
+/**
+ * vmw_ldu_crtc_helper_disable - Turns off CRTC
+ *
+ * @crtc: CRTC to be turned off
+ */
+static void vmw_ldu_crtc_helper_disable(struct drm_crtc *crtc)
+{
+}
+
 static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
 {
 	struct vmw_private *dev_priv;
@@ -346,6 +409,20 @@  static const struct drm_plane_funcs vmw_ldu_cursor_funcs = {
 	.atomic_destroy_state = vmw_du_plane_destroy_state,
 };
 
+/*
+ * Atomic Helpers
+ */
+static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = {
+	.prepare = vmw_ldu_crtc_helper_prepare,
+	.commit = vmw_ldu_crtc_helper_commit,
+	.disable = vmw_ldu_crtc_helper_disable,
+	.mode_set = drm_helper_crtc_mode_set,
+	.mode_set_nofb = vmw_ldu_crtc_mode_set_nofb,
+	.atomic_check = vmw_du_crtc_atomic_check,
+	.atomic_begin = vmw_du_crtc_atomic_begin,
+	.atomic_flush = vmw_du_crtc_atomic_flush,
+};
+
 
 static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
 {
@@ -445,6 +522,8 @@  static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free_unregister;
 	}
 
+	drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs);
+
 	drm_mode_crtc_set_gamma_size(crtc, 256);
 
 	drm_object_attach_property(&connector->base,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index 36d42f5..662024c 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -250,6 +250,109 @@  static int vmw_sou_backing_alloc(struct vmw_private *dev_priv,
 	return ret;
 }
 
+/**
+ * vmw_sou_crtc_mode_set_nofb - Create new screen
+ *
+ * @crtc: CRTC associated with the new screen
+ *
+ * This function creates/destroys a screen.  This function cannot fail, so if
+ * somehow we run into a failure, just do the best we can to get out.
+ */
+static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_screen_object_unit *sou;
+	struct vmw_framebuffer *vfb;
+	struct drm_framebuffer *fb;
+	struct drm_plane_state *ps;
+	struct vmw_plane_state *vps;
+	int ret;
+
+
+	sou      = vmw_crtc_to_sou(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+	ps       = crtc->primary->state;
+	fb       = ps->fb;
+	vps      = vmw_plane_state_to_vps(ps);
+
+	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
+
+	if (sou->defined) {
+		ret = vmw_sou_fifo_destroy(dev_priv, sou);
+		if (ret) {
+			DRM_ERROR("Failed to destroy Screen Object\n");
+			return;
+		}
+	}
+
+	if (vfb) {
+		sou->buffer = vps->dmabuf;
+		sou->buffer_size = vps->dmabuf_size;
+
+		ret = vmw_sou_fifo_create(dev_priv, sou, crtc->x, crtc->y,
+					  &crtc->mode);
+		if (ret)
+			DRM_ERROR("Failed to define Screen Object %dx%d\n",
+				  crtc->x, crtc->y);
+
+		vmw_kms_add_active(dev_priv, &sou->base, vfb);
+	} else {
+		sou->buffer = NULL;
+		sou->buffer_size = 0;
+
+		vmw_kms_del_active(dev_priv, &sou->base);
+	}
+}
+
+/**
+ * vmw_sou_crtc_helper_prepare - Noop
+ *
+ * @crtc: CRTC associated with the new screen
+ *
+ * Prepares the CRTC for a mode set, but we don't need to do anything here.
+ */
+static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc)
+{
+}
+
+/**
+ * vmw_sou_crtc_helper_commit - Noop
+ *
+ * @crtc: CRTC associated with the new screen
+ *
+ * This is called after a mode set has been completed.
+ */
+static void vmw_sou_crtc_helper_commit(struct drm_crtc *crtc)
+{
+}
+
+/**
+ * vmw_sou_crtc_helper_disable - Turns off CRTC
+ *
+ * @crtc: CRTC to be turned off
+ */
+static void vmw_sou_crtc_helper_disable(struct drm_crtc *crtc)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_screen_object_unit *sou;
+	int ret;
+
+
+	if (!crtc) {
+		DRM_ERROR("CRTC is NULL\n");
+		return;
+	}
+
+	sou = vmw_crtc_to_sou(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+
+	if (sou->defined) {
+		ret = vmw_sou_fifo_destroy(dev_priv, sou);
+		if (ret)
+			DRM_ERROR("Failed to destroy Screen Object\n");
+	}
+}
+
 static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
 {
 	struct vmw_private *dev_priv;
@@ -527,6 +630,20 @@  static const struct drm_plane_funcs vmw_sou_cursor_funcs = {
 	.atomic_destroy_state = vmw_du_plane_destroy_state,
 };
 
+/*
+ * Atomic Helpers
+ */
+static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = {
+	.prepare = vmw_sou_crtc_helper_prepare,
+	.commit = vmw_sou_crtc_helper_commit,
+	.disable = vmw_sou_crtc_helper_disable,
+	.mode_set = drm_helper_crtc_mode_set,
+	.mode_set_nofb = vmw_sou_crtc_mode_set_nofb,
+	.atomic_check = vmw_du_crtc_atomic_check,
+	.atomic_begin = vmw_du_crtc_atomic_begin,
+	.atomic_flush = vmw_du_crtc_atomic_flush,
+};
+
 
 static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
 {
@@ -626,6 +743,8 @@  static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free_unregister;
 	}
 
+	drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs);
+
 	drm_mode_crtc_set_gamma_size(crtc, 256);
 
 	drm_object_attach_property(&connector->base,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index b1fae49..6e3cfad 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -500,6 +500,106 @@  static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
 	return ret;
 }
 
+
+/**
+ * vmw_stdu_crtc_mode_set_nofb - Updates screen target size
+ *
+ * @crtc: CRTC associated with the screen target
+ *
+ * This function defines/destroys a screen target
+ *
+ */
+static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_screen_target_display_unit *stdu;
+	int ret;
+
+
+	stdu     = vmw_crtc_to_stdu(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+
+	if (stdu->defined) {
+		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
+		if (ret)
+			DRM_ERROR("Failed to blank CRTC\n");
+
+		(void) vmw_stdu_update_st(dev_priv, stdu);
+
+		ret = vmw_stdu_destroy_st(dev_priv, stdu);
+		if (ret)
+			DRM_ERROR("Failed to destroy Screen Target\n");
+
+		stdu->content_fb_type = SAME_AS_DISPLAY;
+	}
+
+	if (!crtc->state->enable)
+		return;
+
+	vmw_svga_enable(dev_priv);
+	ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, crtc->x, crtc->y);
+
+	if (ret)
+		DRM_ERROR("Failed to define Screen Target of size %dx%d\n",
+			  crtc->x, crtc->y);
+}
+
+
+static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc)
+{
+}
+
+
+static void vmw_stdu_crtc_helper_commit(struct drm_crtc *crtc)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_screen_target_display_unit *stdu;
+	struct vmw_framebuffer *vfb;
+	struct drm_framebuffer *fb;
+
+
+	stdu     = vmw_crtc_to_stdu(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+	fb       = crtc->primary->fb;
+
+	vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL;
+
+	if (vfb)
+		vmw_kms_add_active(dev_priv, &stdu->base, vfb);
+	else
+		vmw_kms_del_active(dev_priv, &stdu->base);
+}
+
+static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc)
+{
+	struct vmw_private *dev_priv;
+	struct vmw_screen_target_display_unit *stdu;
+	int ret;
+
+
+	if (!crtc) {
+		DRM_ERROR("CRTC is NULL\n");
+		return;
+	}
+
+	stdu     = vmw_crtc_to_stdu(crtc);
+	dev_priv = vmw_priv(crtc->dev);
+
+	if (stdu->defined) {
+		ret = vmw_stdu_bind_st(dev_priv, stdu, NULL);
+		if (ret)
+			DRM_ERROR("Failed to blank CRTC\n");
+
+		(void) vmw_stdu_update_st(dev_priv, stdu);
+
+		ret = vmw_stdu_destroy_st(dev_priv, stdu);
+		if (ret)
+			DRM_ERROR("Failed to destroy Screen Target\n");
+
+		stdu->content_fb_type = SAME_AS_DISPLAY;
+	}
+}
+
 /**
  * vmw_stdu_crtc_set_config - Sets a mode
  *
@@ -1113,6 +1213,21 @@  static const struct drm_plane_funcs vmw_stdu_cursor_funcs = {
 };
 
 
+/*
+ * Atomic Helpers
+ */
+static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
+	.prepare = vmw_stdu_crtc_helper_prepare,
+	.commit = vmw_stdu_crtc_helper_commit,
+	.disable = vmw_stdu_crtc_helper_disable,
+	.mode_set = drm_helper_crtc_mode_set,
+	.mode_set_nofb = vmw_stdu_crtc_mode_set_nofb,
+	.atomic_check = vmw_du_crtc_atomic_check,
+	.atomic_begin = vmw_du_crtc_atomic_begin,
+	.atomic_flush = vmw_du_crtc_atomic_flush,
+};
+
+
 /**
  * vmw_stdu_init - Sets up a Screen Target Display Unit
  *
@@ -1219,6 +1334,8 @@  static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
 		goto err_free_unregister;
 	}
 
+	drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs);
+
 	drm_mode_crtc_set_gamma_size(crtc, 256);
 
 	drm_object_attach_property(&connector->base,