All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFCv4 00/14] Atomic/nuclear modeset/pageflip
@ 2013-11-25 14:47 Rob Clark
  2013-11-25 14:47 ` [RFCv4 01/14] drm: add atomic fxns Rob Clark
                   ` (13 more replies)
  0 siblings, 14 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

Previous Revision:
http://lists.freedesktop.org/archives/dri-devel/2013-November/049363.html

Changes since previous:
+ convert new properties over to DRM_MODE_PROP_EXTENDED_TYPE, so we
  aren't consuming one new bit per property type (as suggested by, I
  think, Ville)
+ Fixed usage of ww_ctx, split things out into 'struct drm_modeset_lock'
  to make things easier when we start making locking even more fine
  grained (ie. per-plane locks, etc)
+ Handle connector_ids changes better.  In legacy setcrtc path, we
  implicitly update other CRTCs that are having their connector stolen,
  whereas in atomic ioctl there is no implicit connector stealing (ie.
  this is an error if user does not also remove the connector from it's
  current CRTC.

---------------------------------------------------------------------

Description from original RFC (with updated links):

This patchset is the merging of Ville's atomic modeset ioctl, and
the drm_{crtc,plane}_state stuff from my original nuclear pageflip
RFC.

It is currently working on msm with an updated version of Ville's
glplane test app (removing cursor properties and atomic event):

  https://github.com/robclark/glplane

the libdrm bits can be found here:

  https://github.com/robclark/libdrm/commits/atomic

The msm part is on top of msm-next, the complete branch can be found:

  http://cgit.freedesktop.org/~robclark/linux/log/?h=global-thermonuclear-war-9

Compared to my earlier nuclear pageflip RFC, there is now a set of
drm_atomic helpers which do most of the non-hw-specific work for
the different drivers.

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 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_helper_begin,
    .atomic_set_event = drm_atomic_helper_set_event,
    .atomic_check     = drm_atomic_helper_check,
    .atomic_commit    = drm_atomic_helper_commit,
    .atomic_end       = drm_atomic_helper_end,
    .atomic_helpers   = &drm_atomic_helper_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_helper_{plane,crtc}_set_property,

A driver which can have (for example) conflicting modes across
multiple crtcs (for example, bandwidth limitations or clock/pll
configuration restrictions), can wrap drm_atomic_helper_check()
with their own driver specific .atomic_check() function.

A driver which can support true atomic updates can wrap
drm_atomic_helper_commit().

A driver with custom properties should override the appropriate
get_state(), check_state(), and commit_state() functions in
.atomic_helpers if it uses the drm-atomic-helpers.  Otherwise it
is free to use &drm_atomic_helper_funcs as-is.


Rob Clark (11):
  drm: add atomic fxns
  drm: convert crtc to ww_mutex
  drm: add object property type
  drm: add signed-range property type
  drm: helpers to find mode objects
  drm: split propvals out and blob property support
  drm: convert plane to properties/state
  drm: convert crtc to properties/state
  drm: push locking down into restore_fbdev_mode
  drm/msm: add atomic support
  HACK: drm: allow FB's in drm_mode_object_find

Ville Syrjälä (3):
  drm: Allow drm_mode_object_find() to look up an object of any type
  drm: Refactor object property check code
  drm: Atomic modeset ioctl

 drivers/gpu/drm/Makefile                    |    3 +-
 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_helper.c         |  696 ++++++++++++
 drivers/gpu/drm/drm_crtc.c                  | 1630 +++++++++++++++++++--------
 drivers/gpu/drm/drm_drv.c                   |    1 +
 drivers/gpu/drm/drm_fb_cma_helper.c         |    9 +-
 drivers/gpu/drm/drm_fb_helper.c             |   28 +-
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   15 +-
 drivers/gpu/drm/exynos/exynos_drm_drv.c     |    7 +
 drivers/gpu/drm/exynos/exynos_drm_encoder.c |    6 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c   |    4 +-
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |   19 +-
 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            |   11 +-
 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        |   20 +-
 drivers/gpu/drm/i915/intel_dp.c             |    7 +-
 drivers/gpu/drm/i915/intel_drv.h            |    1 +
 drivers/gpu/drm/i915/intel_fbdev.c          |    6 +-
 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         |   21 +-
 drivers/gpu/drm/i915/intel_tv.c             |   11 +-
 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/mdp4/mdp4_crtc.c        |   69 +-
 drivers/gpu/drm/msm/mdp4/mdp4_kms.c         |    6 +
 drivers/gpu/drm/msm/mdp4/mdp4_kms.h         |    1 +
 drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |   23 +-
 drivers/gpu/drm/msm/msm_atomic.c            |  149 +++
 drivers/gpu/drm/msm/msm_drv.c               |   33 +-
 drivers/gpu/drm/msm/msm_drv.h               |    9 +
 drivers/gpu/drm/msm/msm_gem.c               |   24 +-
 drivers/gpu/drm/msm/msm_gem.h               |   13 +
 drivers/gpu/drm/nouveau/dispnv04/crtc.c     |    1 +
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   12 +-
 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         |   31 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   16 +-
 drivers/gpu/drm/omapdrm/omap_drv.h          |    5 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |   37 +-
 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   |    8 +-
 drivers/gpu/drm/shmobile/shmob_drm_drv.c    |    7 +
 drivers/gpu/drm/shmobile/shmob_drm_plane.c  |    6 +-
 drivers/gpu/drm/tegra/dc.c                  |   16 +-
 drivers/gpu/drm/tegra/fb.c                  |    7 +-
 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         |   12 +-
 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                          |   88 +-
 include/drm/drm_atomic_helper.h             |  193 ++++
 include/drm/drm_crtc.h                      |  341 +++++-
 include/drm/drm_fb_helper.h                 |    3 +-
 include/uapi/drm/drm.h                      |   12 +
 include/uapi/drm/drm_mode.h                 |   38 +
 99 files changed, 3166 insertions(+), 767 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_atomic_helper.c
 create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
 create mode 100644 include/drm/drm_atomic_helper.h

-- 
1.8.4.2

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

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

* [RFCv4 01/14] drm: add atomic fxns
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 02/14] drm: convert crtc to ww_mutex Rob Clark
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 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).
---
 drivers/gpu/drm/Makefile                    |   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_helper.c         | 135 ++++++++++++++++++++++++++
 drivers/gpu/drm/drm_crtc.c                  | 141 +++++++++++++++++-----------
 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             |   5 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c       |   7 ++
 drivers/gpu/drm/mgag200/mgag200_drv.h       |   1 +
 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   4 +-
 drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |   4 +-
 drivers/gpu/drm/msm/msm_drv.c               |   6 ++
 drivers/gpu/drm/msm/msm_drv.h               |   1 +
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   4 +-
 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         |   7 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   6 ++
 drivers/gpu/drm/omapdrm/omap_drv.h          |   5 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |   4 +-
 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                          |  77 +++++++++++++++
 include/drm/drm_atomic_helper.h             | 100 ++++++++++++++++++++
 include/drm/drm_crtc.h                      |  14 +--
 61 files changed, 619 insertions(+), 103 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_atomic_helper.c
 create mode 100644 include/drm/drm_atomic_helper.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index cc08b84..4a2e320 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -13,7 +13,8 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_crtc.o drm_modes.o drm_edid.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_rect.o drm_vma_manager.o drm_flip_work.o \
+		drm_atomic_helper.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 5137f15..90ba5f1 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -216,6 +216,12 @@ static struct drm_driver driver = {
 	.dumb_map_offset = ast_dumb_mmap_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
 
+	.atomic_begin     = drm_atomic_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 9833a1b..72d87d9 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_helper.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 953fc8a..3544a92 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -101,6 +101,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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_funcs,
 };
 
 static struct pci_driver cirrus_pci_driver = {
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index b6aded73..e5a83b2 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_helper.h>
 
 #include <drm/ttm/ttm_bo_api.h>
 #include <drm/ttm/ttm_bo_driver.h>
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
new file mode 100644
index 0000000..46c67b8
--- /dev/null
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -0,0 +1,135 @@
+/*
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+
+/**
+ * drm_atomic_helper_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 private 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)
+ */
+void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
+{
+	struct drm_atomic_helper_state *state;
+	int sz;
+	void *ptr;
+
+	sz = sizeof(*state);
+
+	ptr = kzalloc(sz, GFP_KERNEL);
+
+	state = ptr;
+	ptr = &state[1];
+
+	kref_init(&state->refcount);
+	state->dev = dev;
+	state->flags = flags;
+	return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_begin);
+
+/**
+ * drm_atomic_helper_set_event - set a pending event on mode object
+ * @dev: DRM device
+ * @state: the driver private 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_helper_set_event(struct drm_device *dev,
+		void *state, struct drm_mode_object *obj,
+		struct drm_pending_vblank_event *event)
+{
+	return -EINVAL;  /* for now */
+}
+EXPORT_SYMBOL(drm_atomic_helper_set_event);
+
+/**
+ * drm_atomic_helper_check - validate state object
+ * @dev: DRM device
+ * @state: the driver private state object
+ *
+ * Check the state object to see if the requested state is
+ * physically possible.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_helper_check(struct drm_device *dev, void *state)
+{
+	return 0;  /* for now */
+}
+EXPORT_SYMBOL(drm_atomic_helper_check);
+
+/**
+ * drm_atomic_helper_commit - commit state
+ * @dev: DRM device
+ * @state: the driver private state object
+ *
+ * Commit the state.  This will only be called if atomic_check()
+ * succeeds.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_helper_commit(struct drm_device *dev, void *state)
+{
+	return 0;  /* for now */
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit);
+
+/**
+ * drm_atomic_helper_end - conclude the atomic update
+ * @dev: DRM device
+ * @state: the driver private state object
+ *
+ * Release resources associated with the state object.
+ */
+void drm_atomic_helper_end(struct drm_device *dev, void *state)
+{
+	drm_atomic_helper_state_unreference(state);
+}
+EXPORT_SYMBOL(drm_atomic_helper_end);
+
+void _drm_atomic_helper_state_free(struct kref *kref)
+{
+	struct drm_atomic_helper_state *state =
+		container_of(kref, struct drm_atomic_helper_state, refcount);
+	kfree(state);
+}
+EXPORT_SYMBOL(_drm_atomic_helper_state_free);
+
+
+const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = {
+};
+EXPORT_SYMBOL(drm_atomic_helper_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index d6cf77c..81ac351 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3281,20 +3281,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,
+					   void *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)
@@ -3302,36 +3303,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,
+				      void *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,
+				      void *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, void *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, void *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_mode_object *prop_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;
+
+	prop_obj = drm_mode_object_find(dev, prop_id,
+					DRM_MODE_OBJECT_PROPERTY);
+	if (!prop_obj)
+		return -ENOENT;
+	property = obj_to_property(prop_obj);
+
+	return drm_mode_set_obj_prop(dev, arg_obj, state, property,
+			value, blob_data);
+}
+
 int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
 				      struct drm_file *file_priv)
 {
@@ -3392,57 +3447,35 @@ 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;
+	void *state;
 	int ret = -EINVAL;
-	int i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
 
-	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
-	if (!arg_obj) {
-		ret = -ENOENT;
-		goto out;
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state)) {
+		ret = PTR_ERR(state);
+		goto out_unlock;
 	}
-	if (!arg_obj->properties)
-		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_mode_set_obj_prop_id(dev, state,
+			arg->obj_id, arg->obj_type,
+			arg->prop_id, arg->value, NULL);
+	if (ret)
 		goto out;
 
-	prop_obj = drm_mode_object_find(dev, arg->prop_id,
-					DRM_MODE_OBJECT_PROPERTY);
-	if (!prop_obj) {
-		ret = -ENOENT;
-		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:
+	dev->driver->atomic_end(dev, state);
+out_unlock:
 	drm_modeset_unlock_all(dev);
 	return ret;
 }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index ebc0150..82a9fca 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -254,8 +254,10 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
 }
 
 static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
+					void *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 3a1e6d9..ca3bc6b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -13,6 +13,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
 
 #include <drm/exynos_drm.h>
 
@@ -269,6 +270,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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 fcb0652..2e31fb8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -217,8 +217,10 @@ static void exynos_plane_destroy(struct drm_plane *plane)
 }
 
 static int exynos_plane_set_property(struct drm_plane *plane,
+				     void *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 661af49..d9c612f9 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -212,8 +212,10 @@ static int cdv_intel_crt_get_modes(struct drm_connector *connector)
 }
 
 static int cdv_intel_crt_set_property(struct drm_connector *connector,
+				  void *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 f88a181..523dfc9 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1644,8 +1644,10 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector)
 
 static int
 cdv_intel_dp_set_property(struct drm_connector *connector,
+		      void *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 1c0d723..74e5de1 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -157,8 +157,10 @@ static enum drm_connector_status cdv_hdmi_detect(
 }
 
 static int cdv_hdmi_set_property(struct drm_connector *connector,
+				       void *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 20e08e6..97d76c1 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,
+				       void *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 860a4ee..76dadee 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,
+				void *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 1199180..34c3116 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -676,6 +676,13 @@ static struct drm_driver driver = {
 	.preclose = psb_driver_preclose,
 	.postclose = psb_driver_close,
 
+	.atomic_begin     = drm_atomic_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_funcs,
+
 	.gem_free_object = psb_gem_free_object,
 	.gem_vm_ops = &psb_gem_vm_ops,
 	.dumb_create = psb_gem_dumb_create,
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
index b59e658..9ed4cec 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_helper.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 bde27fd..d8e1911 100644
--- a/drivers/gpu/drm/gma500/psb_intel_drv.h
+++ b/drivers/gpu/drm/gma500/psb_intel_drv.h
@@ -261,8 +261,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,
+					void *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 32342f6..e3a3923 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,
+				       void *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 07d3a9e..5ff83dc 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,
+			void *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 989be12..3261ee7 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -978,6 +978,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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 b5b1b9b..dd7d10d 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -699,8 +699,10 @@ static int intel_crt_get_modes(struct drm_connector *connector)
 }
 
 static int intel_crt_set_property(struct drm_connector *connector,
+				  void *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 eb8139d..c9b3e56 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3160,8 +3160,10 @@ intel_dp_detect_audio(struct drm_connector *connector)
 
 static int
 intel_dp_set_property(struct drm_connector *connector,
+		      void *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 1e49aa8..1283d3e 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_helper.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 03f9ca7..59b1212 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1004,8 +1004,10 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
 
 static int
 intel_hdmi_set_property(struct drm_connector *connector,
+			void *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 c3b4da7..8d52d64 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -482,8 +482,10 @@ static void intel_lvds_destroy(struct drm_connector *connector)
 }
 
 static int intel_lvds_set_property(struct drm_connector *connector,
+				   void *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 a583e8f..9f805df 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2032,8 +2032,10 @@ static bool intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
 
 static int
 intel_sdvo_set_property(struct drm_connector *connector,
+			void *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 18c4062..5fa6839 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1439,8 +1439,9 @@ 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, void *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..59bf5ae 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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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..e01eed3 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_helper.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/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
index 019d530..80af12a 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -424,8 +424,8 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
 	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
 }
 
-static int mdp4_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
+static int mdp4_crtc_set_property(struct drm_crtc *crtc, void *state,
+		struct drm_property *property, uint64_t val, void *blob_data)
 {
 	// XXX
 	return -EINVAL;
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
index 0f0af24..880e96d 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
@@ -84,8 +84,8 @@ void mdp4_plane_install_properties(struct drm_plane *plane,
 	// XXX
 }
 
-int mdp4_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
+int mdp4_plane_set_property(struct drm_plane *plane, void *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 8653769..dadd55a 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -734,6 +734,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_helper_begin,
+	.atomic_set_event   = drm_atomic_helper_set_event,
+	.atomic_check       = drm_atomic_helper_check,
+	.atomic_commit      = drm_atomic_helper_commit,
+	.atomic_end         = drm_atomic_helper_end,
+	.atomic_helpers     = &drm_atomic_helper_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 d39f086..bfd4121 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -40,6 +40,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic_helper.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 3618ac6..fc938f7 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -208,9 +208,9 @@ nv10_set_params(struct nouveau_plane *plane)
 }
 
 static int
-nv10_set_property(struct drm_plane *plane,
+nv10_set_property(struct drm_plane *plane, void *state,
 		  struct drm_property *property,
-		  uint64_t value)
+		  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 1674882..36499e1 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)
+		void *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 7a3759f..8b0d771 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -822,6 +822,13 @@ driver = {
 	.dumb_map_offset = nouveau_display_dumb_map_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
 
+	.atomic_begin     = drm_atomic_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 4b0fb6c..963ebb0 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_helper.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 0fd2eb1..5dd22ab 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -362,8 +362,8 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
 	return 0;
 }
 
-static int omap_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
+static int omap_crtc_set_property(struct drm_crtc *crtc, void *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;
@@ -373,7 +373,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 e7fa3cd..bb07519 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -625,6 +625,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_helper_begin,
+		.atomic_set_event = drm_atomic_helper_set_event,
+		.atomic_check     = drm_atomic_helper_check,
+		.atomic_commit    = drm_atomic_helper_commit,
+		.atomic_end       = drm_atomic_helper_end,
+		.atomic_helpers   = &drm_atomic_helper_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 0784769..c6bd455 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_helper.h>
 #include <drm/omap_drm.h>
 #include <linux/platform_data/omap_drm.h>
 
@@ -173,8 +174,8 @@ int omap_plane_mode_set(struct drm_plane *plane,
 		void (*fxn)(void *), void *arg);
 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);
+int omap_plane_set_property(struct drm_plane *plane, void *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 046d5e6..fe32f1b 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -327,8 +327,8 @@ void omap_plane_install_properties(struct drm_plane *plane,
 	drm_object_attach_property(obj, prop, 0);
 }
 
-int omap_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
+int omap_plane_set_property(struct drm_plane *plane, void *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 5e827c2..d36abbc 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -812,8 +812,10 @@ static enum drm_connector_status qxl_conn_detect(
 }
 
 static int qxl_conn_set_property(struct drm_connector *connector,
+				   void *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 fee8748..ca0dc68 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_helper.h"
 #include "qxl_drv.h"
 #include "qxl_object.h"
 
@@ -221,6 +222,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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 20a768a..2775590 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -368,8 +368,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,
+		void *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	struct drm_device *dev = connector->dev;
 	struct radeon_device *rdev = dev->dev_private;
@@ -691,8 +692,10 @@ static void radeon_connector_destroy(struct drm_connector *connector)
 }
 
 static int radeon_lvds_set_property(struct drm_connector *connector,
+				    void *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 1aee322..5c65f13 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_helper.h>
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
@@ -548,6 +549,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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 0023f97..100ab8b 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_helper.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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 5300064..5691743 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -396,8 +396,10 @@ done:
 }
 
 static int rcar_du_plane_set_property(struct drm_plane *plane,
+				      void *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 0155518..ab761ff 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_helper.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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 116da19..305b961 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_helper_begin,
+	.atomic_set_event   = drm_atomic_helper_set_event,
+	.atomic_check       = drm_atomic_helper_check,
+	.atomic_commit      = drm_atomic_helper_commit,
+	.atomic_end         = drm_atomic_helper_end,
+	.atomic_helpers     = &drm_atomic_helper_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..2f0c093 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_helper.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..48bcc26 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)
+		void *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..cd0c01a 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, 
+			       void *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..9b149f5 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_helper.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_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 20d5485..1302b6f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1235,6 +1235,13 @@ static struct drm_driver driver = {
 	.dumb_map_offset = vmw_dumb_map_offset,
 	.dumb_destroy = vmw_dumb_destroy,
 
+	.atomic_begin     = drm_atomic_helper_begin,
+	.atomic_set_event = drm_atomic_helper_set_event,
+	.atomic_check     = drm_atomic_helper_check,
+	.atomic_commit    = drm_atomic_helper_commit,
+	.atomic_end       = drm_atomic_helper_end,
+	.atomic_helpers   = &drm_atomic_helper_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 e401d5d..4d160f3 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_helper.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 ecb3d86..f91447c 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1994,8 +1994,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
 }
 
 int vmw_du_connector_set_property(struct drm_connector *connector,
+				  void *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..e259d6c 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,
+				  void *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 1d4a920..4c54c88 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -973,6 +973,83 @@ 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 private 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)
+	 */
+	void *(*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 private 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,
+			void *state, struct drm_mode_object *obj,
+			struct drm_pending_vblank_event *event);
+
+	/**
+	 * atomic_check - validate state object
+	 * @dev: DRM device
+	 * @state: the driver private 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, void *state);
+
+	/**
+	 * atomic_commit - commit state
+	 * @dev: DRM device
+	 * @state: the driver private 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, void *state);
+
+	/**
+	 * atomic_end - conclude the atomic update
+	 * @dev: DRM device
+	 * @state: the driver private state object
+
+	 * Release resources associated with the state object.
+	 */
+	void (*atomic_end)(struct drm_device *dev, void *state);
+
+	/**
+	 * Helpers used by drm-atomic-helpers
+	 */
+	const void *atomic_helpers;
+
 	/* Driver private ops for this object */
 	const struct vm_operations_struct *gem_vm_ops;
 
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
new file mode 100644
index 0000000..e70cd7b
--- /dev/null
+++ b/include/drm/drm_atomic_helper.h
@@ -0,0 +1,100 @@
+/*
+ * 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/>.
+ */
+
+#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_helper_begin,
+ *     .atomic_set_event = drm_atomic_helper_set_event,
+ *     .atomic_check     = drm_atomic_helper_check,
+ *     .atomic_commit    = drm_atomic_helper_commit,
+ *     .atomic_end       = drm_atomic_helper_end,
+ *     .atomic_helpers   = &drm_atomic_helper_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_helper_{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_helper_check() with their own
+ * driver specific .atomic_check() function.
+ *
+ * A driver which can support true atomic updates can wrap
+ * drm_atomic_helper_commit().
+ *
+ * A driver with custom properties should override the appropriate get_state(),
+ * check_state(), and commit_state() functions in .atomic_helpers if it uses
+ * the drm-atomic-helpers.  Otherwise it is free to use &drm_atomic_helper_funcs
+ * as-is.
+ */
+
+/**
+ * struct drm_atomic_helper_funcs - helper funcs used by the atomic helpers
+ */
+struct drm_atomic_helper_funcs {
+	int dummy; /* for now */
+};
+
+const extern struct drm_atomic_helper_funcs drm_atomic_helper_funcs;
+
+void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags);
+int drm_atomic_helper_set_event(struct drm_device *dev,
+		void *state, struct drm_mode_object *obj,
+		struct drm_pending_vblank_event *event);
+int drm_atomic_helper_check(struct drm_device *dev, void *state);
+int drm_atomic_helper_commit(struct drm_device *dev, void *state);
+void drm_atomic_helper_end(struct drm_device *dev, void *state);
+
+/**
+ * struct drm_atomic_helper_state - the state object used by atomic helpers
+ */
+struct drm_atomic_helper_state {
+	struct kref refcount;
+	struct drm_device *dev;
+	uint32_t flags;
+};
+
+static inline void
+drm_atomic_helper_state_reference(struct drm_atomic_helper_state *state)
+{
+	kref_get(&state->refcount);
+}
+
+static inline void
+drm_atomic_helper_state_unreference(struct drm_atomic_helper_state *state)
+{
+	void _drm_atomic_helper_state_free(struct kref *kref);
+	kref_put(&state->refcount, _drm_atomic_helper_state_free);
+}
+
+#endif /* DRM_ATOMIC_HELPER_H_ */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index f32c5cd..0ca684a 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -376,8 +376,9 @@ struct drm_crtc_funcs {
 			 struct drm_pending_vblank_event *event,
 			 uint32_t flags);
 
-	int (*set_property)(struct drm_crtc *crtc,
-			    struct drm_property *property, uint64_t val);
+	int (*set_property)(struct drm_crtc *crtc, void *state,
+			    struct drm_property *property, uint64_t val,
+			    void *blob_data);
 };
 
 /**
@@ -487,8 +488,8 @@ 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, void *state,
+			struct drm_property *property, uint64_t val, void *blob_data);
 	void (*destroy)(struct drm_connector *connector);
 	void (*force)(struct drm_connector *connector);
 };
@@ -659,8 +660,9 @@ struct drm_plane_funcs {
 	int (*disable_plane)(struct drm_plane *plane);
 	void (*destroy)(struct drm_plane *plane);
 
-	int (*set_property)(struct drm_plane *plane,
-			    struct drm_property *property, uint64_t val);
+	int (*set_property)(struct drm_plane *plane, void *state,
+			    struct drm_property *property, uint64_t val,
+			    void *blob_data);
 };
 
 /**
-- 
1.8.4.2

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

* [RFCv4 02/14] drm: convert crtc to ww_mutex
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
  2013-11-25 14:47 ` [RFCv4 01/14] drm: add atomic fxns Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 03/14] drm: add object property type Rob Clark
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

For atomic, it will be quite convenient to not have to care so much
about locking order.  And 'state' gives us a convenient place to stash a
ww_ctx for any sort of update that needs to grab multiple crtc locks.

Because we will want to eventually make locking even more fine grained
(giving locks to planes, connectors, etc), split out drm_modeset_lock
so that the atomic state won't eventually have to keep separate lists of
locked planes/crtcs/etc.

The state keeps track of which locks it has aquired, and for the benefit
of NONBLOCK operations, supports transfering "locked" resources to
driver worker/thread to complete the asynchronous state change.
---
 drivers/gpu/drm/drm_atomic_helper.c  | 140 ++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/drm_crtc.c           | 105 +++++++++++++++++++++++---
 drivers/gpu/drm/i915/intel_display.c |  16 ++--
 drivers/gpu/drm/omapdrm/omap_crtc.c  |  10 +--
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c  |   8 +-
 include/drm/drmP.h                   |   5 --
 include/drm/drm_atomic_helper.h      |  17 +++++
 include/drm/drm_crtc.h               |  75 +++++++++++++++++--
 include/uapi/drm/drm_mode.h          |   3 +
 9 files changed, 340 insertions(+), 39 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 46c67b8..8b37cf1 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -49,9 +49,14 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 	state = ptr;
 	ptr = &state[1];
 
+	ww_acquire_init(&state->ww_ctx, &crtc_ww_class);
+	INIT_LIST_HEAD(&state->locked);
+
+	mutex_init(&state->mutex);
 	kref_init(&state->refcount);
 	state->dev = dev;
 	state->flags = flags;
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_helper_begin);
@@ -91,6 +96,103 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state)
 }
 EXPORT_SYMBOL(drm_atomic_helper_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_helper_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	struct drm_modeset_lock *lock;
+
+	mutex_lock(&a->mutex);
+	list_for_each_entry(lock, &a->locked, head)
+		ww_mutex_unlock(&lock->mutex);
+	mutex_unlock(&a->mutex);
+
+	ww_acquire_fini(ww_ctx);
+}
+
+static void grab_locks(struct drm_atomic_helper_state *a,
+		struct ww_acquire_ctx *ww_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, &a->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, &a->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_helper_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	/* and properly release them (clear in_atomic, remove from list): */
+	mutex_lock(&a->mutex);
+	while (!list_empty(&a->locked)) {
+		struct drm_modeset_lock *lock;
+
+		lock = list_first_entry(&a->locked,
+				struct drm_modeset_lock, head);
+
+		drm_modeset_unlock(lock);
+	}
+	mutex_unlock(&a->mutex);
+	ww_acquire_fini(ww_ctx);
+	a->committed = true;
+}
+
+static int atomic_commit(struct drm_atomic_helper_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	int ret = 0;
+
+	commit_locks(a, ww_ctx);
+
+	return ret;
+}
+
 /**
  * drm_atomic_helper_commit - commit state
  * @dev: DRM device
@@ -104,11 +206,26 @@ EXPORT_SYMBOL(drm_atomic_helper_check);
  */
 int drm_atomic_helper_commit(struct drm_device *dev, void *state)
 {
-	return 0;  /* for now */
+	struct drm_atomic_helper_state *a = state;
+	return atomic_commit(a, &a->ww_ctx);
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit);
 
 /**
+ * drm_atomic_helper_commit_unlocked - like drm_atomic_helper_commit
+ * but can be called back by driver in other thread.  Manages the lock
+ * transfer from initiating thread.
+ */
+int drm_atomic_helper_commit_unlocked(struct drm_device *dev, void *state)
+{
+	struct drm_atomic_helper_state *a = state;
+	struct ww_acquire_ctx ww_ctx;
+	grab_locks(a, &ww_ctx);
+	return atomic_commit(a, &ww_ctx);
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit_unlocked);
+
+/**
  * drm_atomic_helper_end - conclude the atomic update
  * @dev: DRM device
  * @state: the driver private state object
@@ -117,15 +234,32 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  */
 void drm_atomic_helper_end(struct drm_device *dev, void *state)
 {
+	struct drm_atomic_helper_state *a = state;
+
+	/* 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->ww_ctx);
+
 	drm_atomic_helper_state_unreference(state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_end);
 
 void _drm_atomic_helper_state_free(struct kref *kref)
 {
-	struct drm_atomic_helper_state *state =
+	struct drm_atomic_helper_state *a =
 		container_of(kref, struct drm_atomic_helper_state, refcount);
-	kfree(state);
+
+	/* in case we haven't already: */
+	if (!a->committed) {
+		grab_locks(a, &a->ww_ctx);
+		commit_locks(a, &a->ww_ctx);
+	}
+
+	mutex_destroy(&a->mutex);
+	kfree(a);
 }
 EXPORT_SYMBOL(_drm_atomic_helper_state_free);
 
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 81ac351..c8a4d2f 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -37,6 +37,89 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_atomic_helper.h>
+
+static int modeset_lock_state(struct drm_modeset_lock *lock,
+		struct drm_atomic_helper_state *a)
+{
+	int ret;
+
+	if (a->flags & DRM_MODE_ATOMIC_NOLOCK)
+		return 0;
+
+retry:
+	ret = ww_mutex_lock(&lock->mutex, &a->ww_ctx);
+	if (!ret) {
+		if (lock->atomic_pending) {
+			/* some other pending update with dropped locks */
+			ww_mutex_unlock(&lock->mutex);
+			if (a->flags & DRM_MODE_ATOMIC_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, &a->locked);
+	} else if (ret == -EALREADY) {
+		/* we already hold the lock.. this is fine */
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @state: atomic state
+ *
+ * If state is not NULL, then then it's acquire context is used
+ * and the lock does not need to be explicitly unlocked, it
+ * will be automatically unlocked when the atomic update is
+ * complete
+ */
+int drm_modeset_lock(struct drm_modeset_lock *lock, void *state)
+{
+	if (state)
+		return modeset_lock_state(lock, state);
+
+	ww_mutex_lock(&lock->mutex, NULL);
+	return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+/**
+ * drm_modeset_lock_all_crtcs - helper to drm_modeset_lock() all CRTCs
+ */
+int drm_modeset_lock_all_crtcs(struct drm_device *dev, void *state)
+{
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_crtc *crtc;
+	int ret = 0;
+
+	list_for_each_entry(crtc, &config->crtc_list, head) {
+		ret = drm_modeset_lock(&crtc->mutex, state);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
+
+/**
+ * drm_modeset_unlock - drop modeset lock
+ * @lock: lock to release
+ */
+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);
 
 /**
  * drm_modeset_lock_all - take all modeset locks
@@ -52,7 +135,7 @@ void drm_modeset_lock_all(struct drm_device *dev)
 	mutex_lock(&dev->mode_config.mutex);
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+		drm_modeset_lock(&crtc->mutex, NULL);
 }
 EXPORT_SYMBOL(drm_modeset_lock_all);
 
@@ -65,7 +148,7 @@ void drm_modeset_unlock_all(struct drm_device *dev)
 	struct drm_crtc *crtc;
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 
 	mutex_unlock(&dev->mode_config.mutex);
 }
@@ -84,7 +167,7 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev)
 		return;
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		WARN_ON(!mutex_is_locked(&crtc->mutex));
+		WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 }
@@ -613,6 +696,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
+DEFINE_WW_CLASS(crtc_ww_class);
+
 /**
  * drm_crtc_init - Initialise a new CRTC object
  * @dev: DRM device
@@ -634,8 +719,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 	crtc->invert_dimensions = false;
 
 	drm_modeset_lock_all(dev);
-	mutex_init(&crtc->mutex);
-	mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+	drm_modeset_lock_init(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);  /* dropped by _unlock_all() */
 
 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
 	if (ret)
@@ -668,6 +753,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 	kfree(crtc->gamma_store);
 	crtc->gamma_store = NULL;
 
+	drm_modeset_lock_fini(&crtc->mutex);
+
 	drm_mode_object_put(dev, &crtc->base);
 	list_del(&crtc->head);
 	dev->mode_config.num_crtc--;
@@ -2284,7 +2371,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 	}
 	crtc = obj_to_crtc(obj);
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	if (req->flags & DRM_MODE_CURSOR_BO) {
 		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
 			ret = -ENXIO;
@@ -2308,7 +2395,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 		}
 	}
 out:
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	return ret;
 
@@ -3657,7 +3744,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 		return -ENOENT;
 	crtc = obj_to_crtc(obj);
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	if (crtc->fb == NULL) {
 		/* The framebuffer is currently unbound, presumably
 		 * due to a hotplug event, that userspace has not
@@ -3741,7 +3828,7 @@ out:
 		drm_framebuffer_unreference(fb);
 	if (old_fb)
 		drm_framebuffer_unreference(old_fb);
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3cddd50..19e76bd 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2232,11 +2232,11 @@ void intel_display_handle_reset(struct drm_device *dev)
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-		mutex_lock(&crtc->mutex);
+		drm_modeset_lock(&crtc->mutex, NULL);
 		if (intel_crtc->active)
 			dev_priv->display.update_plane(crtc, crtc->fb,
 						       crtc->x, crtc->y);
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 	}
 }
 
@@ -7550,7 +7550,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 	if (encoder->crtc) {
 		crtc = encoder->crtc;
 
-		mutex_lock(&crtc->mutex);
+		drm_modeset_lock(&crtc->mutex, NULL);
 
 		old->dpms_mode = connector->dpms;
 		old->load_detect_temp = false;
@@ -7581,7 +7581,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		return false;
 	}
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	intel_encoder->new_crtc = to_intel_crtc(crtc);
 	to_intel_connector(connector)->new_encoder = intel_encoder;
 
@@ -7609,7 +7609,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
 	if (IS_ERR(fb)) {
 		DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 		return false;
 	}
 
@@ -7617,7 +7617,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
 		if (old->release_fb)
 			old->release_fb->funcs->destroy(old->release_fb);
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 		return false;
 	}
 
@@ -7648,7 +7648,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
 			drm_framebuffer_unreference(old->release_fb);
 		}
 
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 		return;
 	}
 
@@ -7656,7 +7656,7 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
 	if (old->dpms_mode != DRM_MODE_DPMS_ON)
 		connector->funcs->dpms(connector, old->dpms_mode);
 
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 }
 
 static int i9xx_pll_refclk(struct drm_device *dev,
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 5dd22ab..357342d 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -307,13 +307,13 @@ static void page_flip_worker(struct work_struct *work)
 	struct drm_display_mode *mode = &crtc->mode;
 	struct drm_gem_object *bo;
 
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
 			0, 0, mode->hdisplay, mode->vdisplay,
 			crtc->x << 16, crtc->y << 16,
 			mode->hdisplay << 16, mode->vdisplay << 16,
 			vblank_cb, crtc);
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	bo = omap_framebuffer_bo(crtc->fb, 0);
 	drm_gem_object_unreference_unlocked(bo);
@@ -447,7 +447,7 @@ static void apply_worker(struct work_struct *work)
 	 * the callbacks and list modification all serialized
 	 * with respect to modesetting ioctls from userspace.
 	 */
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 	dispc_runtime_get();
 
 	/*
@@ -492,7 +492,7 @@ static void apply_worker(struct work_struct *work)
 
 out:
 	dispc_runtime_put();
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 }
 
 int omap_crtc_apply(struct drm_crtc *crtc,
@@ -500,7 +500,7 @@ int omap_crtc_apply(struct drm_crtc *crtc,
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 
-	WARN_ON(!mutex_is_locked(&crtc->mutex));
+	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
 	/* no need to queue it again if it is already queued: */
 	if (apply->queued)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index f91447c..2573b2a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -186,7 +186,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
 	 * can do this since the caller in the drm core doesn't check anything
 	 * which is protected by any looks.
 	 */
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 	drm_modeset_lock_all(dev_priv->dev);
 
 	/* A lot of the code assumes this */
@@ -251,7 +251,7 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
 	ret = 0;
 out:
 	drm_modeset_unlock_all(dev_priv->dev);
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 
 	return ret;
 }
@@ -272,7 +272,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 	 * can do this since the caller in the drm core doesn't check anything
 	 * which is protected by any looks.
 	 */
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 	drm_modeset_lock_all(dev_priv->dev);
 
 	vmw_cursor_update_position(dev_priv, shown,
@@ -280,7 +280,7 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 				   du->cursor_y + du->hotspot_y);
 
 	drm_modeset_unlock_all(dev_priv->dev);
-	mutex_lock(&crtc->mutex);
+	drm_modeset_lock(&crtc->mutex, NULL);
 
 	return 0;
 }
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 4c54c88..f3aecd0 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1306,11 +1306,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev)
 	return ret;
 }
 
-static inline bool drm_modeset_is_locked(struct drm_device *dev)
-{
-	return mutex_is_locked(&dev->mode_config.mutex);
-}
-
 static inline bool drm_is_render_client(struct drm_file *file_priv)
 {
 	return file_priv->minor->type == DRM_MINOR_RENDER;
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index e70cd7b..d77f00e 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -73,6 +73,7 @@ int drm_atomic_helper_set_event(struct drm_device *dev,
 		struct drm_pending_vblank_event *event);
 int drm_atomic_helper_check(struct drm_device *dev, void *state);
 int drm_atomic_helper_commit(struct drm_device *dev, void *state);
+int drm_atomic_helper_commit_unlocked(struct drm_device *dev, void *state);
 void drm_atomic_helper_end(struct drm_device *dev, void *state);
 
 /**
@@ -82,6 +83,22 @@ struct drm_atomic_helper_state {
 	struct kref refcount;
 	struct drm_device *dev;
 	uint32_t flags;
+
+	bool committed;
+
+	struct ww_acquire_ctx ww_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_helper_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;
 };
 
 static inline void
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 0ca684a..d4d6a7f 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -27,6 +27,7 @@
 
 #include <linux/i2c.h>
 #include <linux/spinlock.h>
+#include <linux/ww_mutex.h>
 #include <linux/types.h>
 #include <linux/idr.h>
 #include <linux/fb.h>
@@ -309,6 +310,73 @@ struct drm_property {
 	struct list_head enum_blob_list;
 };
 
+/**
+ * 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
+ *
+ * Used for locking CRTCs and other modeset resources.
+ */
+struct drm_modeset_lock {
+	/**
+	 * 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_helper_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;
+
+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)
+{
+	WARN_ON(!list_empty(&lock->head));
+}
+
+static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)
+{
+	return ww_mutex_is_locked(&lock->mutex);
+}
+
+int drm_modeset_lock(struct drm_modeset_lock *lock, void *state);
+int drm_modeset_lock_all_crtcs(struct drm_device *dev, void *state);
+void drm_modeset_unlock(struct drm_modeset_lock *lock);
+
+void drm_modeset_lock_all(struct drm_device *dev);
+void drm_modeset_unlock_all(struct drm_device *dev);
+void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
+
 struct drm_crtc;
 struct drm_connector;
 struct drm_encoder;
@@ -385,6 +453,7 @@ struct drm_crtc_funcs {
  * drm_crtc - central CRTC control structure
  * @dev: parent DRM device
  * @head: list management
+ * @mutex: per-CRTC locking
  * @base: base KMS object for ID tracking etc.
  * @enabled: is this CRTC enabled?
  * @mode: current mode timings
@@ -417,7 +486,7 @@ struct drm_crtc {
 	 * state, ...) and a write lock for everything which can be update
 	 * without a full modeset (fb, cursor data, ...)
 	 */
-	struct mutex mutex;
+	struct drm_modeset_lock mutex;
 
 	struct drm_mode_object base;
 
@@ -923,10 +992,6 @@ struct drm_prop_enum_list {
 	char *name;
 };
 
-extern void drm_modeset_lock_all(struct drm_device *dev);
-extern void drm_modeset_unlock_all(struct drm_device *dev);
-extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev);
-
 extern int drm_crtc_init(struct drm_device *dev,
 			 struct drm_crtc *crtc,
 			 const struct drm_crtc_funcs *funcs);
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index f104c26..12e2139 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -496,4 +496,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.8.4.2

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

* [RFCv4 03/14] drm: add object property type
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
  2013-11-25 14:47 ` [RFCv4 01/14] drm: add atomic fxns Rob Clark
  2013-11-25 14:47 ` [RFCv4 02/14] drm: convert crtc to ww_mutex Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 04/14] drm: add signed-range " Rob Clark
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

An object property is an id (idr) for a drm mode object.  This
will allow a property to be used set/get a framebuffer, CRTC, etc.
---
 drivers/gpu/drm/drm_crtc.c  | 60 +++++++++++++++++++++++++++++++++++----------
 include/drm/drm_crtc.h      | 27 ++++++++++++++++++++
 include/uapi/drm/drm_mode.h | 14 +++++++++++
 3 files changed, 88 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index c8a4d2f..8013204 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -2913,6 +2913,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
 	if (!property)
 		return NULL;
 
+	property->dev = dev;
+
 	if (num_values) {
 		property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL);
 		if (!property->values)
@@ -2933,6 +2935,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
 	}
 
 	list_add_tail(&property->head, &dev->mode_config.property_list);
+
+	BUG_ON(!drm_property_type_valid(property));
+
 	return property;
 fail:
 	kfree(property->values);
@@ -3016,19 +3021,38 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
 }
 EXPORT_SYMBOL(drm_property_create_range);
 
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+					 int flags, const char *name, uint32_t type)
+{
+	struct drm_property *property;
+
+	flags |= DRM_MODE_PROP_OBJECT;
+
+	property = drm_property_create(dev, flags, name, 1);
+	if (!property)
+		return NULL;
+
+	property->values[0] = type;
+
+	return property;
+}
+EXPORT_SYMBOL(drm_property_create_object);
+
 int drm_property_add_enum(struct drm_property *property, int index,
 			  uint64_t value, const char *name)
 {
 	struct drm_property_enum *prop_enum;
 
-	if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
+	if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
 		return -EINVAL;
 
 	/*
 	 * Bitmask enum properties have the additional constraint of values
 	 * from 0 to 63
 	 */
-	if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63))
+	if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+			(value > 63))
 		return -EINVAL;
 
 	if (!list_empty(&property->enum_blob_list)) {
@@ -3153,10 +3177,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 	}
 	property = obj_to_property(obj);
 
-	if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		list_for_each_entry(prop_enum, &property->enum_blob_list, head)
 			enum_count++;
-	} else if (property->flags & DRM_MODE_PROP_BLOB) {
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
 		list_for_each_entry(prop_blob, &property->enum_blob_list, head)
 			blob_count++;
 	}
@@ -3178,7 +3203,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 	}
 	out_resp->count_values = value_count;
 
-	if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
+	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
 			copied = 0;
 			enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3200,7 +3226,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 		out_resp->count_enum_blobs = enum_count;
 	}
 
-	if (property->flags & DRM_MODE_PROP_BLOB) {
+	if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
 		if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
 			copied = 0;
 			blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
@@ -3331,19 +3357,25 @@ static bool drm_property_change_is_valid(struct drm_property *property,
 {
 	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
 		return false;
-	if (property->flags & DRM_MODE_PROP_RANGE) {
+
+	if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
 		if (value < property->values[0] || value > property->values[1])
 			return false;
 		return true;
-	} else if (property->flags & DRM_MODE_PROP_BITMASK) {
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		int i;
 		uint64_t valid_mask = 0;
 		for (i = 0; i < property->num_values; i++)
 			valid_mask |= (1ULL << property->values[i]);
 		return !(value & ~valid_mask);
-	} else if (property->flags & DRM_MODE_PROP_BLOB) {
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
 		/* Only the driver knows */
 		return true;
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+		/* a zero value for an object property translates to null: */
+		if (value == 0)
+			return true;
+		return drm_property_get_obj(property, value) != NULL;
 	} else {
 		int i;
 		for (i = 0; i < property->num_values; i++)
@@ -3420,9 +3452,9 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 	return ret;
 }
 
-static int drm_mode_set_obj_prop(struct drm_device *dev,
-		struct drm_mode_object *obj, void *state,
-		struct drm_property *property, uint64_t value, void *blob_data)
+static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
+		void *state, struct drm_property *property, 
+		uint64_t value, void *blob_data)
 {
 	if (drm_property_change_is_valid(property, value)) {
 		switch (obj->type) {
@@ -3436,6 +3468,8 @@ static int drm_mode_set_obj_prop(struct drm_device *dev,
 			return drm_mode_plane_set_obj_prop(obj_to_plane(obj),
 					state, property, value, blob_data);
 		}
+	} else {
+		DRM_DEBUG("invalid value: %s = %llx\n", property->name, value);
 	}
 
 	return -EINVAL;
@@ -3470,7 +3504,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, void *state,
 		return -ENOENT;
 	property = obj_to_property(prop_obj);
 
-	return drm_mode_set_obj_prop(dev, arg_obj, state, property,
+	return drm_mode_set_obj_prop(arg_obj, state, property, 
 			value, blob_data);
 }
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index d4d6a7f..c05d5ba 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -306,6 +306,7 @@ struct drm_property {
 	char name[DRM_PROP_NAME_LEN];
 	uint32_t num_values;
 	uint64_t *values;
+	struct drm_device *dev;
 
 	struct list_head enum_blob_list;
 };
@@ -1072,6 +1073,23 @@ extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
 extern void drm_mode_connector_list_update(struct drm_connector *connector);
 extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 						struct edid *edid);
+
+static inline bool drm_property_type_is(struct drm_property *property,
+		uint32_t type)
+{
+	/* instanceof for props.. handles extended type vs original types: */
+	if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+		return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
+	return property->flags & type;
+}
+
+static inline bool drm_property_type_valid(struct drm_property *property)
+{
+	if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+		return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+	return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
+
 extern int drm_object_property_set_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t val);
@@ -1105,6 +1123,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
 					 const char *name,
 					 uint64_t min, uint64_t max);
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+					 int flags, const char *name, uint32_t type);
 extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
 extern int drm_property_add_enum(struct drm_property *property, int index,
 				 uint64_t value, const char *name);
@@ -1123,6 +1143,13 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
 					 int gamma_size);
 extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 		uint32_t id, uint32_t type);
+
+static inline struct drm_mode_object *
+drm_property_get_obj(struct drm_property *property, uint64_t value)
+{
+	return drm_mode_object_find(property->dev, value, property->values[0]);
+}
+
 /* IOCTLs */
 extern int drm_mode_getresources(struct drm_device *dev,
 				 void *data, struct drm_file *file_priv);
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 12e2139..516425d 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -251,6 +251,20 @@ struct drm_mode_get_connector {
 #define DRM_MODE_PROP_BLOB	(1<<4)
 #define DRM_MODE_PROP_BITMASK	(1<<5) /* bitmask of enumerated types */
 
+/* non-extended types: legacy bitmask, one bit per type: */
+#define DRM_MODE_PROP_LEGACY_TYPE  ( \
+		DRM_MODE_PROP_RANGE | \
+		DRM_MODE_PROP_ENUM | \
+		DRM_MODE_PROP_BLOB | \
+		DRM_MODE_PROP_BITMASK)
+
+/* extended-types: rather than continue to consume a bit per type,
+ * grab a chunk of the bits to use as integer type id.
+ */
+#define DRM_MODE_PROP_EXTENDED_TYPE	0x0000ffc0
+#define DRM_MODE_PROP_TYPE(n)		((n) << 6)
+#define DRM_MODE_PROP_OBJECT		DRM_MODE_PROP_TYPE(1)
+
 struct drm_mode_property_enum {
 	__u64 value;
 	char name[DRM_PROP_NAME_LEN];
-- 
1.8.4.2

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

* [RFCv4 04/14] drm: add signed-range property type
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (2 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 03/14] drm: add object property type Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 05/14] drm: helpers to find mode objects Rob Clark
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

Like range, but values are signed.
---
 drivers/gpu/drm/drm_crtc.c  | 29 +++++++++++++++++++++++++----
 include/drm/drm_crtc.h      | 12 ++++++++++++
 include/uapi/drm/drm_mode.h |  1 +
 3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 8013204..e438e24 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3002,14 +3002,12 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_property_create_bitmask);
 
-struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
-					 const char *name,
+static struct drm_property *property_create_range(struct drm_device *dev,
+					 int flags, const char *name,
 					 uint64_t min, uint64_t max)
 {
 	struct drm_property *property;
 
-	flags |= DRM_MODE_PROP_RANGE;
-
 	property = drm_property_create(dev, flags, name, 2);
 	if (!property)
 		return NULL;
@@ -3019,8 +3017,25 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags
 
 	return property;
 }
+
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+					 const char *name,
+					 uint64_t min, uint64_t max)
+{
+	return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
+			name, min, max);
+}
 EXPORT_SYMBOL(drm_property_create_range);
 
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+					 int flags, const char *name,
+					 int64_t min, int64_t max)
+{
+	return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
+			name, I642U64(min), I642U64(max));
+}
+EXPORT_SYMBOL(drm_property_create_signed_range);
+
 struct drm_property *drm_property_create_object(struct drm_device *dev,
 					 int flags, const char *name, uint32_t type)
 {
@@ -3362,6 +3377,12 @@ static bool drm_property_change_is_valid(struct drm_property *property,
 		if (value < property->values[0] || value > property->values[1])
 			return false;
 		return true;
+	} else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
+		int64_t svalue = U642I64(value);
+		if (svalue < U642I64(property->values[0]) ||
+				svalue > U642I64(property->values[1]))
+			return false;
+		return true;
 	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
 		int i;
 		uint64_t valid_mask = 0;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index c05d5ba..17790a0 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -65,6 +65,15 @@ struct drm_object_properties {
 	uint64_t values[DRM_OBJECT_MAX_PROPERTY];
 };
 
+static inline int64_t U642I64(uint64_t val)
+{
+	return (int64_t)*((int64_t *)&val);
+}
+static inline uint64_t I642U64(int64_t val)
+{
+	return (uint64_t)*((uint64_t *)&val);
+}
+
 /*
  * Note on terminology:  here, for brevity and convenience, we refer to connector
  * control chips as 'CRTCs'.  They can control any type of connector, VGA, LVDS,
@@ -1123,6 +1132,9 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
 					 const char *name,
 					 uint64_t min, uint64_t max);
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+					 int flags, const char *name,
+					 int64_t min, int64_t max);
 struct drm_property *drm_property_create_object(struct drm_device *dev,
 					 int flags, const char *name, uint32_t type);
 extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 516425d..6421edc 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -264,6 +264,7 @@ struct drm_mode_get_connector {
 #define DRM_MODE_PROP_EXTENDED_TYPE	0x0000ffc0
 #define DRM_MODE_PROP_TYPE(n)		((n) << 6)
 #define DRM_MODE_PROP_OBJECT		DRM_MODE_PROP_TYPE(1)
+#define DRM_MODE_PROP_SIGNED_RANGE	DRM_MODE_PROP_TYPE(2)
 
 struct drm_mode_property_enum {
 	__u64 value;
-- 
1.8.4.2

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

* [RFCv4 05/14] drm: helpers to find mode objects
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (3 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 04/14] drm: add signed-range " Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 06/14] drm: split propvals out and blob property support Rob Clark
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

Add a few more useful helpers to find mode objects.
---
 drivers/gpu/drm/drm_crtc.c | 97 ++++++++++++++--------------------------------
 include/drm/drm_crtc.h     | 33 ++++++++++++++++
 2 files changed, 63 insertions(+), 67 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index e438e24..622d001 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1633,7 +1633,6 @@ int drm_mode_getcrtc(struct drm_device *dev,
 {
 	struct drm_mode_crtc *crtc_resp = data;
 	struct drm_crtc *crtc;
-	struct drm_mode_object *obj;
 	int ret = 0;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -1641,13 +1640,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
 
 	drm_modeset_lock_all(dev);
 
-	obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+	if (!crtc) {
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	crtc_resp->x = crtc->x;
 	crtc_resp->y = crtc->y;
@@ -1701,7 +1698,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 			  struct drm_file *file_priv)
 {
 	struct drm_mode_get_connector *out_resp = data;
-	struct drm_mode_object *obj;
 	struct drm_connector *connector;
 	struct drm_display_mode *mode;
 	int mode_count = 0;
@@ -1725,13 +1721,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
 	mutex_lock(&dev->mode_config.mutex);
 
-	obj = drm_mode_object_find(dev, out_resp->connector_id,
-				   DRM_MODE_OBJECT_CONNECTOR);
-	if (!obj) {
+	connector = drm_connector_find(dev, out_resp->connector_id);
+	if (!connector) {
 		ret = -ENOENT;
 		goto out;
 	}
-	connector = obj_to_connector(obj);
 
 	props_count = connector->properties.count;
 
@@ -1833,7 +1827,6 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 {
 	struct drm_mode_get_encoder *enc_resp = data;
-	struct drm_mode_object *obj;
 	struct drm_encoder *encoder;
 	int ret = 0;
 
@@ -1841,13 +1834,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, enc_resp->encoder_id,
-				   DRM_MODE_OBJECT_ENCODER);
-	if (!obj) {
+	encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+	if (!encoder) {
 		ret = -ENOENT;
 		goto out;
 	}
-	encoder = obj_to_encoder(obj);
 
 	if (encoder->crtc)
 		enc_resp->crtc_id = encoder->crtc->base.id;
@@ -1922,7 +1913,6 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 {
 	struct drm_mode_get_plane *plane_resp = data;
-	struct drm_mode_object *obj;
 	struct drm_plane *plane;
 	uint32_t __user *format_ptr;
 	int ret = 0;
@@ -1931,13 +1921,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, plane_resp->plane_id,
-				   DRM_MODE_OBJECT_PLANE);
-	if (!obj) {
+	plane = drm_plane_find(dev, plane_resp->plane_id);
+	if (!plane) {
 		ret = -ENOENT;
 		goto out;
 	}
-	plane = obj_to_plane(obj);
 
 	if (plane->crtc)
 		plane_resp->crtc_id = plane->crtc->base.id;
@@ -1987,7 +1975,6 @@ 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;
 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
@@ -2002,14 +1989,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 	 * 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);
 
 	/* No fb means shut it down */
 	if (!plane_req->fb_id) {
@@ -2022,15 +2007,13 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 		goto out;
 	}
 
-	obj = drm_mode_object_find(dev, plane_req->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, plane_req->crtc_id);
+	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown crtc ID %d\n",
 			      plane_req->crtc_id);
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
 	if (!fb) {
@@ -2206,7 +2189,6 @@ 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_mode_object *obj;
 	struct drm_crtc *crtc;
 	struct drm_connector **connector_set = NULL, *connector;
 	struct drm_framebuffer *fb = NULL;
@@ -2224,14 +2206,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 		return -ERANGE;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, crtc_req->crtc_id,
-				   DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
+	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 	DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
 
 	if (crtc_req->mode_valid) {
@@ -2314,15 +2294,13 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 				goto out;
 			}
 
-			obj = drm_mode_object_find(dev, out_id,
-						   DRM_MODE_OBJECT_CONNECTOR);
-			if (!obj) {
+			connector = drm_connector_find(dev, out_id);
+			if (!connector) {
 				DRM_DEBUG_KMS("Connector id %d unknown\n",
 						out_id);
 				ret = -ENOENT;
 				goto out;
 			}
-			connector = obj_to_connector(obj);
 			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
 					connector->base.id,
 					drm_get_connector_name(connector));
@@ -2354,7 +2332,6 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 				  struct drm_mode_cursor2 *req,
 				  struct drm_file *file_priv)
 {
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	int ret = 0;
 
@@ -2364,12 +2341,11 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
 		return -EINVAL;
 
-	obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, req->crtc_id);
+	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
 		return -ENOENT;
 	}
-	crtc = obj_to_crtc(obj);
 
 	drm_modeset_lock(&crtc->mutex, NULL);
 	if (req->flags & DRM_MODE_CURSOR_BO) {
@@ -3166,7 +3142,6 @@ EXPORT_SYMBOL(drm_object_property_get_value);
 int drm_mode_getproperty_ioctl(struct drm_device *dev,
 			       void *data, struct drm_file *file_priv)
 {
-	struct drm_mode_object *obj;
 	struct drm_mode_get_property *out_resp = data;
 	struct drm_property *property;
 	int enum_count = 0;
@@ -3185,12 +3160,11 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
-	if (!obj) {
+	property = drm_property_find(dev, out_resp->prop_id);
+	if (!property) {
 		ret = -ENOENT;
 		goto done;
 	}
-	property = obj_to_property(obj);
 
 	if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
 			drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
@@ -3306,7 +3280,6 @@ static void drm_property_destroy_blob(struct drm_device *dev,
 int drm_mode_getblob_ioctl(struct drm_device *dev,
 			   void *data, struct drm_file *file_priv)
 {
-	struct drm_mode_object *obj;
 	struct drm_mode_get_blob *out_resp = data;
 	struct drm_property_blob *blob;
 	int ret = 0;
@@ -3316,12 +3289,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
-	if (!obj) {
+	blob = drm_property_blob_find(dev, out_resp->blob_id);
+	if (!blob) {
 		ret = -ENOENT;
 		goto done;
 	}
-	blob = obj_to_blob(obj);
 
 	if (out_resp->length == blob->length) {
 		blob_ptr = (void __user *)(unsigned long)out_resp->data;
@@ -3502,7 +3474,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, void *state,
 		uint32_t prop_id, uint64_t value, void *blob_data)
 {
 	struct drm_mode_object *arg_obj;
-	struct drm_mode_object *prop_obj;
 	struct drm_property *property;
 	int i;
 
@@ -3519,11 +3490,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, void *state,
 	if (i == arg_obj->properties->count)
 		return -EINVAL;
 
-	prop_obj = drm_mode_object_find(dev, prop_id,
-					DRM_MODE_OBJECT_PROPERTY);
-	if (!prop_obj)
+	property = drm_property_find(dev, prop_id);
+	if (!property)
 		return -ENOENT;
-	property = obj_to_property(prop_obj);
 
 	return drm_mode_set_obj_prop(arg_obj, state, property, 
 			value, blob_data);
@@ -3671,7 +3640,6 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_lut *crtc_lut = data;
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	void *r_base, *g_base, *b_base;
 	int size;
@@ -3681,12 +3649,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+	if (!crtc) {
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	if (crtc->funcs->gamma_set == NULL) {
 		ret = -ENOSYS;
@@ -3730,7 +3697,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_lut *crtc_lut = data;
-	struct drm_mode_object *obj;
 	struct drm_crtc *crtc;
 	void *r_base, *g_base, *b_base;
 	int size;
@@ -3740,12 +3706,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
-	obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj) {
+	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+	if (!crtc) {
 		ret = -ENOENT;
 		goto out;
 	}
-	crtc = obj_to_crtc(obj);
 
 	/* memcpy into gamma store */
 	if (crtc_lut->gamma_size != crtc->gamma_size) {
@@ -3780,7 +3745,6 @@ 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_object *obj;
 	struct drm_crtc *crtc;
 	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
 	struct drm_pending_vblank_event *e = NULL;
@@ -3794,10 +3758,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
 		return -EINVAL;
 
-	obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
-	if (!obj)
+	crtc = drm_crtc_find(dev, page_flip->crtc_id);
+	if (!crtc)
 		return -ENOENT;
-	crtc = obj_to_crtc(obj);
 
 	drm_modeset_lock(&crtc->mutex, NULL);
 	if (crtc->fb == NULL) {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 17790a0..3daaef6 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1254,6 +1254,15 @@ extern int drm_format_vert_chroma_subsampling(uint32_t format);
 extern const char *drm_get_format_name(uint32_t format);
 
 /* Helpers */
+
+static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
+		uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
+	return mo ? obj_to_plane(mo) : NULL;
+}
+
 static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
 	uint32_t id)
 {
@@ -1270,4 +1279,28 @@ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
 	return mo ? obj_to_encoder(mo) : NULL;
 }
 
+static inline struct drm_connector *drm_connector_find(struct drm_device *dev,
+		uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
+	return mo ? obj_to_connector(mo) : NULL;
+}
+
+static inline struct drm_property *drm_property_find(struct drm_device *dev,
+		uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
+	return mo ? obj_to_property(mo) : NULL;
+}
+
+static inline struct drm_property_blob *
+drm_property_blob_find(struct drm_device *dev, uint32_t id)
+{
+	struct drm_mode_object *mo;
+	mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+	return mo ? obj_to_blob(mo) : NULL;
+}
+
 #endif /* __DRM_CRTC_H__ */
-- 
1.8.4.2

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

* [RFCv4 06/14] drm: split propvals out and blob property support
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (4 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 05/14] drm: helpers to find mode objects Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 07/14] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 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.
---
 drivers/gpu/drm/drm_crtc.c                  | 79 +++++++++++++++++++++--------
 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(+), 57 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 622d001..68eeb2a 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -173,6 +173,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_mode_object *obj,
+		void *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)				\
@@ -727,6 +735,7 @@ int drm_crtc_init(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, &dev->mode_config.crtc_list);
 	dev->mode_config.num_crtc++;
@@ -818,6 +827,7 @@ int drm_connector_init(struct drm_device *dev,
 		goto out;
 
 	connector->base.properties = &connector->properties;
+	connector->base.propvals = &connector->propvals;
 	connector->dev = dev;
 	connector->funcs = funcs;
 	connector->connector_type = connector_type;
@@ -991,6 +1001,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		goto out;
 
 	plane->base.properties = &plane->properties;
+	plane->base.propvals = &plane->propvals;
 	plane->dev = dev;
 	plane->funcs = funcs;
 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
@@ -1791,7 +1802,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;
@@ -3102,19 +3113,33 @@ 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);
 
 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;
 		}
 	}
@@ -3130,7 +3155,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;
 		}
 	}
@@ -3313,27 +3338,35 @@ 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;
 
-	if (connector->edid_blob_ptr)
-		drm_property_destroy_blob(dev, connector->edid_blob_ptr);
+	/* property_blob will be recreated by drm_object_property_set_value(): */
+	connector->edid_blob_ptr = NULL;
 
 	/* 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;
 }
@@ -3411,7 +3444,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;
 }
 
@@ -3425,7 +3460,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;
 }
@@ -3440,7 +3476,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;
 }
@@ -3540,7 +3577,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 0a19401..1b6b6f4 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -431,7 +431,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 523dfc9..f2fd091 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1654,7 +1654,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 74e5de1..188444a 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -191,7 +191,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 97d76c1..2dcdd01 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 76dadee..f29461a 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 e3a3923..090a6b3 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 5ff83dc..0e50506 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 19e76bd..a06dabb 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -8959,8 +8959,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 c9b3e56..7d9d79a 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3171,7 +3171,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 59b1212..566c71b 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1015,7 +1015,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 9f805df..1a334b0 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2044,7 +2044,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;
 
@@ -2121,7 +2122,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;
 
@@ -2133,7 +2136,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;
 
@@ -2145,7 +2150,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;
 
@@ -2157,7 +2164,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 5fa6839..821bd05 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1292,7 +1292,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);
 }
 
 /**
@@ -1449,7 +1450,8 @@ intel_tv_set_property(struct drm_connector *connector, void *state,
 	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 acef48f..64c60fcb 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 36499e1..7390f7a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -227,10 +227,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 bb07519..1b09696 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -553,12 +553,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 4f3ba93..708e044 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -118,7 +118,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 41d563a..da16774 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -83,7 +83,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 562f9a4..b72ba99 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -738,8 +738,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 3daaef6..ff1ecb5 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
@@ -56,12 +57,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];
 };
 
@@ -533,6 +538,7 @@ struct drm_crtc {
 	void *helper_private;
 
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 };
 
 
@@ -698,6 +704,7 @@ struct drm_connector {
 
 	struct drm_property_blob *edid_blob_ptr;
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 
 	uint8_t polled; /* DRM_CONNECTOR_POLL_* */
 
@@ -773,6 +780,7 @@ struct drm_plane {
 	const struct drm_plane_funcs *funcs;
 
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 };
 
 /**
@@ -1100,8 +1108,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.8.4.2

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

* [RFCv4 07/14] drm: Allow drm_mode_object_find() to look up an object of any type
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (5 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 06/14] drm: split propvals out and blob property support Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 08/14] drm: Refactor object property check code Rob Clark
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

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

To avoid having to pass object types from userspace for atomic mode
setting ioctl, allow drm_mode_object_find() to look up an object of any
type. This will only work as long as the all object types share the ID
space.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/drm_crtc.c | 3 ++-
 include/drm/drm_crtc.h     | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 68eeb2a..5142599 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -445,7 +445,8 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 
 	mutex_lock(&dev->mode_config.idr_mutex);
 	obj = idr_find(&dev->mode_config.crtc_idr, id);
-	if (!obj || (obj->type != type) || (obj->id != id))
+	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
+	    (obj->id != id))
 		obj = NULL;
 	mutex_unlock(&dev->mode_config.idr_mutex);
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index ff1ecb5..40eec19 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -52,6 +52,7 @@ struct drm_object_property_values;
 #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
 #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
 #define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
+#define DRM_MODE_OBJECT_ANY 0
 
 struct drm_mode_object {
 	uint32_t id;
-- 
1.8.4.2

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

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

* [RFCv4 08/14] drm: Refactor object property check code
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (6 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 07/14] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 09/14] drm: convert plane to properties/state Rob Clark
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 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>
---
 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 5142599..8180499 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3506,6 +3506,19 @@ static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
 	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, void *state,
 		uint32_t obj_id, uint32_t obj_type,
@@ -3513,20 +3526,10 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev, void *state,
 {
 	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.8.4.2

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

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

* [RFCv4 09/14] drm: convert plane to properties/state
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (7 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 08/14] drm: Refactor object property check code Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2014-01-28 21:52   ` Sean Paul
                     ` (2 more replies)
  2013-11-25 14:47 ` [RFCv4 10/14] drm: convert crtc " Rob Clark
                   ` (4 subsequent siblings)
  13 siblings, 3 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 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).
---
 drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
 drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
 drivers/gpu/drm/drm_fb_helper.c             |  17 +-
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
 drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
 drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
 drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
 drivers/gpu/drm/tegra/dc.c                  |  16 +-
 include/drm/drm_atomic_helper.h             |  39 ++-
 include/drm/drm_crtc.h                      |  88 +++++-
 19 files changed, 654 insertions(+), 189 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 8b37cf1..14e0571 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -39,10 +39,12 @@
 void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 {
 	struct drm_atomic_helper_state *state;
+	int nplanes = dev->mode_config.num_plane;
 	int sz;
 	void *ptr;
 
 	sz = sizeof(*state);
+	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
 
 	ptr = kzalloc(sz, GFP_KERNEL);
 
@@ -57,6 +59,12 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 	state->dev = dev;
 	state->flags = flags;
 
+	state->planes = ptr;
+	ptr = &state->planes[nplanes];
+
+	state->pstates = ptr;
+	ptr = &state->pstates[nplanes];
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_helper_begin);
@@ -92,7 +100,19 @@ EXPORT_SYMBOL(drm_atomic_helper_set_event);
  */
 int drm_atomic_helper_check(struct drm_device *dev, void *state)
 {
-	return 0;  /* for now */
+	struct drm_atomic_helper_state *a = state;
+	int nplanes = dev->mode_config.num_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		if (a->planes[i]) {
+			ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
+
+	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_check);
 
@@ -168,6 +188,18 @@ fail:
 static void commit_locks(struct drm_atomic_helper_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
+	struct drm_device *dev = a->dev;
+	int nplanes = dev->mode_config.num_plane;
+	int i;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			plane->state->state = NULL;
+			drm_atomic_helper_destroy_plane_state(plane, a->pstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	mutex_lock(&a->mutex);
 	while (!list_empty(&a->locked)) {
@@ -186,7 +218,17 @@ static void commit_locks(struct drm_atomic_helper_state *a,
 static int atomic_commit(struct drm_atomic_helper_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
-	int ret = 0;
+	int nplanes = a->dev->mode_config.num_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);
 
@@ -263,7 +305,117 @@ void _drm_atomic_helper_state_free(struct kref *kref)
 }
 EXPORT_SYMBOL(_drm_atomic_helper_state_free);
 
+int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
+		struct drm_property *property, uint64_t val, void *blob_data)
+{
+	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_helper_plane_set_property);
+
+void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate, void *state)
+{
+	/* snapshot current state: */
+	*pstate = *plane->state;
+	pstate->state = state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_init_plane_state);
+
+void drm_atomic_helper_destroy_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	kfree(state);
+}
+EXPORT_SYMBOL(drm_atomic_helper_destroy_plane_state);
+
+static struct drm_plane_state *
+drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
+{
+	struct drm_atomic_helper_state *a = state;
+	struct drm_plane_state *pstate;
+	int ret;
+
+	/* grab lock of current crtc.. if crtc is NULL then grab all: */
+	if (plane->state->crtc)
+		ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
+	else
+		ret = drm_modeset_lock_all_crtcs(plane->dev, state);
+	if (ret)
+		return ERR_PTR(ret);
+
+	pstate = a->pstates[plane->id];
+
+	if (!pstate) {
+		pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+		if (!pstate)
+			return ERR_PTR(-ENOMEM);
+		drm_atomic_helper_init_plane_state(plane, pstate, state);
+		a->planes[plane->id] = plane;
+		a->pstates[plane->id] = pstate;
+	}
+
+	return pstate;
+}
+
+static void
+swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a)
+{
+	struct drm_plane_state *pstate = a->pstates[plane->id];
+
+	/* clear transient state (only valid during atomic update): */
+	pstate->new_fb = false;
+
+	swap(plane->state, a->pstates[plane->id]);
+	plane->base.propvals = &plane->state->propvals;
+}
+
+static int
+drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	struct drm_framebuffer *old_fb = NULL, *fb = NULL;
+	int ret = 0;
+
+	fb = pstate->fb;
+
+	if (pstate->crtc && fb) {
+		ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
+			pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
+			pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
+		if (!ret) {
+			/* on success, update state and fb refcnting: */
+			/* NOTE: if we ensure no driver sets plane->state->fb = NULL
+			 * on disable, we can move this up a level and not duplicate
+			 * nearly the same thing for both update_plane and disable_plane
+			 * cases..  I leave it like this for now to be paranoid due to
+			 * the slightly different ordering in the two cases in the
+			 * original code.
+			 */
+			old_fb = plane->state->fb;
+			swap_plane_state(plane, pstate->state);
+			fb = NULL;
+		}
+	} else {
+		old_fb = plane->state->fb;
+		plane->funcs->disable_plane(plane);
+		swap_plane_state(plane, pstate->state);
+	}
+
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
+	return ret;
+}
 
 const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = {
+		.get_plane_state    = drm_atomic_helper_get_plane_state,
+		.check_plane_state  = drm_plane_check_state,
+		.commit_plane_state = drm_atomic_helper_commit_plane_state,
 };
 EXPORT_SYMBOL(drm_atomic_helper_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 8180499..bb1a4fe 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -680,7 +680,20 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
+		void *state;
+
+		state = dev->driver->atomic_begin(dev, 0);
+		if (IS_ERR(state)) {
+			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
+			return;
+		}
+
+		/* TODO once CRTC is converted to state/properties, we can push the
+		 * locking down into drm_atomic_helper_commit(), since that is where
+		 * the actual changes take place..
+		 */
 		drm_modeset_lock_all(dev);
+
 		/* remove from any CRTC */
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 			if (crtc->fb == fb) {
@@ -695,9 +708,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 		}
 
 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-			if (plane->fb == fb)
-				drm_plane_force_disable(plane);
+			if (plane->state->fb == fb)
+				drm_plane_force_disable(plane, state);
 		}
+
+		/* just disabling stuff shouldn't fail, hopefully: */
+		if(dev->driver->atomic_check(dev, state))
+			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
+		else
+			dev->driver->atomic_commit(dev, state);
+
+		dev->driver->atomic_end(dev, state);
+
 		drm_modeset_unlock_all(dev);
 	}
 
@@ -993,8 +1015,12 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		   const uint32_t *formats, uint32_t format_count,
 		   bool priv)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	if (!plane->state)
+		plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
+
 	drm_modeset_lock_all(dev);
 
 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
@@ -1002,7 +1028,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		goto out;
 
 	plane->base.properties = &plane->properties;
-	plane->base.propvals = &plane->propvals;
+	plane->base.propvals = &plane->state->propvals;
 	plane->dev = dev;
 	plane->funcs = funcs;
 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
@@ -1024,11 +1050,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 	 */
 	if (!priv) {
 		list_add_tail(&plane->head, &dev->mode_config.plane_list);
+		plane->id = dev->mode_config.num_plane;
 		dev->mode_config.num_plane++;
 	} else {
 		INIT_LIST_HEAD(&plane->head);
 	}
 
+	drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_x, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_y, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_w, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_h, 0);
+
  out:
 	drm_modeset_unlock_all(dev);
 
@@ -1060,6 +1098,121 @@ void drm_plane_cleanup(struct drm_plane *plane)
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
 
+int drm_plane_check_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	unsigned int fb_width, fb_height;
+	struct drm_framebuffer *fb = state->fb;
+	int i;
+
+	/* disabling the plane is allowed: */
+	if (!fb)
+		return 0;
+
+	fb_width = fb->width << 16;
+	fb_height = fb->height << 16;
+
+	/* Check whether this plane supports the fb pixel format. */
+	for (i = 0; i < plane->format_count; i++)
+		if (fb->pixel_format == plane->format_types[i])
+			break;
+	if (i == plane->format_count) {
+		DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
+		return -EINVAL;
+	}
+
+	/* Make sure source coordinates are inside the fb. */
+	if (state->src_w > fb_width ||
+			state->src_x > fb_width - state->src_w ||
+			state->src_h > fb_height ||
+			state->src_y > fb_height - state->src_h) {
+		DRM_DEBUG_KMS("Invalid source coordinates "
+			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+			      state->src_w >> 16,
+			      ((state->src_w & 0xffff) * 15625) >> 10,
+			      state->src_h >> 16,
+			      ((state->src_h & 0xffff) * 15625) >> 10,
+			      state->src_x >> 16,
+			      ((state->src_x & 0xffff) * 15625) >> 10,
+			      state->src_y >> 16,
+			      ((state->src_y & 0xffff) * 15625) >> 10);
+		return -ENOSPC;
+	}
+
+	/* Give drivers some help against integer overflows */
+	if (state->crtc_w > INT_MAX ||
+			state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
+			state->crtc_h > INT_MAX ||
+			state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
+		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+			      state->crtc_w, state->crtc_h,
+			      state->crtc_x, state->crtc_y);
+		return -ERANGE;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_check_state);
+
+void drm_plane_commit_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	plane->state = state;
+	plane->base.propvals = &state->propvals;
+}
+EXPORT_SYMBOL(drm_plane_commit_state);
+
+int drm_plane_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data)
+{
+	struct drm_device *dev = plane->dev;
+	struct drm_mode_config *config = &dev->mode_config;
+
+	drm_object_property_set_value(&plane->base,
+			&state->propvals, property, value, blob_data);
+
+	if (property == config->prop_fb_id) {
+		state->new_fb = true;
+		state->fb = drm_framebuffer_lookup(dev, value);
+	} else if (property == config->prop_crtc_id) {
+		struct drm_mode_object *obj = drm_property_get_obj(property, value);
+		struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
+		/* take the lock of the incoming crtc as well, moving
+		 * plane between crtcs is synchronized on both incoming
+		 * and outgoing crtc.
+		 */
+		if (crtc) {
+			int ret = drm_modeset_lock(&crtc->mutex, state->state);
+			if (ret)
+				return ret;
+		}
+		state->crtc = crtc;
+	} else if (property == config->prop_crtc_x) {
+		state->crtc_x = U642I64(value);
+	} else if (property == config->prop_crtc_y) {
+		state->crtc_y = U642I64(value);
+	} else if (property == config->prop_crtc_w) {
+		state->crtc_w = value;
+	} else if (property == config->prop_crtc_h) {
+		state->crtc_h = value;
+	} else if (property == config->prop_src_x) {
+		state->src_x = value;
+	} else if (property == config->prop_src_y) {
+		state->src_y = value;
+	} else if (property == config->prop_src_w) {
+		state->src_w = value;
+	} else if (property == config->prop_src_h) {
+		state->src_h = value;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_set_property);
+
 /**
  * drm_plane_force_disable - Forcibly disable a plane
  * @plane: plane to disable
@@ -1069,20 +1222,15 @@ EXPORT_SYMBOL(drm_plane_cleanup);
  * Used when the plane's current framebuffer is destroyed,
  * and when restoring fbdev mode.
  */
-void drm_plane_force_disable(struct drm_plane *plane)
+void drm_plane_force_disable(struct drm_plane *plane, void *state)
 {
-	int ret;
+	struct drm_mode_config *config = &plane->dev->mode_config;
 
-	if (!plane->fb)
-		return;
-
-	ret = plane->funcs->disable_plane(plane);
-	if (ret)
-		DRM_ERROR("failed to disable plane with busy fb\n");
-	/* disconnect the plane from the fb and crtc: */
-	__drm_framebuffer_unreference(plane->fb);
-	plane->fb = NULL;
-	plane->crtc = NULL;
+	/* should turn off the crtc */
+	drm_mode_plane_set_obj_prop(plane, state,
+		config->prop_crtc_id, 0, NULL);
+	drm_mode_plane_set_obj_prop(plane, state,
+		config->prop_fb_id, 0, NULL);
 }
 EXPORT_SYMBOL(drm_plane_force_disable);
 
@@ -1132,21 +1280,78 @@ EXPORT_SYMBOL(drm_mode_destroy);
 
 static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
-	struct drm_property *edid;
-	struct drm_property *dpms;
+	struct drm_property *prop;
 
 	/*
 	 * Standard properties (apply to all connectors)
 	 */
-	edid = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
 				   DRM_MODE_PROP_IMMUTABLE,
 				   "EDID", 0);
-	dev->mode_config.edid_property = edid;
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.edid_property = prop;
 
-	dpms = drm_property_create_enum(dev, 0,
+	prop = drm_property_create_enum(dev, 0,
 				   "DPMS", drm_dpms_enum_list,
 				   ARRAY_SIZE(drm_dpms_enum_list));
-	dev->mode_config.dpms_property = dpms;
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.dpms_property = prop;
+
+
+	prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_x = prop;
+
+	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;
 
 	return 0;
 }
@@ -1939,13 +2144,13 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 		goto out;
 	}
 
-	if (plane->crtc)
-		plane_resp->crtc_id = plane->crtc->base.id;
+	if (plane->state->crtc)
+		plane_resp->crtc_id = plane->state->crtc->base.id;
 	else
 		plane_resp->crtc_id = 0;
 
-	if (plane->fb)
-		plane_resp->fb_id = plane->fb->base.id;
+	if (plane->state->fb)
+		plane_resp->fb_id = plane->state->fb->base.id;
 	else
 		plane_resp->fb_id = 0;
 
@@ -1987,20 +2192,19 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 {
 	struct drm_mode_set_plane *plane_req = data;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_plane *plane;
-	struct drm_crtc *crtc;
-	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
+	void *state;
 	int ret = 0;
-	unsigned int fb_width, fb_height;
-	int i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-	/*
-	 * First, find the plane, crtc, and fb objects.  If not available,
-	 * we don't bother to call the driver.
-	 */
+retry:
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
+
 	plane = drm_plane_find(dev, plane_req->plane_id);
 	if (!plane) {
 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
@@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 		return -ENOENT;
 	}
 
-	/* No fb means shut it down */
-	if (!plane_req->fb_id) {
-		drm_modeset_lock_all(dev);
-		old_fb = plane->fb;
-		plane->funcs->disable_plane(plane);
-		plane->crtc = NULL;
-		plane->fb = NULL;
-		drm_modeset_unlock_all(dev);
-		goto out;
-	}
-
-	crtc = drm_crtc_find(dev, plane_req->crtc_id);
-	if (!crtc) {
-		DRM_DEBUG_KMS("Unknown crtc ID %d\n",
-			      plane_req->crtc_id);
-		ret = -ENOENT;
-		goto out;
-	}
-
-	fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
-	if (!fb) {
-		DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
-			      plane_req->fb_id);
-		ret = -ENOENT;
-		goto out;
-	}
-
-	/* Check whether this plane supports the fb pixel format. */
-	for (i = 0; i < plane->format_count; i++)
-		if (fb->pixel_format == plane->format_types[i])
-			break;
-	if (i == plane->format_count) {
-		DRM_DEBUG_KMS("Invalid pixel format %s\n",
-			      drm_get_format_name(fb->pixel_format));
-		ret = -EINVAL;
-		goto out;
-	}
-
-	fb_width = fb->width << 16;
-	fb_height = fb->height << 16;
-
-	/* Make sure source coordinates are inside the fb. */
-	if (plane_req->src_w > fb_width ||
-	    plane_req->src_x > fb_width - plane_req->src_w ||
-	    plane_req->src_h > fb_height ||
-	    plane_req->src_y > fb_height - plane_req->src_h) {
-		DRM_DEBUG_KMS("Invalid source coordinates "
-			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
-			      plane_req->src_w >> 16,
-			      ((plane_req->src_w & 0xffff) * 15625) >> 10,
-			      plane_req->src_h >> 16,
-			      ((plane_req->src_h & 0xffff) * 15625) >> 10,
-			      plane_req->src_x >> 16,
-			      ((plane_req->src_x & 0xffff) * 15625) >> 10,
-			      plane_req->src_y >> 16,
-			      ((plane_req->src_y & 0xffff) * 15625) >> 10);
-		ret = -ENOSPC;
-		goto out;
-	}
-
-	/* Give drivers some help against integer overflows */
-	if (plane_req->crtc_w > INT_MAX ||
-	    plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
-	    plane_req->crtc_h > INT_MAX ||
-	    plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
-		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
-			      plane_req->crtc_w, plane_req->crtc_h,
-			      plane_req->crtc_x, plane_req->crtc_y);
-		ret = -ERANGE;
+	ret =
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_id, plane_req->crtc_id, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_fb_id, plane_req->fb_id, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_w, plane_req->crtc_w, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_crtc_h, plane_req->crtc_h, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_w, plane_req->src_w, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_h, plane_req->src_h, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_x, plane_req->src_x, NULL) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+			config->prop_src_y, plane_req->src_y, NULL) ||
+		dev->driver->atomic_check(dev, state);
+	if (ret)
 		goto out;
-	}
 
-	drm_modeset_lock_all(dev);
-	ret = plane->funcs->update_plane(plane, crtc, fb,
-					 plane_req->crtc_x, plane_req->crtc_y,
-					 plane_req->crtc_w, plane_req->crtc_h,
-					 plane_req->src_x, plane_req->src_y,
-					 plane_req->src_w, plane_req->src_h);
-	if (!ret) {
-		old_fb = plane->fb;
-		plane->crtc = crtc;
-		plane->fb = fb;
-		fb = NULL;
-	}
-	drm_modeset_unlock_all(dev);
+	ret = dev->driver->atomic_commit(dev, state);
 
 out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
-	if (old_fb)
-		drm_framebuffer_unreference(old_fb);
-
+	dev->driver->atomic_end(dev, state);
+	if (ret == -EDEADLK)
+		goto retry;
 	return ret;
 }
 
@@ -3427,7 +3570,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
 }
 
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 					   void *state, struct drm_property *property,
 					   uint64_t value, void *blob_data)
 {
@@ -3450,8 +3593,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
 
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 				      void *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -3466,8 +3610,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
 
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 				      void *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -3476,12 +3621,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 	if (plane->funcs->set_property)
 		ret = plane->funcs->set_property(plane, state, property,
 				value, blob_data);
-	if (!ret)
-		drm_object_property_set_value(&plane->base, &plane->propvals,
-				property, value, NULL);
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
 
 static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
 		void *state, struct drm_property *property, 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 1b6b6f4..5773468 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -286,12 +286,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_plane *plane;
 	bool error = false;
+	void *state;
 	int i;
 
 	drm_warn_on_modeset_not_all_locked(dev);
 
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state)) {
+		DRM_ERROR("failed to restore fbdev mode\n");
+		return true;
+	}
+
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head)
-		drm_plane_force_disable(plane);
+		drm_plane_force_disable(plane, state);
+
+	/* just disabling stuff shouldn't fail, hopefully: */
+	if(dev->driver->atomic_check(dev, state))
+		DRM_ERROR("failed to restore fbdev mode\n");
+	else
+		dev->driver->atomic_commit(dev, state);
+
+	dev->driver->atomic_end(dev, state);
 
 	for (i = 0; i < fb_helper->crtc_count; i++) {
 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 82a9fca..4ae55b8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -124,8 +124,8 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	if (ret)
 		return ret;
 
-	plane->crtc = crtc;
-	plane->fb = crtc->fb;
+	plane->state->crtc = crtc;
+	plane->state->fb = crtc->fb;
 
 	exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe);
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 06f1b2a..dbe2e19 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -127,7 +127,7 @@ static void disable_plane_to_crtc(struct drm_device *dev,
 	 * (encoder->crtc)
 	 */
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		if (plane->crtc == old_crtc) {
+		if (plane->state->crtc == old_crtc) {
 			/*
 			 * do not change below call order.
 			 *
@@ -138,7 +138,7 @@ static void disable_plane_to_crtc(struct drm_device *dev,
 			 * have new_crtc because new_crtc was set to
 			 * encoder->crtc in advance.
 			 */
-			plane->crtc = new_crtc;
+			plane->state->crtc = new_crtc;
 			plane->funcs->disable_plane(plane);
 		}
 	}
@@ -247,7 +247,7 @@ static void exynos_drm_encoder_disable(struct drm_encoder *encoder)
 
 	/* all planes connected to this encoder should be also disabled. */
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		if (plane->crtc == encoder->crtc)
+		if (plane->state->crtc == encoder->crtc)
 			plane->funcs->disable_plane(plane);
 	}
 }
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 2e31fb8..d585a4c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -10,6 +10,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 
 #include <drm/exynos_drm.h>
 #include "exynos_drm_drv.h"
@@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
 	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 	struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
 
-	exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+	exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
 			exynos_drm_encoder_plane_commit);
 }
 
@@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
 		if (exynos_plane->enabled)
 			return;
 
-		exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+		exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
 				exynos_drm_encoder_plane_enable);
 
 		exynos_plane->enabled = true;
@@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
 		if (!exynos_plane->enabled)
 			return;
 
-		exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
+		exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
 				exynos_drm_encoder_plane_disable);
 
 		exynos_plane->enabled = false;
@@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	if (ret < 0)
 		return ret;
 
-	plane->crtc = crtc;
+	plane->state->crtc = crtc;
 
 	exynos_plane_commit(plane);
 	exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
@@ -225,13 +226,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 b9fabf8..0d1d34a 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -897,20 +897,21 @@ static int
 intel_disable_plane(struct drm_plane *plane)
 {
 	struct drm_device *dev = plane->dev;
+	struct drm_plane_state *state = plane->state;
 	struct intel_plane *intel_plane = to_intel_plane(plane);
 	struct intel_crtc *intel_crtc;
 
-	if (!plane->fb)
+	if (!state->fb)
 		return 0;
 
-	if (WARN_ON(!plane->crtc))
+	if (WARN_ON(!state->crtc))
 		return -EINVAL;
 
-	intel_crtc = to_intel_crtc(plane->crtc);
+	intel_crtc = to_intel_crtc(state->crtc);
 
 	if (intel_crtc->active) {
-		intel_enable_primary(plane->crtc);
-		intel_plane->disable_plane(plane, plane->crtc);
+		intel_enable_primary(state->crtc);
+		intel_plane->disable_plane(plane, state->crtc);
 	}
 
 	if (intel_plane->obj) {
@@ -1000,11 +1001,12 @@ out_unlock:
 void intel_plane_restore(struct drm_plane *plane)
 {
 	struct intel_plane *intel_plane = to_intel_plane(plane);
+	struct drm_plane_state *state = plane->state;
 
-	if (!plane->crtc || !plane->fb)
+	if (!state->crtc || !state->fb)
 		return;
 
-	intel_update_plane(plane, plane->crtc, plane->fb,
+	intel_update_plane(plane, state->crtc, state->fb,
 			   intel_plane->crtc_x, intel_plane->crtc_y,
 			   intel_plane->crtc_w, intel_plane->crtc_h,
 			   intel_plane->src_x, intel_plane->src_y,
@@ -1013,7 +1015,9 @@ void intel_plane_restore(struct drm_plane *plane)
 
 void intel_plane_disable(struct drm_plane *plane)
 {
-	if (!plane->crtc || !plane->fb)
+	struct drm_plane_state *state = plane->state;
+
+	if (!state->crtc || !state->fb)
 		return;
 
 	intel_disable_plane(plane);
@@ -1023,6 +1027,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
 	.update_plane = intel_update_plane,
 	.disable_plane = intel_disable_plane,
 	.destroy = intel_destroy_plane,
+	.set_property = drm_atomic_helper_plane_set_property,
 };
 
 static uint32_t ilk_plane_formats[] = {
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
index 80af12a..37a3fd2 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -263,7 +263,7 @@ static void blend_setup(struct drm_crtc *crtc)
 			int idx = idxs[pipe_id];
 			if (idx > 0) {
 				const struct mdp4_format *format =
-					to_mdp4_format(msm_framebuffer_format(plane->fb));
+					to_mdp4_format(msm_framebuffer_format(plane->state->fb));
 				alpha[idx-1] = format->alpha_enable;
 			}
 			mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]);
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
index 880e96d..037ac83 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c
@@ -45,11 +45,14 @@ static int mdp4_plane_update(struct drm_plane *plane,
 		uint32_t src_w, uint32_t src_h)
 {
 	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
+	struct drm_plane_state *state = plane->state;
 
 	mdp4_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
+	if (state->fb) {
+		drm_framebuffer_unreference(state->fb);
+		state->fb = NULL;
+	}
 
 	drm_framebuffer_reference(fb);
 
@@ -62,8 +65,8 @@ static int mdp4_plane_disable(struct drm_plane *plane)
 {
 	struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
 	DBG("%s: disable", mdp4_plane->name);
-	if (plane->crtc)
-		mdp4_crtc_detach(plane->crtc, plane);
+	if (plane->state->crtc)
+		mdp4_crtc_detach(plane->state->crtc, plane);
 	return 0;
 }
 
@@ -87,8 +90,10 @@ void mdp4_plane_install_properties(struct drm_plane *plane,
 int mdp4_plane_set_property(struct drm_plane *plane, void *state,
 		struct drm_property *property, uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	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 = {
@@ -117,7 +122,7 @@ void mdp4_plane_set_scanout(struct drm_plane *plane,
 	msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova);
 	mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova);
 
-	plane->fb = fb;
+	plane->state->fb = fb;
 }
 
 #define MDP4_VG_PHASE_STEP_DEFAULT	0x20000000
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index fc938f7..a6a4375 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_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
 
@@ -213,6 +214,10 @@ nv10_set_property(struct drm_plane *plane, void *state,
 		  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;
@@ -227,7 +232,8 @@ nv10_set_property(struct drm_plane *plane, void *state,
 	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);
 
 	nv10_set_params(nv_plane);
 	return 0;
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 357342d..524a81a 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -210,7 +210,7 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
 		/* and any attached overlay planes: */
 		for (i = 0; i < priv->num_planes; i++) {
 			struct drm_plane *plane = priv->planes[i];
-			if (plane->crtc == crtc)
+			if (plane->state->crtc == crtc)
 				WARN_ON(omap_plane_dpms(plane, mode));
 		}
 	}
@@ -651,7 +651,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 
 	omap_crtc->channel = channel;
 	omap_crtc->plane = plane;
-	omap_crtc->plane->crtc = crtc;
+	omap_crtc->plane->state->crtc = crtc;
 	omap_crtc->name = channel_names[channel];
 	omap_crtc->pipe = id;
 
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 1b09696..e1e794a 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -559,7 +559,7 @@ static void dev_lastclose(struct drm_device *dev)
 
 		for (i = 0; i < priv->num_planes; i++) {
 			drm_object_property_set_value(&priv->planes[i]->base,
-					&priv->planes[i]->propvals,
+					&priv->planes[i]->state->propvals,
 					priv->rotation_prop, 0, NULL);
 		}
 	}
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index fe32f1b..b85bd61 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -116,9 +116,10 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 			container_of(apply, struct omap_plane, apply);
 	struct omap_drm_window *win = &omap_plane->win;
 	struct drm_plane *plane = &omap_plane->base;
+	struct drm_plane_state *state = plane->state;
 	struct drm_device *dev = plane->dev;
 	struct omap_overlay_info *info = &omap_plane->info;
-	struct drm_crtc *crtc = plane->crtc;
+	struct drm_crtc *crtc = state->crtc;
 	enum omap_channel channel;
 	bool enabled = omap_plane->enabled && crtc;
 	bool ilace, replication;
@@ -127,7 +128,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 	DBG("%s, enabled=%d", omap_plane->name, enabled);
 
 	/* if fb has changed, pin new fb: */
-	update_pin(plane, enabled ? plane->fb : NULL);
+	update_pin(plane, enabled ? state->fb : NULL);
 
 	if (!enabled) {
 		dispc_ovl_enable(omap_plane->id, false);
@@ -137,7 +138,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 	channel = omap_crtc_channel(crtc);
 
 	/* update scanout: */
-	omap_framebuffer_update_scanout(plane->fb, win, info);
+	omap_framebuffer_update_scanout(state->fb, win, info);
 
 	DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
 			info->out_width, info->out_height,
@@ -179,16 +180,18 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply)
 		cb.fxn(cb.arg);
 
 	if (omap_plane->enabled) {
-		omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
+		omap_framebuffer_flush(plane->state->fb,
+				info->pos_x, info->pos_y,
 				info->out_width, info->out_height);
 	}
 }
 
 static int apply(struct drm_plane *plane)
 {
-	if (plane->crtc) {
+	struct drm_plane_state *state = plane->state;
+	if (state->crtc) {
 		struct omap_plane *omap_plane = to_omap_plane(plane);
-		return omap_crtc_apply(plane->crtc, &omap_plane->apply);
+		return omap_crtc_apply(state->crtc, &omap_plane->apply);
 	}
 	return 0;
 }
@@ -203,6 +206,7 @@ int omap_plane_mode_set(struct drm_plane *plane,
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_window *win = &omap_plane->win;
+	struct drm_plane_state *state = plane->state;
 
 	win->crtc_x = crtc_x;
 	win->crtc_y = crtc_y;
@@ -225,8 +229,8 @@ int omap_plane_mode_set(struct drm_plane *plane,
 		omap_plane->apply_done_cb.arg = arg;
 	}
 
-	plane->fb = fb;
-	plane->crtc = crtc;
+	state->fb = fb;
+	state->crtc = crtc;
 
 	return apply(plane);
 }
@@ -239,10 +243,12 @@ static int omap_plane_update(struct drm_plane *plane,
 		uint32_t src_w, uint32_t src_h)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
+	struct drm_plane_state *state = plane->state;
+
 	omap_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
+	if (state->fb)
+		drm_framebuffer_unreference(state->fb);
 
 	drm_framebuffer_reference(fb);
 
@@ -332,8 +338,12 @@ int omap_plane_set_property(struct drm_plane *plane, void *state,
 {
 	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;
@@ -342,6 +352,9 @@ int omap_plane_set_property(struct drm_plane *plane, void *state,
 		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->info.zorder = val;
 		ret = apply(plane);
+	} else {
+		ret = drm_plane_set_property(plane, 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 5691743..4121a58 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -14,6 +14,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -403,6 +404,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);
@@ -411,7 +416,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_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index b72ba99..9e86b99 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -238,7 +238,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc)
 
 	/* Setup planes. */
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
-		if (plane->crtc == crtc)
+		if (plane->state->crtc == crtc)
 			shmob_drm_plane_setup(plane);
 	}
 
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
index 060ae03..22da5c1 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
@@ -14,6 +14,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -166,10 +167,10 @@ void shmob_drm_plane_setup(struct drm_plane *plane)
 {
 	struct shmob_drm_plane *splane = to_shmob_plane(plane);
 
-	if (plane->fb == NULL)
+	if (plane->state->fb == NULL)
 		return;
 
-	__shmob_drm_plane_setup(splane, plane->fb);
+	__shmob_drm_plane_setup(splane, plane->state->fb);
 }
 
 static int
@@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane)
 static const struct drm_plane_funcs shmob_drm_plane_funcs = {
 	.update_plane = shmob_drm_plane_update,
 	.disable_plane = shmob_drm_plane_disable,
+	.set_property = drm_atomic_helper_plane_set_property,
 	.destroy = shmob_drm_plane_destroy,
 };
 
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index ae1cb31..1794c6e 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -73,11 +73,12 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
 
 static int tegra_plane_disable(struct drm_plane *plane)
 {
-	struct tegra_dc *dc = to_tegra_dc(plane->crtc);
+	struct drm_plane_state *state = plane->state;
+	struct tegra_dc *dc = to_tegra_dc(state->crtc);
 	struct tegra_plane *p = to_tegra_plane(plane);
 	unsigned long value;
 
-	if (!plane->crtc)
+	if (!state->crtc)
 		return 0;
 
 	value = WINDOW_A_SELECT << p->index;
@@ -309,13 +310,14 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
 	struct drm_plane *plane;
 
 	list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
-		if (plane->crtc == crtc) {
+		struct drm_plane_state *state = plane->state;
+		if (state->crtc == crtc) {
 			tegra_plane_disable(plane);
-			plane->crtc = NULL;
+			state->crtc = NULL;
 
-			if (plane->fb) {
-				drm_framebuffer_unreference(plane->fb);
-				plane->fb = NULL;
+			if (state->fb) {
+				drm_framebuffer_unreference(state->fb);
+				state->fb = NULL;
 			}
 		}
 	}
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index d77f00e..4ca8360 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -62,7 +62,9 @@
  * struct drm_atomic_helper_funcs - helper funcs used by the atomic helpers
  */
 struct drm_atomic_helper_funcs {
-	int dummy; /* for now */
+	struct drm_plane_state *(*get_plane_state)(struct drm_plane *plane, void *state);
+	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
+	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
 };
 
 const extern struct drm_atomic_helper_funcs drm_atomic_helper_funcs;
@@ -76,6 +78,39 @@ int drm_atomic_helper_commit(struct drm_device *dev, void *state);
 int drm_atomic_helper_commit_unlocked(struct drm_device *dev, void *state);
 void drm_atomic_helper_end(struct drm_device *dev, void *state);
 
+int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state,
+		struct drm_property *property, uint64_t val, void *blob_data);
+void drm_atomic_helper_init_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate, void *state);
+void drm_atomic_helper_destroy_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+
+static inline struct drm_plane_state *
+drm_atomic_get_plane_state(struct drm_plane *plane, void *state)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			plane->dev->driver->atomic_helpers;
+	return funcs->get_plane_state(plane, state);
+}
+
+static inline int
+drm_atomic_check_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			plane->dev->driver->atomic_helpers;
+	return funcs->check_plane_state(plane, pstate);
+}
+
+static inline int
+drm_atomic_commit_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			plane->dev->driver->atomic_helpers;
+	return funcs->commit_plane_state(plane, pstate);
+}
+
 /**
  * struct drm_atomic_helper_state - the state object used by atomic helpers
  */
@@ -83,6 +118,8 @@ struct drm_atomic_helper_state {
 	struct kref refcount;
 	struct drm_device *dev;
 	uint32_t flags;
+	struct drm_plane **planes;
+	struct drm_plane_state **pstates;
 
 	bool committed;
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 40eec19..9a4c16e 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -753,6 +753,45 @@ struct drm_plane_funcs {
 };
 
 /**
+ * drm_plane_state - mutable plane state
+ * @new_fb: has the fb been changed
+ * @crtc: currently bound CRTC
+ * @fb: currently bound fb
+ * @crtc_x: left position of visible portion of plane on crtc
+ * @crtc_y: upper position of visible portion of plane on crtc
+ * @crtc_w: width of visible portion of plane on crtc
+ * @crtc_h: height of visible portion of plane on crtc
+ * @src_x: left position of visible portion of plane within
+ *   plane (in 16.16)
+ * @src_y: upper position of visible portion of plane within
+ *   plane (in 16.16)
+ * @src_w: width of visible portion of plane (in 16.16)
+ * @src_h: height of visible portion of plane (in 16.16)
+ * @propvals: property values
+ * @state: current global/toplevel state object (for atomic) while an
+ *    update is in progress, NULL otherwise.
+ */
+struct drm_plane_state {
+	bool new_fb            : 1;
+	struct drm_crtc *crtc;
+	struct drm_framebuffer *fb;
+
+	/* Signed dest location allows it to be partially off screen */
+	int32_t crtc_x, crtc_y;
+	uint32_t crtc_w, crtc_h;
+
+	/* Source values are 16.16 fixed point */
+	uint32_t src_x, src_y;
+	uint32_t src_h, src_w;
+
+	bool enabled;
+
+	struct drm_object_property_values propvals;
+
+	void *state;
+};
+
+/**
  * drm_plane - central DRM plane control structure
  * @dev: DRM device this plane belongs to
  * @head: for list management
@@ -760,8 +799,8 @@ struct drm_plane_funcs {
  * @possible_crtcs: pipes this plane can be bound to
  * @format_types: array of formats supported by this plane
  * @format_count: number of formats supported
- * @crtc: currently bound CRTC
- * @fb: currently bound fb
+ * @id: plane number, 0..n
+ * @state: the mutable state
  * @funcs: helper functions
  * @properties: property tracking for this plane
  */
@@ -775,13 +814,17 @@ struct drm_plane {
 	uint32_t *format_types;
 	uint32_t format_count;
 
-	struct drm_crtc *crtc;
-	struct drm_framebuffer *fb;
+	int id;
+
+	/*
+	 * State that can be updated from userspace, and atomically
+	 * commited or rolled back:
+	 */
+	struct drm_plane_state *state;
 
 	const struct drm_plane_funcs *funcs;
 
 	struct drm_object_properties properties;
-	struct drm_object_property_values propvals;
 };
 
 /**
@@ -962,8 +1005,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;
 
@@ -1043,7 +1098,15 @@ extern int drm_plane_init(struct drm_device *dev,
 			  const uint32_t *formats, uint32_t format_count,
 			  bool priv);
 extern void drm_plane_cleanup(struct drm_plane *plane);
-extern void drm_plane_force_disable(struct drm_plane *plane);
+extern void drm_plane_force_disable(struct drm_plane *plane, void *state);
+extern int drm_plane_check_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+extern void drm_plane_commit_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+extern int drm_plane_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data);
 
 extern void drm_encoder_cleanup(struct drm_encoder *encoder);
 
@@ -1115,6 +1178,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj,
 extern int drm_object_property_get_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t *value);
+
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+					   void *state, struct drm_property *property,
+					   uint64_t value, void *blob_data);
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+				      void *state, struct drm_property *property,
+				      uint64_t value, void *blob_data);
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+				      void *state, struct drm_property *property,
+				      uint64_t value, void *blob_data);
+
 extern int drm_framebuffer_init(struct drm_device *dev,
 				struct drm_framebuffer *fb,
 				const struct drm_framebuffer_funcs *funcs);
-- 
1.8.4.2

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

* [RFCv4 10/14] drm: convert crtc to properties/state
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (8 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 09/14] drm: convert plane to properties/state Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-12-11 21:48   ` Matt Roper
  2014-03-04 21:29   ` Sean Paul
  2013-11-25 14:47 ` [RFCv4 11/14] drm: push locking down into restore_fbdev_mode Rob Clark
                   ` (3 subsequent siblings)
  13 siblings, 2 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 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.
---
 drivers/gpu/drm/ast/ast_mode.c             |   1 +
 drivers/gpu/drm/cirrus/cirrus_mode.c       |   1 +
 drivers/gpu/drm/drm_atomic_helper.c        | 277 ++++++++++++-
 drivers/gpu/drm/drm_crtc.c                 | 622 ++++++++++++++++++-----------
 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       |   1 +
 drivers/gpu/drm/mgag200/mgag200_mode.c     |   1 +
 drivers/gpu/drm/msm/mdp4/mdp4_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_helper.h            |  39 ++
 include/drm/drm_crtc.h                     |  82 +++-
 24 files changed, 818 insertions(+), 250 deletions(-)

diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 7fc9f72..13f6943 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -619,6 +619,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_helper_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 adabc3d..9e0b713 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -363,6 +363,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_helper_crtc_set_property,
 	.destroy = cirrus_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 14e0571..9b60536 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -40,11 +40,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 {
 	struct drm_atomic_helper_state *state;
 	int nplanes = dev->mode_config.num_plane;
+	int 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);
 
@@ -65,6 +67,12 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 	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_helper_begin);
@@ -83,7 +91,18 @@ int drm_atomic_helper_set_event(struct drm_device *dev,
 		void *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_helper_set_event);
 
@@ -102,6 +121,7 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state)
 {
 	struct drm_atomic_helper_state *a = state;
 	int nplanes = dev->mode_config.num_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
 	int i, ret = 0;
 
 	for (i = 0; i < nplanes; i++) {
@@ -112,6 +132,14 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state)
 		}
 	}
 
+	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;
+		}
+	}
+
 	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_check);
@@ -190,6 +218,7 @@ static void commit_locks(struct drm_atomic_helper_state *a,
 {
 	struct drm_device *dev = a->dev;
 	int nplanes = dev->mode_config.num_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
 	int i;
 
 	for (i = 0; i < nplanes; i++) {
@@ -200,6 +229,14 @@ static void commit_locks(struct drm_atomic_helper_state *a,
 		}
 	}
 
+	for (i = 0; i < ncrtcs; i++) {
+		struct drm_crtc *crtc = a->crtcs[i];
+		if (crtc) {
+			crtc->state->state = NULL;
+			drm_atomic_helper_destroy_crtc_state(crtc, a->cstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	mutex_lock(&a->mutex);
 	while (!list_empty(&a->locked)) {
@@ -219,6 +256,7 @@ static int atomic_commit(struct drm_atomic_helper_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
 	int nplanes = a->dev->mode_config.num_plane;
+	int ncrtcs = a->dev->mode_config.num_crtc;
 	int i, ret = 0;
 
 	for (i = 0; i < nplanes; i++) {
@@ -230,6 +268,15 @@ static int atomic_commit(struct drm_atomic_helper_state *a,
 		}
 	}
 
+	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;
+		}
+	}
+
 	commit_locks(a, ww_ctx);
 
 	return ret;
@@ -404,7 +451,231 @@ drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
 		swap_plane_state(plane, pstate->state);
 	}
 
+	if (fb)
+		drm_framebuffer_unreference(fb);
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
+	return ret;
+}
+
+int drm_atomic_helper_crtc_set_property(struct drm_crtc *crtc, void *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_helper_crtc_set_property);
+
+void drm_atomic_helper_init_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate, void *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;
+}
+EXPORT_SYMBOL(drm_atomic_helper_init_crtc_state);
+
+void drm_atomic_helper_destroy_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
+{
+	kfree(state->connector_ids);
+	kfree(state);
+}
+EXPORT_SYMBOL(drm_atomic_helper_destroy_crtc_state);
+
+static struct drm_crtc_state *
+drm_atomic_helper_get_crtc_state(struct drm_crtc *crtc, void *state)
+{
+	struct drm_atomic_helper_state *a = state;
+	struct drm_crtc_state *cstate;
+	int ret;
+
+	ret = drm_modeset_lock(&crtc->mutex, state);
+	if (ret)
+		return ERR_PTR(ret);
+
+	cstate = a->cstates[crtc->id];
+
+	if (!cstate) {
+		cstate = kmalloc(sizeof(*cstate), GFP_KERNEL);
+		if (!cstate)
+			return ERR_PTR(-ENOMEM);
+		drm_atomic_helper_init_crtc_state(crtc, cstate, state);
+		a->crtcs[crtc->id] = crtc;
+		a->cstates[crtc->id] = cstate;
+	}
+	return cstate;
+}
+
+static void
+swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_helper_state *a)
+{
+	struct drm_crtc_state *cstate = a->cstates[crtc->id];
+	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->id, event);
+		cstate->event = NULL;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
+
+	/* clear transient state (only valid during atomic update): */
+	cstate->set_config = false;
+	cstate->new_fb = false;
+	cstate->connectors_change = false;
+
+	swap(crtc->state, a->cstates[crtc->id]);
+	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 struct drm_display_mode *get_mode(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
+{
+	struct drm_display_mode *mode = NULL;
+	if (cstate->mode_valid) {
+		struct drm_device *dev = crtc->dev;
+		int ret;
+
+		mode = drm_mode_create(dev);
+		if (!mode)
+			return ERR_PTR(-ENOMEM);
+
+		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;
+}
+
+static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_framebuffer *fb = cstate->fb;
+	struct drm_connector **connector_set = get_connector_set(crtc->dev,
+			cstate->connector_ids, cstate->num_connector_ids);
+	struct drm_display_mode *mode = get_mode(crtc, cstate);
+	struct drm_mode_set set = {
+			.crtc = crtc,
+			.x = cstate->x,
+			.y = cstate->y,
+			.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;
+	}
 
+	ret = drm_mode_set_config_internal(&set);
+	if (!ret)
+		swap_crtc_state(crtc, cstate->state);
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+
+	kfree(connector_set);
+	if (mode)
+		drm_mode_destroy(dev, mode);
+	return ret;
+}
+
+static int
+drm_atomic_helper_commit_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	struct drm_framebuffer *old_fb = NULL, *fb = NULL;
+	struct drm_atomic_helper_state *a = cstate->state;
+	int ret = -EINVAL;
+
+	if (cstate->set_config)
+		return set_config(crtc, cstate);
+
+	if (cstate->fb) {
+		/* pageflip */
+
+		if (crtc->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;
+
+		old_fb = crtc->fb;
+		fb = cstate->fb;
+
+		ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags);
+		if (ret) {
+			/* Keep the old fb, don't unref it. */
+			old_fb = NULL;
+		} else {
+			cstate->event = NULL;
+			swap_crtc_state(crtc, cstate->state);
+			/* Unref only the old framebuffer. */
+			fb = NULL;
+		}
+	} else {
+		/* disable */
+		struct drm_mode_set set = {
+				.crtc = crtc,
+				.fb = NULL,
+		};
+
+		old_fb = crtc->state->fb;
+		ret = drm_mode_set_config_internal(&set);
+		if (!ret) {
+			swap_crtc_state(crtc, cstate->state);
+		}
+	}
+
+out:
 	if (fb)
 		drm_framebuffer_unreference(fb);
 	if (old_fb)
@@ -417,5 +688,9 @@ const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = {
 		.get_plane_state    = drm_atomic_helper_get_plane_state,
 		.check_plane_state  = drm_plane_check_state,
 		.commit_plane_state = drm_atomic_helper_commit_plane_state,
+
+		.get_crtc_state     = drm_atomic_helper_get_crtc_state,
+		.check_crtc_state   = drm_crtc_check_state,
+		.commit_crtc_state  = drm_atomic_helper_commit_crtc_state,
 };
 EXPORT_SYMBOL(drm_atomic_helper_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index bb1a4fe..008b5bb 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -659,8 +659,6 @@ 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));
 
@@ -680,6 +678,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
+		struct drm_mode_config *config = &fb->dev->mode_config;
 		void *state;
 
 		state = dev->driver->atomic_begin(dev, 0);
@@ -688,22 +687,12 @@ 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_helper_commit(), since that is where
-		 * the actual changes take place..
-		 */
-		drm_modeset_lock_all(dev);
-
 		/* remove from any CRTC */
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 			if (crtc->fb == fb) {
 				/* 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);
+				drm_mode_crtc_set_obj_prop(crtc, state,
+					config->prop_fb_id, 0, NULL);
 			}
 		}
 
@@ -719,8 +708,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 			dev->driver->atomic_commit(dev, state);
 
 		dev->driver->atomic_end(dev, state);
-
-		drm_modeset_unlock_all(dev);
 	}
 
 	drm_framebuffer_unreference(fb);
@@ -743,11 +730,15 @@ DEFINE_WW_CLASS(crtc_ww_class);
 int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
 		   const struct drm_crtc_funcs *funcs)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	if (!crtc->state)
+		crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
+
 	crtc->dev = dev;
 	crtc->funcs = funcs;
-	crtc->invert_dimensions = false;
+	crtc->state->invert_dimensions = false;
 
 	drm_modeset_lock_all(dev);
 	drm_modeset_lock_init(&crtc->mutex);
@@ -758,11 +749,17 @@ int drm_crtc_init(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, &dev->mode_config.crtc_list);
 	dev->mode_config.num_crtc++;
 
+	drm_object_attach_property(&crtc->base, config->prop_mode, 0);
+	drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
+	drm_object_attach_property(&crtc->base, config->prop_fb_id, 0);
+	drm_object_attach_property(&crtc->base, config->prop_src_x, 0);
+	drm_object_attach_property(&crtc->base, config->prop_src_y, 0);
+
  out:
 	drm_modeset_unlock_all(dev);
 
@@ -793,6 +790,249 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_cleanup);
 
+// XXX de-duplicate from drm_atomic_helper.c:
+// probably we want to stash the drm_display_mode as a ptr (transient data) on the state
+// so we only convert it once.. maybe in set_property()?
+static struct drm_display_mode *get_mode(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
+{
+	struct drm_display_mode *mode = NULL;
+	if (cstate->mode_valid) {
+		struct drm_device *dev = crtc->dev;
+		int ret;
+
+		mode = drm_mode_create(dev);
+		if (!mode)
+			return ERR_PTR(-ENOMEM);
+
+		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;
+}
+
+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, void *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, void *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,
+					drm_get_connector_name(connector));
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+int drm_crtc_check_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	int hdisplay, vdisplay;
+	struct drm_display_mode *mode = get_mode(crtc, state);
+
+	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);
+
+	/* For some reason crtc x/y offsets are signed internally. */
+	if (state->x > INT_MAX || state->y > INT_MAX)
+		return -ERANGE;
+
+	if (hdisplay > fb->width ||
+	    vdisplay > fb->height ||
+	    state->x > fb->width - hdisplay ||
+	    state->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,
+			      state->x, state->y,
+			      state->invert_dimensions ? " (inverted)" : "");
+		return -ENOSPC;
+	}
+
+	if (crtc->enabled && !state->set_config) {
+		if (crtc->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;
+
+	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: */
+		int ret = drm_modeset_lock_all_crtcs(crtc->dev, state->state);
+		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 if (property == config->prop_fb_id) {
+		state->new_fb = true;
+		state->fb = drm_framebuffer_lookup(dev, value);
+	} else if (property == config->prop_src_x) {
+		int x = *(int *)&value;
+		if (state->x != x) {
+			state->x = x;
+			state->set_config = true;
+		}
+	} else if (property == config->prop_src_y) {
+		int y = *(int *)&value;
+		if (state->y != y) {
+			state->y = y;
+			state->set_config = true;
+		}
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_crtc_set_property);
+
 /**
  * drm_mode_probed_add - add a mode to a connector's probed mode list
  * @connector: connector the new mode
@@ -1353,6 +1593,16 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 		return -ENOMEM;
 	dev->mode_config.prop_crtc_id = 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;
 }
 
@@ -1617,7 +1867,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)
@@ -1863,11 +2113,11 @@ int drm_mode_getcrtc(struct drm_device *dev,
 		goto out;
 	}
 
-	crtc_resp->x = crtc->x;
-	crtc_resp->y = crtc->y;
+	crtc_resp->x = crtc->state->x;
+	crtc_resp->y = crtc->state->y;
 	crtc_resp->gamma_size = crtc->gamma_size;
-	if (crtc->fb)
-		crtc_resp->fb_id = crtc->fb->base.id;
+	if (crtc->state->fb)
+		crtc_resp->fb_id = crtc->state->fb->base.id;
 	else
 		crtc_resp->fb_id = 0;
 
@@ -2287,45 +2537,6 @@ int drm_mode_set_config_internal(struct drm_mode_set *set)
 }
 EXPORT_SYMBOL(drm_mode_set_config_internal);
 
-/*
- * Checks that the framebuffer is big enough for the CRTC viewport
- * (x, y, hdisplay, vdisplay)
- */
-static 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)
-
-{
-	int hdisplay, vdisplay;
-
-	hdisplay = mode->hdisplay;
-	vdisplay = mode->vdisplay;
-
-	if (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 (crtc->invert_dimensions)
-		swap(hdisplay, vdisplay);
-
-	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,
-			      crtc->invert_dimensions ? " (inverted)" : "");
-		return -ENOSPC;
-	}
-
-	return 0;
-}
-
 /**
  * drm_mode_setcrtc - set CRTC configuration
  * @dev: drm device for the ioctl
@@ -2345,22 +2556,15 @@ 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;
+	void *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;
-
-	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);
@@ -2378,55 +2582,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 				ret = -EINVAL;
 				goto out;
 			}
-			fb = crtc->fb;
-			/* Make refcounting symmetric with the lookup path. */
-			drm_framebuffer_reference(fb);
+			fb_id = crtc->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) {
@@ -2434,52 +2598,63 @@ 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,
-					drm_get_connector_name(connector));
-
-			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);
+retry:
+	state = dev->driver->atomic_begin(dev, 0);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
 
-out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
+	/* 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;
 
-	kfree(connector_set);
-	drm_mode_destroy(dev, mode);
-	drm_modeset_unlock_all(dev);
+	ret =
+		drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode) ||
+		drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_connector_ids,
+			crtc_req->count_connectors * sizeof(connector_ids[0]),
+			connector_ids) ||
+		drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_fb_id, fb_id, NULL) ||
+		drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_src_x, crtc_req->x, NULL) ||
+		drm_mode_crtc_set_obj_prop(crtc, state,
+			config->prop_src_y, crtc_req->y, NULL) ||
+		dev->driver->atomic_check(dev, state);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_commit(dev, state);
+
+out:
+	if (state)
+		dev->driver->atomic_end(dev, state);
+	if (ret == -EDEADLK)
+		goto retry;
 	return ret;
 }
 
@@ -3604,9 +3779,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;
 }
@@ -3925,14 +4097,59 @@ 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);
+}
+
 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;
+	void *state;
 	int ret = -EINVAL;
 
 	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -3946,92 +4163,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if (!crtc)
 		return -ENOENT;
 
-	drm_modeset_lock(&crtc->mutex, NULL);
-	if (crtc->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->fb->pixel_format != fb->pixel_format) {
-		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
-		ret = -EINVAL;
-		goto out;
-	}
+retry:
+	state = dev->driver->atomic_begin(dev,
+			page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
 
 	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->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->fb != fb);
-		/* Unref only the old framebuffer. */
-		fb = NULL;
-	}
+	ret = drm_mode_crtc_set_obj_prop(crtc, 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);
+	dev->driver->atomic_end(dev, state);
+	if (ret == -EDEADLK)
+		goto retry;
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 4ae55b8..3ba66b2 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_helper.h>
 
 #include "exynos_drm_crtc.h"
 #include "exynos_drm_drv.h"
@@ -262,6 +263,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;
@@ -286,7 +291,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 8fbfa06..2b4bbf5 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -1022,6 +1022,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_helper_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 c8841ac..35e13ef 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_helper_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 a06dabb..642da2f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -9778,6 +9778,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
 	.cursor_move = intel_crtc_cursor_move,
 	.gamma_set = intel_crtc_gamma_set,
 	.set_config = intel_crtc_set_config,
+	.set_property = drm_atomic_helper_crtc_set_property,
 	.destroy = intel_crtc_destroy,
 	.page_flip = intel_crtc_page_flip,
 };
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index ee6ed63..8b796e1 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_helper_crtc_set_property,
 	.destroy = mga_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
index 37a3fd2..650c147 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -427,8 +427,10 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
 static int mdp4_crtc_set_property(struct drm_crtc *crtc, void *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/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 0e3270c..605fdb6 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_helper_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 f8e66c0..bfcdf8e 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1327,6 +1327,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_helper_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 524a81a..3b9a6fe 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -367,14 +367,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, void *state,
 {
 	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 e1e794a..7a6c44a 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -553,7 +553,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 d36abbc..29b9572 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_helper.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_helper_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 7b25381..84e0c5e 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_helper.h>
 #include <drm/drm_edid.h>
 
 static void avivo_crtc_load_lut(struct drm_crtc *crtc)
@@ -544,6 +545,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_helper_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 a9d24e4..c840ba8 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_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -528,6 +529,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_helper_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 9e86b99..9209526 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_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -496,6 +497,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_helper_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 d36efc1..34d9804 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_helper_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 2ae1eb7..e05b2ea 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_helper.h>
 #include "udl_drv.h"
 
 /*
@@ -383,6 +384,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_helper_crtc_set_property,
 	.destroy = udl_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index 79f7e8e..5d25f21 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -298,6 +298,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_helper_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 26387c3..f65fec7 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -394,6 +394,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_helper_crtc_set_property,
 	.page_flip = vmw_du_page_flip,
 };
 
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 4ca8360..b737b05 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -65,6 +65,10 @@ struct drm_atomic_helper_funcs {
 	struct drm_plane_state *(*get_plane_state)(struct drm_plane *plane, void *state);
 	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
 	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
+
+	struct drm_crtc_state *(*get_crtc_state)(struct drm_crtc *crtc, void *state);
+	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_helper_funcs drm_atomic_helper_funcs;
@@ -111,6 +115,39 @@ drm_atomic_commit_plane_state(struct drm_plane *plane,
 	return funcs->commit_plane_state(plane, pstate);
 }
 
+int drm_atomic_helper_crtc_set_property(struct drm_crtc *crtc, void *state,
+		struct drm_property *property, uint64_t val, void *blob_data);
+void drm_atomic_helper_init_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate, void *state);
+void drm_atomic_helper_destroy_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state);
+
+static inline struct drm_crtc_state *
+drm_atomic_get_crtc_state(struct drm_crtc *crtc, void *state)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			crtc->dev->driver->atomic_helpers;
+	return funcs->get_crtc_state(crtc, state);
+}
+
+static inline int
+drm_atomic_check_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	const struct drm_atomic_helper_funcs *funcs =
+			crtc->dev->driver->atomic_helpers;
+	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_helper_funcs *funcs =
+			crtc->dev->driver->atomic_helpers;
+	return funcs->commit_crtc_state(crtc, cstate);
+}
+
 /**
  * struct drm_atomic_helper_state - the state object used by atomic helpers
  */
@@ -120,6 +157,8 @@ struct drm_atomic_helper_state {
 	uint32_t flags;
 	struct drm_plane **planes;
 	struct drm_plane_state **pstates;
+	struct drm_crtc **crtcs;
+	struct drm_crtc_state **cstates;
 
 	bool committed;
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 9a4c16e..eccee29 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -466,19 +466,57 @@ 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())
+ * @new_fb: has the fb been changed
+ * @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
+ * @fb: the framebuffer that the CRTC is currently bound to
+ * @x: x position on screen
+ * @y: y position on screen
+ * @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 new_fb            : 1;
+	bool connectors_change : 1;
+
+	uint8_t num_connector_ids;
+	uint32_t *connector_ids;
+	struct drm_mode_modeinfo mode;
+	struct drm_framebuffer *fb;
+	int x, y;
+
+	struct drm_pending_vblank_event *event;
+
+	struct drm_object_property_values propvals;
+
+	void *state;
+};
+
+/**
  * drm_crtc - central CRTC control structure
  * @dev: parent DRM device
  * @head: list management
+ * @id: CRTC number, 0..n
  * @mutex: per-CRTC locking
  * @base: base KMS object for ID tracking etc.
+ * @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
@@ -495,6 +533,8 @@ struct drm_crtc {
 	struct drm_device *dev;
 	struct list_head head;
 
+	int id;
+
 	/**
 	 * crtc mutex
 	 *
@@ -506,8 +546,7 @@ struct drm_crtc {
 
 	struct drm_mode_object base;
 
-	/* framebuffer the connector is currently bound to */
-	struct drm_framebuffer *fb;
+	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. */
@@ -515,17 +554,11 @@ struct drm_crtc {
 
 	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 */
@@ -539,7 +572,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:
+	 */
+	struct drm_framebuffer *fb;
+	int x, y;
+	struct drm_display_mode mode;
 };
 
 
@@ -1019,6 +1060,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;
 
@@ -1070,6 +1113,14 @@ 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 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);
@@ -1119,6 +1170,7 @@ extern const char *drm_get_tv_subconnector_name(int val);
 extern const char *drm_get_tv_select_name(int val);
 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 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);
-- 
1.8.4.2

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

* [RFCv4 11/14] drm: push locking down into restore_fbdev_mode
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (9 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 10/14] drm: convert crtc " Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 12/14] drm: Atomic modeset ioctl Rob Clark
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

All the call-sites save one need locking.  By pushing it down and adding
a lockless flag, we can use the new spiffy atomic ww_mutex crtc locking
and simplify all the call-sites.
---
 drivers/gpu/drm/drm_fb_cma_helper.c       |  9 ++-------
 drivers/gpu/drm/drm_fb_helper.c           | 12 +++++++-----
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  4 +---
 drivers/gpu/drm/gma500/psb_drv.c          |  4 +---
 drivers/gpu/drm/i915/intel_fbdev.c        |  6 +-----
 drivers/gpu/drm/msm/msm_drv.c             |  7 ++-----
 drivers/gpu/drm/omapdrm/omap_drv.c        |  4 +---
 drivers/gpu/drm/tegra/fb.c                |  7 ++-----
 include/drm/drm_fb_helper.h               |  3 ++-
 9 files changed, 19 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 61b5a47..df3ad41 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -429,13 +429,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
  */
 void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
 {
-	if (fbdev_cma) {
-		struct drm_device *dev = fbdev_cma->fb_helper.dev;
-
-		drm_modeset_lock_all(dev);
-		drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
-		drm_modeset_unlock_all(dev);
-	}
+	if (fbdev_cma)
+		drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper, false);
 }
 EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 5773468..294fe5e 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -276,12 +276,15 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 /**
  * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration
  * @fb_helper: fbcon to restore
+ * @lockless: true in drm_fb_helper_force_kernel_mode() path, to avoid
+ *   blocking in panic case, everywhere else should use false
  *
  * This should be called from driver's drm ->lastclose callback
  * when implementing an fbcon on top of kms using this helper. This ensures that
  * the user isn't greeted with a black screen when e.g. X dies.
  */
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
+		bool lockless)
 {
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_plane *plane;
@@ -289,9 +292,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 	void *state;
 	int i;
 
-	drm_warn_on_modeset_not_all_locked(dev);
-
-	state = dev->driver->atomic_begin(dev, 0);
+	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;
@@ -343,7 +345,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
 		if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 			continue;
 
-		ret = drm_fb_helper_restore_fbdev_mode(helper);
+		ret = drm_fb_helper_restore_fbdev_mode(helper, true);
 		if (ret)
 			error = true;
 	}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index e7c2f2d..8032021 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -354,7 +354,5 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
 	if (!private || !private->fb_helper)
 		return;
 
-	drm_modeset_lock_all(dev);
-	drm_fb_helper_restore_fbdev_mode(private->fb_helper);
-	drm_modeset_unlock_all(dev);
+	drm_fb_helper_restore_fbdev_mode(private->fb_helper, false);
 }
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 34c3116..a6545fe 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -153,11 +153,9 @@ static void psb_lastclose(struct drm_device *dev)
 	struct drm_psb_private *dev_priv = dev->dev_private;
 	struct psb_fbdev *fbdev = dev_priv->fbdev;
 
-	drm_modeset_lock_all(dev);
-	ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
+	ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper, false);
 	if (ret)
 		DRM_DEBUG("failed to restore crtc mode\n");
-	drm_modeset_unlock_all(dev);
 
 	return;
 }
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 895fcb4..4f7046d 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -313,11 +313,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
 	if (INTEL_INFO(dev)->num_pipes == 0)
 		return;
 
-	drm_modeset_lock_all(dev);
-
-	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
+	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper, false);
 	if (ret)
 		DRM_DEBUG("failed to restore crtc mode\n");
-
-	drm_modeset_unlock_all(dev);
 }
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index dadd55a..cd7cd43 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -319,11 +319,8 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
 static void msm_lastclose(struct drm_device *dev)
 {
 	struct msm_drm_private *priv = dev->dev_private;
-	if (priv->fbdev) {
-		drm_modeset_lock_all(dev);
-		drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-		drm_modeset_unlock_all(dev);
-	}
+	if (priv->fbdev)
+		drm_fb_helper_restore_fbdev_mode(priv->fbdev, false);
 }
 
 static irqreturn_t msm_irq(DRM_IRQ_ARGS)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 7a6c44a..cfd40f9 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -564,9 +564,7 @@ static void dev_lastclose(struct drm_device *dev)
 		}
 	}
 
-	drm_modeset_lock_all(dev);
-	ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-	drm_modeset_unlock_all(dev);
+	ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev, false);
 	if (ret)
 		DBG("failed to restore crtc mode");
 }
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 490f771..59f7fd1 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -384,9 +384,6 @@ void tegra_drm_fb_exit(struct drm_device *drm)
 
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
 {
-	if (fbdev) {
-		drm_modeset_lock_all(fbdev->base.dev);
-		drm_fb_helper_restore_fbdev_mode(&fbdev->base);
-		drm_modeset_unlock_all(fbdev->base.dev);
-	}
+	if (fbdev)
+		drm_fb_helper_restore_fbdev_mode(&fbdev->base, false);
 }
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 471f276..b130a4d 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -108,7 +108,8 @@ int drm_fb_helper_set_par(struct fb_info *info);
 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
 			    struct fb_info *info);
 
-bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper);
+bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
+		bool lockless);
 void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
 			    uint32_t fb_width, uint32_t fb_height);
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
-- 
1.8.4.2

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

* [RFCv4 12/14] drm: Atomic modeset ioctl
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (10 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 11/14] drm: push locking down into restore_fbdev_mode Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 13/14] drm/msm: add atomic support Rob Clark
  2013-11-25 14:47 ` [RFCv4 14/14] HACK: drm: allow FB's in drm_mode_object_find Rob Clark
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

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

The atomic modeset ioctl cna be used to push any number of new values
for object properties. The driver can then check the full device
configuration as single unit, and try to apply the changes atomically.

The ioctl simply takes a list of object IDs and property IDs and their
values. For setting values to blob properties, the property value
indicates the length of the data, and the actual data is passed via
another blob pointer.

The caller can demand non-blocking operation from the ioctl, and if the
driver can't satisfy that requirement an error will be returned.

The caller can also request to receive asynchronous completion events
after the operation has reached the hardware. An event is sent for each
object specified by the caller, whether or not the actual state of
that object changed. Each event also carries a framebuffer ID, which
indicates to user space that the specified object is no longer
accessing that framebuffer.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/drm_crtc.c  | 156 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_drv.c   |   1 +
 include/drm/drmP.h          |   6 ++
 include/drm/drm_crtc.h      |   2 +
 include/uapi/drm/drm.h      |  12 ++++
 include/uapi/drm/drm_mode.h |  20 ++++++
 6 files changed, 197 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 008b5bb..8158250 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -4581,3 +4581,159 @@ void drm_mode_config_cleanup(struct drm_device *dev)
 	idr_destroy(&dev->mode_config.crtc_idr);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
+
+int drm_mode_atomic_ioctl(struct drm_device *dev,
+			  void *data, struct drm_file *file_priv)
+{
+	struct drm_mode_atomic *arg = data;
+	uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr);
+	uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr);
+	uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr);
+	uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr);
+	uint64_t __user *blob_values_ptr = (uint64_t __user *)(unsigned long)(arg->blob_values_ptr);
+	unsigned int copied_objs, copied_props, copied_blobs;
+	void *state;
+	int ret = 0;
+	unsigned int i, j;
+
+	if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS)
+		return -EINVAL;
+
+	/* can't test and expect an event at the same time. */
+	if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) &&
+			(arg->flags & DRM_MODE_PAGE_FLIP_EVENT))
+		return -EINVAL;
+
+retry:
+	copied_objs = 0;
+	copied_props = 0;
+	copied_blobs = 0;
+
+	state = dev->driver->atomic_begin(dev, arg->flags);
+	if (IS_ERR(state)) {
+		ret = PTR_ERR(state);
+		goto out;
+	}
+
+	for (i = 0; i < arg->count_objs; i++) {
+		uint32_t obj_id, count_props;
+		struct drm_mode_object *obj;
+
+		if (get_user(obj_id, objs_ptr + copied_objs)) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
+		if (!obj || !obj->properties) {
+			ret = -ENOENT;
+			goto out;
+		}
+
+		if ((obj->type == DRM_MODE_OBJECT_CRTC) &&
+				(arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) {
+			struct drm_pending_vblank_event *e =
+				create_vblank_event(dev, file_priv, arg->user_data);
+			if (!e) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			ret = dev->driver->atomic_set_event(dev, state, obj, e);
+			if (ret) {
+				destroy_vblank_event(dev, file_priv, e);
+				goto out;
+			}
+		}
+
+		if (get_user(count_props, count_props_ptr + copied_objs)) {
+			ret = -EFAULT;
+			goto out;
+		}
+
+		copied_objs++;
+
+		for (j = 0; j < count_props; j++) {
+			uint32_t prop_id;
+			uint64_t prop_value;
+			struct drm_property *prop;
+			void *blob_data = NULL;
+
+			if (get_user(prop_id, props_ptr + copied_props)) {
+				ret = -EFAULT;
+				goto out;
+			}
+
+			if (!object_has_prop(obj, prop_id)) {
+				ret = -EINVAL;
+				goto out;
+			}
+
+			prop = drm_property_find(dev, prop_id);
+			if (!prop) {
+				ret = -ENOENT;
+				goto out;
+			}
+
+			if (get_user(prop_value, prop_values_ptr + copied_props)) {
+				ret = -EFAULT;
+				goto out;
+			}
+
+			if (!drm_property_change_is_valid(prop, prop_value)) {
+				ret = -EINVAL;
+				goto out;
+			}
+
+			if ((prop->flags & DRM_MODE_PROP_BLOB) && prop_value) {
+				uint64_t blob_ptr;
+
+				if (get_user(blob_ptr, blob_values_ptr + copied_blobs)) {
+					ret = -EFAULT;
+					goto out;
+				}
+
+				blob_data = kmalloc(prop_value, GFP_KERNEL);
+				if (!blob_data) {
+					ret = -ENOMEM;
+					goto out;
+				}
+
+				if (copy_from_user(blob_data, (void __user *)(unsigned long)blob_ptr, prop_value)) {
+					kfree(blob_data);
+					ret = -EFAULT;
+					goto out;
+				}
+			}
+
+			/* User space sends the blob pointer even if we
+			 * don't use it (length==0).
+			 */
+			if (prop->flags & DRM_MODE_PROP_BLOB)
+				copied_blobs++;
+
+			/* The driver will be in charge of blob_data from now on. */
+			ret = drm_mode_set_obj_prop(obj, state, prop,
+					prop_value, blob_data);
+			if (ret)
+				goto out;
+
+			copied_props++;
+		}
+	}
+
+	ret = dev->driver->atomic_check(dev, state);
+	if (ret)
+		goto out;
+
+	if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)
+		goto out;
+
+	ret = dev->driver->atomic_commit(dev, state);
+
+ out:
+	dev->driver->atomic_end(dev, state);
+	if (ret == -EDEADLK)
+		goto retry;
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index d9137e4..cdadc1c 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -167,6 +167,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 };
 
 #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE( drm_ioctls )
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index f3aecd0..ddfb71d 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1157,6 +1157,12 @@ struct drm_vblank_crtc {
 					   once per disable */
 };
 
+struct drm_pending_atomic_event {
+	struct drm_pending_event base;
+	int pipe;
+	struct drm_event_atomic event;
+};
+
 /**
  * DRM device structure. This structure represent a complete card that
  * may contain multiple heads.
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index eccee29..f97c996 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1380,6 +1380,8 @@ extern int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
 					     struct drm_file *file_priv);
 extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
 					   struct drm_file *file_priv);
+extern int drm_mode_atomic_ioctl(struct drm_device *dev,
+				 void *data, struct drm_file *file_priv);
 
 extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
 				 int *bpp);
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 9b24d65..148c6c0 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -759,6 +759,7 @@ struct drm_prime_handle {
 #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES	DRM_IOWR(0xB9, struct drm_mode_obj_get_properties)
 #define DRM_IOCTL_MODE_OBJ_SETPROPERTY	DRM_IOWR(0xBA, struct drm_mode_obj_set_property)
 #define DRM_IOCTL_MODE_CURSOR2		DRM_IOWR(0xBB, struct drm_mode_cursor2)
+#define DRM_IOCTL_MODE_ATOMIC		DRM_IOWR(0xBC, struct drm_mode_atomic)
 
 /**
  * Device specific ioctls should only be in their respective headers
@@ -800,6 +801,17 @@ struct drm_event_vblank {
 	__u32 reserved;
 };
 
+struct drm_event_atomic {
+	struct drm_event base;
+	__u64 user_data;
+	__u32 tv_sec;
+	__u32 tv_usec;
+	__u32 sequence;
+	__u32 obj_id;
+	__u32 old_fb_id;
+	__u32 reserved;
+};
+
 /* typedef area */
 #ifndef __KERNEL__
 typedef struct drm_clip_rect drm_clip_rect_t;
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 6421edc..a4b2e58 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -511,7 +511,27 @@ struct drm_mode_destroy_dumb {
 	uint32_t handle;
 };
 
+/* page-flip flags are valid, plus: */
+#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
 #define DRM_MODE_ATOMIC_NONBLOCK  0x0200
 #define DRM_MODE_ATOMIC_NOLOCK    0x8000  /* only used internally */
 
+#define DRM_MODE_ATOMIC_FLAGS (\
+		DRM_MODE_PAGE_FLIP_EVENT |\
+		DRM_MODE_PAGE_FLIP_ASYNC |\
+		DRM_MODE_ATOMIC_TEST_ONLY |\
+		DRM_MODE_ATOMIC_NONBLOCK)
+
+/* FIXME come up with some sane error reporting mechanism? */
+struct drm_mode_atomic {
+	__u32 flags;
+	__u32 count_objs;
+	__u64 objs_ptr;
+	__u64 count_props_ptr;
+	__u64 props_ptr;
+	__u64 prop_values_ptr;
+	__u64 blob_values_ptr;
+	__u64 user_data;
+};
+
 #endif
-- 
1.8.4.2

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

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

* [RFCv4 13/14] drm/msm: add atomic support
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (11 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 12/14] drm: Atomic modeset ioctl Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  2013-11-25 14:47 ` [RFCv4 14/14] HACK: drm: allow FB's in drm_mode_object_find Rob Clark
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

TODO: probably can split this up into prep patch which splits the
msm_queue_fence_cb out of gem..
---
 drivers/gpu/drm/msm/Makefile         |   1 +
 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c |  57 ++++++--------
 drivers/gpu/drm/msm/mdp4/mdp4_kms.c  |   6 ++
 drivers/gpu/drm/msm/mdp4/mdp4_kms.h  |   1 +
 drivers/gpu/drm/msm/msm_atomic.c     | 149 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/msm/msm_drv.c        |  22 +++++-
 drivers/gpu/drm/msm/msm_drv.h        |   8 ++
 drivers/gpu/drm/msm/msm_gem.c        |  24 +-----
 drivers/gpu/drm/msm/msm_gem.h        |  13 +++
 9 files changed, 228 insertions(+), 53 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 e5fa12b..f7648d1 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -18,6 +18,7 @@ msm-y := \
 	mdp4/mdp4_irq.o \
 	mdp4/mdp4_kms.o \
 	mdp4/mdp4_plane.o \
+	msm_atomic.o \
 	msm_drv.o \
 	msm_fb.o \
 	msm_gem.o \
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
index 650c147..4f6bce3 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -51,7 +51,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
@@ -120,12 +119,16 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
-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->id))
+		return;
+
 	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
 		struct drm_plane *plane = mdp4_crtc->planes[i];
 		if (plane) {
@@ -148,23 +151,6 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
 	mdp4_irq_register(get_kms(crtc), &mdp4_crtc->vblank);
 }
 
-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->fb;
-
-	if (!fb)
-		return;
-
-	mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
-	crtc_flush(crtc);
-
-	/* enable vblank to complete flip: */
-	request_pending(crtc, PENDING_FLIP);
-}
-
 static void unref_fb_worker(struct drm_flip_work *work, void *val)
 {
 	struct mdp4_crtc *mdp4_crtc =
@@ -374,7 +360,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));
 }
@@ -405,23 +391,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) {
+		spin_unlock_irqrestore(&dev->event_lock, flags);
 		dev_err(dev->dev, "already pending flip!\n");
 		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, true, new_fb);
 
-	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
+	mdp4_plane_set_scanout(mdp4_crtc->plane, crtc->fb);
+	mdp4_crtc_flush(crtc);
+
+	/* enable vblank to complete flip: */
+	request_pending(crtc, PENDING_FLIP);
+
+	return 0;
 }
 
 static int mdp4_crtc_set_property(struct drm_crtc *crtc, void *state,
@@ -585,8 +575,13 @@ static void mdp4_crtc_vblank_irq(struct mdp4_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->id)) {
+			/* 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) {
@@ -599,8 +594,8 @@ static void mdp4_crtc_err_irq(struct mdp4_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);
+	DRM_ERROR("%s: error: %08x\n", mdp4_crtc->name, irqstatus);
+	mdp4_crtc_flush(crtc);
 }
 
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
@@ -680,7 +675,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)
@@ -738,8 +733,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(dev, crtc, &mdp4_crtc_funcs);
 	drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
 
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
index 8972ac3..556992b 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_kms.c
@@ -128,6 +128,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(kms);
@@ -154,6 +159,7 @@ static const struct msm_kms_funcs kms_funcs = {
 		.disable_vblank  = mdp4_disable_vblank,
 		.get_format      = mdp4_get_format,
 		.round_pixclk    = mdp4_round_pixclk,
+		.flush           = mdp4_flush,
 		.preclose        = mdp4_preclose,
 		.destroy         = mdp4_destroy,
 };
diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp4/mdp4_kms.h
index eb015c8..34fa393 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_kms.h
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_kms.h
@@ -206,6 +206,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/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
new file mode 100644
index 0000000..5fa65c6
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -0,0 +1,149 @@
+/*
+ * 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_gem.h"
+
+struct msm_async_commit {
+	struct drm_atomic_helper_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_helper_state *state)
+{
+	struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+	if (!c)
+		return NULL;
+
+	drm_atomic_helper_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_helper_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_helper_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]->id);
+
+	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_helper_commit_unlocked(dev, state);
+	else
+		ret = drm_atomic_helper_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, void *state)
+{
+	struct drm_atomic_helper_state *a = state;
+	int nplanes = dev->mode_config.num_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
+	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);
+
+		for (i = 0; i < ncrtcs; i++)
+			if (pending_fb(a->cstates[i]))
+				add_fb(c, a->crtcs[i], a->cstates[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);
+
+		for (i = 0; i < ncrtcs && !ret; i++)
+			if (pending_fb(a->cstates[i]))
+				ret = wait_fb(a->crtcs[i], a->cstates[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 cd7cd43..5aa6ef9 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -537,6 +537,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)
 {
@@ -734,7 +754,7 @@ static struct drm_driver msm_driver = {
 	.atomic_begin       = drm_atomic_helper_begin,
 	.atomic_set_event   = drm_atomic_helper_set_event,
 	.atomic_check       = drm_atomic_helper_check,
-	.atomic_commit      = drm_atomic_helper_commit,
+	.atomic_commit      = msm_atomic_commit,
 	.atomic_end         = drm_atomic_helper_end,
 	.atomic_helpers     = &drm_atomic_helper_funcs,
 #ifdef CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index bfd4121..934623d 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -95,6 +95,9 @@ struct msm_drm_private {
 
 	unsigned int num_connectors;
 	struct drm_connector *connectors[8];
+
+	/* crtc's pending atomic update: */
+	uint32_t pending_crtcs;
 };
 
 struct msm_format {
@@ -134,6 +137,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);
@@ -151,11 +155,15 @@ int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu,
 
 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, void *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 e587d25..8e0f230 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -400,23 +400,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,
@@ -455,12 +443,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 f4f23a5..5249708 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -65,6 +65,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,
-- 
1.8.4.2

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

* [RFCv4 14/14] HACK: drm: allow FB's in drm_mode_object_find
  2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
                   ` (12 preceding siblings ...)
  2013-11-25 14:47 ` [RFCv4 13/14] drm/msm: add atomic support Rob Clark
@ 2013-11-25 14:47 ` Rob Clark
  13 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-11-25 14:47 UTC (permalink / raw)
  To: dri-devel

Ugg.. we do actually want to permit FB's in atomic case, since FB will
be looked up like any other object property value.

Currently we do the FB refcnt'ing dance in atomic->commit(), and would
rather not have to special case FB's or collect an FB ref when we look
up the property.  Not sure if it is better to rework the atomic FB
refcnt'ing or loosen this restriction?
---
 drivers/gpu/drm/drm_crtc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 8158250..e57b7eb 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -441,7 +441,7 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 
 	/* Framebuffers are reference counted and need their own lookup
 	 * function.*/
-	WARN_ON(type == DRM_MODE_OBJECT_FB);
+//	WARN_ON(type == DRM_MODE_OBJECT_FB);
 
 	mutex_lock(&dev->mode_config.idr_mutex);
 	obj = idr_find(&dev->mode_config.crtc_idr, id);
-- 
1.8.4.2

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

* Re: [RFCv4 10/14] drm: convert crtc to properties/state
  2013-11-25 14:47 ` [RFCv4 10/14] drm: convert crtc " Rob Clark
@ 2013-12-11 21:48   ` Matt Roper
  2013-12-11 23:38     ` Rob Clark
  2014-03-04 21:29   ` Sean Paul
  1 sibling, 1 reply; 32+ messages in thread
From: Matt Roper @ 2013-12-11 21:48 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, Nov 25, 2013 at 09:47:34AM -0500, Rob Clark wrote:
...
> +static struct drm_crtc_state *
> +drm_atomic_helper_get_crtc_state(struct drm_crtc *crtc, void *state)
> +{
> +	struct drm_atomic_helper_state *a = state;
> +	struct drm_crtc_state *cstate;
> +	int ret;
> +
> +	ret = drm_modeset_lock(&crtc->mutex, state);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	cstate = a->cstates[crtc->id];

The id field of struct drm_crtc never seems to be initialized in the
current DRM codebase, so this will always wind up looking up cstates[0];
we probably want to initialize crtc->id in drm_crtc_init() at the same
place we increment dev->mode_config.num_crtc.

...
> +static int check_connectors(struct drm_crtc *crtc, void *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);

I think you meant to use ocrtc here rather than crtc, right?


I think you might also want to move patch 11 up above 9 & 10, otherwise
you'll run into the lastclose deadlock while bisecting through this
patchset.


Matt

-- 
Matt Roper
Intel Corporation

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

* Re: [RFCv4 10/14] drm: convert crtc to properties/state
  2013-12-11 21:48   ` Matt Roper
@ 2013-12-11 23:38     ` Rob Clark
  0 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2013-12-11 23:38 UTC (permalink / raw)
  To: Matt Roper; +Cc: dri-devel

On Wed, Dec 11, 2013 at 4:48 PM, Matt Roper <matthew.d.roper@intel.com> wrote:
> On Mon, Nov 25, 2013 at 09:47:34AM -0500, Rob Clark wrote:
> ...
>> +static struct drm_crtc_state *
>> +drm_atomic_helper_get_crtc_state(struct drm_crtc *crtc, void *state)
>> +{
>> +     struct drm_atomic_helper_state *a = state;
>> +     struct drm_crtc_state *cstate;
>> +     int ret;
>> +
>> +     ret = drm_modeset_lock(&crtc->mutex, state);
>> +     if (ret)
>> +             return ERR_PTR(ret);
>> +
>> +     cstate = a->cstates[crtc->id];
>
> The id field of struct drm_crtc never seems to be initialized in the
> current DRM codebase, so this will always wind up looking up cstates[0];
> we probably want to initialize crtc->id in drm_crtc_init() at the same
> place we increment dev->mode_config.num_crtc.
>

yeah, it is supposed to be initialized in drm_crtc_init()..  I was
juggling the patches around a bit, possibly I screwed up and that
ended up in one of the later patches?

I'll go back and check this

> ...
>> +static int check_connectors(struct drm_crtc *crtc, void *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);
>
> I think you meant to use ocrtc here rather than crtc, right?
>

yes, good catch.  I need a driver with more than one connector to test
with, apparently ;-)

>
> I think you might also want to move patch 11 up above 9 & 10, otherwise
> you'll run into the lastclose deadlock while bisecting through this
> patchset.
>

good point.. that should be pretty easy to re-order

BR,
-R


>
> Matt
>
> --
> Matt Roper
> Intel Corporation

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2013-11-25 14:47 ` [RFCv4 09/14] drm: convert plane to properties/state Rob Clark
@ 2014-01-28 21:52   ` Sean Paul
  2014-01-29 13:44     ` Rob Clark
  2014-02-26 21:30   ` Sean Paul
  2014-03-18 15:48   ` Sean Paul
  2 siblings, 1 reply; 32+ messages in thread
From: Sean Paul @ 2014-01-28 21:52 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
> Break the mutable state of a plane out into a separate structure
> and use atomic properties mechanism to set plane attributes.  This
> makes it easier to have some helpers for plane->set_property()
> and for checking for invalid params.  The idea is that individual
> drivers can wrap the state struct in their own struct which adds
> driver specific parameters, for easy build-up of state across
> multiple set_property() calls and for easy atomic commit or roll-
> back.
>
> The same should be done for CRTC, encoder, and connector, but this
> patch only includes the first part (plane).
> ---
>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>  include/drm/drm_atomic_helper.h             |  39 ++-
>  include/drm/drm_crtc.h                      |  88 +++++-
>  19 files changed, 654 insertions(+), 189 deletions(-)
>

<snip>

> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> index 2e31fb8..d585a4c 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> @@ -10,6 +10,7 @@
>   */
>
>  #include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
>
>  #include <drm/exynos_drm.h>
>  #include "exynos_drm_drv.h"
> @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>         struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
>
> -       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
> +       exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>                         exynos_drm_encoder_plane_commit);
>  }
>
> @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>                 if (exynos_plane->enabled)
>                         return;
>
> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>                                 exynos_drm_encoder_plane_enable);
>
>                 exynos_plane->enabled = true;
> @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>                 if (!exynos_plane->enabled)
>                         return;
>
> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>                                 exynos_drm_encoder_plane_disable);
>
>                 exynos_plane->enabled = false;
> @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>         if (ret < 0)
>                 return ret;
>
> -       plane->crtc = crtc;
> +       plane->state->crtc = crtc;
>
>         exynos_plane_commit(plane);
>         exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
> @@ -225,13 +226,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);

Hi Rob,
This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
performs a drm_modeset_lock_all first, then
drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.

Trace:
(__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
(schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
(schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
(__ww_mutex_lock_slowpath+0x208/0x2cc)
(__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
(__ww_mutex_lock+0x50/0xc4)
(__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
(modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
(drm_modeset_lock+0x20/0x30) from [<c028e148>]
(drm_modeset_lock_all_crtcs+0x3c/0x54)
(drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
(drm_atomic_helper_get_plane_state+0x40/0xc4)
(drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
(exynos_plane_set_property+0x3c/0xbc)
(exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
(drm_mode_plane_set_obj_prop+0x3c/0x4c)
(drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
(drm_mode_set_obj_prop+0xac/0x128)
(drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
(drm_mode_obj_set_property_ioctl+0xe4/0x15c)
(drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
(drm_ioctl+0x2e0/0x40c)
(drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)

Sean


> +
> +       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 = {

<snip>

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

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-01-28 21:52   ` Sean Paul
@ 2014-01-29 13:44     ` Rob Clark
  2014-02-07  2:53       ` Sean Paul
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Clark @ 2014-01-29 13:44 UTC (permalink / raw)
  To: Sean Paul; +Cc: dri-devel

On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>> Break the mutable state of a plane out into a separate structure
>> and use atomic properties mechanism to set plane attributes.  This
>> makes it easier to have some helpers for plane->set_property()
>> and for checking for invalid params.  The idea is that individual
>> drivers can wrap the state struct in their own struct which adds
>> driver specific parameters, for easy build-up of state across
>> multiple set_property() calls and for easy atomic commit or roll-
>> back.
>>
>> The same should be done for CRTC, encoder, and connector, but this
>> patch only includes the first part (plane).
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>  include/drm/drm_crtc.h                      |  88 +++++-
>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>
>
> <snip>
>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> index 2e31fb8..d585a4c 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> @@ -10,6 +10,7 @@
>>   */
>>
>>  #include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>>
>>  #include <drm/exynos_drm.h>
>>  #include "exynos_drm_drv.h"
>> @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>         struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
>>
>> -       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>> +       exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>                         exynos_drm_encoder_plane_commit);
>>  }
>>
>> @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>                 if (exynos_plane->enabled)
>>                         return;
>>
>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>                                 exynos_drm_encoder_plane_enable);
>>
>>                 exynos_plane->enabled = true;
>> @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>                 if (!exynos_plane->enabled)
>>                         return;
>>
>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>                                 exynos_drm_encoder_plane_disable);
>>
>>                 exynos_plane->enabled = false;
>> @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>>         if (ret < 0)
>>                 return ret;
>>
>> -       plane->crtc = crtc;
>> +       plane->state->crtc = crtc;
>>
>>         exynos_plane_commit(plane);
>>         exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
>> @@ -225,13 +226,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);
>
> Hi Rob,
> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
> performs a drm_modeset_lock_all first, then
> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.

oh, whoops, I guess neither modetest or glplane test does a
set-property ioctl.  I think we simply need to drop the locking at the
top level ioctl fxn, and let the atomic locking take care of things.

BR,
-R

> Trace:
> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
> (__ww_mutex_lock_slowpath+0x208/0x2cc)
> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
> (__ww_mutex_lock+0x50/0xc4)
> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
> (drm_modeset_lock_all_crtcs+0x3c/0x54)
> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
> (drm_atomic_helper_get_plane_state+0x40/0xc4)
> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
> (exynos_plane_set_property+0x3c/0xbc)
> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
> (drm_mode_set_obj_prop+0xac/0x128)
> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
> (drm_ioctl+0x2e0/0x40c)
> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>
> Sean
>
>
>> +
>> +       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 = {
>
> <snip>
>
>> --
>> 1.8.4.2
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-01-29 13:44     ` Rob Clark
@ 2014-02-07  2:53       ` Sean Paul
  2014-02-07 12:28         ` Rob Clark
  0 siblings, 1 reply; 32+ messages in thread
From: Sean Paul @ 2014-02-07  2:53 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, Jan 29, 2014 at 8:44 AM, Rob Clark <robdclark@gmail.com> wrote:
> On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> Break the mutable state of a plane out into a separate structure
>>> and use atomic properties mechanism to set plane attributes.  This
>>> makes it easier to have some helpers for plane->set_property()
>>> and for checking for invalid params.  The idea is that individual
>>> drivers can wrap the state struct in their own struct which adds
>>> driver specific parameters, for easy build-up of state across
>>> multiple set_property() calls and for easy atomic commit or roll-
>>> back.
>>>
>>> The same should be done for CRTC, encoder, and connector, but this
>>> patch only includes the first part (plane).
>>> ---
>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>
>>
>> <snip>
>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>> index 2e31fb8..d585a4c 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>> @@ -10,6 +10,7 @@
>>>   */
>>>
>>>  #include <drm/drmP.h>
>>> +#include <drm/drm_atomic_helper.h>
>>>
>>>  #include <drm/exynos_drm.h>
>>>  #include "exynos_drm_drv.h"
>>> @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
>>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>>         struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
>>>
>>> -       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>> +       exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>                         exynos_drm_encoder_plane_commit);
>>>  }
>>>
>>> @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>>                 if (exynos_plane->enabled)
>>>                         return;
>>>
>>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>                                 exynos_drm_encoder_plane_enable);
>>>
>>>                 exynos_plane->enabled = true;
>>> @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>>                 if (!exynos_plane->enabled)
>>>                         return;
>>>
>>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>                                 exynos_drm_encoder_plane_disable);
>>>
>>>                 exynos_plane->enabled = false;
>>> @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>>>         if (ret < 0)
>>>                 return ret;
>>>
>>> -       plane->crtc = crtc;
>>> +       plane->state->crtc = crtc;
>>>
>>>         exynos_plane_commit(plane);
>>>         exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
>>> @@ -225,13 +226,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);
>>
>> Hi Rob,
>> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
>> performs a drm_modeset_lock_all first, then
>> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.
>
> oh, whoops, I guess neither modetest or glplane test does a
> set-property ioctl.  I think we simply need to drop the locking at the
> top level ioctl fxn, and let the atomic locking take care of things.
>

Seems like there's another one in the drm_fb_helper_restore_fbdev_mode path.

(modeset_lock_state+0x58/0x174) from [<c028e300>] (drm_modeset_lock+0x20/0x30)
(drm_modeset_lock+0x20/0x30) from [<c028e34c>]
(drm_modeset_lock_all_crtcs+0x3c/0x54)
(drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029b54c>]
(drm_atomic_helper_get_plane_state+0x40/0xc4)
(drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2008>]
(exynos_plane_set_property+0x3c/0xbc)
(exynos_plane_set_property+0x3c/0xbc) from [<c028dcb8>]
(drm_mode_plane_set_obj_prop+0x3c/0x4c)
(drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c028dd08>]
(drm_plane_force_disable+0x40/0x60)
(drm_fb_helper_restore_fbdev_mode+0x80/0x158) from [<c029ee78>]
(exynos_drm_fbdev_restore_mode+0x54/0x6c)
(exynos_drm_fbdev_restore_mode+0x54/0x6c) from [<c029bc28>]
(exynos_drm_lastclose+0x34/0x44)
(exynos_drm_lastclose+0x34/0x44) from [<c0284bb0>] (drm_lastclose+0x44/0x158)
(drm_lastclose+0x44/0x158) from [<c02857c0>] (drm_release+0x4c4/0x51c)
(drm_release+0x4c4/0x51c) from [<c0101410>] (__fput+0x108/0x208)


Sean


> BR,
> -R
>
>> Trace:
>> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
>> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
>> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
>> (__ww_mutex_lock_slowpath+0x208/0x2cc)
>> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
>> (__ww_mutex_lock+0x50/0xc4)
>> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
>> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
>> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
>> (exynos_plane_set_property+0x3c/0xbc)
>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
>> (drm_mode_set_obj_prop+0xac/0x128)
>> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
>> (drm_ioctl+0x2e0/0x40c)
>> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>>
>> Sean
>>
>>
>>> +
>>> +       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 = {
>>
>> <snip>
>>
>>> --
>>> 1.8.4.2
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-02-07  2:53       ` Sean Paul
@ 2014-02-07 12:28         ` Rob Clark
  2014-02-10 17:45           ` Sean Paul
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Clark @ 2014-02-07 12:28 UTC (permalink / raw)
  To: Sean Paul; +Cc: dri-devel

On Thu, Feb 6, 2014 at 9:53 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Wed, Jan 29, 2014 at 8:44 AM, Rob Clark <robdclark@gmail.com> wrote:
>> On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
>>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>>> Break the mutable state of a plane out into a separate structure
>>>> and use atomic properties mechanism to set plane attributes.  This
>>>> makes it easier to have some helpers for plane->set_property()
>>>> and for checking for invalid params.  The idea is that individual
>>>> drivers can wrap the state struct in their own struct which adds
>>>> driver specific parameters, for easy build-up of state across
>>>> multiple set_property() calls and for easy atomic commit or roll-
>>>> back.
>>>>
>>>> The same should be done for CRTC, encoder, and connector, but this
>>>> patch only includes the first part (plane).
>>>> ---
>>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>>
>>>
>>> <snip>
>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>> index 2e31fb8..d585a4c 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>> @@ -10,6 +10,7 @@
>>>>   */
>>>>
>>>>  #include <drm/drmP.h>
>>>> +#include <drm/drm_atomic_helper.h>
>>>>
>>>>  #include <drm/exynos_drm.h>
>>>>  #include "exynos_drm_drv.h"
>>>> @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
>>>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>>>         struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
>>>>
>>>> -       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>>> +       exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>>                         exynos_drm_encoder_plane_commit);
>>>>  }
>>>>
>>>> @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>>>                 if (exynos_plane->enabled)
>>>>                         return;
>>>>
>>>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>>                                 exynos_drm_encoder_plane_enable);
>>>>
>>>>                 exynos_plane->enabled = true;
>>>> @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>>>                 if (!exynos_plane->enabled)
>>>>                         return;
>>>>
>>>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>>                                 exynos_drm_encoder_plane_disable);
>>>>
>>>>                 exynos_plane->enabled = false;
>>>> @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>>>>         if (ret < 0)
>>>>                 return ret;
>>>>
>>>> -       plane->crtc = crtc;
>>>> +       plane->state->crtc = crtc;
>>>>
>>>>         exynos_plane_commit(plane);
>>>>         exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
>>>> @@ -225,13 +226,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);
>>>
>>> Hi Rob,
>>> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
>>> performs a drm_modeset_lock_all first, then
>>> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.
>>
>> oh, whoops, I guess neither modetest or glplane test does a
>> set-property ioctl.  I think we simply need to drop the locking at the
>> top level ioctl fxn, and let the atomic locking take care of things.
>>
>
> Seems like there's another one in the drm_fb_helper_restore_fbdev_mode path.
>
> (modeset_lock_state+0x58/0x174) from [<c028e300>] (drm_modeset_lock+0x20/0x30)
> (drm_modeset_lock+0x20/0x30) from [<c028e34c>]
> (drm_modeset_lock_all_crtcs+0x3c/0x54)
> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029b54c>]
> (drm_atomic_helper_get_plane_state+0x40/0xc4)
> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2008>]
> (exynos_plane_set_property+0x3c/0xbc)
> (exynos_plane_set_property+0x3c/0xbc) from [<c028dcb8>]
> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c028dd08>]
> (drm_plane_force_disable+0x40/0x60)
> (drm_fb_helper_restore_fbdev_mode+0x80/0x158) from [<c029ee78>]
> (exynos_drm_fbdev_restore_mode+0x54/0x6c)
> (exynos_drm_fbdev_restore_mode+0x54/0x6c) from [<c029bc28>]
> (exynos_drm_lastclose+0x34/0x44)
> (exynos_drm_lastclose+0x34/0x44) from [<c0284bb0>] (drm_lastclose+0x44/0x158)
> (drm_lastclose+0x44/0x158) from [<c02857c0>] (drm_release+0x4c4/0x51c)
> (drm_release+0x4c4/0x51c) from [<c0101410>] (__fput+0x108/0x208)
>

In theory the drm_modeset_{lock,unlock}_all() stuff should be removed
from exynos_drm_fbdev_restore_mode().. perhaps I missed updating
exynos?

BR,
-R

>
> Sean
>
>
>> BR,
>> -R
>>
>>> Trace:
>>> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
>>> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
>>> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
>>> (__ww_mutex_lock_slowpath+0x208/0x2cc)
>>> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
>>> (__ww_mutex_lock+0x50/0xc4)
>>> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
>>> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
>>> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
>>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
>>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
>>> (exynos_plane_set_property+0x3c/0xbc)
>>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
>>> (drm_mode_set_obj_prop+0xac/0x128)
>>> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
>>> (drm_ioctl+0x2e0/0x40c)
>>> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>>>
>>> Sean
>>>
>>>
>>>> +
>>>> +       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 = {
>>>
>>> <snip>
>>>
>>>> --
>>>> 1.8.4.2
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-02-07 12:28         ` Rob Clark
@ 2014-02-10 17:45           ` Sean Paul
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Paul @ 2014-02-10 17:45 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Fri, Feb 7, 2014 at 7:28 AM, Rob Clark <robdclark@gmail.com> wrote:
> On Thu, Feb 6, 2014 at 9:53 PM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Wed, Jan 29, 2014 at 8:44 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
>>>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>>>> Break the mutable state of a plane out into a separate structure
>>>>> and use atomic properties mechanism to set plane attributes.  This
>>>>> makes it easier to have some helpers for plane->set_property()
>>>>> and for checking for invalid params.  The idea is that individual
>>>>> drivers can wrap the state struct in their own struct which adds
>>>>> driver specific parameters, for easy build-up of state across
>>>>> multiple set_property() calls and for easy atomic commit or roll-
>>>>> back.
>>>>>
>>>>> The same should be done for CRTC, encoder, and connector, but this
>>>>> patch only includes the first part (plane).
>>>>> ---
>>>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>>>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>>>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>>>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>>>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>>>
>>>>
>>>> <snip>
>>>>
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>>> index 2e31fb8..d585a4c 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>>> @@ -10,6 +10,7 @@
>>>>>   */
>>>>>
>>>>>  #include <drm/drmP.h>
>>>>> +#include <drm/drm_atomic_helper.h>
>>>>>
>>>>>  #include <drm/exynos_drm.h>
>>>>>  #include "exynos_drm_drv.h"
>>>>> @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane)
>>>>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>>>>         struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
>>>>>
>>>>> -       exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>>>> +       exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>>>                         exynos_drm_encoder_plane_commit);
>>>>>  }
>>>>>
>>>>> @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>>>>                 if (exynos_plane->enabled)
>>>>>                         return;
>>>>>
>>>>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>>>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>>>                                 exynos_drm_encoder_plane_enable);
>>>>>
>>>>>                 exynos_plane->enabled = true;
>>>>> @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode)
>>>>>                 if (!exynos_plane->enabled)
>>>>>                         return;
>>>>>
>>>>> -               exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
>>>>> +               exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos,
>>>>>                                 exynos_drm_encoder_plane_disable);
>>>>>
>>>>>                 exynos_plane->enabled = false;
>>>>> @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>>>>>         if (ret < 0)
>>>>>                 return ret;
>>>>>
>>>>> -       plane->crtc = crtc;
>>>>> +       plane->state->crtc = crtc;
>>>>>
>>>>>         exynos_plane_commit(plane);
>>>>>         exynos_plane_dpms(plane, DRM_MODE_DPMS_ON);
>>>>> @@ -225,13 +226,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);
>>>>
>>>> Hi Rob,
>>>> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
>>>> performs a drm_modeset_lock_all first, then
>>>> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.
>>>
>>> oh, whoops, I guess neither modetest or glplane test does a
>>> set-property ioctl.  I think we simply need to drop the locking at the
>>> top level ioctl fxn, and let the atomic locking take care of things.
>>>
>>
>> Seems like there's another one in the drm_fb_helper_restore_fbdev_mode path.
>>
>> (modeset_lock_state+0x58/0x174) from [<c028e300>] (drm_modeset_lock+0x20/0x30)
>> (drm_modeset_lock+0x20/0x30) from [<c028e34c>]
>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029b54c>]
>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2008>]
>> (exynos_plane_set_property+0x3c/0xbc)
>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dcb8>]
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c028dd08>]
>> (drm_plane_force_disable+0x40/0x60)
>> (drm_fb_helper_restore_fbdev_mode+0x80/0x158) from [<c029ee78>]
>> (exynos_drm_fbdev_restore_mode+0x54/0x6c)
>> (exynos_drm_fbdev_restore_mode+0x54/0x6c) from [<c029bc28>]
>> (exynos_drm_lastclose+0x34/0x44)
>> (exynos_drm_lastclose+0x34/0x44) from [<c0284bb0>] (drm_lastclose+0x44/0x158)
>> (drm_lastclose+0x44/0x158) from [<c02857c0>] (drm_release+0x4c4/0x51c)
>> (drm_release+0x4c4/0x51c) from [<c0101410>] (__fput+0x108/0x208)
>>
>
> In theory the drm_modeset_{lock,unlock}_all() stuff should be removed
> from exynos_drm_fbdev_restore_mode().. perhaps I missed updating
> exynos?
>

Yeah, seems like it. IIRC, this was added during Daniel's locking
changes, so maybe it was too new.

Sean



> BR,
> -R
>
>>
>> Sean
>>
>>
>>> BR,
>>> -R
>>>
>>>> Trace:
>>>> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
>>>> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
>>>> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
>>>> (__ww_mutex_lock_slowpath+0x208/0x2cc)
>>>> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
>>>> (__ww_mutex_lock+0x50/0xc4)
>>>> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
>>>> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
>>>> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
>>>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>>>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
>>>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>>>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
>>>> (exynos_plane_set_property+0x3c/0xbc)
>>>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
>>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
>>>> (drm_mode_set_obj_prop+0xac/0x128)
>>>> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
>>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
>>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
>>>> (drm_ioctl+0x2e0/0x40c)
>>>> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>>>>
>>>> Sean
>>>>
>>>>
>>>>> +
>>>>> +       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 = {
>>>>
>>>> <snip>
>>>>
>>>>> --
>>>>> 1.8.4.2
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2013-11-25 14:47 ` [RFCv4 09/14] drm: convert plane to properties/state Rob Clark
  2014-01-28 21:52   ` Sean Paul
@ 2014-02-26 21:30   ` Sean Paul
  2014-02-27  0:18     ` Rob Clark
  2014-03-18 15:48   ` Sean Paul
  2 siblings, 1 reply; 32+ messages in thread
From: Sean Paul @ 2014-02-26 21:30 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
> Break the mutable state of a plane out into a separate structure
> and use atomic properties mechanism to set plane attributes.  This
> makes it easier to have some helpers for plane->set_property()
> and for checking for invalid params.  The idea is that individual
> drivers can wrap the state struct in their own struct which adds
> driver specific parameters, for easy build-up of state across
> multiple set_property() calls and for easy atomic commit or roll-
> back.
>
> The same should be done for CRTC, encoder, and connector, but this
> patch only includes the first part (plane).


Hi Rob,
I've been tracking down a crash that came up on my exynos board when I
applied this patch. I hope you can hold my hand a little bit and give
some guidance.

<snip>

> +static int
> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
> +               struct drm_plane_state *pstate)
> +{
> +       struct drm_framebuffer *old_fb = NULL, *fb = NULL;
> +       int ret = 0;
> +
> +       fb = pstate->fb;
> +
> +       if (pstate->crtc && fb) {
> +               ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
> +                       pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
> +                       pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
> +               if (!ret) {
> +                       /* on success, update state and fb refcnting: */
> +                       /* NOTE: if we ensure no driver sets plane->state->fb = NULL
> +                        * on disable, we can move this up a level and not duplicate
> +                        * nearly the same thing for both update_plane and disable_plane
> +                        * cases..  I leave it like this for now to be paranoid due to
> +                        * the slightly different ordering in the two cases in the
> +                        * original code.
> +                        */
> +                       old_fb = plane->state->fb;

I think this is slightly different than what we had before in
setplane. In the previous code, we had:

ret = plane->funcs->update_plane(plane, crtc, fb,
                                                  plane_req->crtc_x,
plane_req->crtc_y,
                                                  plane_req->crtc_w,
plane_req->crtc_h,
                                                  plane_req->src_x,
plane_req->src_y,
                                                  plane_req->src_w,
plane_req->src_h);
if (!ret) {
        old_fb = plane->fb;
        fb = NULL;
        plane->crtc = crtc;
        plane->fb = fb;
}

In this case, we'd unreference old_fb which is the previous plane->fb.
AFAICT, update_plane did not set plane->fb to fb, so it would, in
fact, be the old plane.

In the new code, drm_mode_setplane calls drm_mode_plane_set_obj_prop
on fb_id, which will update plane->state->fb. Then we come in here and
unreference it as old_fb. I _believe_ we're taking one more reference
than we need in this case.

What I'm seeing in exynos is that when we setplane to an fb, we leave
it with refcount == 1. If we disable that plane, we'll free the fb.
Unfortunately, this leaves the fb in the fbs list and the next time we
try to iterate that list Bad Things Happen.

Does this make any sense?

Sean

> +                       swap_plane_state(plane, pstate->state);
> +                       fb = NULL;
> +               }
> +       } else {
> +               old_fb = plane->state->fb;
> +               plane->funcs->disable_plane(plane);
> +               swap_plane_state(plane, pstate->state);
> +       }
> +
> +
> +       if (fb)
> +               drm_framebuffer_unreference(fb);
> +       if (old_fb)
> +               drm_framebuffer_unreference(old_fb);
> +
> +       return ret;
> +}
>

<snip>

> +int drm_plane_set_property(struct drm_plane *plane,
> +               struct drm_plane_state *state,
> +               struct drm_property *property,
> +               uint64_t value, void *blob_data)
> +{
> +       struct drm_device *dev = plane->dev;
> +       struct drm_mode_config *config = &dev->mode_config;
> +
> +       drm_object_property_set_value(&plane->base,
> +                       &state->propvals, property, value, blob_data);
> +
> +       if (property == config->prop_fb_id) {
> +               state->new_fb = true;
> +               state->fb = drm_framebuffer_lookup(dev, value);
> +       } else if (property == config->prop_crtc_id) {
> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
> +               /* take the lock of the incoming crtc as well, moving
> +                * plane between crtcs is synchronized on both incoming
> +                * and outgoing crtc.
> +                */
> +               if (crtc) {
> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
> +                       if (ret)
> +                               return ret;
> +               }
> +               state->crtc = crtc;
> +       } else if (property == config->prop_crtc_x) {
> +               state->crtc_x = U642I64(value);
> +       } else if (property == config->prop_crtc_y) {
> +               state->crtc_y = U642I64(value);
> +       } else if (property == config->prop_crtc_w) {
> +               state->crtc_w = value;
> +       } else if (property == config->prop_crtc_h) {
> +               state->crtc_h = value;
> +       } else if (property == config->prop_src_x) {
> +               state->src_x = value;
> +       } else if (property == config->prop_src_y) {
> +               state->src_y = value;
> +       } else if (property == config->prop_src_w) {
> +               state->src_w = value;
> +       } else if (property == config->prop_src_h) {
> +               state->src_h = value;
> +       } else {
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_set_property);
> +

<snip>

> @@ -1987,20 +2192,19 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>                         struct drm_file *file_priv)
>  {
>         struct drm_mode_set_plane *plane_req = data;
> +       struct drm_mode_config *config = &dev->mode_config;
>         struct drm_plane *plane;
> -       struct drm_crtc *crtc;
> -       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
> +       void *state;
>         int ret = 0;
> -       unsigned int fb_width, fb_height;
> -       int i;
>
>         if (!drm_core_check_feature(dev, DRIVER_MODESET))
>                 return -EINVAL;
>
> -       /*
> -        * First, find the plane, crtc, and fb objects.  If not available,
> -        * we don't bother to call the driver.
> -        */
> +retry:
> +       state = dev->driver->atomic_begin(dev, 0);
> +       if (IS_ERR(state))
> +               return PTR_ERR(state);
> +
>         plane = drm_plane_find(dev, plane_req->plane_id);
>         if (!plane) {
>                 DRM_DEBUG_KMS("Unknown plane ID %d\n",
> @@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>                 return -ENOENT;
>         }
>
> -       /* No fb means shut it down */
> -       if (!plane_req->fb_id) {
> -               drm_modeset_lock_all(dev);
> -               old_fb = plane->fb;
> -               plane->funcs->disable_plane(plane);
> -               plane->crtc = NULL;
> -               plane->fb = NULL;
> -               drm_modeset_unlock_all(dev);
> -               goto out;
> -       }
> -
> -       crtc = drm_crtc_find(dev, plane_req->crtc_id);
> -       if (!crtc) {
> -               DRM_DEBUG_KMS("Unknown crtc ID %d\n",
> -                             plane_req->crtc_id);
> -               ret = -ENOENT;
> -               goto out;
> -       }
> -
> -       fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
> -       if (!fb) {
> -               DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
> -                             plane_req->fb_id);
> -               ret = -ENOENT;
> -               goto out;
> -       }
> -
> -       /* Check whether this plane supports the fb pixel format. */
> -       for (i = 0; i < plane->format_count; i++)
> -               if (fb->pixel_format == plane->format_types[i])
> -                       break;
> -       if (i == plane->format_count) {
> -               DRM_DEBUG_KMS("Invalid pixel format %s\n",
> -                             drm_get_format_name(fb->pixel_format));
> -               ret = -EINVAL;
> -               goto out;
> -       }
> -
> -       fb_width = fb->width << 16;
> -       fb_height = fb->height << 16;
> -
> -       /* Make sure source coordinates are inside the fb. */
> -       if (plane_req->src_w > fb_width ||
> -           plane_req->src_x > fb_width - plane_req->src_w ||
> -           plane_req->src_h > fb_height ||
> -           plane_req->src_y > fb_height - plane_req->src_h) {
> -               DRM_DEBUG_KMS("Invalid source coordinates "
> -                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
> -                             plane_req->src_w >> 16,
> -                             ((plane_req->src_w & 0xffff) * 15625) >> 10,
> -                             plane_req->src_h >> 16,
> -                             ((plane_req->src_h & 0xffff) * 15625) >> 10,
> -                             plane_req->src_x >> 16,
> -                             ((plane_req->src_x & 0xffff) * 15625) >> 10,
> -                             plane_req->src_y >> 16,
> -                             ((plane_req->src_y & 0xffff) * 15625) >> 10);
> -               ret = -ENOSPC;
> -               goto out;
> -       }
> -
> -       /* Give drivers some help against integer overflows */
> -       if (plane_req->crtc_w > INT_MAX ||
> -           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
> -           plane_req->crtc_h > INT_MAX ||
> -           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
> -               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
> -                             plane_req->crtc_w, plane_req->crtc_h,
> -                             plane_req->crtc_x, plane_req->crtc_y);
> -               ret = -ERANGE;
> +       ret =
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_crtc_id, plane_req->crtc_id, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_fb_id, plane_req->fb_id, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_crtc_w, plane_req->crtc_w, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_crtc_h, plane_req->crtc_h, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_src_w, plane_req->src_w, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_src_h, plane_req->src_h, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_src_x, plane_req->src_x, NULL) ||
> +               drm_mode_plane_set_obj_prop(plane, state,
> +                       config->prop_src_y, plane_req->src_y, NULL) ||
> +               dev->driver->atomic_check(dev, state);
> +       if (ret)
>                 goto out;
> -       }
>
> -       drm_modeset_lock_all(dev);
> -       ret = plane->funcs->update_plane(plane, crtc, fb,
> -                                        plane_req->crtc_x, plane_req->crtc_y,
> -                                        plane_req->crtc_w, plane_req->crtc_h,
> -                                        plane_req->src_x, plane_req->src_y,
> -                                        plane_req->src_w, plane_req->src_h);
> -       if (!ret) {
> -               old_fb = plane->fb;
> -               plane->crtc = crtc;
> -               plane->fb = fb;
> -               fb = NULL;
> -       }
> -       drm_modeset_unlock_all(dev);
> +       ret = dev->driver->atomic_commit(dev, state);
>
>  out:
> -       if (fb)
> -               drm_framebuffer_unreference(fb);
> -       if (old_fb)
> -               drm_framebuffer_unreference(old_fb);
> -
> +       dev->driver->atomic_end(dev, state);
> +       if (ret == -EDEADLK)
> +               goto retry;
>         return ret;
>  }
>

<snip>

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-02-26 21:30   ` Sean Paul
@ 2014-02-27  0:18     ` Rob Clark
  2014-03-03 19:22       ` Sean Paul
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Clark @ 2014-02-27  0:18 UTC (permalink / raw)
  To: Sean Paul; +Cc: dri-devel

On Wed, Feb 26, 2014 at 4:30 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>> Break the mutable state of a plane out into a separate structure
>> and use atomic properties mechanism to set plane attributes.  This
>> makes it easier to have some helpers for plane->set_property()
>> and for checking for invalid params.  The idea is that individual
>> drivers can wrap the state struct in their own struct which adds
>> driver specific parameters, for easy build-up of state across
>> multiple set_property() calls and for easy atomic commit or roll-
>> back.
>>
>> The same should be done for CRTC, encoder, and connector, but this
>> patch only includes the first part (plane).
>
>
> Hi Rob,
> I've been tracking down a crash that came up on my exynos board when I
> applied this patch. I hope you can hold my hand a little bit and give
> some guidance.
>
> <snip>
>
>> +static int
>> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
>> +               struct drm_plane_state *pstate)
>> +{
>> +       struct drm_framebuffer *old_fb = NULL, *fb = NULL;
>> +       int ret = 0;
>> +
>> +       fb = pstate->fb;
>> +
>> +       if (pstate->crtc && fb) {
>> +               ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
>> +                       pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
>> +                       pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
>> +               if (!ret) {
>> +                       /* on success, update state and fb refcnting: */
>> +                       /* NOTE: if we ensure no driver sets plane->state->fb = NULL
>> +                        * on disable, we can move this up a level and not duplicate
>> +                        * nearly the same thing for both update_plane and disable_plane
>> +                        * cases..  I leave it like this for now to be paranoid due to
>> +                        * the slightly different ordering in the two cases in the
>> +                        * original code.
>> +                        */
>> +                       old_fb = plane->state->fb;
>
> I think this is slightly different than what we had before in
> setplane. In the previous code, we had:
>
> ret = plane->funcs->update_plane(plane, crtc, fb,
>                                                   plane_req->crtc_x,
> plane_req->crtc_y,
>                                                   plane_req->crtc_w,
> plane_req->crtc_h,
>                                                   plane_req->src_x,
> plane_req->src_y,
>                                                   plane_req->src_w,
> plane_req->src_h);
> if (!ret) {
>         old_fb = plane->fb;
>         fb = NULL;
>         plane->crtc = crtc;
>         plane->fb = fb;
> }
>
> In this case, we'd unreference old_fb which is the previous plane->fb.
> AFAICT, update_plane did not set plane->fb to fb, so it would, in
> fact, be the old plane.

to be honest, I need to dig up that branch again and remember (and
rebase it while I'm at it)..

I don't think the driver should be changing state->fb (ie. the state
object should in theory, once constructed, be a sort of constant
thing).  So until swap_plane_state() plane->state->fb is *supposed* to
be the old fb.

For crtc, as there were so many places in code accessing crtc->fb (and
it was pretty intertwined in the crtc helpers), the way I left it as:
crtc->state->fb is what is requested by userspace, and crtc->fb is
what is currently used (which is updated to the new cstate->fb in the
course of modeset).

I don't remember if I left it the same way for plane (which isn't
referenced in nearly as many places, and which doesn't have a big
chunk of modeset helper to care about).

> In the new code, drm_mode_setplane calls drm_mode_plane_set_obj_prop
> on fb_id, which will update plane->state->fb. Then we come in here and
> unreference it as old_fb. I _believe_ we're taking one more reference
> than we need in this case.

well, it should be updating 'pstate', what will become the new
plane->state..  But not the current plane->state.

BR,
-R

> What I'm seeing in exynos is that when we setplane to an fb, we leave
> it with refcount == 1. If we disable that plane, we'll free the fb.
> Unfortunately, this leaves the fb in the fbs list and the next time we
> try to iterate that list Bad Things Happen.
>
> Does this make any sense?
>
> Sean
>
>> +                       swap_plane_state(plane, pstate->state);
>> +                       fb = NULL;
>> +               }
>> +       } else {
>> +               old_fb = plane->state->fb;
>> +               plane->funcs->disable_plane(plane);
>> +               swap_plane_state(plane, pstate->state);
>> +       }
>> +
>> +
>> +       if (fb)
>> +               drm_framebuffer_unreference(fb);
>> +       if (old_fb)
>> +               drm_framebuffer_unreference(old_fb);
>> +
>> +       return ret;
>> +}
>>
>
> <snip>
>
>> +int drm_plane_set_property(struct drm_plane *plane,
>> +               struct drm_plane_state *state,
>> +               struct drm_property *property,
>> +               uint64_t value, void *blob_data)
>> +{
>> +       struct drm_device *dev = plane->dev;
>> +       struct drm_mode_config *config = &dev->mode_config;
>> +
>> +       drm_object_property_set_value(&plane->base,
>> +                       &state->propvals, property, value, blob_data);
>> +
>> +       if (property == config->prop_fb_id) {
>> +               state->new_fb = true;
>> +               state->fb = drm_framebuffer_lookup(dev, value);
>> +       } else if (property == config->prop_crtc_id) {
>> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
>> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>> +               /* take the lock of the incoming crtc as well, moving
>> +                * plane between crtcs is synchronized on both incoming
>> +                * and outgoing crtc.
>> +                */
>> +               if (crtc) {
>> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
>> +                       if (ret)
>> +                               return ret;
>> +               }
>> +               state->crtc = crtc;
>> +       } else if (property == config->prop_crtc_x) {
>> +               state->crtc_x = U642I64(value);
>> +       } else if (property == config->prop_crtc_y) {
>> +               state->crtc_y = U642I64(value);
>> +       } else if (property == config->prop_crtc_w) {
>> +               state->crtc_w = value;
>> +       } else if (property == config->prop_crtc_h) {
>> +               state->crtc_h = value;
>> +       } else if (property == config->prop_src_x) {
>> +               state->src_x = value;
>> +       } else if (property == config->prop_src_y) {
>> +               state->src_y = value;
>> +       } else if (property == config->prop_src_w) {
>> +               state->src_w = value;
>> +       } else if (property == config->prop_src_h) {
>> +               state->src_h = value;
>> +       } else {
>> +               return -EINVAL;
>> +       }
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(drm_plane_set_property);
>> +
>
> <snip>
>
>> @@ -1987,20 +2192,19 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>                         struct drm_file *file_priv)
>>  {
>>         struct drm_mode_set_plane *plane_req = data;
>> +       struct drm_mode_config *config = &dev->mode_config;
>>         struct drm_plane *plane;
>> -       struct drm_crtc *crtc;
>> -       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>> +       void *state;
>>         int ret = 0;
>> -       unsigned int fb_width, fb_height;
>> -       int i;
>>
>>         if (!drm_core_check_feature(dev, DRIVER_MODESET))
>>                 return -EINVAL;
>>
>> -       /*
>> -        * First, find the plane, crtc, and fb objects.  If not available,
>> -        * we don't bother to call the driver.
>> -        */
>> +retry:
>> +       state = dev->driver->atomic_begin(dev, 0);
>> +       if (IS_ERR(state))
>> +               return PTR_ERR(state);
>> +
>>         plane = drm_plane_find(dev, plane_req->plane_id);
>>         if (!plane) {
>>                 DRM_DEBUG_KMS("Unknown plane ID %d\n",
>> @@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>                 return -ENOENT;
>>         }
>>
>> -       /* No fb means shut it down */
>> -       if (!plane_req->fb_id) {
>> -               drm_modeset_lock_all(dev);
>> -               old_fb = plane->fb;
>> -               plane->funcs->disable_plane(plane);
>> -               plane->crtc = NULL;
>> -               plane->fb = NULL;
>> -               drm_modeset_unlock_all(dev);
>> -               goto out;
>> -       }
>> -
>> -       crtc = drm_crtc_find(dev, plane_req->crtc_id);
>> -       if (!crtc) {
>> -               DRM_DEBUG_KMS("Unknown crtc ID %d\n",
>> -                             plane_req->crtc_id);
>> -               ret = -ENOENT;
>> -               goto out;
>> -       }
>> -
>> -       fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
>> -       if (!fb) {
>> -               DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
>> -                             plane_req->fb_id);
>> -               ret = -ENOENT;
>> -               goto out;
>> -       }
>> -
>> -       /* Check whether this plane supports the fb pixel format. */
>> -       for (i = 0; i < plane->format_count; i++)
>> -               if (fb->pixel_format == plane->format_types[i])
>> -                       break;
>> -       if (i == plane->format_count) {
>> -               DRM_DEBUG_KMS("Invalid pixel format %s\n",
>> -                             drm_get_format_name(fb->pixel_format));
>> -               ret = -EINVAL;
>> -               goto out;
>> -       }
>> -
>> -       fb_width = fb->width << 16;
>> -       fb_height = fb->height << 16;
>> -
>> -       /* Make sure source coordinates are inside the fb. */
>> -       if (plane_req->src_w > fb_width ||
>> -           plane_req->src_x > fb_width - plane_req->src_w ||
>> -           plane_req->src_h > fb_height ||
>> -           plane_req->src_y > fb_height - plane_req->src_h) {
>> -               DRM_DEBUG_KMS("Invalid source coordinates "
>> -                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>> -                             plane_req->src_w >> 16,
>> -                             ((plane_req->src_w & 0xffff) * 15625) >> 10,
>> -                             plane_req->src_h >> 16,
>> -                             ((plane_req->src_h & 0xffff) * 15625) >> 10,
>> -                             plane_req->src_x >> 16,
>> -                             ((plane_req->src_x & 0xffff) * 15625) >> 10,
>> -                             plane_req->src_y >> 16,
>> -                             ((plane_req->src_y & 0xffff) * 15625) >> 10);
>> -               ret = -ENOSPC;
>> -               goto out;
>> -       }
>> -
>> -       /* Give drivers some help against integer overflows */
>> -       if (plane_req->crtc_w > INT_MAX ||
>> -           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
>> -           plane_req->crtc_h > INT_MAX ||
>> -           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
>> -               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>> -                             plane_req->crtc_w, plane_req->crtc_h,
>> -                             plane_req->crtc_x, plane_req->crtc_y);
>> -               ret = -ERANGE;
>> +       ret =
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_id, plane_req->crtc_id, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_fb_id, plane_req->fb_id, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_w, plane_req->crtc_w, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_h, plane_req->crtc_h, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_w, plane_req->src_w, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_h, plane_req->src_h, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_x, plane_req->src_x, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_y, plane_req->src_y, NULL) ||
>> +               dev->driver->atomic_check(dev, state);
>> +       if (ret)
>>                 goto out;
>> -       }
>>
>> -       drm_modeset_lock_all(dev);
>> -       ret = plane->funcs->update_plane(plane, crtc, fb,
>> -                                        plane_req->crtc_x, plane_req->crtc_y,
>> -                                        plane_req->crtc_w, plane_req->crtc_h,
>> -                                        plane_req->src_x, plane_req->src_y,
>> -                                        plane_req->src_w, plane_req->src_h);
>> -       if (!ret) {
>> -               old_fb = plane->fb;
>> -               plane->crtc = crtc;
>> -               plane->fb = fb;
>> -               fb = NULL;
>> -       }
>> -       drm_modeset_unlock_all(dev);
>> +       ret = dev->driver->atomic_commit(dev, state);
>>
>>  out:
>> -       if (fb)
>> -               drm_framebuffer_unreference(fb);
>> -       if (old_fb)
>> -               drm_framebuffer_unreference(old_fb);
>> -
>> +       dev->driver->atomic_end(dev, state);
>> +       if (ret == -EDEADLK)
>> +               goto retry;
>>         return ret;
>>  }
>>
>
> <snip>

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-02-27  0:18     ` Rob Clark
@ 2014-03-03 19:22       ` Sean Paul
  2014-03-03 19:40         ` Rob Clark
  0 siblings, 1 reply; 32+ messages in thread
From: Sean Paul @ 2014-03-03 19:22 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Wed, Feb 26, 2014 at 7:18 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Wed, Feb 26, 2014 at 4:30 PM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> Break the mutable state of a plane out into a separate structure
>>> and use atomic properties mechanism to set plane attributes.  This
>>> makes it easier to have some helpers for plane->set_property()
>>> and for checking for invalid params.  The idea is that individual
>>> drivers can wrap the state struct in their own struct which adds
>>> driver specific parameters, for easy build-up of state across
>>> multiple set_property() calls and for easy atomic commit or roll-
>>> back.
>>>
>>> The same should be done for CRTC, encoder, and connector, but this
>>> patch only includes the first part (plane).
>>
>>
>> Hi Rob,
>> I've been tracking down a crash that came up on my exynos board when I
>> applied this patch. I hope you can hold my hand a little bit and give
>> some guidance.
>>
>> <snip>
>>
>>> +static int
>>> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
>>> +               struct drm_plane_state *pstate)
>>> +{
>>> +       struct drm_framebuffer *old_fb = NULL, *fb = NULL;
>>> +       int ret = 0;
>>> +
>>> +       fb = pstate->fb;
>>> +
>>> +       if (pstate->crtc && fb) {
>>> +               ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
>>> +                       pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
>>> +                       pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
>>> +               if (!ret) {
>>> +                       /* on success, update state and fb refcnting: */
>>> +                       /* NOTE: if we ensure no driver sets plane->state->fb = NULL
>>> +                        * on disable, we can move this up a level and not duplicate
>>> +                        * nearly the same thing for both update_plane and disable_plane
>>> +                        * cases..  I leave it like this for now to be paranoid due to
>>> +                        * the slightly different ordering in the two cases in the
>>> +                        * original code.
>>> +                        */
>>> +                       old_fb = plane->state->fb;
>>
>> I think this is slightly different than what we had before in
>> setplane. In the previous code, we had:
>>
>> ret = plane->funcs->update_plane(plane, crtc, fb,
>>                                                   plane_req->crtc_x,
>> plane_req->crtc_y,
>>                                                   plane_req->crtc_w,
>> plane_req->crtc_h,
>>                                                   plane_req->src_x,
>> plane_req->src_y,
>>                                                   plane_req->src_w,
>> plane_req->src_h);
>> if (!ret) {
>>         old_fb = plane->fb;
>>         fb = NULL;
>>         plane->crtc = crtc;
>>         plane->fb = fb;
>> }
>>
>> In this case, we'd unreference old_fb which is the previous plane->fb.
>> AFAICT, update_plane did not set plane->fb to fb, so it would, in
>> fact, be the old plane.
>
> to be honest, I need to dig up that branch again and remember (and
> rebase it while I'm at it)..
>
> I don't think the driver should be changing state->fb (ie. the state
> object should in theory, once constructed, be a sort of constant
> thing).  So until swap_plane_state() plane->state->fb is *supposed* to
> be the old fb.
>

Hey Rob,
Sorry for the delay getting back to you. Indeed, I was confused in my
last email, thanks for straightening me out.

I traced through things a little more carefully and it looks like my
fb is being unreferenced every time we call set_property on the plane.
On exynos, we set a private zpos property via the set_property ioctl.
In this case, drm_mode_set_property_ioctl does not take a reference on
the plane's current fb, but we put a reference in atomic_commit
(assuming it's got a valid crtc/fb). Eventually, this runs the
refcount down to 0 and we end up freeing the fb early.

I think the fix here is to only unreference the plane's current fb in
the case where new_fb is true. ie:

diff --git a/drivers/gpu/drm/drm_atomic_helper.c
b/drivers/gpu/drm/drm_atomic_helper.c
index 14e0571..ec60d4e 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -394,7 +394,8 @@ drm_atomic_helper_commit_plane_state(struct
drm_plane *plane,
                         * the slightly different ordering in the two
cases in the
                         * original code.
                         */
-                       old_fb = plane->state->fb;
+                       if (pstate->new_fb)
+                               old_fb = plane->state->fb;
                        swap_plane_state(plane, pstate->state);
                        fb = NULL;
                }

Seem reasonable?

Sean



> For crtc, as there were so many places in code accessing crtc->fb (and
> it was pretty intertwined in the crtc helpers), the way I left it as:
> crtc->state->fb is what is requested by userspace, and crtc->fb is
> what is currently used (which is updated to the new cstate->fb in the
> course of modeset).
>
> I don't remember if I left it the same way for plane (which isn't
> referenced in nearly as many places, and which doesn't have a big
> chunk of modeset helper to care about).
>
>> In the new code, drm_mode_setplane calls drm_mode_plane_set_obj_prop
>> on fb_id, which will update plane->state->fb. Then we come in here and
>> unreference it as old_fb. I _believe_ we're taking one more reference
>> than we need in this case.
>
> well, it should be updating 'pstate', what will become the new
> plane->state..  But not the current plane->state.
>
> BR,
> -R
>
>> What I'm seeing in exynos is that when we setplane to an fb, we leave
>> it with refcount == 1. If we disable that plane, we'll free the fb.
>> Unfortunately, this leaves the fb in the fbs list and the next time we
>> try to iterate that list Bad Things Happen.
>>
>> Does this make any sense?
>>
>> Sean
>>
>>> +                       swap_plane_state(plane, pstate->state);
>>> +                       fb = NULL;
>>> +               }
>>> +       } else {
>>> +               old_fb = plane->state->fb;
>>> +               plane->funcs->disable_plane(plane);
>>> +               swap_plane_state(plane, pstate->state);
>>> +       }
>>> +
>>> +
>>> +       if (fb)
>>> +               drm_framebuffer_unreference(fb);
>>> +       if (old_fb)
>>> +               drm_framebuffer_unreference(old_fb);
>>> +
>>> +       return ret;
>>> +}
>>>
>>
>> <snip>
>>
>>> +int drm_plane_set_property(struct drm_plane *plane,
>>> +               struct drm_plane_state *state,
>>> +               struct drm_property *property,
>>> +               uint64_t value, void *blob_data)
>>> +{
>>> +       struct drm_device *dev = plane->dev;
>>> +       struct drm_mode_config *config = &dev->mode_config;
>>> +
>>> +       drm_object_property_set_value(&plane->base,
>>> +                       &state->propvals, property, value, blob_data);
>>> +
>>> +       if (property == config->prop_fb_id) {
>>> +               state->new_fb = true;
>>> +               state->fb = drm_framebuffer_lookup(dev, value);
>>> +       } else if (property == config->prop_crtc_id) {
>>> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
>>> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>>> +               /* take the lock of the incoming crtc as well, moving
>>> +                * plane between crtcs is synchronized on both incoming
>>> +                * and outgoing crtc.
>>> +                */
>>> +               if (crtc) {
>>> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
>>> +                       if (ret)
>>> +                               return ret;
>>> +               }
>>> +               state->crtc = crtc;
>>> +       } else if (property == config->prop_crtc_x) {
>>> +               state->crtc_x = U642I64(value);
>>> +       } else if (property == config->prop_crtc_y) {
>>> +               state->crtc_y = U642I64(value);
>>> +       } else if (property == config->prop_crtc_w) {
>>> +               state->crtc_w = value;
>>> +       } else if (property == config->prop_crtc_h) {
>>> +               state->crtc_h = value;
>>> +       } else if (property == config->prop_src_x) {
>>> +               state->src_x = value;
>>> +       } else if (property == config->prop_src_y) {
>>> +               state->src_y = value;
>>> +       } else if (property == config->prop_src_w) {
>>> +               state->src_w = value;
>>> +       } else if (property == config->prop_src_h) {
>>> +               state->src_h = value;
>>> +       } else {
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(drm_plane_set_property);
>>> +
>>
>> <snip>
>>
>>> @@ -1987,20 +2192,19 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>>                         struct drm_file *file_priv)
>>>  {
>>>         struct drm_mode_set_plane *plane_req = data;
>>> +       struct drm_mode_config *config = &dev->mode_config;
>>>         struct drm_plane *plane;
>>> -       struct drm_crtc *crtc;
>>> -       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>>> +       void *state;
>>>         int ret = 0;
>>> -       unsigned int fb_width, fb_height;
>>> -       int i;
>>>
>>>         if (!drm_core_check_feature(dev, DRIVER_MODESET))
>>>                 return -EINVAL;
>>>
>>> -       /*
>>> -        * First, find the plane, crtc, and fb objects.  If not available,
>>> -        * we don't bother to call the driver.
>>> -        */
>>> +retry:
>>> +       state = dev->driver->atomic_begin(dev, 0);
>>> +       if (IS_ERR(state))
>>> +               return PTR_ERR(state);
>>> +
>>>         plane = drm_plane_find(dev, plane_req->plane_id);
>>>         if (!plane) {
>>>                 DRM_DEBUG_KMS("Unknown plane ID %d\n",
>>> @@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>>                 return -ENOENT;
>>>         }
>>>
>>> -       /* No fb means shut it down */
>>> -       if (!plane_req->fb_id) {
>>> -               drm_modeset_lock_all(dev);
>>> -               old_fb = plane->fb;
>>> -               plane->funcs->disable_plane(plane);
>>> -               plane->crtc = NULL;
>>> -               plane->fb = NULL;
>>> -               drm_modeset_unlock_all(dev);
>>> -               goto out;
>>> -       }
>>> -
>>> -       crtc = drm_crtc_find(dev, plane_req->crtc_id);
>>> -       if (!crtc) {
>>> -               DRM_DEBUG_KMS("Unknown crtc ID %d\n",
>>> -                             plane_req->crtc_id);
>>> -               ret = -ENOENT;
>>> -               goto out;
>>> -       }
>>> -
>>> -       fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
>>> -       if (!fb) {
>>> -               DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
>>> -                             plane_req->fb_id);
>>> -               ret = -ENOENT;
>>> -               goto out;
>>> -       }
>>> -
>>> -       /* Check whether this plane supports the fb pixel format. */
>>> -       for (i = 0; i < plane->format_count; i++)
>>> -               if (fb->pixel_format == plane->format_types[i])
>>> -                       break;
>>> -       if (i == plane->format_count) {
>>> -               DRM_DEBUG_KMS("Invalid pixel format %s\n",
>>> -                             drm_get_format_name(fb->pixel_format));
>>> -               ret = -EINVAL;
>>> -               goto out;
>>> -       }
>>> -
>>> -       fb_width = fb->width << 16;
>>> -       fb_height = fb->height << 16;
>>> -
>>> -       /* Make sure source coordinates are inside the fb. */
>>> -       if (plane_req->src_w > fb_width ||
>>> -           plane_req->src_x > fb_width - plane_req->src_w ||
>>> -           plane_req->src_h > fb_height ||
>>> -           plane_req->src_y > fb_height - plane_req->src_h) {
>>> -               DRM_DEBUG_KMS("Invalid source coordinates "
>>> -                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>>> -                             plane_req->src_w >> 16,
>>> -                             ((plane_req->src_w & 0xffff) * 15625) >> 10,
>>> -                             plane_req->src_h >> 16,
>>> -                             ((plane_req->src_h & 0xffff) * 15625) >> 10,
>>> -                             plane_req->src_x >> 16,
>>> -                             ((plane_req->src_x & 0xffff) * 15625) >> 10,
>>> -                             plane_req->src_y >> 16,
>>> -                             ((plane_req->src_y & 0xffff) * 15625) >> 10);
>>> -               ret = -ENOSPC;
>>> -               goto out;
>>> -       }
>>> -
>>> -       /* Give drivers some help against integer overflows */
>>> -       if (plane_req->crtc_w > INT_MAX ||
>>> -           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
>>> -           plane_req->crtc_h > INT_MAX ||
>>> -           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
>>> -               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>>> -                             plane_req->crtc_w, plane_req->crtc_h,
>>> -                             plane_req->crtc_x, plane_req->crtc_y);
>>> -               ret = -ERANGE;
>>> +       ret =
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_crtc_id, plane_req->crtc_id, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_fb_id, plane_req->fb_id, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_crtc_w, plane_req->crtc_w, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_crtc_h, plane_req->crtc_h, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_src_w, plane_req->src_w, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_src_h, plane_req->src_h, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_src_x, plane_req->src_x, NULL) ||
>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>> +                       config->prop_src_y, plane_req->src_y, NULL) ||
>>> +               dev->driver->atomic_check(dev, state);
>>> +       if (ret)
>>>                 goto out;
>>> -       }
>>>
>>> -       drm_modeset_lock_all(dev);
>>> -       ret = plane->funcs->update_plane(plane, crtc, fb,
>>> -                                        plane_req->crtc_x, plane_req->crtc_y,
>>> -                                        plane_req->crtc_w, plane_req->crtc_h,
>>> -                                        plane_req->src_x, plane_req->src_y,
>>> -                                        plane_req->src_w, plane_req->src_h);
>>> -       if (!ret) {
>>> -               old_fb = plane->fb;
>>> -               plane->crtc = crtc;
>>> -               plane->fb = fb;
>>> -               fb = NULL;
>>> -       }
>>> -       drm_modeset_unlock_all(dev);
>>> +       ret = dev->driver->atomic_commit(dev, state);
>>>
>>>  out:
>>> -       if (fb)
>>> -               drm_framebuffer_unreference(fb);
>>> -       if (old_fb)
>>> -               drm_framebuffer_unreference(old_fb);
>>> -
>>> +       dev->driver->atomic_end(dev, state);
>>> +       if (ret == -EDEADLK)
>>> +               goto retry;
>>>         return ret;
>>>  }
>>>
>>
>> <snip>

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-03-03 19:22       ` Sean Paul
@ 2014-03-03 19:40         ` Rob Clark
  0 siblings, 0 replies; 32+ messages in thread
From: Rob Clark @ 2014-03-03 19:40 UTC (permalink / raw)
  To: Sean Paul; +Cc: dri-devel

On Mon, Mar 3, 2014 at 2:22 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Wed, Feb 26, 2014 at 7:18 PM, Rob Clark <robdclark@gmail.com> wrote:
>> On Wed, Feb 26, 2014 at 4:30 PM, Sean Paul <seanpaul@chromium.org> wrote:
>>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>>> Break the mutable state of a plane out into a separate structure
>>>> and use atomic properties mechanism to set plane attributes.  This
>>>> makes it easier to have some helpers for plane->set_property()
>>>> and for checking for invalid params.  The idea is that individual
>>>> drivers can wrap the state struct in their own struct which adds
>>>> driver specific parameters, for easy build-up of state across
>>>> multiple set_property() calls and for easy atomic commit or roll-
>>>> back.
>>>>
>>>> The same should be done for CRTC, encoder, and connector, but this
>>>> patch only includes the first part (plane).
>>>
>>>
>>> Hi Rob,
>>> I've been tracking down a crash that came up on my exynos board when I
>>> applied this patch. I hope you can hold my hand a little bit and give
>>> some guidance.
>>>
>>> <snip>
>>>
>>>> +static int
>>>> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
>>>> +               struct drm_plane_state *pstate)
>>>> +{
>>>> +       struct drm_framebuffer *old_fb = NULL, *fb = NULL;
>>>> +       int ret = 0;
>>>> +
>>>> +       fb = pstate->fb;
>>>> +
>>>> +       if (pstate->crtc && fb) {
>>>> +               ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb,
>>>> +                       pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h,
>>>> +                       pstate->src_x,  pstate->src_y,  pstate->src_w,  pstate->src_h);
>>>> +               if (!ret) {
>>>> +                       /* on success, update state and fb refcnting: */
>>>> +                       /* NOTE: if we ensure no driver sets plane->state->fb = NULL
>>>> +                        * on disable, we can move this up a level and not duplicate
>>>> +                        * nearly the same thing for both update_plane and disable_plane
>>>> +                        * cases..  I leave it like this for now to be paranoid due to
>>>> +                        * the slightly different ordering in the two cases in the
>>>> +                        * original code.
>>>> +                        */
>>>> +                       old_fb = plane->state->fb;
>>>
>>> I think this is slightly different than what we had before in
>>> setplane. In the previous code, we had:
>>>
>>> ret = plane->funcs->update_plane(plane, crtc, fb,
>>>                                                   plane_req->crtc_x,
>>> plane_req->crtc_y,
>>>                                                   plane_req->crtc_w,
>>> plane_req->crtc_h,
>>>                                                   plane_req->src_x,
>>> plane_req->src_y,
>>>                                                   plane_req->src_w,
>>> plane_req->src_h);
>>> if (!ret) {
>>>         old_fb = plane->fb;
>>>         fb = NULL;
>>>         plane->crtc = crtc;
>>>         plane->fb = fb;
>>> }
>>>
>>> In this case, we'd unreference old_fb which is the previous plane->fb.
>>> AFAICT, update_plane did not set plane->fb to fb, so it would, in
>>> fact, be the old plane.
>>
>> to be honest, I need to dig up that branch again and remember (and
>> rebase it while I'm at it)..
>>
>> I don't think the driver should be changing state->fb (ie. the state
>> object should in theory, once constructed, be a sort of constant
>> thing).  So until swap_plane_state() plane->state->fb is *supposed* to
>> be the old fb.
>>
>
> Hey Rob,
> Sorry for the delay getting back to you. Indeed, I was confused in my
> last email, thanks for straightening me out.
>
> I traced through things a little more carefully and it looks like my
> fb is being unreferenced every time we call set_property on the plane.
> On exynos, we set a private zpos property via the set_property ioctl.
> In this case, drm_mode_set_property_ioctl does not take a reference on
> the plane's current fb, but we put a reference in atomic_commit
> (assuming it's got a valid crtc/fb). Eventually, this runs the
> refcount down to 0 and we end up freeing the fb early.
>
> I think the fix here is to only unreference the plane's current fb in
> the case where new_fb is true. ie:
>
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c
> index 14e0571..ec60d4e 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -394,7 +394,8 @@ drm_atomic_helper_commit_plane_state(struct
> drm_plane *plane,
>                          * the slightly different ordering in the two
> cases in the
>                          * original code.
>                          */
> -                       old_fb = plane->state->fb;
> +                       if (pstate->new_fb)
> +                               old_fb = plane->state->fb;
>                         swap_plane_state(plane, pstate->state);
>                         fb = NULL;
>                 }
>
> Seem reasonable?

I *think* so.. although to be safe you should probably ask me again
after I rebase atomic and have my head back in it..

tbh, I'm not completely happy with the way fb refcnt'ing is working at
the moment with atomic.  In particular to deal with edge cases like
userspace setting same fb property twice in one ioctl.  I'm thinking
about switching to have two ref's held to the fb, so the crtc/plane
holds one ref for active scanout buffer (as it does currently), and
the state object holds it's own ref.

BR,
-R

>
> Sean
>
>
>
>> For crtc, as there were so many places in code accessing crtc->fb (and
>> it was pretty intertwined in the crtc helpers), the way I left it as:
>> crtc->state->fb is what is requested by userspace, and crtc->fb is
>> what is currently used (which is updated to the new cstate->fb in the
>> course of modeset).
>>
>> I don't remember if I left it the same way for plane (which isn't
>> referenced in nearly as many places, and which doesn't have a big
>> chunk of modeset helper to care about).
>>
>>> In the new code, drm_mode_setplane calls drm_mode_plane_set_obj_prop
>>> on fb_id, which will update plane->state->fb. Then we come in here and
>>> unreference it as old_fb. I _believe_ we're taking one more reference
>>> than we need in this case.
>>
>> well, it should be updating 'pstate', what will become the new
>> plane->state..  But not the current plane->state.
>>
>> BR,
>> -R
>>
>>> What I'm seeing in exynos is that when we setplane to an fb, we leave
>>> it with refcount == 1. If we disable that plane, we'll free the fb.
>>> Unfortunately, this leaves the fb in the fbs list and the next time we
>>> try to iterate that list Bad Things Happen.
>>>
>>> Does this make any sense?
>>>
>>> Sean
>>>
>>>> +                       swap_plane_state(plane, pstate->state);
>>>> +                       fb = NULL;
>>>> +               }
>>>> +       } else {
>>>> +               old_fb = plane->state->fb;
>>>> +               plane->funcs->disable_plane(plane);
>>>> +               swap_plane_state(plane, pstate->state);
>>>> +       }
>>>> +
>>>> +
>>>> +       if (fb)
>>>> +               drm_framebuffer_unreference(fb);
>>>> +       if (old_fb)
>>>> +               drm_framebuffer_unreference(old_fb);
>>>> +
>>>> +       return ret;
>>>> +}
>>>>
>>>
>>> <snip>
>>>
>>>> +int drm_plane_set_property(struct drm_plane *plane,
>>>> +               struct drm_plane_state *state,
>>>> +               struct drm_property *property,
>>>> +               uint64_t value, void *blob_data)
>>>> +{
>>>> +       struct drm_device *dev = plane->dev;
>>>> +       struct drm_mode_config *config = &dev->mode_config;
>>>> +
>>>> +       drm_object_property_set_value(&plane->base,
>>>> +                       &state->propvals, property, value, blob_data);
>>>> +
>>>> +       if (property == config->prop_fb_id) {
>>>> +               state->new_fb = true;
>>>> +               state->fb = drm_framebuffer_lookup(dev, value);
>>>> +       } else if (property == config->prop_crtc_id) {
>>>> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
>>>> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>>>> +               /* take the lock of the incoming crtc as well, moving
>>>> +                * plane between crtcs is synchronized on both incoming
>>>> +                * and outgoing crtc.
>>>> +                */
>>>> +               if (crtc) {
>>>> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
>>>> +                       if (ret)
>>>> +                               return ret;
>>>> +               }
>>>> +               state->crtc = crtc;
>>>> +       } else if (property == config->prop_crtc_x) {
>>>> +               state->crtc_x = U642I64(value);
>>>> +       } else if (property == config->prop_crtc_y) {
>>>> +               state->crtc_y = U642I64(value);
>>>> +       } else if (property == config->prop_crtc_w) {
>>>> +               state->crtc_w = value;
>>>> +       } else if (property == config->prop_crtc_h) {
>>>> +               state->crtc_h = value;
>>>> +       } else if (property == config->prop_src_x) {
>>>> +               state->src_x = value;
>>>> +       } else if (property == config->prop_src_y) {
>>>> +               state->src_y = value;
>>>> +       } else if (property == config->prop_src_w) {
>>>> +               state->src_w = value;
>>>> +       } else if (property == config->prop_src_h) {
>>>> +               state->src_h = value;
>>>> +       } else {
>>>> +               return -EINVAL;
>>>> +       }
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +EXPORT_SYMBOL(drm_plane_set_property);
>>>> +
>>>
>>> <snip>
>>>
>>>> @@ -1987,20 +2192,19 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>>>                         struct drm_file *file_priv)
>>>>  {
>>>>         struct drm_mode_set_plane *plane_req = data;
>>>> +       struct drm_mode_config *config = &dev->mode_config;
>>>>         struct drm_plane *plane;
>>>> -       struct drm_crtc *crtc;
>>>> -       struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>>>> +       void *state;
>>>>         int ret = 0;
>>>> -       unsigned int fb_width, fb_height;
>>>> -       int i;
>>>>
>>>>         if (!drm_core_check_feature(dev, DRIVER_MODESET))
>>>>                 return -EINVAL;
>>>>
>>>> -       /*
>>>> -        * First, find the plane, crtc, and fb objects.  If not available,
>>>> -        * we don't bother to call the driver.
>>>> -        */
>>>> +retry:
>>>> +       state = dev->driver->atomic_begin(dev, 0);
>>>> +       if (IS_ERR(state))
>>>> +               return PTR_ERR(state);
>>>> +
>>>>         plane = drm_plane_find(dev, plane_req->plane_id);
>>>>         if (!plane) {
>>>>                 DRM_DEBUG_KMS("Unknown plane ID %d\n",
>>>> @@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>>>                 return -ENOENT;
>>>>         }
>>>>
>>>> -       /* No fb means shut it down */
>>>> -       if (!plane_req->fb_id) {
>>>> -               drm_modeset_lock_all(dev);
>>>> -               old_fb = plane->fb;
>>>> -               plane->funcs->disable_plane(plane);
>>>> -               plane->crtc = NULL;
>>>> -               plane->fb = NULL;
>>>> -               drm_modeset_unlock_all(dev);
>>>> -               goto out;
>>>> -       }
>>>> -
>>>> -       crtc = drm_crtc_find(dev, plane_req->crtc_id);
>>>> -       if (!crtc) {
>>>> -               DRM_DEBUG_KMS("Unknown crtc ID %d\n",
>>>> -                             plane_req->crtc_id);
>>>> -               ret = -ENOENT;
>>>> -               goto out;
>>>> -       }
>>>> -
>>>> -       fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
>>>> -       if (!fb) {
>>>> -               DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
>>>> -                             plane_req->fb_id);
>>>> -               ret = -ENOENT;
>>>> -               goto out;
>>>> -       }
>>>> -
>>>> -       /* Check whether this plane supports the fb pixel format. */
>>>> -       for (i = 0; i < plane->format_count; i++)
>>>> -               if (fb->pixel_format == plane->format_types[i])
>>>> -                       break;
>>>> -       if (i == plane->format_count) {
>>>> -               DRM_DEBUG_KMS("Invalid pixel format %s\n",
>>>> -                             drm_get_format_name(fb->pixel_format));
>>>> -               ret = -EINVAL;
>>>> -               goto out;
>>>> -       }
>>>> -
>>>> -       fb_width = fb->width << 16;
>>>> -       fb_height = fb->height << 16;
>>>> -
>>>> -       /* Make sure source coordinates are inside the fb. */
>>>> -       if (plane_req->src_w > fb_width ||
>>>> -           plane_req->src_x > fb_width - plane_req->src_w ||
>>>> -           plane_req->src_h > fb_height ||
>>>> -           plane_req->src_y > fb_height - plane_req->src_h) {
>>>> -               DRM_DEBUG_KMS("Invalid source coordinates "
>>>> -                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>>>> -                             plane_req->src_w >> 16,
>>>> -                             ((plane_req->src_w & 0xffff) * 15625) >> 10,
>>>> -                             plane_req->src_h >> 16,
>>>> -                             ((plane_req->src_h & 0xffff) * 15625) >> 10,
>>>> -                             plane_req->src_x >> 16,
>>>> -                             ((plane_req->src_x & 0xffff) * 15625) >> 10,
>>>> -                             plane_req->src_y >> 16,
>>>> -                             ((plane_req->src_y & 0xffff) * 15625) >> 10);
>>>> -               ret = -ENOSPC;
>>>> -               goto out;
>>>> -       }
>>>> -
>>>> -       /* Give drivers some help against integer overflows */
>>>> -       if (plane_req->crtc_w > INT_MAX ||
>>>> -           plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
>>>> -           plane_req->crtc_h > INT_MAX ||
>>>> -           plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
>>>> -               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>>>> -                             plane_req->crtc_w, plane_req->crtc_h,
>>>> -                             plane_req->crtc_x, plane_req->crtc_y);
>>>> -               ret = -ERANGE;
>>>> +       ret =
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_crtc_id, plane_req->crtc_id, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_fb_id, plane_req->fb_id, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_crtc_w, plane_req->crtc_w, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_crtc_h, plane_req->crtc_h, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_src_w, plane_req->src_w, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_src_h, plane_req->src_h, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_src_x, plane_req->src_x, NULL) ||
>>>> +               drm_mode_plane_set_obj_prop(plane, state,
>>>> +                       config->prop_src_y, plane_req->src_y, NULL) ||
>>>> +               dev->driver->atomic_check(dev, state);
>>>> +       if (ret)
>>>>                 goto out;
>>>> -       }
>>>>
>>>> -       drm_modeset_lock_all(dev);
>>>> -       ret = plane->funcs->update_plane(plane, crtc, fb,
>>>> -                                        plane_req->crtc_x, plane_req->crtc_y,
>>>> -                                        plane_req->crtc_w, plane_req->crtc_h,
>>>> -                                        plane_req->src_x, plane_req->src_y,
>>>> -                                        plane_req->src_w, plane_req->src_h);
>>>> -       if (!ret) {
>>>> -               old_fb = plane->fb;
>>>> -               plane->crtc = crtc;
>>>> -               plane->fb = fb;
>>>> -               fb = NULL;
>>>> -       }
>>>> -       drm_modeset_unlock_all(dev);
>>>> +       ret = dev->driver->atomic_commit(dev, state);
>>>>
>>>>  out:
>>>> -       if (fb)
>>>> -               drm_framebuffer_unreference(fb);
>>>> -       if (old_fb)
>>>> -               drm_framebuffer_unreference(old_fb);
>>>> -
>>>> +       dev->driver->atomic_end(dev, state);
>>>> +       if (ret == -EDEADLK)
>>>> +               goto retry;
>>>>         return ret;
>>>>  }
>>>>
>>>
>>> <snip>

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

* Re: [RFCv4 10/14] drm: convert crtc to properties/state
  2013-11-25 14:47 ` [RFCv4 10/14] drm: convert crtc " Rob Clark
  2013-12-11 21:48   ` Matt Roper
@ 2014-03-04 21:29   ` Sean Paul
  2014-03-04 22:04     ` Rob Clark
  1 sibling, 1 reply; 32+ messages in thread
From: Sean Paul @ 2014-03-04 21:29 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
> 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.
> ---

<snip>

> +
> +static int remove_connector(struct drm_crtc *ocrtc,
> +               struct drm_crtc_state *ostate, void *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, void *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);

Hi Rob,
This will populate state's placeholder for ocrtc, which will have the
unintended consequence of committing ocrtc's state and thus
unreferencing ocrtc's current fb in
drm_atomic_helper_commit_crtc_state.

Maybe a new transient state bit in drm_crtc_state which avoids the
commit_crtc_state call is in order?

> +               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,
> +                                       drm_get_connector_name(connector));
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +int drm_crtc_check_state(struct drm_crtc *crtc,
> +               struct drm_crtc_state *state)
> +{
> +       struct drm_framebuffer *fb = state->fb;
> +       int hdisplay, vdisplay;
> +       struct drm_display_mode *mode = get_mode(crtc, state);
> +
> +       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);
> +
> +       /* For some reason crtc x/y offsets are signed internally. */
> +       if (state->x > INT_MAX || state->y > INT_MAX)
> +               return -ERANGE;
> +
> +       if (hdisplay > fb->width ||
> +           vdisplay > fb->height ||
> +           state->x > fb->width - hdisplay ||
> +           state->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,
> +                             state->x, state->y,
> +                             state->invert_dimensions ? " (inverted)" : "");
> +               return -ENOSPC;
> +       }
> +
> +       if (crtc->enabled && !state->set_config) {
> +               if (crtc->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);
> +

<snip>

>  /**
>   * drm_mode_setcrtc - set CRTC configuration
>   * @dev: drm device for the ioctl
> @@ -2345,22 +2556,15 @@ 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;
> +       void *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;
> -
> -       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);
> @@ -2378,55 +2582,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
>                                 ret = -EINVAL;
>                                 goto out;
>                         }
> -                       fb = crtc->fb;
> -                       /* Make refcounting symmetric with the lookup path. */
> -                       drm_framebuffer_reference(fb);
> +                       fb_id = crtc->base.id;

s/crtc->base.id/crtc->fb->base.id/ here?

>                 } 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) {
> @@ -2434,52 +2598,63 @@ 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,
> -                                       drm_get_connector_name(connector));
> -
> -                       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);
> +retry:
> +       state = dev->driver->atomic_begin(dev, 0);
> +       if (IS_ERR(state))
> +               return PTR_ERR(state);
>
> -out:
> -       if (fb)
> -               drm_framebuffer_unreference(fb);
> +       /* 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;
>
> -       kfree(connector_set);
> -       drm_mode_destroy(dev, mode);
> -       drm_modeset_unlock_all(dev);
> +       ret =
> +               drm_mode_crtc_set_obj_prop(crtc, state,
> +                       config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode) ||
> +               drm_mode_crtc_set_obj_prop(crtc, state,
> +                       config->prop_connector_ids,
> +                       crtc_req->count_connectors * sizeof(connector_ids[0]),
> +                       connector_ids) ||
> +               drm_mode_crtc_set_obj_prop(crtc, state,
> +                       config->prop_fb_id, fb_id, NULL) ||
> +               drm_mode_crtc_set_obj_prop(crtc, state,
> +                       config->prop_src_x, crtc_req->x, NULL) ||
> +               drm_mode_crtc_set_obj_prop(crtc, state,
> +                       config->prop_src_y, crtc_req->y, NULL) ||
> +               dev->driver->atomic_check(dev, state);
> +       if (ret)
> +               goto out;
> +
> +       ret = dev->driver->atomic_commit(dev, state);
> +
> +out:
> +       if (state)
> +               dev->driver->atomic_end(dev, state);
> +       if (ret == -EDEADLK)
> +               goto retry;
>         return ret;
>  }
>

<snip>

>  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;
> +       void *state;
>         int ret = -EINVAL;
>
>         if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
> @@ -3946,92 +4163,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>         if (!crtc)
>                 return -ENOENT;
>
> -       drm_modeset_lock(&crtc->mutex, NULL);
> -       if (crtc->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->fb->pixel_format != fb->pixel_format) {
> -               DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
> -               ret = -EINVAL;
> -               goto out;
> -       }
> +retry:
> +       state = dev->driver->atomic_begin(dev,
> +                       page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
> +       if (IS_ERR(state))
> +               return PTR_ERR(state);
>
>         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->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->fb != fb);
> -               /* Unref only the old framebuffer. */
> -               fb = NULL;
> -       }
> +       ret = drm_mode_crtc_set_obj_prop(crtc, 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;

If atomic_check fails, I think we need to unreference page_flip->fb_id

> +
> +       ret = dev->driver->atomic_commit(dev, state);
>
> +out:
> +       if (ret && e)
> +               destroy_vblank_event(dev, file_priv, e);
> +       dev->driver->atomic_end(dev, state);
> +       if (ret == -EDEADLK)
> +               goto retry;
>         return ret;
>  }
>

<snip>

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

* Re: [RFCv4 10/14] drm: convert crtc to properties/state
  2014-03-04 21:29   ` Sean Paul
@ 2014-03-04 22:04     ` Rob Clark
  2014-03-04 22:36       ` Sean Paul
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Clark @ 2014-03-04 22:04 UTC (permalink / raw)
  To: Sean Paul; +Cc: dri-devel

On Tue, Mar 4, 2014 at 4:29 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>> 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.
>> ---

<snip>

>> +static int check_connectors(struct drm_crtc *crtc, void *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);
>
> Hi Rob,
> This will populate state's placeholder for ocrtc, which will have the
> unintended consequence of committing ocrtc's state and thus
> unreferencing ocrtc's current fb in
> drm_atomic_helper_commit_crtc_state.
>
> Maybe a new transient state bit in drm_crtc_state which avoids the
> commit_crtc_state call is in order?

probably not a bad idea to avoid unnecessary commit,  but need to
check if that is just masking a refcnt'ing problem.  Ie. userspace
*should* be allowed to set properties on the crtc (for example, set
color correction properties, etc) without causing an unintended
finalizing of the fb..

<snip>

>> @@ -3946,92 +4163,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>>         if (!crtc)
>>                 return -ENOENT;
>>
>> -       drm_modeset_lock(&crtc->mutex, NULL);
>> -       if (crtc->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->fb->pixel_format != fb->pixel_format) {
>> -               DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
>> -               ret = -EINVAL;
>> -               goto out;
>> -       }
>> +retry:
>> +       state = dev->driver->atomic_begin(dev,
>> +                       page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
>> +       if (IS_ERR(state))
>> +               return PTR_ERR(state);
>>
>>         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->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->fb != fb);
>> -               /* Unref only the old framebuffer. */
>> -               fb = NULL;
>> -       }
>> +       ret = drm_mode_crtc_set_obj_prop(crtc, 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;
>
> If atomic_check fails, I think we need to unreference page_flip->fb_id

I don't have the branch in front of me (although I'm back in kernel
land now, so once I finish up a few other little things, I should be
rebasing in next few days).. so my memory could be a bit rusty, but:

I had added cstate->new_fb (set to true whenever we take a ref to the
fb), which should probably be using that to decide when to unref when
cleaning up applied or unapplied state.  But I don't see any other
references to new_fb in this patch, so I guess I either lost or forgot
something ;-)

BR,
-R

>> +
>> +       ret = dev->driver->atomic_commit(dev, state);
>>
>> +out:
>> +       if (ret && e)
>> +               destroy_vblank_event(dev, file_priv, e);
>> +       dev->driver->atomic_end(dev, state);
>> +       if (ret == -EDEADLK)
>> +               goto retry;
>>         return ret;
>>  }
>>
>
> <snip>

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

* Re: [RFCv4 10/14] drm: convert crtc to properties/state
  2014-03-04 22:04     ` Rob Clark
@ 2014-03-04 22:36       ` Sean Paul
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Paul @ 2014-03-04 22:36 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Tue, Mar 4, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Tue, Mar 4, 2014 at 4:29 PM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> 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.
>>> ---
>
> <snip>
>
>>> +static int check_connectors(struct drm_crtc *crtc, void *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);
>>
>> Hi Rob,
>> This will populate state's placeholder for ocrtc, which will have the
>> unintended consequence of committing ocrtc's state and thus
>> unreferencing ocrtc's current fb in
>> drm_atomic_helper_commit_crtc_state.
>>
>> Maybe a new transient state bit in drm_crtc_state which avoids the
>> commit_crtc_state call is in order?
>
> probably not a bad idea to avoid unnecessary commit,  but need to
> check if that is just masking a refcnt'ing problem.  Ie. userspace
> *should* be allowed to set properties on the crtc (for example, set
> color correction properties, etc) without causing an unintended
> finalizing of the fb..
>

Right, agreed.

Whenever I do a setcrtc on crtc A, I lose a reference to crtc B's
current fb (and vice versa). If I short-circuit this code, we don't
populate crtc B's cstate in state and the fb is not unreferenced. The
alternative would be to take a reference on crtc B's fb here, but that
will cause problems if crtc B is meant to be involved in the commit.

I pasted what I have locally below, everything seems well-behaved now:

diff --git a/drivers/gpu/drm/drm_atomic_helper.c
b/drivers/gpu/drm/drm_atomic_helper.c
index 2386ab1..9fe1276 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -485,6 +485,7 @@ void drm_atomic_helper_init_crtc_state(struct
drm_crtc *crtc,
        /* this should never happen.. but make sure! */
        WARN_ON(cstate->event);
        cstate->event = NULL;
+       cstate->commit_state = false;
 }
 EXPORT_SYMBOL(drm_atomic_helper_init_crtc_state);

@@ -631,6 +632,9 @@ drm_atomic_helper_commit_crtc_state(struct drm_crtc *crtc,
        struct drm_atomic_helper_state *a = 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 4834dd7..49eda31 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -959,6 +959,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc,
        struct drm_device *dev = crtc->dev;
        struct drm_mode_config *config = &dev->mode_config;

+       state->commit_state = true;
        drm_object_property_set_value(&crtc->base,
                        &state->propvals, property, value, blob_data);

diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index b81a64c..043ca1e 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -482,6 +482,7 @@ struct drm_crtc_state {
        bool set_config        : 1;
        bool new_fb            : 1;
        bool connectors_change : 1;
+       bool commit_state      : 1;

        uint8_t num_connector_ids;
        uint32_t *connector_ids;



> <snip>
>
>>> @@ -3946,92 +4163,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>>>         if (!crtc)
>>>                 return -ENOENT;
>>>
>>> -       drm_modeset_lock(&crtc->mutex, NULL);
>>> -       if (crtc->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->fb->pixel_format != fb->pixel_format) {
>>> -               DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
>>> -               ret = -EINVAL;
>>> -               goto out;
>>> -       }
>>> +retry:
>>> +       state = dev->driver->atomic_begin(dev,
>>> +                       page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
>>> +       if (IS_ERR(state))
>>> +               return PTR_ERR(state);
>>>
>>>         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->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->fb != fb);
>>> -               /* Unref only the old framebuffer. */
>>> -               fb = NULL;
>>> -       }
>>> +       ret = drm_mode_crtc_set_obj_prop(crtc, 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;
>>
>> If atomic_check fails, I think we need to unreference page_flip->fb_id
>
> I don't have the branch in front of me (although I'm back in kernel
> land now, so once I finish up a few other little things, I should be
> rebasing in next few days).. so my memory could be a bit rusty, but:
>
> I had added cstate->new_fb (set to true whenever we take a ref to the
> fb), which should probably be using that to decide when to unref when
> cleaning up applied or unapplied state.  But I don't see any other
> references to new_fb in this patch, so I guess I either lost or forgot
> something ;-)
>
> BR,
> -R
>
>>> +
>>> +       ret = dev->driver->atomic_commit(dev, state);
>>>
>>> +out:
>>> +       if (ret && e)
>>> +               destroy_vblank_event(dev, file_priv, e);
>>> +       dev->driver->atomic_end(dev, state);
>>> +       if (ret == -EDEADLK)
>>> +               goto retry;
>>>         return ret;
>>>  }
>>>
>>
>> <snip>

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2013-11-25 14:47 ` [RFCv4 09/14] drm: convert plane to properties/state Rob Clark
  2014-01-28 21:52   ` Sean Paul
  2014-02-26 21:30   ` Sean Paul
@ 2014-03-18 15:48   ` Sean Paul
  2014-03-18 16:24     ` Rob Clark
  2 siblings, 1 reply; 32+ messages in thread
From: Sean Paul @ 2014-03-18 15:48 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
> Break the mutable state of a plane out into a separate structure
> and use atomic properties mechanism to set plane attributes.  This
> makes it easier to have some helpers for plane->set_property()
> and for checking for invalid params.  The idea is that individual
> drivers can wrap the state struct in their own struct which adds
> driver specific parameters, for easy build-up of state across
> multiple set_property() calls and for easy atomic commit or roll-
> back.
>
> The same should be done for CRTC, encoder, and connector, but this
> patch only includes the first part (plane).
> ---
>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>  include/drm/drm_atomic_helper.h             |  39 ++-
>  include/drm/drm_crtc.h                      |  88 +++++-
>  19 files changed, 654 insertions(+), 189 deletions(-)
>

<snip>

> +static struct drm_plane_state *
> +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
> +{
> +       struct drm_atomic_helper_state *a = state;
> +       struct drm_plane_state *pstate;
> +       int ret;
> +
> +       /* grab lock of current crtc.. if crtc is NULL then grab all: */
> +       if (plane->state->crtc)
> +               ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
> +       else
> +               ret = drm_modeset_lock_all_crtcs(plane->dev, state);


Hi Rob,
There seems to be a recursive lock in the
drm_fb_helper_restore_fbdev_mode path. The code loops through all
planes and does a force disable. If 2 of the planes do not have a crtc
assigned, we'll try to lock all crtcs twice. Here's a stack trace:

[   14.040141] Call Trace:
[   14.040144]  [<ffffffff9669023a>] drm_modeset_lock+0x46/0x1b7
[   14.040147]  [<ffffffff96689eb6>] ? drm_err+0x6e/0x85
[   14.040149]  [<ffffffff96690425>] drm_modeset_lock_all_crtcs+0x7a/0xdf
[   14.040152]  [<ffffffff9669b6b2>] drm_atomic_helper_get_plane_state+0x34/0x99
[   14.040154]  [<ffffffff9669af9d>]
drm_atomic_helper_plane_set_property+0x30/0x56
[   14.040157]  [<ffffffff9668ee04>] drm_mode_plane_set_obj_prop+0x18/0x20
[   14.040159]  [<ffffffff9668ee34>] drm_plane_force_disable+0x28/0x46
[   14.040161]  [<ffffffff9667e1f7>] drm_fb_helper_restore_fbdev_mode+0x84/0x140
[   14.040163]  [<ffffffff966e0bea>] intel_fb_restore_mode+0x26/0x8c
[   14.040166]  [<ffffffff966a1f41>] i915_driver_lastclose+0x2c/0x3e
[   14.040169]  [<ffffffff96685399>] drm_lastclose+0x49/0x238
[   14.040171]  [<ffffffff96685d24>] drm_release+0x513/0x546
[   14.040174]  [<ffffffff964e8842>] __fput+0xfb/0x1d3
[   14.040178]  [<ffffffff964e8928>] ____fput+0xe/0x10
[   14.040180]  [<ffffffff9644bf49>] task_work_run+0x7d/0x94
[   14.040182]  [<ffffffff96401c8f>] do_notify_resume+0x57/0x5b
[   14.040184]  [<ffffffff968b5308>] int_signal+0x12/0x17


> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       pstate = a->pstates[plane->id];
> +
> +       if (!pstate) {
> +               pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
> +               if (!pstate)
> +                       return ERR_PTR(-ENOMEM);
> +               drm_atomic_helper_init_plane_state(plane, pstate, state);
> +               a->planes[plane->id] = plane;
> +               a->pstates[plane->id] = pstate;
> +       }
> +
> +       return pstate;
> +}
> +

<snip>

> +int drm_plane_set_property(struct drm_plane *plane,
> +               struct drm_plane_state *state,
> +               struct drm_property *property,
> +               uint64_t value, void *blob_data)
> +{
> +       struct drm_device *dev = plane->dev;
> +       struct drm_mode_config *config = &dev->mode_config;
> +
> +       drm_object_property_set_value(&plane->base,
> +                       &state->propvals, property, value, blob_data);
> +
> +       if (property == config->prop_fb_id) {
> +               state->new_fb = true;
> +               state->fb = drm_framebuffer_lookup(dev, value);
> +       } else if (property == config->prop_crtc_id) {
> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
> +               /* take the lock of the incoming crtc as well, moving
> +                * plane between crtcs is synchronized on both incoming
> +                * and outgoing crtc.
> +                */
> +               if (crtc) {
> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);

I haven't tested this, but it seems it might be susceptible to the
same issue if the plane does not have an assigned crtc.

Sean

> +                       if (ret)
> +                               return ret;
> +               }
> +               state->crtc = crtc;
> +       } else if (property == config->prop_crtc_x) {
> +               state->crtc_x = U642I64(value);
> +       } else if (property == config->prop_crtc_y) {
> +               state->crtc_y = U642I64(value);
> +       } else if (property == config->prop_crtc_w) {
> +               state->crtc_w = value;
> +       } else if (property == config->prop_crtc_h) {
> +               state->crtc_h = value;
> +       } else if (property == config->prop_src_x) {
> +               state->src_x = value;
> +       } else if (property == config->prop_src_y) {
> +               state->src_y = value;
> +       } else if (property == config->prop_src_w) {
> +               state->src_w = value;
> +       } else if (property == config->prop_src_h) {
> +               state->src_h = value;
> +       } else {
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-03-18 15:48   ` Sean Paul
@ 2014-03-18 16:24     ` Rob Clark
  2014-03-18 17:33       ` Sean Paul
  0 siblings, 1 reply; 32+ messages in thread
From: Rob Clark @ 2014-03-18 16:24 UTC (permalink / raw)
  To: Sean Paul; +Cc: dri-devel

On Tue, Mar 18, 2014 at 11:48 AM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>> Break the mutable state of a plane out into a separate structure
>> and use atomic properties mechanism to set plane attributes.  This
>> makes it easier to have some helpers for plane->set_property()
>> and for checking for invalid params.  The idea is that individual
>> drivers can wrap the state struct in their own struct which adds
>> driver specific parameters, for easy build-up of state across
>> multiple set_property() calls and for easy atomic commit or roll-
>> back.
>>
>> The same should be done for CRTC, encoder, and connector, but this
>> patch only includes the first part (plane).
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>  include/drm/drm_crtc.h                      |  88 +++++-
>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>
>
> <snip>
>
>> +static struct drm_plane_state *
>> +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
>> +{
>> +       struct drm_atomic_helper_state *a = state;
>> +       struct drm_plane_state *pstate;
>> +       int ret;
>> +
>> +       /* grab lock of current crtc.. if crtc is NULL then grab all: */
>> +       if (plane->state->crtc)
>> +               ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
>> +       else
>> +               ret = drm_modeset_lock_all_crtcs(plane->dev, state);
>
>
> Hi Rob,
> There seems to be a recursive lock in the
> drm_fb_helper_restore_fbdev_mode path. The code loops through all
> planes and does a force disable. If 2 of the planes do not have a crtc
> assigned, we'll try to lock all crtcs twice. Here's a stack trace:

so what is *supposed* to happen is that in both cases, the crtc's are
locked against the same ww_ctx.  So the second time will be a no-op.

It gets a bit tricky with the whole lock-handoff-to-worker feature
which drivers can choose to use, since when the locks are re-acquired
it is (out of necessity) with a different ww_ctx.

I did fix something around those lines with the recent updated atomic
series (rebased on primary-planes, fwiw).  And also added a WARN_ON()
to catch places which don't acquire their needed locks before the
commit phase.

I'm pretty sure this is the same thing you are hitting here

> [   14.040141] Call Trace:
> [   14.040144]  [<ffffffff9669023a>] drm_modeset_lock+0x46/0x1b7
> [   14.040147]  [<ffffffff96689eb6>] ? drm_err+0x6e/0x85
> [   14.040149]  [<ffffffff96690425>] drm_modeset_lock_all_crtcs+0x7a/0xdf
> [   14.040152]  [<ffffffff9669b6b2>] drm_atomic_helper_get_plane_state+0x34/0x99
> [   14.040154]  [<ffffffff9669af9d>]
> drm_atomic_helper_plane_set_property+0x30/0x56
> [   14.040157]  [<ffffffff9668ee04>] drm_mode_plane_set_obj_prop+0x18/0x20
> [   14.040159]  [<ffffffff9668ee34>] drm_plane_force_disable+0x28/0x46
> [   14.040161]  [<ffffffff9667e1f7>] drm_fb_helper_restore_fbdev_mode+0x84/0x140
> [   14.040163]  [<ffffffff966e0bea>] intel_fb_restore_mode+0x26/0x8c
> [   14.040166]  [<ffffffff966a1f41>] i915_driver_lastclose+0x2c/0x3e
> [   14.040169]  [<ffffffff96685399>] drm_lastclose+0x49/0x238
> [   14.040171]  [<ffffffff96685d24>] drm_release+0x513/0x546
> [   14.040174]  [<ffffffff964e8842>] __fput+0xfb/0x1d3
> [   14.040178]  [<ffffffff964e8928>] ____fput+0xe/0x10
> [   14.040180]  [<ffffffff9644bf49>] task_work_run+0x7d/0x94
> [   14.040182]  [<ffffffff96401c8f>] do_notify_resume+0x57/0x5b
> [   14.040184]  [<ffffffff968b5308>] int_signal+0x12/0x17
>
>
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       pstate = a->pstates[plane->id];
>> +
>> +       if (!pstate) {
>> +               pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
>> +               if (!pstate)
>> +                       return ERR_PTR(-ENOMEM);
>> +               drm_atomic_helper_init_plane_state(plane, pstate, state);
>> +               a->planes[plane->id] = plane;
>> +               a->pstates[plane->id] = pstate;
>> +       }
>> +
>> +       return pstate;
>> +}
>> +
>
> <snip>
>
>> +int drm_plane_set_property(struct drm_plane *plane,
>> +               struct drm_plane_state *state,
>> +               struct drm_property *property,
>> +               uint64_t value, void *blob_data)
>> +{
>> +       struct drm_device *dev = plane->dev;
>> +       struct drm_mode_config *config = &dev->mode_config;
>> +
>> +       drm_object_property_set_value(&plane->base,
>> +                       &state->propvals, property, value, blob_data);
>> +
>> +       if (property == config->prop_fb_id) {
>> +               state->new_fb = true;
>> +               state->fb = drm_framebuffer_lookup(dev, value);
>> +       } else if (property == config->prop_crtc_id) {
>> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
>> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>> +               /* take the lock of the incoming crtc as well, moving
>> +                * plane between crtcs is synchronized on both incoming
>> +                * and outgoing crtc.
>> +                */
>> +               if (crtc) {
>> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
>
> I haven't tested this, but it seems it might be susceptible to the
> same issue if the plane does not have an assigned crtc.

well, as long as the locks that will be needed in commit() are grabbed
prior to commit(), it should be all good.  But there was a case where
I needed to grab crtc locks earlier.

Eventually we should just have plane locks, which would reduce the
cases where we have to grab crtc locks.  But I was really trying to
not *completely* re-write kms in one go ;-)

BR,
-R

>
> Sean
>
>> +                       if (ret)
>> +                               return ret;
>> +               }
>> +               state->crtc = crtc;
>> +       } else if (property == config->prop_crtc_x) {
>> +               state->crtc_x = U642I64(value);
>> +       } else if (property == config->prop_crtc_y) {
>> +               state->crtc_y = U642I64(value);
>> +       } else if (property == config->prop_crtc_w) {
>> +               state->crtc_w = value;
>> +       } else if (property == config->prop_crtc_h) {
>> +               state->crtc_h = value;
>> +       } else if (property == config->prop_src_x) {
>> +               state->src_x = value;
>> +       } else if (property == config->prop_src_y) {
>> +               state->src_y = value;
>> +       } else if (property == config->prop_src_w) {
>> +               state->src_w = value;
>> +       } else if (property == config->prop_src_h) {
>> +               state->src_h = value;
>> +       } else {
>> +               return -EINVAL;
>> +       }
>> +
>> +       return 0;
>> +}

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

* Re: [RFCv4 09/14] drm: convert plane to properties/state
  2014-03-18 16:24     ` Rob Clark
@ 2014-03-18 17:33       ` Sean Paul
  0 siblings, 0 replies; 32+ messages in thread
From: Sean Paul @ 2014-03-18 17:33 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Tue, Mar 18, 2014 at 12:24 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Tue, Mar 18, 2014 at 11:48 AM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> Break the mutable state of a plane out into a separate structure
>>> and use atomic properties mechanism to set plane attributes.  This
>>> makes it easier to have some helpers for plane->set_property()
>>> and for checking for invalid params.  The idea is that individual
>>> drivers can wrap the state struct in their own struct which adds
>>> driver specific parameters, for easy build-up of state across
>>> multiple set_property() calls and for easy atomic commit or roll-
>>> back.
>>>
>>> The same should be done for CRTC, encoder, and connector, but this
>>> patch only includes the first part (plane).
>>> ---
>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>  drivers/gpu/drm/drm_fb_helper.c             |  17 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   4 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_encoder.c |   6 +-
>>>  drivers/gpu/drm/exynos/exynos_drm_plane.c   |  15 +-
>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>
>>
>> <snip>
>>
>>> +static struct drm_plane_state *
>>> +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state)
>>> +{
>>> +       struct drm_atomic_helper_state *a = state;
>>> +       struct drm_plane_state *pstate;
>>> +       int ret;
>>> +
>>> +       /* grab lock of current crtc.. if crtc is NULL then grab all: */
>>> +       if (plane->state->crtc)
>>> +               ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
>>> +       else
>>> +               ret = drm_modeset_lock_all_crtcs(plane->dev, state);
>>
>>
>> Hi Rob,
>> There seems to be a recursive lock in the
>> drm_fb_helper_restore_fbdev_mode path. The code loops through all
>> planes and does a force disable. If 2 of the planes do not have a crtc
>> assigned, we'll try to lock all crtcs twice. Here's a stack trace:
>
> so what is *supposed* to happen is that in both cases, the crtc's are
> locked against the same ww_ctx.  So the second time will be a no-op.
>

Right, OK, I was slightly mistaken. It seems like what was happening
is that we were setting lock->atomic_pending the first time we grabbed
each lock and then blocking on atomic_pending in the second go-around.


> It gets a bit tricky with the whole lock-handoff-to-worker feature
> which drivers can choose to use, since when the locks are re-acquired
> it is (out of necessity) with a different ww_ctx.
>
> I did fix something around those lines with the recent updated atomic
> series (rebased on primary-planes, fwiw).  And also added a WARN_ON()
> to catch places which don't acquire their needed locks before the
> commit phase.
>

Indeed, this seems to be fixed in your cold-fusion branch since it
only acquires the locks once, when the state is allocated.

Sean



> I'm pretty sure this is the same thing you are hitting here
>
>> [   14.040141] Call Trace:
>> [   14.040144]  [<ffffffff9669023a>] drm_modeset_lock+0x46/0x1b7
>> [   14.040147]  [<ffffffff96689eb6>] ? drm_err+0x6e/0x85
>> [   14.040149]  [<ffffffff96690425>] drm_modeset_lock_all_crtcs+0x7a/0xdf
>> [   14.040152]  [<ffffffff9669b6b2>] drm_atomic_helper_get_plane_state+0x34/0x99
>> [   14.040154]  [<ffffffff9669af9d>]
>> drm_atomic_helper_plane_set_property+0x30/0x56
>> [   14.040157]  [<ffffffff9668ee04>] drm_mode_plane_set_obj_prop+0x18/0x20
>> [   14.040159]  [<ffffffff9668ee34>] drm_plane_force_disable+0x28/0x46
>> [   14.040161]  [<ffffffff9667e1f7>] drm_fb_helper_restore_fbdev_mode+0x84/0x140
>> [   14.040163]  [<ffffffff966e0bea>] intel_fb_restore_mode+0x26/0x8c
>> [   14.040166]  [<ffffffff966a1f41>] i915_driver_lastclose+0x2c/0x3e
>> [   14.040169]  [<ffffffff96685399>] drm_lastclose+0x49/0x238
>> [   14.040171]  [<ffffffff96685d24>] drm_release+0x513/0x546
>> [   14.040174]  [<ffffffff964e8842>] __fput+0xfb/0x1d3
>> [   14.040178]  [<ffffffff964e8928>] ____fput+0xe/0x10
>> [   14.040180]  [<ffffffff9644bf49>] task_work_run+0x7d/0x94
>> [   14.040182]  [<ffffffff96401c8f>] do_notify_resume+0x57/0x5b
>> [   14.040184]  [<ffffffff968b5308>] int_signal+0x12/0x17
>>
>>
>>> +       if (ret)
>>> +               return ERR_PTR(ret);
>>> +
>>> +       pstate = a->pstates[plane->id];
>>> +
>>> +       if (!pstate) {
>>> +               pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
>>> +               if (!pstate)
>>> +                       return ERR_PTR(-ENOMEM);
>>> +               drm_atomic_helper_init_plane_state(plane, pstate, state);
>>> +               a->planes[plane->id] = plane;
>>> +               a->pstates[plane->id] = pstate;
>>> +       }
>>> +
>>> +       return pstate;
>>> +}
>>> +
>>
>> <snip>
>>
>>> +int drm_plane_set_property(struct drm_plane *plane,
>>> +               struct drm_plane_state *state,
>>> +               struct drm_property *property,
>>> +               uint64_t value, void *blob_data)
>>> +{
>>> +       struct drm_device *dev = plane->dev;
>>> +       struct drm_mode_config *config = &dev->mode_config;
>>> +
>>> +       drm_object_property_set_value(&plane->base,
>>> +                       &state->propvals, property, value, blob_data);
>>> +
>>> +       if (property == config->prop_fb_id) {
>>> +               state->new_fb = true;
>>> +               state->fb = drm_framebuffer_lookup(dev, value);
>>> +       } else if (property == config->prop_crtc_id) {
>>> +               struct drm_mode_object *obj = drm_property_get_obj(property, value);
>>> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>>> +               /* take the lock of the incoming crtc as well, moving
>>> +                * plane between crtcs is synchronized on both incoming
>>> +                * and outgoing crtc.
>>> +                */
>>> +               if (crtc) {
>>> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
>>
>> I haven't tested this, but it seems it might be susceptible to the
>> same issue if the plane does not have an assigned crtc.
>
> well, as long as the locks that will be needed in commit() are grabbed
> prior to commit(), it should be all good.  But there was a case where
> I needed to grab crtc locks earlier.
>
> Eventually we should just have plane locks, which would reduce the
> cases where we have to grab crtc locks.  But I was really trying to
> not *completely* re-write kms in one go ;-)
>
> BR,
> -R
>
>>
>> Sean
>>
>>> +                       if (ret)
>>> +                               return ret;
>>> +               }
>>> +               state->crtc = crtc;
>>> +       } else if (property == config->prop_crtc_x) {
>>> +               state->crtc_x = U642I64(value);
>>> +       } else if (property == config->prop_crtc_y) {
>>> +               state->crtc_y = U642I64(value);
>>> +       } else if (property == config->prop_crtc_w) {
>>> +               state->crtc_w = value;
>>> +       } else if (property == config->prop_crtc_h) {
>>> +               state->crtc_h = value;
>>> +       } else if (property == config->prop_src_x) {
>>> +               state->src_x = value;
>>> +       } else if (property == config->prop_src_y) {
>>> +               state->src_y = value;
>>> +       } else if (property == config->prop_src_w) {
>>> +               state->src_w = value;
>>> +       } else if (property == config->prop_src_h) {
>>> +               state->src_h = value;
>>> +       } else {
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       return 0;
>>> +}

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

end of thread, other threads:[~2014-03-18 17:34 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-25 14:47 [RFCv4 00/14] Atomic/nuclear modeset/pageflip Rob Clark
2013-11-25 14:47 ` [RFCv4 01/14] drm: add atomic fxns Rob Clark
2013-11-25 14:47 ` [RFCv4 02/14] drm: convert crtc to ww_mutex Rob Clark
2013-11-25 14:47 ` [RFCv4 03/14] drm: add object property type Rob Clark
2013-11-25 14:47 ` [RFCv4 04/14] drm: add signed-range " Rob Clark
2013-11-25 14:47 ` [RFCv4 05/14] drm: helpers to find mode objects Rob Clark
2013-11-25 14:47 ` [RFCv4 06/14] drm: split propvals out and blob property support Rob Clark
2013-11-25 14:47 ` [RFCv4 07/14] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
2013-11-25 14:47 ` [RFCv4 08/14] drm: Refactor object property check code Rob Clark
2013-11-25 14:47 ` [RFCv4 09/14] drm: convert plane to properties/state Rob Clark
2014-01-28 21:52   ` Sean Paul
2014-01-29 13:44     ` Rob Clark
2014-02-07  2:53       ` Sean Paul
2014-02-07 12:28         ` Rob Clark
2014-02-10 17:45           ` Sean Paul
2014-02-26 21:30   ` Sean Paul
2014-02-27  0:18     ` Rob Clark
2014-03-03 19:22       ` Sean Paul
2014-03-03 19:40         ` Rob Clark
2014-03-18 15:48   ` Sean Paul
2014-03-18 16:24     ` Rob Clark
2014-03-18 17:33       ` Sean Paul
2013-11-25 14:47 ` [RFCv4 10/14] drm: convert crtc " Rob Clark
2013-12-11 21:48   ` Matt Roper
2013-12-11 23:38     ` Rob Clark
2014-03-04 21:29   ` Sean Paul
2014-03-04 22:04     ` Rob Clark
2014-03-04 22:36       ` Sean Paul
2013-11-25 14:47 ` [RFCv4 11/14] drm: push locking down into restore_fbdev_mode Rob Clark
2013-11-25 14:47 ` [RFCv4 12/14] drm: Atomic modeset ioctl Rob Clark
2013-11-25 14:47 ` [RFCv4 13/14] drm/msm: add atomic support Rob Clark
2013-11-25 14:47 ` [RFCv4 14/14] HACK: drm: allow FB's in drm_mode_object_find 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.