[PATCH 1/7] drm: add atomic fxns

Daniel Vetter daniel at ffwll.ch
Wed Jul 23 14:34:00 PDT 2014


On Wed, Jul 23, 2014 at 03:38:14PM -0400, Rob Clark wrote:
> The 'atomic' mechanism allows for multiple properties to be updated,
> checked, and commited atomically.  This will be the basis of atomic-
> modeset and nuclear-pageflip.
> 
> The basic flow is:
> 
>    state = dev->atomic_begin();
>    for (... one or more ...)
>       obj->set_property(obj, state, prop, value);
>    if (dev->atomic_check(state))
>       dev->atomic_commit(state);
>    dev->atomic_end(state);
> 
> The split of check and commit steps is to allow for ioctls with a
> test-only flag (which would skip the commit step).
> 
> Signed-off-by: Rob Clark <robdclark at gmail.com>

[snip]
> +	if (flags & DRM_MODE_ATOMIC_NOLOCK)
> +		acquire_flags |= DRM_MODESET_ACQUIRE_NOLOCK;
> +	if (flags & DRM_MODE_ATOMIC_NONBLOCK)
> +		acquire_flags |= DRM_MODESET_ACQUIRE_NONBLOCK;

Just a very quick reply. Can you please remove the code which does the
NOLOCK/NONBLOCK stuff here? It's not really part of the property
conversion and I'm still not sold on those concepts.

At least I want to review them once we add them, maybe at the very end
where everything else is clear. Afaics nothing in this series actually
uses this.
-Daniel

> +
> +	drm_modeset_acquire_init(&state->acquire_ctx, acquire_flags);
> +
> +	state->dev = dev;
> +	state->flags = flags;
> +
> +	return state;
> +}
> +EXPORT_SYMBOL(drm_atomic_begin);
> +
> +/**
> + * drm_atomic_set_event - set a pending event on mode object
> + * @dev: DRM device
> + * @state: the driver state object
> + * @obj: the object to set the event on
> + * @event: the event to send back
> + *
> + * Set pending event for an update on the specified object.  The
> + * event is to be sent back to userspace after the update completes.
> + */
> +int drm_atomic_set_event(struct drm_device *dev,
> +		struct drm_atomic_state *state, struct drm_mode_object *obj,
> +		struct drm_pending_vblank_event *event)
> +{
> +	return -EINVAL;  /* for now */
> +}
> +EXPORT_SYMBOL(drm_atomic_set_event);
> +
> +/**
> + * drm_atomic_check - validate state object
> + * @dev: DRM device
> + * @state: the driver state object
> + *
> + * Check the state object to see if the requested state is
> + * physically possible.
> + *
> + * RETURNS
> + * Zero for success or -errno
> + */
> +int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
> +{
> +	struct drm_atomic_state *a = state;
> +	a->acquire_ctx.frozen = true;
> +	return 0;  /* for now */
> +}
> +EXPORT_SYMBOL(drm_atomic_check);
> +
> +/* Note that we drop and re-acquire the locks w/ ww_mutex directly,
> + * since we keep the crtc in our list with in_atomic == true.
> + */
> +
> +static void drop_locks(struct drm_atomic_state *a,
> +		struct ww_acquire_ctx *ww_ctx)
> +{
> +	struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
> +	struct drm_modeset_lock *lock;
> +
> +	mutex_lock(&ctx->mutex);
> +	list_for_each_entry(lock, &ctx->locked, head)
> +		ww_mutex_unlock(&lock->mutex);
> +	mutex_unlock(&ctx->mutex);
> +
> +	ww_acquire_fini(ww_ctx);
> +}
> +
> +static void grab_locks(struct drm_atomic_state *a,
> +		struct ww_acquire_ctx *ww_ctx)
> +{
> +	struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
> +	struct drm_modeset_lock *lock, *slow_locked, *contended;
> +	int ret;
> +
> +	lock = slow_locked = contended = NULL;
> +
> +
> +	ww_acquire_init(ww_ctx, &crtc_ww_class);
> +
> +	/*
> +	 * We need to do proper rain^Hww dance.. another context
> +	 * could sneak in a grab the lock in order to check
> +	 * crtc->in_atomic, and we get -EDEADLK.  But the winner
> +	 * will realize the mistake when it sees crtc->in_atomic
> +	 * already set, and then drop lock and return -EBUSY.
> +	 * So we just need to keep dancing until we win.
> +	 */
> +retry:
> +	ret = 0;
> +	list_for_each_entry(lock, &ctx->locked, head) {
> +		if (lock == slow_locked) {
> +			slow_locked = NULL;
> +			continue;
> +		}
> +		contended = lock;
> +		ret = ww_mutex_lock(&lock->mutex, ww_ctx);
> +		if (ret)
> +			goto fail;
> +	}
> +
> +fail:
> +	if (ret == -EDEADLK) {
> +		/* we lost out in a seqno race, backoff, lock and retry.. */
> +
> +		list_for_each_entry(lock, &ctx->locked, head) {
> +			if (lock == contended)
> +				break;
> +			ww_mutex_unlock(&lock->mutex);
> +		}
> +
> +		if (slow_locked)
> +			ww_mutex_unlock(&slow_locked->mutex);
> +
> +		ww_mutex_lock_slow(&contended->mutex, ww_ctx);
> +		slow_locked = contended;
> +		goto retry;
> +	}
> +	WARN_ON(ret);   /* if we get EALREADY then something is fubar */
> +}
> +
> +static void commit_locks(struct drm_atomic_state *a,
> +		struct ww_acquire_ctx *ww_ctx)
> +{
> +	/* and properly release them (clear in_atomic, remove from list): */
> +	drm_modeset_drop_locks(&a->acquire_ctx);
> +	ww_acquire_fini(ww_ctx);
> +	a->committed = true;
> +}
> +
> +static int atomic_commit(struct drm_atomic_state *a,
> +		struct ww_acquire_ctx *ww_ctx)
> +{
> +	int ret = 0;
> +
> +	commit_locks(a, ww_ctx);
> +
> +	return ret;
> +}
> +
> +/**
> + * drm_atomic_commit - commit state
> + * @dev: DRM device
> + * @state: the driver state object
> + *
> + * Commit the state.  This will only be called if atomic_check()
> + * succeeds.
> + *
> + * RETURNS
> + * Zero for success or -errno
> + */
> +int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *a)
> +{
> +	return atomic_commit(a, &a->acquire_ctx.ww_ctx);
> +}
> +EXPORT_SYMBOL(drm_atomic_commit);
> +
> +/**
> + * drm_atomic_commit_unlocked - like drm_atomic_commit
> + * but can be called back by driver in other thread.  Manages the lock
> + * transfer from initiating thread.
> + */
> +int drm_atomic_commit_unlocked(struct drm_device *dev,
> +		struct drm_atomic_state *a)
> +{
> +	struct ww_acquire_ctx ww_ctx;
> +	grab_locks(a, &ww_ctx);
> +	return atomic_commit(a, &ww_ctx);
> +}
> +EXPORT_SYMBOL(drm_atomic_commit_unlocked);
> +
> +/**
> + * drm_atomic_end - conclude the atomic update
> + * @dev: DRM device
> + * @state: the driver state object
> + *
> + * Release resources associated with the state object.
> + */
> +void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *a)
> +{
> +	/* if commit is happening from another thread, it will
> +	 * block grabbing locks until we drop (and not set
> +	 * a->committed until after), so this is not a race:
> +	 */
> +	if (!a->committed)
> +		drop_locks(a, &a->acquire_ctx.ww_ctx);
> +
> +	drm_atomic_state_unreference(a);
> +}
> +EXPORT_SYMBOL(drm_atomic_end);
> +
> +void _drm_atomic_state_free(struct kref *kref)
> +{
> +	struct drm_atomic_state *a =
> +		container_of(kref, struct drm_atomic_state, refcount);
> +
> +	/* in case we haven't already: */
> +	if (!a->committed) {
> +		grab_locks(a, &a->acquire_ctx.ww_ctx);
> +		commit_locks(a, &a->acquire_ctx.ww_ctx);
> +	}
> +
> +	__drm_modeset_acquire_fini(&a->acquire_ctx);
> +
> +	kfree(a);
> +}
> +EXPORT_SYMBOL(_drm_atomic_state_free);
> +
> +
> +const struct drm_atomic_funcs drm_atomic_funcs = {
> +};
> +EXPORT_SYMBOL(drm_atomic_funcs);
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 1ccf5cb..6710de3 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -38,6 +38,7 @@
>  #include <drm/drm_edid.h>
>  #include <drm/drm_fourcc.h>
>  #include <drm/drm_modeset_lock.h>
> +#include <drm/drm_atomic.h>
>  
>  #include "drm_crtc_internal.h"
>  
> @@ -4056,20 +4057,21 @@ 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_mode_object *obj,
> -					   struct drm_property *property,
> -					   uint64_t value)
> +static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> +					   struct drm_atomic_state *state, struct drm_property *property,
> +					   uint64_t value, void *blob_data)
>  {
>  	int ret = -EINVAL;
> -	struct drm_connector *connector = obj_to_connector(obj);
>  
>  	/* Do DPMS ourselves */
>  	if (property == connector->dev->mode_config.dpms_property) {
>  		if (connector->funcs->dpms)
>  			(*connector->funcs->dpms)(connector, (int)value);
>  		ret = 0;
> -	} else if (connector->funcs->set_property)
> -		ret = connector->funcs->set_property(connector, property, value);
> +	} else if (connector->funcs->set_property) {
> +		ret = connector->funcs->set_property(connector, state,
> +				property, value, blob_data);
> +	}
>  
>  	/* store the property value if successful */
>  	if (!ret)
> @@ -4077,38 +4079,90 @@ static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
>  	return ret;
>  }
>  
> -static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
> -				      struct drm_property *property,
> -				      uint64_t value)
> +static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> +				      struct drm_atomic_state *state, struct drm_property *property,
> +				      uint64_t value, void *blob_data)
>  {
>  	int ret = -EINVAL;
> -	struct drm_crtc *crtc = obj_to_crtc(obj);
>  
>  	if (crtc->funcs->set_property)
> -		ret = crtc->funcs->set_property(crtc, property, value);
> +		ret = crtc->funcs->set_property(crtc, state, property,
> +				value, blob_data);
>  	if (!ret)
> -		drm_object_property_set_value(obj, property, value);
> +		drm_object_property_set_value(&crtc->base, property, value);
>  
>  	return ret;
>  }
>  
> -static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
> -				      struct drm_property *property,
> -				      uint64_t value)
> +static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> +				      struct drm_atomic_state *state, struct drm_property *property,
> +				      uint64_t value, void *blob_data)
>  {
>  	int ret = -EINVAL;
> -	struct drm_plane *plane = obj_to_plane(obj);
>  
>  	if (plane->funcs->set_property)
> -		ret = plane->funcs->set_property(plane, property, value);
> +		ret = plane->funcs->set_property(plane, state, property,
> +				value, blob_data);
>  	if (!ret)
> -		drm_object_property_set_value(obj, property, value);
> +		drm_object_property_set_value(&plane->base, property, value);
>  
>  	return ret;
>  }
>  
> +static int drm_mode_set_obj_prop(struct drm_device *dev,
> +		struct drm_mode_object *obj, struct drm_atomic_state *state,
> +		struct drm_property *property, uint64_t value, void *blob_data)
> +{
> +	if (drm_property_change_is_valid(property, value)) {
> +		switch (obj->type) {
> +		case DRM_MODE_OBJECT_CONNECTOR:
> +			return drm_mode_connector_set_obj_prop(obj_to_connector(obj),
> +					state, property, value, blob_data);
> +		case DRM_MODE_OBJECT_CRTC:
> +			return drm_mode_crtc_set_obj_prop(obj_to_crtc(obj),
> +					state, property, value, blob_data);
> +		case DRM_MODE_OBJECT_PLANE:
> +			return drm_mode_plane_set_obj_prop(obj_to_plane(obj),
> +					state, property, value, blob_data);
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +/* call with mode_config mutex held */
> +static int drm_mode_set_obj_prop_id(struct drm_device *dev,
> +		struct drm_atomic_state *state,
> +		uint32_t obj_id, uint32_t obj_type,
> +		uint32_t prop_id, uint64_t value, void *blob_data)
> +{
> +	struct drm_mode_object *arg_obj;
> +	struct drm_property *property;
> +	int i;
> +
> +	arg_obj = drm_mode_object_find(dev, obj_id, obj_type);
> +	if (!arg_obj)
> +		return -ENOENT;
> +	if (!arg_obj->properties)
> +		return -EINVAL;
> +
> +	for (i = 0; i < arg_obj->properties->count; i++)
> +		if (arg_obj->properties->ids[i] == prop_id)
> +			break;
> +
> +	if (i == arg_obj->properties->count)
> +		return -EINVAL;
> +
> +	property = drm_property_find(dev, prop_id);
> +	if (!property)
> +		return -ENOENT;
> +
> +	return drm_mode_set_obj_prop(dev, arg_obj, state, property,
> +			value, blob_data);
> +}
> +
>  /**
> - * drm_mode_getproperty_ioctl - get the current value of a object's property
> + * drm_mode_obj_get_properties_ioctl - get the current value of a object's property
>   * @dev: DRM device
>   * @data: ioctl data
>   * @file_priv: DRM file info
> @@ -4198,58 +4252,41 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
>  				    struct drm_file *file_priv)
>  {
>  	struct drm_mode_obj_set_property *arg = data;
> -	struct drm_mode_object *arg_obj;
> -	struct drm_mode_object *prop_obj;
> -	struct drm_property *property;
> +	struct drm_mode_config *config = &dev->mode_config;
> +	struct drm_atomic_state *state;
>  	int ret = -EINVAL;
> -	int i;
>  
>  	if (!drm_core_check_feature(dev, DRIVER_MODESET))
>  		return -EINVAL;
>  
> -	drm_modeset_lock_all(dev);
> +retry:
> +	state = dev->driver->atomic_begin(dev, 0);
> +	if (IS_ERR(state))
> +		return PTR_ERR(state);
>  
> -	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
> -	if (!arg_obj) {
> -		ret = -ENOENT;
> -		goto out;
> -	}
> -	if (!arg_obj->properties)
> +	ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
> +	if (ret)
>  		goto out;
> -
> -	for (i = 0; i < arg_obj->properties->count; i++)
> -		if (arg_obj->properties->ids[i] == arg->prop_id)
> -			break;
> -
> -	if (i == arg_obj->properties->count)
> +	ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
> +	if (ret)
>  		goto out;
>  
> -	prop_obj = drm_mode_object_find(dev, arg->prop_id,
> -					DRM_MODE_OBJECT_PROPERTY);
> -	if (!prop_obj) {
> -		ret = -ENOENT;
> +	ret = drm_mode_set_obj_prop_id(dev, state,
> +			arg->obj_id, arg->obj_type,
> +			arg->prop_id, arg->value, NULL);
> +	if (ret)
>  		goto out;
> -	}
> -	property = obj_to_property(prop_obj);
>  
> -	if (!drm_property_change_is_valid(property, arg->value))
> +	ret = dev->driver->atomic_check(dev, state);
> +	if (ret)
>  		goto out;
>  
> -	switch (arg_obj->type) {
> -	case DRM_MODE_OBJECT_CONNECTOR:
> -		ret = drm_mode_connector_set_obj_prop(arg_obj, property,
> -						      arg->value);
> -		break;
> -	case DRM_MODE_OBJECT_CRTC:
> -		ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
> -		break;
> -	case DRM_MODE_OBJECT_PLANE:
> -		ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value);
> -		break;
> -	}
> +	ret = dev->driver->atomic_commit(dev, state);
>  
>  out:
> -	drm_modeset_unlock_all(dev);
> +	dev->driver->atomic_end(dev, state);
> +	if (ret == -EDEADLK)
> +		goto retry;
>  	return ret;
>  }
>  
> diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
> index 0dc57d5..6c6b292 100644
> --- a/drivers/gpu/drm/drm_modeset_lock.c
> +++ b/drivers/gpu/drm/drm_modeset_lock.c
> @@ -67,9 +67,18 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
>  	memset(ctx, 0, sizeof(*ctx));
>  	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
>  	INIT_LIST_HEAD(&ctx->locked);
> +	mutex_init(&ctx->mutex);
> +	ctx->nolock = !!(flags & DRM_MODESET_ACQUIRE_NOLOCK);
> +	ctx->nonblock = !!(flags & DRM_MODESET_ACQUIRE_NONBLOCK);
>  }
>  EXPORT_SYMBOL(drm_modeset_acquire_init);
>  
> +/* special version for atomic.. which needs to ww_acquire_fini() itself */
> +void __drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
> +{
> +	mutex_destroy(&ctx->mutex);
> +}
> +
>  /**
>   * drm_modeset_acquire_fini - cleanup acquire context
>   * @ctx: the acquire context
> @@ -77,6 +86,7 @@ EXPORT_SYMBOL(drm_modeset_acquire_init);
>  void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
>  {
>  	ww_acquire_fini(&ctx->ww_ctx);
> +	__drm_modeset_acquire_fini(ctx);
>  }
>  EXPORT_SYMBOL(drm_modeset_acquire_fini);
>  
> @@ -89,6 +99,7 @@ EXPORT_SYMBOL(drm_modeset_acquire_fini);
>  void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
>  {
>  	WARN_ON(ctx->contended);
> +	mutex_lock(&ctx->mutex);
>  	while (!list_empty(&ctx->locked)) {
>  		struct drm_modeset_lock *lock;
>  
> @@ -97,6 +108,7 @@ void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
>  
>  		drm_modeset_unlock(lock);
>  	}
> +	mutex_unlock(&ctx->mutex);
>  }
>  EXPORT_SYMBOL(drm_modeset_drop_locks);
>  
> @@ -106,8 +118,13 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
>  {
>  	int ret;
>  
> +	if (ctx->nolock)
> +		return 0;
> +
> +	WARN_ON(ctx->frozen);    /* all locks should be held by now! */
>  	WARN_ON(ctx->contended);
>  
> +retry:
>  	if (interruptible && slow) {
>  		ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx);
>  	} else if (interruptible) {
> @@ -119,6 +136,15 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
>  		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
>  	}
>  	if (!ret) {
> +		if (lock->atomic_pending) {
> +			/* some other pending update with dropped locks */
> +			ww_mutex_unlock(&lock->mutex);
> +			if (ctx->nonblock)
> +				return -EBUSY;
> +			wait_event(lock->event, !lock->atomic_pending);
> +			goto retry;
> +		}
> +		lock->atomic_pending = true;
>  		WARN_ON(!list_empty(&lock->head));
>  		list_add(&lock->head, &ctx->locked);
>  	} else if (ret == -EALREADY) {
> @@ -222,7 +248,9 @@ EXPORT_SYMBOL(drm_modeset_lock_interruptible);
>  void drm_modeset_unlock(struct drm_modeset_lock *lock)
>  {
>  	list_del_init(&lock->head);
> +	lock->atomic_pending = false;
>  	ww_mutex_unlock(&lock->mutex);
> +	wake_up_all(&lock->event);
>  }
>  EXPORT_SYMBOL(drm_modeset_unlock);
>  
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> index 95c9435..4cb016b 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> @@ -281,8 +281,10 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
>  }
>  
>  static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
> +					struct drm_atomic_state *state,
>  					struct drm_property *property,
> -					uint64_t val)
> +					uint64_t val,
> +					void *blob_data)
>  {
>  	struct drm_device *dev = crtc->dev;
>  	struct exynos_drm_private *dev_priv = dev->dev_private;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> index d82e3cb..c7e2ea5a 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
> @@ -14,6 +14,7 @@
>  #include <linux/pm_runtime.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  
>  #include <linux/anon_inodes.h>
>  #include <linux/component.h>
> @@ -338,6 +339,12 @@ static struct drm_driver exynos_drm_driver = {
>  	.dumb_create		= exynos_drm_gem_dumb_create,
>  	.dumb_map_offset	= exynos_drm_gem_dumb_map_offset,
>  	.dumb_destroy		= drm_gem_dumb_destroy,
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
>  	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
>  	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
>  	.gem_prime_export	= exynos_dmabuf_prime_export,
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> index 8371cbd..9da0935 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> @@ -212,8 +212,10 @@ static void exynos_plane_destroy(struct drm_plane *plane)
>  }
>  
>  static int exynos_plane_set_property(struct drm_plane *plane,
> +				     struct drm_atomic_state *state,
>  				     struct drm_property *property,
> -				     uint64_t val)
> +				     uint64_t val,
> +				     void *blob_data)
>  {
>  	struct drm_device *dev = plane->dev;
>  	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
> diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
> index 248c33a..4faefb7 100644
> --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
> +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
> @@ -205,8 +205,10 @@ static int cdv_intel_crt_get_modes(struct drm_connector *connector)
>  }
>  
>  static int cdv_intel_crt_set_property(struct drm_connector *connector,
> +				  struct drm_atomic_state *state,
>  				  struct drm_property *property,
> -				  uint64_t value)
> +				  uint64_t value,
> +				  void *blob_data)
>  {
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
> index a4cc0e6..54fca10 100644
> --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
> +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
> @@ -1645,8 +1645,10 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector)
>  
>  static int
>  cdv_intel_dp_set_property(struct drm_connector *connector,
> +		      struct drm_atomic_state *state,
>  		      struct drm_property *property,
> -		      uint64_t val)
> +		      uint64_t val,
> +		      void *blob_data)
>  {
>  	struct drm_psb_private *dev_priv = connector->dev->dev_private;
>  	struct gma_encoder *encoder = gma_attached_encoder(connector);
> diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
> index 4268bf2..46065de 100644
> --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
> +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
> @@ -150,8 +150,10 @@ static enum drm_connector_status cdv_hdmi_detect(
>  }
>  
>  static int cdv_hdmi_set_property(struct drm_connector *connector,
> +				       struct drm_atomic_state *state,
>  				       struct drm_property *property,
> -				       uint64_t value)
> +				       uint64_t value,
> +				       void *blob_data)
>  {
>  	struct drm_encoder *encoder = connector->encoder;
>  
> diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
> index 0b77039..79826b1 100644
> --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
> +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
> @@ -452,8 +452,10 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector)
>  }
>  
>  static int cdv_intel_lvds_set_property(struct drm_connector *connector,
> +				       struct drm_atomic_state *state,
>  				       struct drm_property *property,
> -				       uint64_t value)
> +				       uint64_t value,
> +				       void *blob_data)
>  {
>  	struct drm_encoder *encoder = connector->encoder;
>  
> diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
> index abf2248..98decea 100644
> --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c
> +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
> @@ -243,8 +243,10 @@ mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
>  }
>  
>  static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
> +				struct drm_atomic_state *state,
>  				struct drm_property *property,
> -				uint64_t value)
> +				uint64_t value,
> +				void *blob_data)
>  {
>  	struct drm_encoder *encoder = connector->encoder;
>  
> diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
> index 6e8fe9e..01c5edc 100644
> --- a/drivers/gpu/drm/gma500/psb_drv.c
> +++ b/drivers/gpu/drm/gma500/psb_drv.c
> @@ -487,6 +487,13 @@ static struct drm_driver driver = {
>  	.disable_vblank = psb_disable_vblank,
>  	.get_vblank_counter = psb_get_vblank_counter,
>  
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  	.gem_free_object = psb_gem_free_object,
>  	.gem_vm_ops = &psb_gem_vm_ops,
>  
> diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
> index 55ebe2b..413ea37 100644
> --- a/drivers/gpu/drm/gma500/psb_drv.h
> +++ b/drivers/gpu/drm/gma500/psb_drv.h
> @@ -25,6 +25,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_global.h>
>  #include <drm/gma_drm.h>
> +#include <drm/drm_atomic.h>
>  #include "psb_reg.h"
>  #include "psb_intel_drv.h"
>  #include "gma_display.h"
> diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h
> index 336bd3a..96e9759 100644
> --- a/drivers/gpu/drm/gma500/psb_intel_drv.h
> +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h
> @@ -254,8 +254,10 @@ extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder,
>  extern int psb_intel_lvds_mode_valid(struct drm_connector *connector,
>  				     struct drm_display_mode *mode);
>  extern int psb_intel_lvds_set_property(struct drm_connector *connector,
> +					struct drm_atomic_state *state,
>  					struct drm_property *property,
> -					uint64_t value);
> +					uint64_t value,
> +					void *blob_data);
>  extern void psb_intel_lvds_destroy(struct drm_connector *connector);
>  extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs;
>  
> diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
> index 88aad95..e939e62 100644
> --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
> +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
> @@ -569,8 +569,10 @@ void psb_intel_lvds_destroy(struct drm_connector *connector)
>  }
>  
>  int psb_intel_lvds_set_property(struct drm_connector *connector,
> +				       struct drm_atomic_state *state,
>  				       struct drm_property *property,
> -				       uint64_t value)
> +				       uint64_t value,
> +				       void *blob_data)
>  {
>  	struct drm_encoder *encoder = connector->encoder;
>  
> diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
> index 0be96fd..e8be7e9 100644
> --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
> +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
> @@ -1705,8 +1705,10 @@ static bool psb_intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
>  
>  static int
>  psb_intel_sdvo_set_property(struct drm_connector *connector,
> +			struct drm_atomic_state *state,
>  			struct drm_property *property,
> -			uint64_t val)
> +			uint64_t val,
> +			void *blob_data)
>  {
>  	struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector);
>  	struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector);
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index 83cb43a..c0bfa60 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -1544,6 +1544,14 @@ static struct drm_driver driver = {
>  	.dumb_create = i915_gem_dumb_create,
>  	.dumb_map_offset = i915_gem_mmap_gtt,
>  	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  	.ioctls = i915_ioctls,
>  	.fops = &i915_driver_fops,
>  	.name = DRIVER_NAME,
> diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
> index 88db4b6..3054a75 100644
> --- a/drivers/gpu/drm/i915/intel_crt.c
> +++ b/drivers/gpu/drm/i915/intel_crt.c
> @@ -753,8 +753,10 @@ out:
>  }
>  
>  static int intel_crt_set_property(struct drm_connector *connector,
> +				  struct drm_atomic_state *state,
>  				  struct drm_property *property,
> -				  uint64_t value)
> +				  uint64_t value,
> +				  void *blob_data)
>  {
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index ae3737c..9f53e10 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -3719,8 +3719,10 @@ intel_dp_detect_audio(struct drm_connector *connector)
>  
>  static int
>  intel_dp_set_property(struct drm_connector *connector,
> +		      struct drm_atomic_state *state,
>  		      struct drm_property *property,
> -		      uint64_t val)
> +		      uint64_t val,
> +		      void *blob_data)
>  {
>  	struct drm_i915_private *dev_priv = connector->dev->dev_private;
>  	struct intel_connector *intel_connector = to_intel_connector(connector);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index c5e2ac4..9145756 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -31,6 +31,7 @@
>  #include "i915_drv.h"
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drm_dp_helper.h>
>  
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 2422413..a790103 100644
> --- a/drivers/gpu/drm/i915/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> @@ -1063,8 +1063,10 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
>  
>  static int
>  intel_hdmi_set_property(struct drm_connector *connector,
> +			struct drm_atomic_state *state,
>  			struct drm_property *property,
> -			uint64_t val)
> +			uint64_t val,
> +			void *blob_data)
>  {
>  	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
>  	struct intel_digital_port *intel_dig_port =
> diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
> index c511287..4bc01a4 100644
> --- a/drivers/gpu/drm/i915/intel_lvds.c
> +++ b/drivers/gpu/drm/i915/intel_lvds.c
> @@ -480,8 +480,10 @@ static void intel_lvds_destroy(struct drm_connector *connector)
>  }
>  
>  static int intel_lvds_set_property(struct drm_connector *connector,
> +				   struct drm_atomic_state *state,
>  				   struct drm_property *property,
> -				   uint64_t value)
> +				   uint64_t value,
> +				   void *blob_data)
>  {
>  	struct intel_connector *intel_connector = to_intel_connector(connector);
>  	struct drm_device *dev = connector->dev;
> diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
> index 9350edd..12357a8 100644
> --- a/drivers/gpu/drm/i915/intel_sdvo.c
> +++ b/drivers/gpu/drm/i915/intel_sdvo.c
> @@ -2065,8 +2065,10 @@ static bool intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
>  
>  static int
>  intel_sdvo_set_property(struct drm_connector *connector,
> +			struct drm_atomic_state *state,
>  			struct drm_property *property,
> -			uint64_t val)
> +			uint64_t val,
> +			void *blob_data)
>  {
>  	struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
>  	struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
> diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
> index e211eef..7ec9bc5 100644
> --- a/drivers/gpu/drm/i915/intel_tv.c
> +++ b/drivers/gpu/drm/i915/intel_tv.c
> @@ -1445,8 +1445,10 @@ intel_tv_destroy(struct drm_connector *connector)
>  
>  
>  static int
> -intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
> -		      uint64_t val)
> +intel_tv_set_property(struct drm_connector *connector,
> +		      struct drm_atomic_state *state,
> +		      struct drm_property *property,
> +		      uint64_t val, void *blob_data)
>  {
>  	struct drm_device *dev = connector->dev;
>  	struct intel_tv *intel_tv = intel_attached_tv(connector);
> diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
> index f15ea3c..0425bdd 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_drv.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
> @@ -103,6 +103,13 @@ static struct drm_driver driver = {
>  	.dumb_create = mgag200_dumb_create,
>  	.dumb_map_offset = mgag200_dumb_mmap_offset,
>  	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
>  };
>  
>  static struct pci_driver mgag200_pci_driver = {
> diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
> index cf11ee6..c4d1600 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_drv.h
> +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
> @@ -16,6 +16,7 @@
>  #include <video/vga.h>
>  
>  #include <drm/drm_fb_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/ttm/ttm_bo_api.h>
>  #include <drm/ttm/ttm_bo_driver.h>
>  #include <drm/ttm/ttm_placement.h>
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> index 74cebb5..60d7e73 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> @@ -466,7 +466,8 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
>  }
>  
>  static int mdp4_crtc_set_property(struct drm_crtc *crtc,
> -		struct drm_property *property, uint64_t val)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	// XXX
>  	return -EINVAL;
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> index 66f33db..8c064dc 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> @@ -85,7 +85,8 @@ void mdp4_plane_install_properties(struct drm_plane *plane,
>  }
>  
>  int mdp4_plane_set_property(struct drm_plane *plane,
> -		struct drm_property *property, uint64_t val)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	// XXX
>  	return -EINVAL;
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> index ebe2e60..deb6647 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> @@ -384,7 +384,8 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
>  }
>  
>  static int mdp5_crtc_set_property(struct drm_crtc *crtc,
> -		struct drm_property *property, uint64_t val)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	// XXX
>  	return -EINVAL;
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> index f3daec4..d560e42 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> @@ -103,7 +103,8 @@ void mdp5_plane_install_properties(struct drm_plane *plane,
>  }
>  
>  int mdp5_plane_set_property(struct drm_plane *plane,
> -		struct drm_property *property, uint64_t val)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	// XXX
>  	return -EINVAL;
> diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
> index 5984ec2..81531ce1 100644
> --- a/drivers/gpu/drm/msm/msm_drv.c
> +++ b/drivers/gpu/drm/msm/msm_drv.c
> @@ -858,6 +858,12 @@ static struct drm_driver msm_driver = {
>  	.gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
>  	.gem_prime_vmap     = msm_gem_prime_vmap,
>  	.gem_prime_vunmap   = msm_gem_prime_vunmap,
> +	.atomic_begin       = drm_atomic_begin,
> +	.atomic_set_event   = drm_atomic_set_event,
> +	.atomic_check       = drm_atomic_check,
> +	.atomic_commit      = drm_atomic_commit,
> +	.atomic_end         = drm_atomic_end,
> +	.atomic_funcs       = &drm_atomic_funcs,
>  #ifdef CONFIG_DEBUG_FS
>  	.debugfs_init       = msm_debugfs_init,
>  	.debugfs_cleanup    = msm_debugfs_cleanup,
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 8a2c5fd..5c2919c 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -50,6 +50,7 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_fb_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/msm_drm.h>
>  
>  struct msm_kms;
> diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> index ab03f77..577e6aa 100644
> --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> @@ -221,8 +221,9 @@ nv10_set_params(struct nouveau_plane *plane)
>  
>  static int
>  nv_set_property(struct drm_plane *plane,
> -		struct drm_property *property,
> -		uint64_t value)
> +		  struct drm_atomic_state *state,
> +		  struct drm_property *property,
> +		  uint64_t value, void *blob_data)
>  {
>  	struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
>  
> diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
> index dbdc9ad..95eeea1 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_connector.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
> @@ -441,7 +441,8 @@ nouveau_connector_force(struct drm_connector *connector)
>  
>  static int
>  nouveau_connector_set_property(struct drm_connector *connector,
> -			       struct drm_property *property, uint64_t value)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t value, void *blob_data)
>  {
>  	struct nouveau_display *disp = nouveau_display(connector->dev);
>  	struct nouveau_connector *nv_connector = nouveau_connector(connector);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
> index 5425ffe..54341f6 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_drm.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
> @@ -859,6 +859,13 @@ driver = {
>  	.dumb_map_offset = nouveau_display_dumb_map_offset,
>  	.dumb_destroy = drm_gem_dumb_destroy,
>  
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  	.name = DRIVER_NAME,
>  	.desc = DRIVER_DESC,
>  #ifdef GIT_REVISION
> diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h
> index 7efbafa..8941696 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_drm.h
> +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
> @@ -29,6 +29,7 @@
>  #include <subdev/vm.h>
>  
>  #include <drmP.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/nouveau_drm.h>
>  
>  #include <drm/ttm/ttm_bo_api.h>
> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
> index 2d28dc3..a75934d 100644
> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
> @@ -382,7 +382,8 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
>  }
>  
>  static int omap_crtc_set_property(struct drm_crtc *crtc,
> -		struct drm_property *property, uint64_t val)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>  	struct omap_drm_private *priv = crtc->dev->dev_private;
> @@ -392,7 +393,8 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
>  				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
>  	}
>  
> -	return omap_plane_set_property(omap_crtc->plane, property, val);
> +	return omap_plane_set_property(omap_crtc->plane, state,
> +			property, val, blob_data);
>  }
>  
>  static const struct drm_crtc_funcs omap_crtc_funcs = {
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index 002b972..fe3983b 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -649,6 +649,12 @@ static struct drm_driver omap_drm_driver = {
>  		.dumb_create = omap_gem_dumb_create,
>  		.dumb_map_offset = omap_gem_dumb_map_offset,
>  		.dumb_destroy = drm_gem_dumb_destroy,
> +		.atomic_begin     = drm_atomic_begin,
> +		.atomic_set_event = drm_atomic_set_event,
> +		.atomic_check     = drm_atomic_check,
> +		.atomic_commit    = drm_atomic_commit,
> +		.atomic_end       = drm_atomic_end,
> +		.atomic_funcs     = &drm_atomic_funcs,
>  		.ioctls = ioctls,
>  		.num_ioctls = DRM_OMAP_NUM_IOCTLS,
>  		.fops = &omapdriver_fops,
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
> index 284b80f..346fc8a 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.h
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.h
> @@ -25,6 +25,7 @@
>  #include <linux/types.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/omap_drm.h>
>  #include <linux/platform_data/omap_drm.h>
>  
> @@ -178,7 +179,8 @@ int omap_plane_mode_set(struct drm_plane *plane,
>  void omap_plane_install_properties(struct drm_plane *plane,
>  		struct drm_mode_object *obj);
>  int omap_plane_set_property(struct drm_plane *plane,
> -		struct drm_property *property, uint64_t val);
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data);
>  
>  struct drm_encoder *omap_encoder_init(struct drm_device *dev,
>  		struct omap_dss_device *dssdev);
> diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
> index 3cf31ee..8ae5c49 100644
> --- a/drivers/gpu/drm/omapdrm/omap_plane.c
> +++ b/drivers/gpu/drm/omapdrm/omap_plane.c
> @@ -336,7 +336,8 @@ void omap_plane_install_properties(struct drm_plane *plane,
>  }
>  
>  int omap_plane_set_property(struct drm_plane *plane,
> -		struct drm_property *property, uint64_t val)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	struct omap_plane *omap_plane = to_omap_plane(plane);
>  	struct omap_drm_private *priv = plane->dev->dev_private;
> diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
> index b8ced08..3dcc70c 100644
> --- a/drivers/gpu/drm/qxl/qxl_display.c
> +++ b/drivers/gpu/drm/qxl/qxl_display.c
> @@ -823,8 +823,10 @@ static enum drm_connector_status qxl_conn_detect(
>  }
>  
>  static int qxl_conn_set_property(struct drm_connector *connector,
> +				   struct drm_atomic_state *state,
>  				   struct drm_property *property,
> -				   uint64_t value)
> +				   uint64_t value,
> +				   void *blob_data)
>  {
>  	DRM_DEBUG("\n");
>  	return 0;
> diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
> index 6e93663..1984c2c 100644
> --- a/drivers/gpu/drm/qxl/qxl_drv.c
> +++ b/drivers/gpu/drm/qxl/qxl_drv.c
> @@ -34,6 +34,7 @@
>  #include "drmP.h"
>  #include "drm/drm.h"
>  #include "drm_crtc_helper.h"
> +#include "drm_atomic.h"
>  #include "qxl_drv.h"
>  #include "qxl_object.h"
>  
> @@ -220,6 +221,14 @@ static struct drm_driver qxl_driver = {
>  	.dumb_create = qxl_mode_dumb_create,
>  	.dumb_map_offset = qxl_mode_dumb_mmap,
>  	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  #if defined(CONFIG_DEBUG_FS)
>  	.debugfs_init = qxl_debugfs_init,
>  	.debugfs_cleanup = qxl_debugfs_takedown,
> diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
> index c667c43..eb62df4 100644
> --- a/drivers/gpu/drm/radeon/radeon_connectors.c
> +++ b/drivers/gpu/drm/radeon/radeon_connectors.c
> @@ -409,8 +409,9 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn
>  	}
>  }
>  
> -static int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property,
> -				  uint64_t val)
> +static int radeon_connector_set_property(struct drm_connector *connector,
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
>  {
>  	struct drm_device *dev = connector->dev;
>  	struct radeon_device *rdev = dev->dev_private;
> @@ -732,8 +733,10 @@ static void radeon_connector_destroy(struct drm_connector *connector)
>  }
>  
>  static int radeon_lvds_set_property(struct drm_connector *connector,
> +				    struct drm_atomic_state *state,
>  				    struct drm_property *property,
> -				    uint64_t value)
> +				    uint64_t value,
> +				    void *blob_data)
>  {
>  	struct drm_device *dev = connector->dev;
>  	struct radeon_encoder *radeon_encoder;
> diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
> index cb14213..28c15ed 100644
> --- a/drivers/gpu/drm/radeon/radeon_drv.c
> +++ b/drivers/gpu/drm/radeon/radeon_drv.c
> @@ -34,6 +34,7 @@
>  #include "radeon_drv.h"
>  
>  #include <drm/drm_pciids.h>
> +#include <drm/drm_atomic.h>
>  #include <linux/console.h>
>  #include <linux/module.h>
>  #include <linux/pm_runtime.h>
> @@ -558,6 +559,14 @@ static struct drm_driver kms_driver = {
>  	.dumb_create = radeon_mode_dumb_create,
>  	.dumb_map_offset = radeon_mode_dumb_mmap,
>  	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  	.fops = &radeon_driver_kms_fops,
>  
>  	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> index 792fd1d..3f642a8 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
> @@ -21,6 +21,7 @@
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -175,6 +176,12 @@ static struct drm_driver rcar_du_driver = {
>  	.dumb_create		= rcar_du_dumb_create,
>  	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
>  	.dumb_destroy		= drm_gem_dumb_destroy,
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
>  	.fops			= &rcar_du_fops,
>  	.name			= "rcar-du",
>  	.desc			= "Renesas R-Car Display Unit",
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> index 3fb69d9..3a5d843 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> @@ -397,8 +397,10 @@ done:
>  }
>  
>  static int rcar_du_plane_set_property(struct drm_plane *plane,
> +				      struct drm_atomic_state *state,
>  				      struct drm_property *property,
> -				      uint64_t value)
> +				      uint64_t value,
> +				      void *blob_data)
>  {
>  	struct rcar_du_plane *rplane = to_rcar_plane(plane);
>  	struct rcar_du_group *rgrp = rplane->group;
> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
> index 82c84c7..7474ffb 100644
> --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
> +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
> @@ -21,6 +21,7 @@
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
>  #include "shmob_drm_crtc.h"
> @@ -285,6 +286,12 @@ static struct drm_driver shmob_drm_driver = {
>  	.dumb_create		= drm_gem_cma_dumb_create,
>  	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
>  	.dumb_destroy		= drm_gem_dumb_destroy,
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
>  	.fops			= &shmob_drm_fops,
>  	.name			= "shmob-drm",
>  	.desc			= "Renesas SH Mobile DRM",
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
> index 6be623b..aa62aee 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
> @@ -514,6 +514,12 @@ static struct drm_driver tilcdc_driver = {
>  	.dumb_create        = drm_gem_cma_dumb_create,
>  	.dumb_map_offset    = drm_gem_cma_dumb_map_offset,
>  	.dumb_destroy       = drm_gem_dumb_destroy,
> +	.atomic_begin       = drm_atomic_begin,
> +	.atomic_set_event   = drm_atomic_set_event,
> +	.atomic_check       = drm_atomic_check,
> +	.atomic_commit      = drm_atomic_commit,
> +	.atomic_end         = drm_atomic_end,
> +	.atomic_funcs       = &drm_atomic_funcs,
>  #ifdef CONFIG_DEBUG_FS
>  	.debugfs_init       = tilcdc_debugfs_init,
>  	.debugfs_cleanup    = tilcdc_debugfs_cleanup,
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h
> index 7596c14..3806618 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h
> @@ -31,6 +31,7 @@
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_gem_cma_helper.h>
>  #include <drm/drm_fb_cma_helper.h>
>  
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
> index 3775fd4..a422bed 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
> @@ -207,7 +207,8 @@ static struct drm_encoder *slave_connector_best_encoder(
>  }
>  
>  static int slave_connector_set_property(struct drm_connector *connector,
> -		struct drm_property *property, uint64_t value)
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t value, void *blob_data)
>  {
>  	struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
>  	return get_slave_funcs(encoder)->set_property(encoder,
> diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
> index e026a9e..6f14aad 100644
> --- a/drivers/gpu/drm/udl/udl_connector.c
> +++ b/drivers/gpu/drm/udl/udl_connector.c
> @@ -108,9 +108,9 @@ udl_best_single_encoder(struct drm_connector *connector)
>  	return drm_encoder_find(connector->dev, enc_id);
>  }
>  
> -static int udl_connector_set_property(struct drm_connector *connector,
> -				      struct drm_property *property,
> -				      uint64_t val)
> +static int udl_connector_set_property(struct drm_connector *connector, 
> +			       struct drm_atomic_state *state, struct drm_property *property,
> +			       uint64_t val, void *blob_data)
>  {
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
> index 3ddd6cd..52ade41 100644
> --- a/drivers/gpu/drm/udl/udl_drv.c
> +++ b/drivers/gpu/drm/udl/udl_drv.c
> @@ -9,6 +9,7 @@
>  #include <linux/module.h>
>  #include <drm/drm_usb.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include "udl_drv.h"
>  
>  static struct drm_driver driver;
> @@ -88,6 +89,13 @@ static struct drm_driver driver = {
>  	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
>  	.gem_prime_import = udl_gem_prime_import,
>  
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  	.name = DRIVER_NAME,
>  	.desc = DRIVER_DESC,
>  	.date = DRIVER_DATE,
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
> index f31a754..b7af574 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
> @@ -1426,6 +1426,13 @@ static struct drm_driver driver = {
>  	.prime_fd_to_handle = vmw_prime_fd_to_handle,
>  	.prime_handle_to_fd = vmw_prime_handle_to_fd,
>  
> +	.atomic_begin     = drm_atomic_begin,
> +	.atomic_set_event = drm_atomic_set_event,
> +	.atomic_check     = drm_atomic_check,
> +	.atomic_commit    = drm_atomic_commit,
> +	.atomic_end       = drm_atomic_end,
> +	.atomic_funcs     = &drm_atomic_funcs,
> +
>  	.fops = &vmwgfx_driver_fops,
>  	.name = VMWGFX_DRIVER_NAME,
>  	.desc = VMWGFX_DRIVER_DESC,
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
> index c181175..bcc1a07 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
> @@ -30,6 +30,7 @@
>  
>  #include "vmwgfx_reg.h"
>  #include <drm/drmP.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/vmwgfx_drm.h>
>  #include <drm/drm_hashtab.h>
>  #include <linux/suspend.h>
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index 991e5c8..a2227cd 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -2005,8 +2005,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
>  }
>  
>  int vmw_du_connector_set_property(struct drm_connector *connector,
> +				  struct drm_atomic_state *state,
>  				  struct drm_property *property,
> -				  uint64_t val)
> +				  uint64_t val,
> +				  void *blob_data)
>  {
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> index 8d038c3..6b02fdb 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
> @@ -141,8 +141,10 @@ vmw_du_connector_detect(struct drm_connector *connector, bool force);
>  int vmw_du_connector_fill_modes(struct drm_connector *connector,
>  				uint32_t max_width, uint32_t max_height);
>  int vmw_du_connector_set_property(struct drm_connector *connector,
> +				  struct drm_atomic_state *state,
>  				  struct drm_property *property,
> -				  uint64_t val);
> +				  uint64_t val,
> +				  void *blob_data);
>  
>  
>  /*
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index 9b6a445..350eb2e 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -946,6 +946,86 @@ struct drm_driver {
>  			    struct drm_device *dev,
>  			    uint32_t handle);
>  
> +	/*
> +	 * Atomic functions:
> +	 */
> +
> +	/**
> +	 * atomic_begin - start a sequence of atomic updates
> +	 * @dev: DRM device
> +	 * @flags: the modifier flags that userspace has requested
> +	 *
> +	 * Begin a sequence of atomic property sets.  Returns a driver
> +	 * private state object that is passed back into the various
> +	 * object's set_property() fxns, and into the remainder of the
> +	 * atomic funcs.  The state object should accumulate the changes
> +	 * from one o more set_property()'s.  At the end, the state can
> +	 * be checked, and optionally committed.
> +	 *
> +	 * RETURNS
> +	 *   a driver state object, which is passed back in to the
> +	 *   various other atomic fxns, or error (such as -EBUSY if
> +	 *   there is still a pending async update)
> +	 */
> +	struct drm_atomic_state *(*atomic_begin)(struct drm_device *dev, uint32_t flags);
> +
> +	/**
> +	 * atomic_set_event - set a pending event on mode object
> +	 * @dev: DRM device
> +	 * @state: the driver state object
> +	 * @obj: the object to set the event on
> +	 * @event: the event to send back
> +	 *
> +	 * Set pending event for an update on the specified object.  The
> +	 * event is to be sent back to userspace after the update completes.
> +	 */
> +	int (*atomic_set_event)(struct drm_device *dev,
> +			struct drm_atomic_state *state, struct drm_mode_object *obj,
> +			struct drm_pending_vblank_event *event);
> +
> +	/**
> +	 * atomic_check - validate state object
> +	 * @dev: DRM device
> +	 * @state: the driver state object
> +	 *
> +	 * Check the state object to see if the requested state is
> +	 * physically possible.
> +	 *
> +	 * RETURNS
> +	 * Zero for success or -errno
> +	 */
> +	int (*atomic_check)(struct drm_device *dev,
> +			struct drm_atomic_state *state);
> +
> +	/**
> +	 * atomic_commit - commit state
> +	 * @dev: DRM device
> +	 * @state: the driver state object
> +	 *
> +	 * Commit the state.  This will only be called if atomic_check()
> +	 * succeeds.
> +	 *
> +	 * RETURNS
> +	 * Zero for success or -errno
> +	 */
> +	int (*atomic_commit)(struct drm_device *dev,
> +			struct drm_atomic_state *state);
> +
> +	/**
> +	 * atomic_end - conclude the atomic update
> +	 * @dev: DRM device
> +	 * @state: the driver state object
> +
> +	 * Release resources associated with the state object.
> +	 */
> +	void (*atomic_end)(struct drm_device *dev,
> +			struct drm_atomic_state *state);
> +
> +	/**
> +	 * Funcs used by drm-atomic-helpers
> +	 */
> +	const struct drm_atomic_funcs *atomic_funcs;
> +
>  	/* Driver private ops for this object */
>  	const struct vm_operations_struct *gem_vm_ops;
>  
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> new file mode 100644
> index 0000000..ff72b81
> --- /dev/null
> +++ b/include/drm/drm_atomic.h
> @@ -0,0 +1,114 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Author: Rob Clark <robdclark at gmail.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +#ifndef DRM_ATOMIC_HELPER_H_
> +#define DRM_ATOMIC_HELPER_H_
> +
> +/**
> + * DOC: atomic state helpers
> + *
> + * Base helper atomic state and functions.  Drivers are free to either
> + * use these as-is, extend them, or completely replace them, in order
> + * to implement the atomic KMS API.
> + *
> + * A naive driver, with no special constraints or hw support for atomic
> + * updates may simply add the following to their driver struct:
> + *
> + *     .atomic_begin     = drm_atomic_begin,
> + *     .atomic_set_event = drm_atomic_set_event,
> + *     .atomic_check     = drm_atomic_check,
> + *     .atomic_commit    = drm_atomic_commit,
> + *     .atomic_end       = drm_atomic_end,
> + *     .atomics   = &drm_atomic_funcs,
> + *
> + * In addition, if you're plane/crtc doesn't already have it's own custom
> + * properties, then add to your plane/crtc_funcs:
> + *
> + *     .set_property     = drm_atomic_{plane,crtc}_set_property,
> + *
> + * Unlike the crtc helpers, it is intended that the atomic helpers can be
> + * used piecemeal by the drivers, either using all or overriding parts as
> + * needed.
> + *
> + * A driver which can have (for example) conflicting modes across multiple
> + * crtcs (for example, bandwidth limitations or clock/pll configuration
> + * restrictions), can simply wrap drm_atomic_check() with their own
> + * driver specific .atomic_check() function.
> + *
> + * A driver which can support true atomic updates can wrap
> + * drm_atomic_commit().
> + *
> + * A driver with custom properties should override the appropriate get_state(),
> + * check_state(), and commit_state() functions in .atomics if it uses
> + * the drm-atomic-helpers.  Otherwise it is free to use &drm_atomic_funcs
> + * as-is.
> + */
> +
> +/**
> + * struct drm_atomic_funcs - helper funcs used by the atomic helpers
> + */
> +struct drm_atomic_funcs {
> +	int dummy; /* for now */
> +};
> +
> +const extern struct drm_atomic_funcs drm_atomic_funcs;
> +
> +struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
> +		uint32_t flags);
> +int drm_atomic_set_event(struct drm_device *dev,
> +		struct drm_atomic_state *state, struct drm_mode_object *obj,
> +		struct drm_pending_vblank_event *event);
> +int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
> +int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state);
> +int drm_atomic_commit_unlocked(struct drm_device *dev,
> +		struct drm_atomic_state *state);
> +void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
> +
> +/**
> + * struct drm_atomic_state - the state object used by atomic helpers
> + */
> +struct drm_atomic_state {
> +	struct kref refcount;
> +	struct drm_device *dev;
> +	uint32_t flags;
> +
> +	bool committed;
> +	bool checked;       /* just for debugging */
> +
> +	struct drm_modeset_acquire_ctx acquire_ctx;
> +};
> +
> +static inline void
> +drm_atomic_state_reference(struct drm_atomic_state *state)
> +{
> +	kref_get(&state->refcount);
> +}
> +
> +static inline void
> +drm_atomic_state_unreference(struct drm_atomic_state *state)
> +{
> +	void _drm_atomic_state_free(struct kref *kref);
> +	kref_put(&state->refcount, _drm_atomic_state_free);
> +}
> +
> +#endif /* DRM_ATOMIC_HELPER_H_ */
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index e529b68..c8f9c28 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -219,6 +219,8 @@ struct drm_encoder;
>  struct drm_pending_vblank_event;
>  struct drm_plane;
>  struct drm_bridge;
> +struct drm_atomic_state;
> +struct drm_atomic_funcs;
>  
>  /**
>   * drm_crtc_funcs - control CRTCs for a given device
> @@ -281,7 +283,9 @@ struct drm_crtc_funcs {
>  			 uint32_t flags);
>  
>  	int (*set_property)(struct drm_crtc *crtc,
> -			    struct drm_property *property, uint64_t val);
> +			    struct drm_atomic_state *state,
> +			    struct drm_property *property, uint64_t val,
> +			    void *blob_data);
>  };
>  
>  /**
> @@ -399,8 +403,9 @@ struct drm_connector_funcs {
>  	enum drm_connector_status (*detect)(struct drm_connector *connector,
>  					    bool force);
>  	int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
> -	int (*set_property)(struct drm_connector *connector, struct drm_property *property,
> -			     uint64_t val);
> +	int (*set_property)(struct drm_connector *connector,
> +			struct drm_atomic_state *state,
> +			struct drm_property *property, uint64_t val, void *blob_data);
>  	void (*destroy)(struct drm_connector *connector);
>  	void (*force)(struct drm_connector *connector);
>  };
> @@ -574,7 +579,9 @@ struct drm_plane_funcs {
>  	void (*destroy)(struct drm_plane *plane);
>  
>  	int (*set_property)(struct drm_plane *plane,
> -			    struct drm_property *property, uint64_t val);
> +			    struct drm_atomic_state *state,
> +			    struct drm_property *property, uint64_t val,
> +			    void *blob_data);
>  };
>  
>  enum drm_plane_type {
> diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
> index 402aa7a..9226c78 100644
> --- a/include/drm/drm_modeset_lock.h
> +++ b/include/drm/drm_modeset_lock.h
> @@ -32,6 +32,9 @@ struct drm_modeset_lock;
>   * drm_modeset_acquire_ctx - locking context (see ww_acquire_ctx)
>   * @ww_ctx: base acquire ctx
>   * @contended: used internally for -EDEADLK handling
> + * @nolock: bypass locking (for emergencies)
> + * @nonblock: don't block
> + * @frozen: for debugging
>   * @locked: list of held locks
>   *
>   * Each thread competing for a set of locks must use one acquire
> @@ -42,6 +45,16 @@ struct drm_modeset_acquire_ctx {
>  
>  	struct ww_acquire_ctx ww_ctx;
>  
> +	bool nolock : 1;
> +	bool nonblock : 1;
> +
> +	/**
> +	 * Just for debugging, the context is 'frozen' in drm_atomic_check()
> +	 * to catch anyone who might be trying to acquire a lock after it is
> +	 * too late.
> +	 */
> +	bool frozen : 1;
> +
>  	/**
>  	 * Contended lock: if a lock is contended you should only call
>  	 * drm_modeset_backoff() which drops locks and slow-locks the
> @@ -53,11 +66,23 @@ struct drm_modeset_acquire_ctx {
>  	 * list of held locks (drm_modeset_lock)
>  	 */
>  	struct list_head locked;
> +
> +	/* currently simply for protecting against 'locked' list manipulation
> +	 * between original thread calling atomic->end() and driver thread
> +	 * calling back drm_atomic_commit_unlocked().
> +	 *
> +	 * Other spots are sufficiently synchronized by virtue of holding
> +	 * the lock's ww_mutex.  But during the lock/resource hand-over to the
> +	 * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
> +	 */
> +	struct mutex mutex;
>  };
>  
>  /**
>   * drm_modeset_lock - used for locking modeset resources.
>   * @mutex: resource locking
> + * @atomic_pending: is this resource part of a still-pending
> + *    atomic update
>   * @head: used to hold it's place on state->locked list when
>   *    part of an atomic update
>   *
> @@ -70,16 +95,37 @@ struct drm_modeset_lock {
>  	struct ww_mutex mutex;
>  
>  	/**
> +	 * Are we busy (pending asynchronous/NONBLOCK update)?  Any further
> +	 * asynchronous update will return -EBUSY if it also needs to acquire
> +	 * this lock.  While a synchronous update will block until the pending
> +	 * async update completes.
> +	 *
> +	 * Drivers must ensure the update is completed before sending vblank
> +	 * event to userspace.  Typically this just means don't send event
> +	 * before drm_atomic_commit_unlocked() returns.
> +	 */
> +	bool atomic_pending;
> +
> +	/**
>  	 * Resources that are locked as part of an atomic update are added
>  	 * to a list (so we know what to unlock at the end).
>  	 */
>  	struct list_head head;
> +
> +	/**
> +	 * For waiting on atomic_pending locks, if not a NONBLOCK operation.
> +	 */
> +	wait_queue_head_t event;
>  };
>  
>  extern struct ww_class crtc_ww_class;
>  
> +#define DRM_MODESET_ACQUIRE_NOLOCK     0x0001
> +#define DRM_MODESET_ACQUIRE_NONBLOCK   0x0002
> +
>  void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
>  		uint32_t flags);
> +void __drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
>  void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
>  void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
>  void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
> @@ -93,6 +139,7 @@ static inline void drm_modeset_lock_init(struct drm_modeset_lock *lock)
>  {
>  	ww_mutex_init(&lock->mutex, &crtc_ww_class);
>  	INIT_LIST_HEAD(&lock->head);
> +	init_waitqueue_head(&lock->event);
>  }
>  
>  /**
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index def54f9..81b815f 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -512,4 +512,7 @@ struct drm_mode_destroy_dumb {
>  	uint32_t handle;
>  };
>  
> +#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
> +#define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
> +
>  #endif
> -- 
> 1.9.3
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch


More information about the dri-devel mailing list