diff mbox

[RFCv2,10/13] drm: convert plane to properties/state

Message ID 1381771608-15237-11-git-send-email-robdclark@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rob Clark Oct. 14, 2013, 5:26 p.m. UTC
Break the mutable state of a plane out into a separate structure
and use atomic properties mechanism to set plane attributes.  This
makes it easier to have some helpers for plane->set_property()
and for checking for invalid params.  The idea is that individual
drivers can wrap the state struct in their own struct which adds
driver specific parameters, for easy build-up of state across
multiple set_property() calls and for easy atomic commit or roll-
back.

The same should be done for CRTC, encoder, and connector, but this
patch only includes the first part (plane).
---
 drivers/gpu/drm/drm_atomic_helper.c         | 137 +++++++++-
 drivers/gpu/drm/drm_crtc.c                  | 399 +++++++++++++++++++---------
 drivers/gpu/drm/drm_fb_helper.c             |  17 +-
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |  13 +-
 drivers/gpu/drm/i915/intel_sprite.c         |  19 +-
 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
 drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  18 +-
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |  30 ++-
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   5 +-
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
 drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
 drivers/gpu/host1x/drm/dc.c                 |  16 +-
 include/drm/drm_atomic_helper.h             |  37 ++-
 include/drm/drm_crtc.h                      |  88 +++++-
 18 files changed, 615 insertions(+), 190 deletions(-)

Comments

Matt Roper Oct. 24, 2013, 9:48 p.m. UTC | #1
On Mon, Oct 14, 2013 at 01:26:45PM -0400, Rob Clark wrote:
> Break the mutable state of a plane out into a separate structure
> and use atomic properties mechanism to set plane attributes.  This
> makes it easier to have some helpers for plane->set_property()
> and for checking for invalid params.  The idea is that individual
> drivers can wrap the state struct in their own struct which adds
> driver specific parameters, for easy build-up of state across
> multiple set_property() calls and for easy atomic commit or roll-
> back.
> 
> The same should be done for CRTC, encoder, and connector, but this
> patch only includes the first part (plane).
> ---
>  drivers/gpu/drm/drm_atomic_helper.c         | 137 +++++++++-
>  drivers/gpu/drm/drm_crtc.c                  | 399 +++++++++++++++++++---------
>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  13 +-
>  drivers/gpu/drm/i915/intel_sprite.c         |  19 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  18 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |  30 ++-
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   5 +-
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>  drivers/gpu/host1x/drm/dc.c                 |  16 +-
>  include/drm/drm_atomic_helper.h             |  37 ++-
>  include/drm/drm_crtc.h                      |  88 +++++-
>  18 files changed, 615 insertions(+), 190 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 46c67b8..0618113 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -39,10 +39,12 @@
>  void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
>  {
>  	struct drm_atomic_helper_state *state;
> +	int nplanes = dev->mode_config.num_plane;
>  	int sz;
>  	void *ptr;
>  
>  	sz = sizeof(*state);
> +	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
>  
>  	ptr = kzalloc(sz, GFP_KERNEL);
>  
> @@ -52,6 +54,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
>  	kref_init(&state->refcount);
>  	state->dev = dev;
>  	state->flags = flags;
> +
> +	state->planes = ptr;
> +	ptr = &state->planes[nplanes];
> +
> +	state->pstates = ptr;
> +	ptr = &state->pstates[nplanes];
> +
>  	return state;
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_begin);
> @@ -87,7 +96,19 @@ EXPORT_SYMBOL(drm_atomic_helper_set_event);
>   */
>  int drm_atomic_helper_check(struct drm_device *dev, void *state)
>  {
> -	return 0;  /* for now */
> +	struct drm_atomic_helper_state *a = state;
> +	int nplanes = dev->mode_config.num_plane;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < nplanes; i++) {
> +		if (a->planes[i]) {
> +			ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
> +			if (ret)
> +				break;
> +		}
> +	}
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_check);
>  
> @@ -104,7 +125,19 @@ EXPORT_SYMBOL(drm_atomic_helper_check);
>   */
>  int drm_atomic_helper_commit(struct drm_device *dev, void *state)
>  {
> -	return 0;  /* for now */
> +	struct drm_atomic_helper_state *a = state;
> +	int nplanes = dev->mode_config.num_plane;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < nplanes; i++) {
> +		if (a->planes[i]) {
> +			ret = drm_atomic_commit_plane_state(a->planes[i], a->pstates[i]);
> +			if (ret)
> +				break;
> +		}
> +	}
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_atomic_helper_commit);
>  
> @@ -125,11 +158,111 @@ void _drm_atomic_helper_state_free(struct kref *kref)
>  {
>  	struct drm_atomic_helper_state *state =
>  		container_of(kref, struct drm_atomic_helper_state, refcount);
> +	struct drm_device *dev = state->dev;
> +	int nplanes = dev->mode_config.num_plane;
> +	int i;
> +
> +	for (i = 0; i < nplanes; i++) {
> +		if (state->pstates[i]) {
> +			state->planes[i]->state->state = NULL;
> +			kfree(state->pstates[i]);
> +		}
> +	}
> +
>  	kfree(state);
>  }
>  EXPORT_SYMBOL(_drm_atomic_helper_state_free);
>  
> +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
> +		struct drm_property *property, uint64_t val, void *blob_data)
> +{
> +	return drm_plane_set_property(plane,
> +			drm_atomic_get_plane_state(plane, state),
> +			property, val, blob_data);
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_plane_set_property);
> +
> +void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate, void *state)
> +{
> +	/* snapshot current state: */
> +	*pstate = *plane->state;
> +	pstate->state = state;
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_init_plane_state);
> +
> +static struct drm_plane_state *
> +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
> +{
> +	struct drm_atomic_helper_state *a = state;
> +	struct drm_plane_state *pstate = a->pstates[plane->id];
> +	if (!pstate) {
> +		pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
> +		drm_atomic_helper_init_plane_state(plane, pstate, state);
> +		a->planes[plane->id] = plane;
> +		a->pstates[plane->id] = pstate;
> +	}
> +	return pstate;
> +}
> +
> +static void
> +swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a)
> +{
> +	swap(plane->state, a->pstates[plane->id]);
> +	plane->base.propvals = &plane->state->propvals;
> +}
> +
> +static int
> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct drm_framebuffer *old_fb = NULL, *fb = NULL;
> +	int ret = 0;
> +
> +	/* probably more fine grain locking would be ok of old crtc
> +	 * and new crtc were same..
> +	 */
> +	drm_modeset_lock_all(dev);
> +
> +	fb = pstate->fb;
> +
> +	if (pstate->crtc && fb) {
> +		ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
> +			pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
> +			pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
> +		if (!ret) {
> +			/* on success, update state and fb refcnting: */
> +			/* NOTE: if we ensure no driver sets plane->state->fb = NULL
> +			 * on disable, we can move this up a level and not duplicate
> +			 * nearly the same thing for both update_plane and disable_plane
> +			 * cases..  I leave it like this for now to be paranoid due to
> +			 * the slightly different ordering in the two cases in the
> +			 * original code.
> +			 */
> +			old_fb = plane->state->fb;
> +			swap_plane_state(plane, pstate->state);
> +			fb = NULL;
> +		}
> +	} else {
> +		old_fb = plane->state->fb;
> +		plane->funcs->disable_plane(plane);
> +		swap_plane_state(plane, pstate->state);
> +	}
> +
> +	drm_modeset_unlock_all(dev);
> +
> +	if (fb)
> +		drm_framebuffer_unreference(fb);
> +	if (old_fb)
> +		drm_framebuffer_unreference(old_fb);
> +
> +	return ret;
> +}
>  
>  const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = {
> +		.get_plane_state    = drm_atomic_helper_get_plane_state,
> +		.check_plane_state  = drm_plane_check_state,
> +		.commit_plane_state = drm_atomic_helper_commit_plane_state,
>  };
>  EXPORT_SYMBOL(drm_atomic_helper_funcs);
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 9f46f3b..3cf235e 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -595,7 +595,20 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  	 * in this manner.
>  	 */
>  	if (atomic_read(&fb->refcount.refcount) > 1) {
> +		void *state;
> +
> +		state = dev->driver->atomic_begin(dev, 0);
> +		if (IS_ERR(state)) {
> +			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
> +			return;
> +		}
> +
> +		/* TODO once CRTC is converted to state/properties, we can push the
> +		 * locking down into drm_atomic_helper_commit(), since that is where
> +		 * the actual changes take place..
> +		 */
>  		drm_modeset_lock_all(dev);
> +
>  		/* remove from any CRTC */
>  		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>  			if (crtc->fb == fb) {
> @@ -610,9 +623,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  		}
>  
>  		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
> -			if (plane->fb == fb)
> -				drm_plane_force_disable(plane);
> +			if (plane->state->fb == fb)
> +				drm_plane_force_disable(plane, state);
>  		}
> +
> +		/* just disabling stuff shouldn't fail, hopefully: */
> +		if(dev->driver->atomic_check(dev, state))
> +			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
> +		else
> +			dev->driver->atomic_commit(dev, state);
> +
> +		dev->driver->atomic_end(dev, state);
> +
>  		drm_modeset_unlock_all(dev);
>  	}
>  
> @@ -904,8 +926,12 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>  		   const uint32_t *formats, uint32_t format_count,
>  		   bool priv)
>  {
> +	struct drm_mode_config *config = &dev->mode_config;
>  	int ret;
>  
> +	if (!plane->state)
> +		plane->state = kzalloc(sizeof(plane->state), GFP_KERNEL);
> +
>  	drm_modeset_lock_all(dev);
>  
>  	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
> @@ -913,7 +939,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>  		goto out;
>  
>  	plane->base.properties = &plane->properties;
> -	plane->base.propvals = &plane->propvals;
> +	plane->base.propvals = &plane->state->propvals;
>  	plane->dev = dev;
>  	plane->funcs = funcs;
>  	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
> @@ -935,11 +961,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>  	 */
>  	if (!priv) {
>  		list_add_tail(&plane->head, &dev->mode_config.plane_list);
> +		plane->id = dev->mode_config.num_plane;
>  		dev->mode_config.num_plane++;
>  	} else {
>  		INIT_LIST_HEAD(&plane->head);
>  	}
>  
> +	drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_x, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_y, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_w, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_h, 0);
> +
>   out:
>  	drm_modeset_unlock_all(dev);
>  
> @@ -971,6 +1009,111 @@ void drm_plane_cleanup(struct drm_plane *plane)
>  }
>  EXPORT_SYMBOL(drm_plane_cleanup);
>  
> +int drm_plane_check_state(struct drm_plane *plane,
> +		struct drm_plane_state *state)
> +{
> +	unsigned int fb_width, fb_height;
> +	struct drm_framebuffer *fb = state->fb;
> +	int i;
> +
> +	/* disabling the plane is allowed: */
> +	if (!fb)
> +		return 0;
> +
> +	fb_width = fb->width << 16;
> +	fb_height = fb->height << 16;
> +
> +	/* Check whether this plane supports the fb pixel format. */
> +	for (i = 0; i < plane->format_count; i++)
> +		if (fb->pixel_format == plane->format_types[i])
> +			break;
> +	if (i == plane->format_count) {
> +		DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
> +		return -EINVAL;
> +	}
> +
> +	/* Make sure source coordinates are inside the fb. */
> +	if (state->src_w > fb_width ||
> +			state->src_x > fb_width - state->src_w ||
> +			state->src_h > fb_height ||
> +			state->src_y > fb_height - state->src_h) {
> +		DRM_DEBUG_KMS("Invalid source coordinates "
> +			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
> +			      state->src_w >> 16,
> +			      ((state->src_w & 0xffff) * 15625) >> 10,
> +			      state->src_h >> 16,
> +			      ((state->src_h & 0xffff) * 15625) >> 10,
> +			      state->src_x >> 16,
> +			      ((state->src_x & 0xffff) * 15625) >> 10,
> +			      state->src_y >> 16,
> +			      ((state->src_y & 0xffff) * 15625) >> 10);
> +		return -ENOSPC;
> +	}
> +
> +	/* Give drivers some help against integer overflows */
> +	if (state->crtc_w > INT_MAX ||
> +			state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
> +			state->crtc_h > INT_MAX ||
> +			state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
> +		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
> +			      state->crtc_w, state->crtc_h,
> +			      state->crtc_x, state->crtc_y);
> +		return -ERANGE;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_check_state);
> +
> +void drm_plane_commit_state(struct drm_plane *plane,
> +		struct drm_plane_state *state)
> +{
> +	plane->state = state;
> +	plane->base.propvals = &state->propvals;
> +}
> +EXPORT_SYMBOL(drm_plane_commit_state);
> +
> +int drm_plane_set_property(struct drm_plane *plane,
> +		struct drm_plane_state *state,
> +		struct drm_property *property,
> +		uint64_t value, void *blob_data)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct drm_mode_config *config = &dev->mode_config;
> +
> +	drm_object_property_set_value(&plane->base,
> +			&state->propvals, property, value, blob_data);
> +
> +	if (property == config->prop_fb_id) {
> +		state->new_fb = true;
> +		state->fb = drm_framebuffer_lookup(dev, value);
> +	} else if (property == config->prop_crtc_id) {
> +		struct drm_mode_object *obj = drm_property_get_obj(property, value);
> +		state->crtc = obj ? obj_to_crtc(obj) : NULL;
> +	} else if (property == config->prop_crtc_x) {
> +		state->crtc_x = U642I64(value);
> +	} else if (property == config->prop_crtc_y) {
> +		state->crtc_y = U642I64(value);
> +	} else if (property == config->prop_crtc_w) {
> +		state->crtc_w = value;
> +	} else if (property == config->prop_crtc_h) {
> +		state->crtc_h = value;
> +	} else if (property == config->prop_src_x) {
> +		state->src_x = value;
> +	} else if (property == config->prop_src_y) {
> +		state->src_y = value;
> +	} else if (property == config->prop_src_w) {
> +		state->src_w = value;
> +	} else if (property == config->prop_src_h) {
> +		state->src_h = value;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_set_property);
> +
>  /**
>   * drm_plane_force_disable - Forcibly disable a plane
>   * @plane: plane to disable
> @@ -980,20 +1123,15 @@ EXPORT_SYMBOL(drm_plane_cleanup);
>   * Used when the plane's current framebuffer is destroyed,
>   * and when restoring fbdev mode.
>   */
> -void drm_plane_force_disable(struct drm_plane *plane)
> +void drm_plane_force_disable(struct drm_plane *plane, void *state)
>  {
> -	int ret;
> +	struct drm_mode_config *config = &plane->dev->mode_config;
>  
> -	if (!plane->fb)
> -		return;
> -
> -	ret = plane->funcs->disable_plane(plane);
> -	if (ret)
> -		DRM_ERROR("failed to disable plane with busy fb\n");
> -	/* disconnect the plane from the fb and crtc: */
> -	__drm_framebuffer_unreference(plane->fb);
> -	plane->fb = NULL;
> -	plane->crtc = NULL;
> +	/* should turn off the crtc */
> +	drm_mode_plane_set_obj_prop(plane, state,
> +		config->prop_crtc_id, 0, NULL);
> +	drm_mode_plane_set_obj_prop(plane, state,
> +		config->prop_fb_id, 0, NULL);
>  }
>  EXPORT_SYMBOL(drm_plane_force_disable);
>  
> @@ -1043,21 +1181,89 @@ EXPORT_SYMBOL(drm_mode_destroy);
>  
>  static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
>  {
> -	struct drm_property *edid;
> -	struct drm_property *dpms;
> +	struct drm_property *prop;
>  
>  	/*
>  	 * Standard properties (apply to all connectors)
>  	 */
> -	edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
> +	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
>  				   DRM_MODE_PROP_IMMUTABLE,
>  				   "EDID", 0);
> -	dev->mode_config.edid_property = edid;
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.edid_property = prop;
>  
> -	dpms = drm_property_create_enum(dev, 0,
> +	prop = drm_property_create_enum(dev, 0,
>  				   "DPMS", drm_dpms_enum_list,
>  				   ARRAY_SIZE(drm_dpms_enum_list));
> -	dev->mode_config.dpms_property = dpms;
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.dpms_property = prop;
> +
> +
> +	/* TODO we need the driver to control which of these are dynamic
> +	 * and which are not..  or maybe we should just set all to zero
> +	 * and let the individual drivers frob the prop->flags for the
> +	 * properties they can support dynamic changes on..
> +	 */
> +
> +	prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC,
> +			"SRC_X", 0, UINT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_src_x = prop;
> +
> +	prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC,
> +			"SRC_Y", 0, UINT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_src_y = prop;
> +
> +	prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_src_w = prop;
> +
> +	prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_src_h = prop;
> +
> +	prop = drm_property_create_range(dev,
> +			DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED,
> +			"CRTC_X", I642U64(INT_MIN), I642U64(INT_MAX));
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_crtc_x = prop;
> +
> +	prop = drm_property_create_range(dev,
> +			DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED,
> +			"CRTC_Y", I642U64(INT_MIN), I642U64(INT_MAX));
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_crtc_y = prop;
> +
> +	prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_crtc_w = prop;
> +
> +	prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_crtc_h = prop;
> +
> +	prop = drm_property_create_object(dev, DRM_MODE_PROP_DYNAMIC,
> +			"FB_ID", DRM_MODE_OBJECT_FB);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_fb_id = prop;
> +
> +	prop = drm_property_create_object(dev, 0,
> +			"CRTC_ID", DRM_MODE_OBJECT_CRTC);
> +	if (!prop)
> +		return -ENOMEM;
> +	dev->mode_config.prop_crtc_id = prop;
>  
>  	return 0;
>  }
> @@ -1842,13 +2048,13 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
>  	}
>  	plane = obj_to_plane(obj);
>  
> -	if (plane->crtc)
> -		plane_resp->crtc_id = plane->crtc->base.id;
> +	if (plane->state->crtc)
> +		plane_resp->crtc_id = plane->state->crtc->base.id;
>  	else
>  		plane_resp->crtc_id = 0;
>  
> -	if (plane->fb)
> -		plane_resp->fb_id = plane->fb->base.id;
> +	if (plane->state->fb)
> +		plane_resp->fb_id = plane->state->fb->base.id;
>  	else
>  		plane_resp->fb_id = 0;
>  
> @@ -1890,21 +2096,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>  			struct drm_file *file_priv)
>  {
>  	struct drm_mode_set_plane *plane_req = data;
> +	struct drm_mode_config *config = &dev->mode_config;
>  	struct drm_mode_object *obj;
> -	struct drm_plane *plane;
> -	struct drm_crtc *crtc;
> -	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
> +	void *state;
>  	int ret = 0;
> -	unsigned int fb_width, fb_height;
> -	int i;
>  
>  	if (!drm_core_check_feature(dev, DRIVER_MODESET))
>  		return -EINVAL;
>  
> -	/*
> -	 * First, find the plane, crtc, and fb objects.  If not available,
> -	 * we don't bother to call the driver.
> -	 */
> +	state = dev->driver->atomic_begin(dev, 0);
> +	if (IS_ERR(state))
> +		return PTR_ERR(state);
> +
>  	obj = drm_mode_object_find(dev, plane_req->plane_id,
>  				   DRM_MODE_OBJECT_PLANE);
>  	if (!obj) {
> @@ -1912,102 +2115,36 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>  			      plane_req->plane_id);
>  		return -ENOENT;
>  	}
> -	plane = obj_to_plane(obj);
> -
> -	/* No fb means shut it down */
> -	if (!plane_req->fb_id) {
> -		drm_modeset_lock_all(dev);
> -		old_fb = plane->fb;
> -		plane->funcs->disable_plane(plane);
> -		plane->crtc = NULL;
> -		plane->fb = NULL;
> -		drm_modeset_unlock_all(dev);
> -		goto out;
> -	}
>  
> -	obj = drm_mode_object_find(dev, plane_req->crtc_id,
> -				   DRM_MODE_OBJECT_CRTC);
> -	if (!obj) {
> -		DRM_DEBUG_KMS("Unknown crtc ID %d\n",
> -			      plane_req->crtc_id);
> -		ret = -ENOENT;
> -		goto out;
> -	}
> -	crtc = obj_to_crtc(obj);
> -
> -	fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
> -	if (!fb) {
> -		DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
> -			      plane_req->fb_id);
> -		ret = -ENOENT;
> -		goto out;
> -	}
> -
> -	/* Check whether this plane supports the fb pixel format. */
> -	for (i = 0; i < plane->format_count; i++)
> -		if (fb->pixel_format == plane->format_types[i])
> -			break;
> -	if (i == plane->format_count) {
> -		DRM_DEBUG_KMS("Invalid pixel format %s\n",
> -			      drm_get_format_name(fb->pixel_format));
> -		ret = -EINVAL;
> -		goto out;
> -	}
> -
> -	fb_width = fb->width << 16;
> -	fb_height = fb->height << 16;
> -
> -	/* Make sure source coordinates are inside the fb. */
> -	if (plane_req->src_w > fb_width ||
> -	    plane_req->src_x > fb_width - plane_req->src_w ||
> -	    plane_req->src_h > fb_height ||
> -	    plane_req->src_y > fb_height - plane_req->src_h) {
> -		DRM_DEBUG_KMS("Invalid source coordinates "
> -			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
> -			      plane_req->src_w >> 16,
> -			      ((plane_req->src_w & 0xffff) * 15625) >> 10,
> -			      plane_req->src_h >> 16,
> -			      ((plane_req->src_h & 0xffff) * 15625) >> 10,
> -			      plane_req->src_x >> 16,
> -			      ((plane_req->src_x & 0xffff) * 15625) >> 10,
> -			      plane_req->src_y >> 16,
> -			      ((plane_req->src_y & 0xffff) * 15625) >> 10);
> -		ret = -ENOSPC;
> -		goto out;
> -	}
> -
> -	/* Give drivers some help against integer overflows */
> -	if (plane_req->crtc_w > INT_MAX ||
> -	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
> -	    plane_req->crtc_h > INT_MAX ||
> -	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
> -		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
> -			      plane_req->crtc_w, plane_req->crtc_h,
> -			      plane_req->crtc_x, plane_req->crtc_y);
> -		ret = -ERANGE;
> +	ret =
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_crtc_id, plane_req->crtc_id, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_fb_id, plane_req->fb_id, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_crtc_w, plane_req->crtc_w, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_crtc_h, plane_req->crtc_h, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_src_w, plane_req->src_w, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_src_h, plane_req->src_h, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_src_x, plane_req->src_x, NULL) ||
> +		drm_mode_set_obj_prop(obj, state,
> +			config->prop_src_y, plane_req->src_y, NULL) ||
> +		dev->driver->atomic_check(dev, state);
> +	if (ret)
>  		goto out;
> -	}
>  
> -	drm_modeset_lock_all(dev);
> -	ret = plane->funcs->update_plane(plane, crtc, fb,
> -					 plane_req->crtc_x, plane_req->crtc_y,
> -					 plane_req->crtc_w, plane_req->crtc_h,
> -					 plane_req->src_x, plane_req->src_y,
> -					 plane_req->src_w, plane_req->src_h);
> -	if (!ret) {
> -		old_fb = plane->fb;
> -		plane->crtc = crtc;
> -		plane->fb = fb;
> -		fb = NULL;
> -	}
> -	drm_modeset_unlock_all(dev);
> +	ret = dev->driver->atomic_commit(dev, state);
>  
>  out:
> -	if (fb)
> -		drm_framebuffer_unreference(fb);
> -	if (old_fb)
> -		drm_framebuffer_unreference(old_fb);
> -
> +	dev->driver->atomic_end(dev, state);
>  	return ret;
>  }
>  
> @@ -3296,7 +3433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
>  	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
>  }
>  
> -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>  					   void *state, struct drm_property *property,
>  					   uint64_t value, void *blob_data)
>  {
> @@ -3319,8 +3456,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
>  
> -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>  				      void *state, struct drm_property *property,
>  				      uint64_t value, void *blob_data)
>  {
> @@ -3335,8 +3473,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
>  
> -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>  				      void *state, struct drm_property *property,
>  				      uint64_t value, void *blob_data)
>  {
> @@ -3345,12 +3484,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>  	if (plane->funcs->set_property)
>  		ret = plane->funcs->set_property(plane, state, property,
>  				value, blob_data);
> -	if (!ret)
> -		drm_object_property_set_value(&plane->base, &plane->propvals,
> -				property, value, NULL);
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
>  
>  static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
>  		void *state, struct drm_property *property, 
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 24898dc..f1fc605 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -290,12 +290,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
>  	struct drm_device *dev = fb_helper->dev;
>  	struct drm_plane *plane;
>  	bool error = false;
> +	void *state;
>  	int i;
>  
>  	drm_warn_on_modeset_not_all_locked(dev);
>  
> +	state = dev->driver->atomic_begin(dev, 0);
> +	if (IS_ERR(state)) {
> +		DRM_ERROR("failed to restore fbdev mode\n");
> +		return true;
> +	}
> +
>  	list_for_each_entry(plane, &dev->mode_config.plane_list, head)
> -		drm_plane_force_disable(plane);
> +		drm_plane_force_disable(plane, state);
> +
> +	/* just disabling stuff shouldn't fail, hopefully: */
> +	if(dev->driver->atomic_check(dev, state))
> +		DRM_ERROR("failed to restore fbdev mode\n");
> +	else
> +		dev->driver->atomic_commit(dev, state);

I'm seeing some deadlocks here on i915 when we trigger the lastclose
handler.  We're already holding mode_config.mutex when we enter this
function, but drm_atomic_helper_commit_plane_state does another
drm_modeset_lock_all() call.


Matt

> +
> +	dev->driver->atomic_end(dev, state);
>  
>  	for (i = 0; i < fb_helper->crtc_count; i++) {
>  		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> index 82a9fca..4ae55b8 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> @@ -124,8 +124,8 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
>  	if (ret)
>  		return ret;
>  
> -	plane->crtc = crtc;
> -	plane->fb = crtc->fb;
> +	plane->state->crtc = crtc;
> +	plane->state->fb = crtc->fb;
>  
>  	exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
>  
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
> index 06f1b2a..dbe2e19 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
> @@ -127,7 +127,7 @@ static void disable_plane_to_crtc(struct drm_device *dev,
>  	 * (encoder->crtc)
>  	 */
>  	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
> -		if (plane->crtc == old_crtc) {
> +		if (plane->state->crtc == old_crtc) {
>  			/*
>  			 * do not change below call order.
>  			 *
> @@ -138,7 +138,7 @@ static void disable_plane_to_crtc(struct drm_device *dev,
>  			 * have new_crtc because new_crtc was set to
>  			 * encoder->crtc in advance.
>  			 */
> -			plane->crtc = new_crtc;
> +			plane->state->crtc = new_crtc;
>  			plane->funcs->disable_plane(plane);
>  		}
>  	}
> @@ -247,7 +247,7 @@ static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
>  
>  	/* all planes connected to this encoder should be also disabled. */
>  	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
> -		if (plane->crtc == encoder->crtc)
> +		if (plane->state->crtc == encoder->crtc)
>  			plane->funcs->disable_plane(plane);
>  	}
>  }
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> index 2e31fb8..45d0fa3 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> @@ -10,6 +10,7 @@
>   */
>  
>  #include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
>  
>  #include <drm/exynos_drm.h>
>  #include "exynos_drm_drv.h"
> @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
>  	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>  	struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
>  
> -	exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
> +	exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>  			exynos_drm_encoder_plane_commit);
>  }
>  
> @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>  		if (exynos_plane->enabled)
>  			return;
>  
> -		exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
> +		exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>  				exynos_drm_encoder_plane_enable);
>  
>  		exynos_plane->enabled = true;
> @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>  		if (!exynos_plane->enabled)
>  			return;
>  
> -		exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
> +		exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>  				exynos_drm_encoder_plane_disable);
>  
>  		exynos_plane->enabled = false;
> @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	if (ret < 0)
>  		return ret;
>  
> -	plane->crtc = crtc;
> +	plane->state->crtc = crtc;
>  
>  	exynos_plane_commit(plane);
>  	exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
> @@ -229,6 +230,10 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>  	if (property == dev_priv->plane_zpos_property) {
>  		exynos_plane->overlay.zpos = val;
>  		return 0;
> +	} else {
> +		return drm_plane_set_property(plane,
> +				drm_atomic_get_plane_state(plane, state),
> +				property, val, blob_data);
>  	}
>  
>  	return -EINVAL;
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> index ad6ec4b..d8c2869 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -868,16 +868,17 @@ intel_disable_plane(struct drm_plane *plane)
>  {
>  	struct drm_device *dev = plane->dev;
>  	struct intel_plane *intel_plane = to_intel_plane(plane);
> +	struct drm_plane_state *state = plane->state;
>  	int ret = 0;
>  
> -	if (!plane->fb)
> +	if (!state->fb)
>  		return 0;
>  
> -	if (WARN_ON(!plane->crtc))
> +	if (WARN_ON(!state->crtc))
>  		return -EINVAL;
>  
> -	intel_enable_primary(plane->crtc);
> -	intel_plane->disable_plane(plane, plane->crtc);
> +	intel_enable_primary(state->crtc);
> +	intel_plane->disable_plane(plane, state->crtc);
>  
>  	if (!intel_plane->obj)
>  		goto out;
> @@ -966,11 +967,12 @@ out_unlock:
>  void intel_plane_restore(struct drm_plane *plane)
>  {
>  	struct intel_plane *intel_plane = to_intel_plane(plane);
> +	struct drm_plane_state *state = plane->state;
>  
> -	if (!plane->crtc || !plane->fb)
> +	if (!state->crtc || !state->fb)
>  		return;
>  
> -	intel_update_plane(plane, plane->crtc, plane->fb,
> +	intel_update_plane(plane, state->crtc, state->fb,
>  			   intel_plane->crtc_x, intel_plane->crtc_y,
>  			   intel_plane->crtc_w, intel_plane->crtc_h,
>  			   intel_plane->src_x, intel_plane->src_y,
> @@ -979,7 +981,9 @@ void intel_plane_restore(struct drm_plane *plane)
>  
>  void intel_plane_disable(struct drm_plane *plane)
>  {
> -	if (!plane->crtc || !plane->fb)
> +	struct drm_plane_state *state = plane->state;
> +
> +	if (!state->crtc || !state->fb)
>  		return;
>  
>  	intel_disable_plane(plane);
> @@ -989,6 +993,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
>  	.update_plane = intel_update_plane,
>  	.disable_plane = intel_disable_plane,
>  	.destroy = intel_destroy_plane,
> +	.set_property = drm_atomic_helper_plane_set_property,
>  };
>  
>  static uint32_t ilk_plane_formats[] = {
> diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
> index d3fd7ca..1d6a6ee 100644
> --- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
> @@ -243,7 +243,7 @@ static void blend_setup(struct drm_crtc *crtc)
>  			int idx = idxs[pipe_id];
>  			if (idx > 0) {
>  				const struct mdp4_format *format =
> -					to_mdp4_format(msm_framebuffer_format(plane->fb));
> +					to_mdp4_format(msm_framebuffer_format(plane->state->fb));
>  				alpha[idx-1] = format->alpha_enable;
>  			}
>  			mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]);
> diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
> index 880e96d..8338b1a 100644
> --- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
> +++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
> @@ -45,11 +45,14 @@ static int mdp4_plane_update(struct drm_plane *plane,
>  		uint32_t src_w, uint32_t src_h)
>  {
>  	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
> +	struct drm_plane_state *state = plane->state;
>  
>  	mdp4_plane->enabled = true;
>  
> -	if (plane->fb)
> -		drm_framebuffer_unreference(plane->fb);
> +	if (state->fb) {
> +		drm_framebuffer_unreference(state->fb);
> +		state->fb = NULL;
> +	}
>  
>  	drm_framebuffer_reference(fb);
>  
> @@ -62,8 +65,8 @@ static int mdp4_plane_disable(struct drm_plane *plane)
>  {
>  	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
>  	DBG("%s: disable", mdp4_plane->name);
> -	if (plane->crtc)
> -		mdp4_crtc_detach(plane->crtc, plane);
> +	if (plane->state->crtc)
> +		mdp4_crtc_detach(plane->state->crtc, plane);
>  	return 0;
>  }
>  
> @@ -87,8 +90,9 @@ void mdp4_plane_install_properties(struct drm_plane *plane,
>  int mdp4_plane_set_property(struct drm_plane *plane, void *state,
>  		struct drm_property *property, uint64_t val, void *blob_data)
>  {
> -	// XXX
> -	return -EINVAL;
> +	return drm_plane_set_property(plane,
> +			drm_atomic_get_plane_state(plane, state),
> +			property, val, blob_data);
>  }
>  
>  static const struct drm_plane_funcs mdp4_plane_funcs = {
> @@ -117,7 +121,7 @@ void mdp4_plane_set_scanout(struct drm_plane *plane,
>  	msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova);
>  	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova);
>  
> -	plane->fb = fb;
> +	plane->state->fb = fb;
>  }
>  
>  #define MDP4_VG_PHASE_STEP_DEFAULT	0x20000000
> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
> index 5dd22ab..d53a689 100644
> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
> @@ -210,7 +210,7 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
>  		/* and any attached overlay planes: */
>  		for (i = 0; i < priv->num_planes; i++) {
>  			struct drm_plane *plane = priv->planes[i];
> -			if (plane->crtc == crtc)
> +			if (plane->state->crtc == crtc)
>  				WARN_ON(omap_plane_dpms(plane, mode));
>  		}
>  	}
> @@ -651,7 +651,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
>  
>  	omap_crtc->channel = channel;
>  	omap_crtc->plane = plane;
> -	omap_crtc->plane->crtc = crtc;
> +	omap_crtc->plane->state->crtc = crtc;
>  	omap_crtc->name = channel_names[channel];
>  	omap_crtc->pipe = id;
>  
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index 225d0e9..0e15e2c 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -559,7 +559,7 @@ static void dev_lastclose(struct drm_device *dev)
>  
>  		for (i = 0; i < priv->num_planes; i++) {
>  			drm_object_property_set_value(&priv->planes[i]->base,
> -					&priv->planes[i]->propvals,
> +					&priv->planes[i]->state->propvals,
>  					priv->rotation_prop, 0, NULL);
>  		}
>  	}
> diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
> index fe32f1b..d11a879 100644
> --- a/drivers/gpu/drm/omapdrm/omap_plane.c
> +++ b/drivers/gpu/drm/omapdrm/omap_plane.c
> @@ -116,9 +116,10 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
>  			container_of(apply, struct omap_plane, apply);
>  	struct omap_drm_window *win = &omap_plane->win;
>  	struct drm_plane *plane = &omap_plane->base;
> +	struct drm_plane_state *state = plane->state;
>  	struct drm_device *dev = plane->dev;
>  	struct omap_overlay_info *info = &omap_plane->info;
> -	struct drm_crtc *crtc = plane->crtc;
> +	struct drm_crtc *crtc = state->crtc;
>  	enum omap_channel channel;
>  	bool enabled = omap_plane->enabled && crtc;
>  	bool ilace, replication;
> @@ -127,7 +128,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
>  	DBG("%s, enabled=%d", omap_plane->name, enabled);
>  
>  	/* if fb has changed, pin new fb: */
> -	update_pin(plane, enabled ? plane->fb : NULL);
> +	update_pin(plane, enabled ? state->fb : NULL);
>  
>  	if (!enabled) {
>  		dispc_ovl_enable(omap_plane->id, false);
> @@ -137,7 +138,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
>  	channel = omap_crtc_channel(crtc);
>  
>  	/* update scanout: */
> -	omap_framebuffer_update_scanout(plane->fb, win, info);
> +	omap_framebuffer_update_scanout(state->fb, win, info);
>  
>  	DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
>  			info->out_width, info->out_height,
> @@ -179,16 +180,18 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply)
>  		cb.fxn(cb.arg);
>  
>  	if (omap_plane->enabled) {
> -		omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
> +		omap_framebuffer_flush(plane->state->fb,
> +				info->pos_x, info->pos_y,
>  				info->out_width, info->out_height);
>  	}
>  }
>  
>  static int apply(struct drm_plane *plane)
>  {
> -	if (plane->crtc) {
> +	struct drm_plane_state *state = plane->state;
> +	if (state->crtc) {
>  		struct omap_plane *omap_plane = to_omap_plane(plane);
> -		return omap_crtc_apply(plane->crtc, &omap_plane->apply);
> +		return omap_crtc_apply(state->crtc, &omap_plane->apply);
>  	}
>  	return 0;
>  }
> @@ -203,6 +206,7 @@ int omap_plane_mode_set(struct drm_plane *plane,
>  {
>  	struct omap_plane *omap_plane = to_omap_plane(plane);
>  	struct omap_drm_window *win = &omap_plane->win;
> +	struct drm_plane_state *state = plane->state;
>  
>  	win->crtc_x = crtc_x;
>  	win->crtc_y = crtc_y;
> @@ -225,8 +229,8 @@ int omap_plane_mode_set(struct drm_plane *plane,
>  		omap_plane->apply_done_cb.arg = arg;
>  	}
>  
> -	plane->fb = fb;
> -	plane->crtc = crtc;
> +	state->fb = fb;
> +	state->crtc = crtc;
>  
>  	return apply(plane);
>  }
> @@ -239,10 +243,12 @@ static int omap_plane_update(struct drm_plane *plane,
>  		uint32_t src_w, uint32_t src_h)
>  {
>  	struct omap_plane *omap_plane = to_omap_plane(plane);
> +	struct drm_plane_state *state = plane->state;
> +
>  	omap_plane->enabled = true;
>  
> -	if (plane->fb)
> -		drm_framebuffer_unreference(plane->fb);
> +	if (state->fb)
> +		drm_framebuffer_unreference(state->fb);
>  
>  	drm_framebuffer_reference(fb);
>  
> @@ -342,6 +348,10 @@ int omap_plane_set_property(struct drm_plane *plane, void *state,
>  		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
>  		omap_plane->info.zorder = val;
>  		ret = apply(plane);
> +	} else {
> +		ret = drm_plane_set_property(plane,
> +				drm_atomic_get_plane_state(plane, state),
> +				property, val, blob_data);
>  	}
>  
>  	return ret;
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> index 5691743..909e241 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> @@ -14,6 +14,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic_helper.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -411,7 +412,9 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
>  	else if (property == rgrp->planes.zpos)
>  		rcar_du_plane_set_zpos(rplane, value);
>  	else
> -		return -EINVAL;
> +		return drm_plane_set_property(plane,
> +				drm_atomic_get_plane_state(plane, state),
> +				property, value, blob_data);
>  
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> index 035bd67..e62b0f2 100644
> --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> @@ -238,7 +238,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
>  
>  	/* Setup planes. */
>  	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
> -		if (plane->crtc == crtc)
> +		if (plane->state->crtc == crtc)
>  			shmob_drm_plane_setup(plane);
>  	}
>  
> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> index 060ae03..22da5c1 100644
> --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> @@ -14,6 +14,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic_helper.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -166,10 +167,10 @@ void shmob_drm_plane_setup(struct drm_plane *plane)
>  {
>  	struct shmob_drm_plane *splane = to_shmob_plane(plane);
>  
> -	if (plane->fb == NULL)
> +	if (plane->state->fb == NULL)
>  		return;
>  
> -	__shmob_drm_plane_setup(splane, plane->fb);
> +	__shmob_drm_plane_setup(splane, plane->state->fb);
>  }
>  
>  static int
> @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane)
>  static const struct drm_plane_funcs shmob_drm_plane_funcs = {
>  	.update_plane = shmob_drm_plane_update,
>  	.disable_plane = shmob_drm_plane_disable,
> +	.set_property = drm_atomic_helper_plane_set_property,
>  	.destroy = shmob_drm_plane_destroy,
>  };
>  
> diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c
> index b1a05ad..5766010 100644
> --- a/drivers/gpu/host1x/drm/dc.c
> +++ b/drivers/gpu/host1x/drm/dc.c
> @@ -75,11 +75,11 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
>  
>  static int tegra_plane_disable(struct drm_plane *plane)
>  {
> -	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
> +	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
>  	struct tegra_plane *p = to_tegra_plane(plane);
>  	unsigned long value;
>  
> -	if (!plane->crtc)
> +	if (!plane->state->crtc)
>  		return 0;
>  
>  	value = WINDOW_A_SELECT << p->index;
> @@ -104,6 +104,7 @@ static void tegra_plane_destroy(struct drm_plane *plane)
>  static const struct drm_plane_funcs tegra_plane_funcs = {
>  	.update_plane = tegra_plane_update,
>  	.disable_plane = tegra_plane_disable,
> +	.set_property = drm_atomic_helper_plane_set_property,
>  	.destroy = tegra_plane_destroy,
>  };
>  
> @@ -267,13 +268,14 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
>  	struct drm_plane *plane;
>  
>  	list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
> -		if (plane->crtc == crtc) {
> +		struct drm_plane_state *state = plane->state;
> +		if (state->crtc == crtc) {
>  			tegra_plane_disable(plane);
> -			plane->crtc = NULL;
> +			state->crtc = NULL;
>  
> -			if (plane->fb) {
> -				drm_framebuffer_unreference(plane->fb);
> -				plane->fb = NULL;
> +			if (state->fb) {
> +				drm_framebuffer_unreference(state->fb);
> +				state->fb = NULL;
>  			}
>  		}
>  	}
> diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
> index e70cd7b..d0d29e1 100644
> --- a/include/drm/drm_atomic_helper.h
> +++ b/include/drm/drm_atomic_helper.h
> @@ -62,7 +62,9 @@
>   * struct drm_atomic_helper_funcs - helper funcs used by the atomic helpers
>   */
>  struct drm_atomic_helper_funcs {
> -	int dummy; /* for now */
> +	struct drm_plane_state *(*get_plane_state)(struct drm_plane *plane, void *state);
> +	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
> +	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
>  };
>  
>  const extern struct drm_atomic_helper_funcs drm_atomic_helper_funcs;
> @@ -75,6 +77,37 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state);
>  int drm_atomic_helper_commit(struct drm_device *dev, void *state);
>  void drm_atomic_helper_end(struct drm_device *dev, void *state);
>  
> +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
> +		struct drm_property *property, uint64_t val, void *blob_data);
> +void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate, void *state);
> +
> +static inline struct drm_plane_state *
> +drm_atomic_get_plane_state(struct drm_plane *plane, void *state)
> +{
> +	const struct drm_atomic_helper_funcs *funcs =
> +			plane->dev->driver->atomic_helpers;
> +	return funcs->get_plane_state(plane, state);
> +}
> +
> +static inline int
> +drm_atomic_check_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate)
> +{
> +	const struct drm_atomic_helper_funcs *funcs =
> +			plane->dev->driver->atomic_helpers;
> +	return funcs->check_plane_state(plane, pstate);
> +}
> +
> +static inline int
> +drm_atomic_commit_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate)
> +{
> +	const struct drm_atomic_helper_funcs *funcs =
> +			plane->dev->driver->atomic_helpers;
> +	return funcs->commit_plane_state(plane, pstate);
> +}
> +
>  /**
>   * struct drm_atomic_helper_state - the state object used by atomic helpers
>   */
> @@ -82,6 +115,8 @@ struct drm_atomic_helper_state {
>  	struct kref refcount;
>  	struct drm_device *dev;
>  	uint32_t flags;
> +	struct drm_plane **planes;
> +	struct drm_plane_state **pstates;
>  };
>  
>  static inline void
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 8831562..ee46a6a 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -675,6 +675,45 @@ struct drm_plane_funcs {
>  };
>  
>  /**
> + * drm_plane_state - mutable plane state
> + * @new_fb: has the fb been changed
> + * @crtc: currently bound CRTC
> + * @fb: currently bound fb
> + * @crtc_x: left position of visible portion of plane on crtc
> + * @crtc_y: upper position of visible portion of plane on crtc
> + * @crtc_w: width of visible portion of plane on crtc
> + * @crtc_h: height of visible portion of plane on crtc
> + * @src_x: left position of visible portion of plane within
> + *   plane (in 16.16)
> + * @src_y: upper position of visible portion of plane within
> + *   plane (in 16.16)
> + * @src_w: width of visible portion of plane (in 16.16)
> + * @src_h: height of visible portion of plane (in 16.16)
> + * @propvals: property values
> + * @state: current global/toplevel state object (for atomic) while an
> + *    update is in progress, NULL otherwise.
> + */
> +struct drm_plane_state {
> +	bool new_fb            : 1;
> +	struct drm_crtc *crtc;
> +	struct drm_framebuffer *fb;
> +
> +	/* Signed dest location allows it to be partially off screen */
> +	int32_t crtc_x, crtc_y;
> +	uint32_t crtc_w, crtc_h;
> +
> +	/* Source values are 16.16 fixed point */
> +	uint32_t src_x, src_y;
> +	uint32_t src_h, src_w;
> +
> +	bool enabled;
> +
> +	struct drm_object_property_values propvals;
> +
> +	void *state;
> +};
> +
> +/**
>   * drm_plane - central DRM plane control structure
>   * @dev: DRM device this plane belongs to
>   * @head: for list management
> @@ -682,8 +721,8 @@ struct drm_plane_funcs {
>   * @possible_crtcs: pipes this plane can be bound to
>   * @format_types: array of formats supported by this plane
>   * @format_count: number of formats supported
> - * @crtc: currently bound CRTC
> - * @fb: currently bound fb
> + * @id: plane number, 0..n
> + * @state: the mutable state
>   * @funcs: helper functions
>   * @properties: property tracking for this plane
>   */
> @@ -697,13 +736,17 @@ struct drm_plane {
>  	uint32_t *format_types;
>  	uint32_t format_count;
>  
> -	struct drm_crtc *crtc;
> -	struct drm_framebuffer *fb;
> +	int id;
> +
> +	/*
> +	 * State that can be updated from userspace, and atomically
> +	 * commited or rolled back:
> +	 */
> +	struct drm_plane_state *state;
>  
>  	const struct drm_plane_funcs *funcs;
>  
>  	struct drm_object_properties properties;
> -	struct drm_object_property_values propvals;
>  };
>  
>  /**
> @@ -884,8 +927,20 @@ struct drm_mode_config {
>  	bool poll_running;
>  	struct delayed_work output_poll_work;
>  
> -	/* pointers to standard properties */
> +	/* just so blob properties can always be in a list: */
>  	struct list_head property_blob_list;
> +
> +	/* pointers to standard properties */
> +	struct drm_property *prop_src_x;
> +	struct drm_property *prop_src_y;
> +	struct drm_property *prop_src_w;
> +	struct drm_property *prop_src_h;
> +	struct drm_property *prop_crtc_x;
> +	struct drm_property *prop_crtc_y;
> +	struct drm_property *prop_crtc_w;
> +	struct drm_property *prop_crtc_h;
> +	struct drm_property *prop_fb_id;
> +	struct drm_property *prop_crtc_id;
>  	struct drm_property *edid_property;
>  	struct drm_property *dpms_property;
>  
> @@ -969,7 +1024,15 @@ extern int drm_plane_init(struct drm_device *dev,
>  			  const uint32_t *formats, uint32_t format_count,
>  			  bool priv);
>  extern void drm_plane_cleanup(struct drm_plane *plane);
> -extern void drm_plane_force_disable(struct drm_plane *plane);
> +extern void drm_plane_force_disable(struct drm_plane *plane, void *state);
> +extern int drm_plane_check_state(struct drm_plane *plane,
> +		struct drm_plane_state *state);
> +extern void drm_plane_commit_state(struct drm_plane *plane,
> +		struct drm_plane_state *state);
> +extern int drm_plane_set_property(struct drm_plane *plane,
> +		struct drm_plane_state *state,
> +		struct drm_property *property,
> +		uint64_t value, void *blob_data);
>  
>  extern void drm_encoder_cleanup(struct drm_encoder *encoder);
>  
> @@ -1023,6 +1086,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj,
>  extern int drm_object_property_get_value(struct drm_mode_object *obj,
>  					 struct drm_property *property,
>  					 uint64_t *value);
> +
> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> +					   void *state, struct drm_property *property,
> +					   uint64_t value, void *blob_data);
> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> +				      void *state, struct drm_property *property,
> +				      uint64_t value, void *blob_data);
> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> +				      void *state, struct drm_property *property,
> +				      uint64_t value, void *blob_data);
> +
>  extern int drm_framebuffer_init(struct drm_device *dev,
>  				struct drm_framebuffer *fb,
>  				const struct drm_framebuffer_funcs *funcs);
> -- 
> 1.8.3.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Rob Clark Oct. 24, 2013, 11:43 p.m. UTC | #2
On Thu, Oct 24, 2013 at 5:48 PM, Matt Roper <matthew.d.roper@intel.com> wrote:
> On Mon, Oct 14, 2013 at 01:26:45PM -0400, Rob Clark wrote:
>> Break the mutable state of a plane out into a separate structure
>> and use atomic properties mechanism to set plane attributes.  This
>> makes it easier to have some helpers for plane->set_property()
>> and for checking for invalid params.  The idea is that individual
>> drivers can wrap the state struct in their own struct which adds
>> driver specific parameters, for easy build-up of state across
>> multiple set_property() calls and for easy atomic commit or roll-
>> back.
>>
>> The same should be done for CRTC, encoder, and connector, but this
>> patch only includes the first part (plane).
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c         | 137 +++++++++-
>>  drivers/gpu/drm/drm_crtc.c                  | 399 +++++++++++++++++++---------
>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  13 +-
>>  drivers/gpu/drm/i915/intel_sprite.c         |  19 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  18 +-
>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  30 ++-
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   5 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>  drivers/gpu/host1x/drm/dc.c                 |  16 +-
>>  include/drm/drm_atomic_helper.h             |  37 ++-
>>  include/drm/drm_crtc.h                      |  88 +++++-
>>  18 files changed, 615 insertions(+), 190 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
>> index 46c67b8..0618113 100644
>> --- a/drivers/gpu/drm/drm_atomic_helper.c
>> +++ b/drivers/gpu/drm/drm_atomic_helper.c
>> @@ -39,10 +39,12 @@
>>  void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
>>  {
>>       struct drm_atomic_helper_state *state;
>> +     int nplanes = dev->mode_config.num_plane;
>>       int sz;
>>       void *ptr;
>>
>>       sz = sizeof(*state);
>> +     sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
>>
>>       ptr = kzalloc(sz, GFP_KERNEL);
>>
>> @@ -52,6 +54,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
>>       kref_init(&state->refcount);
>>       state->dev = dev;
>>       state->flags = flags;
>> +
>> +     state->planes = ptr;
>> +     ptr = &state->planes[nplanes];
>> +
>> +     state->pstates = ptr;
>> +     ptr = &state->pstates[nplanes];
>> +
>>       return state;
>>  }
>>  EXPORT_SYMBOL(drm_atomic_helper_begin);
>> @@ -87,7 +96,19 @@ EXPORT_SYMBOL(drm_atomic_helper_set_event);
>>   */
>>  int drm_atomic_helper_check(struct drm_device *dev, void *state)
>>  {
>> -     return 0;  /* for now */
>> +     struct drm_atomic_helper_state *a = state;
>> +     int nplanes = dev->mode_config.num_plane;
>> +     int i, ret = 0;
>> +
>> +     for (i = 0; i < nplanes; i++) {
>> +             if (a->planes[i]) {
>> +                     ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
>> +                     if (ret)
>> +                             break;
>> +             }
>> +     }
>> +
>> +     return ret;
>>  }
>>  EXPORT_SYMBOL(drm_atomic_helper_check);
>>
>> @@ -104,7 +125,19 @@ EXPORT_SYMBOL(drm_atomic_helper_check);
>>   */
>>  int drm_atomic_helper_commit(struct drm_device *dev, void *state)
>>  {
>> -     return 0;  /* for now */
>> +     struct drm_atomic_helper_state *a = state;
>> +     int nplanes = dev->mode_config.num_plane;
>> +     int i, ret = 0;
>> +
>> +     for (i = 0; i < nplanes; i++) {
>> +             if (a->planes[i]) {
>> +                     ret = drm_atomic_commit_plane_state(a->planes[i], a->pstates[i]);
>> +                     if (ret)
>> +                             break;
>> +             }
>> +     }
>> +
>> +     return ret;
>>  }
>>  EXPORT_SYMBOL(drm_atomic_helper_commit);
>>
>> @@ -125,11 +158,111 @@ void _drm_atomic_helper_state_free(struct kref *kref)
>>  {
>>       struct drm_atomic_helper_state *state =
>>               container_of(kref, struct drm_atomic_helper_state, refcount);
>> +     struct drm_device *dev = state->dev;
>> +     int nplanes = dev->mode_config.num_plane;
>> +     int i;
>> +
>> +     for (i = 0; i < nplanes; i++) {
>> +             if (state->pstates[i]) {
>> +                     state->planes[i]->state->state = NULL;
>> +                     kfree(state->pstates[i]);
>> +             }
>> +     }
>> +
>>       kfree(state);
>>  }
>>  EXPORT_SYMBOL(_drm_atomic_helper_state_free);
>>
>> +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
>> +             struct drm_property *property, uint64_t val, void *blob_data)
>> +{
>> +     return drm_plane_set_property(plane,
>> +                     drm_atomic_get_plane_state(plane, state),
>> +                     property, val, blob_data);
>> +}
>> +EXPORT_SYMBOL(drm_atomic_helper_plane_set_property);
>> +
>> +void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
>> +             struct drm_plane_state *pstate, void *state)
>> +{
>> +     /* snapshot current state: */
>> +     *pstate = *plane->state;
>> +     pstate->state = state;
>> +}
>> +EXPORT_SYMBOL(drm_atomic_helper_init_plane_state);
>> +
>> +static struct drm_plane_state *
>> +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
>> +{
>> +     struct drm_atomic_helper_state *a = state;
>> +     struct drm_plane_state *pstate = a->pstates[plane->id];
>> +     if (!pstate) {
>> +             pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
>> +             drm_atomic_helper_init_plane_state(plane, pstate, state);
>> +             a->planes[plane->id] = plane;
>> +             a->pstates[plane->id] = pstate;
>> +     }
>> +     return pstate;
>> +}
>> +
>> +static void
>> +swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a)
>> +{
>> +     swap(plane->state, a->pstates[plane->id]);
>> +     plane->base.propvals = &plane->state->propvals;
>> +}
>> +
>> +static int
>> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
>> +             struct drm_plane_state *pstate)
>> +{
>> +     struct drm_device *dev = plane->dev;
>> +     struct drm_framebuffer *old_fb = NULL, *fb = NULL;
>> +     int ret = 0;
>> +
>> +     /* probably more fine grain locking would be ok of old crtc
>> +      * and new crtc were same..
>> +      */
>> +     drm_modeset_lock_all(dev);
>> +
>> +     fb = pstate->fb;
>> +
>> +     if (pstate->crtc && fb) {
>> +             ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
>> +                     pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
>> +                     pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
>> +             if (!ret) {
>> +                     /* on success, update state and fb refcnting: */
>> +                     /* NOTE: if we ensure no driver sets plane->state->fb = NULL
>> +                      * on disable, we can move this up a level and not duplicate
>> +                      * nearly the same thing for both update_plane and disable_plane
>> +                      * cases..  I leave it like this for now to be paranoid due to
>> +                      * the slightly different ordering in the two cases in the
>> +                      * original code.
>> +                      */
>> +                     old_fb = plane->state->fb;
>> +                     swap_plane_state(plane, pstate->state);
>> +                     fb = NULL;
>> +             }
>> +     } else {
>> +             old_fb = plane->state->fb;
>> +             plane->funcs->disable_plane(plane);
>> +             swap_plane_state(plane, pstate->state);
>> +     }
>> +
>> +     drm_modeset_unlock_all(dev);
>> +
>> +     if (fb)
>> +             drm_framebuffer_unreference(fb);
>> +     if (old_fb)
>> +             drm_framebuffer_unreference(old_fb);
>> +
>> +     return ret;
>> +}
>>
>>  const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = {
>> +             .get_plane_state    = drm_atomic_helper_get_plane_state,
>> +             .check_plane_state  = drm_plane_check_state,
>> +             .commit_plane_state = drm_atomic_helper_commit_plane_state,
>>  };
>>  EXPORT_SYMBOL(drm_atomic_helper_funcs);
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index 9f46f3b..3cf235e 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -595,7 +595,20 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>>        * in this manner.
>>        */
>>       if (atomic_read(&fb->refcount.refcount) > 1) {
>> +             void *state;
>> +
>> +             state = dev->driver->atomic_begin(dev, 0);
>> +             if (IS_ERR(state)) {
>> +                     DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
>> +                     return;
>> +             }
>> +
>> +             /* TODO once CRTC is converted to state/properties, we can push the
>> +              * locking down into drm_atomic_helper_commit(), since that is where
>> +              * the actual changes take place..
>> +              */
>>               drm_modeset_lock_all(dev);
>> +
>>               /* remove from any CRTC */
>>               list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>>                       if (crtc->fb == fb) {
>> @@ -610,9 +623,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>>               }
>>
>>               list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
>> -                     if (plane->fb == fb)
>> -                             drm_plane_force_disable(plane);
>> +                     if (plane->state->fb == fb)
>> +                             drm_plane_force_disable(plane, state);
>>               }
>> +
>> +             /* just disabling stuff shouldn't fail, hopefully: */
>> +             if(dev->driver->atomic_check(dev, state))
>> +                     DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
>> +             else
>> +                     dev->driver->atomic_commit(dev, state);
>> +
>> +             dev->driver->atomic_end(dev, state);
>> +
>>               drm_modeset_unlock_all(dev);
>>       }
>>
>> @@ -904,8 +926,12 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>>                  const uint32_t *formats, uint32_t format_count,
>>                  bool priv)
>>  {
>> +     struct drm_mode_config *config = &dev->mode_config;
>>       int ret;
>>
>> +     if (!plane->state)
>> +             plane->state = kzalloc(sizeof(plane->state), GFP_KERNEL);
>> +
>>       drm_modeset_lock_all(dev);
>>
>>       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
>> @@ -913,7 +939,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>>               goto out;
>>
>>       plane->base.properties = &plane->properties;
>> -     plane->base.propvals = &plane->propvals;
>> +     plane->base.propvals = &plane->state->propvals;
>>       plane->dev = dev;
>>       plane->funcs = funcs;
>>       plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
>> @@ -935,11 +961,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>>        */
>>       if (!priv) {
>>               list_add_tail(&plane->head, &dev->mode_config.plane_list);
>> +             plane->id = dev->mode_config.num_plane;
>>               dev->mode_config.num_plane++;
>>       } else {
>>               INIT_LIST_HEAD(&plane->head);
>>       }
>>
>> +     drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_x, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_y, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_w, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_h, 0);
>> +
>>   out:
>>       drm_modeset_unlock_all(dev);
>>
>> @@ -971,6 +1009,111 @@ void drm_plane_cleanup(struct drm_plane *plane)
>>  }
>>  EXPORT_SYMBOL(drm_plane_cleanup);
>>
>> +int drm_plane_check_state(struct drm_plane *plane,
>> +             struct drm_plane_state *state)
>> +{
>> +     unsigned int fb_width, fb_height;
>> +     struct drm_framebuffer *fb = state->fb;
>> +     int i;
>> +
>> +     /* disabling the plane is allowed: */
>> +     if (!fb)
>> +             return 0;
>> +
>> +     fb_width = fb->width << 16;
>> +     fb_height = fb->height << 16;
>> +
>> +     /* Check whether this plane supports the fb pixel format. */
>> +     for (i = 0; i < plane->format_count; i++)
>> +             if (fb->pixel_format == plane->format_types[i])
>> +                     break;
>> +     if (i == plane->format_count) {
>> +             DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Make sure source coordinates are inside the fb. */
>> +     if (state->src_w > fb_width ||
>> +                     state->src_x > fb_width - state->src_w ||
>> +                     state->src_h > fb_height ||
>> +                     state->src_y > fb_height - state->src_h) {
>> +             DRM_DEBUG_KMS("Invalid source coordinates "
>> +                           "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>> +                           state->src_w >> 16,
>> +                           ((state->src_w & 0xffff) * 15625) >> 10,
>> +                           state->src_h >> 16,
>> +                           ((state->src_h & 0xffff) * 15625) >> 10,
>> +                           state->src_x >> 16,
>> +                           ((state->src_x & 0xffff) * 15625) >> 10,
>> +                           state->src_y >> 16,
>> +                           ((state->src_y & 0xffff) * 15625) >> 10);
>> +             return -ENOSPC;
>> +     }
>> +
>> +     /* Give drivers some help against integer overflows */
>> +     if (state->crtc_w > INT_MAX ||
>> +                     state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
>> +                     state->crtc_h > INT_MAX ||
>> +                     state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
>> +             DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>> +                           state->crtc_w, state->crtc_h,
>> +                           state->crtc_x, state->crtc_y);
>> +             return -ERANGE;
>> +     }
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(drm_plane_check_state);
>> +
>> +void drm_plane_commit_state(struct drm_plane *plane,
>> +             struct drm_plane_state *state)
>> +{
>> +     plane->state = state;
>> +     plane->base.propvals = &state->propvals;
>> +}
>> +EXPORT_SYMBOL(drm_plane_commit_state);
>> +
>> +int drm_plane_set_property(struct drm_plane *plane,
>> +             struct drm_plane_state *state,
>> +             struct drm_property *property,
>> +             uint64_t value, void *blob_data)
>> +{
>> +     struct drm_device *dev = plane->dev;
>> +     struct drm_mode_config *config = &dev->mode_config;
>> +
>> +     drm_object_property_set_value(&plane->base,
>> +                     &state->propvals, property, value, blob_data);
>> +
>> +     if (property == config->prop_fb_id) {
>> +             state->new_fb = true;
>> +             state->fb = drm_framebuffer_lookup(dev, value);
>> +     } else if (property == config->prop_crtc_id) {
>> +             struct drm_mode_object *obj = drm_property_get_obj(property, value);
>> +             state->crtc = obj ? obj_to_crtc(obj) : NULL;
>> +     } else if (property == config->prop_crtc_x) {
>> +             state->crtc_x = U642I64(value);
>> +     } else if (property == config->prop_crtc_y) {
>> +             state->crtc_y = U642I64(value);
>> +     } else if (property == config->prop_crtc_w) {
>> +             state->crtc_w = value;
>> +     } else if (property == config->prop_crtc_h) {
>> +             state->crtc_h = value;
>> +     } else if (property == config->prop_src_x) {
>> +             state->src_x = value;
>> +     } else if (property == config->prop_src_y) {
>> +             state->src_y = value;
>> +     } else if (property == config->prop_src_w) {
>> +             state->src_w = value;
>> +     } else if (property == config->prop_src_h) {
>> +             state->src_h = value;
>> +     } else {
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(drm_plane_set_property);
>> +
>>  /**
>>   * drm_plane_force_disable - Forcibly disable a plane
>>   * @plane: plane to disable
>> @@ -980,20 +1123,15 @@ EXPORT_SYMBOL(drm_plane_cleanup);
>>   * Used when the plane's current framebuffer is destroyed,
>>   * and when restoring fbdev mode.
>>   */
>> -void drm_plane_force_disable(struct drm_plane *plane)
>> +void drm_plane_force_disable(struct drm_plane *plane, void *state)
>>  {
>> -     int ret;
>> +     struct drm_mode_config *config = &plane->dev->mode_config;
>>
>> -     if (!plane->fb)
>> -             return;
>> -
>> -     ret = plane->funcs->disable_plane(plane);
>> -     if (ret)
>> -             DRM_ERROR("failed to disable plane with busy fb\n");
>> -     /* disconnect the plane from the fb and crtc: */
>> -     __drm_framebuffer_unreference(plane->fb);
>> -     plane->fb = NULL;
>> -     plane->crtc = NULL;
>> +     /* should turn off the crtc */
>> +     drm_mode_plane_set_obj_prop(plane, state,
>> +             config->prop_crtc_id, 0, NULL);
>> +     drm_mode_plane_set_obj_prop(plane, state,
>> +             config->prop_fb_id, 0, NULL);
>>  }
>>  EXPORT_SYMBOL(drm_plane_force_disable);
>>
>> @@ -1043,21 +1181,89 @@ EXPORT_SYMBOL(drm_mode_destroy);
>>
>>  static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
>>  {
>> -     struct drm_property *edid;
>> -     struct drm_property *dpms;
>> +     struct drm_property *prop;
>>
>>       /*
>>        * Standard properties (apply to all connectors)
>>        */
>> -     edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
>> +     prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
>>                                  DRM_MODE_PROP_IMMUTABLE,
>>                                  "EDID", 0);
>> -     dev->mode_config.edid_property = edid;
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.edid_property = prop;
>>
>> -     dpms = drm_property_create_enum(dev, 0,
>> +     prop = drm_property_create_enum(dev, 0,
>>                                  "DPMS", drm_dpms_enum_list,
>>                                  ARRAY_SIZE(drm_dpms_enum_list));
>> -     dev->mode_config.dpms_property = dpms;
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.dpms_property = prop;
>> +
>> +
>> +     /* TODO we need the driver to control which of these are dynamic
>> +      * and which are not..  or maybe we should just set all to zero
>> +      * and let the individual drivers frob the prop->flags for the
>> +      * properties they can support dynamic changes on..
>> +      */
>> +
>> +     prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC,
>> +                     "SRC_X", 0, UINT_MAX);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_src_x = prop;
>> +
>> +     prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC,
>> +                     "SRC_Y", 0, UINT_MAX);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_src_y = prop;
>> +
>> +     prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_src_w = prop;
>> +
>> +     prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_src_h = prop;
>> +
>> +     prop = drm_property_create_range(dev,
>> +                     DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED,
>> +                     "CRTC_X", I642U64(INT_MIN), I642U64(INT_MAX));
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_crtc_x = prop;
>> +
>> +     prop = drm_property_create_range(dev,
>> +                     DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED,
>> +                     "CRTC_Y", I642U64(INT_MIN), I642U64(INT_MAX));
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_crtc_y = prop;
>> +
>> +     prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_crtc_w = prop;
>> +
>> +     prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_crtc_h = prop;
>> +
>> +     prop = drm_property_create_object(dev, DRM_MODE_PROP_DYNAMIC,
>> +                     "FB_ID", DRM_MODE_OBJECT_FB);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_fb_id = prop;
>> +
>> +     prop = drm_property_create_object(dev, 0,
>> +                     "CRTC_ID", DRM_MODE_OBJECT_CRTC);
>> +     if (!prop)
>> +             return -ENOMEM;
>> +     dev->mode_config.prop_crtc_id = prop;
>>
>>       return 0;
>>  }
>> @@ -1842,13 +2048,13 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
>>       }
>>       plane = obj_to_plane(obj);
>>
>> -     if (plane->crtc)
>> -             plane_resp->crtc_id = plane->crtc->base.id;
>> +     if (plane->state->crtc)
>> +             plane_resp->crtc_id = plane->state->crtc->base.id;
>>       else
>>               plane_resp->crtc_id = 0;
>>
>> -     if (plane->fb)
>> -             plane_resp->fb_id = plane->fb->base.id;
>> +     if (plane->state->fb)
>> +             plane_resp->fb_id = plane->state->fb->base.id;
>>       else
>>               plane_resp->fb_id = 0;
>>
>> @@ -1890,21 +2096,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>                       struct drm_file *file_priv)
>>  {
>>       struct drm_mode_set_plane *plane_req = data;
>> +     struct drm_mode_config *config = &dev->mode_config;
>>       struct drm_mode_object *obj;
>> -     struct drm_plane *plane;
>> -     struct drm_crtc *crtc;
>> -     struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>> +     void *state;
>>       int ret = 0;
>> -     unsigned int fb_width, fb_height;
>> -     int i;
>>
>>       if (!drm_core_check_feature(dev, DRIVER_MODESET))
>>               return -EINVAL;
>>
>> -     /*
>> -      * First, find the plane, crtc, and fb objects.  If not available,
>> -      * we don't bother to call the driver.
>> -      */
>> +     state = dev->driver->atomic_begin(dev, 0);
>> +     if (IS_ERR(state))
>> +             return PTR_ERR(state);
>> +
>>       obj = drm_mode_object_find(dev, plane_req->plane_id,
>>                                  DRM_MODE_OBJECT_PLANE);
>>       if (!obj) {
>> @@ -1912,102 +2115,36 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>                             plane_req->plane_id);
>>               return -ENOENT;
>>       }
>> -     plane = obj_to_plane(obj);
>> -
>> -     /* No fb means shut it down */
>> -     if (!plane_req->fb_id) {
>> -             drm_modeset_lock_all(dev);
>> -             old_fb = plane->fb;
>> -             plane->funcs->disable_plane(plane);
>> -             plane->crtc = NULL;
>> -             plane->fb = NULL;
>> -             drm_modeset_unlock_all(dev);
>> -             goto out;
>> -     }
>>
>> -     obj = drm_mode_object_find(dev, plane_req->crtc_id,
>> -                                DRM_MODE_OBJECT_CRTC);
>> -     if (!obj) {
>> -             DRM_DEBUG_KMS("Unknown crtc ID %d\n",
>> -                           plane_req->crtc_id);
>> -             ret = -ENOENT;
>> -             goto out;
>> -     }
>> -     crtc = obj_to_crtc(obj);
>> -
>> -     fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
>> -     if (!fb) {
>> -             DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
>> -                           plane_req->fb_id);
>> -             ret = -ENOENT;
>> -             goto out;
>> -     }
>> -
>> -     /* Check whether this plane supports the fb pixel format. */
>> -     for (i = 0; i < plane->format_count; i++)
>> -             if (fb->pixel_format == plane->format_types[i])
>> -                     break;
>> -     if (i == plane->format_count) {
>> -             DRM_DEBUG_KMS("Invalid pixel format %s\n",
>> -                           drm_get_format_name(fb->pixel_format));
>> -             ret = -EINVAL;
>> -             goto out;
>> -     }
>> -
>> -     fb_width = fb->width << 16;
>> -     fb_height = fb->height << 16;
>> -
>> -     /* Make sure source coordinates are inside the fb. */
>> -     if (plane_req->src_w > fb_width ||
>> -         plane_req->src_x > fb_width - plane_req->src_w ||
>> -         plane_req->src_h > fb_height ||
>> -         plane_req->src_y > fb_height - plane_req->src_h) {
>> -             DRM_DEBUG_KMS("Invalid source coordinates "
>> -                           "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>> -                           plane_req->src_w >> 16,
>> -                           ((plane_req->src_w & 0xffff) * 15625) >> 10,
>> -                           plane_req->src_h >> 16,
>> -                           ((plane_req->src_h & 0xffff) * 15625) >> 10,
>> -                           plane_req->src_x >> 16,
>> -                           ((plane_req->src_x & 0xffff) * 15625) >> 10,
>> -                           plane_req->src_y >> 16,
>> -                           ((plane_req->src_y & 0xffff) * 15625) >> 10);
>> -             ret = -ENOSPC;
>> -             goto out;
>> -     }
>> -
>> -     /* Give drivers some help against integer overflows */
>> -     if (plane_req->crtc_w > INT_MAX ||
>> -         plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
>> -         plane_req->crtc_h > INT_MAX ||
>> -         plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
>> -             DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>> -                           plane_req->crtc_w, plane_req->crtc_h,
>> -                           plane_req->crtc_x, plane_req->crtc_y);
>> -             ret = -ERANGE;
>> +     ret =
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_crtc_id, plane_req->crtc_id, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_fb_id, plane_req->fb_id, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_crtc_w, plane_req->crtc_w, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_crtc_h, plane_req->crtc_h, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_src_w, plane_req->src_w, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_src_h, plane_req->src_h, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_src_x, plane_req->src_x, NULL) ||
>> +             drm_mode_set_obj_prop(obj, state,
>> +                     config->prop_src_y, plane_req->src_y, NULL) ||
>> +             dev->driver->atomic_check(dev, state);
>> +     if (ret)
>>               goto out;
>> -     }
>>
>> -     drm_modeset_lock_all(dev);
>> -     ret = plane->funcs->update_plane(plane, crtc, fb,
>> -                                      plane_req->crtc_x, plane_req->crtc_y,
>> -                                      plane_req->crtc_w, plane_req->crtc_h,
>> -                                      plane_req->src_x, plane_req->src_y,
>> -                                      plane_req->src_w, plane_req->src_h);
>> -     if (!ret) {
>> -             old_fb = plane->fb;
>> -             plane->crtc = crtc;
>> -             plane->fb = fb;
>> -             fb = NULL;
>> -     }
>> -     drm_modeset_unlock_all(dev);
>> +     ret = dev->driver->atomic_commit(dev, state);
>>
>>  out:
>> -     if (fb)
>> -             drm_framebuffer_unreference(fb);
>> -     if (old_fb)
>> -             drm_framebuffer_unreference(old_fb);
>> -
>> +     dev->driver->atomic_end(dev, state);
>>       return ret;
>>  }
>>
>> @@ -3296,7 +3433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
>>       return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
>>  }
>>
>> -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>>                                          void *state, struct drm_property *property,
>>                                          uint64_t value, void *blob_data)
>>  {
>> @@ -3319,8 +3456,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>>
>>       return ret;
>>  }
>> +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
>>
>> -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>>                                     void *state, struct drm_property *property,
>>                                     uint64_t value, void *blob_data)
>>  {
>> @@ -3335,8 +3473,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>>
>>       return ret;
>>  }
>> +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
>>
>> -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>>                                     void *state, struct drm_property *property,
>>                                     uint64_t value, void *blob_data)
>>  {
>> @@ -3345,12 +3484,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>>       if (plane->funcs->set_property)
>>               ret = plane->funcs->set_property(plane, state, property,
>>                               value, blob_data);
>> -     if (!ret)
>> -             drm_object_property_set_value(&plane->base, &plane->propvals,
>> -                             property, value, NULL);
>>
>>       return ret;
>>  }
>> +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
>>
>>  static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
>>               void *state, struct drm_property *property,
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
>> index 24898dc..f1fc605 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -290,12 +290,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
>>       struct drm_device *dev = fb_helper->dev;
>>       struct drm_plane *plane;
>>       bool error = false;
>> +     void *state;
>>       int i;
>>
>>       drm_warn_on_modeset_not_all_locked(dev);
>>
>> +     state = dev->driver->atomic_begin(dev, 0);
>> +     if (IS_ERR(state)) {
>> +             DRM_ERROR("failed to restore fbdev mode\n");
>> +             return true;
>> +     }
>> +
>>       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
>> -             drm_plane_force_disable(plane);
>> +             drm_plane_force_disable(plane, state);
>> +
>> +     /* just disabling stuff shouldn't fail, hopefully: */
>> +     if(dev->driver->atomic_check(dev, state))
>> +             DRM_ERROR("failed to restore fbdev mode\n");
>> +     else
>> +             dev->driver->atomic_commit(dev, state);
>
> I'm seeing some deadlocks here on i915 when we trigger the lastclose
> handler.  We're already holding mode_config.mutex when we enter this
> function, but drm_atomic_helper_commit_plane_state does another
> drm_modeset_lock_all() call.


oh, sorry, I should have mentioned that for now I got rid of the locks
around the call to drm_fb_helper_restore_fbdev_mode().. that hack is
intended to be cleaned up somehow by (I think) pushing the lock down
into _restore_fbdev_mode(), plus ww_mutex conversion, which will be
part of next next round of RFC (as soon as I have time to implement
it).

BR,
-R
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 46c67b8..0618113 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -39,10 +39,12 @@ 
 void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 {
 	struct drm_atomic_helper_state *state;
+	int nplanes = dev->mode_config.num_plane;
 	int sz;
 	void *ptr;
 
 	sz = sizeof(*state);
+	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
 
 	ptr = kzalloc(sz, GFP_KERNEL);
 
@@ -52,6 +54,13 @@  void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 	kref_init(&state->refcount);
 	state->dev = dev;
 	state->flags = flags;
+
+	state->planes = ptr;
+	ptr = &state->planes[nplanes];
+
+	state->pstates = ptr;
+	ptr = &state->pstates[nplanes];
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_helper_begin);
@@ -87,7 +96,19 @@  EXPORT_SYMBOL(drm_atomic_helper_set_event);
  */
 int drm_atomic_helper_check(struct drm_device *dev, void *state)
 {
-	return 0;  /* for now */
+	struct drm_atomic_helper_state *a = state;
+	int nplanes = dev->mode_config.num_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		if (a->planes[i]) {
+			ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
+
+	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_check);
 
@@ -104,7 +125,19 @@  EXPORT_SYMBOL(drm_atomic_helper_check);
  */
 int drm_atomic_helper_commit(struct drm_device *dev, void *state)
 {
-	return 0;  /* for now */
+	struct drm_atomic_helper_state *a = state;
+	int nplanes = dev->mode_config.num_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		if (a->planes[i]) {
+			ret = drm_atomic_commit_plane_state(a->planes[i], a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
+
+	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit);
 
@@ -125,11 +158,111 @@  void _drm_atomic_helper_state_free(struct kref *kref)
 {
 	struct drm_atomic_helper_state *state =
 		container_of(kref, struct drm_atomic_helper_state, refcount);
+	struct drm_device *dev = state->dev;
+	int nplanes = dev->mode_config.num_plane;
+	int i;
+
+	for (i = 0; i < nplanes; i++) {
+		if (state->pstates[i]) {
+			state->planes[i]->state->state = NULL;
+			kfree(state->pstates[i]);
+		}
+	}
+
 	kfree(state);
 }
 EXPORT_SYMBOL(_drm_atomic_helper_state_free);
 
+int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
+		struct drm_property *property, uint64_t val, void *blob_data)
+{
+	return drm_plane_set_property(plane,
+			drm_atomic_get_plane_state(plane, state),
+			property, val, blob_data);
+}
+EXPORT_SYMBOL(drm_atomic_helper_plane_set_property);
+
+void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate, void *state)
+{
+	/* snapshot current state: */
+	*pstate = *plane->state;
+	pstate->state = state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_init_plane_state);
+
+static struct drm_plane_state *
+drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
+{
+	struct drm_atomic_helper_state *a = state;
+	struct drm_plane_state *pstate = a->pstates[plane->id];
+	if (!pstate) {
+		pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+		drm_atomic_helper_init_plane_state(plane, pstate, state);
+		a->planes[plane->id] = plane;
+		a->pstates[plane->id] = pstate;
+	}
+	return pstate;
+}
+
+static void
+swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a)
+{
+	swap(plane->state, a->pstates[plane->id]);
+	plane->base.propvals = &plane->state->propvals;
+}
+
+static int
+drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	struct drm_device *dev = plane->dev;
+	struct drm_framebuffer *old_fb = NULL, *fb = NULL;
+	int ret = 0;
+
+	/* probably more fine grain locking would be ok of old crtc
+	 * and new crtc were same..
+	 */
+	drm_modeset_lock_all(dev);
+
+	fb = pstate->fb;
+
+	if (pstate->crtc && fb) {
+		ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
+			pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
+			pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
+		if (!ret) {
+			/* on success, update state and fb refcnting: */
+			/* NOTE: if we ensure no driver sets plane->state->fb = NULL
+			 * on disable, we can move this up a level and not duplicate
+			 * nearly the same thing for both update_plane and disable_plane
+			 * cases..  I leave it like this for now to be paranoid due to
+			 * the slightly different ordering in the two cases in the
+			 * original code.
+			 */
+			old_fb = plane->state->fb;
+			swap_plane_state(plane, pstate->state);
+			fb = NULL;
+		}
+	} else {
+		old_fb = plane->state->fb;
+		plane->funcs->disable_plane(plane);
+		swap_plane_state(plane, pstate->state);
+	}
+
+	drm_modeset_unlock_all(dev);
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
+	return ret;
+}
 
 const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = {
+		.get_plane_state    = drm_atomic_helper_get_plane_state,
+		.check_plane_state  = drm_plane_check_state,
+		.commit_plane_state = drm_atomic_helper_commit_plane_state,
 };
 EXPORT_SYMBOL(drm_atomic_helper_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 9f46f3b..3cf235e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -595,7 +595,20 @@  void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
+		void *state;
+
+		state = dev->driver->atomic_begin(dev, 0);
+		if (IS_ERR(state)) {
+			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
+			return;
+		}
+
+		/* TODO once CRTC is converted to state/properties, we can push the
+		 * locking down into drm_atomic_helper_commit(), since that is where
+		 * the actual changes take place..
+		 */
 		drm_modeset_lock_all(dev);
+
 		/* remove from any CRTC */
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 			if (crtc->fb == fb) {
@@ -610,9 +623,18 @@  void drm_framebuffer_remove(struct drm_framebuffer *fb)
 		}
 
 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-			if (plane->fb == fb)
-				drm_plane_force_disable(plane);
+			if (plane->state->fb == fb)
+				drm_plane_force_disable(plane, state);
 		}
+
+		/* just disabling stuff shouldn't fail, hopefully: */
+		if(dev->driver->atomic_check(dev, state))
+			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
+		else
+			dev->driver->atomic_commit(dev, state);
+
+		dev->driver->atomic_end(dev, state);
+
 		drm_modeset_unlock_all(dev);
 	}
 
@@ -904,8 +926,12 @@  int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		   const uint32_t *formats, uint32_t format_count,
 		   bool priv)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	if (!plane->state)
+		plane->state = kzalloc(sizeof(plane->state), GFP_KERNEL);
+
 	drm_modeset_lock_all(dev);
 
 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
@@ -913,7 +939,7 @@  int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		goto out;
 
 	plane->base.properties = &plane->properties;
-	plane->base.propvals = &plane->propvals;
+	plane->base.propvals = &plane->state->propvals;
 	plane->dev = dev;
 	plane->funcs = funcs;
 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
@@ -935,11 +961,23 @@  int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 	 */
 	if (!priv) {
 		list_add_tail(&plane->head, &dev->mode_config.plane_list);
+		plane->id = dev->mode_config.num_plane;
 		dev->mode_config.num_plane++;
 	} else {
 		INIT_LIST_HEAD(&plane->head);
 	}
 
+	drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_x, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_y, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_w, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_h, 0);
+
  out:
 	drm_modeset_unlock_all(dev);
 
@@ -971,6 +1009,111 @@  void drm_plane_cleanup(struct drm_plane *plane)
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
 
+int drm_plane_check_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	unsigned int fb_width, fb_height;
+	struct drm_framebuffer *fb = state->fb;
+	int i;
+
+	/* disabling the plane is allowed: */
+	if (!fb)
+		return 0;
+
+	fb_width = fb->width << 16;
+	fb_height = fb->height << 16;
+
+	/* Check whether this plane supports the fb pixel format. */
+	for (i = 0; i < plane->format_count; i++)
+		if (fb->pixel_format == plane->format_types[i])
+			break;
+	if (i == plane->format_count) {
+		DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
+		return -EINVAL;
+	}
+
+	/* Make sure source coordinates are inside the fb. */
+	if (state->src_w > fb_width ||
+			state->src_x > fb_width - state->src_w ||
+			state->src_h > fb_height ||
+			state->src_y > fb_height - state->src_h) {
+		DRM_DEBUG_KMS("Invalid source coordinates "
+			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+			      state->src_w >> 16,
+			      ((state->src_w & 0xffff) * 15625) >> 10,
+			      state->src_h >> 16,
+			      ((state->src_h & 0xffff) * 15625) >> 10,
+			      state->src_x >> 16,
+			      ((state->src_x & 0xffff) * 15625) >> 10,
+			      state->src_y >> 16,
+			      ((state->src_y & 0xffff) * 15625) >> 10);
+		return -ENOSPC;
+	}
+
+	/* Give drivers some help against integer overflows */
+	if (state->crtc_w > INT_MAX ||
+			state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
+			state->crtc_h > INT_MAX ||
+			state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
+		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+			      state->crtc_w, state->crtc_h,
+			      state->crtc_x, state->crtc_y);
+		return -ERANGE;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_check_state);
+
+void drm_plane_commit_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	plane->state = state;
+	plane->base.propvals = &state->propvals;
+}
+EXPORT_SYMBOL(drm_plane_commit_state);
+
+int drm_plane_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data)
+{
+	struct drm_device *dev = plane->dev;
+	struct drm_mode_config *config = &dev->mode_config;
+
+	drm_object_property_set_value(&plane->base,
+			&state->propvals, property, value, blob_data);
+
+	if (property == config->prop_fb_id) {
+		state->new_fb = true;
+		state->fb = drm_framebuffer_lookup(dev, value);
+	} else if (property == config->prop_crtc_id) {
+		struct drm_mode_object *obj = drm_property_get_obj(property, value);
+		state->crtc = obj ? obj_to_crtc(obj) : NULL;
+	} else if (property == config->prop_crtc_x) {
+		state->crtc_x = U642I64(value);
+	} else if (property == config->prop_crtc_y) {
+		state->crtc_y = U642I64(value);
+	} else if (property == config->prop_crtc_w) {
+		state->crtc_w = value;
+	} else if (property == config->prop_crtc_h) {
+		state->crtc_h = value;
+	} else if (property == config->prop_src_x) {
+		state->src_x = value;
+	} else if (property == config->prop_src_y) {
+		state->src_y = value;
+	} else if (property == config->prop_src_w) {
+		state->src_w = value;
+	} else if (property == config->prop_src_h) {
+		state->src_h = value;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_set_property);
+
 /**
  * drm_plane_force_disable - Forcibly disable a plane
  * @plane: plane to disable
@@ -980,20 +1123,15 @@  EXPORT_SYMBOL(drm_plane_cleanup);
  * Used when the plane's current framebuffer is destroyed,
  * and when restoring fbdev mode.
  */
-void drm_plane_force_disable(struct drm_plane *plane)
+void drm_plane_force_disable(struct drm_plane *plane, void *state)
 {
-	int ret;
+	struct drm_mode_config *config = &plane->dev->mode_config;
 
-	if (!plane->fb)
-		return;
-
-	ret = plane->funcs->disable_plane(plane);
-	if (ret)
-		DRM_ERROR("failed to disable plane with busy fb\n");
-	/* disconnect the plane from the fb and crtc: */
-	__drm_framebuffer_unreference(plane->fb);
-	plane->fb = NULL;
-	plane->crtc = NULL;
+	/* should turn off the crtc */
+	drm_mode_plane_set_obj_prop(plane, state,
+		config->prop_crtc_id, 0, NULL);
+	drm_mode_plane_set_obj_prop(plane, state,
+		config->prop_fb_id, 0, NULL);
 }
 EXPORT_SYMBOL(drm_plane_force_disable);
 
@@ -1043,21 +1181,89 @@  EXPORT_SYMBOL(drm_mode_destroy);
 
 static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
-	struct drm_property *edid;
-	struct drm_property *dpms;
+	struct drm_property *prop;
 
 	/*
 	 * Standard properties (apply to all connectors)
 	 */
-	edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
 				   DRM_MODE_PROP_IMMUTABLE,
 				   "EDID", 0);
-	dev->mode_config.edid_property = edid;
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.edid_property = prop;
 
-	dpms = drm_property_create_enum(dev, 0,
+	prop = drm_property_create_enum(dev, 0,
 				   "DPMS", drm_dpms_enum_list,
 				   ARRAY_SIZE(drm_dpms_enum_list));
-	dev->mode_config.dpms_property = dpms;
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.dpms_property = prop;
+
+
+	/* TODO we need the driver to control which of these are dynamic
+	 * and which are not..  or maybe we should just set all to zero
+	 * and let the individual drivers frob the prop->flags for the
+	 * properties they can support dynamic changes on..
+	 */
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC,
+			"SRC_X", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_x = prop;
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC,
+			"SRC_Y", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_y = prop;
+
+	prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_w = prop;
+
+	prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_h = prop;
+
+	prop = drm_property_create_range(dev,
+			DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED,
+			"CRTC_X", I642U64(INT_MIN), I642U64(INT_MAX));
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_x = prop;
+
+	prop = drm_property_create_range(dev,
+			DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED,
+			"CRTC_Y", I642U64(INT_MIN), I642U64(INT_MAX));
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_y = prop;
+
+	prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_w = prop;
+
+	prop = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_h = prop;
+
+	prop = drm_property_create_object(dev, DRM_MODE_PROP_DYNAMIC,
+			"FB_ID", DRM_MODE_OBJECT_FB);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_fb_id = prop;
+
+	prop = drm_property_create_object(dev, 0,
+			"CRTC_ID", DRM_MODE_OBJECT_CRTC);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_id = prop;
 
 	return 0;
 }
@@ -1842,13 +2048,13 @@  int drm_mode_getplane(struct drm_device *dev, void *data,
 	}
 	plane = obj_to_plane(obj);
 
-	if (plane->crtc)
-		plane_resp->crtc_id = plane->crtc->base.id;
+	if (plane->state->crtc)
+		plane_resp->crtc_id = plane->state->crtc->base.id;
 	else
 		plane_resp->crtc_id = 0;
 
-	if (plane->fb)
-		plane_resp->fb_id = plane->fb->base.id;
+	if (plane->state->fb)
+		plane_resp->fb_id = plane->state->fb->base.id;
 	else
 		plane_resp->fb_id = 0;
 
@@ -1890,21 +2096,18 @@  int drm_mode_setplane(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 {
 	struct drm_mode_set_plane *plane_req = data;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_mode_object *obj;
-	struct drm_plane *plane;
-	struct drm_crtc *crtc;
-	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
+	void *state;
 	int ret = 0;
-	unsigned int fb_width, fb_height;
-	int i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-	/*
-	 * First, find the plane, crtc, and fb objects.  If not available,
-	 * we don't bother to call the driver.
-	 */
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
+
 	obj = drm_mode_object_find(dev, plane_req->plane_id,
 				   DRM_MODE_OBJECT_PLANE);
 	if (!obj) {
@@ -1912,102 +2115,36 @@  int drm_mode_setplane(struct drm_device *dev, void *data,
 			      plane_req->plane_id);
 		return -ENOENT;
 	}
-	plane = obj_to_plane(obj);
-
-	/* No fb means shut it down */
-	if (!plane_req->fb_id) {
-		drm_modeset_lock_all(dev);
-		old_fb = plane->fb;
-		plane->funcs->disable_plane(plane);
-		plane->crtc = NULL;
-		plane->fb = NULL;
-		drm_modeset_unlock_all(dev);
-		goto out;
-	}
 
-	obj = drm_mode_object_find(dev, plane_req->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
-		DRM_DEBUG_KMS("Unknown crtc ID %d\n",
-			      plane_req->crtc_id);
-		ret = -ENOENT;
-		goto out;
-	}
-	crtc = obj_to_crtc(obj);
-
-	fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
-	if (!fb) {
-		DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
-			      plane_req->fb_id);
-		ret = -ENOENT;
-		goto out;
-	}
-
-	/* Check whether this plane supports the fb pixel format. */
-	for (i = 0; i < plane->format_count; i++)
-		if (fb->pixel_format == plane->format_types[i])
-			break;
-	if (i == plane->format_count) {
-		DRM_DEBUG_KMS("Invalid pixel format %s\n",
-			      drm_get_format_name(fb->pixel_format));
-		ret = -EINVAL;
-		goto out;
-	}
-
-	fb_width = fb->width << 16;
-	fb_height = fb->height << 16;
-
-	/* Make sure source coordinates are inside the fb. */
-	if (plane_req->src_w > fb_width ||
-	    plane_req->src_x > fb_width - plane_req->src_w ||
-	    plane_req->src_h > fb_height ||
-	    plane_req->src_y > fb_height - plane_req->src_h) {
-		DRM_DEBUG_KMS("Invalid source coordinates "
-			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
-			      plane_req->src_w >> 16,
-			      ((plane_req->src_w & 0xffff) * 15625) >> 10,
-			      plane_req->src_h >> 16,
-			      ((plane_req->src_h & 0xffff) * 15625) >> 10,
-			      plane_req->src_x >> 16,
-			      ((plane_req->src_x & 0xffff) * 15625) >> 10,
-			      plane_req->src_y >> 16,
-			      ((plane_req->src_y & 0xffff) * 15625) >> 10);
-		ret = -ENOSPC;
-		goto out;
-	}
-
-	/* Give drivers some help against integer overflows */
-	if (plane_req->crtc_w > INT_MAX ||
-	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
-	    plane_req->crtc_h > INT_MAX ||
-	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
-		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
-			      plane_req->crtc_w, plane_req->crtc_h,
-			      plane_req->crtc_x, plane_req->crtc_y);
-		ret = -ERANGE;
+	ret =
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_crtc_id, plane_req->crtc_id, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_fb_id, plane_req->fb_id, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_crtc_w, plane_req->crtc_w, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_crtc_h, plane_req->crtc_h, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_src_w, plane_req->src_w, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_src_h, plane_req->src_h, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_src_x, plane_req->src_x, NULL) ||
+		drm_mode_set_obj_prop(obj, state,
+			config->prop_src_y, plane_req->src_y, NULL) ||
+		dev->driver->atomic_check(dev, state);
+	if (ret)
 		goto out;
-	}
 
-	drm_modeset_lock_all(dev);
-	ret = plane->funcs->update_plane(plane, crtc, fb,
-					 plane_req->crtc_x, plane_req->crtc_y,
-					 plane_req->crtc_w, plane_req->crtc_h,
-					 plane_req->src_x, plane_req->src_y,
-					 plane_req->src_w, plane_req->src_h);
-	if (!ret) {
-		old_fb = plane->fb;
-		plane->crtc = crtc;
-		plane->fb = fb;
-		fb = NULL;
-	}
-	drm_modeset_unlock_all(dev);
+	ret = dev->driver->atomic_commit(dev, state);
 
 out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
-	if (old_fb)
-		drm_framebuffer_unreference(old_fb);
-
+	dev->driver->atomic_end(dev, state);
 	return ret;
 }
 
@@ -3296,7 +3433,7 @@  int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
 }
 
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 					   void *state, struct drm_property *property,
 					   uint64_t value, void *blob_data)
 {
@@ -3319,8 +3456,9 @@  static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
 
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 				      void *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -3335,8 +3473,9 @@  static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
 
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 				      void *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -3345,12 +3484,10 @@  static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 	if (plane->funcs->set_property)
 		ret = plane->funcs->set_property(plane, state, property,
 				value, blob_data);
-	if (!ret)
-		drm_object_property_set_value(&plane->base, &plane->propvals,
-				property, value, NULL);
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
 
 static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
 		void *state, struct drm_property *property, 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 24898dc..f1fc605 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -290,12 +290,27 @@  bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_plane *plane;
 	bool error = false;
+	void *state;
 	int i;
 
 	drm_warn_on_modeset_not_all_locked(dev);
 
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state)) {
+		DRM_ERROR("failed to restore fbdev mode\n");
+		return true;
+	}
+
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head)
-		drm_plane_force_disable(plane);
+		drm_plane_force_disable(plane, state);
+
+	/* just disabling stuff shouldn't fail, hopefully: */
+	if(dev->driver->atomic_check(dev, state))
+		DRM_ERROR("failed to restore fbdev mode\n");
+	else
+		dev->driver->atomic_commit(dev, state);
+
+	dev->driver->atomic_end(dev, state);
 
 	for (i = 0; i < fb_helper->crtc_count; i++) {
 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 82a9fca..4ae55b8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -124,8 +124,8 @@  exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	if (ret)
 		return ret;
 
-	plane->crtc = crtc;
-	plane->fb = crtc->fb;
+	plane->state->crtc = crtc;
+	plane->state->fb = crtc->fb;
 
 	exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 06f1b2a..dbe2e19 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -127,7 +127,7 @@  static void disable_plane_to_crtc(struct drm_device *dev,
 	 * (encoder->crtc)
 	 */
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		if (plane->crtc == old_crtc) {
+		if (plane->state->crtc == old_crtc) {
 			/*
 			 * do not change below call order.
 			 *
@@ -138,7 +138,7 @@  static void disable_plane_to_crtc(struct drm_device *dev,
 			 * have new_crtc because new_crtc was set to
 			 * encoder->crtc in advance.
 			 */
-			plane->crtc = new_crtc;
+			plane->state->crtc = new_crtc;
 			plane->funcs->disable_plane(plane);
 		}
 	}
@@ -247,7 +247,7 @@  static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
 
 	/* all planes connected to this encoder should be also disabled. */
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		if (plane->crtc == encoder->crtc)
+		if (plane->state->crtc == encoder->crtc)
 			plane->funcs->disable_plane(plane);
 	}
 }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 2e31fb8..45d0fa3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -10,6 +10,7 @@ 
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 
 #include <drm/exynos_drm.h>
 #include "exynos_drm_drv.h"
@@ -149,7 +150,7 @@  void exynos_plane_commit(struct drm_plane *plane)
 	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 	struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
 
-	exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+	exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
 			exynos_drm_encoder_plane_commit);
 }
 
@@ -162,7 +163,7 @@  void exynos_plane_dpms(struct drm_plane *plane, int mode)
 		if (exynos_plane->enabled)
 			return;
 
-		exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+		exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
 				exynos_drm_encoder_plane_enable);
 
 		exynos_plane->enabled = true;
@@ -170,7 +171,7 @@  void exynos_plane_dpms(struct drm_plane *plane, int mode)
 		if (!exynos_plane->enabled)
 			return;
 
-		exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+		exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
 				exynos_drm_encoder_plane_disable);
 
 		exynos_plane->enabled = false;
@@ -192,7 +193,7 @@  exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	if (ret < 0)
 		return ret;
 
-	plane->crtc = crtc;
+	plane->state->crtc = crtc;
 
 	exynos_plane_commit(plane);
 	exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
@@ -229,6 +230,10 @@  static int exynos_plane_set_property(struct drm_plane *plane,
 	if (property == dev_priv->plane_zpos_property) {
 		exynos_plane->overlay.zpos = val;
 		return 0;
+	} else {
+		return drm_plane_set_property(plane,
+				drm_atomic_get_plane_state(plane, state),
+				property, val, blob_data);
 	}
 
 	return -EINVAL;
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index ad6ec4b..d8c2869 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -868,16 +868,17 @@  intel_disable_plane(struct drm_plane *plane)
 {
 	struct drm_device *dev = plane->dev;
 	struct intel_plane *intel_plane = to_intel_plane(plane);
+	struct drm_plane_state *state = plane->state;
 	int ret = 0;
 
-	if (!plane->fb)
+	if (!state->fb)
 		return 0;
 
-	if (WARN_ON(!plane->crtc))
+	if (WARN_ON(!state->crtc))
 		return -EINVAL;
 
-	intel_enable_primary(plane->crtc);
-	intel_plane->disable_plane(plane, plane->crtc);
+	intel_enable_primary(state->crtc);
+	intel_plane->disable_plane(plane, state->crtc);
 
 	if (!intel_plane->obj)
 		goto out;
@@ -966,11 +967,12 @@  out_unlock:
 void intel_plane_restore(struct drm_plane *plane)
 {
 	struct intel_plane *intel_plane = to_intel_plane(plane);
+	struct drm_plane_state *state = plane->state;
 
-	if (!plane->crtc || !plane->fb)
+	if (!state->crtc || !state->fb)
 		return;
 
-	intel_update_plane(plane, plane->crtc, plane->fb,
+	intel_update_plane(plane, state->crtc, state->fb,
 			   intel_plane->crtc_x, intel_plane->crtc_y,
 			   intel_plane->crtc_w, intel_plane->crtc_h,
 			   intel_plane->src_x, intel_plane->src_y,
@@ -979,7 +981,9 @@  void intel_plane_restore(struct drm_plane *plane)
 
 void intel_plane_disable(struct drm_plane *plane)
 {
-	if (!plane->crtc || !plane->fb)
+	struct drm_plane_state *state = plane->state;
+
+	if (!state->crtc || !state->fb)
 		return;
 
 	intel_disable_plane(plane);
@@ -989,6 +993,7 @@  static const struct drm_plane_funcs intel_plane_funcs = {
 	.update_plane = intel_update_plane,
 	.disable_plane = intel_disable_plane,
 	.destroy = intel_destroy_plane,
+	.set_property = drm_atomic_helper_plane_set_property,
 };
 
 static uint32_t ilk_plane_formats[] = {
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
index d3fd7ca..1d6a6ee 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -243,7 +243,7 @@  static void blend_setup(struct drm_crtc *crtc)
 			int idx = idxs[pipe_id];
 			if (idx > 0) {
 				const struct mdp4_format *format =
-					to_mdp4_format(msm_framebuffer_format(plane->fb));
+					to_mdp4_format(msm_framebuffer_format(plane->state->fb));
 				alpha[idx-1] = format->alpha_enable;
 			}
 			mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]);
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
index 880e96d..8338b1a 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
@@ -45,11 +45,14 @@  static int mdp4_plane_update(struct drm_plane *plane,
 		uint32_t src_w, uint32_t src_h)
 {
 	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
+	struct drm_plane_state *state = plane->state;
 
 	mdp4_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
+	if (state->fb) {
+		drm_framebuffer_unreference(state->fb);
+		state->fb = NULL;
+	}
 
 	drm_framebuffer_reference(fb);
 
@@ -62,8 +65,8 @@  static int mdp4_plane_disable(struct drm_plane *plane)
 {
 	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
 	DBG("%s: disable", mdp4_plane->name);
-	if (plane->crtc)
-		mdp4_crtc_detach(plane->crtc, plane);
+	if (plane->state->crtc)
+		mdp4_crtc_detach(plane->state->crtc, plane);
 	return 0;
 }
 
@@ -87,8 +90,9 @@  void mdp4_plane_install_properties(struct drm_plane *plane,
 int mdp4_plane_set_property(struct drm_plane *plane, void *state,
 		struct drm_property *property, uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	return drm_plane_set_property(plane,
+			drm_atomic_get_plane_state(plane, state),
+			property, val, blob_data);
 }
 
 static const struct drm_plane_funcs mdp4_plane_funcs = {
@@ -117,7 +121,7 @@  void mdp4_plane_set_scanout(struct drm_plane *plane,
 	msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova);
 	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova);
 
-	plane->fb = fb;
+	plane->state->fb = fb;
 }
 
 #define MDP4_VG_PHASE_STEP_DEFAULT	0x20000000
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 5dd22ab..d53a689 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -210,7 +210,7 @@  static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
 		/* and any attached overlay planes: */
 		for (i = 0; i < priv->num_planes; i++) {
 			struct drm_plane *plane = priv->planes[i];
-			if (plane->crtc == crtc)
+			if (plane->state->crtc == crtc)
 				WARN_ON(omap_plane_dpms(plane, mode));
 		}
 	}
@@ -651,7 +651,7 @@  struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 
 	omap_crtc->channel = channel;
 	omap_crtc->plane = plane;
-	omap_crtc->plane->crtc = crtc;
+	omap_crtc->plane->state->crtc = crtc;
 	omap_crtc->name = channel_names[channel];
 	omap_crtc->pipe = id;
 
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 225d0e9..0e15e2c 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -559,7 +559,7 @@  static void dev_lastclose(struct drm_device *dev)
 
 		for (i = 0; i < priv->num_planes; i++) {
 			drm_object_property_set_value(&priv->planes[i]->base,
-					&priv->planes[i]->propvals,
+					&priv->planes[i]->state->propvals,
 					priv->rotation_prop, 0, NULL);
 		}
 	}
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index fe32f1b..d11a879 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -116,9 +116,10 @@  static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 			container_of(apply, struct omap_plane, apply);
 	struct omap_drm_window *win = &omap_plane->win;
 	struct drm_plane *plane = &omap_plane->base;
+	struct drm_plane_state *state = plane->state;
 	struct drm_device *dev = plane->dev;
 	struct omap_overlay_info *info = &omap_plane->info;
-	struct drm_crtc *crtc = plane->crtc;
+	struct drm_crtc *crtc = state->crtc;
 	enum omap_channel channel;
 	bool enabled = omap_plane->enabled && crtc;
 	bool ilace, replication;
@@ -127,7 +128,7 @@  static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 	DBG("%s, enabled=%d", omap_plane->name, enabled);
 
 	/* if fb has changed, pin new fb: */
-	update_pin(plane, enabled ? plane->fb : NULL);
+	update_pin(plane, enabled ? state->fb : NULL);
 
 	if (!enabled) {
 		dispc_ovl_enable(omap_plane->id, false);
@@ -137,7 +138,7 @@  static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 	channel = omap_crtc_channel(crtc);
 
 	/* update scanout: */
-	omap_framebuffer_update_scanout(plane->fb, win, info);
+	omap_framebuffer_update_scanout(state->fb, win, info);
 
 	DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
 			info->out_width, info->out_height,
@@ -179,16 +180,18 @@  static void omap_plane_post_apply(struct omap_drm_apply *apply)
 		cb.fxn(cb.arg);
 
 	if (omap_plane->enabled) {
-		omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
+		omap_framebuffer_flush(plane->state->fb,
+				info->pos_x, info->pos_y,
 				info->out_width, info->out_height);
 	}
 }
 
 static int apply(struct drm_plane *plane)
 {
-	if (plane->crtc) {
+	struct drm_plane_state *state = plane->state;
+	if (state->crtc) {
 		struct omap_plane *omap_plane = to_omap_plane(plane);
-		return omap_crtc_apply(plane->crtc, &omap_plane->apply);
+		return omap_crtc_apply(state->crtc, &omap_plane->apply);
 	}
 	return 0;
 }
@@ -203,6 +206,7 @@  int omap_plane_mode_set(struct drm_plane *plane,
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_window *win = &omap_plane->win;
+	struct drm_plane_state *state = plane->state;
 
 	win->crtc_x = crtc_x;
 	win->crtc_y = crtc_y;
@@ -225,8 +229,8 @@  int omap_plane_mode_set(struct drm_plane *plane,
 		omap_plane->apply_done_cb.arg = arg;
 	}
 
-	plane->fb = fb;
-	plane->crtc = crtc;
+	state->fb = fb;
+	state->crtc = crtc;
 
 	return apply(plane);
 }
@@ -239,10 +243,12 @@  static int omap_plane_update(struct drm_plane *plane,
 		uint32_t src_w, uint32_t src_h)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
+	struct drm_plane_state *state = plane->state;
+
 	omap_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
+	if (state->fb)
+		drm_framebuffer_unreference(state->fb);
 
 	drm_framebuffer_reference(fb);
 
@@ -342,6 +348,10 @@  int omap_plane_set_property(struct drm_plane *plane, void *state,
 		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->info.zorder = val;
 		ret = apply(plane);
+	} else {
+		ret = drm_plane_set_property(plane,
+				drm_atomic_get_plane_state(plane, state),
+				property, val, blob_data);
 	}
 
 	return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
index 5691743..909e241 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -14,6 +14,7 @@ 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -411,7 +412,9 @@  static int rcar_du_plane_set_property(struct drm_plane *plane,
 	else if (property == rgrp->planes.zpos)
 		rcar_du_plane_set_zpos(rplane, value);
 	else
-		return -EINVAL;
+		return drm_plane_set_property(plane,
+				drm_atomic_get_plane_state(plane, state),
+				property, value, blob_data);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index 035bd67..e62b0f2 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -238,7 +238,7 @@  static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
 
 	/* Setup planes. */
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		if (plane->crtc == crtc)
+		if (plane->state->crtc == crtc)
 			shmob_drm_plane_setup(plane);
 	}
 
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
index 060ae03..22da5c1 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
@@ -14,6 +14,7 @@ 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -166,10 +167,10 @@  void shmob_drm_plane_setup(struct drm_plane *plane)
 {
 	struct shmob_drm_plane *splane = to_shmob_plane(plane);
 
-	if (plane->fb == NULL)
+	if (plane->state->fb == NULL)
 		return;
 
-	__shmob_drm_plane_setup(splane, plane->fb);
+	__shmob_drm_plane_setup(splane, plane->state->fb);
 }
 
 static int
@@ -228,6 +229,7 @@  static void shmob_drm_plane_destroy(struct drm_plane *plane)
 static const struct drm_plane_funcs shmob_drm_plane_funcs = {
 	.update_plane = shmob_drm_plane_update,
 	.disable_plane = shmob_drm_plane_disable,
+	.set_property = drm_atomic_helper_plane_set_property,
 	.destroy = shmob_drm_plane_destroy,
 };
 
diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c
index b1a05ad..5766010 100644
--- a/drivers/gpu/host1x/drm/dc.c
+++ b/drivers/gpu/host1x/drm/dc.c
@@ -75,11 +75,11 @@  static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 
 static int tegra_plane_disable(struct drm_plane *plane)
 {
-	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
+	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
 	struct tegra_plane *p = to_tegra_plane(plane);
 	unsigned long value;
 
-	if (!plane->crtc)
+	if (!plane->state->crtc)
 		return 0;
 
 	value = WINDOW_A_SELECT << p->index;
@@ -104,6 +104,7 @@  static void tegra_plane_destroy(struct drm_plane *plane)
 static const struct drm_plane_funcs tegra_plane_funcs = {
 	.update_plane = tegra_plane_update,
 	.disable_plane = tegra_plane_disable,
+	.set_property = drm_atomic_helper_plane_set_property,
 	.destroy = tegra_plane_destroy,
 };
 
@@ -267,13 +268,14 @@  static void tegra_crtc_disable(struct drm_crtc *crtc)
 	struct drm_plane *plane;
 
 	list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
-		if (plane->crtc == crtc) {
+		struct drm_plane_state *state = plane->state;
+		if (state->crtc == crtc) {
 			tegra_plane_disable(plane);
-			plane->crtc = NULL;
+			state->crtc = NULL;
 
-			if (plane->fb) {
-				drm_framebuffer_unreference(plane->fb);
-				plane->fb = NULL;
+			if (state->fb) {
+				drm_framebuffer_unreference(state->fb);
+				state->fb = NULL;
 			}
 		}
 	}
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index e70cd7b..d0d29e1 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -62,7 +62,9 @@ 
  * struct drm_atomic_helper_funcs - helper funcs used by the atomic helpers
  */
 struct drm_atomic_helper_funcs {
-	int dummy; /* for now */
+	struct drm_plane_state *(*get_plane_state)(struct drm_plane *plane, void *state);
+	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
+	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
 };
 
 const extern struct drm_atomic_helper_funcs drm_atomic_helper_funcs;
@@ -75,6 +77,37 @@  int drm_atomic_helper_check(struct drm_device *dev, void *state);
 int drm_atomic_helper_commit(struct drm_device *dev, void *state);
 void drm_atomic_helper_end(struct drm_device *dev, void *state);
 
+int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
+		struct drm_property *property, uint64_t val, void *blob_data);
+void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate, void *state);
+
+static inline struct drm_plane_state *
+drm_atomic_get_plane_state(struct drm_plane *plane, void *state)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			plane->dev->driver->atomic_helpers;
+	return funcs->get_plane_state(plane, state);
+}
+
+static inline int
+drm_atomic_check_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			plane->dev->driver->atomic_helpers;
+	return funcs->check_plane_state(plane, pstate);
+}
+
+static inline int
+drm_atomic_commit_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			plane->dev->driver->atomic_helpers;
+	return funcs->commit_plane_state(plane, pstate);
+}
+
 /**
  * struct drm_atomic_helper_state - the state object used by atomic helpers
  */
@@ -82,6 +115,8 @@  struct drm_atomic_helper_state {
 	struct kref refcount;
 	struct drm_device *dev;
 	uint32_t flags;
+	struct drm_plane **planes;
+	struct drm_plane_state **pstates;
 };
 
 static inline void
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 8831562..ee46a6a 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -675,6 +675,45 @@  struct drm_plane_funcs {
 };
 
 /**
+ * drm_plane_state - mutable plane state
+ * @new_fb: has the fb been changed
+ * @crtc: currently bound CRTC
+ * @fb: currently bound fb
+ * @crtc_x: left position of visible portion of plane on crtc
+ * @crtc_y: upper position of visible portion of plane on crtc
+ * @crtc_w: width of visible portion of plane on crtc
+ * @crtc_h: height of visible portion of plane on crtc
+ * @src_x: left position of visible portion of plane within
+ *   plane (in 16.16)
+ * @src_y: upper position of visible portion of plane within
+ *   plane (in 16.16)
+ * @src_w: width of visible portion of plane (in 16.16)
+ * @src_h: height of visible portion of plane (in 16.16)
+ * @propvals: property values
+ * @state: current global/toplevel state object (for atomic) while an
+ *    update is in progress, NULL otherwise.
+ */
+struct drm_plane_state {
+	bool new_fb            : 1;
+	struct drm_crtc *crtc;
+	struct drm_framebuffer *fb;
+
+	/* Signed dest location allows it to be partially off screen */
+	int32_t crtc_x, crtc_y;
+	uint32_t crtc_w, crtc_h;
+
+	/* Source values are 16.16 fixed point */
+	uint32_t src_x, src_y;
+	uint32_t src_h, src_w;
+
+	bool enabled;
+
+	struct drm_object_property_values propvals;
+
+	void *state;
+};
+
+/**
  * drm_plane - central DRM plane control structure
  * @dev: DRM device this plane belongs to
  * @head: for list management
@@ -682,8 +721,8 @@  struct drm_plane_funcs {
  * @possible_crtcs: pipes this plane can be bound to
  * @format_types: array of formats supported by this plane
  * @format_count: number of formats supported
- * @crtc: currently bound CRTC
- * @fb: currently bound fb
+ * @id: plane number, 0..n
+ * @state: the mutable state
  * @funcs: helper functions
  * @properties: property tracking for this plane
  */
@@ -697,13 +736,17 @@  struct drm_plane {
 	uint32_t *format_types;
 	uint32_t format_count;
 
-	struct drm_crtc *crtc;
-	struct drm_framebuffer *fb;
+	int id;
+
+	/*
+	 * State that can be updated from userspace, and atomically
+	 * commited or rolled back:
+	 */
+	struct drm_plane_state *state;
 
 	const struct drm_plane_funcs *funcs;
 
 	struct drm_object_properties properties;
-	struct drm_object_property_values propvals;
 };
 
 /**
@@ -884,8 +927,20 @@  struct drm_mode_config {
 	bool poll_running;
 	struct delayed_work output_poll_work;
 
-	/* pointers to standard properties */
+	/* just so blob properties can always be in a list: */
 	struct list_head property_blob_list;
+
+	/* pointers to standard properties */
+	struct drm_property *prop_src_x;
+	struct drm_property *prop_src_y;
+	struct drm_property *prop_src_w;
+	struct drm_property *prop_src_h;
+	struct drm_property *prop_crtc_x;
+	struct drm_property *prop_crtc_y;
+	struct drm_property *prop_crtc_w;
+	struct drm_property *prop_crtc_h;
+	struct drm_property *prop_fb_id;
+	struct drm_property *prop_crtc_id;
 	struct drm_property *edid_property;
 	struct drm_property *dpms_property;
 
@@ -969,7 +1024,15 @@  extern int drm_plane_init(struct drm_device *dev,
 			  const uint32_t *formats, uint32_t format_count,
 			  bool priv);
 extern void drm_plane_cleanup(struct drm_plane *plane);
-extern void drm_plane_force_disable(struct drm_plane *plane);
+extern void drm_plane_force_disable(struct drm_plane *plane, void *state);
+extern int drm_plane_check_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+extern void drm_plane_commit_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+extern int drm_plane_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data);
 
 extern void drm_encoder_cleanup(struct drm_encoder *encoder);
 
@@ -1023,6 +1086,17 @@  extern int drm_object_property_set_value(struct drm_mode_object *obj,
 extern int drm_object_property_get_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t *value);
+
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+					   void *state, struct drm_property *property,
+					   uint64_t value, void *blob_data);
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+				      void *state, struct drm_property *property,
+				      uint64_t value, void *blob_data);
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+				      void *state, struct drm_property *property,
+				      uint64_t value, void *blob_data);
+
 extern int drm_framebuffer_init(struct drm_device *dev,
 				struct drm_framebuffer *fb,
 				const struct drm_framebuffer_funcs *funcs);