All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/7] drm: add atomic fxns
  2014-05-30 17:23 [PATCH 0/7] prepare for atomic/nuclear modeset/pageflip (vN+1) Rob Clark
@ 2014-05-30 17:23 ` Rob Clark
  0 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-05-30 17:23 UTC (permalink / raw)
  To: dri-devel

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@gmail.com>
---
 drivers/gpu/drm/Makefile                    |   2 +-
 drivers/gpu/drm/armada/armada_crtc.c        |   3 +-
 drivers/gpu/drm/armada/armada_output.c      |   3 +-
 drivers/gpu/drm/armada/armada_overlay.c     |   3 +-
 drivers/gpu/drm/ast/ast_drv.c               |   6 +
 drivers/gpu/drm/ast/ast_drv.h               |   1 +
 drivers/gpu/drm/cirrus/cirrus_drv.c         |   6 +
 drivers/gpu/drm/cirrus/cirrus_drv.h         |   1 +
 drivers/gpu/drm/drm_atomic.c                | 274 ++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_crtc.c                  | 149 +++++++++------
 drivers/gpu/drm/drm_modeset_lock.c          |  28 +++
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_drv.c     |   7 +
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_crt.c      |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_dp.c       |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   4 +-
 drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   4 +-
 drivers/gpu/drm/gma500/psb_drv.c            |   7 +
 drivers/gpu/drm/gma500/psb_drv.h            |   1 +
 drivers/gpu/drm/gma500/psb_intel_drv.h      |   4 +-
 drivers/gpu/drm/gma500/psb_intel_lvds.c     |   4 +-
 drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   4 +-
 drivers/gpu/drm/i915/i915_drv.c             |   8 +
 drivers/gpu/drm/i915/intel_crt.c            |   4 +-
 drivers/gpu/drm/i915/intel_dp.c             |   4 +-
 drivers/gpu/drm/i915/intel_drv.h            |   1 +
 drivers/gpu/drm/i915/intel_hdmi.c           |   4 +-
 drivers/gpu/drm/i915/intel_lvds.c           |   4 +-
 drivers/gpu/drm/i915/intel_sdvo.c           |   4 +-
 drivers/gpu/drm/i915/intel_tv.c             |   6 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c       |   7 +
 drivers/gpu/drm/mgag200/mgag200_drv.h       |   1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   3 +-
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   3 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   3 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   3 +-
 drivers/gpu/drm/msm/msm_drv.c               |   6 +
 drivers/gpu/drm/msm/msm_drv.h               |   1 +
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   5 +-
 drivers/gpu/drm/nouveau/nouveau_connector.c |   3 +-
 drivers/gpu/drm/nouveau/nouveau_drm.c       |   7 +
 drivers/gpu/drm/nouveau/nouveau_drm.h       |   1 +
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   6 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   6 +
 drivers/gpu/drm/omapdrm/omap_drv.h          |   4 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |   3 +-
 drivers/gpu/drm/qxl/qxl_display.c           |   4 +-
 drivers/gpu/drm/qxl/qxl_drv.c               |   9 +
 drivers/gpu/drm/radeon/radeon_connectors.c  |   9 +-
 drivers/gpu/drm/radeon/radeon_drv.c         |   9 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.c       |   7 +
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   4 +-
 drivers/gpu/drm/shmobile/shmob_drm_drv.c    |   7 +
 drivers/gpu/drm/tilcdc/tilcdc_drv.c         |   6 +
 drivers/gpu/drm/tilcdc/tilcdc_drv.h         |   1 +
 drivers/gpu/drm/tilcdc/tilcdc_slave.c       |   3 +-
 drivers/gpu/drm/udl/udl_connector.c         |   6 +-
 drivers/gpu/drm/udl/udl_drv.c               |   8 +
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.c         |   7 +
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.h         |   1 +
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c         |   4 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h         |   4 +-
 include/drm/drmP.h                          |  80 ++++++++
 include/drm/drm_atomic.h                    | 114 ++++++++++++
 include/drm/drm_crtc.h                      |  15 +-
 include/drm/drm_modeset_lock.h              |  41 +++++
 include/uapi/drm/drm_mode.h                 |   3 +
 69 files changed, 867 insertions(+), 103 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_atomic.c
 create mode 100644 include/drm/drm_atomic.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index bf4c12d..c1d5f73 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
 		drm_rect.o drm_vma_manager.o drm_flip_work.o \
-		drm_plane_helper.o drm_modeset_lock.o
+		drm_plane_helper.o drm_modeset_lock.o drm_atomic.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 81c34f9..7d3c649 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -961,7 +961,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 
 static int
 armada_drm_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 armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
index d685a54..9915306 100644
--- a/drivers/gpu/drm/armada/armada_output.c
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -54,7 +54,8 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
 }
 
 static int armada_drm_connector_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
+	struct drm_atomic_state *state, struct drm_property *property,
+	uint64_t value, void *blob_data)
 {
 	struct armada_connector *dconn = drm_to_armada_conn(conn);
 
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index c5b06fd..601ba9a 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -283,7 +283,8 @@ static void armada_plane_destroy(struct drm_plane *plane)
 }
 
 static int armada_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 armada_private *priv = plane->dev->dev_private;
 	struct armada_plane *dplane = drm_to_armada_plane(plane);
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 2ba39ac..cd81d68 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -215,6 +215,12 @@ static struct drm_driver driver = {
 	.dumb_map_offset = ast_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 int __init ast_init(void)
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 5d6a875..0d21ff8 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -29,6 +29,7 @@
 #define __AST_DRV_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>
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index 08ce520..023db21 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -137,6 +137,12 @@ static struct drm_driver driver = {
 	.dumb_create = cirrus_dumb_create,
 	.dumb_map_offset = cirrus_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 const struct dev_pm_ops cirrus_pm_ops = {
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index 117d3ec..f929bc8 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.h
@@ -14,6 +14,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>
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
new file mode 100644
index 0000000..560fe23
--- /dev/null
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@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.
+ */
+
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+
+/**
+ * drm_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 *drm_atomic_begin(struct drm_device *dev,
+		uint32_t flags)
+{
+	struct drm_atomic_state *state;
+	uint32_t acquire_flags = 0;
+	int sz;
+	void *ptr;
+
+	sz = sizeof(*state);
+
+	ptr = kzalloc(sz, GFP_KERNEL);
+
+	state = ptr;
+	ptr = &state[1];
+
+	kref_init(&state->refcount);
+
+	if (flags & DRM_MODE_ATOMIC_NOLOCK)
+		acquire_flags |= DRM_MODESET_ACQUIRE_NOLOCK;
+	if (flags & DRM_MODE_ATOMIC_NONBLOCK)
+		acquire_flags |= DRM_MODESET_ACQUIRE_NONBLOCK;
+
+	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 186bed4..a1bb8aa 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"
 
@@ -3833,20 +3834,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)
@@ -3854,38 +3856,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
@@ -3975,58 +4029,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 edd8508..38a846b 100644
--- a/drivers/gpu/drm/drm_modeset_lock.c
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -31,18 +31,29 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *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);
+}
+
 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);
 
 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;
 
@@ -51,6 +62,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);
 
@@ -60,8 +72,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) {
@@ -73,6 +90,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) {
@@ -154,7 +180,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 1ef5ab9..2a56973 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 2d27ba2..318969d 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>
 
@@ -341,6 +342,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 c18268c..6d637d3 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 9ff30c2..31d872d 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 b99084b..f0e4d78 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 8ecc920..7ffaed4 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 6e91b20..2318a46 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 59ea45e..f342ae7 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 d7778d0..6b64d9d 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 deeb082..724b525 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 284e748..a3d1291d 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1119,6 +1119,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 33328fc..0e52fc8 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -725,8 +725,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 9926dc1..caa573c 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3271,8 +3271,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 824f8ae..8c838e4 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 b606162..8e160b0 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1044,8 +1044,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 1b1541d..4028237 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -486,8 +486,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 2bf09e8..4e36ee99 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2052,8 +2052,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 97d9fc9..26dc38d 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 ef9957d..efa19c8 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -468,7 +468,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 6ea10bd..ff48944 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -386,7 +386,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 47f7bbb..5cbf226 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -100,7 +100,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 afedd8b..36b9273 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -812,6 +812,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 9d10ee0..afc990e 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 d07ce02..3351e72 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -447,7 +447,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 ddd8375..9a96d6d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -858,6 +858,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 3ab9072..b54c970 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -819,8 +819,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 bcfe5d5..b0f75a7 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -360,8 +360,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;
@@ -683,8 +684,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 15447a41..745791d 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>
@@ -545,6 +546,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 b20b694..3579b07 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -519,6 +519,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 0938036..1280a65 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 595068b..693f3ec 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
@@ -206,7 +206,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 b44d548..936244d 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -115,9 +115,9 @@ udl_best_single_encoder(struct drm_connector *connector)
 	return encoder;
 }
 
-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 6bdd15e..3ec6792 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1433,6 +1433,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 6b252a8..b0d6f3a 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 8f3edc4..c881ba1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -2007,8 +2007,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 a9b8a5d..5ac3587 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -948,6 +948,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@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 3a3f7da..4963429 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -216,6 +216,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
@@ -278,7 +280,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);
 };
 
 /**
@@ -392,8 +396,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);
 };
@@ -562,7 +567,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 cfb1dc1..39560a4 100644
--- a/include/drm/drm_modeset_lock.h
+++ b/include/drm/drm_modeset_lock.h
@@ -32,6 +32,15 @@ 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
 	 * contended lock.
@@ -40,6 +49,16 @@ struct drm_modeset_acquire_ctx {
 
 	/* list of 'struct 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;
 };
 
 /**
@@ -59,16 +78,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);
@@ -78,6 +118,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);
 }
 
 static inline void drm_modeset_lock_fini(struct drm_modeset_lock *lock)
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index ded505e..6421edc 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -511,4 +511,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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 0/7] prepare for atomic.. the great propertyification
@ 2014-07-23 19:38 Rob Clark
  2014-07-23 19:38 ` [PATCH 1/7] drm: add atomic fxns Rob Clark
                   ` (7 more replies)
  0 siblings, 8 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

This is mostly just a rebase+resend.  Was going to send it a bit earlier
but had a few things to fix up as a result of the rebase.

At this point, I think next steps are roughly:
1) introduce plane->mutex
2) decide what we want to do about events
3) add actual ioctl

I think we could shoot for merging this series next, and then adding
plane->mutex in 3.18?

Before we add the ioctl, I think we want to sort out events for updates
to non-primary layers, and what the interface to drivers should look like.
Ie. just add event to ->update_plane() or should we completely ditch
->page_flip() and ->update_plane()?

Technically, I think we could get away without a new API and just let
drivers grab all the events in their ->atomic_commit(), but I suspect
core could provide something more useful to drivers.  I guess it would
be useful to have a few more drivers converted over to see what makes
sense.

Rob Clark (5):
  drm: add atomic fxns
  drm: split propvals out and blob property support
  drm: convert plane to properties/state
  drm: convert crtc to properties/state
  drm/msm: add atomic support

Sean Paul (1):
  drm: Fix up the atomic legacy paths so they work

Ville Syrjälä (1):
  drm: Refactor object property check code

 drivers/gpu/drm/Makefile                    |    2 +-
 drivers/gpu/drm/armada/armada_crtc.c        |   14 +-
 drivers/gpu/drm/armada/armada_output.c      |    3 +-
 drivers/gpu/drm/armada/armada_overlay.c     |   14 +-
 drivers/gpu/drm/ast/ast_drv.c               |    6 +
 drivers/gpu/drm/ast/ast_drv.h               |    1 +
 drivers/gpu/drm/ast/ast_mode.c              |    1 +
 drivers/gpu/drm/cirrus/cirrus_drv.c         |    6 +
 drivers/gpu/drm/cirrus/cirrus_drv.h         |    1 +
 drivers/gpu/drm/cirrus/cirrus_mode.c        |    1 +
 drivers/gpu/drm/drm_atomic.c                |  733 +++++++++++++++
 drivers/gpu/drm/drm_crtc.c                  | 1351 ++++++++++++++++++---------
 drivers/gpu/drm/drm_fb_helper.c             |   55 +-
 drivers/gpu/drm/drm_irq.c                   |    8 +-
 drivers/gpu/drm/drm_modeset_lock.c          |   28 +
 drivers/gpu/drm/drm_plane_helper.c          |    2 +
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   11 +-
 drivers/gpu/drm/exynos/exynos_drm_drv.c     |    7 +
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |   11 +-
 drivers/gpu/drm/gma500/cdv_intel_crt.c      |    4 +-
 drivers/gpu/drm/gma500/cdv_intel_display.c  |    1 +
 drivers/gpu/drm/gma500/cdv_intel_dp.c       |    7 +-
 drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |    7 +-
 drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   10 +-
 drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   12 +-
 drivers/gpu/drm/gma500/psb_drv.c            |    7 +
 drivers/gpu/drm/gma500/psb_drv.h            |    1 +
 drivers/gpu/drm/gma500/psb_intel_display.c  |    1 +
 drivers/gpu/drm/gma500/psb_intel_drv.h      |    4 +-
 drivers/gpu/drm/gma500/psb_intel_lvds.c     |   10 +-
 drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   23 +-
 drivers/gpu/drm/i2c/ch7006_drv.c            |    4 +-
 drivers/gpu/drm/i915/i915_drv.c             |    8 +
 drivers/gpu/drm/i915/intel_crt.c            |    4 +-
 drivers/gpu/drm/i915/intel_display.c        |    6 +-
 drivers/gpu/drm/i915/intel_dp.c             |    7 +-
 drivers/gpu/drm/i915/intel_drv.h            |    1 +
 drivers/gpu/drm/i915/intel_hdmi.c           |    7 +-
 drivers/gpu/drm/i915/intel_lvds.c           |    4 +-
 drivers/gpu/drm/i915/intel_sdvo.c           |   23 +-
 drivers/gpu/drm/i915/intel_sprite.c         |    1 +
 drivers/gpu/drm/i915/intel_tv.c             |   12 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c       |    7 +
 drivers/gpu/drm/mgag200/mgag200_drv.h       |    1 +
 drivers/gpu/drm/mgag200/mgag200_mode.c      |    1 +
 drivers/gpu/drm/msm/Makefile                |    1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   66 +-
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c     |    6 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h     |    1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   14 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   65 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c     |    6 +
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h     |    2 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   14 +-
 drivers/gpu/drm/msm/msm_atomic.c            |  141 +++
 drivers/gpu/drm/msm/msm_drv.c               |   26 +
 drivers/gpu/drm/msm/msm_drv.h               |    8 +
 drivers/gpu/drm/msm/msm_gem.c               |   24 +-
 drivers/gpu/drm/msm/msm_gem.h               |   13 +
 drivers/gpu/drm/msm/msm_kms.h               |    1 +
 drivers/gpu/drm/nouveau/dispnv04/crtc.c     |    1 +
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   13 +-
 drivers/gpu/drm/nouveau/dispnv04/tvnv17.c   |    3 +-
 drivers/gpu/drm/nouveau/nouveau_connector.c |    7 +-
 drivers/gpu/drm/nouveau/nouveau_drm.c       |    7 +
 drivers/gpu/drm/nouveau/nouveau_drm.h       |    1 +
 drivers/gpu/drm/nouveau/nv50_display.c      |    1 +
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   16 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   12 +-
 drivers/gpu/drm/omapdrm/omap_drv.h          |    4 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |   10 +-
 drivers/gpu/drm/qxl/qxl_display.c           |    6 +-
 drivers/gpu/drm/qxl/qxl_drv.c               |    9 +
 drivers/gpu/drm/radeon/radeon_connectors.c  |    9 +-
 drivers/gpu/drm/radeon/radeon_display.c     |    2 +
 drivers/gpu/drm/radeon/radeon_drv.c         |    9 +
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c      |    2 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.c       |    7 +
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c   |    3 +-
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   12 +-
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c    |    3 +-
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |    6 +-
 drivers/gpu/drm/shmobile/shmob_drm_drv.c    |    7 +
 drivers/gpu/drm/shmobile/shmob_drm_plane.c  |    2 +
 drivers/gpu/drm/tilcdc/tilcdc_crtc.c        |    1 +
 drivers/gpu/drm/tilcdc/tilcdc_drv.c         |    6 +
 drivers/gpu/drm/tilcdc/tilcdc_drv.h         |    1 +
 drivers/gpu/drm/tilcdc/tilcdc_slave.c       |    3 +-
 drivers/gpu/drm/udl/udl_connector.c         |    6 +-
 drivers/gpu/drm/udl/udl_drv.c               |    8 +
 drivers/gpu/drm/udl/udl_modeset.c           |    2 +
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.c         |    7 +
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.h         |    1 +
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c         |    4 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h         |    4 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c         |    1 +
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c        |    1 +
 include/drm/drmP.h                          |   80 ++
 include/drm/drm_atomic.h                    |  170 ++++
 include/drm/drm_crtc.h                      |  238 ++++-
 include/drm/drm_modeset_lock.h              |   47 +
 include/uapi/drm/drm_mode.h                 |    3 +
 102 files changed, 2901 insertions(+), 650 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_atomic.c
 create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
 create mode 100644 include/drm/drm_atomic.h

-- 
1.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/7] drm: add atomic fxns
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-23 21:34   ` Daniel Vetter
  2014-07-24 10:02   ` Thierry Reding
  2014-07-23 19:38 ` [PATCH 2/7] drm: split propvals out and blob property support Rob Clark
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

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@gmail.com>
---
 drivers/gpu/drm/Makefile                    |   2 +-
 drivers/gpu/drm/armada/armada_crtc.c        |   3 +-
 drivers/gpu/drm/armada/armada_output.c      |   3 +-
 drivers/gpu/drm/armada/armada_overlay.c     |   3 +-
 drivers/gpu/drm/ast/ast_drv.c               |   6 +
 drivers/gpu/drm/ast/ast_drv.h               |   1 +
 drivers/gpu/drm/cirrus/cirrus_drv.c         |   6 +
 drivers/gpu/drm/cirrus/cirrus_drv.h         |   1 +
 drivers/gpu/drm/drm_atomic.c                | 274 ++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_crtc.c                  | 149 +++++++++------
 drivers/gpu/drm/drm_modeset_lock.c          |  28 +++
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_drv.c     |   7 +
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_crt.c      |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_dp.c       |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   4 +-
 drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   4 +-
 drivers/gpu/drm/gma500/psb_drv.c            |   7 +
 drivers/gpu/drm/gma500/psb_drv.h            |   1 +
 drivers/gpu/drm/gma500/psb_intel_drv.h      |   4 +-
 drivers/gpu/drm/gma500/psb_intel_lvds.c     |   4 +-
 drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   4 +-
 drivers/gpu/drm/i915/i915_drv.c             |   8 +
 drivers/gpu/drm/i915/intel_crt.c            |   4 +-
 drivers/gpu/drm/i915/intel_dp.c             |   4 +-
 drivers/gpu/drm/i915/intel_drv.h            |   1 +
 drivers/gpu/drm/i915/intel_hdmi.c           |   4 +-
 drivers/gpu/drm/i915/intel_lvds.c           |   4 +-
 drivers/gpu/drm/i915/intel_sdvo.c           |   4 +-
 drivers/gpu/drm/i915/intel_tv.c             |   6 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c       |   7 +
 drivers/gpu/drm/mgag200/mgag200_drv.h       |   1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   3 +-
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   3 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   3 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   3 +-
 drivers/gpu/drm/msm/msm_drv.c               |   6 +
 drivers/gpu/drm/msm/msm_drv.h               |   1 +
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   5 +-
 drivers/gpu/drm/nouveau/nouveau_connector.c |   3 +-
 drivers/gpu/drm/nouveau/nouveau_drm.c       |   7 +
 drivers/gpu/drm/nouveau/nouveau_drm.h       |   1 +
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   6 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   6 +
 drivers/gpu/drm/omapdrm/omap_drv.h          |   4 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |   3 +-
 drivers/gpu/drm/qxl/qxl_display.c           |   4 +-
 drivers/gpu/drm/qxl/qxl_drv.c               |   9 +
 drivers/gpu/drm/radeon/radeon_connectors.c  |   9 +-
 drivers/gpu/drm/radeon/radeon_drv.c         |   9 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.c       |   7 +
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   4 +-
 drivers/gpu/drm/shmobile/shmob_drm_drv.c    |   7 +
 drivers/gpu/drm/tilcdc/tilcdc_drv.c         |   6 +
 drivers/gpu/drm/tilcdc/tilcdc_drv.h         |   1 +
 drivers/gpu/drm/tilcdc/tilcdc_slave.c       |   3 +-
 drivers/gpu/drm/udl/udl_connector.c         |   6 +-
 drivers/gpu/drm/udl/udl_drv.c               |   8 +
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.c         |   7 +
 drivers/gpu/drm/vmwgfx/vmwgfx_drv.h         |   1 +
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c         |   4 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.h         |   4 +-
 include/drm/drmP.h                          |  80 ++++++++
 include/drm/drm_atomic.h                    | 114 ++++++++++++
 include/drm/drm_crtc.h                      |  15 +-
 include/drm/drm_modeset_lock.h              |  47 +++++
 include/uapi/drm/drm_mode.h                 |   3 +
 69 files changed, 873 insertions(+), 103 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_atomic.c
 create mode 100644 include/drm/drm_atomic.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index af9a609..78c6ffd 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
 		drm_rect.o drm_vma_manager.o drm_flip_work.o \
-		drm_modeset_lock.o
+		drm_modeset_lock.o drm_atomic.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 81c34f9..7d3c649 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -961,7 +961,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 
 static int
 armada_drm_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 armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
index abbc309..9ae31d5 100644
--- a/drivers/gpu/drm/armada/armada_output.c
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -54,7 +54,8 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
 }
 
 static int armada_drm_connector_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
+	struct drm_atomic_state *state, struct drm_property *property,
+	uint64_t value, void *blob_data)
 {
 	struct armada_connector *dconn = drm_to_armada_conn(conn);
 
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index c5b06fd..601ba9a 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -283,7 +283,8 @@ static void armada_plane_destroy(struct drm_plane *plane)
 }
 
 static int armada_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 armada_private *priv = plane->dev->dev_private;
 	struct armada_plane *dplane = drm_to_armada_plane(plane);
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 44074fb..1a2f4af 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -213,6 +213,12 @@ static struct drm_driver driver = {
 	.dumb_map_offset = ast_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 int __init ast_init(void)
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 5d6a875..0d21ff8 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -29,6 +29,7 @@
 #define __AST_DRV_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>
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index 08ce520..023db21 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -137,6 +137,12 @@ static struct drm_driver driver = {
 	.dumb_create = cirrus_dumb_create,
 	.dumb_map_offset = cirrus_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 const struct dev_pm_ops cirrus_pm_ops = {
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index 117d3ec..f929bc8 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.h
@@ -14,6 +14,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>
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
new file mode 100644
index 0000000..560fe23
--- /dev/null
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@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.
+ */
+
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+
+/**
+ * drm_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 *drm_atomic_begin(struct drm_device *dev,
+		uint32_t flags)
+{
+	struct drm_atomic_state *state;
+	uint32_t acquire_flags = 0;
+	int sz;
+	void *ptr;
+
+	sz = sizeof(*state);
+
+	ptr = kzalloc(sz, GFP_KERNEL);
+
+	state = ptr;
+	ptr = &state[1];
+
+	kref_init(&state->refcount);
+
+	if (flags & DRM_MODE_ATOMIC_NOLOCK)
+		acquire_flags |= DRM_MODESET_ACQUIRE_NOLOCK;
+	if (flags & DRM_MODE_ATOMIC_NONBLOCK)
+		acquire_flags |= DRM_MODESET_ACQUIRE_NONBLOCK;
+
+	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@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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 2/7] drm: split propvals out and blob property support
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
  2014-07-23 19:38 ` [PATCH 1/7] drm: add atomic fxns Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-23 19:38 ` [PATCH 3/7] drm: Refactor object property check code Rob Clark
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

Split property values out into a different struct, so we can later
move property values into state structs.  This will allow the
property values to stay in sync w/ the state updates which are
either discarded or atomically committed.

And since we are touching all the same code, add support for mutable
blob properties, which will also be needed for atomic modeset.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c                  | 78 +++++++++++++++++++++--------
 drivers/gpu/drm/drm_fb_helper.c             |  3 +-
 drivers/gpu/drm/gma500/cdv_intel_dp.c       |  3 +-
 drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |  3 +-
 drivers/gpu/drm/gma500/cdv_intel_lvds.c     |  6 ++-
 drivers/gpu/drm/gma500/mdfld_dsi_output.c   |  8 +--
 drivers/gpu/drm/gma500/psb_intel_lvds.c     |  6 ++-
 drivers/gpu/drm/gma500/psb_intel_sdvo.c     | 19 +++++--
 drivers/gpu/drm/i2c/ch7006_drv.c            |  4 +-
 drivers/gpu/drm/i915/intel_display.c        |  3 +-
 drivers/gpu/drm/i915/intel_dp.c             |  3 +-
 drivers/gpu/drm/i915/intel_hdmi.c           |  3 +-
 drivers/gpu/drm/i915/intel_sdvo.c           | 19 +++++--
 drivers/gpu/drm/i915/intel_tv.c             |  6 ++-
 drivers/gpu/drm/nouveau/dispnv04/tvnv17.c   |  3 +-
 drivers/gpu/drm/nouveau/nouveau_connector.c |  4 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |  6 ++-
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c   |  3 +-
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c    |  3 +-
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |  4 +-
 include/drm/drm_crtc.h                      | 11 +++-
 21 files changed, 142 insertions(+), 56 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 6710de3..82d4fb4 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -141,6 +141,14 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 
+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);
+static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev,
+		int length, void *data);
+static void drm_property_destroy_blob(struct drm_device *dev,
+		struct drm_property_blob *blob);
+
 /* Avoid boilerplate.  I'm tired of typing. */
 #define DRM_ENUM_NAME_FN(fnname, list)				\
 	const char *fnname(int val)				\
@@ -748,6 +756,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 		goto out;
 
 	crtc->base.properties = &crtc->properties;
+	crtc->base.propvals = &crtc->propvals;
 
 	list_add_tail(&crtc->head, &config->crtc_list);
 	config->num_crtc++;
@@ -855,6 +864,7 @@ int drm_connector_init(struct drm_device *dev,
 		goto out_unlock;
 
 	connector->base.properties = &connector->properties;
+	connector->base.propvals = &connector->propvals;
 	connector->dev = dev;
 	connector->funcs = funcs;
 	connector->connector_type = connector_type;
@@ -1143,6 +1153,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		goto out;
 
 	plane->base.properties = &plane->properties;
+	plane->base.propvals = &plane->propvals;
 	plane->dev = dev;
 	plane->funcs = funcs;
 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
@@ -1978,7 +1989,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 				goto out;
 			}
 
-			if (put_user(connector->properties.values[i],
+			if (put_user(connector->propvals.values[i],
 				     prop_values + copied)) {
 				ret = -EFAULT;
 				goto out;
@@ -3655,7 +3666,7 @@ void drm_object_attach_property(struct drm_mode_object *obj,
 	}
 
 	obj->properties->ids[count] = property->base.id;
-	obj->properties->values[count] = init_val;
+	obj->propvals->values[count] = init_val;
 	obj->properties->count++;
 }
 EXPORT_SYMBOL(drm_object_attach_property);
@@ -3674,13 +3685,27 @@ EXPORT_SYMBOL(drm_object_attach_property);
  * Zero on success, error code on failure.
  */
 int drm_object_property_set_value(struct drm_mode_object *obj,
-				  struct drm_property *property, uint64_t val)
+				  struct drm_object_property_values *propvals,
+				  struct drm_property *property, uint64_t val,
+				  void *blob_data)
 {
 	int i;
 
 	for (i = 0; i < obj->properties->count; i++) {
 		if (obj->properties->ids[i] == property->base.id) {
-			obj->properties->values[i] = val;
+			struct drm_device *dev = property->dev;
+			if (property->flags & DRM_MODE_PROP_BLOB) {
+				struct drm_property_blob *blob, *old_blob = NULL;
+				old_blob = drm_property_blob_find(dev, propvals->values[i]);
+				blob = drm_property_create_blob(dev, val, blob_data);
+				if (!blob)
+					return -ENOMEM;
+				propvals->values[i] = blob->base.id;
+				if (old_blob)
+					drm_property_destroy_blob(dev, old_blob);
+			} else {
+				propvals->values[i] = val;
+			}
 			return 0;
 		}
 	}
@@ -3710,7 +3735,7 @@ int drm_object_property_get_value(struct drm_mode_object *obj,
 
 	for (i = 0; i < obj->properties->count; i++) {
 		if (obj->properties->ids[i] == property->base.id) {
-			*val = obj->properties->values[i];
+			*val = obj->propvals->values[i];
 			return 0;
 		}
 	}
@@ -3929,8 +3954,9 @@ int drm_mode_connector_set_path_property(struct drm_connector *connector,
 		return -EINVAL;
 
 	ret = drm_object_property_set_value(&connector->base,
+					    &connector->propvals,
 					    dev->mode_config.path_property,
-					    connector->path_blob_ptr->base.id);
+					    connector->path_blob_ptr->base.id, NULL);
 	return ret;
 }
 EXPORT_SYMBOL(drm_mode_connector_set_path_property);
@@ -3950,7 +3976,9 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 					    struct edid *edid)
 {
 	struct drm_device *dev = connector->dev;
-	int ret, size;
+	struct drm_mode_object *obj = &connector->base;
+	struct drm_property *edid_prop = dev->mode_config.edid_property;
+	int i, ret, size;
 
 	/* ignore requests to set edid when overridden */
 	if (connector->override_edid)
@@ -3961,20 +3989,26 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 
 	/* Delete edid, when there is none. */
 	if (!edid) {
-		connector->edid_blob_ptr = NULL;
-		ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0);
+		ret = drm_object_property_set_value(obj,
+				&connector->propvals, edid_prop, 0, NULL);
 		return ret;
 	}
 
 	size = EDID_LENGTH * (1 + edid->extensions);
-	connector->edid_blob_ptr = drm_property_create_blob(connector->dev,
-							    size, edid);
-	if (!connector->edid_blob_ptr)
-		return -EINVAL;
 
-	ret = drm_object_property_set_value(&connector->base,
-					       dev->mode_config.edid_property,
-					       connector->edid_blob_ptr->base.id);
+	ret = drm_object_property_set_value(obj,
+			&connector->propvals, edid_prop, size, edid);
+	if (ret)
+		return ret;
+
+	/* find the blob object created for us by drm_object_property_set_value(): */
+	for (i = 0; i < obj->properties->count; i++) {
+		if (obj->properties->ids[i] == edid_prop->base.id) {
+			connector->edid_blob_ptr = drm_property_blob_find(dev,
+					connector->propvals.values[i]);
+			break;
+		}
+	}
 
 	return ret;
 }
@@ -4075,7 +4109,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 
 	/* store the property value if successful */
 	if (!ret)
-		drm_object_property_set_value(&connector->base, property, value);
+		drm_object_property_set_value(&connector->base,
+				&connector->propvals, property, value, blob_data);
+
 	return ret;
 }
 
@@ -4089,7 +4125,8 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 		ret = crtc->funcs->set_property(crtc, state, property,
 				value, blob_data);
 	if (!ret)
-		drm_object_property_set_value(&crtc->base, property, value);
+		drm_object_property_set_value(&crtc->base, &crtc->propvals,
+				property, value, NULL);
 
 	return ret;
 }
@@ -4104,7 +4141,8 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 		ret = plane->funcs->set_property(plane, state, property,
 				value, blob_data);
 	if (!ret)
-		drm_object_property_set_value(&plane->base, property, value);
+		drm_object_property_set_value(&plane->base, &plane->propvals,
+				property, value, NULL);
 
 	return ret;
 }
@@ -4218,7 +4256,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
 				ret = -EFAULT;
 				goto out;
 			}
-			if (put_user(obj->properties->values[i],
+			if (put_user(obj->propvals->values[i],
 				     prop_values_ptr + copied)) {
 				ret = -EFAULT;
 				goto out;
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 3144db9..ecce98d 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -539,7 +539,8 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
 			connector = fb_helper->connector_info[j]->connector;
 			connector->funcs->dpms(connector, dpms_mode);
 			drm_object_property_set_value(&connector->base,
-				dev->mode_config.dpms_property, dpms_mode);
+				&connector->propvals,
+				dev->mode_config.dpms_property, dpms_mode, NULL);
 		}
 	}
 	drm_modeset_unlock_all(dev);
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index 54fca10..dacb265 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1655,7 +1655,8 @@ cdv_intel_dp_set_property(struct drm_connector *connector,
 	struct cdv_intel_dp *intel_dp = encoder->dev_priv;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			&connector->propvals, property, val, blob_data);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index 46065de..08ac02c 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -184,7 +184,8 @@ static int cdv_hdmi_set_property(struct drm_connector *connector,
 			return 0;
 
 		if (drm_object_property_set_value(&connector->base,
-							property, value))
+							&connector->propvals,
+							property, value, blob_data))
 			return -1;
 
 		centre = (curValue == DRM_MODE_SCALE_NO_SCALE) ||
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 79826b1..92fce06 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -486,8 +486,9 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
 			return 0;
 
 		if (drm_object_property_set_value(&connector->base,
+							&connector->propvals,
 							property,
-							value))
+							value, blob_data))
 			return -1;
 
 		if (crtc->saved_mode.hdisplay != 0 &&
@@ -501,8 +502,9 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
 		}
 	} else if (!strcmp(property->name, "backlight") && encoder) {
 		if (drm_object_property_set_value(&connector->base,
+							&connector->propvals,
 							property,
-							value))
+							value, blob_data))
 			return -1;
 		else
                         gma_backlight_set(encoder->dev, value);
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
index 98decea..440f090 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
@@ -276,7 +276,8 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
 			goto set_prop_done;
 
 		if (drm_object_property_set_value(&connector->base,
-							property, value))
+							&connector->propvals,
+							property, value, blob_data))
 			goto set_prop_error;
 
 		centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
@@ -300,8 +301,9 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
 			}
 		}
 	} else if (!strcmp(property->name, "backlight") && encoder) {
-		if (drm_object_property_set_value(&connector->base, property,
-									value))
+		if (drm_object_property_set_value(&connector->base,
+							&connector->propvals,
+							property, value, blob_data))
 			goto set_prop_error;
 		else
 			gma_backlight_set(encoder->dev, value);
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index e939e62..b333c36 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -606,8 +606,9 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
 			goto set_prop_done;
 
 		if (drm_object_property_set_value(&connector->base,
+							&connector->propvals,
 							property,
-							value))
+							value, blob_data))
 			goto set_prop_error;
 
 		if (crtc->saved_mode.hdisplay != 0 &&
@@ -621,8 +622,9 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
 		}
 	} else if (!strcmp(property->name, "backlight")) {
 		if (drm_object_property_set_value(&connector->base,
+							&connector->propvals,
 							property,
-							value))
+							value, blob_data))
 			goto set_prop_error;
 		else
                         gma_backlight_set(encoder->dev, value);
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index e8be7e9..900fadd 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -1717,7 +1717,8 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
 	uint8_t cmd;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			&connector->propvals, property, val, blob_data);
 	if (ret)
 		return ret;
 
@@ -1773,7 +1774,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
 		temp_value = val;
 		if (psb_intel_sdvo_connector->left == property) {
 			drm_object_property_set_value(&connector->base,
-							 psb_intel_sdvo_connector->right, val);
+							 &connector->propvals,
+							 psb_intel_sdvo_connector->right,
+							 val, blob_data);
 			if (psb_intel_sdvo_connector->left_margin == temp_value)
 				return 0;
 
@@ -1785,7 +1788,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
 			goto set_value;
 		} else if (psb_intel_sdvo_connector->right == property) {
 			drm_object_property_set_value(&connector->base,
-							 psb_intel_sdvo_connector->left, val);
+							 &connector->propvals,
+							 psb_intel_sdvo_connector->left,
+							 val, blob_data);
 			if (psb_intel_sdvo_connector->right_margin == temp_value)
 				return 0;
 
@@ -1797,7 +1802,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
 			goto set_value;
 		} else if (psb_intel_sdvo_connector->top == property) {
 			drm_object_property_set_value(&connector->base,
-							 psb_intel_sdvo_connector->bottom, val);
+							 &connector->propvals,
+							 psb_intel_sdvo_connector->bottom,
+							 val, blob_data);
 			if (psb_intel_sdvo_connector->top_margin == temp_value)
 				return 0;
 
@@ -1809,7 +1816,9 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
 			goto set_value;
 		} else if (psb_intel_sdvo_connector->bottom == property) {
 			drm_object_property_set_value(&connector->base,
-							 psb_intel_sdvo_connector->top, val);
+							 &connector->propvals,
+							 psb_intel_sdvo_connector->top,
+							 val, blob_data);
 			if (psb_intel_sdvo_connector->bottom_margin == temp_value)
 				return 0;
 
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
index 51fa323..a89c205 100644
--- a/drivers/gpu/drm/i2c/ch7006_drv.c
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -214,9 +214,9 @@ static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encod
 	else
 		priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
 
-	drm_object_property_set_value(&connector->base,
+	drm_object_property_set_value(&connector->base, &connector->propvals,
 			encoder->dev->mode_config.tv_subconnector_property,
-							priv->subconnector);
+			priv->subconnector, NULL);
 
 	return priv->subconnector ? connector_status_connected :
 					connector_status_disconnected;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index ac1de69..4fa9dd1 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10285,8 +10285,9 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
 
 			connector->dpms = DRM_MODE_DPMS_ON;
 			drm_object_property_set_value(&connector->base,
+							 &connector->propvals,
 							 dpms_property,
-							 DRM_MODE_DPMS_ON);
+							 DRM_MODE_DPMS_ON, NULL);
 
 			intel_encoder = to_intel_encoder(connector->encoder);
 			intel_encoder->connectors_active = true;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 9f53e10..5420f97 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3730,7 +3730,8 @@ intel_dp_set_property(struct drm_connector *connector,
 	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			&connector->propvals, property, val, blob_data);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index a790103..0fa5ea8 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1074,7 +1074,8 @@ intel_hdmi_set_property(struct drm_connector *connector,
 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			&connector->propvals, property, val, blob_data);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 12357a8..b975608 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2077,7 +2077,8 @@ intel_sdvo_set_property(struct drm_connector *connector,
 	uint8_t cmd;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			&connector->propvals, property, val, blob_data);
 	if (ret)
 		return ret;
 
@@ -2154,7 +2155,9 @@ intel_sdvo_set_property(struct drm_connector *connector,
 		temp_value = val;
 		if (intel_sdvo_connector->left == property) {
 			drm_object_property_set_value(&connector->base,
-							 intel_sdvo_connector->right, val);
+							 &connector->propvals,
+							 intel_sdvo_connector->right,
+							 val, blob_data);
 			if (intel_sdvo_connector->left_margin == temp_value)
 				return 0;
 
@@ -2166,7 +2169,9 @@ intel_sdvo_set_property(struct drm_connector *connector,
 			goto set_value;
 		} else if (intel_sdvo_connector->right == property) {
 			drm_object_property_set_value(&connector->base,
-							 intel_sdvo_connector->left, val);
+							 &connector->propvals,
+							 intel_sdvo_connector->left,
+							 val, blob_data);
 			if (intel_sdvo_connector->right_margin == temp_value)
 				return 0;
 
@@ -2178,7 +2183,9 @@ intel_sdvo_set_property(struct drm_connector *connector,
 			goto set_value;
 		} else if (intel_sdvo_connector->top == property) {
 			drm_object_property_set_value(&connector->base,
-							 intel_sdvo_connector->bottom, val);
+							 &connector->propvals,
+							 intel_sdvo_connector->bottom,
+							 val, blob_data);
 			if (intel_sdvo_connector->top_margin == temp_value)
 				return 0;
 
@@ -2190,7 +2197,9 @@ intel_sdvo_set_property(struct drm_connector *connector,
 			goto set_value;
 		} else if (intel_sdvo_connector->bottom == property) {
 			drm_object_property_set_value(&connector->base,
-							 intel_sdvo_connector->top, val);
+							 &connector->propvals,
+							 intel_sdvo_connector->top,
+							 val, blob_data);
 			if (intel_sdvo_connector->bottom_margin == temp_value)
 				return 0;
 
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 7ec9bc5..8c24453 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1297,7 +1297,8 @@ static void intel_tv_find_better_format(struct drm_connector *connector)
 
 	intel_tv->tv_format = tv_mode->name;
 	drm_object_property_set_value(&connector->base,
-		connector->dev->mode_config.tv_mode_property, i);
+		&connector->propvals,
+		connector->dev->mode_config.tv_mode_property, i, NULL);
 }
 
 /**
@@ -1456,7 +1457,8 @@ intel_tv_set_property(struct drm_connector *connector,
 	int ret = 0;
 	bool changed = false;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			 &connector->propvals, property, val, blob_data);
 	if (ret < 0)
 		goto out;
 
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
index 195bd8e..14b0c1e 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
@@ -196,8 +196,9 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
 	}
 
 	drm_object_property_set_value(&connector->base,
+					 &connector->propvals,
 					 conf->tv_subconnector_property,
-					 tv_enc->subconnector);
+					 tv_enc->subconnector, NULL);
 
 	if (!reliable) {
 		return connector_status_unknown;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 95eeea1..4615460 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -229,10 +229,12 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
 
 	if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
 		drm_object_property_set_value(&connector->base,
+			&connector->propvals,
 			dev->mode_config.dvi_i_subconnector_property,
 			nv_encoder->dcb->type == DCB_OUTPUT_TMDS ?
 			DRM_MODE_SUBCONNECTOR_DVID :
-			DRM_MODE_SUBCONNECTOR_DVIA);
+			DRM_MODE_SUBCONNECTOR_DVIA,
+			NULL);
 	}
 }
 
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index fe3983b..5ca0a7c 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -579,12 +579,14 @@ static void dev_lastclose(struct drm_device *dev)
 		 */
 		for (i = 0; i < priv->num_crtcs; i++) {
 			drm_object_property_set_value(&priv->crtcs[i]->base,
-					priv->rotation_prop, 0);
+					&priv->crtcs[i]->propvals,
+					priv->rotation_prop, 0, NULL);
 		}
 
 		for (i = 0; i < priv->num_planes; i++) {
 			drm_object_property_set_value(&priv->planes[i]->base,
-					priv->rotation_prop, 0);
+					&priv->planes[i]->propvals,
+					priv->rotation_prop, 0, NULL);
 		}
 	}
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index 21426bd..3f17e08 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -111,7 +111,8 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
 
 	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
 	drm_object_property_set_value(&connector->base,
-		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+		&connector->propvals,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
 
 	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
 	if (ret < 0)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 8af3944..b6bd7b9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -76,7 +76,8 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
 
 	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
 	drm_object_property_set_value(&connector->base,
-		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+		&connector->propvals,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
 
 	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
 	if (ret < 0)
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index 47875de..78b2a86 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -741,8 +741,8 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev,
 	connector->encoder = encoder;
 
 	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-	drm_object_property_set_value(&connector->base,
-		sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+	drm_object_property_set_value(&connector->base, &connector->propvals,
+		sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
 
 	return 0;
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index c8f9c28..a0ee418 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -41,6 +41,7 @@ struct drm_framebuffer;
 struct drm_object_properties;
 struct drm_file;
 struct drm_clip_rect;
+struct drm_object_property_values;
 
 #define DRM_MODE_OBJECT_CRTC 0xcccccccc
 #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
@@ -57,12 +58,16 @@ struct drm_mode_object {
 	uint32_t id;
 	uint32_t type;
 	struct drm_object_properties *properties;
+	struct drm_object_property_values *propvals;
 };
 
 #define DRM_OBJECT_MAX_PROPERTY 24
 struct drm_object_properties {
 	int count;
 	uint32_t ids[DRM_OBJECT_MAX_PROPERTY];
+};
+
+struct drm_object_property_values {
 	uint64_t values[DRM_OBJECT_MAX_PROPERTY];
 };
 
@@ -369,6 +374,7 @@ struct drm_crtc {
 	void *helper_private;
 
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 };
 
 
@@ -532,6 +538,7 @@ struct drm_connector {
 
 	struct drm_property_blob *edid_blob_ptr;
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 
 	struct drm_property_blob *path_blob_ptr;
 
@@ -620,6 +627,7 @@ struct drm_plane {
 	const struct drm_plane_funcs *funcs;
 
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 
 	enum drm_plane_type type;
 };
@@ -988,8 +996,9 @@ static inline bool drm_property_type_valid(struct drm_property *property)
 }
 
 extern int drm_object_property_set_value(struct drm_mode_object *obj,
+					 struct drm_object_property_values *propvals,
 					 struct drm_property *property,
-					 uint64_t val);
+					 uint64_t val, void *blob_data);
 extern int drm_object_property_get_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t *value);
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 3/7] drm: Refactor object property check code
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
  2014-07-23 19:38 ` [PATCH 1/7] drm: add atomic fxns Rob Clark
  2014-07-23 19:38 ` [PATCH 2/7] drm: split propvals out and blob property support Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-23 19:38 ` [PATCH 4/7] drm: convert plane to properties/state Rob Clark
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

From: Ville Syrjälä <ville.syrjala@linux.intel.com>

Refactor the code to check whether an object has a specific property
to a new function.

v1: original
v2: rebase on atomic -- Rob Clark
v3: EINVAL->ENOENT

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 82d4fb4..8c41db6 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -4168,6 +4168,19 @@ static int drm_mode_set_obj_prop(struct drm_device *dev,
 	return -EINVAL;
 }
 
+static bool object_has_prop(const struct drm_mode_object *obj, u32 prop_id)
+{
+	int i;
+
+	if (!obj->properties)
+		return false;
+
+	for (i = 0; i < obj->properties->count; i++)
+		if (obj->properties->ids[i] == prop_id)
+			return true;
+	return false;
+}
+
 /* call with mode_config mutex held */
 static int drm_mode_set_obj_prop_id(struct drm_device *dev,
 		struct drm_atomic_state *state,
@@ -4176,20 +4189,10 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
 {
 	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)
+	if (!(arg_obj && object_has_prop(arg_obj, prop_id)))
 		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)
-- 
1.9.3

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 4/7] drm: convert plane to properties/state
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
                   ` (2 preceding siblings ...)
  2014-07-23 19:38 ` [PATCH 3/7] drm: Refactor object property check code Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-24  0:42   ` Matt Roper
  2014-07-23 19:38 ` [PATCH 5/7] drm: convert crtc " Rob Clark
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

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).

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/armada/armada_overlay.c    |  11 +-
 drivers/gpu/drm/drm_atomic.c               | 235 +++++++++++++-
 drivers/gpu/drm/drm_crtc.c                 | 502 ++++++++++++++++++++---------
 drivers/gpu/drm/drm_fb_helper.c            |  30 +-
 drivers/gpu/drm/drm_plane_helper.c         |   2 +
 drivers/gpu/drm/exynos/exynos_drm_plane.c  |   7 +-
 drivers/gpu/drm/i915/intel_sprite.c        |   1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c  |   6 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c  |   6 +-
 drivers/gpu/drm/nouveau/dispnv04/overlay.c |   8 +-
 drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
 drivers/gpu/drm/omapdrm/omap_plane.c       |   7 +
 drivers/gpu/drm/rcar-du/rcar_du_plane.c    |   8 +-
 drivers/gpu/drm/shmobile/shmob_drm_plane.c |   2 +
 include/drm/drm_atomic.h                   |  29 +-
 include/drm/drm_crtc.h                     | 112 ++++++-
 16 files changed, 789 insertions(+), 179 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 601ba9a..041ea89 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -7,6 +7,7 @@
  * published by the Free Software Foundation.
  */
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include "armada_crtc.h"
 #include "armada_drm.h"
 #include "armada_fb.h"
@@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane,
 {
 	struct armada_private *priv = plane->dev->dev_private;
 	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
 	bool update_attr = false;
+	int ret = 0;
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == priv->colorkey_prop) {
 #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
@@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane,
 	} else if (property == priv->saturation_prop) {
 		dplane->prop.saturation = val;
 		update_attr = true;
+	} else {
+		ret = drm_plane_set_property(plane, pstate, property,
+				val, blob_data);
 	}
 
 	if (update_attr && dplane->base.crtc)
 		armada_ovl_update_attr(&dplane->prop,
 				       drm_to_armada_crtc(dplane->base.crtc));
 
-	return 0;
+	return ret;
 }
 
 static const struct drm_plane_funcs armada_plane_funcs = {
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 560fe23..5a9916c 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -24,6 +24,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
+#include <drm/drm_plane_helper.h>
 
 /**
  * drm_atomic_begin - start a sequence of atomic updates
@@ -47,10 +48,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 {
 	struct drm_atomic_state *state;
 	uint32_t acquire_flags = 0;
+	int nplanes = dev->mode_config.num_total_plane;
 	int sz;
 	void *ptr;
 
 	sz = sizeof(*state);
+	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
 
 	ptr = kzalloc(sz, GFP_KERNEL);
 
@@ -69,6 +72,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 	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_begin);
@@ -105,8 +114,20 @@ EXPORT_SYMBOL(drm_atomic_set_event);
 int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
 	struct drm_atomic_state *a = state;
+	int nplanes = dev->mode_config.num_total_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;
+		}
+	}
+
 	a->acquire_ctx.frozen = true;
-	return 0;  /* for now */
+
+	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_check);
 
@@ -184,6 +205,18 @@ fail:
 static void commit_locks(struct drm_atomic_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
+	struct drm_device *dev = a->dev;
+	int nplanes = dev->mode_config.num_total_plane;
+	int i;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			plane->state->state = NULL;
+			drm_plane_destroy_state(plane, a->pstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	drm_modeset_drop_locks(&a->acquire_ctx);
 	ww_acquire_fini(ww_ctx);
@@ -193,7 +226,17 @@ static void commit_locks(struct drm_atomic_state *a,
 static int atomic_commit(struct drm_atomic_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
-	int ret = 0;
+	int nplanes = a->dev->mode_config.num_total_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
 
 	commit_locks(a, ww_ctx);
 
@@ -268,7 +311,195 @@ void _drm_atomic_state_free(struct kref *kref)
 }
 EXPORT_SYMBOL(_drm_atomic_state_free);
 
+int drm_atomic_plane_set_property(struct drm_plane *plane,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
+{
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
+}
+EXPORT_SYMBOL(drm_atomic_plane_set_property);
+
+static void init_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate, struct drm_atomic_state *state)
+{
+	/* snapshot current state: */
+	*pstate = *plane->state;
+	pstate->state = state;
+	if (pstate->fb)
+		drm_framebuffer_reference(pstate->fb);
+}
+
+struct drm_plane_state *
+drm_atomic_get_plane_state(struct drm_plane *plane,
+		struct drm_atomic_state *state)
+{
+	struct drm_atomic_state *a = state;
+	struct drm_plane_state *pstate;
+	int ret;
+
+	pstate = a->pstates[plane->index];
+
+	if (!pstate) {
+		struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
+
+		/* grab lock of current crtc.. if crtc is NULL then grab all: */
+		if (plane->state->crtc)
+			ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
+		else
+			ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
+		if (ret)
+			return ERR_PTR(ret);
+
+		pstate = drm_plane_create_state(plane);
+		if (!pstate)
+			return ERR_PTR(-ENOMEM);
+		init_plane_state(plane, pstate, state);
+		a->planes[plane->index] = plane;
+		a->pstates[plane->index] = pstate;
+	}
+
+	return pstate;
+}
+EXPORT_SYMBOL(drm_atomic_get_plane_state);
+
+static void
+swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a)
+{
+	struct drm_plane_state *pstate = a->pstates[plane->index];
+
+	/* clear transient state (only valid during atomic update): */
+	pstate->update_plane = false;
+	pstate->new_fb = false;
+
+	swap(plane->state, a->pstates[plane->index]);
+	plane->base.propvals = &plane->state->propvals;
+}
+
+/* For primary plane, if the driver implements ->page_flip(), then
+ * we can use that.  But drivers can now choose not to bother with
+ * implementing page_flip().
+ */
+static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate)
+{
+	struct drm_crtc *crtc = pstate->crtc;
+	return (plane == crtc->primary) && crtc->funcs->page_flip &&
+			!pstate->update_plane;
+}
+
+/* clear crtc/fb, ie. after disable_plane().  But takes care to keep
+ * the property state in sync.  Once we get rid of plane->crtc/fb ptrs
+ * and just use state, we can get rid of this fxn:
+ */
+static void
+reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate)
+{
+	struct drm_mode_config *config = &plane->dev->mode_config;
+	drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
+	drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
+	plane->crtc = NULL;
+	plane->fb = NULL;
+}
+
+static bool
+is_primary_helper(struct drm_plane *plane)
+{
+#ifdef CONFIG_DRM_KMS_HELPER
+	if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+			(plane->funcs == &drm_primary_helper_funcs))
+		return true;
+#endif
+	return false;
+}
+
+static int
+commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
+{
+	struct drm_atomic_state *a = pstate->state;
+	struct drm_framebuffer *old_fb = plane->fb;
+	struct drm_framebuffer *fb = pstate->fb;
+	bool enabled = pstate->crtc && fb;
+	int ret = 0;
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	if (!enabled) {
+		if (is_primary_helper(plane)) {
+			/* primary plane helpers don't like ->disable_plane()..
+			 * so this hack for now until someone comes up with
+			 * something better:
+			 */
+			ret = 0;
+		} else {
+			ret = plane->funcs->disable_plane(plane);
+			reset_plane(plane, pstate);
+		}
+	} else {
+		struct drm_crtc *crtc = pstate->crtc;
+		if (pstate->update_plane ||
+				(pstate->new_fb && !can_flip(plane, pstate))) {
+			ret = plane->funcs->update_plane(plane, 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 == 0) {
+				/*
+				 * For page_flip(), the driver does this, but for
+				 * update_plane() it doesn't.. hurray \o/
+				 */
+				plane->crtc = crtc;
+				plane->fb = fb;
+				fb = NULL;  /* don't unref */
+			}
+
+		} else if (pstate->new_fb) {
+			ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
+			if (ret == 0) {
+				/*
+				 * Warn if the driver hasn't properly updated the plane->fb
+				 * field to reflect that the new framebuffer is now used.
+				 * Failing to do so will screw with the reference counting
+				 * on framebuffers.
+				 */
+				WARN_ON(plane->fb != fb);
+				fb = NULL;  /* don't unref */
+			}
+		} else {
+			old_fb = NULL;
+			ret = 0;
+		}
+	}
+
+	if (ret) {
+		/* Keep the old fb, don't unref it. */
+		old_fb = NULL;
+	} else {
+		/* 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.
+		 */
+		swap_plane_state(plane, pstate->state);
+	}
+
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
+	return ret;
+}
 
 const struct drm_atomic_funcs drm_atomic_funcs = {
+		.check_plane_state  = drm_plane_check_state,
+		.commit_plane_state = commit_plane_state,
 };
 EXPORT_SYMBOL(drm_atomic_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 8c41db6..026dcc7 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -693,6 +693,18 @@ 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_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) {
@@ -709,8 +721,17 @@ 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);
+				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);
 	}
 
@@ -1144,18 +1165,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 			     const uint32_t *formats, uint32_t format_count,
 			     enum drm_plane_type type)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	/* this is now required: */
+	WARN_ON(!funcs->set_property);
+
 	drm_modeset_lock_all(dev);
 
 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
 	if (ret)
 		goto out;
 
+	plane->funcs = funcs;
+	plane->state = drm_plane_create_state(plane);
 	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,
 				      GFP_KERNEL);
 	if (!plane->format_types) {
@@ -1170,15 +1196,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 	plane->possible_crtcs = possible_crtcs;
 	plane->type = type;
 
-	list_add_tail(&plane->head, &dev->mode_config.plane_list);
-	dev->mode_config.num_total_plane++;
+	list_add_tail(&plane->head, &config->plane_list);
+	plane->index = config->num_total_plane;
+	config->num_total_plane++;
 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-		dev->mode_config.num_overlay_plane++;
+		config->num_overlay_plane++;
 
 	drm_object_attach_property(&plane->base,
-				   dev->mode_config.plane_type_property,
+				   config->plane_type_property,
 				   plane->type);
 
+	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);
 
@@ -1239,10 +1277,151 @@ void drm_plane_cleanup(struct drm_plane *plane)
 	dev->mode_config.num_total_plane--;
 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
 		dev->mode_config.num_overlay_plane--;
+	drm_plane_destroy_state(plane, plane->state);
 	drm_modeset_unlock_all(dev);
 }
 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) {
+		struct drm_framebuffer *old_fb = state->fb;
+		/*
+		 * NOTE: the ref to the fb could have been lost between
+		 * drm_property_change_is_valid() and now.  The upshot
+		 * is that drm_framebuffer_lookup() could return NULL
+		 * and we'd disable the plane.
+		 *
+		 * We *could* return an error in that case.  But if (for
+		 * example) _setcrtc() raced with _rmfb() and _rmfb()
+		 * came after, it would disable what was enabled in the
+		 * _setcrtc().  Which is the same end result that we get
+		 * here, just skipping briefly setting the mode.
+		 */
+		state->fb = drm_framebuffer_lookup(dev, value);
+		if (old_fb)
+			drm_framebuffer_unreference(old_fb);
+		state->new_fb = true;
+	} else if (property == config->prop_crtc_id) {
+		struct drm_crtc *crtc = drm_crtc_find(dev, value);
+		/*
+		 * Take the lock of the incoming crtc as well, moving
+		 * plane between crtcs is synchronized on both incoming
+		 * and outgoing crtc.
+		 */
+		if (crtc) {
+			struct drm_atomic_state *a = state->state;
+			int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
+			if (ret)
+				return ret;
+		}
+		state->crtc = crtc;
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_x) {
+		state->crtc_x = U642I64(value);
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_y) {
+		state->crtc_y = U642I64(value);
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_w) {
+		state->crtc_w = value;
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_h) {
+		state->crtc_h = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_x) {
+		state->src_x = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_y) {
+		state->src_y = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_w) {
+		state->src_w = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_h) {
+		state->src_h = value;
+		state->update_plane = true;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_set_property);
+
 /**
  * drm_plane_force_disable - Forcibly disable a plane
  * @plane: plane to disable
@@ -1252,50 +1431,101 @@ 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,
+		struct drm_atomic_state *state)
 {
-	struct drm_framebuffer *old_fb = plane->fb;
-	int ret;
-
-	if (!old_fb)
-		return;
+	struct drm_mode_config *config = &plane->dev->mode_config;
 
-	ret = plane->funcs->disable_plane(plane);
-	if (ret) {
-		DRM_ERROR("failed to disable plane with busy fb\n");
-		return;
-	}
-	/* disconnect the plane from the fb and crtc: */
-	__drm_framebuffer_unreference(old_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);
 
 static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
-	struct drm_property *edid;
-	struct drm_property *dpms;
-	struct drm_property *dev_path;
+	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;
+
+
+	prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_x = prop;
 
-	dev_path = drm_property_create(dev,
+	prop = drm_property_create_range(dev, 0, "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_signed_range(dev, 0, "CRTC_X",
+			INT_MIN, INT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_x = prop;
+
+	prop = drm_property_create_signed_range(dev, 0, "CRTC_Y",
+			INT_MIN, 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, 0, "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;
+
+	prop = drm_property_create(dev,
 				       DRM_MODE_PROP_BLOB |
 				       DRM_MODE_PROP_IMMUTABLE,
 				       "PATH", 0);
-	dev->mode_config.path_property = dev_path;
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.path_property = prop;
 
 	return 0;
 }
@@ -2196,6 +2426,7 @@ out:
 	return ret;
 }
 
+
 /*
  * setplane_internal - setplane handler for internal callers
  *
@@ -2206,8 +2437,7 @@ out:
  * src_{x,y,w,h} are provided in 16.16 fixed point format
  */
 static int setplane_internal(struct drm_plane *plane,
-			     struct drm_crtc *crtc,
-			     struct drm_framebuffer *fb,
+			     uint32_t crtc_id, uint32_t fb_id,
 			     int32_t crtc_x, int32_t crtc_y,
 			     uint32_t crtc_w, uint32_t crtc_h,
 			     /* src_{x,y,w,h} values are 16.16 fixed point */
@@ -2215,84 +2445,89 @@ static int setplane_internal(struct drm_plane *plane,
 			     uint32_t src_w, uint32_t src_h)
 {
 	struct drm_device *dev = plane->dev;
-	struct drm_framebuffer *old_fb = NULL;
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_atomic_state *state;
 	int ret = 0;
-	unsigned int fb_width, fb_height;
-	int i;
 
-	/* No fb means shut it down */
-	if (!fb) {
-		drm_modeset_lock_all(dev);
-		old_fb = plane->fb;
-		ret = plane->funcs->disable_plane(plane);
-		if (!ret) {
-			plane->crtc = NULL;
-			plane->fb = NULL;
-		} else {
-			old_fb = NULL;
-		}
-		drm_modeset_unlock_all(dev);
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return -EINVAL;
+
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
+
+retry:
+	ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+	if (ret)
 		goto out;
-	}
 
-	/* Check whether this plane is usable on this CRTC */
-	if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
-		DRM_DEBUG_KMS("Invalid crtc for plane\n");
-		ret = -EINVAL;
+	ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
 		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;
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_id, crtc_id, NULL);
+	if (ret)
 		goto out;
-	}
 
-	fb_width = fb->width << 16;
-	fb_height = fb->height << 16;
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_fb_id, fb_id, NULL);
+	if (ret)
+		goto out;
 
-	/* Make sure source coordinates are inside the fb. */
-	if (src_w > fb_width ||
-	    src_x > fb_width - src_w ||
-	    src_h > fb_height ||
-	    src_y > fb_height - src_h) {
-		DRM_DEBUG_KMS("Invalid source coordinates "
-			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
-			      src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
-			      src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
-			      src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
-			      src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
-		ret = -ENOSPC;
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_x, I642U64(crtc_x), NULL);
+	if (ret)
 		goto out;
-	}
 
-	drm_modeset_lock_all(dev);
-	old_fb = plane->fb;
-	ret = plane->funcs->update_plane(plane, crtc, fb,
-					 crtc_x, crtc_y, crtc_w, crtc_h,
-					 src_x, src_y, src_w, src_h);
-	if (!ret) {
-		plane->crtc = crtc;
-		plane->fb = fb;
-		fb = NULL;
-	} else {
-		old_fb = NULL;
-	}
-	drm_modeset_unlock_all(dev);
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_y, I642U64(crtc_y), NULL);
+	if (ret)
+		goto out;
 
-out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
-	if (old_fb)
-		drm_framebuffer_unreference(old_fb);
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_w, crtc_w, NULL);
+	if (ret)
+		goto out;
 
-	return ret;
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_h, crtc_h, NULL);
+	if (ret)
+		goto out;
 
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_w, src_w, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_h, src_h, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_x, src_x, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_y, src_y, NULL);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_check(dev, state);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_commit(dev, state);
+
+out:
+	if (ret == -EDEADLK) {
+		drm_modeset_backoff(&state->acquire_ctx);
+		goto retry;
+	}
+	dev->driver->atomic_end(dev, state);
+	return ret;
 }
 
 /**
@@ -2312,65 +2547,23 @@ 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_object *obj;
 	struct drm_plane *plane;
-	struct drm_crtc *crtc = NULL;
-	struct drm_framebuffer *fb = NULL;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-	/* 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);
-		return -ERANGE;
-	}
-
-	/*
-	 * First, find the plane, crtc, and fb objects.  If not available,
-	 * we don't bother to call the driver.
-	 */
-	obj = drm_mode_object_find(dev, plane_req->plane_id,
-				   DRM_MODE_OBJECT_PLANE);
-	if (!obj) {
+	plane = drm_plane_find(dev, plane_req->plane_id);
+	if (!plane) {
 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
 			      plane_req->plane_id);
 		return -ENOENT;
 	}
-	plane = obj_to_plane(obj);
-
-	if (plane_req->fb_id) {
-		fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
-		if (!fb) {
-			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
-				      plane_req->fb_id);
-			return -ENOENT;
-		}
-
-		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);
-			return -ENOENT;
-		}
-		crtc = obj_to_crtc(obj);
-	}
 
-	/*
-	 * setplane_internal will take care of deref'ing either the old or new
-	 * framebuffer depending on success.
-	 */
-	return setplane_internal(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);
+	return setplane_internal(plane, plane_req->crtc_id, plane_req->fb_id,
+			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);
 }
 
 /**
@@ -2702,10 +2895,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
 	 * setplane_internal will take care of deref'ing either the old or new
 	 * framebuffer depending on success.
 	 */
-	ret = setplane_internal(crtc->cursor, crtc, fb,
+	ret = setplane_internal(crtc->cursor,
+				crtc->base.id, fb ? fb->base.id : 0,
 				crtc_x, crtc_y, crtc_w, crtc_h,
 				0, 0, src_w, src_h);
-
+	drm_framebuffer_unreference(fb);
 	/* Update successful; save new cursor position, if necessary */
 	if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
 		crtc->cursor_x = req->x;
@@ -4091,7 +4285,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,
 					   struct drm_atomic_state *state, struct drm_property *property,
 					   uint64_t value, void *blob_data)
 {
@@ -4114,8 +4308,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,
 				      struct drm_atomic_state *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -4130,8 +4325,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,
 				      struct drm_atomic_state *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -4140,12 +4336,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_device *dev,
 		struct drm_mode_object *obj, struct drm_atomic_state *state,
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index ecce98d..0dc0780 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -336,18 +336,33 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 
-static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
+		bool lockless)
 {
 	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, lockless ?
+			DRM_MODE_ATOMIC_NOLOCK : 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)
 		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
-			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;
@@ -379,7 +394,7 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
  */
 static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
-	return restore_fbdev_mode(fb_helper);
+	return restore_fbdev_mode(fb_helper, true);
 }
 
 /**
@@ -392,12 +407,7 @@ static bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
  */
 bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
 {
-	struct drm_device *dev = fb_helper->dev;
-	bool ret;
-	drm_modeset_lock_all(dev);
-	ret = restore_fbdev_mode(fb_helper);
-	drm_modeset_unlock_all(dev);
-	return ret;
+	return restore_fbdev_mode(fb_helper, false);
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
 
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index 827ec1a..613111c 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -28,6 +28,7 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic.h>
 
 #define SUBPIXEL_MASK 0xffff
 
@@ -301,6 +302,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy);
 const struct drm_plane_funcs drm_primary_helper_funcs = {
 	.update_plane = drm_primary_helper_update,
 	.disable_plane = drm_primary_helper_disable,
+	.set_property = drm_atomic_plane_set_property,
 	.destroy = drm_primary_helper_destroy,
 };
 EXPORT_SYMBOL(drm_primary_helper_funcs);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 9da0935..8cf7442 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.h>
 
 #include <drm/exynos_drm.h>
 #include "exynos_drm_drv.h"
@@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
 	struct drm_device *dev = plane->dev;
 	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 	struct exynos_drm_private *dev_priv = dev->dev_private;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == dev_priv->plane_zpos_property) {
 		exynos_plane->overlay.zpos = val;
 		return 0;
 	}
 
-	return -EINVAL;
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static struct drm_plane_funcs exynos_plane_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 6afd1cf..adbf1a0 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -1203,6 +1203,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_plane_set_property,
 };
 
 static uint32_t ilk_plane_formats[] = {
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
index 8c064dc..4c92985 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
@@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static const struct drm_plane_funcs mdp4_plane_funcs = {
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index d560e42..13820b4 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -106,8 +106,10 @@ int mdp5_plane_set_property(struct drm_plane *plane,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static const struct drm_plane_funcs mdp5_plane_funcs = {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index 577e6aa..97b48b5 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -24,6 +24,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
 
@@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane,
 		  uint64_t value, void *blob_data)
 {
 	struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == nv_plane->props.colorkey)
 		nv_plane->colorkey = value;
@@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane,
 	else if (property == nv_plane->props.iturbt_709)
 		nv_plane->iturbt_709 = value;
 	else
-		return -EINVAL;
+		return drm_plane_set_property(plane, pstate,
+				property, value, blob_data);
 
 	if (nv_plane->set_params)
 		nv_plane->set_params(nv_plane);
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 5ca0a7c..9630a32 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -585,7 +585,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 8ae5c49..7b93b16 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane,
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_private *priv = plane->dev->dev_private;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
 	int ret = -EINVAL;
 
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+
 	if (property == priv->rotation_prop) {
 		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->win.rotation = val;
@@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane,
 		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, pstate, 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 3a5d843..015c76a 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.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
 {
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 	struct rcar_du_group *rgrp = rplane->group;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == rgrp->planes.alpha)
 		rcar_du_plane_set_alpha(rplane, value);
@@ -412,7 +417,8 @@ 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, pstate,
+				property, value, blob_data);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
index 060ae03..ccf03ea 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.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -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_plane_set_property,
 	.destroy = shmob_drm_plane_destroy,
 };
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index ff72b81..78e93ec 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -68,7 +68,8 @@
  * struct drm_atomic_funcs - helper funcs used by the atomic helpers
  */
 struct drm_atomic_funcs {
-	int dummy; /* for now */
+	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_funcs drm_atomic_funcs;
@@ -84,6 +85,30 @@ 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);
 
+int drm_atomic_plane_set_property(struct drm_plane *plane,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data);
+struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
+		struct drm_atomic_state *state);
+
+static inline int
+drm_atomic_check_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			plane->dev->driver->atomic_funcs;
+	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_funcs *funcs =
+			plane->dev->driver->atomic_funcs;
+	return funcs->commit_plane_state(plane, pstate);
+}
+
 /**
  * struct drm_atomic_state - the state object used by atomic helpers
  */
@@ -91,6 +116,8 @@ struct drm_atomic_state {
 	struct kref refcount;
 	struct drm_device *dev;
 	uint32_t flags;
+	struct drm_plane **planes;
+	struct drm_plane_state **pstates;
 
 	bool committed;
 	bool checked;       /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index a0ee418..b10eaac 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -585,6 +585,9 @@ struct drm_plane_funcs {
 	int (*disable_plane)(struct drm_plane *plane);
 	void (*destroy)(struct drm_plane *plane);
 
+	struct drm_plane_state *(*create_state)(struct drm_plane *plane);
+	void (*destroy_state)(struct drm_plane *plane,
+			    struct drm_plane_state *pstate);
 	int (*set_property)(struct drm_plane *plane,
 			    struct drm_atomic_state *state,
 			    struct drm_property *property, uint64_t val,
@@ -598,6 +601,48 @@ enum drm_plane_type {
 };
 
 /**
+ * drm_plane_state - mutable plane state
+ * @update_plane: if full update_plane() is needed (vs pageflip)
+ * @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 update_plane      : 1;
+	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;
+
+	struct drm_atomic_state *state;
+};
+
+/**
  * drm_plane - central DRM plane control structure
  * @dev: DRM device this plane belongs to
  * @head: for list management
@@ -607,6 +652,8 @@ enum drm_plane_type {
  * @format_count: number of formats supported
  * @crtc: currently bound CRTC
  * @fb: currently bound fb
+ * @index: plane number, 0..n
+ * @state: the mutable state
  * @funcs: helper functions
  * @properties: property tracking for this plane
  * @type: type of plane (overlay, primary, cursor)
@@ -624,10 +671,17 @@ struct drm_plane {
 	struct drm_crtc *crtc;
 	struct drm_framebuffer *fb;
 
+	int index;
+
+	/*
+	 * 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;
 
 	enum drm_plane_type type;
 };
@@ -820,8 +874,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;
 	struct drm_property *path_property;
@@ -946,11 +1012,20 @@ extern int drm_plane_init(struct drm_device *dev,
 			  const uint32_t *formats, uint32_t format_count,
 			  bool is_primary);
 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,
+		struct drm_atomic_state *state);
 extern int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 				   int x, int y,
 				   const struct drm_display_mode *mode,
 				   const struct drm_framebuffer *fb);
+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);
 
@@ -1002,6 +1077,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,
+					   struct drm_atomic_state *state, struct drm_property *property,
+					   uint64_t value, void *blob_data);
+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 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);
+
 extern int drm_framebuffer_init(struct drm_device *dev,
 				struct drm_framebuffer *fb,
 				const struct drm_framebuffer_funcs *funcs);
@@ -1182,6 +1268,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
 	return mo ? obj_to_blob(mo) : NULL;
 }
 
+static inline struct drm_plane_state *
+drm_plane_create_state(struct drm_plane *plane)
+{
+	if (plane->funcs->create_state)
+		return plane->funcs->create_state(plane);
+	return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
+}
+
+static inline void
+drm_plane_destroy_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	if (pstate->fb)
+		drm_framebuffer_unreference(pstate->fb);
+	if (plane->funcs->destroy_state)
+		plane->funcs->destroy_state(plane, pstate);
+	else
+		kfree(pstate);
+}
+
 /* Plane list iterator for legacy (overlay only) planes. */
 #define drm_for_each_legacy_plane(plane, planelist) \
 	list_for_each_entry(plane, planelist, head) \
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 5/7] drm: convert crtc to properties/state
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
                   ` (3 preceding siblings ...)
  2014-07-23 19:38 ` [PATCH 4/7] drm: convert plane to properties/state Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-23 19:38 ` [PATCH 6/7] drm/msm: add atomic support Rob Clark
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

Break the mutable state of a crtc out into a separate structure
and use atomic properties mechanism to set crtc attributes.  This
makes it easier to have some helpers for crtc->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.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/armada/armada_crtc.c       |  11 +-
 drivers/gpu/drm/ast/ast_mode.c             |   1 +
 drivers/gpu/drm/cirrus/cirrus_mode.c       |   1 +
 drivers/gpu/drm/drm_atomic.c               | 230 +++++++++-
 drivers/gpu/drm/drm_crtc.c                 | 654 +++++++++++++++++++----------
 drivers/gpu/drm/drm_fb_helper.c            |  24 +-
 drivers/gpu/drm/drm_irq.c                  |   8 +-
 drivers/gpu/drm/exynos/exynos_drm_crtc.c   |   7 +-
 drivers/gpu/drm/gma500/cdv_intel_display.c |   1 +
 drivers/gpu/drm/gma500/psb_intel_display.c |   1 +
 drivers/gpu/drm/i915/intel_display.c       |   3 +-
 drivers/gpu/drm/mgag200/mgag200_mode.c     |   1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c   |   6 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c   |   6 +-
 drivers/gpu/drm/nouveau/dispnv04/crtc.c    |   1 +
 drivers/gpu/drm/nouveau/nv50_display.c     |   1 +
 drivers/gpu/drm/omapdrm/omap_crtc.c        |  12 +-
 drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
 drivers/gpu/drm/qxl/qxl_display.c          |   2 +
 drivers/gpu/drm/radeon/radeon_display.c    |   2 +
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c     |   2 +
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c  |   2 +
 drivers/gpu/drm/tilcdc/tilcdc_crtc.c       |   1 +
 drivers/gpu/drm/udl/udl_modeset.c          |   2 +
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c        |   1 +
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c       |   1 +
 include/drm/drm_atomic.h                   |  29 ++
 include/drm/drm_crtc.h                     | 103 ++++-
 28 files changed, 864 insertions(+), 251 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 7d3c649..6237af4 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -9,6 +9,7 @@
 #include <linux/clk.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include "armada_crtc.h"
 #include "armada_drm.h"
 #include "armada_fb.h"
@@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 {
 	struct armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
 	bool update_csc = false;
+	int ret = 0;
+
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
 
 	if (property == priv->csc_yuv_prop) {
 		dcrtc->csc_yuv_mode = val;
@@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 	} else if (property == priv->csc_rgb_prop) {
 		dcrtc->csc_rgb_mode = val;
 		update_csc = true;
+	} else {
+		ret = drm_crtc_set_property(crtc, cstate, property,
+				val, blob_data);
 	}
 
 	if (update_csc) {
@@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 		writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
 	}
 
-	return 0;
+	return ret;
 }
 
 static struct drm_crtc_funcs armada_crtc_funcs = {
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 5389350..d9ec1bf 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
 	.cursor_move = ast_cursor_move,
 	.reset = ast_crtc_reset,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.gamma_set = ast_crtc_gamma_set,
 	.destroy = ast_crtc_destroy,
 };
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index e1c5c32..17c0bf7 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc)
 static const struct drm_crtc_funcs cirrus_crtc_funcs = {
 	.gamma_set = cirrus_crtc_gamma_set,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = cirrus_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 5a9916c..cc12114 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -49,11 +49,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 	struct drm_atomic_state *state;
 	uint32_t acquire_flags = 0;
 	int nplanes = dev->mode_config.num_total_plane;
+	int ncrtcs  = dev->mode_config.num_crtc;
 	int sz;
 	void *ptr;
 
 	sz = sizeof(*state);
 	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
+	sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
 
 	ptr = kzalloc(sz, GFP_KERNEL);
 
@@ -78,6 +80,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 	state->pstates = ptr;
 	ptr = &state->pstates[nplanes];
 
+	state->crtcs = ptr;
+	ptr = &state->crtcs[ncrtcs];
+
+	state->cstates = ptr;
+	ptr = &state->cstates[ncrtcs];
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_begin);
@@ -96,7 +104,18 @@ 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 */
+	switch (obj->type) {
+	case DRM_MODE_OBJECT_CRTC: {
+		struct drm_crtc_state *cstate =
+			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
+		if (IS_ERR(cstate))
+			return PTR_ERR(cstate);
+		cstate->event = event;
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_set_event);
 
@@ -115,6 +134,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
 	struct drm_atomic_state *a = state;
 	int nplanes = dev->mode_config.num_total_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
 	int i, ret = 0;
 
 	for (i = 0; i < nplanes; i++) {
@@ -124,6 +144,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 				break;
 		}
 	}
+	for (i = 0; i < ncrtcs; i++) {
+		if (a->crtcs[i]) {
+			ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]);
+			if (ret)
+				break;
+		}
+	}
 
 	a->acquire_ctx.frozen = true;
 
@@ -207,6 +234,7 @@ static void commit_locks(struct drm_atomic_state *a,
 {
 	struct drm_device *dev = a->dev;
 	int nplanes = dev->mode_config.num_total_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
 	int i;
 
 	for (i = 0; i < nplanes; i++) {
@@ -217,6 +245,14 @@ static void commit_locks(struct drm_atomic_state *a,
 		}
 	}
 
+	for (i = 0; i < ncrtcs; i++) {
+		struct drm_crtc *crtc = a->crtcs[i];
+		if (crtc) {
+			crtc->state->state = NULL;
+			drm_crtc_destroy_state(crtc, a->cstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	drm_modeset_drop_locks(&a->acquire_ctx);
 	ww_acquire_fini(ww_ctx);
@@ -227,8 +263,18 @@ static int atomic_commit(struct drm_atomic_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
 	int nplanes = a->dev->mode_config.num_total_plane;
+	int ncrtcs = a->dev->mode_config.num_crtc;
 	int i, ret = 0;
 
+	for (i = 0; i < ncrtcs; i++) {
+		struct drm_crtc *crtc = a->crtcs[i];
+		if (crtc) {
+			ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]);
+			if (ret)
+				break;
+		}
+	}
+
 	for (i = 0; i < nplanes; i++) {
 		struct drm_plane *plane = a->planes[i];
 		if (plane) {
@@ -418,6 +464,7 @@ static int
 commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 {
 	struct drm_atomic_state *a = pstate->state;
+	struct drm_crtc_state *cstate = NULL;
 	struct drm_framebuffer *old_fb = plane->fb;
 	struct drm_framebuffer *fb = pstate->fb;
 	bool enabled = pstate->crtc && fb;
@@ -439,8 +486,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 		}
 	} else {
 		struct drm_crtc *crtc = pstate->crtc;
+		cstate = drm_atomic_get_crtc_state(crtc, pstate->state);
 		if (pstate->update_plane ||
 				(pstate->new_fb && !can_flip(plane, pstate))) {
+			WARN_ON(cstate->event);
 			ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
 					pstate->crtc_x, pstate->crtc_y,
 					pstate->crtc_w, pstate->crtc_h,
@@ -457,7 +506,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 			}
 
 		} else if (pstate->new_fb) {
-			ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
+			ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags);
 			if (ret == 0) {
 				/*
 				 * Warn if the driver hasn't properly updated the plane->fb
@@ -487,9 +536,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 		 * original code.
 		 */
 		swap_plane_state(plane, pstate->state);
+		if (cstate)
+			cstate->event = NULL;
 	}
 
-
 	if (fb)
 		drm_framebuffer_unreference(fb);
 	if (old_fb)
@@ -498,8 +548,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 	return ret;
 }
 
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
+{
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+}
+EXPORT_SYMBOL(drm_atomic_crtc_set_property);
+
+static void init_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate, struct drm_atomic_state *state)
+{
+	/* snapshot current state: */
+	*cstate = *crtc->state;
+	cstate->state = state;
+
+	if (cstate->connector_ids) {
+		int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]);
+		cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL);
+	}
+
+	/* this should never happen.. but make sure! */
+	WARN_ON(cstate->event);
+	cstate->event = NULL;
+}
+
+struct drm_crtc_state *
+drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
+{
+	struct drm_crtc_state *cstate;
+	int ret;
+
+	cstate = a->cstates[crtc->index];
+
+	if (!cstate) {
+		ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
+		if (ret)
+			return ERR_PTR(ret);
+
+		cstate = drm_crtc_create_state(crtc);
+		if (!cstate)
+			return ERR_PTR(-ENOMEM);
+		init_crtc_state(crtc, cstate, a);
+		a->crtcs[crtc->index] = crtc;
+		a->cstates[crtc->index] = cstate;
+
+		/* we'll need it later, so make sure we have state
+		 * for primary plane too:
+		 */
+		drm_atomic_get_plane_state(crtc->primary, a);
+	}
+	return cstate;
+}
+EXPORT_SYMBOL(drm_atomic_get_crtc_state);
+
+static void
+swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
+{
+	struct drm_crtc_state *cstate = a->cstates[crtc->index];
+	struct drm_device *dev = crtc->dev;
+	struct drm_pending_vblank_event *event = cstate->event;
+
+	if (event) {
+		/* hrm, need to sort out a better way to send events for
+		 * other-than-pageflip.. but modeset is not async, so:
+		 */
+		unsigned long flags;
+		spin_lock_irqsave(&dev->event_lock, flags);
+		drm_send_vblank_event(dev, crtc->index, event);
+		cstate->event = NULL;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+
+	/* clear transient state (only valid during atomic update): */
+	cstate->set_config = false;
+	cstate->connectors_change = false;
+
+	swap(crtc->state, a->cstates[crtc->index]);
+	crtc->base.propvals = &crtc->state->propvals;
+}
+
+static struct drm_connector **get_connector_set(struct drm_device *dev,
+		uint32_t *connector_ids, uint32_t num_connector_ids)
+{
+	struct drm_connector **connector_set = NULL;
+	int i;
+
+	connector_set = kmalloc(num_connector_ids *
+			sizeof(struct drm_connector *),
+			GFP_KERNEL);
+	if (!connector_set)
+		return NULL;
+
+	for (i = 0; i < num_connector_ids; i++)
+		connector_set[i] = drm_connector_find(dev, connector_ids[i]);
+
+	return connector_set;
+}
+
+static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_plane_state *pstate =
+			drm_atomic_get_plane_state(crtc->primary, cstate->state);
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_connector **connector_set = get_connector_set(crtc->dev,
+			cstate->connector_ids, cstate->num_connector_ids);
+	struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate);
+	struct drm_mode_set set = {
+			.crtc = crtc,
+			.x = pstate->src_x >> 16,
+			.y = pstate->src_y >> 16,
+			.mode = mode,
+			.num_connectors = cstate->num_connector_ids,
+			.connectors = connector_set,
+			.fb = fb,
+	};
+	int ret;
+
+	if (IS_ERR(mode)) {
+		ret = PTR_ERR(mode);
+		return ret;
+	}
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	ret = drm_mode_set_config_internal(&set);
+	if (!ret) {
+		swap_crtc_state(crtc, cstate->state);
+		pstate->new_fb = pstate->update_plane = false;
+	}
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+
+	kfree(connector_set);
+	if (mode)
+		drm_mode_destroy(dev, mode);
+	return ret;
+}
+
+static int
+commit_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	struct drm_plane_state *pstate =
+			drm_atomic_get_plane_state(crtc->primary, cstate->state);
+	int ret = -EINVAL;
+
+	if (cstate->set_config)
+		return set_config(crtc, cstate);
+
+	if (!pstate->fb) {
+		/* disable */
+		struct drm_mode_set set = {
+				.crtc = crtc,
+				.fb = NULL,
+		};
+
+		ret = drm_mode_set_config_internal(&set);
+		if (!ret) {
+			swap_crtc_state(crtc, cstate->state);
+		}
+	}
+
+	return ret;
+}
+
 const struct drm_atomic_funcs drm_atomic_funcs = {
 		.check_plane_state  = drm_plane_check_state,
 		.commit_plane_state = commit_plane_state,
+
+		.check_crtc_state   = drm_crtc_check_state,
+		.commit_crtc_state  = commit_crtc_state,
 };
 EXPORT_SYMBOL(drm_atomic_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 026dcc7..d5ad7e1 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -670,10 +670,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
 	struct drm_device *dev = fb->dev;
-	struct drm_crtc *crtc;
 	struct drm_plane *plane;
-	struct drm_mode_set set;
-	int ret;
 
 	WARN_ON(!list_empty(&fb->filp_head));
 
@@ -693,7 +690,9 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
-		void *state;
+		struct drm_mode_config *config = &dev->mode_config;
+		struct drm_atomic_state *state;
+		int ret;
 
 		state = dev->driver->atomic_begin(dev, 0);
 		if (IS_ERR(state)) {
@@ -701,24 +700,15 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 			return;
 		}
 
-		/* TODO once CRTC is converted to state/properties, we can push the
-		 * locking down into drm_atomic_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->primary->fb == fb) {
-				/* should turn off the crtc */
-				memset(&set, 0, sizeof(struct drm_mode_set));
-				set.crtc = crtc;
-				set.fb = NULL;
-				ret = drm_mode_set_config_internal(&set);
-				if (ret)
-					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
-			}
-		}
+retry:
+		ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+		if (ret)
+			goto out;
+		ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+		if (ret)
+			goto out;
 
+		/* remove from any plane */
 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
 			if (plane->fb == fb)
 				drm_plane_force_disable(plane, state);
@@ -730,9 +720,12 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 		else
 			dev->driver->atomic_commit(dev, state);
 
+out:
+		if (ret == -EDEADLK) {
+			drm_modeset_backoff(&state->acquire_ctx);
+			goto retry;
+		}
 		dev->driver->atomic_end(dev, state);
-
-		drm_modeset_unlock_all(dev);
 	}
 
 	drm_framebuffer_unreference(fb);
@@ -763,9 +756,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	/* this is now required: */
+	WARN_ON(!funcs->set_property);
+
 	crtc->dev = dev;
 	crtc->funcs = funcs;
-	crtc->invert_dimensions = false;
+	crtc->state = drm_crtc_create_state(crtc);
+	crtc->state->invert_dimensions = false;
 
 	drm_modeset_lock_all(dev);
 	drm_modeset_lock_init(&crtc->mutex);
@@ -777,7 +774,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 		goto out;
 
 	crtc->base.properties = &crtc->properties;
-	crtc->base.propvals = &crtc->propvals;
+	crtc->base.propvals = &crtc->state->propvals;
 
 	list_add_tail(&crtc->head, &config->crtc_list);
 	config->num_crtc++;
@@ -785,9 +782,12 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 	crtc->primary = primary;
 	crtc->cursor = cursor;
 	if (primary)
-		primary->possible_crtcs = 1 << drm_crtc_index(crtc);
+		primary->possible_crtcs = 1 << crtc->index;
 	if (cursor)
-		cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
+		cursor->possible_crtcs = 1 << crtc->index;
+
+	drm_object_attach_property(&crtc->base, config->prop_mode, 0);
+	drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
 
  out:
 	drm_modeset_unlock_all(dev);
@@ -816,31 +816,244 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 	drm_mode_object_put(dev, &crtc->base);
 	list_del(&crtc->head);
 	dev->mode_config.num_crtc--;
+
+	drm_crtc_destroy_state(crtc, crtc->state);
 }
 EXPORT_SYMBOL(drm_crtc_cleanup);
 
-/**
- * drm_crtc_index - find the index of a registered CRTC
- * @crtc: CRTC to find index for
- *
- * Given a registered CRTC, return the index of that CRTC within a DRM
- * device's list of CRTCs.
- */
-unsigned int drm_crtc_index(struct drm_crtc *crtc)
+/* get display-mode from user-mode */
+struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
 {
-	unsigned int index = 0;
-	struct drm_crtc *tmp;
+	struct drm_display_mode *mode = NULL;
+	if (cstate->mode_valid) {
+		struct drm_device *dev = crtc->dev;
+		int ret;
 
-	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
-		if (tmp == crtc)
-			return index;
+		mode = drm_mode_create(dev);
+		if (!mode)
+			return ERR_PTR(-ENOMEM);
 
-		index++;
+		ret = drm_crtc_convert_umode(mode, &cstate->mode);
+		if (ret) {
+			DRM_DEBUG_KMS("Invalid mode\n");
+			drm_mode_destroy(dev, mode);
+			return ERR_PTR(ret);
+		}
+
+		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
 	}
+	return mode;
+}
 
-	BUG();
+static int connector_idx(struct drm_crtc_state *state,
+		uint32_t connector_id)
+{
+	int i;
+	for (i = 0; i < state->num_connector_ids; i++)
+		if (state->connector_ids[i] == connector_id)
+			return i;
+	return -1;
+}
+
+static int remove_connector(struct drm_crtc *ocrtc,
+		struct drm_crtc_state *ostate, struct drm_atomic_state *state,
+		int idx)
+{
+	struct drm_mode_config *config = &ocrtc->dev->mode_config;
+	uint32_t *new_connector_ids;
+	int a, b;
+
+	/* before deletion point: */
+	a = idx * sizeof(ostate->connector_ids[0]);
+
+	/* after deletion point: */
+	b = (ostate->num_connector_ids - 1 - idx) *
+			sizeof(ostate->connector_ids[0]);
+
+	new_connector_ids = kmalloc(a+b, GFP_KERNEL);
+	if (!new_connector_ids)
+		return -ENOMEM;
+
+	memcpy(new_connector_ids, ostate->connector_ids, a);
+	memcpy(&new_connector_ids[idx],
+			&ostate->connector_ids[idx + 1], b);
+
+	return drm_mode_crtc_set_obj_prop(ocrtc, state,
+		config->prop_connector_ids, a + b,
+		new_connector_ids);
+}
+
+static int check_connectors(struct drm_crtc *crtc,
+		struct drm_atomic_state *state, bool fix,
+		uint32_t *connector_ids, uint32_t num_connector_ids)
+{
+	struct drm_mode_config *config = &crtc->dev->mode_config;
+	struct drm_crtc *ocrtc; /* other connector */
+
+	list_for_each_entry(ocrtc, &config->crtc_list, head) {
+		struct drm_crtc_state *ostate; /* other state */
+		unsigned i;
+
+		if (ocrtc == crtc)
+			continue;
+
+		ostate = drm_atomic_get_crtc_state(crtc, state);
+		if (IS_ERR(ostate))
+			return PTR_ERR(ostate);
+
+		for (i = 0; i < num_connector_ids; i++) {
+			struct drm_connector *connector;
+			uint32_t cid = connector_ids[i];
+			int idx;
+
+retry:
+			idx = connector_idx(ostate, cid);
+			if (idx < 0)
+				continue;
+
+			if (fix) {
+				int ret = remove_connector(ocrtc,
+						ostate, state, idx);
+				if (ret)
+					return ret;
+				goto retry;
+			}
+
+			connector = drm_connector_find(crtc->dev, cid);
+			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n",
+					connector->base.id, connector->name);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+int drm_crtc_check_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
+{
+	struct drm_plane *primary = crtc->primary;
+	struct drm_plane_state *pstate =
+			drm_atomic_get_plane_state(primary, state->state);
+	struct drm_framebuffer *fb = pstate->fb;
+	int hdisplay, vdisplay;
+	struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state);
+	unsigned x, y;
+
+	if (IS_ERR(mode))
+		return PTR_ERR(mode);
+
+	/* disabling the crtc is allowed: */
+	if (!(fb && state->mode_valid))
+		return 0;
+
+	hdisplay = state->mode.hdisplay;
+	vdisplay = state->mode.vdisplay;
+
+	if (mode && drm_mode_is_stereo(mode)) {
+		struct drm_display_mode adjusted = *mode;
+
+		drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
+		hdisplay = adjusted.crtc_hdisplay;
+		vdisplay = adjusted.crtc_vdisplay;
+	}
+
+	if (state->invert_dimensions)
+		swap(hdisplay, vdisplay);
+
+	x = pstate->src_x >> 16;
+	y = pstate->src_y >> 16;
+
+	if (hdisplay > fb->width ||
+	    vdisplay > fb->height ||
+	    x > fb->width - hdisplay ||
+	    y > fb->height - vdisplay) {
+		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
+			      fb->width, fb->height, hdisplay, vdisplay,
+			      x, y, state->invert_dimensions ? " (inverted)" : "");
+		return -ENOSPC;
+	}
+
+	if (crtc->enabled && !state->set_config) {
+		if (primary->state->fb->pixel_format != fb->pixel_format) {
+			DRM_DEBUG_KMS("Page flip is not allowed to "
+					"change frame buffer format.\n");
+			return -EINVAL;
+		}
+	}
+
+	if (state->num_connector_ids == 0) {
+		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
+		return -EINVAL;
+	}
+
+	if (state->connectors_change) {
+		int ret = check_connectors(crtc, state->state, false,
+				state->connector_ids, state->num_connector_ids);
+		if (ret)
+			return ret;
+	}
+
+	if (mode)
+		drm_mode_destroy(crtc->dev, mode);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_crtc_check_state);
+
+void drm_crtc_commit_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
+{
+	crtc->state = state;
+	crtc->base.propvals = &state->propvals;
+}
+EXPORT_SYMBOL(drm_crtc_commit_state);
+
+int drm_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_crtc_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_mode_config *config = &dev->mode_config;
+
+	/* grab primary plane state now, to ensure locks are held, etc. */
+	drm_atomic_get_plane_state(crtc->primary, state->state);
+
+	drm_object_property_set_value(&crtc->base,
+			&state->propvals, property, value, blob_data);
+
+	if (property == config->prop_mode) {
+		if (!blob_data) {
+			memset(&state->mode, 0, sizeof(state->mode));
+			state->mode_valid = false;
+		} else {
+			/* check size: */
+			if (value < sizeof(struct drm_mode_modeinfo))
+				return -EINVAL;
+			state->mode = *(struct drm_mode_modeinfo *)blob_data;
+			state->mode_valid = true;
+		}
+		state->set_config = true;
+	} else if (property == config->prop_connector_ids) {
+		/* if connector-id's changing, we need to have all the locks: */
+		struct drm_atomic_state *a = state->state;
+		int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx);
+		if (ret)
+			return ret;
+		state->connectors_change = true;
+		state->num_connector_ids = value / sizeof(state->connector_ids[0]);
+		kfree(state->connector_ids);
+		state->connector_ids = blob_data;
+		state->set_config = true;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
 }
-EXPORT_SYMBOL(drm_crtc_index);
+EXPORT_SYMBOL(drm_crtc_set_property);
 
 /*
  * drm_mode_remove - remove and free a mode
@@ -1293,6 +1506,10 @@ int drm_plane_check_state(struct drm_plane *plane,
 	if (!fb)
 		return 0;
 
+	/* we'll need this later during commit: */
+	if (state->crtc)
+		drm_atomic_get_crtc_state(state->crtc, state->state);
+
 	fb_width = fb->width << 16;
 	fb_height = fb->height << 16;
 
@@ -1527,6 +1744,16 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 		return -ENOMEM;
 	dev->mode_config.path_property = prop;
 
+	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_connector_ids = prop;
+
+	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_mode = prop;
+
 	return 0;
 }
 
@@ -1825,7 +2052,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
  * Returns:
  * Zero on success, errno on failure.
  */
-static int drm_crtc_convert_umode(struct drm_display_mode *out,
+int drm_crtc_convert_umode(struct drm_display_mode *out,
 				  const struct drm_mode_modeinfo *in)
 {
 	if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
@@ -2072,8 +2299,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
 		goto out;
 	}
 
-	crtc_resp->x = crtc->x;
-	crtc_resp->y = crtc->y;
+	crtc_resp->x = crtc->primary->state->src_x >> 16;
+	crtc_resp->y = crtc->primary->state->src_y >> 16;
 	crtc_resp->gamma_size = crtc->gamma_size;
 	if (crtc->primary->fb)
 		crtc_resp->fb_id = crtc->primary->fb->base.id;
@@ -2638,7 +2865,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 		vdisplay = adjusted.crtc_vdisplay;
 	}
 
-	if (crtc->invert_dimensions)
+	if (crtc->state->invert_dimensions)
 		swap(hdisplay, vdisplay);
 
 	if (hdisplay > fb->width ||
@@ -2647,7 +2874,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 	    y > fb->height - vdisplay) {
 		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
 			      fb->width, fb->height, hdisplay, vdisplay, x, y,
-			      crtc->invert_dimensions ? " (inverted)" : "");
+			      crtc->state->invert_dimensions ? " (inverted)" : "");
 		return -ENOSPC;
 	}
 
@@ -2674,22 +2901,17 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_mode_crtc *crtc_req = data;
 	struct drm_crtc *crtc;
-	struct drm_connector **connector_set = NULL, *connector;
-	struct drm_framebuffer *fb = NULL;
-	struct drm_display_mode *mode = NULL;
-	struct drm_mode_set set;
-	uint32_t __user *set_connectors_ptr;
+	uint32_t fb_id = -1;
+	uint32_t *connector_ids = NULL;
+	struct drm_atomic_state *state = NULL;
 	int ret;
 	int i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-	/* For some reason crtc x/y offsets are signed internally. */
-	if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
-		return -ERANGE;
+	mutex_lock(&dev->mode_config.mutex);
 
-	drm_modeset_lock_all(dev);
 	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
 	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
@@ -2707,55 +2929,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 				ret = -EINVAL;
 				goto out;
 			}
-			fb = crtc->primary->fb;
-			/* Make refcounting symmetric with the lookup path. */
-			drm_framebuffer_reference(fb);
+			fb_id = crtc->primary->fb->base.id;
 		} else {
-			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
-			if (!fb) {
-				DRM_DEBUG_KMS("Unknown FB ID%d\n",
-						crtc_req->fb_id);
-				ret = -ENOENT;
-				goto out;
-			}
+			fb_id = crtc_req->fb_id;
 		}
-
-		mode = drm_mode_create(dev);
-		if (!mode) {
-			ret = -ENOMEM;
-			goto out;
-		}
-
-		ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
-		if (ret) {
-			DRM_DEBUG_KMS("Invalid mode\n");
-			goto out;
-		}
-
-		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-
-		ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
-					      mode, fb);
-		if (ret)
-			goto out;
-
-	}
-
-	if (crtc_req->count_connectors == 0 && mode) {
-		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
-		ret = -EINVAL;
-		goto out;
-	}
-
-	if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
-		DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
-			  crtc_req->count_connectors);
-		ret = -EINVAL;
-		goto out;
 	}
 
 	if (crtc_req->count_connectors > 0) {
-		u32 out_id;
+		uint32_t __user *set_connectors_ptr =
+				(uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
 
 		/* Avoid unbounded kernel memory allocation */
 		if (crtc_req->count_connectors > config->num_connector) {
@@ -2763,52 +2945,97 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 			goto out;
 		}
 
-		connector_set = kmalloc(crtc_req->count_connectors *
-					sizeof(struct drm_connector *),
+		connector_ids = kmalloc(crtc_req->count_connectors *
+					sizeof(connector_ids[0]),
 					GFP_KERNEL);
-		if (!connector_set) {
+		if (!connector_ids) {
 			ret = -ENOMEM;
 			goto out;
 		}
 
 		for (i = 0; i < crtc_req->count_connectors; i++) {
-			set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
+			u32 out_id;
+
 			if (get_user(out_id, &set_connectors_ptr[i])) {
 				ret = -EFAULT;
 				goto out;
 			}
-
-			connector = drm_connector_find(dev, out_id);
-			if (!connector) {
-				DRM_DEBUG_KMS("Connector id %d unknown\n",
-						out_id);
-				ret = -ENOENT;
-				goto out;
-			}
-			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-					connector->base.id,
-					connector->name);
-
-			connector_set[i] = connector;
+			connector_ids[i] = out_id;
 		}
 	}
 
-	set.crtc = crtc;
-	set.x = crtc_req->x;
-	set.y = crtc_req->y;
-	set.mode = mode;
-	set.connectors = connector_set;
-	set.num_connectors = crtc_req->count_connectors;
-	set.fb = fb;
-	ret = drm_mode_set_config_internal(&set);
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state)) {
+		ret = PTR_ERR(state);
+		goto out;
+	}
 
-out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
+retry:
+	ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+	if (ret)
+		goto out;
+	ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
+		goto out;
 
-	kfree(connector_set);
-	drm_mode_destroy(dev, mode);
-	drm_modeset_unlock_all(dev);
+	/* If connectors change, we need to check if we need to steal one
+	 * from another CRTC..  setcrtc makes this implicit, but atomic
+	 * treats it as an error so we need to handle here:
+	 */
+	ret = check_connectors(crtc, state, true,
+		connector_ids, crtc_req->count_connectors);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_connector_ids,
+			crtc_req->count_connectors * sizeof(connector_ids[0]),
+			connector_ids);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_crtc_id, crtc->base.id, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_fb_id, fb_id, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_src_x, crtc_req->x << 16, NULL);
+	if (ret)
+		goto out;
+
+	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_src_y, crtc_req->y << 16, NULL);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_check(dev, state);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_commit(dev, state);
+
+out:
+	if (state) {
+		if (ret == -EDEADLK) {
+			drm_modeset_backoff(&state->acquire_ctx);
+			goto retry;
+		}
+		dev->driver->atomic_end(dev, state);
+	}
+	mutex_unlock(&dev->mode_config.mutex);
+	if (ret)
+		kfree(connector_ids);
 	return ret;
 }
 
@@ -4319,9 +4546,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 	if (crtc->funcs->set_property)
 		ret = crtc->funcs->set_property(crtc, state, property,
 				value, blob_data);
-	if (!ret)
-		drm_object_property_set_value(&crtc->base, &crtc->propvals,
-				property, value, NULL);
 
 	return ret;
 }
@@ -4494,11 +4718,11 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-retry:
 	state = dev->driver->atomic_begin(dev, 0);
 	if (IS_ERR(state))
 		return PTR_ERR(state);
 
+retry:
 	ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
 	if (ret)
 		goto out;
@@ -4519,9 +4743,11 @@ retry:
 	ret = dev->driver->atomic_commit(dev, state);
 
 out:
-	dev->driver->atomic_end(dev, state);
-	if (ret == -EDEADLK)
+	if (ret == -EDEADLK) {
+		drm_modeset_backoff(&state->acquire_ctx);
 		goto retry;
+	}
+	dev->driver->atomic_end(dev, state);
 	return ret;
 }
 
@@ -4713,6 +4939,51 @@ out:
 	return ret;
 }
 
+static struct drm_pending_vblank_event *create_vblank_event(
+		struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
+{
+	struct drm_pending_vblank_event *e = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (file_priv->event_space < sizeof e->event) {
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		goto out;
+	}
+	file_priv->event_space -= sizeof e->event;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	e = kzalloc(sizeof *e, GFP_KERNEL);
+	if (e == NULL) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+		file_priv->event_space += sizeof e->event;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		goto out;
+	}
+
+	e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
+	e->event.base.length = sizeof e->event;
+	e->event.user_data = user_data;
+	e->base.event = &e->event.base;
+	e->base.file_priv = file_priv;
+	e->base.destroy =
+		(void (*) (struct drm_pending_event *)) kfree;
+
+out:
+	return e;
+}
+
+static void destroy_vblank_event(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_pending_vblank_event *e)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	file_priv->event_space += sizeof e->event;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+	kfree(e);
+}
+
 /**
  * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
  * @dev: DRM device
@@ -4735,10 +5006,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_page_flip *page_flip = data;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_crtc *crtc;
-	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
 	struct drm_pending_vblank_event *e = NULL;
-	unsigned long flags;
+	struct drm_atomic_state *state;
 	int ret = -EINVAL;
 
 	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -4752,92 +5023,43 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if (!crtc)
 		return -ENOENT;
 
-	drm_modeset_lock(&crtc->mutex, NULL);
-	if (crtc->primary->fb == NULL) {
-		/* The framebuffer is currently unbound, presumably
-		 * due to a hotplug event, that userspace has not
-		 * yet discovered.
-		 */
-		ret = -EBUSY;
-		goto out;
-	}
-
-	if (crtc->funcs->page_flip == NULL)
-		goto out;
-
-	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
-	if (!fb) {
-		ret = -ENOENT;
-		goto out;
-	}
-
-	ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
-	if (ret)
-		goto out;
-
-	if (crtc->primary->fb->pixel_format != fb->pixel_format) {
-		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
-		ret = -EINVAL;
-		goto out;
-	}
+	state = dev->driver->atomic_begin(dev,
+			page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
 
+retry:
 	if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-		ret = -ENOMEM;
-		spin_lock_irqsave(&dev->event_lock, flags);
-		if (file_priv->event_space < sizeof e->event) {
-			spin_unlock_irqrestore(&dev->event_lock, flags);
+		e = create_vblank_event(dev, file_priv, page_flip->user_data);
+		if (!e) {
+			ret = -ENOMEM;
 			goto out;
 		}
-		file_priv->event_space -= sizeof e->event;
-		spin_unlock_irqrestore(&dev->event_lock, flags);
-
-		e = kzalloc(sizeof *e, GFP_KERNEL);
-		if (e == NULL) {
-			spin_lock_irqsave(&dev->event_lock, flags);
-			file_priv->event_space += sizeof e->event;
-			spin_unlock_irqrestore(&dev->event_lock, flags);
+		ret = dev->driver->atomic_set_event(dev, state, &crtc->base, e);
+		if (ret) {
 			goto out;
 		}
-
-		e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
-		e->event.base.length = sizeof e->event;
-		e->event.user_data = page_flip->user_data;
-		e->base.event = &e->event.base;
-		e->base.file_priv = file_priv;
-		e->base.destroy =
-			(void (*) (struct drm_pending_event *)) kfree;
 	}
 
-	old_fb = crtc->primary->fb;
-	ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
-	if (ret) {
-		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-			spin_lock_irqsave(&dev->event_lock, flags);
-			file_priv->event_space += sizeof e->event;
-			spin_unlock_irqrestore(&dev->event_lock, flags);
-			kfree(e);
-		}
-		/* Keep the old fb, don't unref it. */
-		old_fb = NULL;
-	} else {
-		/*
-		 * Warn if the driver hasn't properly updated the crtc->fb
-		 * field to reflect that the new framebuffer is now used.
-		 * Failing to do so will screw with the reference counting
-		 * on framebuffers.
-		 */
-		WARN_ON(crtc->primary->fb != fb);
-		/* Unref only the old framebuffer. */
-		fb = NULL;
-	}
+	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_fb_id, page_flip->fb_id, NULL);
+	if (ret)
+		goto out;
 
-out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
-	if (old_fb)
-		drm_framebuffer_unreference(old_fb);
-	drm_modeset_unlock(&crtc->mutex);
+	ret = dev->driver->atomic_check(dev, state);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_commit(dev, state);
 
+out:
+	if (ret && e)
+		destroy_vblank_event(dev, file_priv, e);
+	if (ret == -EDEADLK) {
+		drm_modeset_backoff(&state->acquire_ctx);
+		goto retry;
+	}
+	dev->driver->atomic_end(dev, state);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 0dc0780..300649b 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -36,6 +36,7 @@
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -340,10 +341,11 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 		bool lockless)
 {
 	struct drm_device *dev = fb_helper->dev;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_plane *plane;
 	bool error = false;
-	void *state;
-	int i;
+	struct drm_atomic_state *state;
+	int ret, i;
 
 	state = dev->driver->atomic_begin(dev, lockless ?
 			DRM_MODE_ATOMIC_NOLOCK : 0);
@@ -352,6 +354,14 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 		return true;
 	}
 
+retry:
+	ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+	if (ret)
+		goto out;
+	ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
+		goto out;
+
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head)
 		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
 			drm_plane_force_disable(plane, state);
@@ -362,8 +372,15 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 	else
 		dev->driver->atomic_commit(dev, state);
 
+out:
+	if (ret == -EDEADLK) {
+		drm_modeset_backoff(&state->acquire_ctx);
+		goto retry;
+	}
 	dev->driver->atomic_end(dev, state);
 
+	drm_modeset_lock_all(dev);
+
 	for (i = 0; i < fb_helper->crtc_count; i++) {
 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
 		struct drm_crtc *crtc = mode_set->crtc;
@@ -379,6 +396,9 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 		if (ret)
 			error = true;
 	}
+
+	drm_modeset_unlock_all(dev);
+
 	return error;
 }
 /**
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 0de123a..200276d 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -957,7 +957,7 @@ EXPORT_SYMBOL(drm_vblank_get);
  */
 int drm_crtc_vblank_get(struct drm_crtc *crtc)
 {
-	return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+	return drm_vblank_get(crtc->dev, crtc->index);
 }
 EXPORT_SYMBOL(drm_crtc_vblank_get);
 
@@ -994,7 +994,7 @@ EXPORT_SYMBOL(drm_vblank_put);
  */
 void drm_crtc_vblank_put(struct drm_crtc *crtc)
 {
-	drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+	drm_vblank_put(crtc->dev, crtc->index);
 }
 EXPORT_SYMBOL(drm_crtc_vblank_put);
 
@@ -1058,7 +1058,7 @@ EXPORT_SYMBOL(drm_vblank_off);
  */
 void drm_crtc_vblank_off(struct drm_crtc *crtc)
 {
-	drm_vblank_off(crtc->dev, drm_crtc_index(crtc));
+	drm_vblank_off(crtc->dev, crtc->index);
 }
 EXPORT_SYMBOL(drm_crtc_vblank_off);
 
@@ -1099,7 +1099,7 @@ EXPORT_SYMBOL(drm_vblank_on);
  */
 void drm_crtc_vblank_on(struct drm_crtc *crtc)
 {
-	drm_vblank_on(crtc->dev, drm_crtc_index(crtc));
+	drm_vblank_on(crtc->dev, crtc->index);
 }
 EXPORT_SYMBOL(drm_crtc_vblank_on);
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 4cb016b..0b7af3f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -14,6 +14,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 
 #include "exynos_drm_crtc.h"
 #include "exynos_drm_drv.h"
@@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
 	struct drm_device *dev = crtc->dev;
 	struct exynos_drm_private *dev_priv = dev->dev_private;
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
 
 	if (property == dev_priv->crtc_mode_property) {
 		enum exynos_crtc_mode mode = val;
@@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
 		return 0;
 	}
 
-	return -EINVAL;
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
 }
 
 static struct drm_crtc_funcs exynos_crtc_funcs = {
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
index 6672732..5b6eee9 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
 	.cursor_move = gma_crtc_cursor_move,
 	.gamma_set = gma_crtc_gamma_set,
 	.set_config = gma_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = gma_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index 87b50ba..79b5692 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = {
 	.cursor_move = gma_crtc_cursor_move,
 	.gamma_set = gma_crtc_gamma_set,
 	.set_config = gma_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = gma_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 4fa9dd1..830b8f2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -11308,6 +11308,7 @@ out_config:
 static const struct drm_crtc_funcs intel_crtc_funcs = {
 	.gamma_set = intel_crtc_gamma_set,
 	.set_config = intel_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = intel_crtc_destroy,
 	.page_flip = intel_crtc_page_flip,
 };
@@ -11765,7 +11766,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 
 	drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
 
-	WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe);
+	WARN_ON(intel_crtc->base.index != intel_crtc->pipe);
 	return;
 
 fail:
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 45f04de..730bed9 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = {
 	.cursor_move = mga_crtc_cursor_move,
 	.gamma_set = mga_crtc_gamma_set,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = mga_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index 60d7e73..cb68082 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -469,8 +469,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
 }
 
 #define CURSOR_WIDTH 64
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index deb6647..54d20a5 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -387,8 +387,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
 }
 
 static const struct drm_crtc_funcs mdp5_crtc_funcs = {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 41be342..9e24632 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
 	.cursor_move = nv04_crtc_cursor_move,
 	.gamma_set = nv_crtc_gamma_set,
 	.set_config = nouveau_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = nouveau_crtc_page_flip,
 	.destroy = nv_crtc_destroy,
 };
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 4c534b7..65d477d 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1331,6 +1331,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
 	.cursor_move = nv50_crtc_cursor_move,
 	.gamma_set = nv50_crtc_gamma_set,
 	.set_config = nouveau_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = nv50_crtc_destroy,
 	.page_flip = nouveau_crtc_page_flip,
 };
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index a75934d..772687b 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct omap_drm_private *priv = crtc->dev->dev_private;
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	int ret;
+
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
 
 	if (property == priv->rotation_prop) {
-		crtc->invert_dimensions =
+		cstate->invert_dimensions =
 				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
 	}
 
-	return omap_plane_set_property(omap_crtc->plane, state,
+	ret = omap_plane_set_property(omap_crtc->plane, state,
 			property, val, blob_data);
+	if (ret)
+		ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+	return ret;
 }
 
 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 9630a32..4b4bc42 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev)
 		 */
 		for (i = 0; i < priv->num_crtcs; i++) {
 			drm_object_property_set_value(&priv->crtcs[i]->base,
-					&priv->crtcs[i]->propvals,
+					&priv->crtcs[i]->state->propvals,
 					priv->rotation_prop, 0, NULL);
 		}
 
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 3dcc70c..118e498 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -29,6 +29,7 @@
 #include "qxl_drv.h"
 #include "qxl_object.h"
 #include "drm_crtc_helper.h"
+#include "drm_atomic.h"
 
 static bool qxl_head_enabled(struct qxl_head *head)
 {
@@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
 	.cursor_set2 = qxl_crtc_cursor_set2,
 	.cursor_move = qxl_crtc_cursor_move,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = qxl_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 13896ed..c89c20d 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -32,6 +32,7 @@
 
 #include <linux/pm_runtime.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_edid.h>
 
 #include <linux/gcd.h>
@@ -622,6 +623,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = {
 	.cursor_move = radeon_crtc_cursor_move,
 	.gamma_set = radeon_crtc_gamma_set,
 	.set_config = radeon_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = radeon_crtc_destroy,
 	.page_flip = radeon_crtc_page_flip,
 };
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 299267d..f5a3d55 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -17,6 +17,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.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>
 
@@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
 static const struct drm_crtc_funcs crtc_funcs = {
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = rcar_du_crtc_page_flip,
 };
 
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index 78b2a86..4022025 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -17,6 +17,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.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>
 
@@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
 static const struct drm_crtc_funcs crtc_funcs = {
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = shmob_drm_crtc_page_flip,
 };
 
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index d642d4a0..95ffae7 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
 		.destroy        = tilcdc_crtc_destroy,
 		.set_config     = drm_crtc_helper_set_config,
+		.set_property   = drm_atomic_crtc_set_property,
 		.page_flip      = tilcdc_crtc_page_flip,
 };
 
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index dc145d3..b597a25 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -14,6 +14,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include "udl_drv.h"
 
 /*
@@ -403,6 +404,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
 
 static const struct drm_crtc_funcs udl_crtc_funcs = {
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = udl_crtc_destroy,
 	.page_flip = udl_crtc_page_flip,
 };
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index 15e185a..6ed37de 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
 	.cursor_move = vmw_du_crtc_cursor_move,
 	.gamma_set = vmw_du_crtc_gamma_set,
 	.destroy = vmw_ldu_crtc_destroy,
+	.set_property = drm_atomic_crtc_set_property,
 	.set_config = vmw_ldu_crtc_set_config,
 };
 
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index b295463..20fda38 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
 	.gamma_set = vmw_du_crtc_gamma_set,
 	.destroy = vmw_sou_crtc_destroy,
 	.set_config = vmw_sou_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = vmw_du_page_flip,
 };
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 78e93ec..7946b7f 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -70,6 +70,9 @@
 struct drm_atomic_funcs {
 	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);
+
+	int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
+	int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
 };
 
 const extern struct drm_atomic_funcs drm_atomic_funcs;
@@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane,
 	return funcs->commit_plane_state(plane, pstate);
 }
 
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data);
+struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc,
+		struct drm_atomic_state *state);
+
+static inline int
+drm_atomic_check_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			crtc->dev->driver->atomic_funcs;
+	return funcs->check_crtc_state(crtc, cstate);
+}
+
+static inline int
+drm_atomic_commit_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			crtc->dev->driver->atomic_funcs;
+	return funcs->commit_crtc_state(crtc, cstate);
+}
+
 /**
  * struct drm_atomic_state - the state object used by atomic helpers
  */
@@ -118,6 +145,8 @@ struct drm_atomic_state {
 	uint32_t flags;
 	struct drm_plane **planes;
 	struct drm_plane_state **pstates;
+	struct drm_crtc **crtcs;
+	struct drm_crtc_state **cstates;
 
 	bool committed;
 	bool checked;       /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index b10eaac..084fce0 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -287,6 +287,10 @@ struct drm_crtc_funcs {
 			 struct drm_pending_vblank_event *event,
 			 uint32_t flags);
 
+	struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc);
+	void (*destroy_state)(struct drm_crtc *crtc,
+			    struct drm_crtc_state *cstate);
+
 	int (*set_property)(struct drm_crtc *crtc,
 			    struct drm_atomic_state *state,
 			    struct drm_property *property, uint64_t val,
@@ -294,21 +298,52 @@ struct drm_crtc_funcs {
 };
 
 /**
+ * drm_crtc_state - mutable crtc state
+ * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
+ *    invert the width/height of the crtc.  This is used if the driver
+ *    is performing 90 or 270 degree rotated scanout
+ * @mode_valid: a valid mode has been set
+ * @set_config: needs modeset (crtc->set_config())
+ * @connectors_change: the connector-ids array has changed
+ * @num_connector_ids: the number of connector-ids
+ * @connector_ids: array of connector ids
+ * @mode: current mode timings
+ * @event: pending pageflip event
+ * @propvals: property values
+ * @state: current global/toplevel state object (for atomic) while an
+ *    update is in progress, NULL otherwise.
+ */
+struct drm_crtc_state {
+	bool invert_dimensions : 1;
+	bool mode_valid        : 1;
+
+	/* transient state, only valid during atomic operation: */
+	bool set_config        : 1;
+	bool connectors_change : 1;
+
+	uint8_t num_connector_ids;
+	uint32_t *connector_ids;
+	struct drm_mode_modeinfo mode;
+
+	struct drm_pending_vblank_event *event;
+
+	struct drm_object_property_values propvals;
+
+	struct drm_atomic_state *state;
+};
+
+/**
  * drm_crtc - central CRTC control structure
  * @dev: parent DRM device
  * @head: list management
+ * @index: CRTC number, 0..n
  * @mutex: per-CRTC locking
  * @base: base KMS object for ID tracking etc.
  * @primary: primary plane for this CRTC
  * @cursor: cursor plane for this CRTC
+ * @state: the mutable state
  * @enabled: is this CRTC enabled?
- * @mode: current mode timings
  * @hwmode: mode timings as programmed to hw regs
- * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
- *    invert the width/height of the crtc.  This is used if the driver
- *    is performing 90 or 270 degree rotated scanout
- * @x: x position on screen
- * @y: y position on screen
  * @funcs: CRTC control functions
  * @gamma_size: size of gamma ramp
  * @gamma_store: gamma ramp values
@@ -325,6 +360,8 @@ struct drm_crtc {
 	struct drm_device *dev;
 	struct list_head head;
 
+	int index;
+
 	/**
 	 * crtc mutex
 	 *
@@ -344,23 +381,19 @@ struct drm_crtc {
 	int cursor_x;
 	int cursor_y;
 
+	struct drm_crtc_state *state;
+
 	/* Temporary tracking of the old fb while a modeset is ongoing. Used
 	 * by drm_mode_set_config_internal to implement correct refcounting. */
 	struct drm_framebuffer *old_fb;
 
 	bool enabled;
 
-	/* Requested mode from modesetting. */
-	struct drm_display_mode mode;
-
 	/* Programmed mode in hw, after adjustments for encoders,
 	 * crtc, panel scaling etc. Needed for timestamping etc.
 	 */
 	struct drm_display_mode hwmode;
 
-	bool invert_dimensions;
-
-	int x, y;
 	const struct drm_crtc_funcs *funcs;
 
 	/* CRTC gamma size for reporting to userspace */
@@ -374,9 +407,15 @@ struct drm_crtc {
 	void *helper_private;
 
 	struct drm_object_properties properties;
-	struct drm_object_property_values propvals;
-};
 
+	/* These are (temporary) duplicate information from what is in the
+	 * drm_crtc_state struct..  keeping duplicate copy here makes the
+	 * switch to atomic far less intrusive.  Once all the drivers and
+	 * the crtc/fb helpers are updated, then we can remove these:
+	 */
+	int x, y;
+	struct drm_display_mode mode;
+};
 
 /**
  * drm_connector_funcs - control connectors on a given device
@@ -888,6 +927,8 @@ struct drm_mode_config {
 	struct drm_property *prop_crtc_h;
 	struct drm_property *prop_fb_id;
 	struct drm_property *prop_crtc_id;
+	struct drm_property *prop_connector_ids;
+	struct drm_property *prop_mode;
 	struct drm_property *edid_property;
 	struct drm_property *dpms_property;
 	struct drm_property *path_property;
@@ -949,7 +990,8 @@ extern int drm_crtc_init(struct drm_device *dev,
 			 struct drm_crtc *crtc,
 			 const struct drm_crtc_funcs *funcs);
 extern void drm_crtc_cleanup(struct drm_crtc *crtc);
-extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
+struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate);
 
 /**
  * drm_crtc_mask - find the mask of a registered CRTC
@@ -960,9 +1002,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
  */
 static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
 {
-	return 1 << drm_crtc_index(crtc);
+	return 1 << crtc->index;
 }
 
+extern int drm_crtc_check_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state);
+extern void drm_crtc_commit_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state);
+extern int drm_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_crtc_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data);
+
 extern void drm_connector_ida_init(void);
 extern void drm_connector_ida_destroy(void);
 extern int drm_connector_init(struct drm_device *dev,
@@ -1040,6 +1091,7 @@ extern void drm_fb_release(struct drm_file *file_priv);
 extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
 extern void drm_mode_group_destroy(struct drm_mode_group *group);
 extern void drm_reinit_primary_mode_group(struct drm_device *dev);
+extern int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in);
 extern bool drm_probe_ddc(struct i2c_adapter *adapter);
 extern struct edid *drm_get_edid(struct drm_connector *connector,
 				 struct i2c_adapter *adapter);
@@ -1268,6 +1320,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
 	return mo ? obj_to_blob(mo) : NULL;
 }
 
+static inline struct drm_crtc_state *
+drm_crtc_create_state(struct drm_crtc *crtc)
+{
+	if (crtc->funcs->create_state)
+		return crtc->funcs->create_state(crtc);
+	return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL);
+}
+
+static inline void
+drm_crtc_destroy_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	kfree(cstate->connector_ids);
+	if (crtc->funcs->destroy_state)
+		crtc->funcs->destroy_state(crtc, cstate);
+	else
+		kfree(cstate);
+}
+
 static inline struct drm_plane_state *
 drm_plane_create_state(struct drm_plane *plane)
 {
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 6/7] drm/msm: add atomic support
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
                   ` (4 preceding siblings ...)
  2014-07-23 19:38 ` [PATCH 5/7] drm: convert crtc " Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-23 19:38 ` [PATCH 7/7] drm: Fix up the atomic legacy paths so they work Rob Clark
  2014-07-24 12:25 ` [PATCH 0/7] prepare for atomic.. the great propertyification Daniel Vetter
  7 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/msm/Makefile              |   1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c  |  57 ++++++------
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c   |   6 ++
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h   |   1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c |   5 --
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c  |  56 ++++++------
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c   |   6 ++
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h   |   2 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c |   5 --
 drivers/gpu/drm/msm/msm_atomic.c          | 141 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/msm_drv.c             |  22 ++++-
 drivers/gpu/drm/msm/msm_drv.h             |   7 ++
 drivers/gpu/drm/msm/msm_gem.c             |  24 +----
 drivers/gpu/drm/msm/msm_gem.h             |  13 +++
 drivers/gpu/drm/msm/msm_kms.h             |   1 +
 15 files changed, 253 insertions(+), 94 deletions(-)
 create mode 100644 drivers/gpu/drm/msm/msm_atomic.c

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 93ca49c..5e6af3c 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -27,6 +27,7 @@ msm-y := \
 	mdp/mdp5/mdp5_kms.o \
 	mdp/mdp5/mdp5_plane.o \
 	mdp/mdp5/mdp5_smp.o \
+	msm_atomic.o \
 	msm_drv.o \
 	msm_fb.o \
 	msm_gem.o \
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index cb68082..a7c1eea 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -52,7 +52,6 @@ struct mdp4_crtc {
 
 	/* if there is a pending flip, these will be non-null: */
 	struct drm_pending_vblank_event *event;
-	struct msm_fence_cb pageflip_cb;
 
 #define PENDING_CURSOR 0x1
 #define PENDING_FLIP   0x2
@@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
 	mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank);
 }
 
-static void crtc_flush(struct drm_crtc *crtc)
+void mdp4_crtc_flush(struct drm_crtc *crtc)
 {
+	struct msm_drm_private *priv = crtc->dev->dev_private;
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct mdp4_kms *mdp4_kms = get_kms(crtc);
 	uint32_t i, flush = 0;
 
+	if (priv->pending_crtcs & (1 << crtc->index))
+		return;
+
 	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
 		struct drm_plane *plane = mdp4_crtc->planes[i];
 		if (plane) {
@@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
 	 * so that we can safely queue unref to current fb (ie. next
 	 * vblank we know hw is done w/ previous scanout_fb).
 	 */
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 
 	if (mdp4_crtc->scanout_fb)
 		drm_flip_work_queue(&mdp4_crtc->unref_fb_work,
@@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
-static void pageflip_cb(struct msm_fence_cb *cb)
-{
-	struct mdp4_crtc *mdp4_crtc =
-		container_of(cb, struct mdp4_crtc, pageflip_cb);
-	struct drm_crtc *crtc = &mdp4_crtc->base;
-	struct drm_framebuffer *fb = crtc->primary->fb;
-
-	if (!fb)
-		return;
-
-	drm_framebuffer_reference(fb);
-	mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
-	update_scanout(crtc, fb);
-}
-
 static void unref_fb_worker(struct drm_flip_work *work, void *val)
 {
 	struct mdp4_crtc *mdp4_crtc =
@@ -404,7 +392,7 @@ static void mdp4_crtc_prepare(struct drm_crtc *crtc)
 static void mdp4_crtc_commit(struct drm_crtc *crtc)
 {
 	mdp4_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 	/* drop the ref to mdp clk's that we got in prepare: */
 	mdp4_disable(get_kms(crtc));
 }
@@ -446,23 +434,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
-	struct drm_gem_object *obj;
 	unsigned long flags;
 
+	spin_lock_irqsave(&dev->event_lock, flags);
 	if (mdp4_crtc->event) {
 		dev_err(dev->dev, "already pending flip!\n");
+		spin_unlock_irqrestore(&dev->event_lock, flags);
 		return -EBUSY;
 	}
 
-	obj = msm_framebuffer_bo(new_fb, 0);
-
-	spin_lock_irqsave(&dev->event_lock, flags);
 	mdp4_crtc->event = event;
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	update_fb(crtc, new_fb);
 
-	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
+
+	/* grab extra ref for update_scanout() */
+	drm_framebuffer_reference(new_fb);
+	mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb);
+	update_scanout(crtc, new_fb);
+
+	return 0;
 }
 
 static int mdp4_crtc_set_property(struct drm_crtc *crtc,
@@ -596,7 +588,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 	mdp4_crtc->cursor.y = y;
 	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
 
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 	request_pending(crtc, PENDING_CURSOR);
 
 	return 0;
@@ -633,8 +625,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	pending = atomic_xchg(&mdp4_crtc->pending, 0);
 
 	if (pending & PENDING_FLIP) {
-		complete_flip(crtc, NULL);
-		drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
+		if (priv->pending_crtcs & (1 << crtc->index)) {
+			/* our update hasn't been flushed yet: */
+			request_pending(crtc, PENDING_FLIP);
+		} else {
+			complete_flip(crtc, NULL);
+			drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
+		}
 	}
 
 	if (pending & PENDING_CURSOR) {
@@ -648,7 +645,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);
 	struct drm_crtc *crtc = &mdp4_crtc->base;
 	DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 }
 
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
@@ -728,7 +725,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp4_pipe pipe_id,
 	mdp4_crtc->planes[pipe_id] = plane;
 	blend_setup(crtc);
 	if (mdp4_crtc->enabled && (plane != mdp4_crtc->plane))
-		crtc_flush(crtc);
+		mdp4_crtc_flush(crtc);
 }
 
 void mdp4_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
@@ -790,8 +787,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
 	ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
 			"unref cursor", unref_cursor_worker);
 
-	INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
-
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);
 	drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
index 733646c..bacee89 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
@@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
 	return mdp4_dtv_round_pixclk(encoder, rate);
 }
 
+static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc)
+{
+	mdp4_crtc_flush(crtc);
+}
+
 static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
 {
 	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
@@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = {
 		.disable_vblank  = mdp4_disable_vblank,
 		.get_format      = mdp_get_format,
 		.round_pixclk    = mdp4_round_pixclk,
+		.flush           = mdp4_flush,
 		.preclose        = mdp4_preclose,
 		.destroy         = mdp4_destroy,
 	},
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
index 3225da8..c3cbd1a 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
@@ -185,6 +185,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane);
 struct drm_plane *mdp4_plane_init(struct drm_device *dev,
 		enum mdp4_pipe pipe_id, bool private_plane);
 
+void mdp4_crtc_flush(struct drm_crtc *crtc);
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
 void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
index 4c92985..f35848d 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
@@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
 
 	mdp4_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
-
-	drm_framebuffer_reference(fb);
-
 	return mdp4_plane_mode_set(plane, crtc, fb,
 			crtc_x, crtc_y, crtc_w, crtc_h,
 			src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 54d20a5..70f8fa9 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -35,7 +35,6 @@ struct mdp5_crtc {
 
 	/* if there is a pending flip, these will be non-null: */
 	struct drm_pending_vblank_event *event;
-	struct msm_fence_cb pageflip_cb;
 
 #define PENDING_CURSOR 0x1
 #define PENDING_FLIP   0x2
@@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
 	mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
 }
 
-static void crtc_flush(struct drm_crtc *crtc)
+void mdp5_crtc_flush(struct drm_crtc *crtc)
 {
+	struct msm_drm_private *priv = crtc->dev->dev_private;
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct mdp5_kms *mdp5_kms = get_kms(crtc);
 	int id = mdp5_crtc->id;
 	uint32_t i, flush = 0;
 
+	if (priv->pending_crtcs & (1 << crtc->index))
+		return;
+
 	for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
 		struct drm_plane *plane = mdp5_crtc->planes[i];
 		if (plane) {
@@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
 	 * so that we can safely queue unref to current fb (ie. next
 	 * vblank we know hw is done w/ previous scanout_fb).
 	 */
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 
 	if (mdp5_crtc->scanout_fb)
 		drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
@@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 	}
 }
 
-static void pageflip_cb(struct msm_fence_cb *cb)
-{
-	struct mdp5_crtc *mdp5_crtc =
-		container_of(cb, struct mdp5_crtc, pageflip_cb);
-	struct drm_crtc *crtc = &mdp5_crtc->base;
-	struct drm_framebuffer *fb = mdp5_crtc->fb;
-
-	if (!fb)
-		return;
-
-	drm_framebuffer_reference(fb);
-	mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
-	update_scanout(crtc, fb);
-}
-
 static void unref_fb_worker(struct drm_flip_work *work, void *val)
 {
 	struct mdp5_crtc *mdp5_crtc =
@@ -322,7 +310,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc)
 static void mdp5_crtc_commit(struct drm_crtc *crtc)
 {
 	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 	/* drop the ref to mdp clk's that we got in prepare: */
 	mdp5_disable(get_kms(crtc));
 }
@@ -364,23 +352,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
 {
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
-	struct drm_gem_object *obj;
 	unsigned long flags;
 
+	spin_lock_irqsave(&dev->event_lock, flags);
 	if (mdp5_crtc->event) {
 		dev_err(dev->dev, "already pending flip!\n");
+		spin_unlock_irqrestore(&dev->event_lock, flags);
 		return -EBUSY;
 	}
 
-	obj = msm_framebuffer_bo(new_fb, 0);
-
-	spin_lock_irqsave(&dev->event_lock, flags);
 	mdp5_crtc->event = event;
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	update_fb(crtc, new_fb);
 
-	return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb);
+	/* grab extra ref for update_scanout() */
+	drm_framebuffer_reference(new_fb);
+	mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb);
+	update_scanout(crtc, new_fb);
+
+	return 0;
 }
 
 static int mdp5_crtc_set_property(struct drm_crtc *crtc,
@@ -422,8 +413,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	pending = atomic_xchg(&mdp5_crtc->pending, 0);
 
 	if (pending & PENDING_FLIP) {
-		complete_flip(crtc, NULL);
-		drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
+		if (priv->pending_crtcs & (1 << crtc->index)) {
+			/* our update hasn't been flushed yet: */
+			request_pending(crtc, PENDING_FLIP);
+		} else {
+			complete_flip(crtc, NULL);
+			drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
+		}
 	}
 }
 
@@ -432,7 +428,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err);
 	struct drm_crtc *crtc = &mdp5_crtc->base;
 	DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 }
 
 uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
@@ -499,7 +495,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
 			MDP5_CTL_OP_MODE(MODE_NONE) |
 			MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
 
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 }
 
 static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
@@ -515,7 +511,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
 	mdp5_crtc->planes[pipe_id] = plane;
 	blend_setup(crtc);
 	if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
-		crtc_flush(crtc);
+		mdp5_crtc_flush(crtc);
 }
 
 void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
@@ -561,8 +557,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
 	if (ret)
 		goto fail;
 
-	INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
-
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
 	drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index 31a2c63..f0822f5 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -206,6 +206,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
 	return rate;
 }
 
+static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc)
+{
+	mdp5_crtc_flush(crtc);
+}
+
 static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
 {
 	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
@@ -239,6 +244,7 @@ static const struct mdp_kms_funcs kms_funcs = {
 		.disable_vblank  = mdp5_disable_vblank,
 		.get_format      = mdp_get_format,
 		.round_pixclk    = mdp5_round_pixclk,
+		.flush           = mdp5_flush,
 		.preclose        = mdp5_preclose,
 		.destroy         = mdp5_destroy,
 	},
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 5bf340d..ebf7ab5 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -223,8 +223,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
 struct drm_plane *mdp5_plane_init(struct drm_device *dev,
 		enum mdp5_pipe pipe, bool private_plane);
 
+void mdp5_crtc_flush(struct drm_crtc *crtc);
 uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
-
 void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
 		enum mdp5_intf intf_id);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 13820b4..2ab32ad 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
 
 	mdp5_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
-
-	drm_framebuffer_reference(fb);
-
 	return mdp5_plane_mode_set(plane, crtc, fb,
 			crtc_x, crtc_y, crtc_w, crtc_h,
 			src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
new file mode 100644
index 0000000..2fe362a
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+#include "msm_gem.h"
+
+struct msm_async_commit {
+	struct drm_atomic_state *state;
+	uint32_t fence;
+	struct msm_fence_cb fence_cb;
+};
+
+static void fence_cb(struct msm_fence_cb *cb);
+static int commit_sync(struct drm_device *dev, void *state, bool unlocked);
+
+static struct msm_async_commit *new_commit(struct drm_atomic_state *state)
+{
+	struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+	if (!c)
+		return NULL;
+
+	drm_atomic_state_reference(state);
+	c->state = state;
+	INIT_FENCE_CB(&c->fence_cb, fence_cb);
+
+	return c;
+}
+static void free_commit(struct msm_async_commit *c)
+{
+	drm_atomic_state_unreference(c->state);
+	kfree(c);
+}
+
+static void fence_cb(struct msm_fence_cb *cb)
+{
+	struct msm_async_commit *c =
+			container_of(cb, struct msm_async_commit, fence_cb);
+	commit_sync(c->state->dev, c->state, true);
+	free_commit(c);
+}
+
+static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc,
+		struct drm_framebuffer *fb)
+{
+	struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0);
+	c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
+}
+
+static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb)
+{
+	// XXX TODO wait..
+	return 0;
+}
+
+#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb)
+
+static int commit_sync(struct drm_device *dev, void *state, bool unlocked)
+{
+	struct drm_atomic_state *a = state;
+	struct msm_drm_private *priv = dev->dev_private;
+	struct msm_kms *kms = priv->kms;
+	int ncrtcs = dev->mode_config.num_crtc;
+	uint32_t pending_crtcs = 0;
+	int i, ret;
+
+	for (i = 0; i < ncrtcs; i++)
+		if (a->crtcs[i])
+			pending_crtcs |= (1 << a->crtcs[i]->index);
+
+	mutex_lock(&dev->struct_mutex);
+	WARN_ON(priv->pending_crtcs & pending_crtcs);
+	priv->pending_crtcs |= pending_crtcs;
+	mutex_unlock(&dev->struct_mutex);
+
+	if (unlocked)
+		ret = drm_atomic_commit_unlocked(dev, state);
+	else
+		ret = drm_atomic_commit(dev, state);
+
+	mutex_lock(&dev->struct_mutex);
+	priv->pending_crtcs &= ~pending_crtcs;
+	mutex_unlock(&dev->struct_mutex);
+
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ncrtcs; i++)
+		if (a->crtcs[i])
+			kms->funcs->flush(kms, a->crtcs[i]);
+
+	return 0;
+}
+
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state)
+{
+	struct drm_atomic_state *a = state;
+	int nplanes = dev->mode_config.num_total_plane;
+	int i;
+
+	if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) {
+		/* non-block mode: defer commit until fb's are ready */
+		struct msm_async_commit *c = new_commit(state);
+
+		if (!c)
+			return -ENOMEM;
+
+		for (i = 0; i < nplanes; i++)
+			if (pending_fb(a->pstates[i]))
+				add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb);
+
+		return msm_queue_fence_cb(dev, &c->fence_cb, c->fence);
+	} else {
+		/* blocking mode: wait until fb's are ready */
+		int ret = 0;
+
+		for (i = 0; i < nplanes && !ret; i++)
+			if (pending_fb(a->pstates[i]))
+				ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb);
+
+		if (ret)
+			return ret;
+
+		return commit_sync(dev, state, false);
+	}
+}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 81531ce1..4d0d0a9 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -646,6 +646,26 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
 	return ret;
 }
 
+int msm_queue_fence_cb(struct drm_device *dev,
+		struct msm_fence_cb *cb, uint32_t fence)
+{
+	struct msm_drm_private *priv = dev->dev_private;
+	int ret = 0;
+
+	mutex_lock(&dev->struct_mutex);
+	if (!list_empty(&cb->work.entry)) {
+		ret = -EINVAL;
+	} else if (fence > priv->completed_fence) {
+		cb->fence = fence;
+		list_add_tail(&cb->work.entry, &priv->fence_cbs);
+	} else {
+		queue_work(priv->wq, &cb->work);
+	}
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
 /* called from workqueue */
 void msm_update_fence(struct drm_device *dev, uint32_t fence)
 {
@@ -861,7 +881,7 @@ static struct drm_driver msm_driver = {
 	.atomic_begin       = drm_atomic_begin,
 	.atomic_set_event   = drm_atomic_set_event,
 	.atomic_check       = drm_atomic_check,
-	.atomic_commit      = drm_atomic_commit,
+	.atomic_commit      = msm_atomic_commit,
 	.atomic_end         = drm_atomic_end,
 	.atomic_funcs       = &drm_atomic_funcs,
 #ifdef CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 5c2919c..a66ea4e 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -116,6 +116,9 @@ struct msm_drm_private {
 	unsigned int num_connectors;
 	struct drm_connector *connectors[8];
 
+	/* crtc's pending atomic update: */
+	uint32_t pending_crtcs;
+
 	/* VRAM carveout, used when no IOMMU: */
 	struct {
 		unsigned long size;
@@ -149,11 +152,15 @@ int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);
 
 int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
 		struct timespec *timeout);
+int msm_queue_fence_cb(struct drm_device *dev,
+		struct msm_fence_cb *cb, uint32_t fence);
 void msm_update_fence(struct drm_device *dev, uint32_t fence);
 
 int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 		struct drm_file *file);
 
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state);
+
 int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 4b1b82a..1b205c24 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -397,23 +397,11 @@ void *msm_gem_vaddr(struct drm_gem_object *obj)
 int msm_gem_queue_inactive_cb(struct drm_gem_object *obj,
 		struct msm_fence_cb *cb)
 {
-	struct drm_device *dev = obj->dev;
-	struct msm_drm_private *priv = dev->dev_private;
 	struct msm_gem_object *msm_obj = to_msm_bo(obj);
-	int ret = 0;
+	uint32_t fence = msm_gem_fence(msm_obj,
+			MSM_PREP_READ | MSM_PREP_WRITE);
 
-	mutex_lock(&dev->struct_mutex);
-	if (!list_empty(&cb->work.entry)) {
-		ret = -EINVAL;
-	} else if (is_active(msm_obj)) {
-		cb->fence = max(msm_obj->read_fence, msm_obj->write_fence);
-		list_add_tail(&cb->work.entry, &priv->fence_cbs);
-	} else {
-		queue_work(priv->wq, &cb->work);
-	}
-	mutex_unlock(&dev->struct_mutex);
-
-	return ret;
+	return msm_queue_fence_cb(obj->dev, cb, fence);
 }
 
 void msm_gem_move_to_active(struct drm_gem_object *obj,
@@ -452,12 +440,8 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op,
 	int ret = 0;
 
 	if (is_active(msm_obj)) {
-		uint32_t fence = 0;
+		uint32_t fence = msm_gem_fence(msm_obj, op);
 
-		if (op & MSM_PREP_READ)
-			fence = msm_obj->write_fence;
-		if (op & MSM_PREP_WRITE)
-			fence = max(fence, msm_obj->read_fence);
 		if (op & MSM_PREP_NOSYNC)
 			timeout = NULL;
 
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index bfb0526..8fbbd05 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -70,6 +70,19 @@ static inline bool is_active(struct msm_gem_object *msm_obj)
 	return msm_obj->gpu != NULL;
 }
 
+static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj,
+		uint32_t op)
+{
+	uint32_t fence = 0;
+
+	if (op & MSM_PREP_READ)
+		fence = msm_obj->write_fence;
+	if (op & MSM_PREP_WRITE)
+		fence = max(fence, msm_obj->read_fence);
+
+	return fence;
+}
+
 #define MAX_CMDS 4
 
 /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 0643774..91ddffc 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -42,6 +42,7 @@ struct msm_kms_funcs {
 	const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format);
 	long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
 			struct drm_encoder *encoder);
+	void (*flush)(struct msm_kms *kms, struct drm_crtc *crtc);
 	/* cleanup: */
 	void (*preclose)(struct msm_kms *kms, struct drm_file *file);
 	void (*destroy)(struct msm_kms *kms);
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 7/7] drm: Fix up the atomic legacy paths so they work
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
                   ` (5 preceding siblings ...)
  2014-07-23 19:38 ` [PATCH 6/7] drm/msm: add atomic support Rob Clark
@ 2014-07-23 19:38 ` Rob Clark
  2014-07-24 12:25 ` [PATCH 0/7] prepare for atomic.. the great propertyification Daniel Vetter
  7 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-23 19:38 UTC (permalink / raw)
  To: dri-devel

From: Sean Paul <seanpaul@chromium.org>

BUG=chromium:336809
TEST=Tested on snow & peppy

Change-Id: Icd864ac52da9c973202f3ac4629824ef5eec2ac9
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_atomic.c |  4 ++++
 drivers/gpu/drm/drm_crtc.c   | 11 ++++++++---
 include/drm/drm_crtc.h       |  1 +
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index cc12114..5b1201f 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -626,6 +626,7 @@ swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
 	/* clear transient state (only valid during atomic update): */
 	cstate->set_config = false;
 	cstate->connectors_change = false;
+	cstate->commit_state = false;
 
 	swap(crtc->state, a->cstates[crtc->index]);
 	crtc->base.propvals = &crtc->state->propvals;
@@ -700,6 +701,9 @@ commit_crtc_state(struct drm_crtc *crtc,
 			drm_atomic_get_plane_state(crtc->primary, cstate->state);
 	int ret = -EINVAL;
 
+	if (!cstate->commit_state)
+		return 0;
+
 	if (cstate->set_config)
 		return set_config(crtc, cstate);
 
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index d5ad7e1..ca7bff9 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -777,7 +777,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 	crtc->base.propvals = &crtc->state->propvals;
 
 	list_add_tail(&crtc->head, &config->crtc_list);
-	config->num_crtc++;
+	crtc->index = config->num_crtc++;
 
 	crtc->primary = primary;
 	crtc->cursor = cursor;
@@ -898,7 +898,7 @@ static int check_connectors(struct drm_crtc *crtc,
 		if (ocrtc == crtc)
 			continue;
 
-		ostate = drm_atomic_get_crtc_state(crtc, state);
+		ostate = drm_atomic_get_crtc_state(ocrtc, state);
 		if (IS_ERR(ostate))
 			return PTR_ERR(ostate);
 
@@ -926,7 +926,6 @@ retry:
 			return -EINVAL;
 		}
 	}
-
 	return 0;
 }
 
@@ -948,6 +947,10 @@ int drm_crtc_check_state(struct drm_crtc *crtc,
 	if (!(fb && state->mode_valid))
 		return 0;
 
+	/* We're not committing this state, ignore */
+	if (!state->commit_state)
+		return 0;
+
 	hdisplay = state->mode.hdisplay;
 	vdisplay = state->mode.vdisplay;
 
@@ -1021,6 +1024,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc,
 	/* grab primary plane state now, to ensure locks are held, etc. */
 	drm_atomic_get_plane_state(crtc->primary, state->state);
 
+	state->commit_state = true;
 	drm_object_property_set_value(&crtc->base,
 			&state->propvals, property, value, blob_data);
 
@@ -1032,6 +1036,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc,
 			/* check size: */
 			if (value < sizeof(struct drm_mode_modeinfo))
 				return -EINVAL;
+
 			state->mode = *(struct drm_mode_modeinfo *)blob_data;
 			state->mode_valid = true;
 		}
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 084fce0..7afe392 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -320,6 +320,7 @@ struct drm_crtc_state {
 	/* transient state, only valid during atomic operation: */
 	bool set_config        : 1;
 	bool connectors_change : 1;
+	bool commit_state      : 1;
 
 	uint8_t num_connector_ids;
 	uint32_t *connector_ids;
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/7] drm: add atomic fxns
  2014-07-23 19:38 ` [PATCH 1/7] drm: add atomic fxns Rob Clark
@ 2014-07-23 21:34   ` Daniel Vetter
  2014-07-23 21:38     ` Daniel Vetter
  2014-07-24 10:02   ` Thierry Reding
  1 sibling, 1 reply; 20+ messages in thread
From: Daniel Vetter @ 2014-07-23 21:34 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

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@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@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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/7] drm: add atomic fxns
  2014-07-23 21:34   ` Daniel Vetter
@ 2014-07-23 21:38     ` Daniel Vetter
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-07-23 21:38 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, Jul 23, 2014 at 11:34:00PM +0200, Daniel Vetter wrote:
> 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@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.

Self-correct: NOLOCK is used for the fbdev restore code. Imo we should
drop the modeset_lock_all this one uses and switch to using the atomic
locking scheme like everywhere. That way fbdev modeset changes work
exactly like any other modeset change.

fbdev state itself can be protected with dev->mode_config.mutex, which
wraps around all the ww_mutex dancing.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 4/7] drm: convert plane to properties/state
  2014-07-23 19:38 ` [PATCH 4/7] drm: convert plane to properties/state Rob Clark
@ 2014-07-24  0:42   ` Matt Roper
  0 siblings, 0 replies; 20+ messages in thread
From: Matt Roper @ 2014-07-24  0:42 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, Jul 23, 2014 at 03:38:17PM -0400, Rob Clark wrote:
...
> @@ -2702,10 +2895,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
>  	 * setplane_internal will take care of deref'ing either the old or new
>  	 * framebuffer depending on success.
>  	 */
> -	ret = setplane_internal(crtc->cursor, crtc, fb,
> +	ret = setplane_internal(crtc->cursor,
> +				crtc->base.id, fb ? fb->base.id : 0,
>  				crtc_x, crtc_y, crtc_w, crtc_h,
>  				0, 0, src_w, src_h);
> -
> +	drm_framebuffer_unreference(fb);

I haven't had time to look through this in depth yet, but can't we wind
up unreferencing NULL here?

Also, the comment above this call should probably be updated/removed
since the locking works a bit differently now?


Matt

-- 
Matt Roper
Graphics Software Engineer
IoTG Platform Enabling & Development
Intel Corporation
(916) 356-2795

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/7] drm: add atomic fxns
  2014-07-23 19:38 ` [PATCH 1/7] drm: add atomic fxns Rob Clark
  2014-07-23 21:34   ` Daniel Vetter
@ 2014-07-24 10:02   ` Thierry Reding
  2014-07-24 18:31     ` "Stéphane Viau"
  1 sibling, 1 reply; 20+ messages in thread
From: Thierry Reding @ 2014-07-24 10:02 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 10296 bytes --]

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@gmail.com>
> ---
>  drivers/gpu/drm/Makefile                    |   2 +-
>  drivers/gpu/drm/armada/armada_crtc.c        |   3 +-
>  drivers/gpu/drm/armada/armada_output.c      |   3 +-
>  drivers/gpu/drm/armada/armada_overlay.c     |   3 +-
>  drivers/gpu/drm/ast/ast_drv.c               |   6 +
>  drivers/gpu/drm/ast/ast_drv.h               |   1 +
>  drivers/gpu/drm/cirrus/cirrus_drv.c         |   6 +
>  drivers/gpu/drm/cirrus/cirrus_drv.h         |   1 +
>  drivers/gpu/drm/drm_atomic.c                | 274 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_crtc.c                  | 149 +++++++++------
>  drivers/gpu/drm/drm_modeset_lock.c          |  28 +++
>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>  drivers/gpu/drm/exynos/exynos_drm_drv.c     |   7 +
>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |   4 +-
>  drivers/gpu/drm/gma500/cdv_intel_crt.c      |   4 +-
>  drivers/gpu/drm/gma500/cdv_intel_dp.c       |   4 +-
>  drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |   4 +-
>  drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   4 +-
>  drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   4 +-
>  drivers/gpu/drm/gma500/psb_drv.c            |   7 +
>  drivers/gpu/drm/gma500/psb_drv.h            |   1 +
>  drivers/gpu/drm/gma500/psb_intel_drv.h      |   4 +-
>  drivers/gpu/drm/gma500/psb_intel_lvds.c     |   4 +-
>  drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   4 +-
>  drivers/gpu/drm/i915/i915_drv.c             |   8 +
>  drivers/gpu/drm/i915/intel_crt.c            |   4 +-
>  drivers/gpu/drm/i915/intel_dp.c             |   4 +-
>  drivers/gpu/drm/i915/intel_drv.h            |   1 +
>  drivers/gpu/drm/i915/intel_hdmi.c           |   4 +-
>  drivers/gpu/drm/i915/intel_lvds.c           |   4 +-
>  drivers/gpu/drm/i915/intel_sdvo.c           |   4 +-
>  drivers/gpu/drm/i915/intel_tv.c             |   6 +-
>  drivers/gpu/drm/mgag200/mgag200_drv.c       |   7 +
>  drivers/gpu/drm/mgag200/mgag200_drv.h       |   1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   3 +-
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   3 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   3 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   3 +-
>  drivers/gpu/drm/msm/msm_drv.c               |   6 +
>  drivers/gpu/drm/msm/msm_drv.h               |   1 +
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   5 +-
>  drivers/gpu/drm/nouveau/nouveau_connector.c |   3 +-
>  drivers/gpu/drm/nouveau/nouveau_drm.c       |   7 +
>  drivers/gpu/drm/nouveau/nouveau_drm.h       |   1 +
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   6 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   6 +
>  drivers/gpu/drm/omapdrm/omap_drv.h          |   4 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |   3 +-
>  drivers/gpu/drm/qxl/qxl_display.c           |   4 +-
>  drivers/gpu/drm/qxl/qxl_drv.c               |   9 +
>  drivers/gpu/drm/radeon/radeon_connectors.c  |   9 +-
>  drivers/gpu/drm/radeon/radeon_drv.c         |   9 +
>  drivers/gpu/drm/rcar-du/rcar_du_drv.c       |   7 +
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   4 +-
>  drivers/gpu/drm/shmobile/shmob_drm_drv.c    |   7 +
>  drivers/gpu/drm/tilcdc/tilcdc_drv.c         |   6 +
>  drivers/gpu/drm/tilcdc/tilcdc_drv.h         |   1 +
>  drivers/gpu/drm/tilcdc/tilcdc_slave.c       |   3 +-
>  drivers/gpu/drm/udl/udl_connector.c         |   6 +-
>  drivers/gpu/drm/udl/udl_drv.c               |   8 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_drv.c         |   7 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_drv.h         |   1 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c         |   4 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.h         |   4 +-
>  include/drm/drmP.h                          |  80 ++++++++
>  include/drm/drm_atomic.h                    | 114 ++++++++++++
>  include/drm/drm_crtc.h                      |  15 +-
>  include/drm/drm_modeset_lock.h              |  47 +++++
>  include/uapi/drm/drm_mode.h                 |   3 +
>  69 files changed, 873 insertions(+), 103 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_atomic.c
>  create mode 100644 include/drm/drm_atomic.h

The above list seems to include only drivers that actually implement
properties. But that also means that none of the .atomic_* functions
will get set for drivers that lack properties. I'm wondering if that
isn't going to cause problems later on. What's the effect of not
providing .atomic_*() implementations?

Also for the drivers that are modified in this patch the only change
seems to be the addition of a new parameter to .set_property(), but
that alone surely isn't enough to implement atomic modesetting, right?

So I guess my question is: wouldn't it be more useful to consistently
set .atomic_* for all drivers? Even if they don't provide any custom
properties they'll get some of the standard ones.

> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
[...]
> +/**
> + * drm_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)
> + */

This section should be "Return: a driver state object ..." according to
Documentation/kernel-doc-nano-HOWTO.txt.

> +struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
> +		uint32_t flags)

Nit: I think we usually try to align arguments on subsequent lines with
the first argument on the first line.

> +{
> +	struct drm_atomic_state *state;
> +	uint32_t acquire_flags = 0;
> +	int sz;
> +	void *ptr;
> +
> +	sz = sizeof(*state);
> +
> +	ptr = kzalloc(sz, GFP_KERNEL);

Shouldn't this error out on ptr == NULL?

> +
> +	state = ptr;
> +	ptr = &state[1];

I'm not exactly sure what this is doing. ptr doesn't seem to be used
anymore, so the last assignment becomes unnecessary. It also seems wrong
since ptr now points to unallocated memory.

And if that assignment is removed, then state = ptr isn't necessary
either anymore, since you could simply do:

	state = kzalloc(sz, GFP_KERNEL);

But maybe I'm missing something?

> +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;
> +
> +

There's a gratuituous newline here.

> +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;
> +}

These look somewhat unnecessary, but I suspect that subsequent patches
will flesh them out.

> +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);
> +
> +

There's another gratuituous blank line above.

> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
[...]
> -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);
> +	}

Why the extra braces here? There's still only one statement in the
block.

>  /**
> - * 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

This isn't really introduced by this patch, but isn't this kerneldoc
comment wrong? drm_mode_obj_get_properties_ioctl() seems to return the
values of all properties of an object rather than just one.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/7] prepare for atomic.. the great propertyification
  2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
                   ` (6 preceding siblings ...)
  2014-07-23 19:38 ` [PATCH 7/7] drm: Fix up the atomic legacy paths so they work Rob Clark
@ 2014-07-24 12:25 ` Daniel Vetter
  2014-07-24 13:36   ` Rob Clark
  7 siblings, 1 reply; 20+ messages in thread
From: Daniel Vetter @ 2014-07-24 12:25 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, Jul 23, 2014 at 03:38:13PM -0400, Rob Clark wrote:
> This is mostly just a rebase+resend.  Was going to send it a bit earlier
> but had a few things to fix up as a result of the rebase.
> 
> At this point, I think next steps are roughly:
> 1) introduce plane->mutex
> 2) decide what we want to do about events
> 3) add actual ioctl
> 
> I think we could shoot for merging this series next, and then adding
> plane->mutex in 3.18?
> 
> Before we add the ioctl, I think we want to sort out events for updates
> to non-primary layers, and what the interface to drivers should look like.
> Ie. just add event to ->update_plane() or should we completely ditch
> ->page_flip() and ->update_plane()?
> 
> Technically, I think we could get away without a new API and just let
> drivers grab all the events in their ->atomic_commit(), but I suspect
> core could provide something more useful to drivers.  I guess it would
> be useful to have a few more drivers converted over to see what makes
> sense.

Ok so we've had a lot of discussions about this on irc and overall I'm not
terribly happy with what this looks like. I have a few concerns:

- The entire atomic conversion for drivers is monolithic, at least at the
  interface level. So if you want to do this step-by-step you're forced to
  add hacks and e.g. bypass pageflips until that's implemented. E.g. on
  i915 I see no chance that we'll get atomic ready for all cases (so
  nonblocking flips and multi-crtc and everything on all platforms) in one
  go.

- Related to that is that we force legacy ioctls through atomic. E.g. on
  older i915 platforms I very much expect that we won't ever convert the
  pageflip code to atomic for them and just not support atomic flips of
  cursor + primary plane. At least not at the beginning. I know that's
  different from my stance a year ago, but I've become a bit more
  realistic.

- The entire conversion is monolithic and we can't do it on a
  driver-by-driver basis. Everyone has to go through the new atomic
  interfaces on a flag day. drm is much bigger now and forcing everyone to
  convert at the same time is really risky. Again I know I've changed my
  mind again, but drm is a lot bigger and there's a lot more drivers that
  implement pageflip and cursors, i.e. stuff that's special.

- The separation between interface marshalling code in the drm core and
  helper functions for drivers to implement stuff is fuzzy at best. Thus
  far we've had an excellent seperation in this are for kms, I don't think
  it's vise to throw that out.

So here's my proposal for how to get this in without breaking the world

1. We add a complete new set of ->atomic_foo driver entry points to all
relevant objects. So instead of changing all the set_prop functions in a
flag-day like operation we just add a new interface. I haven't double
checked, but I guess that means per-obj ->atomic_set_prop functions plus a
global ->atomic_check and ->atomic_commit.

2. We add a new drm_atomic_helper.c library which provides functions to
implement all the legacy interfaces (page_flip, update_plane, set_prop)
using atomic interfaces. We should be able to 1:1 reuse Rob's code for
this, but instead of changing the actual current interface code we put
that into helpers.

We don't need anything for cursor ioctls since cursor plane helpers
already map the legacy cursor ioctls.

3. Rob's current code reuses the existing ->update_plane, ->pageflip and
other entry points to implement the atomic commit helper functions. Imo
that's a bad layering violation, and if we plug helpers in there we can't
use them.

Instead I think we should add a new per-plane ->atomic_commit functions
clearly marked as optional. Maybe even as part of an opaque
plane_helper_funcs pointer like we have with crtc/encoder/connector and
crtc helpers. msm would then implement it's atomic commit function in
there (since it's the only driver currently supporting atomic for real).

3b. We could adjust crtc helpers to use the plane commit hook instead of
the set_base function when avialable.

4. We do a pimped atomic helper based upon crtc helpers and the above
plane commit function added in 3. It would essentially implement what
Rob's current helper does, i.e.

Step 1: Shut down all crtcs which change using the crtc helpers. This step
is obviously optional and ommitted when there's nothing to do.

Step 2: Loop over all planes and call ->plane_commit or whatever we will
call the interface added in 3. above.

Step 3: Enable all crtcs that need enabling.

5. We start converting drivers. Every driver can convert at it's own pace,
opting in for atomic behaviour step-by-step.

6. We optionally use the atomic interface in the fb helper. It's imo
important to keep the code here parallel so that drivers can convert at
their own leisure.

7. We add the atomic ioctl.

8. Various cleanups once 5. is completed for all drivers - which will
likely take at least a year:
- Remove ->set_base from crtc helpers.
- Remove legacy functions from fbdev helpers.
- ...

I know this is a stack of work, but most of it just shuffles code around
that already exists 1:1 in Rob's series. And I really think that this is
where we should ultimately end up at, and going indirectly to that point
will be much more painful.

Also note that nothing in here really touches shared code, so we can
squeeze this into 3.17 even really late without risk to existing drivers.
Imo it's too late already in the 3.17 cycle for the current series, which
touches everything. If we get everything up to step 4. in now we can
convert 1-2 drivers in 3.18 and enable the atomic ioctl after that.

And I realize that everyon will totally hate this because apparently
google is making a hard move with adf and plans to enforce usage of that,
so this entire thing is a bit ugly.

Cheers, Daniel

> 
> Rob Clark (5):
>   drm: add atomic fxns
>   drm: split propvals out and blob property support
>   drm: convert plane to properties/state
>   drm: convert crtc to properties/state
>   drm/msm: add atomic support
> 
> Sean Paul (1):
>   drm: Fix up the atomic legacy paths so they work
> 
> Ville Syrjälä (1):
>   drm: Refactor object property check code
> 
>  drivers/gpu/drm/Makefile                    |    2 +-
>  drivers/gpu/drm/armada/armada_crtc.c        |   14 +-
>  drivers/gpu/drm/armada/armada_output.c      |    3 +-
>  drivers/gpu/drm/armada/armada_overlay.c     |   14 +-
>  drivers/gpu/drm/ast/ast_drv.c               |    6 +
>  drivers/gpu/drm/ast/ast_drv.h               |    1 +
>  drivers/gpu/drm/ast/ast_mode.c              |    1 +
>  drivers/gpu/drm/cirrus/cirrus_drv.c         |    6 +
>  drivers/gpu/drm/cirrus/cirrus_drv.h         |    1 +
>  drivers/gpu/drm/cirrus/cirrus_mode.c        |    1 +
>  drivers/gpu/drm/drm_atomic.c                |  733 +++++++++++++++
>  drivers/gpu/drm/drm_crtc.c                  | 1351 ++++++++++++++++++---------
>  drivers/gpu/drm/drm_fb_helper.c             |   55 +-
>  drivers/gpu/drm/drm_irq.c                   |    8 +-
>  drivers/gpu/drm/drm_modeset_lock.c          |   28 +
>  drivers/gpu/drm/drm_plane_helper.c          |    2 +
>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   11 +-
>  drivers/gpu/drm/exynos/exynos_drm_drv.c     |    7 +
>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |   11 +-
>  drivers/gpu/drm/gma500/cdv_intel_crt.c      |    4 +-
>  drivers/gpu/drm/gma500/cdv_intel_display.c  |    1 +
>  drivers/gpu/drm/gma500/cdv_intel_dp.c       |    7 +-
>  drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |    7 +-
>  drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   10 +-
>  drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   12 +-
>  drivers/gpu/drm/gma500/psb_drv.c            |    7 +
>  drivers/gpu/drm/gma500/psb_drv.h            |    1 +
>  drivers/gpu/drm/gma500/psb_intel_display.c  |    1 +
>  drivers/gpu/drm/gma500/psb_intel_drv.h      |    4 +-
>  drivers/gpu/drm/gma500/psb_intel_lvds.c     |   10 +-
>  drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   23 +-
>  drivers/gpu/drm/i2c/ch7006_drv.c            |    4 +-
>  drivers/gpu/drm/i915/i915_drv.c             |    8 +
>  drivers/gpu/drm/i915/intel_crt.c            |    4 +-
>  drivers/gpu/drm/i915/intel_display.c        |    6 +-
>  drivers/gpu/drm/i915/intel_dp.c             |    7 +-
>  drivers/gpu/drm/i915/intel_drv.h            |    1 +
>  drivers/gpu/drm/i915/intel_hdmi.c           |    7 +-
>  drivers/gpu/drm/i915/intel_lvds.c           |    4 +-
>  drivers/gpu/drm/i915/intel_sdvo.c           |   23 +-
>  drivers/gpu/drm/i915/intel_sprite.c         |    1 +
>  drivers/gpu/drm/i915/intel_tv.c             |   12 +-
>  drivers/gpu/drm/mgag200/mgag200_drv.c       |    7 +
>  drivers/gpu/drm/mgag200/mgag200_drv.h       |    1 +
>  drivers/gpu/drm/mgag200/mgag200_mode.c      |    1 +
>  drivers/gpu/drm/msm/Makefile                |    1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   66 +-
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c     |    6 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h     |    1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   14 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   65 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c     |    6 +
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h     |    2 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   14 +-
>  drivers/gpu/drm/msm/msm_atomic.c            |  141 +++
>  drivers/gpu/drm/msm/msm_drv.c               |   26 +
>  drivers/gpu/drm/msm/msm_drv.h               |    8 +
>  drivers/gpu/drm/msm/msm_gem.c               |   24 +-
>  drivers/gpu/drm/msm/msm_gem.h               |   13 +
>  drivers/gpu/drm/msm/msm_kms.h               |    1 +
>  drivers/gpu/drm/nouveau/dispnv04/crtc.c     |    1 +
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   13 +-
>  drivers/gpu/drm/nouveau/dispnv04/tvnv17.c   |    3 +-
>  drivers/gpu/drm/nouveau/nouveau_connector.c |    7 +-
>  drivers/gpu/drm/nouveau/nouveau_drm.c       |    7 +
>  drivers/gpu/drm/nouveau/nouveau_drm.h       |    1 +
>  drivers/gpu/drm/nouveau/nv50_display.c      |    1 +
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   16 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   12 +-
>  drivers/gpu/drm/omapdrm/omap_drv.h          |    4 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |   10 +-
>  drivers/gpu/drm/qxl/qxl_display.c           |    6 +-
>  drivers/gpu/drm/qxl/qxl_drv.c               |    9 +
>  drivers/gpu/drm/radeon/radeon_connectors.c  |    9 +-
>  drivers/gpu/drm/radeon/radeon_display.c     |    2 +
>  drivers/gpu/drm/radeon/radeon_drv.c         |    9 +
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c      |    2 +
>  drivers/gpu/drm/rcar-du/rcar_du_drv.c       |    7 +
>  drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c   |    3 +-
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   12 +-
>  drivers/gpu/drm/rcar-du/rcar_du_vgacon.c    |    3 +-
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |    6 +-
>  drivers/gpu/drm/shmobile/shmob_drm_drv.c    |    7 +
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |    2 +
>  drivers/gpu/drm/tilcdc/tilcdc_crtc.c        |    1 +
>  drivers/gpu/drm/tilcdc/tilcdc_drv.c         |    6 +
>  drivers/gpu/drm/tilcdc/tilcdc_drv.h         |    1 +
>  drivers/gpu/drm/tilcdc/tilcdc_slave.c       |    3 +-
>  drivers/gpu/drm/udl/udl_connector.c         |    6 +-
>  drivers/gpu/drm/udl/udl_drv.c               |    8 +
>  drivers/gpu/drm/udl/udl_modeset.c           |    2 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_drv.c         |    7 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_drv.h         |    1 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c         |    4 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.h         |    4 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c         |    1 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c        |    1 +
>  include/drm/drmP.h                          |   80 ++
>  include/drm/drm_atomic.h                    |  170 ++++
>  include/drm/drm_crtc.h                      |  238 ++++-
>  include/drm/drm_modeset_lock.h              |   47 +
>  include/uapi/drm/drm_mode.h                 |    3 +
>  102 files changed, 2901 insertions(+), 650 deletions(-)
>  create mode 100644 drivers/gpu/drm/drm_atomic.c
>  create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
>  create mode 100644 include/drm/drm_atomic.h
> 
> -- 
> 1.9.3
> 

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/7] prepare for atomic.. the great propertyification
  2014-07-24 12:25 ` [PATCH 0/7] prepare for atomic.. the great propertyification Daniel Vetter
@ 2014-07-24 13:36   ` Rob Clark
  2014-07-24 14:00     ` Daniel Vetter
  0 siblings, 1 reply; 20+ messages in thread
From: Rob Clark @ 2014-07-24 13:36 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Thu, Jul 24, 2014 at 8:25 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Wed, Jul 23, 2014 at 03:38:13PM -0400, Rob Clark wrote:
>> This is mostly just a rebase+resend.  Was going to send it a bit earlier
>> but had a few things to fix up as a result of the rebase.
>>
>> At this point, I think next steps are roughly:
>> 1) introduce plane->mutex
>> 2) decide what we want to do about events
>> 3) add actual ioctl
>>
>> I think we could shoot for merging this series next, and then adding
>> plane->mutex in 3.18?
>>
>> Before we add the ioctl, I think we want to sort out events for updates
>> to non-primary layers, and what the interface to drivers should look like.
>> Ie. just add event to ->update_plane() or should we completely ditch
>> ->page_flip() and ->update_plane()?
>>
>> Technically, I think we could get away without a new API and just let
>> drivers grab all the events in their ->atomic_commit(), but I suspect
>> core could provide something more useful to drivers.  I guess it would
>> be useful to have a few more drivers converted over to see what makes
>> sense.
>
> Ok so we've had a lot of discussions about this on irc and overall I'm not
> terribly happy with what this looks like. I have a few concerns:
>
> - The entire atomic conversion for drivers is monolithic, at least at the
>   interface level. So if you want to do this step-by-step you're forced to
>   add hacks and e.g. bypass pageflips until that's implemented. E.g. on
>   i915 I see no chance that we'll get atomic ready for all cases (so
>   nonblocking flips and multi-crtc and everything on all platforms) in one
>   go.

So, there interface is *intended* to be monolithic, in a way..  many
years ago, I started by adding side by side atomic vs legacy paths,
and it was pretty ugly in core.  I'm much happier with the current
iteration.

A slightly later iteration preserved atomic helpers (so drivers could
do completely their own thing).  I ended up dropping that because most
of what atomic does is manage the interface to whatever is doing
modeset/pageflip/whatever.  I completely expect some changes around
the commit stuff..  that part is intended to either be
wrapped/extended by the driver or replaced.  Possibly it could be made
more clear what drivers are expected to use vs extend or replace.  I
expect this to evolve a bit as more drivers are converted.

Note that the current atomic "layer" results in 1:1 conversion from
old userspace API to legacy vfuncs.  This way what is on top of atomic
API and what is beneath (ie. the driver) has same code paths either
way (old or new userspace, converted or not driver).  The edge cases
don't come in until atomic ioctl is exposed, and for that I expect a
driver flag to enable the new ioctl.  You could even set the flag
based on hw generation if you want.

> - Related to that is that we force legacy ioctls through atomic. E.g. on
>   older i915 platforms I very much expect that we won't ever convert the
>   pageflip code to atomic for them and just not support atomic flips of
>   cursor + primary plane. At least not at the beginning. I know that's
>   different from my stance a year ago, but I've become a bit more
>   realistic.
>
> - The entire conversion is monolithic and we can't do it on a
>   driver-by-driver basis. Everyone has to go through the new atomic
>   interfaces on a flag day. drm is much bigger now and forcing everyone to
>   convert at the same time is really risky. Again I know I've changed my
>   mind again, but drm is a lot bigger and there's a lot more drivers that
>   implement pageflip and cursors, i.e. stuff that's special.

the only flag day part is plugging in atomic functions and couple
vfunc API changes around set_prop().. the current design allows for
conversion on driver by driver basis.

> - The separation between interface marshalling code in the drm core and
>   helper functions for drivers to implement stuff is fuzzy at best. Thus
>   far we've had an excellent seperation in this are for kms, I don't think
>   it's vise to throw that out.

Interface marshalling should not be helper.  Everyone needs the same
thing, since what is on top is the same.

> So here's my proposal for how to get this in without breaking the world
>
> 1. We add a complete new set of ->atomic_foo driver entry points to all
> relevant objects. So instead of changing all the set_prop functions in a
> flag-day like operation we just add a new interface. I haven't double
> checked, but I guess that means per-obj ->atomic_set_prop functions plus a
> global ->atomic_check and ->atomic_commit.

that sounds much worse

> 2. We add a new drm_atomic_helper.c library which provides functions to
> implement all the legacy interfaces (page_flip, update_plane, set_prop)
> using atomic interfaces. We should be able to 1:1 reuse Rob's code for
> this, but instead of changing the actual current interface code we put
> that into helpers.

So, I've started ripping out page_flip.. now w/ primary plane helpers
we can do everything in terms of update_plane().  I have an idea to
handle events.  It isn't so bad, but forces me to do some re-arranging
in drm/msm that I was planning to postpone otherwise.

(And I just got a fedex package w/ new toys, er, hardware.. but I'll
try to finish this today)

> We don't need anything for cursor ioctls since cursor plane helpers
> already map the legacy cursor ioctls.
>
> 3. Rob's current code reuses the existing ->update_plane, ->pageflip and
> other entry points to implement the atomic commit helper functions. Imo
> that's a bad layering violation, and if we plug helpers in there we can't
> use them.

Well, it is only a layering violation because you have a slightly
different idea of where the layers should be.  ;-)

Atomic (or really the state stuff.. maybe I should have called it
"transaction" or something like that to avoid the atomic
connotation..) should be on top of, not below, helpers.

Probably the commit part should be more clearly separated out and
marked as "helper", because that is the part that is about taking the
state that userspace (or fbdev) has asked for and applying it to the
hw.  This is really the part where drivers for some hw might want to
do something different.  Although I admit lumping it in w/
drm_atomic.c makes this not very clear.

And then beneath atomic we introduce new interfaces (if needed..
although update_plane isn't so bad once we toss out page_flip).  We
could introduce new ->commit_state() vfuncs, and alternate set of
commit helper which uses those.. at least for planes I suspect
->commit_state() ends up looking a lot like ->update_plane.

> Instead I think we should add a new per-plane ->atomic_commit functions
> clearly marked as optional. Maybe even as part of an opaque
> plane_helper_funcs pointer like we have with crtc/encoder/connector and
> crtc helpers. msm would then implement it's atomic commit function in
> there (since it's the only driver currently supporting atomic for real).
>
> 3b. We could adjust crtc helpers to use the plane commit hook instead of
> the set_base function when avialable.

would be a nice cleanup either way.. but I think it is independent..

> 4. We do a pimped atomic helper based upon crtc helpers and the above
> plane commit function added in 3. It would essentially implement what
> Rob's current helper does, i.e.
>
> Step 1: Shut down all crtcs which change using the crtc helpers. This step
> is obviously optional and ommitted when there's nothing to do.
>
> Step 2: Loop over all planes and call ->plane_commit or whatever we will
> call the interface added in 3. above.
>
> Step 3: Enable all crtcs that need enabling.
>
> 5. We start converting drivers. Every driver can convert at it's own pace,
> opting in for atomic behaviour step-by-step.
>
> 6. We optionally use the atomic interface in the fb helper. It's imo
> important to keep the code here parallel so that drivers can convert at
> their own leisure.

this is one thing I really wanted to avoid.  It was already getting
ugly the first time I tried it, and I hadn't even converted
everything.

BR,
-R

> 7. We add the atomic ioctl.
>
> 8. Various cleanups once 5. is completed for all drivers - which will
> likely take at least a year:
> - Remove ->set_base from crtc helpers.
> - Remove legacy functions from fbdev helpers.
> - ...
>
> I know this is a stack of work, but most of it just shuffles code around
> that already exists 1:1 in Rob's series. And I really think that this is
> where we should ultimately end up at, and going indirectly to that point
> will be much more painful.
>
> Also note that nothing in here really touches shared code, so we can
> squeeze this into 3.17 even really late without risk to existing drivers.
> Imo it's too late already in the 3.17 cycle for the current series, which
> touches everything. If we get everything up to step 4. in now we can
> convert 1-2 drivers in 3.18 and enable the atomic ioctl after that.
>
> And I realize that everyon will totally hate this because apparently
> google is making a hard move with adf and plans to enforce usage of that,
> so this entire thing is a bit ugly.
>
> Cheers, Daniel
>
>>
>> Rob Clark (5):
>>   drm: add atomic fxns
>>   drm: split propvals out and blob property support
>>   drm: convert plane to properties/state
>>   drm: convert crtc to properties/state
>>   drm/msm: add atomic support
>>
>> Sean Paul (1):
>>   drm: Fix up the atomic legacy paths so they work
>>
>> Ville Syrjälä (1):
>>   drm: Refactor object property check code
>>
>>  drivers/gpu/drm/Makefile                    |    2 +-
>>  drivers/gpu/drm/armada/armada_crtc.c        |   14 +-
>>  drivers/gpu/drm/armada/armada_output.c      |    3 +-
>>  drivers/gpu/drm/armada/armada_overlay.c     |   14 +-
>>  drivers/gpu/drm/ast/ast_drv.c               |    6 +
>>  drivers/gpu/drm/ast/ast_drv.h               |    1 +
>>  drivers/gpu/drm/ast/ast_mode.c              |    1 +
>>  drivers/gpu/drm/cirrus/cirrus_drv.c         |    6 +
>>  drivers/gpu/drm/cirrus/cirrus_drv.h         |    1 +
>>  drivers/gpu/drm/cirrus/cirrus_mode.c        |    1 +
>>  drivers/gpu/drm/drm_atomic.c                |  733 +++++++++++++++
>>  drivers/gpu/drm/drm_crtc.c                  | 1351 ++++++++++++++++++---------
>>  drivers/gpu/drm/drm_fb_helper.c             |   55 +-
>>  drivers/gpu/drm/drm_irq.c                   |    8 +-
>>  drivers/gpu/drm/drm_modeset_lock.c          |   28 +
>>  drivers/gpu/drm/drm_plane_helper.c          |    2 +
>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   11 +-
>>  drivers/gpu/drm/exynos/exynos_drm_drv.c     |    7 +
>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |   11 +-
>>  drivers/gpu/drm/gma500/cdv_intel_crt.c      |    4 +-
>>  drivers/gpu/drm/gma500/cdv_intel_display.c  |    1 +
>>  drivers/gpu/drm/gma500/cdv_intel_dp.c       |    7 +-
>>  drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |    7 +-
>>  drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   10 +-
>>  drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   12 +-
>>  drivers/gpu/drm/gma500/psb_drv.c            |    7 +
>>  drivers/gpu/drm/gma500/psb_drv.h            |    1 +
>>  drivers/gpu/drm/gma500/psb_intel_display.c  |    1 +
>>  drivers/gpu/drm/gma500/psb_intel_drv.h      |    4 +-
>>  drivers/gpu/drm/gma500/psb_intel_lvds.c     |   10 +-
>>  drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   23 +-
>>  drivers/gpu/drm/i2c/ch7006_drv.c            |    4 +-
>>  drivers/gpu/drm/i915/i915_drv.c             |    8 +
>>  drivers/gpu/drm/i915/intel_crt.c            |    4 +-
>>  drivers/gpu/drm/i915/intel_display.c        |    6 +-
>>  drivers/gpu/drm/i915/intel_dp.c             |    7 +-
>>  drivers/gpu/drm/i915/intel_drv.h            |    1 +
>>  drivers/gpu/drm/i915/intel_hdmi.c           |    7 +-
>>  drivers/gpu/drm/i915/intel_lvds.c           |    4 +-
>>  drivers/gpu/drm/i915/intel_sdvo.c           |   23 +-
>>  drivers/gpu/drm/i915/intel_sprite.c         |    1 +
>>  drivers/gpu/drm/i915/intel_tv.c             |   12 +-
>>  drivers/gpu/drm/mgag200/mgag200_drv.c       |    7 +
>>  drivers/gpu/drm/mgag200/mgag200_drv.h       |    1 +
>>  drivers/gpu/drm/mgag200/mgag200_mode.c      |    1 +
>>  drivers/gpu/drm/msm/Makefile                |    1 +
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   66 +-
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c     |    6 +
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h     |    1 +
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   14 +-
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   65 +-
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c     |    6 +
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h     |    2 +-
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   14 +-
>>  drivers/gpu/drm/msm/msm_atomic.c            |  141 +++
>>  drivers/gpu/drm/msm/msm_drv.c               |   26 +
>>  drivers/gpu/drm/msm/msm_drv.h               |    8 +
>>  drivers/gpu/drm/msm/msm_gem.c               |   24 +-
>>  drivers/gpu/drm/msm/msm_gem.h               |   13 +
>>  drivers/gpu/drm/msm/msm_kms.h               |    1 +
>>  drivers/gpu/drm/nouveau/dispnv04/crtc.c     |    1 +
>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   13 +-
>>  drivers/gpu/drm/nouveau/dispnv04/tvnv17.c   |    3 +-
>>  drivers/gpu/drm/nouveau/nouveau_connector.c |    7 +-
>>  drivers/gpu/drm/nouveau/nouveau_drm.c       |    7 +
>>  drivers/gpu/drm/nouveau/nouveau_drm.h       |    1 +
>>  drivers/gpu/drm/nouveau/nv50_display.c      |    1 +
>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   16 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   12 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.h          |    4 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c        |   10 +-
>>  drivers/gpu/drm/qxl/qxl_display.c           |    6 +-
>>  drivers/gpu/drm/qxl/qxl_drv.c               |    9 +
>>  drivers/gpu/drm/radeon/radeon_connectors.c  |    9 +-
>>  drivers/gpu/drm/radeon/radeon_display.c     |    2 +
>>  drivers/gpu/drm/radeon/radeon_drv.c         |    9 +
>>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c      |    2 +
>>  drivers/gpu/drm/rcar-du/rcar_du_drv.c       |    7 +
>>  drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c   |    3 +-
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   12 +-
>>  drivers/gpu/drm/rcar-du/rcar_du_vgacon.c    |    3 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |    6 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_drv.c    |    7 +
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |    2 +
>>  drivers/gpu/drm/tilcdc/tilcdc_crtc.c        |    1 +
>>  drivers/gpu/drm/tilcdc/tilcdc_drv.c         |    6 +
>>  drivers/gpu/drm/tilcdc/tilcdc_drv.h         |    1 +
>>  drivers/gpu/drm/tilcdc/tilcdc_slave.c       |    3 +-
>>  drivers/gpu/drm/udl/udl_connector.c         |    6 +-
>>  drivers/gpu/drm/udl/udl_drv.c               |    8 +
>>  drivers/gpu/drm/udl/udl_modeset.c           |    2 +
>>  drivers/gpu/drm/vmwgfx/vmwgfx_drv.c         |    7 +
>>  drivers/gpu/drm/vmwgfx/vmwgfx_drv.h         |    1 +
>>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c         |    4 +-
>>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.h         |    4 +-
>>  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c         |    1 +
>>  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c        |    1 +
>>  include/drm/drmP.h                          |   80 ++
>>  include/drm/drm_atomic.h                    |  170 ++++
>>  include/drm/drm_crtc.h                      |  238 ++++-
>>  include/drm/drm_modeset_lock.h              |   47 +
>>  include/uapi/drm/drm_mode.h                 |    3 +
>>  102 files changed, 2901 insertions(+), 650 deletions(-)
>>  create mode 100644 drivers/gpu/drm/drm_atomic.c
>>  create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
>>  create mode 100644 include/drm/drm_atomic.h
>>
>> --
>> 1.9.3
>>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/7] prepare for atomic.. the great propertyification
  2014-07-24 13:36   ` Rob Clark
@ 2014-07-24 14:00     ` Daniel Vetter
  2014-07-24 14:56       ` Rob Clark
  0 siblings, 1 reply; 20+ messages in thread
From: Daniel Vetter @ 2014-07-24 14:00 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Thu, Jul 24, 2014 at 09:36:20AM -0400, Rob Clark wrote:
> On Thu, Jul 24, 2014 at 8:25 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Wed, Jul 23, 2014 at 03:38:13PM -0400, Rob Clark wrote:
> >> This is mostly just a rebase+resend.  Was going to send it a bit earlier
> >> but had a few things to fix up as a result of the rebase.
> >>
> >> At this point, I think next steps are roughly:
> >> 1) introduce plane->mutex
> >> 2) decide what we want to do about events
> >> 3) add actual ioctl
> >>
> >> I think we could shoot for merging this series next, and then adding
> >> plane->mutex in 3.18?
> >>
> >> Before we add the ioctl, I think we want to sort out events for updates
> >> to non-primary layers, and what the interface to drivers should look like.
> >> Ie. just add event to ->update_plane() or should we completely ditch
> >> ->page_flip() and ->update_plane()?
> >>
> >> Technically, I think we could get away without a new API and just let
> >> drivers grab all the events in their ->atomic_commit(), but I suspect
> >> core could provide something more useful to drivers.  I guess it would
> >> be useful to have a few more drivers converted over to see what makes
> >> sense.
> >
> > Ok so we've had a lot of discussions about this on irc and overall I'm not
> > terribly happy with what this looks like. I have a few concerns:
> >
> > - The entire atomic conversion for drivers is monolithic, at least at the
> >   interface level. So if you want to do this step-by-step you're forced to
> >   add hacks and e.g. bypass pageflips until that's implemented. E.g. on
> >   i915 I see no chance that we'll get atomic ready for all cases (so
> >   nonblocking flips and multi-crtc and everything on all platforms) in one
> >   go.
> 
> So, there interface is *intended* to be monolithic, in a way..  many
> years ago, I started by adding side by side atomic vs legacy paths,
> and it was pretty ugly in core.  I'm much happier with the current
> iteration.
> 
> A slightly later iteration preserved atomic helpers (so drivers could
> do completely their own thing).  I ended up dropping that because most
> of what atomic does is manage the interface to whatever is doing
> modeset/pageflip/whatever.  I completely expect some changes around
> the commit stuff..  that part is intended to either be
> wrapped/extended by the driver or replaced.  Possibly it could be made
> more clear what drivers are expected to use vs extend or replace.  I
> expect this to evolve a bit as more drivers are converted.
> 
> Note that the current atomic "layer" results in 1:1 conversion from
> old userspace API to legacy vfuncs.  This way what is on top of atomic
> API and what is beneath (ie. the driver) has same code paths either
> way (old or new userspace, converted or not driver).  The edge cases
> don't come in until atomic ioctl is exposed, and for that I expect a
> driver flag to enable the new ioctl.  You could even set the flag
> based on hw generation if you want.
> 
> > - Related to that is that we force legacy ioctls through atomic. E.g. on
> >   older i915 platforms I very much expect that we won't ever convert the
> >   pageflip code to atomic for them and just not support atomic flips of
> >   cursor + primary plane. At least not at the beginning. I know that's
> >   different from my stance a year ago, but I've become a bit more
> >   realistic.
> >
> > - The entire conversion is monolithic and we can't do it on a
> >   driver-by-driver basis. Everyone has to go through the new atomic
> >   interfaces on a flag day. drm is much bigger now and forcing everyone to
> >   convert at the same time is really risky. Again I know I've changed my
> >   mind again, but drm is a lot bigger and there's a lot more drivers that
> >   implement pageflip and cursors, i.e. stuff that's special.
> 
> the only flag day part is plugging in atomic functions and couple
> vfunc API changes around set_prop().. the current design allows for
> conversion on driver by driver basis.
> 
> > - The separation between interface marshalling code in the drm core and
> >   helper functions for drivers to implement stuff is fuzzy at best. Thus
> >   far we've had an excellent seperation in this are for kms, I don't think
> >   it's vise to throw that out.
> 
> Interface marshalling should not be helper.  Everyone needs the same
> thing, since what is on top is the same.

Erhm I've certainly not meant the interface marshalling to be a helper.
This started with "The separation ..." after all, so I want inteface
marshilling as fixed code in the drm core, and everything else outside in
a separate drm_atomic_helper.c module.

> > So here's my proposal for how to get this in without breaking the world
> >
> > 1. We add a complete new set of ->atomic_foo driver entry points to all
> > relevant objects. So instead of changing all the set_prop functions in a
> > flag-day like operation we just add a new interface. I haven't double
> > checked, but I guess that means per-obj ->atomic_set_prop functions plus a
> > global ->atomic_check and ->atomic_commit.
> 
> that sounds much worse

Why that? Afaics your patch only changes the interfaces but leaves the
semantics the same (i915 does set_config_internal all over the place in
set_prop). So essentially you have both interface, but merged into one.
And especially for the set_prop the semantics our different.

> > 2. We add a new drm_atomic_helper.c library which provides functions to
> > implement all the legacy interfaces (page_flip, update_plane, set_prop)
> > using atomic interfaces. We should be able to 1:1 reuse Rob's code for
> > this, but instead of changing the actual current interface code we put
> > that into helpers.
> 
> So, I've started ripping out page_flip.. now w/ primary plane helpers
> we can do everything in terms of update_plane().  I have an idea to
> handle events.  It isn't so bad, but forces me to do some re-arranging
> in drm/msm that I was planning to postpone otherwise.
> 
> (And I just got a fedex package w/ new toys, er, hardware.. but I'll
> try to finish this today)

Well in that case I think we should demote ->update_plane and friends to
helper status, since for a proper atomic implemenation for i915 we can't
use them.

> > We don't need anything for cursor ioctls since cursor plane helpers
> > already map the legacy cursor ioctls.
> >
> > 3. Rob's current code reuses the existing ->update_plane, ->pageflip and
> > other entry points to implement the atomic commit helper functions. Imo
> > that's a bad layering violation, and if we plug helpers in there we can't
> > use them.
> 
> Well, it is only a layering violation because you have a slightly
> different idea of where the layers should be.  ;-)
> 
> Atomic (or really the state stuff.. maybe I should have called it
> "transaction" or something like that to avoid the atomic
> connotation..) should be on top of, not below, helpers.

I'm talking only about the legacy interfaces here, and my thinking is that
we should not add a midlayer here (the atomic helper stuff) but directly
pass it to drivers. They can the choose how to implement this.

With the current scheme I essentially have to add a bunch of hacks for
i915 to fish out the old pageflip semantics to keep gen2 going. That's
fairly ugly, and the only reason is that you force a midlay between the
existing legacy ioctls and i915.

Of course for the actual atomic ioctl I don't want this inversion. But
that's not what I'm talking about here.

> Probably the commit part should be more clearly separated out and
> marked as "helper", because that is the part that is about taking the
> state that userspace (or fbdev) has asked for and applying it to the
> hw.  This is really the part where drivers for some hw might want to
> do something different.  Although I admit lumping it in w/
> drm_atomic.c makes this not very clear.
> 
> And then beneath atomic we introduce new interfaces (if needed..
> although update_plane isn't so bad once we toss out page_flip).  We
> could introduce new ->commit_state() vfuncs, and alternate set of
> commit helper which uses those.. at least for planes I suspect
> ->commit_state() ends up looking a lot like ->update_plane.

All ok with me, but really not my concern. This is all for drivers which
support atomic, I'll have a driver which will (at least partially and
likely forever) not support atomic everywhere.

> > Instead I think we should add a new per-plane ->atomic_commit functions
> > clearly marked as optional. Maybe even as part of an opaque
> > plane_helper_funcs pointer like we have with crtc/encoder/connector and
> > crtc helpers. msm would then implement it's atomic commit function in
> > there (since it's the only driver currently supporting atomic for real).
> >
> > 3b. We could adjust crtc helpers to use the plane commit hook instead of
> > the set_base function when avialable.
> 
> would be a nice cleanup either way.. but I think it is independent..

I think I've raised this a few times in the past, but imo having somewhat
clear helper semantics would help. Atm they try to do a bit too much
(legacy support, generic helpers, interface with crtc helpers without
changing too much) and don't look good in any of them.

> > 4. We do a pimped atomic helper based upon crtc helpers and the above
> > plane commit function added in 3. It would essentially implement what
> > Rob's current helper does, i.e.
> >
> > Step 1: Shut down all crtcs which change using the crtc helpers. This step
> > is obviously optional and ommitted when there's nothing to do.
> >
> > Step 2: Loop over all planes and call ->plane_commit or whatever we will
> > call the interface added in 3. above.
> >
> > Step 3: Enable all crtcs that need enabling.
> >
> > 5. We start converting drivers. Every driver can convert at it's own pace,
> > opting in for atomic behaviour step-by-step.
> >
> > 6. We optionally use the atomic interface in the fb helper. It's imo
> > important to keep the code here parallel so that drivers can convert at
> > their own leisure.
> 
> this is one thing I really wanted to avoid.  It was already getting
> ugly the first time I tried it, and I hadn't even converted
> everything.

Well I think we can't avoid ugliness in that area. There simply will be
parallel support for atomic in different drivers.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/7] prepare for atomic.. the great propertyification
  2014-07-24 14:00     ` Daniel Vetter
@ 2014-07-24 14:56       ` Rob Clark
  2014-07-25  8:15         ` Daniel Vetter
  0 siblings, 1 reply; 20+ messages in thread
From: Rob Clark @ 2014-07-24 14:56 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Thu, Jul 24, 2014 at 10:00 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Thu, Jul 24, 2014 at 09:36:20AM -0400, Rob Clark wrote:
>> On Thu, Jul 24, 2014 at 8:25 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Wed, Jul 23, 2014 at 03:38:13PM -0400, Rob Clark wrote:
>> >> This is mostly just a rebase+resend.  Was going to send it a bit earlier
>> >> but had a few things to fix up as a result of the rebase.
>> >>
>> >> At this point, I think next steps are roughly:
>> >> 1) introduce plane->mutex
>> >> 2) decide what we want to do about events
>> >> 3) add actual ioctl
>> >>
>> >> I think we could shoot for merging this series next, and then adding
>> >> plane->mutex in 3.18?
>> >>
>> >> Before we add the ioctl, I think we want to sort out events for updates
>> >> to non-primary layers, and what the interface to drivers should look like.
>> >> Ie. just add event to ->update_plane() or should we completely ditch
>> >> ->page_flip() and ->update_plane()?
>> >>
>> >> Technically, I think we could get away without a new API and just let
>> >> drivers grab all the events in their ->atomic_commit(), but I suspect
>> >> core could provide something more useful to drivers.  I guess it would
>> >> be useful to have a few more drivers converted over to see what makes
>> >> sense.
>> >
>> > Ok so we've had a lot of discussions about this on irc and overall I'm not
>> > terribly happy with what this looks like. I have a few concerns:
>> >
>> > - The entire atomic conversion for drivers is monolithic, at least at the
>> >   interface level. So if you want to do this step-by-step you're forced to
>> >   add hacks and e.g. bypass pageflips until that's implemented. E.g. on
>> >   i915 I see no chance that we'll get atomic ready for all cases (so
>> >   nonblocking flips and multi-crtc and everything on all platforms) in one
>> >   go.
>>
>> So, there interface is *intended* to be monolithic, in a way..  many
>> years ago, I started by adding side by side atomic vs legacy paths,
>> and it was pretty ugly in core.  I'm much happier with the current
>> iteration.
>>
>> A slightly later iteration preserved atomic helpers (so drivers could
>> do completely their own thing).  I ended up dropping that because most
>> of what atomic does is manage the interface to whatever is doing
>> modeset/pageflip/whatever.  I completely expect some changes around
>> the commit stuff..  that part is intended to either be
>> wrapped/extended by the driver or replaced.  Possibly it could be made
>> more clear what drivers are expected to use vs extend or replace.  I
>> expect this to evolve a bit as more drivers are converted.
>>
>> Note that the current atomic "layer" results in 1:1 conversion from
>> old userspace API to legacy vfuncs.  This way what is on top of atomic
>> API and what is beneath (ie. the driver) has same code paths either
>> way (old or new userspace, converted or not driver).  The edge cases
>> don't come in until atomic ioctl is exposed, and for that I expect a
>> driver flag to enable the new ioctl.  You could even set the flag
>> based on hw generation if you want.
>>
>> > - Related to that is that we force legacy ioctls through atomic. E.g. on
>> >   older i915 platforms I very much expect that we won't ever convert the
>> >   pageflip code to atomic for them and just not support atomic flips of
>> >   cursor + primary plane. At least not at the beginning. I know that's
>> >   different from my stance a year ago, but I've become a bit more
>> >   realistic.
>> >
>> > - The entire conversion is monolithic and we can't do it on a
>> >   driver-by-driver basis. Everyone has to go through the new atomic
>> >   interfaces on a flag day. drm is much bigger now and forcing everyone to
>> >   convert at the same time is really risky. Again I know I've changed my
>> >   mind again, but drm is a lot bigger and there's a lot more drivers that
>> >   implement pageflip and cursors, i.e. stuff that's special.
>>
>> the only flag day part is plugging in atomic functions and couple
>> vfunc API changes around set_prop().. the current design allows for
>> conversion on driver by driver basis.
>>
>> > - The separation between interface marshalling code in the drm core and
>> >   helper functions for drivers to implement stuff is fuzzy at best. Thus
>> >   far we've had an excellent seperation in this are for kms, I don't think
>> >   it's vise to throw that out.
>>
>> Interface marshalling should not be helper.  Everyone needs the same
>> thing, since what is on top is the same.
>
> Erhm I've certainly not meant the interface marshalling to be a helper.
> This started with "The separation ..." after all, so I want inteface
> marshilling as fixed code in the drm core, and everything else outside in
> a separate drm_atomic_helper.c module.

ok, it's entirely possibly that I'm missunderstanding a bit your proposal..

>> > So here's my proposal for how to get this in without breaking the world
>> >
>> > 1. We add a complete new set of ->atomic_foo driver entry points to all
>> > relevant objects. So instead of changing all the set_prop functions in a
>> > flag-day like operation we just add a new interface. I haven't double
>> > checked, but I guess that means per-obj ->atomic_set_prop functions plus a
>> > global ->atomic_check and ->atomic_commit.
>>
>> that sounds much worse
>
> Why that? Afaics your patch only changes the interfaces but leaves the
> semantics the same (i915 does set_config_internal all over the place in
> set_prop). So essentially you have both interface, but merged into one.
> And especially for the set_prop the semantics our different.

well, it was the doubling up of code paths in core for handling legacy
and atomic side-by-side that I was trying to avoid

I do remember seeing i915 set_config_internal (looks like it has been
refactored into intel_set_mode())..  iirc, it was all on
connector->set_prop(), so it would essentially be it's own
atomic/transaction.

>> > 2. We add a new drm_atomic_helper.c library which provides functions to
>> > implement all the legacy interfaces (page_flip, update_plane, set_prop)
>> > using atomic interfaces. We should be able to 1:1 reuse Rob's code for
>> > this, but instead of changing the actual current interface code we put
>> > that into helpers.
>>
>> So, I've started ripping out page_flip.. now w/ primary plane helpers
>> we can do everything in terms of update_plane().  I have an idea to
>> handle events.  It isn't so bad, but forces me to do some re-arranging
>> in drm/msm that I was planning to postpone otherwise.
>>
>> (And I just got a fedex package w/ new toys, er, hardware.. but I'll
>> try to finish this today)
>
> Well in that case I think we should demote ->update_plane and friends to
> helper status, since for a proper atomic implemenation for i915 we can't
> use them.

->update_plane() probably should have been helper all along (but I'd
kinda like to get something merged before we do too much other
cleanup.. I spend enough of my time rebasing patches..)

>> > We don't need anything for cursor ioctls since cursor plane helpers
>> > already map the legacy cursor ioctls.
>> >
>> > 3. Rob's current code reuses the existing ->update_plane, ->pageflip and
>> > other entry points to implement the atomic commit helper functions. Imo
>> > that's a bad layering violation, and if we plug helpers in there we can't
>> > use them.
>>
>> Well, it is only a layering violation because you have a slightly
>> different idea of where the layers should be.  ;-)
>>
>> Atomic (or really the state stuff.. maybe I should have called it
>> "transaction" or something like that to avoid the atomic
>> connotation..) should be on top of, not below, helpers.
>
> I'm talking only about the legacy interfaces here, and my thinking is that
> we should not add a midlayer here (the atomic helper stuff) but directly
> pass it to drivers. They can the choose how to implement this.

so you actually have exactly what you want already.. just rename
'struct drm_atomic_funcs' to 'struct drm_atomic_helper_funcs' and your
own i915_atomic_commit() fxn.

> With the current scheme I essentially have to add a bunch of hacks for
> i915 to fish out the old pageflip semantics to keep gen2 going. That's
> fairly ugly, and the only reason is that you force a midlay between the
> existing legacy ioctls and i915.

not quite sure I follow.. isn't that what primary-plane helpers are
supposed to do for you?

update: oh, I guess they don't.. that sort of screws my plan to move
over to just ->update_plane().

> Of course for the actual atomic ioctl I don't want this inversion. But
> that's not what I'm talking about here.
>
>> Probably the commit part should be more clearly separated out and
>> marked as "helper", because that is the part that is about taking the
>> state that userspace (or fbdev) has asked for and applying it to the
>> hw.  This is really the part where drivers for some hw might want to
>> do something different.  Although I admit lumping it in w/
>> drm_atomic.c makes this not very clear.
>>
>> And then beneath atomic we introduce new interfaces (if needed..
>> although update_plane isn't so bad once we toss out page_flip).  We
>> could introduce new ->commit_state() vfuncs, and alternate set of
>> commit helper which uses those.. at least for planes I suspect
>> ->commit_state() ends up looking a lot like ->update_plane.
>
> All ok with me, but really not my concern. This is all for drivers which
> support atomic, I'll have a driver which will (at least partially and
> likely forever) not support atomic everywhere.

admittedly it wasn't a case I thought of much (one driver, supporting
atomic for only certain generations).. although I don't particularly
see the problem here.  You might just need to plug in different helper
fxn ptrs depending on generation.

>> > Instead I think we should add a new per-plane ->atomic_commit functions
>> > clearly marked as optional. Maybe even as part of an opaque
>> > plane_helper_funcs pointer like we have with crtc/encoder/connector and
>> > crtc helpers. msm would then implement it's atomic commit function in
>> > there (since it's the only driver currently supporting atomic for real).
>> >
>> > 3b. We could adjust crtc helpers to use the plane commit hook instead of
>> > the set_base function when avialable.
>>
>> would be a nice cleanup either way.. but I think it is independent..
>
> I think I've raised this a few times in the past, but imo having somewhat
> clear helper semantics would help. Atm they try to do a bit too much
> (legacy support, generic helpers, interface with crtc helpers without
> changing too much) and don't look good in any of them.
>
>> > 4. We do a pimped atomic helper based upon crtc helpers and the above
>> > plane commit function added in 3. It would essentially implement what
>> > Rob's current helper does, i.e.
>> >
>> > Step 1: Shut down all crtcs which change using the crtc helpers. This step
>> > is obviously optional and ommitted when there's nothing to do.
>> >
>> > Step 2: Loop over all planes and call ->plane_commit or whatever we will
>> > call the interface added in 3. above.
>> >
>> > Step 3: Enable all crtcs that need enabling.
>> >
>> > 5. We start converting drivers. Every driver can convert at it's own pace,
>> > opting in for atomic behaviour step-by-step.
>> >
>> > 6. We optionally use the atomic interface in the fb helper. It's imo
>> > important to keep the code here parallel so that drivers can convert at
>> > their own leisure.
>>
>> this is one thing I really wanted to avoid.  It was already getting
>> ugly the first time I tried it, and I hadn't even converted
>> everything.
>
> Well I think we can't avoid ugliness in that area. There simply will be
> parallel support for atomic in different drivers.

I think it is avoidable.  I mean, I've used my patchset on an
unconverted i915 (as well as both converted and unconverted msm).. the
commit helper part of things successfully turns legacy APIs back into
legacy vfunc calls.

BR,
-R


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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/7] drm: add atomic fxns
  2014-07-24 10:02   ` Thierry Reding
@ 2014-07-24 18:31     ` "Stéphane Viau"
  0 siblings, 0 replies; 20+ messages in thread
From: "Stéphane Viau" @ 2014-07-24 18:31 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

Hi,

>>  {
>>  	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);
>> +	}
>
> Why the extra braces here? There's still only one statement in the
> block.
>

According to Documentation/kernel-doc-nano-HOWTO.txt, these extra braces are
supposed to be present:

"
		Chapter 3: Placing Braces and Spaces
[...]
Do not unnecessarily use braces where a single statement will do.
[...]
This does not apply if only one branch of a conditional statement is a single
statement; in the latter case use braces in both branches:

if (condition) {
	do_this();
	do_that();
} else {
	otherwise();
}
"

Stéphane.

>>  /**
>> - * 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
>
> This isn't really introduced by this patch, but isn't this kerneldoc
> comment wrong? drm_mode_obj_get_properties_ioctl() seems to return the
> values of all properties of an object rather than just one.
>
> Thierry
>


-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/7] prepare for atomic.. the great propertyification
  2014-07-24 14:56       ` Rob Clark
@ 2014-07-25  8:15         ` Daniel Vetter
  2014-07-25 12:13           ` Rob Clark
  0 siblings, 1 reply; 20+ messages in thread
From: Daniel Vetter @ 2014-07-25  8:15 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Thu, Jul 24, 2014 at 10:56:07AM -0400, Rob Clark wrote:
[snip]

> ok, it's entirely possibly that I'm missunderstanding a bit your proposal..

Yeah I get that feeling a bit too. I'll cut out the technical details for
now and will try to concentrate on one example. Maybe that clarifies.


[snip]

> >> > So here's my proposal for how to get this in without breaking the world
> >> >
> >> > 1. We add a complete new set of ->atomic_foo driver entry points to all
> >> > relevant objects. So instead of changing all the set_prop functions in a
> >> > flag-day like operation we just add a new interface. I haven't double
> >> > checked, but I guess that means per-obj ->atomic_set_prop functions plus a
> >> > global ->atomic_check and ->atomic_commit.
> >>
> >> that sounds much worse
> >
> > Why that? Afaics your patch only changes the interfaces but leaves the
> > semantics the same (i915 does set_config_internal all over the place in
> > set_prop). So essentially you have both interface, but merged into one.
> > And especially for the set_prop the semantics our different.
> 
> well, it was the doubling up of code paths in core for handling legacy
> and atomic side-by-side that I was trying to avoid
> 
> I do remember seeing i915 set_config_internal (looks like it has been
> refactored into intel_set_mode())..  iirc, it was all on
> connector->set_prop(), so it would essentially be it's own
> atomic/transaction.

So currently our set_prop functions work like that:
1. We parse the property, sanity check it and store it in the relevant
object structure.
2. We check whether (after all the checking and parsing) there has been an
actual change in configuration. E.g. for the audio stuff if the use sets
back to "auto" we check whether the old config matches the new autoconfig
state or not. So it's not just "has the prop value changed", we actually
diff the resulting hw config.
3. If there is a change, we update the hw state with a call to mode_set
iff the pipe is on.

There's a few reasons why this works and why it looks like that.
- Our internal mode set code currently doesn't notice changes in property
  values. We plan to fix this eventually by shuffling the compute_config
  code around, but that's a lot of work.
- Our internal mode set function _always_ forces a full modeset on the
  crtc you pass it (besides doing modeset on other crtcs where tracked
  state has changed). We need to have this hack to make the above set_prop
  sequence work.

Now with atomic we want completely different semantics:
- Set_prop only updates state. We need to drop the state computation and
  diffing and the forced mode_set.
- The eventual commit will force a mode_set. Note that calling
  ->set_config will not be enough since that doesn't have the "force full
  mode-set" hack which the internal version has. And we can't do this
  since userspace uses set_crtc/->set_config to update frontbuffers which
  absolutely must not result in a full modeset.

So looking just at the ->set_prop function we have 2 completely different
semantics. Now I agree that with your patch i915 keeps on working. But the
problem I have is converting i915 over to the new world. Since you've
removed the old entry point I am left with no other choice than to convert
everything at once. And there's a lot more than just the hdmi audio
property - page_flip has slightly different semantics with atomic,
update_plane dito, set-crtc the same.

Which means I either have to convert i915 in one go (impossible given the
usual churn I face) or I just end up implementing the infrastructure I've
asked for (which means I get to reinstante all the old legacy ioctl
paths). Since with the doubled-up entry points I can e.g. just convert
hdmi set_prop to atomic, which means I have a minimal use-case to validate
the core infrastructure in i915 (i.e. the state diffing we need to improve
in the mode_set code) and can ignore all the nonsense in the tv connectors
and sdvo encoders and plane props and crtcs props and hacked-up other crap
we have all around. It's still going to be a major pain, but I expect the
transition to be much more smoothly.

My experience with the universal plane stuff really is that even slight
semantic differences in the interfaces bite you hard and there's no way to
work around that. The only sane way really is to have parallel entry
points with helpers so that transitioned drivers can remap legacy
interfaces to the more powerful new ones.

I hope this explains a bit better where I see the big risk with your
approach and what exactly I'm proposing.

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/7] prepare for atomic.. the great propertyification
  2014-07-25  8:15         ` Daniel Vetter
@ 2014-07-25 12:13           ` Rob Clark
  0 siblings, 0 replies; 20+ messages in thread
From: Rob Clark @ 2014-07-25 12:13 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Fri, Jul 25, 2014 at 4:15 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Thu, Jul 24, 2014 at 10:56:07AM -0400, Rob Clark wrote:
> [snip]
>
>> ok, it's entirely possibly that I'm missunderstanding a bit your proposal..
>
> Yeah I get that feeling a bit too. I'll cut out the technical details for
> now and will try to concentrate on one example. Maybe that clarifies.
>
>
> [snip]
>
>> >> > So here's my proposal for how to get this in without breaking the world
>> >> >
>> >> > 1. We add a complete new set of ->atomic_foo driver entry points to all
>> >> > relevant objects. So instead of changing all the set_prop functions in a
>> >> > flag-day like operation we just add a new interface. I haven't double
>> >> > checked, but I guess that means per-obj ->atomic_set_prop functions plus a
>> >> > global ->atomic_check and ->atomic_commit.
>> >>
>> >> that sounds much worse
>> >
>> > Why that? Afaics your patch only changes the interfaces but leaves the
>> > semantics the same (i915 does set_config_internal all over the place in
>> > set_prop). So essentially you have both interface, but merged into one.
>> > And especially for the set_prop the semantics our different.
>>
>> well, it was the doubling up of code paths in core for handling legacy
>> and atomic side-by-side that I was trying to avoid
>>
>> I do remember seeing i915 set_config_internal (looks like it has been
>> refactored into intel_set_mode())..  iirc, it was all on
>> connector->set_prop(), so it would essentially be it's own
>> atomic/transaction.
>
> So currently our set_prop functions work like that:
> 1. We parse the property, sanity check it and store it in the relevant
> object structure.
> 2. We check whether (after all the checking and parsing) there has been an
> actual change in configuration. E.g. for the audio stuff if the use sets
> back to "auto" we check whether the old config matches the new autoconfig
> state or not. So it's not just "has the prop value changed", we actually
> diff the resulting hw config.
> 3. If there is a change, we update the hw state with a call to mode_set
> iff the pipe is on.
>
> There's a few reasons why this works and why it looks like that.
> - Our internal mode set code currently doesn't notice changes in property
>   values. We plan to fix this eventually by shuffling the compute_config
>   code around, but that's a lot of work.
> - Our internal mode set function _always_ forces a full modeset on the
>   crtc you pass it (besides doing modeset on other crtcs where tracked
>   state has changed). We need to have this hack to make the above set_prop
>   sequence work.
>
> Now with atomic we want completely different semantics:
> - Set_prop only updates state. We need to drop the state computation and
>   diffing and the forced mode_set.

So probably the first thing, is that we need to bring connectors into
the atomic game..  probably that is something that i915 should take
the lead on, since you actually have a bunch of connector properties
which, from the sounds of it, should play by atomic rules.  I don't
have anything as complicated in msm, and wasn't entirely sure what
would be needed for connectors, so I punted on that part.

That said, crtc_state->set_config, etc, flags should be what you need
to know if you need a full modeset or not.  You just need your
connector's set_prop to grab the appropriate crtc_state and set the
appropriate flag(s).  So I'm not sure that I see any fundamental
problem here[*].

[*] well, combining connector changes w/ connector property changes
perhaps..  you may actually need your ->atomic_check() step to
propagate the "I need a modeset" flag from connector to crtc.. but
otoh a connector change is a full modeset, so maybe not.

> - The eventual commit will force a mode_set. Note that calling
>   ->set_config will not be enough since that doesn't have the "force full
>   mode-set" hack which the internal version has. And we can't do this
>   since userspace uses set_crtc/->set_config to update frontbuffers which
>   absolutely must not result in a full modeset.
>
> So looking just at the ->set_prop function we have 2 completely different
> semantics. Now I agree that with your patch i915 keeps on working. But the
> problem I have is converting i915 over to the new world. Since you've
> removed the old entry point I am left with no other choice than to convert
> everything at once. And there's a lot more than just the hdmi audio
> property - page_flip has slightly different semantics with atomic,
> update_plane dito, set-crtc the same.
>
> Which means I either have to convert i915 in one go (impossible given the
> usual churn I face) or I just end up implementing the infrastructure I've
> asked for (which means I get to reinstante all the old legacy ioctl
> paths). Since with the doubled-up entry points I can e.g. just convert
> hdmi set_prop to atomic, which means I have a minimal use-case to validate
> the core infrastructure in i915 (i.e. the state diffing we need to improve
> in the mode_set code) and can ignore all the nonsense in the tv connectors
> and sdvo encoders and plane props and crtcs props and hacked-up other crap
> we have all around. It's still going to be a major pain, but I expect the
> transition to be much more smoothly.
>
> My experience with the universal plane stuff really is that even slight
> semantic differences in the interfaces bite you hard and there's no way to
> work around that. The only sane way really is to have parallel entry
> points with helpers so that transitioned drivers can remap legacy
> interfaces to the more powerful new ones.
>
> I hope this explains a bit better where I see the big risk with your
> approach and what exactly I'm proposing.

Well, I'm still not convinced that my approach won't work.. at the end
of the day it is turning old interface -> new -> old.  And as long as
the new->old step is only having to deal with the subset of the new
more powerful API that was the old interface, the round-trip should be
doable/straightforward.

But I agree that your approach is more conservative, in that it is
more obviously not touching legacy stuff.  Although I'm not completely
sure yet how atomic helpers and crtc helpers fit together with your
approach.  I am really not ready to ditch crtc helpers for modeset yet
in msm.  I suppose that is mostly a matter of where the new vfuncs
live (esp. if we have both atomic and non-atomic versions of
->set_prop())..

I've got some other things to finish first, but I'll probably cook up
a patch to convert drm_display_mode to a refcnt'd object, which will
let me use display mode ptrs in drm_crtc_state, and gets rid of the
biggest awkwardness that I was seeing with your approach.

After that, I suppose I can give your proposal a try.  The change
shuffles things around quite a bit, so I doubt I'll have time to
finish it before the merge window, although as you point out it
wouldn't really effect any existing driver so probably safe for later
-rc merge.  Not sure that I'd have the driver bits for msm ready in
time, but I guess even if we were not ready to merge any driver bits,
it might be more convenient for everyone to have the core bits in
place the kernel before for merging driver bits in 3.18...

BR,
-R

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

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2014-07-25 12:13 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
2014-07-23 19:38 ` [PATCH 1/7] drm: add atomic fxns Rob Clark
2014-07-23 21:34   ` Daniel Vetter
2014-07-23 21:38     ` Daniel Vetter
2014-07-24 10:02   ` Thierry Reding
2014-07-24 18:31     ` "Stéphane Viau"
2014-07-23 19:38 ` [PATCH 2/7] drm: split propvals out and blob property support Rob Clark
2014-07-23 19:38 ` [PATCH 3/7] drm: Refactor object property check code Rob Clark
2014-07-23 19:38 ` [PATCH 4/7] drm: convert plane to properties/state Rob Clark
2014-07-24  0:42   ` Matt Roper
2014-07-23 19:38 ` [PATCH 5/7] drm: convert crtc " Rob Clark
2014-07-23 19:38 ` [PATCH 6/7] drm/msm: add atomic support Rob Clark
2014-07-23 19:38 ` [PATCH 7/7] drm: Fix up the atomic legacy paths so they work Rob Clark
2014-07-24 12:25 ` [PATCH 0/7] prepare for atomic.. the great propertyification Daniel Vetter
2014-07-24 13:36   ` Rob Clark
2014-07-24 14:00     ` Daniel Vetter
2014-07-24 14:56       ` Rob Clark
2014-07-25  8:15         ` Daniel Vetter
2014-07-25 12:13           ` Rob Clark
  -- strict thread matches above, loose matches on Subject: below --
2014-05-30 17:23 [PATCH 0/7] prepare for atomic/nuclear modeset/pageflip (vN+1) Rob Clark
2014-05-30 17:23 ` [PATCH 1/7] drm: add atomic fxns Rob Clark

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.