All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rob Clark <robdclark@gmail.com>
To: dri-devel@lists.freedesktop.org
Subject: [PATCH 1/7] drm: add atomic fxns
Date: Wed, 23 Jul 2014 15:38:14 -0400	[thread overview]
Message-ID: <1406144300-4995-2-git-send-email-robdclark@gmail.com> (raw)
In-Reply-To: <1406144300-4995-1-git-send-email-robdclark@gmail.com>

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

  reply	other threads:[~2014-07-23 19:38 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-23 19:38 [PATCH 0/7] prepare for atomic.. the great propertyification Rob Clark
2014-07-23 19:38 ` Rob Clark [this message]
2014-07-23 21:34   ` [PATCH 1/7] drm: add atomic fxns 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1406144300-4995-2-git-send-email-robdclark@gmail.com \
    --to=robdclark@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.