All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip
@ 2014-05-24 18:30 Rob Clark
  2014-05-24 18:30 ` [PATCH 01/17] drm: fix typo Rob Clark
                   ` (17 more replies)
  0 siblings, 18 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

One more time, with feeling..

Previous revision of series:
http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html

And if you prefer, in git form:
http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion
git://people.freedesktop.org/~robclark/linux cold-fusion

This series does not include the actual atomic ioctl, but it does
include all the needed infrastructure changes.

Compared to previous revision, I've split out drm_modeset_acquire_ctx
from drm_atomic_state, so that we can ww acquire mode_config and crtc
locks outside of atomic transactions.  (Which keeps lockdep happy wrt.
drm_modeset_lock_all().)  I also got to the point in juggling things
around where I wanted to let the compiler type checking do it's job,
so 's/void *state/struct drm_atomic_state *state/g'.

At this point, I've tested this on i915 (few different generation
laptops), radeon, and msm.  And of course w/ ww debug (deadlock in-
jection) enabled.  So I think all the locking related paths should
be covered.

I believe Thierry has tested a slighly older revision on tegra.  I
would of course appreciate more testing on other drivers for which I
don't have the hw.  But I think it is pretty much ready to go.  I do
still owe some docs updates, I will send some patches for that in
the near future, but didn't want to hold up giving others a chance
to start banging on this.

Rob Clark (14):
  drm: fix typo
  drm: add atomic fxns
  drm: convert crtc and mode_config 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: allow FB's in drm_mode_object_find
  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
  drm: spiff out FB refcnting traces
  drm: more conservative locking

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

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

 drivers/gpu/drm/Makefile                    |    2 +-
 drivers/gpu/drm/armada/armada_crtc.c        |   14 +-
 drivers/gpu/drm/armada/armada_fbdev.c       |    4 +-
 drivers/gpu/drm/armada/armada_output.c      |    3 +-
 drivers/gpu/drm/armada/armada_overlay.c     |   14 +-
 drivers/gpu/drm/ast/ast_drv.c               |    6 +
 drivers/gpu/drm/ast/ast_drv.h               |    1 +
 drivers/gpu/drm/ast/ast_mode.c              |    1 +
 drivers/gpu/drm/cirrus/cirrus_drv.c         |    6 +
 drivers/gpu/drm/cirrus/cirrus_drv.h         |    1 +
 drivers/gpu/drm/cirrus/cirrus_mode.c        |    1 +
 drivers/gpu/drm/drm_atomic.c                |  720 +++++++++++++
 drivers/gpu/drm/drm_crtc.c                  | 1534 ++++++++++++++++++---------
 drivers/gpu/drm/drm_crtc_helper.c           |    6 +-
 drivers/gpu/drm/drm_fb_cma_helper.c         |   15 +-
 drivers/gpu/drm/drm_fb_helper.c             |   69 +-
 drivers/gpu/drm/drm_modes.c                 |    4 +-
 drivers/gpu/drm/drm_modeset_lock.c          |  159 +++
 drivers/gpu/drm/drm_plane_helper.c          |    2 +
 drivers/gpu/drm/drm_probe_helper.c          |   10 +-
 drivers/gpu/drm/drm_sysfs.c                 |    4 +-
 drivers/gpu/drm/exynos/exynos_drm_crtc.c    |   11 +-
 drivers/gpu/drm/exynos/exynos_drm_drv.c     |    7 +
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c   |    8 +-
 drivers/gpu/drm/exynos/exynos_drm_plane.c   |   11 +-
 drivers/gpu/drm/gma500/cdv_intel_crt.c      |    4 +-
 drivers/gpu/drm/gma500/cdv_intel_display.c  |    1 +
 drivers/gpu/drm/gma500/cdv_intel_dp.c       |    7 +-
 drivers/gpu/drm/gma500/cdv_intel_hdmi.c     |    7 +-
 drivers/gpu/drm/gma500/cdv_intel_lvds.c     |   16 +-
 drivers/gpu/drm/gma500/mdfld_dsi_output.c   |   12 +-
 drivers/gpu/drm/gma500/oaktrail_lvds.c      |    6 +-
 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     |   16 +-
 drivers/gpu/drm/gma500/psb_intel_sdvo.c     |   23 +-
 drivers/gpu/drm/i2c/ch7006_drv.c            |    4 +-
 drivers/gpu/drm/i915/i915_debugfs.c         |   12 +-
 drivers/gpu/drm/i915/i915_drv.c             |   16 +-
 drivers/gpu/drm/i915/i915_irq.c             |    6 +-
 drivers/gpu/drm/i915/intel_crt.c            |    4 +-
 drivers/gpu/drm/i915/intel_display.c        |   24 +-
 drivers/gpu/drm/i915/intel_dp.c             |   25 +-
 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           |   10 +-
 drivers/gpu/drm/i915/intel_opregion.c       |    4 +-
 drivers/gpu/drm/i915/intel_overlay.c        |    4 +-
 drivers/gpu/drm/i915/intel_panel.c          |    8 +-
 drivers/gpu/drm/i915/intel_sdvo.c           |   23 +-
 drivers/gpu/drm/i915/intel_sprite.c         |    3 +-
 drivers/gpu/drm/i915/intel_tv.c             |   12 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c       |    7 +
 drivers/gpu/drm/mgag200/mgag200_drv.h       |    1 +
 drivers/gpu/drm/mgag200/mgag200_mode.c      |    1 +
 drivers/gpu/drm/msm/Makefile                |    1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c    |   70 +-
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c     |    6 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h     |    1 +
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c   |   14 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c    |   69 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c     |    6 +
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h     |    2 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c   |   14 +-
 drivers/gpu/drm/msm/msm_atomic.c            |  141 +++
 drivers/gpu/drm/msm/msm_drv.c               |   33 +-
 drivers/gpu/drm/msm/msm_drv.h               |    8 +
 drivers/gpu/drm/msm/msm_gem.c               |   24 +-
 drivers/gpu/drm/msm/msm_gem.h               |   13 +
 drivers/gpu/drm/msm/msm_kms.h               |    1 +
 drivers/gpu/drm/nouveau/dispnv04/crtc.c     |    1 +
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   13 +-
 drivers/gpu/drm/nouveau/dispnv04/tvnv17.c   |    3 +-
 drivers/gpu/drm/nouveau/nouveau_connector.c |    7 +-
 drivers/gpu/drm/nouveau/nouveau_drm.c       |    7 +
 drivers/gpu/drm/nouveau/nouveau_drm.h       |    1 +
 drivers/gpu/drm/nouveau/nv50_display.c      |    1 +
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   26 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   16 +-
 drivers/gpu/drm/omapdrm/omap_drv.h          |    4 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |   14 +-
 drivers/gpu/drm/qxl/qxl_display.c           |    6 +-
 drivers/gpu/drm/qxl/qxl_drv.c               |    9 +
 drivers/gpu/drm/radeon/radeon_connectors.c  |    9 +-
 drivers/gpu/drm/radeon/radeon_display.c     |    2 +
 drivers/gpu/drm/radeon/radeon_drv.c         |    9 +
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c      |    2 +
 drivers/gpu/drm/rcar-du/rcar_du_drv.c       |    7 +
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c   |    3 +-
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   12 +-
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c    |    3 +-
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |    6 +-
 drivers/gpu/drm/shmobile/shmob_drm_drv.c    |    7 +
 drivers/gpu/drm/shmobile/shmob_drm_plane.c  |    2 +
 drivers/gpu/drm/tegra/fb.c                  |    7 +-
 drivers/gpu/drm/tilcdc/tilcdc_crtc.c        |    5 +-
 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         |   26 +-
 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                          |   85 +-
 include/drm/drm_atomic.h                    |  170 +++
 include/drm/drm_crtc.h                      |  326 +++++-
 include/drm/drm_fb_helper.h                 |    3 +-
 include/drm/drm_modeset_lock.h              |  130 +++
 include/uapi/drm/drm_mode.h                 |   18 +
 117 files changed, 3401 insertions(+), 882 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_atomic.c
 create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
 create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
 create mode 100644 include/drm/drm_atomic.h
 create mode 100644 include/drm/drm_modeset_lock.h

-- 
1.9.0

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

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

* [PATCH 01/17] drm: fix typo
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 02/17] drm: add atomic fxns Rob Clark
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc_helper.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index a8b78e7..54e8fdb 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -147,7 +147,7 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev)
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (!drm_helper_encoder_in_use(encoder)) {
 			drm_encoder_disable(encoder);
-			/* disconnector encoder from any connector */
+			/* disconnect encoder from any connector */
 			encoder->crtc = NULL;
 		}
 	}
-- 
1.9.0

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

* [PATCH 02/17] drm: add atomic fxns
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
  2014-05-24 18:30 ` [PATCH 01/17] drm: fix typo Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex Rob Clark
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

The 'atomic' mechanism allows for multiple properties to be updated,
checked, and commited atomically.  This will be the basis of atomic-
modeset and nuclear-pageflip.

The basic flow is:

   state = dev->atomic_begin();
   for (... one or more ...)
      obj->set_property(obj, state, prop, value);
   if (dev->atomic_check(state))
      dev->atomic_commit(state);
   dev->atomic_end(state);

The split of check and commit steps is to allow for ioctls with a
test-only flag (which would skip the commit step).

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

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 48e38ba..ba2ed83 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
 		drm_rect.o drm_vma_manager.o drm_flip_work.o \
-		drm_plane_helper.o
+		drm_plane_helper.o drm_atomic.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 81c34f9..7d3c649 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -961,7 +961,8 @@ static int armada_drm_crtc_page_flip(struct drm_crtc *crtc,
 
 static int
 armada_drm_crtc_set_property(struct drm_crtc *crtc,
-	struct drm_property *property, uint64_t val)
+	struct drm_atomic_state *state, struct drm_property *property,
+	uint64_t val, void *blob_data)
 {
 	struct armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c
index d685a54..9915306 100644
--- a/drivers/gpu/drm/armada/armada_output.c
+++ b/drivers/gpu/drm/armada/armada_output.c
@@ -54,7 +54,8 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
 }
 
 static int armada_drm_connector_set_property(struct drm_connector *conn,
-	struct drm_property *property, uint64_t value)
+	struct drm_atomic_state *state, struct drm_property *property,
+	uint64_t value, void *blob_data)
 {
 	struct armada_connector *dconn = drm_to_armada_conn(conn);
 
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index c5b06fd..601ba9a 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -283,7 +283,8 @@ static void armada_plane_destroy(struct drm_plane *plane)
 }
 
 static int armada_plane_set_property(struct drm_plane *plane,
-	struct drm_property *property, uint64_t val)
+	struct drm_atomic_state *state, struct drm_property *property,
+	uint64_t val, void *blob_data)
 {
 	struct armada_private *priv = plane->dev->dev_private;
 	struct armada_plane *dplane = drm_to_armada_plane(plane);
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 2ba39ac..cd81d68 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -215,6 +215,12 @@ static struct drm_driver driver = {
 	.dumb_map_offset = ast_dumb_mmap_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
 
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
 };
 
 static int __init ast_init(void)
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 5d6a875..0d21ff8 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -29,6 +29,7 @@
 #define __AST_DRV_H__
 
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic.h>
 
 #include <drm/ttm/ttm_bo_api.h>
 #include <drm/ttm/ttm_bo_driver.h>
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index 08ce520..023db21 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -137,6 +137,12 @@ static struct drm_driver driver = {
 	.dumb_create = cirrus_dumb_create,
 	.dumb_map_offset = cirrus_dumb_mmap_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
 };
 
 static const struct dev_pm_ops cirrus_pm_ops = {
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index 117d3ec..f929bc8 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.h
@@ -14,6 +14,7 @@
 #include <video/vga.h>
 
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic.h>
 
 #include <drm/ttm/ttm_bo_api.h>
 #include <drm/ttm/ttm_bo_driver.h>
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
new file mode 100644
index 0000000..12fcbc0
--- /dev/null
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+
+/**
+ * drm_atomic_begin - start a sequence of atomic updates
+ * @dev: DRM device
+ * @flags: the modifier flags that userspace has requested
+ *
+ * Begin a sequence of atomic property sets.  Returns a driver
+ * private state object that is passed back into the various
+ * object's set_property() fxns, and into the remainder of the
+ * atomic funcs.  The state object should accumulate the changes
+ * from one o more set_property()'s.  At the end, the state can
+ * be checked, and optionally committed.
+ *
+ * RETURNS
+ *   a driver state object, which is passed back in to the
+ *   various other atomic fxns, or error (such as -EBUSY if
+ *   there is still a pending async update)
+ */
+struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
+		uint32_t flags)
+{
+	struct drm_atomic_state *state;
+	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_begin);
+
+/**
+ * drm_atomic_set_event - set a pending event on mode object
+ * @dev: DRM device
+ * @state: the driver state object
+ * @obj: the object to set the event on
+ * @event: the event to send back
+ *
+ * Set pending event for an update on the specified object.  The
+ * event is to be sent back to userspace after the update completes.
+ */
+int drm_atomic_set_event(struct drm_device *dev,
+		struct drm_atomic_state *state, struct drm_mode_object *obj,
+		struct drm_pending_vblank_event *event)
+{
+	return -EINVAL;  /* for now */
+}
+EXPORT_SYMBOL(drm_atomic_set_event);
+
+/**
+ * drm_atomic_check - validate state object
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * Check the state object to see if the requested state is
+ * physically possible.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
+{
+	return 0;  /* for now */
+}
+EXPORT_SYMBOL(drm_atomic_check);
+
+/**
+ * drm_atomic_commit - commit state
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * Commit the state.  This will only be called if atomic_check()
+ * succeeds.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state)
+{
+	return 0;  /* for now */
+}
+EXPORT_SYMBOL(drm_atomic_commit);
+
+/**
+ * drm_atomic_end - conclude the atomic update
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * Release resources associated with the state object.
+ */
+void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state)
+{
+	drm_atomic_state_unreference(state);
+}
+EXPORT_SYMBOL(drm_atomic_end);
+
+void _drm_atomic_state_free(struct kref *kref)
+{
+	struct drm_atomic_state *state =
+		container_of(kref, struct drm_atomic_state, refcount);
+	kfree(state);
+}
+EXPORT_SYMBOL(_drm_atomic_state_free);
+
+
+const struct drm_atomic_funcs drm_atomic_funcs = {
+};
+EXPORT_SYMBOL(drm_atomic_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 34f0bf1..1876abb 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3731,20 +3731,21 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
 }
 
-static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
-					   struct drm_property *property,
-					   uint64_t value)
+static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+					   struct drm_atomic_state *state, struct drm_property *property,
+					   uint64_t value, void *blob_data)
 {
 	int ret = -EINVAL;
-	struct drm_connector *connector = obj_to_connector(obj);
 
 	/* Do DPMS ourselves */
 	if (property == connector->dev->mode_config.dpms_property) {
 		if (connector->funcs->dpms)
 			(*connector->funcs->dpms)(connector, (int)value);
 		ret = 0;
-	} else if (connector->funcs->set_property)
-		ret = connector->funcs->set_property(connector, property, value);
+	} else if (connector->funcs->set_property) {
+		ret = connector->funcs->set_property(connector, state,
+				property, value, blob_data);
+	}
 
 	/* store the property value if successful */
 	if (!ret)
@@ -3752,38 +3753,93 @@ static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
 	return ret;
 }
 
-static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
-				      struct drm_property *property,
-				      uint64_t value)
+static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+				      struct drm_atomic_state *state, struct drm_property *property,
+				      uint64_t value, void *blob_data)
 {
 	int ret = -EINVAL;
-	struct drm_crtc *crtc = obj_to_crtc(obj);
 
 	if (crtc->funcs->set_property)
-		ret = crtc->funcs->set_property(crtc, property, value);
+		ret = crtc->funcs->set_property(crtc, state, property,
+				value, blob_data);
 	if (!ret)
-		drm_object_property_set_value(obj, property, value);
+		drm_object_property_set_value(&crtc->base, property, value);
 
 	return ret;
 }
 
-static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj,
-				      struct drm_property *property,
-				      uint64_t value)
+static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+				      struct drm_atomic_state *state, struct drm_property *property,
+				      uint64_t value, void *blob_data)
 {
 	int ret = -EINVAL;
-	struct drm_plane *plane = obj_to_plane(obj);
 
 	if (plane->funcs->set_property)
-		ret = plane->funcs->set_property(plane, property, value);
+		ret = plane->funcs->set_property(plane, state, property,
+				value, blob_data);
 	if (!ret)
-		drm_object_property_set_value(obj, property, value);
+		drm_object_property_set_value(&plane->base, property, value);
 
 	return ret;
 }
 
+static int drm_mode_set_obj_prop(struct drm_device *dev,
+		struct drm_mode_object *obj, struct drm_atomic_state *state,
+		struct drm_property *property, uint64_t value, void *blob_data)
+{
+	if (drm_property_change_is_valid(property, value)) {
+		switch (obj->type) {
+		case DRM_MODE_OBJECT_CONNECTOR:
+			return drm_mode_connector_set_obj_prop(obj_to_connector(obj),
+					state, property, value, blob_data);
+		case DRM_MODE_OBJECT_CRTC:
+			return drm_mode_crtc_set_obj_prop(obj_to_crtc(obj),
+					state, property, value, blob_data);
+		case DRM_MODE_OBJECT_PLANE:
+			return drm_mode_plane_set_obj_prop(obj_to_plane(obj),
+					state, property, value, blob_data);
+		}
+	}
+
+	return -EINVAL;
+}
+
+/* call with mode_config mutex held */
+static int drm_mode_set_obj_prop_id(struct drm_device *dev,
+		struct drm_atomic_state *state,
+		uint32_t obj_id, uint32_t obj_type,
+		uint32_t prop_id, uint64_t value, void *blob_data)
+{
+	struct drm_mode_object *arg_obj;
+	struct drm_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);
+}
+
 /**
- * drm_mode_getproperty_ioctl - get the current value of a object's property
+ * drm_mode_obj_get_properties_ioctl - get the current value of a object's property
  * @dev: DRM device
  * @data: ioctl data
  * @file_priv: DRM file info
@@ -3873,57 +3929,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;
+	struct drm_atomic_state *state;
 	int ret = -EINVAL;
-	int i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
 	drm_modeset_lock_all(dev);
 
-	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 1ef5ab9..2a56973 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -281,8 +281,10 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
 }
 
 static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
+					struct drm_atomic_state *state,
 					struct drm_property *property,
-					uint64_t val)
+					uint64_t val,
+					void *blob_data)
 {
 	struct drm_device *dev = crtc->dev;
 	struct exynos_drm_private *dev_priv = dev->dev_private;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 2d27ba2..318969d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -14,6 +14,7 @@
 #include <linux/pm_runtime.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 
 #include <linux/anon_inodes.h>
 
@@ -341,6 +342,12 @@ static struct drm_driver exynos_drm_driver = {
 	.dumb_create		= exynos_drm_gem_dumb_create,
 	.dumb_map_offset	= exynos_drm_gem_dumb_map_offset,
 	.dumb_destroy		= drm_gem_dumb_destroy,
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
 	.gem_prime_export	= exynos_dmabuf_prime_export,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 8371cbd..9da0935 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -212,8 +212,10 @@ static void exynos_plane_destroy(struct drm_plane *plane)
 }
 
 static int exynos_plane_set_property(struct drm_plane *plane,
+				     struct drm_atomic_state *state,
 				     struct drm_property *property,
-				     uint64_t val)
+				     uint64_t val,
+				     void *blob_data)
 {
 	struct drm_device *dev = plane->dev;
 	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
index c18268c..6d637d3 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -205,8 +205,10 @@ static int cdv_intel_crt_get_modes(struct drm_connector *connector)
 }
 
 static int cdv_intel_crt_set_property(struct drm_connector *connector,
+				  struct drm_atomic_state *state,
 				  struct drm_property *property,
-				  uint64_t value)
+				  uint64_t value,
+				  void *blob_data)
 {
 	return 0;
 }
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index 9ff30c2..31d872d 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1645,8 +1645,10 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector)
 
 static int
 cdv_intel_dp_set_property(struct drm_connector *connector,
+		      struct drm_atomic_state *state,
 		      struct drm_property *property,
-		      uint64_t val)
+		      uint64_t val,
+		      void *blob_data)
 {
 	struct drm_psb_private *dev_priv = connector->dev->dev_private;
 	struct gma_encoder *encoder = gma_attached_encoder(connector);
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index b99084b..f0e4d78 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -150,8 +150,10 @@ static enum drm_connector_status cdv_hdmi_detect(
 }
 
 static int cdv_hdmi_set_property(struct drm_connector *connector,
+				       struct drm_atomic_state *state,
 				       struct drm_property *property,
-				       uint64_t value)
+				       uint64_t value,
+				       void *blob_data)
 {
 	struct drm_encoder *encoder = connector->encoder;
 
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 8ecc920..7ffaed4 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -452,8 +452,10 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector)
 }
 
 static int cdv_intel_lvds_set_property(struct drm_connector *connector,
+				       struct drm_atomic_state *state,
 				       struct drm_property *property,
-				       uint64_t value)
+				       uint64_t value,
+				       void *blob_data)
 {
 	struct drm_encoder *encoder = connector->encoder;
 
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
index 6e91b20..2318a46 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
@@ -243,8 +243,10 @@ mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
 }
 
 static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
+				struct drm_atomic_state *state,
 				struct drm_property *property,
-				uint64_t value)
+				uint64_t value,
+				void *blob_data)
 {
 	struct drm_encoder *encoder = connector->encoder;
 
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 0a3101a..0180292 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -489,6 +489,13 @@ static struct drm_driver driver = {
 	.disable_vblank = psb_disable_vblank,
 	.get_vblank_counter = psb_get_vblank_counter,
 
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 	.gem_free_object = psb_gem_free_object,
 	.gem_vm_ops = &psb_gem_vm_ops,
 
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
index 55ebe2b..413ea37 100644
--- a/drivers/gpu/drm/gma500/psb_drv.h
+++ b/drivers/gpu/drm/gma500/psb_drv.h
@@ -25,6 +25,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_global.h>
 #include <drm/gma_drm.h>
+#include <drm/drm_atomic.h>
 #include "psb_reg.h"
 #include "psb_intel_drv.h"
 #include "gma_display.h"
diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h
index 336bd3a..96e9759 100644
--- a/drivers/gpu/drm/gma500/psb_intel_drv.h
+++ b/drivers/gpu/drm/gma500/psb_intel_drv.h
@@ -254,8 +254,10 @@ extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder,
 extern int psb_intel_lvds_mode_valid(struct drm_connector *connector,
 				     struct drm_display_mode *mode);
 extern int psb_intel_lvds_set_property(struct drm_connector *connector,
+					struct drm_atomic_state *state,
 					struct drm_property *property,
-					uint64_t value);
+					uint64_t value,
+					void *blob_data);
 extern void psb_intel_lvds_destroy(struct drm_connector *connector);
 extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs;
 
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index d7778d0..6b64d9d 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -569,8 +569,10 @@ void psb_intel_lvds_destroy(struct drm_connector *connector)
 }
 
 int psb_intel_lvds_set_property(struct drm_connector *connector,
+				       struct drm_atomic_state *state,
 				       struct drm_property *property,
-				       uint64_t value)
+				       uint64_t value,
+				       void *blob_data)
 {
 	struct drm_encoder *encoder = connector->encoder;
 
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index deeb082..724b525 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -1705,8 +1705,10 @@ static bool psb_intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
 
 static int
 psb_intel_sdvo_set_property(struct drm_connector *connector,
+			struct drm_atomic_state *state,
 			struct drm_property *property,
-			uint64_t val)
+			uint64_t val,
+			void *blob_data)
 {
 	struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector);
 	struct psb_intel_sdvo_connector *psb_intel_sdvo_connector = to_psb_intel_sdvo_connector(connector);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 208e185..e3a2482 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1119,6 +1119,14 @@ static struct drm_driver driver = {
 	.dumb_create = i915_gem_dumb_create,
 	.dumb_map_offset = i915_gem_mmap_gtt,
 	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 	.ioctls = i915_ioctls,
 	.fops = &i915_driver_fops,
 	.name = DRIVER_NAME,
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 22d8347..d4198ff 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -724,8 +724,10 @@ out:
 }
 
 static int intel_crt_set_property(struct drm_connector *connector,
+				  struct drm_atomic_state *state,
 				  struct drm_property *property,
-				  uint64_t value)
+				  uint64_t value,
+				  void *blob_data)
 {
 	return 0;
 }
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 34ed143..c63d210 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3270,8 +3270,10 @@ intel_dp_detect_audio(struct drm_connector *connector)
 
 static int
 intel_dp_set_property(struct drm_connector *connector,
+		      struct drm_atomic_state *state,
 		      struct drm_property *property,
-		      uint64_t val)
+		      uint64_t val,
+		      void *blob_data)
 {
 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
 	struct intel_connector *intel_connector = to_intel_connector(connector);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d8b540b..64aeae0 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -31,6 +31,7 @@
 #include "i915_drv.h"
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_dp_helper.h>
 
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index b606162..8e160b0 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1044,8 +1044,10 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
 
 static int
 intel_hdmi_set_property(struct drm_connector *connector,
+			struct drm_atomic_state *state,
 			struct drm_property *property,
-			uint64_t val)
+			uint64_t val,
+			void *blob_data)
 {
 	struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
 	struct intel_digital_port *intel_dig_port =
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 1b1541d..4028237 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -486,8 +486,10 @@ static void intel_lvds_destroy(struct drm_connector *connector)
 }
 
 static int intel_lvds_set_property(struct drm_connector *connector,
+				   struct drm_atomic_state *state,
 				   struct drm_property *property,
-				   uint64_t value)
+				   uint64_t value,
+				   void *blob_data)
 {
 	struct intel_connector *intel_connector = to_intel_connector(connector);
 	struct drm_device *dev = connector->dev;
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 2bf09e8..4e36ee99 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2052,8 +2052,10 @@ static bool intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
 
 static int
 intel_sdvo_set_property(struct drm_connector *connector,
+			struct drm_atomic_state *state,
 			struct drm_property *property,
-			uint64_t val)
+			uint64_t val,
+			void *blob_data)
 {
 	struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
 	struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index e0193e8..05ee0d5 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1444,8 +1444,10 @@ intel_tv_destroy(struct drm_connector *connector)
 
 
 static int
-intel_tv_set_property(struct drm_connector *connector, struct drm_property *property,
-		      uint64_t val)
+intel_tv_set_property(struct drm_connector *connector,
+		      struct drm_atomic_state *state,
+		      struct drm_property *property,
+		      uint64_t val, void *blob_data)
 {
 	struct drm_device *dev = connector->dev;
 	struct intel_tv *intel_tv = intel_attached_tv(connector);
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index f15ea3c..0425bdd 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -103,6 +103,13 @@ static struct drm_driver driver = {
 	.dumb_create = mgag200_dumb_create,
 	.dumb_map_offset = mgag200_dumb_mmap_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
 };
 
 static struct pci_driver mgag200_pci_driver = {
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index cf11ee6..c4d1600 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -16,6 +16,7 @@
 #include <video/vga.h>
 
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/ttm/ttm_bo_api.h>
 #include <drm/ttm/ttm_bo_driver.h>
 #include <drm/ttm/ttm_placement.h>
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index ef9957d..efa19c8 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -468,7 +468,8 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
 }
 
 static int mdp4_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	// XXX
 	return -EINVAL;
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
index 66f33db..8c064dc 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
@@ -85,7 +85,8 @@ void mdp4_plane_install_properties(struct drm_plane *plane,
 }
 
 int mdp4_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	// XXX
 	return -EINVAL;
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 6ea10bd..ff48944 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -386,7 +386,8 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
 }
 
 static int mdp5_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	// XXX
 	return -EINVAL;
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 47f7bbb..5cbf226 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -100,7 +100,8 @@ void mdp5_plane_install_properties(struct drm_plane *plane,
 }
 
 int mdp5_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	// XXX
 	return -EINVAL;
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 50ec1be..46e0f29 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -815,6 +815,12 @@ static struct drm_driver msm_driver = {
 	.gem_prime_import_sg_table = msm_gem_prime_import_sg_table,
 	.gem_prime_vmap     = msm_gem_prime_vmap,
 	.gem_prime_vunmap   = msm_gem_prime_vunmap,
+	.atomic_begin       = drm_atomic_begin,
+	.atomic_set_event   = drm_atomic_set_event,
+	.atomic_check       = drm_atomic_check,
+	.atomic_commit      = drm_atomic_commit,
+	.atomic_end         = drm_atomic_end,
+	.atomic_funcs       = &drm_atomic_funcs,
 #ifdef CONFIG_DEBUG_FS
 	.debugfs_init       = msm_debugfs_init,
 	.debugfs_cleanup    = msm_debugfs_cleanup,
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 9d10ee0..afc990e 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -50,6 +50,7 @@ static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/msm_drm.h>
 
 struct msm_kms;
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index ab03f77..577e6aa 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -221,8 +221,9 @@ nv10_set_params(struct nouveau_plane *plane)
 
 static int
 nv_set_property(struct drm_plane *plane,
-		struct drm_property *property,
-		uint64_t value)
+		  struct drm_atomic_state *state,
+		  struct drm_property *property,
+		  uint64_t value, void *blob_data)
 {
 	struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index d07ce02..3351e72 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -447,7 +447,8 @@ nouveau_connector_force(struct drm_connector *connector)
 
 static int
 nouveau_connector_set_property(struct drm_connector *connector,
-			       struct drm_property *property, uint64_t value)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t value, void *blob_data)
 {
 	struct nouveau_display *disp = nouveau_display(connector->dev);
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index ddd8375..9a96d6d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -858,6 +858,13 @@ driver = {
 	.dumb_map_offset = nouveau_display_dumb_map_offset,
 	.dumb_destroy = drm_gem_dumb_destroy,
 
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
 #ifdef GIT_REVISION
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h
index 7efbafa..8941696 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -29,6 +29,7 @@
 #include <subdev/vm.h>
 
 #include <drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/nouveau_drm.h>
 
 #include <drm/ttm/ttm_bo_api.h>
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index e3c47a8..a7c0010 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -382,7 +382,8 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
 }
 
 static int omap_crtc_set_property(struct drm_crtc *crtc,
-		struct drm_property *property, uint64_t val)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct omap_drm_private *priv = crtc->dev->dev_private;
@@ -392,7 +393,8 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
 				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
 	}
 
-	return omap_plane_set_property(omap_crtc->plane, property, val);
+	return omap_plane_set_property(omap_crtc->plane, state,
+			property, val, blob_data);
 }
 
 static const struct drm_crtc_funcs omap_crtc_funcs = {
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index c8270e4..d3c9161 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -651,6 +651,12 @@ static struct drm_driver omap_drm_driver = {
 		.dumb_create = omap_gem_dumb_create,
 		.dumb_map_offset = omap_gem_dumb_map_offset,
 		.dumb_destroy = drm_gem_dumb_destroy,
+		.atomic_begin     = drm_atomic_begin,
+		.atomic_set_event = drm_atomic_set_event,
+		.atomic_check     = drm_atomic_check,
+		.atomic_commit    = drm_atomic_commit,
+		.atomic_end       = drm_atomic_end,
+		.atomic_funcs     = &drm_atomic_funcs,
 		.ioctls = ioctls,
 		.num_ioctls = DRM_OMAP_NUM_IOCTLS,
 		.fops = &omapdriver_fops,
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
index 284b80f..346fc8a 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -25,6 +25,7 @@
 #include <linux/types.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/omap_drm.h>
 #include <linux/platform_data/omap_drm.h>
 
@@ -178,7 +179,8 @@ int omap_plane_mode_set(struct drm_plane *plane,
 void omap_plane_install_properties(struct drm_plane *plane,
 		struct drm_mode_object *obj);
 int omap_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val);
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data);
 
 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
 		struct omap_dss_device *dssdev);
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 3cf31ee..8ae5c49 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -336,7 +336,8 @@ void omap_plane_install_properties(struct drm_plane *plane,
 }
 
 int omap_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_private *priv = plane->dev->dev_private;
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 3ab9072..b54c970 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -819,8 +819,10 @@ static enum drm_connector_status qxl_conn_detect(
 }
 
 static int qxl_conn_set_property(struct drm_connector *connector,
+				   struct drm_atomic_state *state,
 				   struct drm_property *property,
-				   uint64_t value)
+				   uint64_t value,
+				   void *blob_data)
 {
 	DRM_DEBUG("\n");
 	return 0;
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 6e93663..1984c2c 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -34,6 +34,7 @@
 #include "drmP.h"
 #include "drm/drm.h"
 #include "drm_crtc_helper.h"
+#include "drm_atomic.h"
 #include "qxl_drv.h"
 #include "qxl_object.h"
 
@@ -220,6 +221,14 @@ static struct drm_driver qxl_driver = {
 	.dumb_create = qxl_mode_dumb_create,
 	.dumb_map_offset = qxl_mode_dumb_mmap,
 	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 #if defined(CONFIG_DEBUG_FS)
 	.debugfs_init = qxl_debugfs_init,
 	.debugfs_cleanup = qxl_debugfs_takedown,
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index ea50e0a..f7ecdfc 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -359,8 +359,9 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn
 	}
 }
 
-static int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property,
-				  uint64_t val)
+static int radeon_connector_set_property(struct drm_connector *connector,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
 {
 	struct drm_device *dev = connector->dev;
 	struct radeon_device *rdev = dev->dev_private;
@@ -682,8 +683,10 @@ static void radeon_connector_destroy(struct drm_connector *connector)
 }
 
 static int radeon_lvds_set_property(struct drm_connector *connector,
+				    struct drm_atomic_state *state,
 				    struct drm_property *property,
-				    uint64_t value)
+				    uint64_t value,
+				    void *blob_data)
 {
 	struct drm_device *dev = connector->dev;
 	struct radeon_encoder *radeon_encoder;
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 15447a41..745791d 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -34,6 +34,7 @@
 #include "radeon_drv.h"
 
 #include <drm/drm_pciids.h>
+#include <drm/drm_atomic.h>
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
@@ -545,6 +546,14 @@ static struct drm_driver kms_driver = {
 	.dumb_create = radeon_mode_dumb_create,
 	.dumb_map_offset = radeon_mode_dumb_mmap,
 	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 	.fops = &radeon_driver_kms_fops,
 
 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 792fd1d..3f642a8 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -21,6 +21,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -175,6 +176,12 @@ static struct drm_driver rcar_du_driver = {
 	.dumb_create		= rcar_du_dumb_create,
 	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
 	.dumb_destroy		= drm_gem_dumb_destroy,
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
 	.fops			= &rcar_du_fops,
 	.name			= "rcar-du",
 	.desc			= "Renesas R-Car Display Unit",
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
index 3fb69d9..3a5d843 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -397,8 +397,10 @@ done:
 }
 
 static int rcar_du_plane_set_property(struct drm_plane *plane,
+				      struct drm_atomic_state *state,
 				      struct drm_property *property,
-				      uint64_t value)
+				      uint64_t value,
+				      void *blob_data)
 {
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 	struct rcar_du_group *rgrp = rplane->group;
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
index 82c84c7..7474ffb 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c
@@ -21,6 +21,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "shmob_drm_crtc.h"
@@ -285,6 +286,12 @@ static struct drm_driver shmob_drm_driver = {
 	.dumb_create		= drm_gem_cma_dumb_create,
 	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
 	.dumb_destroy		= drm_gem_dumb_destroy,
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
 	.fops			= &shmob_drm_fops,
 	.name			= "shmob-drm",
 	.desc			= "Renesas SH Mobile DRM",
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
index b20b694..3579b07 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -519,6 +519,12 @@ static struct drm_driver tilcdc_driver = {
 	.dumb_create        = drm_gem_cma_dumb_create,
 	.dumb_map_offset    = drm_gem_cma_dumb_map_offset,
 	.dumb_destroy       = drm_gem_dumb_destroy,
+	.atomic_begin       = drm_atomic_begin,
+	.atomic_set_event   = drm_atomic_set_event,
+	.atomic_check       = drm_atomic_check,
+	.atomic_commit      = drm_atomic_commit,
+	.atomic_end         = drm_atomic_end,
+	.atomic_funcs       = &drm_atomic_funcs,
 #ifdef CONFIG_DEBUG_FS
 	.debugfs_init       = tilcdc_debugfs_init,
 	.debugfs_cleanup    = tilcdc_debugfs_cleanup,
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h
index 0938036..1280a65 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h
@@ -31,6 +31,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
index 595068b..693f3ec 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c
@@ -206,7 +206,8 @@ static struct drm_encoder *slave_connector_best_encoder(
 }
 
 static int slave_connector_set_property(struct drm_connector *connector,
-		struct drm_property *property, uint64_t value)
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t value, void *blob_data)
 {
 	struct drm_encoder *encoder = to_slave_connector(connector)->encoder;
 	return get_slave_funcs(encoder)->set_property(encoder,
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index b44d548..936244d 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -115,9 +115,9 @@ udl_best_single_encoder(struct drm_connector *connector)
 	return encoder;
 }
 
-static int udl_connector_set_property(struct drm_connector *connector,
-				      struct drm_property *property,
-				      uint64_t val)
+static int udl_connector_set_property(struct drm_connector *connector, 
+			       struct drm_atomic_state *state, struct drm_property *property,
+			       uint64_t val, void *blob_data)
 {
 	return 0;
 }
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 3ddd6cd..52ade41 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <drm/drm_usb.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include "udl_drv.h"
 
 static struct drm_driver driver;
@@ -88,6 +89,13 @@ static struct drm_driver driver = {
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 	.gem_prime_import = udl_gem_prime_import,
 
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 	.name = DRIVER_NAME,
 	.desc = DRIVER_DESC,
 	.date = DRIVER_DATE,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 6bdd15e..3ec6792 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1433,6 +1433,13 @@ static struct drm_driver driver = {
 	.prime_fd_to_handle = vmw_prime_fd_to_handle,
 	.prime_handle_to_fd = vmw_prime_handle_to_fd,
 
+	.atomic_begin     = drm_atomic_begin,
+	.atomic_set_event = drm_atomic_set_event,
+	.atomic_check     = drm_atomic_check,
+	.atomic_commit    = drm_atomic_commit,
+	.atomic_end       = drm_atomic_end,
+	.atomic_funcs     = &drm_atomic_funcs,
+
 	.fops = &vmwgfx_driver_fops,
 	.name = VMWGFX_DRIVER_NAME,
 	.desc = VMWGFX_DRIVER_DESC,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index 6b252a8..b0d6f3a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -30,6 +30,7 @@
 
 #include "vmwgfx_reg.h"
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/vmwgfx_drm.h>
 #include <drm/drm_hashtab.h>
 #include <linux/suspend.h>
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index e7199b4..6b9db5e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -2007,8 +2007,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
 }
 
 int vmw_du_connector_set_property(struct drm_connector *connector,
+				  struct drm_atomic_state *state,
 				  struct drm_property *property,
-				  uint64_t val)
+				  uint64_t val,
+				  void *blob_data)
 {
 	return 0;
 }
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index 8d038c3..6b02fdb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -141,8 +141,10 @@ vmw_du_connector_detect(struct drm_connector *connector, bool force);
 int vmw_du_connector_fill_modes(struct drm_connector *connector,
 				uint32_t max_width, uint32_t max_height);
 int vmw_du_connector_set_property(struct drm_connector *connector,
+				  struct drm_atomic_state *state,
 				  struct drm_property *property,
-				  uint64_t val);
+				  uint64_t val,
+				  void *blob_data);
 
 
 /*
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 12f10bc..e8d0d17 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -948,6 +948,86 @@ struct drm_driver {
 			    struct drm_device *dev,
 			    uint32_t handle);
 
+	/*
+	 * Atomic functions:
+	 */
+
+	/**
+	 * atomic_begin - start a sequence of atomic updates
+	 * @dev: DRM device
+	 * @flags: the modifier flags that userspace has requested
+	 *
+	 * Begin a sequence of atomic property sets.  Returns a driver
+	 * private state object that is passed back into the various
+	 * object's set_property() fxns, and into the remainder of the
+	 * atomic funcs.  The state object should accumulate the changes
+	 * from one o more set_property()'s.  At the end, the state can
+	 * be checked, and optionally committed.
+	 *
+	 * RETURNS
+	 *   a driver state object, which is passed back in to the
+	 *   various other atomic fxns, or error (such as -EBUSY if
+	 *   there is still a pending async update)
+	 */
+	struct drm_atomic_state *(*atomic_begin)(struct drm_device *dev, uint32_t flags);
+
+	/**
+	 * atomic_set_event - set a pending event on mode object
+	 * @dev: DRM device
+	 * @state: the driver state object
+	 * @obj: the object to set the event on
+	 * @event: the event to send back
+	 *
+	 * Set pending event for an update on the specified object.  The
+	 * event is to be sent back to userspace after the update completes.
+	 */
+	int (*atomic_set_event)(struct drm_device *dev,
+			struct drm_atomic_state *state, struct drm_mode_object *obj,
+			struct drm_pending_vblank_event *event);
+
+	/**
+	 * atomic_check - validate state object
+	 * @dev: DRM device
+	 * @state: the driver state object
+	 *
+	 * Check the state object to see if the requested state is
+	 * physically possible.
+	 *
+	 * RETURNS
+	 * Zero for success or -errno
+	 */
+	int (*atomic_check)(struct drm_device *dev,
+			struct drm_atomic_state *state);
+
+	/**
+	 * atomic_commit - commit state
+	 * @dev: DRM device
+	 * @state: the driver state object
+	 *
+	 * Commit the state.  This will only be called if atomic_check()
+	 * succeeds.
+	 *
+	 * RETURNS
+	 * Zero for success or -errno
+	 */
+	int (*atomic_commit)(struct drm_device *dev,
+			struct drm_atomic_state *state);
+
+	/**
+	 * atomic_end - conclude the atomic update
+	 * @dev: DRM device
+	 * @state: the driver state object
+
+	 * Release resources associated with the state object.
+	 */
+	void (*atomic_end)(struct drm_device *dev,
+			struct drm_atomic_state *state);
+
+	/**
+	 * Funcs used by drm-atomic-helpers
+	 */
+	const struct drm_atomic_funcs *atomic_funcs;
+
 	/* Driver private ops for this object */
 	const struct vm_operations_struct *gem_vm_ops;
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
new file mode 100644
index 0000000..5274212
--- /dev/null
+++ b/include/drm/drm_atomic.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DRM_ATOMIC_HELPER_H_
+#define DRM_ATOMIC_HELPER_H_
+
+/**
+ * DOC: atomic state helpers
+ *
+ * Base helper atomic state and functions.  Drivers are free to either
+ * use these as-is, extend them, or completely replace them, in order
+ * to implement the atomic KMS API.
+ *
+ * A naive driver, with no special constraints or hw support for atomic
+ * updates may simply add the following to their driver struct:
+ *
+ *     .atomic_begin     = drm_atomic_begin,
+ *     .atomic_set_event = drm_atomic_set_event,
+ *     .atomic_check     = drm_atomic_check,
+ *     .atomic_commit    = drm_atomic_commit,
+ *     .atomic_end       = drm_atomic_end,
+ *     .atomics   = &drm_atomic_funcs,
+ *
+ * In addition, if you're plane/crtc doesn't already have it's own custom
+ * properties, then add to your plane/crtc_funcs:
+ *
+ *     .set_property     = drm_atomic_{plane,crtc}_set_property,
+ *
+ * Unlike the crtc helpers, it is intended that the atomic helpers can be
+ * used piecemeal by the drivers, either using all or overriding parts as
+ * needed.
+ *
+ * A driver which can have (for example) conflicting modes across multiple
+ * crtcs (for example, bandwidth limitations or clock/pll configuration
+ * restrictions), can simply wrap drm_atomic_check() with their own
+ * driver specific .atomic_check() function.
+ *
+ * A driver which can support true atomic updates can wrap
+ * drm_atomic_commit().
+ *
+ * A driver with custom properties should override the appropriate get_state(),
+ * check_state(), and commit_state() functions in .atomics if it uses
+ * the drm-atomic-helpers.  Otherwise it is free to use &drm_atomic_funcs
+ * as-is.
+ */
+
+/**
+ * struct drm_atomic_funcs - helper funcs used by the atomic helpers
+ */
+struct drm_atomic_funcs {
+	int dummy; /* for now */
+};
+
+const extern struct drm_atomic_funcs drm_atomic_funcs;
+
+struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
+		uint32_t flags);
+int drm_atomic_set_event(struct drm_device *dev,
+		struct drm_atomic_state *state, struct drm_mode_object *obj,
+		struct drm_pending_vblank_event *event);
+int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
+int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state);
+void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
+
+/**
+ * struct drm_atomic_state - the state object used by atomic helpers
+ */
+struct drm_atomic_state {
+	struct kref refcount;
+	struct drm_device *dev;
+	uint32_t flags;
+};
+
+static inline void
+drm_atomic_state_reference(struct drm_atomic_state *state)
+{
+	kref_get(&state->refcount);
+}
+
+static inline void
+drm_atomic_state_unreference(struct drm_atomic_state *state)
+{
+	void _drm_atomic_state_free(struct kref *kref);
+	kref_put(&state->refcount, _drm_atomic_state_free);
+}
+
+#endif /* DRM_ATOMIC_HELPER_H_ */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 698d54e..4790cad 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -200,6 +200,8 @@ struct drm_encoder;
 struct drm_pending_vblank_event;
 struct drm_plane;
 struct drm_bridge;
+struct drm_atomic_state;
+struct drm_atomic_funcs;
 
 /**
  * drm_crtc_funcs - control CRTCs for a given device
@@ -262,7 +264,9 @@ struct drm_crtc_funcs {
 			 uint32_t flags);
 
 	int (*set_property)(struct drm_crtc *crtc,
-			    struct drm_property *property, uint64_t val);
+			    struct drm_atomic_state *state,
+			    struct drm_property *property, uint64_t val,
+			    void *blob_data);
 };
 
 /**
@@ -375,8 +379,9 @@ struct drm_connector_funcs {
 	enum drm_connector_status (*detect)(struct drm_connector *connector,
 					    bool force);
 	int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
-	int (*set_property)(struct drm_connector *connector, struct drm_property *property,
-			     uint64_t val);
+	int (*set_property)(struct drm_connector *connector,
+			struct drm_atomic_state *state,
+			struct drm_property *property, uint64_t val, void *blob_data);
 	void (*destroy)(struct drm_connector *connector);
 	void (*force)(struct drm_connector *connector);
 };
@@ -541,7 +546,9 @@ struct drm_plane_funcs {
 	void (*destroy)(struct drm_plane *plane);
 
 	int (*set_property)(struct drm_plane *plane,
-			    struct drm_property *property, uint64_t val);
+			    struct drm_atomic_state *state,
+			    struct drm_property *property, uint64_t val,
+			    void *blob_data);
 };
 
 enum drm_plane_type {
-- 
1.9.0

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

* [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
  2014-05-24 18:30 ` [PATCH 01/17] drm: fix typo Rob Clark
  2014-05-24 18:30 ` [PATCH 02/17] drm: add atomic fxns Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-25 22:10   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 04/17] drm: add object property type Rob Clark
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 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.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/Makefile                  |   2 +-
 drivers/gpu/drm/drm_atomic.c              | 140 ++++++++++++++++++++++++--
 drivers/gpu/drm/drm_crtc.c                |  88 ++++++++++++-----
 drivers/gpu/drm/drm_crtc_helper.c         |   4 +-
 drivers/gpu/drm/drm_fb_cma_helper.c       |   6 +-
 drivers/gpu/drm/drm_fb_helper.c           |  18 ++--
 drivers/gpu/drm/drm_modes.c               |   4 +-
 drivers/gpu/drm/drm_modeset_lock.c        | 159 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_probe_helper.c        |  10 +-
 drivers/gpu/drm/drm_sysfs.c               |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |   4 +-
 drivers/gpu/drm/gma500/cdv_intel_lvds.c   |   6 +-
 drivers/gpu/drm/gma500/oaktrail_lvds.c    |   6 +-
 drivers/gpu/drm/gma500/psb_intel_lvds.c   |   6 +-
 drivers/gpu/drm/i915/i915_debugfs.c       |  12 +--
 drivers/gpu/drm/i915/i915_drv.c           |   8 +-
 drivers/gpu/drm/i915/i915_irq.c           |   6 +-
 drivers/gpu/drm/i915/intel_display.c      |  20 ++--
 drivers/gpu/drm/i915/intel_dp.c           |  18 ++--
 drivers/gpu/drm/i915/intel_lvds.c         |   6 +-
 drivers/gpu/drm/i915/intel_opregion.c     |   4 +-
 drivers/gpu/drm/i915/intel_overlay.c      |   4 +-
 drivers/gpu/drm/i915/intel_panel.c        |   8 +-
 drivers/gpu/drm/i915/intel_sprite.c       |   2 +-
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c  |   4 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c  |   4 +-
 drivers/gpu/drm/omapdrm/omap_crtc.c       |  10 +-
 drivers/gpu/drm/omapdrm/omap_plane.c      |   4 +-
 drivers/gpu/drm/tilcdc/tilcdc_crtc.c      |   4 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c       |  22 ++---
 include/drm/drmP.h                        |   5 -
 include/drm/drm_atomic.h                  |   7 ++
 include/drm/drm_crtc.h                    |  19 ++--
 include/drm/drm_modeset_lock.h            | 130 ++++++++++++++++++++++++
 include/uapi/drm/drm_mode.h               |   3 +
 35 files changed, 614 insertions(+), 143 deletions(-)
 create mode 100644 drivers/gpu/drm/drm_modeset_lock.c
 create mode 100644 include/drm/drm_modeset_lock.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index ba2ed83..7a6a9f5 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -14,7 +14,7 @@ drm-y       :=	drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 		drm_info.o drm_debugfs.o drm_encoder_slave.o \
 		drm_trace_points.o drm_global.o drm_prime.o \
 		drm_rect.o drm_vma_manager.o drm_flip_work.o \
-		drm_plane_helper.o drm_atomic.o
+		drm_plane_helper.o drm_atomic.o drm_modeset_lock.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 12fcbc0..45df5e5 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -57,8 +57,14 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 	ptr = &state[1];
 
 	kref_init(&state->refcount);
+
+	drm_modeset_acquire_init(&state->acquire_ctx,
+			!!(flags & DRM_MODE_ATOMIC_NOLOCK),
+			!!(flags & DRM_MODE_ATOMIC_NONBLOCK));
+
 	state->dev = dev;
 	state->flags = flags;
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_begin);
@@ -94,10 +100,102 @@ EXPORT_SYMBOL(drm_atomic_set_event);
  */
 int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
+	struct drm_atomic_state *a = state;
+	a->acquire_ctx.frozen = true;
 	return 0;  /* for now */
 }
 EXPORT_SYMBOL(drm_atomic_check);
 
+/* Note that we drop and re-acquire the locks w/ ww_mutex directly,
+ * since we keep the crtc in our list with in_atomic == true.
+ */
+
+static void drop_locks(struct drm_atomic_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
+	struct drm_modeset_lock *lock;
+
+	mutex_lock(&ctx->mutex);
+	list_for_each_entry(lock, &ctx->locked, head)
+		ww_mutex_unlock(&lock->mutex);
+	mutex_unlock(&ctx->mutex);
+
+	ww_acquire_fini(ww_ctx);
+}
+
+static void grab_locks(struct drm_atomic_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
+	struct drm_modeset_lock *lock, *slow_locked, *contended;
+	int ret;
+
+	lock = slow_locked = contended = NULL;
+
+
+	ww_acquire_init(ww_ctx, &crtc_ww_class);
+
+	/*
+	 * We need to do proper rain^Hww dance.. another context
+	 * could sneak in a grab the lock in order to check
+	 * crtc->in_atomic, and we get -EDEADLK.  But the winner
+	 * will realize the mistake when it sees crtc->in_atomic
+	 * already set, and then drop lock and return -EBUSY.
+	 * So we just need to keep dancing until we win.
+	 */
+retry:
+	ret = 0;
+	list_for_each_entry(lock, &ctx->locked, head) {
+		if (lock == slow_locked) {
+			slow_locked = NULL;
+			continue;
+		}
+		contended = lock;
+		ret = ww_mutex_lock(&lock->mutex, ww_ctx);
+		if (ret)
+			goto fail;
+	}
+
+fail:
+	if (ret == -EDEADLK) {
+		/* we lost out in a seqno race, backoff, lock and retry.. */
+
+		list_for_each_entry(lock, &ctx->locked, head) {
+			if (lock == contended)
+				break;
+			ww_mutex_unlock(&lock->mutex);
+		}
+
+		if (slow_locked)
+			ww_mutex_unlock(&slow_locked->mutex);
+
+		ww_mutex_lock_slow(&contended->mutex, ww_ctx);
+		slow_locked = contended;
+		goto retry;
+	}
+	WARN_ON(ret);   /* if we get EALREADY then something is fubar */
+}
+
+static void commit_locks(struct drm_atomic_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	/* and properly release them (clear in_atomic, remove from list): */
+	drm_modeset_drop_locks(&a->acquire_ctx);
+	ww_acquire_fini(ww_ctx);
+	a->committed = true;
+}
+
+static int atomic_commit(struct drm_atomic_state *a,
+		struct ww_acquire_ctx *ww_ctx)
+{
+	int ret = 0;
+
+	commit_locks(a, ww_ctx);
+
+	return ret;
+}
+
 /**
  * drm_atomic_commit - commit state
  * @dev: DRM device
@@ -109,30 +207,60 @@ EXPORT_SYMBOL(drm_atomic_check);
  * RETURNS
  * Zero for success or -errno
  */
-int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state)
+int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *a)
 {
-	return 0;  /* for now */
+	return atomic_commit(a, &a->acquire_ctx.ww_ctx);
 }
 EXPORT_SYMBOL(drm_atomic_commit);
 
 /**
+ * drm_atomic_commit_unlocked - like drm_atomic_commit
+ * but can be called back by driver in other thread.  Manages the lock
+ * transfer from initiating thread.
+ */
+int drm_atomic_commit_unlocked(struct drm_device *dev,
+		struct drm_atomic_state *a)
+{
+	struct ww_acquire_ctx ww_ctx;
+	grab_locks(a, &ww_ctx);
+	return atomic_commit(a, &ww_ctx);
+}
+EXPORT_SYMBOL(drm_atomic_commit_unlocked);
+
+/**
  * drm_atomic_end - conclude the atomic update
  * @dev: DRM device
  * @state: the driver state object
  *
  * Release resources associated with the state object.
  */
-void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state)
+void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *a)
 {
-	drm_atomic_state_unreference(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->acquire_ctx.ww_ctx);
+
+	drm_atomic_state_unreference(a);
 }
 EXPORT_SYMBOL(drm_atomic_end);
 
 void _drm_atomic_state_free(struct kref *kref)
 {
-	struct drm_atomic_state *state =
+	struct drm_atomic_state *a =
 		container_of(kref, struct drm_atomic_state, refcount);
-	kfree(state);
+
+	/* in case we haven't already: */
+	if (!a->committed) {
+		grab_locks(a, &a->acquire_ctx.ww_ctx);
+		commit_locks(a, &a->acquire_ctx.ww_ctx);
+	}
+
+	drm_modeset_acquire_fini(&a->acquire_ctx);
+
+	kfree(a);
 }
 EXPORT_SYMBOL(_drm_atomic_state_free);
 
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 1876abb..dd10e4c 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -37,8 +37,8 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_atomic.h>
 
-#include "drm_crtc_internal.h"
 
 /**
  * drm_modeset_lock_all - take all modeset locks
@@ -50,12 +50,32 @@
  */
 void drm_modeset_lock_all(struct drm_device *dev)
 {
-	struct drm_crtc *crtc;
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_modeset_acquire_ctx *ctx;
+	int ret;
 
-	mutex_lock(&dev->mode_config.mutex);
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (WARN_ON(!ctx))
+		return;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex);
+retry:
+	drm_modeset_acquire_init(ctx, false, false);
+
+	ret = drm_modeset_lock(&config->mutex, ctx) ||
+			drm_modeset_lock_all_crtcs(dev, ctx);
+	if (ret == -EDEADLK) {
+		drm_modeset_drop_locks(ctx);
+		ww_acquire_fini(&ctx->ww_ctx);
+		drm_modeset_acquire_fini(ctx);
+		goto retry;
+	}
+
+	WARN_ON(config->acquire_ctx);
+
+	/* now we hold the locks, so now that it is safe, stash the
+	 * ctx for drm_modeset_unlock_all():
+	 */
+	config->acquire_ctx = ctx;
 }
 EXPORT_SYMBOL(drm_modeset_lock_all);
 
@@ -67,12 +87,18 @@ EXPORT_SYMBOL(drm_modeset_lock_all);
  */
 void drm_modeset_unlock_all(struct drm_device *dev)
 {
-	struct drm_crtc *crtc;
+	struct drm_mode_config *config = &dev->mode_config;
+	struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
-		mutex_unlock(&crtc->mutex);
+	if (WARN_ON(!ctx))
+		return;
+
+	config->acquire_ctx = NULL;
+	drm_modeset_drop_locks(ctx);
+	ww_acquire_fini(&ctx->ww_ctx);
+	drm_modeset_acquire_fini(ctx);
 
-	mutex_unlock(&dev->mode_config.mutex);
+	kfree(ctx);
 }
 EXPORT_SYMBOL(drm_modeset_unlock_all);
 
@@ -91,9 +117,9 @@ 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));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 }
 EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
 
@@ -691,6 +717,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);
 
+DEFINE_WW_CLASS(crtc_ww_class);
+
 /**
  * drm_crtc_init_with_planes - Initialise a new CRTC object with
  *    specified primary and cursor planes.
@@ -710,6 +738,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 			      void *cursor,
 			      const struct drm_crtc_funcs *funcs)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
 	crtc->dev = dev;
@@ -717,8 +746,9 @@ int drm_crtc_init_with_planes(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);
+	/* dropped by _unlock_all(): */
+	drm_modeset_lock(&crtc->mutex, config->acquire_ctx);
 
 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
 	if (ret)
@@ -755,6 +785,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--;
@@ -1794,7 +1826,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
 	DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 
 	obj = drm_mode_object_find(dev, out_resp->connector_id,
 				   DRM_MODE_OBJECT_CONNECTOR);
@@ -1895,7 +1927,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 	out_resp->count_encoders = encoders_count;
 
 out:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return ret;
 }
@@ -2496,7 +2528,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;
@@ -2520,7 +2552,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 		}
 	}
 out:
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 
 	return ret;
 
@@ -3929,20 +3961,25 @@ 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_config *config = &dev->mode_config;
 	struct drm_atomic_state *state;
 	int ret = -EINVAL;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-	drm_modeset_lock_all(dev);
-
+retry:
 	state = dev->driver->atomic_begin(dev, 0);
 	if (IS_ERR(state)) {
 		ret = PTR_ERR(state);
-		goto out_unlock;
+		goto out;
 	}
 
+	ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) ||
+			drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
+		goto out;
+
 	ret = drm_mode_set_obj_prop_id(dev, state,
 			arg->obj_id, arg->obj_type,
 			arg->prop_id, arg->value, NULL);
@@ -3957,8 +3994,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
 
 out:
 	dev->driver->atomic_end(dev, state);
-out_unlock:
-	drm_modeset_unlock_all(dev);
+	if (ret == -EDEADLK)
+		goto retry;
 	return ret;
 }
 
@@ -4195,7 +4232,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->primary->fb == NULL) {
 		/* The framebuffer is currently unbound, presumably
 		 * due to a hotplug event, that userspace has not
@@ -4279,7 +4316,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;
 }
@@ -4643,7 +4680,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling);
  */
 void drm_mode_config_init(struct drm_device *dev)
 {
-	mutex_init(&dev->mode_config.mutex);
+	drm_modeset_lock_init(&dev->mode_config.mutex);
 	mutex_init(&dev->mode_config.idr_mutex);
 	mutex_init(&dev->mode_config.fb_lock);
 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
@@ -4743,5 +4780,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
 	}
 
 	idr_destroy(&dev->mode_config.crtc_idr);
+	drm_modeset_lock_fini(&dev->mode_config.mutex);
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 54e8fdb..8c30259 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -88,7 +88,7 @@ bool drm_helper_encoder_in_use(struct drm_encoder *encoder)
 	struct drm_connector *connector;
 	struct drm_device *dev = encoder->dev;
 
-	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder == encoder)
 			return true;
@@ -112,7 +112,7 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
 	struct drm_encoder *encoder;
 	struct drm_device *dev = crtc->dev;
 
-	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
 		if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder))
 			return true;
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 61b5a47..65540b7 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -211,13 +211,13 @@ int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg)
 	struct drm_framebuffer *fb;
 	int ret;
 
-	ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+	ret = drm_modeset_lock_interruptible(&dev->mode_config.mutex, NULL);
 	if (ret)
 		return ret;
 
 	ret = mutex_lock_interruptible(&dev->struct_mutex);
 	if (ret) {
-		mutex_unlock(&dev->mode_config.mutex);
+		drm_modeset_unlock(&dev->mode_config.mutex);
 		return ret;
 	}
 
@@ -225,7 +225,7 @@ int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg)
 		drm_fb_cma_describe(fb, m);
 
 	mutex_unlock(&dev->struct_mutex);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index e95ed58..142f6bd 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -331,7 +331,11 @@ static bool drm_fb_helper_force_kernel_mode(void)
 		if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
 			continue;
 
-		if (!mutex_trylock(&dev->mode_config.mutex)) {
+		/* NOTE: we use lockless flag below to avoid grabbing other
+		 * modeset locks.  So just trylock the underlying mutex
+		 * directly:
+		 */
+		if (!mutex_trylock(&dev->mode_config.mutex.mutex.base)) {
 			error = true;
 			continue;
 		}
@@ -340,7 +344,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
 		if (ret)
 			error = true;
 
-		mutex_unlock(&dev->mode_config.mutex);
+		mutex_unlock(&dev->mode_config.mutex.mutex.base);
 	}
 	return error;
 }
@@ -1557,11 +1561,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
 
 	drm_fb_helper_parse_command_line(fb_helper);
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	count = drm_fb_helper_probe_connector_modes(fb_helper,
 						    dev->mode_config.max_width,
 						    dev->mode_config.max_height);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	/*
 	 * we shouldn't end up with no modes here.
 	 */
@@ -1601,10 +1605,10 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 	if (!fb_helper->fb)
 		return 0;
 
-	mutex_lock(&fb_helper->dev->mode_config.mutex);
+	drm_modeset_lock(&fb_helper->dev->mode_config.mutex, NULL);
 	if (!drm_fb_helper_is_bound(fb_helper)) {
 		fb_helper->delayed_hotplug = true;
-		mutex_unlock(&fb_helper->dev->mode_config.mutex);
+		drm_modeset_unlock(&fb_helper->dev->mode_config.mutex);
 		return 0;
 	}
 	DRM_DEBUG_KMS("\n");
@@ -1613,7 +1617,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 	max_height = fb_helper->fb->height;
 
 	drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
-	mutex_unlock(&fb_helper->dev->mode_config.mutex);
+	drm_modeset_unlock(&fb_helper->dev->mode_config.mutex);
 
 	drm_modeset_lock_all(dev);
 	drm_setup_crtcs(fb_helper);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index bedf189..d8a6b16 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -116,7 +116,7 @@ EXPORT_SYMBOL(drm_mode_destroy);
 void drm_mode_probed_add(struct drm_connector *connector,
 			 struct drm_display_mode *mode)
 {
-	WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.mutex));
 
 	list_add_tail(&mode->head, &connector->probed_modes);
 }
@@ -1029,7 +1029,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector,
 	struct drm_display_mode *pmode, *pt;
 	int found_it;
 
-	WARN_ON(!mutex_is_locked(&connector->dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.mutex));
 
 	list_for_each_entry_safe(pmode, pt, &connector->probed_modes,
 				 head) {
diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c
new file mode 100644
index 0000000..49f1afc
--- /dev/null
+++ b/drivers/gpu/drm/drm_modeset_lock.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_modeset_lock.h>
+
+
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+		bool nolock, bool nonblock)
+{
+	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
+	INIT_LIST_HEAD(&ctx->locked);
+	mutex_init(&ctx->mutex);
+	ctx->nolock = nolock;
+	ctx->nonblock = nonblock;
+}
+
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx)
+{
+	/*
+	 * NOTE: it is intentional that ww_acquire_fini() is not called
+	 * here.. due to the way lock handover works in drm_atomic
+	 */
+	mutex_destroy(&ctx->mutex);
+}
+
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx)
+{
+	mutex_lock(&ctx->mutex);
+	while (!list_empty(&ctx->locked)) {
+		struct drm_modeset_lock *lock;
+
+		lock = list_first_entry(&ctx->locked,
+				struct drm_modeset_lock, head);
+
+		drm_modeset_unlock(lock);
+	}
+	mutex_unlock(&ctx->mutex);
+}
+
+static int modeset_lock(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx, bool interruptible)
+{
+	int ret;
+
+	if (ctx->nolock)
+		return 0;
+
+	WARN_ON(ctx->frozen);    /* all locks should be held by now! */
+
+retry:
+	if (interruptible)
+		ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx);
+	else
+		ret = ww_mutex_lock(&lock->mutex, &ctx->ww_ctx);
+	if (!ret) {
+		if (lock->atomic_pending) {
+			/* some other pending update with dropped locks */
+			ww_mutex_unlock(&lock->mutex);
+			if (ctx->nonblock)
+				return -EBUSY;
+			wait_event(lock->event, !lock->atomic_pending);
+			goto retry;
+		}
+		lock->atomic_pending = true;
+		WARN_ON(!list_empty(&lock->head));
+		list_add(&lock->head, &ctx->locked);
+	} else if (ret == -EALREADY) {
+		/* we already hold the lock.. this is fine */
+		ret = 0;
+	}
+
+	return ret;
+}
+
+/**
+ * drm_modeset_lock - take modeset lock
+ * @lock: lock to take
+ * @ctx: acquire ctx
+ *
+ * If ctx 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,
+		struct drm_modeset_acquire_ctx *ctx)
+{
+	if (ctx)
+		return modeset_lock(lock, ctx, false);
+
+	ww_mutex_lock(&lock->mutex, NULL);
+	return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock);
+
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx)
+{
+	if (ctx)
+		return modeset_lock(lock, ctx, true);
+
+	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
+}
+EXPORT_SYMBOL(drm_modeset_lock_interruptible);
+
+/**
+ * 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_crtcs - helper to drm_modeset_lock() all CRTCs
+ */
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+		struct drm_modeset_acquire_ctx *ctx)
+{
+	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, ctx);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_modeset_lock_all_crtcs);
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 79f07f2..4552fe5 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -93,7 +93,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
 	int mode_flags = 0;
 	bool verbose_prune = true;
 
-	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 
 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
 			drm_get_connector_name(connector));
@@ -255,7 +255,7 @@ static void output_poll_execute(struct work_struct *work)
 	if (!drm_kms_helper_poll)
 		return;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 
 		/* Ignore forced connectors. */
@@ -293,7 +293,7 @@ static void output_poll_execute(struct work_struct *work)
 		}
 	}
 
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	if (changed)
 		drm_kms_helper_hotplug_event(dev);
@@ -419,7 +419,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
 	if (!dev->mode_config.poll_enabled)
 		return false;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 
 		/* Only handle HPD capable connectors. */
@@ -438,7 +438,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
 			changed = true;
 	}
 
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	if (changed)
 		drm_kms_helper_hotplug_event(dev);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index c22c309..981e570 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -173,12 +173,12 @@ static ssize_t status_show(struct device *device,
 	enum drm_connector_status status;
 	int ret;
 
-	ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex);
+	ret = drm_modeset_lock_interruptible(&connector->dev->mode_config.mutex, NULL);
 	if (ret)
 		return ret;
 
 	status = connector->funcs->detect(connector, true);
-	mutex_unlock(&connector->dev->mode_config.mutex);
+	drm_modeset_unlock(&connector->dev->mode_config.mutex);
 
 	return snprintf(buf, PAGE_SIZE, "%s\n",
 			drm_get_connector_status_name(status));
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index addbf75..2371716 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -242,7 +242,7 @@ bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
 	struct drm_connector *connector;
 	bool ret = false;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 		if (connector->status != connector_status_connected)
 			continue;
@@ -250,7 +250,7 @@ bool exynos_drm_fbdev_is_anything_connected(struct drm_device *dev)
 		ret = true;
 		break;
 	}
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 7ffaed4..f9ee300 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -714,7 +714,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
 	 * Attempt to get the fixed panel mode from DDC.  Assume that the
 	 * preferred mode is the right one.
 	 */
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	psb_intel_ddc_get_modes(connector,
 				&gma_encoder->ddc_bus->adapter);
 	list_for_each_entry(scan, &connector->probed_modes, head) {
@@ -775,12 +775,12 @@ void cdv_intel_lvds_init(struct drm_device *dev,
 	}
 
 out:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	drm_sysfs_connector_add(connector);
 	return;
 
 failed_find:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	printk(KERN_ERR "Failed find\n");
 	if (gma_encoder->ddc_bus)
 		psb_intel_i2c_destroy(gma_encoder->ddc_bus);
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index 9b09946..b8c1c39 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -359,7 +359,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
 	 *    if closed, act like it's not there for now
 	 */
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
 	if (i2c_adap == NULL)
 		dev_err(dev->dev, "No ddc adapter available!\n");
@@ -402,13 +402,13 @@ void oaktrail_lvds_init(struct drm_device *dev,
 	}
 
 out:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	drm_sysfs_connector_add(connector);
 	return;
 
 failed_find:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
 	if (gma_encoder->ddc_bus)
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index 6b64d9d..3442e44 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -779,7 +779,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
 	 * Attempt to get the fixed panel mode from DDC.  Assume that the
 	 * preferred mode is the right one.
 	 */
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter);
 	list_for_each_entry(scan, &connector->probed_modes, head) {
 		if (scan->type & DRM_MODE_TYPE_PREFERRED) {
@@ -830,12 +830,12 @@ void psb_intel_lvds_init(struct drm_device *dev,
 	 * actually having one.
 	 */
 out:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	drm_sysfs_connector_add(connector);
 	return;
 
 failed_find:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	if (lvds_priv->ddc_bus)
 		psb_intel_i2c_destroy(lvds_priv->ddc_bus);
 failed_ddc:
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 18b3565..693c13b 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1635,7 +1635,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 
 #ifdef CONFIG_DRM_I915_FBDEV
 	struct drm_i915_private *dev_priv = dev->dev_private;
-	int ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+	int ret = drm_modeset_lock_interruptible(&dev->mode_config.mutex, NULL);
 	if (ret)
 		return ret;
 
@@ -1650,7 +1650,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 		   atomic_read(&fb->base.refcount.refcount));
 	describe_obj(m, fb->obj);
 	seq_putc(m, '\n');
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 #endif
 
 	mutex_lock(&dev->mode_config.fb_lock);
@@ -1681,7 +1681,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
 	struct i915_hw_context *ctx;
 	int ret, i;
 
-	ret = mutex_lock_interruptible(&dev->mode_config.mutex);
+	ret = drm_modeset_lock_interruptible(&dev->mode_config.mutex, NULL);
 	if (ret)
 		return ret;
 
@@ -1711,7 +1711,7 @@ static int i915_context_status(struct seq_file *m, void *unused)
 		seq_putc(m, '\n');
 	}
 
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return 0;
 }
@@ -2573,7 +2573,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
 
 	*source = INTEL_PIPE_CRC_SOURCE_PIPE;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list,
 			    base.head) {
 		if (!encoder->base.crtc)
@@ -2609,7 +2609,7 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
 			break;
 		}
 	}
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index e3a2482..8f2741a 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -475,10 +475,10 @@ static int i915_drm_freeze(struct drm_device *dev)
 		 * Disable CRTCs directly since we want to preserve sw state
 		 * for _thaw.
 		 */
-		mutex_lock(&dev->mode_config.mutex);
+		drm_modeset_lock(&dev->mode_config.mutex, NULL);
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
 			dev_priv->display.crtc_disable(crtc);
-		mutex_unlock(&dev->mode_config.mutex);
+		drm_modeset_unlock(&dev->mode_config.mutex);
 
 		intel_modeset_suspend_hw(dev);
 	}
@@ -546,14 +546,14 @@ static void intel_resume_hotplug(struct drm_device *dev)
 	struct drm_mode_config *mode_config = &dev->mode_config;
 	struct intel_encoder *encoder;
 
-	mutex_lock(&mode_config->mutex);
+	drm_modeset_lock(&mode_config->mutex, NULL);
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
 	list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
 		if (encoder->hot_plug)
 			encoder->hot_plug(encoder);
 
-	mutex_unlock(&mode_config->mutex);
+	drm_modeset_unlock(&mode_config->mutex);
 
 	/* Just fire off a uevent and let userspace tell us what to do */
 	drm_helper_hpd_irq_event(dev);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 2d76183..e8e931f 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -940,7 +940,7 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
 {
 	enum drm_connector_status old_status;
 
-	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 	old_status = connector->status;
 
 	connector->status = connector->funcs->detect(connector, false);
@@ -979,7 +979,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	if (!dev_priv->enable_hotplug_processing)
 		return;
 
-	mutex_lock(&mode_config->mutex);
+	drm_modeset_lock(&mode_config->mutex, NULL);
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
@@ -1026,7 +1026,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
 				changed = true;
 		}
 	}
-	mutex_unlock(&mode_config->mutex);
+	drm_modeset_unlock(&mode_config->mutex);
 
 	if (changed)
 		drm_kms_helper_hotplug_event(dev);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 72b5c34..562e575 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2363,7 +2363,7 @@ 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);
 		/*
 		 * FIXME: Once we have proper support for primary planes (and
 		 * disabling them without disabling the entire crtc) allow again
@@ -2374,7 +2374,7 @@ void intel_display_handle_reset(struct drm_device *dev)
 							       crtc->primary->fb,
 							       crtc->x,
 							       crtc->y);
-		mutex_unlock(&crtc->mutex);
+		drm_modeset_unlock(&crtc->mutex);
 	}
 }
 
@@ -8002,7 +8002,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;
@@ -8033,7 +8033,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;
 
@@ -8083,7 +8083,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 		intel_crtc->new_config = &intel_crtc->config;
 	else
 		intel_crtc->new_config = NULL;
-	mutex_unlock(&crtc->mutex);
+	drm_modeset_unlock(&crtc->mutex);
 	return false;
 }
 
@@ -8112,7 +8112,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;
 	}
 
@@ -8120,7 +8120,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,
@@ -10578,7 +10578,7 @@ enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
 {
 	struct drm_encoder *encoder = connector->base.encoder;
 
-	WARN_ON(!mutex_is_locked(&connector->base.dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&connector->base.dev->mode_config.mutex));
 
 	if (!encoder)
 		return INVALID_PIPE;
@@ -11336,9 +11336,9 @@ void intel_modeset_init(struct drm_device *dev)
 	/* Just in case the BIOS is doing something questionable. */
 	intel_disable_fbc(dev);
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	intel_modeset_setup_hw_state(dev, false);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list,
 			    base.head) {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index c63d210..e4c0b10 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1131,7 +1131,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
 	u32 pp;
 	u32 pp_stat_reg, pp_ctrl_reg;
 
-	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 
 	if (!intel_dp->want_panel_vdd && edp_have_panel_vdd(intel_dp)) {
 		struct intel_digital_port *intel_dig_port =
@@ -1168,9 +1168,9 @@ static void edp_panel_vdd_work(struct work_struct *__work)
 						 struct intel_dp, panel_vdd_work);
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	edp_panel_vdd_off_sync(intel_dp);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 }
 
 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync)
@@ -3385,9 +3385,9 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
 	drm_encoder_cleanup(encoder);
 	if (is_edp(intel_dp)) {
 		cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
-		mutex_lock(&dev->mode_config.mutex);
+		drm_modeset_lock(&dev->mode_config.mutex, NULL);
 		edp_panel_vdd_off_sync(intel_dp);
-		mutex_unlock(&dev->mode_config.mutex);
+		drm_modeset_unlock(&dev->mode_config.mutex);
 	}
 	kfree(intel_dig_port);
 }
@@ -3829,7 +3829,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 	/* We now know it's not a ghost, init power sequence regs. */
 	intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, power_seq);
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	edid = drm_get_edid(connector, &intel_dp->aux.ddc);
 	if (edid) {
 		if (drm_add_edid_modes(connector, edid)) {
@@ -3863,7 +3863,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 		if (fixed_mode)
 			fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
 	}
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
 	intel_panel_setup_backlight(connector);
@@ -3966,9 +3966,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 		drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
 		if (is_edp(intel_dp)) {
 			cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
-			mutex_lock(&dev->mode_config.mutex);
+			drm_modeset_lock(&dev->mode_config.mutex, NULL);
 			edp_panel_vdd_off_sync(intel_dp);
-			mutex_unlock(&dev->mode_config.mutex);
+			drm_modeset_unlock(&dev->mode_config.mutex);
 		}
 		drm_sysfs_connector_remove(connector);
 		drm_connector_cleanup(connector);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 4028237..bdee5d8 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -997,7 +997,7 @@ void intel_lvds_init(struct drm_device *dev)
 	 * Attempt to get the fixed panel mode from DDC.  Assume that the
 	 * preferred mode is the right one.
 	 */
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, pin));
 	if (edid) {
 		if (drm_add_edid_modes(connector, edid)) {
@@ -1091,7 +1091,7 @@ void intel_lvds_init(struct drm_device *dev)
 		goto failed;
 
 out:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
 	DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
@@ -1121,7 +1121,7 @@ out:
 	return;
 
 failed:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
 	drm_connector_cleanup(connector);
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index acde294..81d2998 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -410,7 +410,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 	if (bclp > 255)
 		return ASLC_BACKLIGHT_FAILED;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 
 	/*
 	 * Update backlight on all connectors that support backlight (usually
@@ -421,7 +421,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 		intel_panel_set_backlight(intel_connector, bclp, 255);
 	iowrite32(DIV_ROUND_UP(bclp * 100, 255) | ASLE_CBLV_VALID, &asle->cblv);
 
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index d8adc91..38f8234 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -688,7 +688,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
 	u32 swidth, swidthsw, sheight, ostride;
 
 	BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-	BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	BUG_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 	BUG_ON(!overlay);
 
 	ret = intel_overlay_release_old_vid(overlay);
@@ -793,7 +793,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
 	int ret;
 
 	BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-	BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	BUG_ON(!drm_modeset_is_locked(&dev->mode_config.mutex));
 
 	ret = intel_overlay_recover_from_interrupt(overlay);
 	if (ret != 0)
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 44ad415..4121b48 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -823,12 +823,12 @@ static int intel_backlight_device_update_status(struct backlight_device *bd)
 	struct intel_connector *connector = bl_get_data(bd);
 	struct drm_device *dev = connector->base.dev;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	DRM_DEBUG_KMS("updating intel_backlight, brightness=%d/%d\n",
 		      bd->props.brightness, bd->props.max_brightness);
 	intel_panel_set_backlight(connector, bd->props.brightness,
 				  bd->props.max_brightness);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	return 0;
 }
 
@@ -840,9 +840,9 @@ static int intel_backlight_device_get_brightness(struct backlight_device *bd)
 	int ret;
 
 	intel_runtime_pm_get(dev_priv);
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	ret = intel_panel_get_backlight(connector);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 	intel_runtime_pm_put(dev_priv);
 
 	return ret;
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 213cd58..c235546 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -55,7 +55,7 @@ static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl
 	int scanline, min, max, vblank_start;
 	DEFINE_WAIT(wait);
 
-	WARN_ON(!mutex_is_locked(&crtc->base.mutex));
+	WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex));
 
 	vblank_start = mode->crtc_vblank_start;
 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index efa19c8..7cf0f78 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -198,9 +198,9 @@ static void unref_fb_worker(struct drm_flip_work *work, void *val)
 		container_of(work, struct mdp4_crtc, unref_fb_work);
 	struct drm_device *dev = mdp4_crtc->base.dev;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	drm_framebuffer_unreference(val);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 }
 
 static void unref_cursor_worker(struct drm_flip_work *work, void *val)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index ff48944..771390b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -186,9 +186,9 @@ static void unref_fb_worker(struct drm_flip_work *work, void *val)
 		container_of(work, struct mdp5_crtc, unref_fb_work);
 	struct drm_device *dev = mdp5_crtc->base.dev;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	drm_framebuffer_unreference(val);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 }
 
 static void mdp5_crtc_destroy(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index a7c0010..a75934d 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -319,13 +319,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->primary->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->primary->fb, 0);
 	drm_gem_object_unreference_unlocked(bo);
@@ -467,7 +467,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();
 
 	/*
@@ -512,7 +512,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,
@@ -520,7 +520,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/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 8ae5c49..2d3c975 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -72,9 +72,9 @@ static void unpin_worker(struct drm_flip_work *work, void *val)
 	struct drm_device *dev = omap_plane->base.dev;
 
 	omap_framebuffer_unpin(val);
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	drm_framebuffer_unreference(val);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 }
 
 /* update which fb (if any) is pinned for scanout */
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index d642d4a0..92839ba 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -45,9 +45,9 @@ static void unref_worker(struct drm_flip_work *work, void *val)
 		container_of(work, struct tilcdc_crtc, unref_work);
 	struct drm_device *dev = tilcdc_crtc->base.dev;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	drm_framebuffer_unreference(val);
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 }
 
 static void set_scanout(struct drm_crtc *crtc, int n)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 6b9db5e..f338e3e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -187,7 +187,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 */
@@ -252,7 +252,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;
 }
@@ -273,7 +273,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,
@@ -281,7 +281,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;
 }
@@ -387,7 +387,7 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
 	struct vmw_display_unit *du;
 	struct drm_crtc *crtc;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		du = vmw_crtc_to_du(crtc);
@@ -401,7 +401,7 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
 					64, 64, du->hotspot_x, du->hotspot_y);
 	}
 
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 }
 
 /*
@@ -1506,7 +1506,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
 	int ret = 0;
 
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 	if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
 
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -1515,7 +1515,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
 			du->hotspot_y = arg->yhot;
 		}
 
-		mutex_unlock(&dev->mode_config.mutex);
+		drm_modeset_unlock(&dev->mode_config.mutex);
 		return 0;
 	}
 
@@ -1532,7 +1532,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
 	du->hotspot_y = arg->yhot;
 
 out:
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return ret;
 }
@@ -1682,7 +1682,7 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
 	struct vmw_display_unit *du;
 	struct drm_connector *con;
 
-	mutex_lock(&dev->mode_config.mutex);
+	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 
 #if 0
 	{
@@ -1712,7 +1712,7 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
 		con->status = vmw_du_connector_detect(con, true);
 	}
 
-	mutex_unlock(&dev->mode_config.mutex);
+	drm_modeset_unlock(&dev->mode_config.mutex);
 
 	return 0;
 }
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index e8d0d17..5ac3587 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -1264,11 +1264,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(const struct drm_file *file_priv)
 {
 	return file_priv->minor->type == DRM_MINOR_RENDER;
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 5274212..ff72b81 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -80,6 +80,8 @@ int drm_atomic_set_event(struct drm_device *dev,
 		struct drm_pending_vblank_event *event);
 int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
 int drm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state);
+int drm_atomic_commit_unlocked(struct drm_device *dev,
+		struct drm_atomic_state *state);
 void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
 
 /**
@@ -89,6 +91,11 @@ struct drm_atomic_state {
 	struct kref refcount;
 	struct drm_device *dev;
 	uint32_t flags;
+
+	bool committed;
+	bool checked;       /* just for debugging */
+
+	struct drm_modeset_acquire_ctx acquire_ctx;
 };
 
 static inline void
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 4790cad..83e0f88 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -33,6 +33,7 @@
 #include <linux/hdmi.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_lock.h>
 
 struct drm_device;
 struct drm_mode_set;
@@ -194,6 +195,10 @@ struct drm_property {
 	struct list_head enum_blob_list;
 };
 
+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;
@@ -273,6 +278,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.
  * @primary: primary plane for this CRTC
  * @cursor: cursor plane for this CRTC
@@ -307,7 +313,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;
 
@@ -729,7 +735,12 @@ struct drm_mode_group {
  * global restrictions are also here, e.g. dimension restrictions.
  */
 struct drm_mode_config {
-	struct mutex mutex; /* protects configuration (mode lists etc.) */
+
+	/* protects configuration (mode lists etc.): */
+	struct drm_modeset_lock mutex;
+	/* the current acquire ctx (for drm_modeset_lock_all()) */
+	struct drm_modeset_acquire_ctx *acquire_ctx;
+
 	struct mutex idr_mutex; /* for IDR management */
 	struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */
 	/* this is limited to one for now */
@@ -830,10 +841,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_with_planes(struct drm_device *dev,
 				     struct drm_crtc *crtc,
 				     struct drm_plane *primary,
diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h
new file mode 100644
index 0000000..bc83a10
--- /dev/null
+++ b/include/drm/drm_modeset_lock.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DRM_MODESET_LOCK_H_
+#define DRM_MODESET_LOCK_H_
+
+#include <linux/ww_mutex.h>
+
+struct drm_modeset_acquire_ctx {
+
+	struct ww_acquire_ctx ww_ctx;
+
+	bool nolock : 1;
+	bool nonblock : 1;
+
+	/* just for debugging, the context is 'frozen' in drm_atomic_check()
+	 * to catch anyone who might be trying to acquire a lock after it is
+	 * too late.
+	 */
+	bool frozen : 1;
+
+	/* list of 'struct drm_modeset_lock': */
+	struct list_head locked;
+
+	/* currently simply for protecting against 'locked' list manipulation
+	 * between original thread calling atomic->end() and driver thread
+	 * calling back drm_atomic_commit_unlocked().
+	 *
+	 * Other spots are sufficiently synchronized by virtue of holding
+	 * the lock's ww_mutex.  But during the lock/resource hand-over to the
+	 * driver thread (drop_locks()/grab_locks()), we cannot rely on this.
+	 */
+	struct mutex mutex;
+};
+
+/**
+ * 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_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;
+
+void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
+		bool nolock, bool nonblock);
+void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
+
+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,
+		struct drm_modeset_acquire_ctx *ctx);
+int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
+		struct drm_modeset_acquire_ctx *ctx);
+void drm_modeset_unlock(struct drm_modeset_lock *lock);
+
+struct drm_device;
+int drm_modeset_lock_all_crtcs(struct drm_device *dev,
+		struct drm_modeset_acquire_ctx *ctx);
+
+#endif /* DRM_MODESET_LOCK_H_ */
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.9.0

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

* [PATCH 04/17] drm: add object property type
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (2 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26  8:29   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 05/17] drm: add signed-range " Rob Clark
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 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.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 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 dd10e4c..c049ba7 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3142,6 +3142,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)
@@ -3162,6 +3164,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);
@@ -3299,6 +3304,23 @@ 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);
+
 /**
  * drm_property_add_enum - add a possible value to an enumeration property
  * @property: enumeration property to change
@@ -3319,14 +3341,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
 {
 	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)) {
@@ -3509,10 +3533,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++;
 	}
@@ -3534,7 +3559,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;
@@ -3556,7 +3582,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;
@@ -3712,19 +3738,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++)
@@ -3815,9 +3847,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, struct drm_atomic_state *state,
-		struct drm_property *property, uint64_t value, void *blob_data)
+static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t value, void *blob_data)
 {
 	if (drm_property_change_is_valid(property, value)) {
 		switch (obj->type) {
@@ -3831,6 +3863,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;
@@ -3866,7 +3900,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
 		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 83e0f88..65bfb8b 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -191,6 +191,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;
 };
@@ -941,6 +942,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
 
 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);
@@ -974,6 +992,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);
@@ -990,6 +1010,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.9.0

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

* [PATCH 05/17] drm: add signed-range property type
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (3 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 04/17] drm: add object property type Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 06/17] drm: helpers to find mode objects Rob Clark
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

Like range, but values are signed.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c  | 45 +++++++++++++++++++++++++++++++++------------
 include/drm/drm_crtc.h      | 12 ++++++++++++
 include/uapi/drm/drm_mode.h |  1 +
 3 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index c049ba7..fa86fba 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3267,6 +3267,22 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_property_create_bitmask);
 
+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;
+
+	property = drm_property_create(dev, flags, name, 2);
+	if (!property)
+		return NULL;
+
+	property->values[0] = min;
+	property->values[1] = max;
+
+	return property;
+}
+
 /**
  * drm_property_create - create a new ranged property type
  * @dev: drm device
@@ -3289,21 +3305,20 @@ 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 *property;
-
-	flags |= DRM_MODE_PROP_RANGE;
-
-	property = drm_property_create(dev, flags, name, 2);
-	if (!property)
-		return NULL;
-
-	property->values[0] = min;
-	property->values[1] = max;
-
-	return property;
+	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)
 {
@@ -3743,6 +3758,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 65bfb8b..cd4a61a 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);
+}
+
 enum drm_connector_force {
 	DRM_FORCE_UNSPECIFIED,
 	DRM_FORCE_OFF,
@@ -992,6 +1001,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.9.0

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

* [PATCH 06/17] drm: helpers to find mode objects
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (4 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 05/17] drm: add signed-range " Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26  8:37   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 07/17] drm: split propvals out and blob property support Rob Clark
                   ` (11 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

Add a few more useful helpers to find mode objects.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 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 fa86fba..bd12185 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1736,7 +1736,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))
@@ -1744,13 +1743,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;
@@ -1804,7 +1801,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;
@@ -1828,13 +1824,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
 	drm_modeset_lock(&dev->mode_config.mutex, NULL);
 
-	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;
 
@@ -1949,7 +1943,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;
 
@@ -1957,13 +1950,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;
@@ -2061,7 +2052,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;
@@ -2070,13 +2060,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;
@@ -2129,7 +2117,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;
@@ -2144,14 +2131,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) {
@@ -2168,15 +2153,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) {
@@ -2363,7 +2346,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;
@@ -2381,14 +2363,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) {
@@ -2471,15 +2451,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));
@@ -2511,7 +2489,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;
 
@@ -2521,12 +2498,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) {
@@ -3522,7 +3498,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;
@@ -3541,12 +3516,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)) {
@@ -3676,7 +3650,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;
@@ -3686,12 +3659,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;
@@ -3898,7 +3870,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
 		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;
 
@@ -3915,11 +3886,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
 	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);
@@ -4126,7 +4095,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;
@@ -4136,12 +4104,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;
@@ -4200,7 +4167,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;
@@ -4210,12 +4176,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) {
@@ -4268,7 +4233,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;
@@ -4282,10 +4246,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->primary->fb == NULL) {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index cd4a61a..b940a29 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1112,6 +1112,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)
 {
@@ -1128,6 +1137,30 @@ 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;
+}
+
 /* Plane list iterator for legacy (overlay only) planes. */
 #define drm_for_each_legacy_plane(plane, planelist) \
 	list_for_each_entry(plane, planelist, head) \
-- 
1.9.0

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

* [PATCH 07/17] drm: split propvals out and blob property support
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (5 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 06/17] drm: helpers to find mode objects Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 08/17] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

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

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

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c                  | 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 bd12185..e01c286 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -123,6 +123,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,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t value, void *blob_data);
+static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev,
+		int length, void *data);
+static void drm_property_destroy_blob(struct drm_device *dev,
+		struct drm_property_blob *blob);
+
 /* Avoid boilerplate.  I'm tired of typing. */
 #define DRM_ENUM_NAME_FN(fnname, list)				\
 	const char *fnname(int val)				\
@@ -755,6 +763,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 		goto out;
 
 	crtc->base.properties = &crtc->properties;
+	crtc->base.propvals = &crtc->propvals;
 
 	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
 	dev->mode_config.num_crtc++;
@@ -859,6 +868,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;
@@ -1077,6 +1087,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 		goto out;
 
 	plane->base.properties = &plane->properties;
+	plane->base.propvals = &plane->propvals;
 	plane->dev = dev;
 	plane->funcs = funcs;
 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
@@ -1894,7 +1905,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;
@@ -3418,7 +3429,7 @@ void drm_object_attach_property(struct drm_mode_object *obj,
 	}
 
 	obj->properties->ids[count] = property->base.id;
-	obj->properties->values[count] = init_val;
+	obj->propvals->values[count] = init_val;
 	obj->properties->count++;
 }
 EXPORT_SYMBOL(drm_object_attach_property);
@@ -3437,13 +3448,27 @@ EXPORT_SYMBOL(drm_object_attach_property);
  * Zero on success, error code on failure.
  */
 int drm_object_property_set_value(struct drm_mode_object *obj,
-				  struct drm_property *property, uint64_t val)
+				  struct drm_object_property_values *propvals,
+				  struct drm_property *property, uint64_t val,
+				  void *blob_data)
 {
 	int i;
 
 	for (i = 0; i < obj->properties->count; i++) {
 		if (obj->properties->ids[i] == property->base.id) {
-			obj->properties->values[i] = val;
+			struct drm_device *dev = property->dev;
+			if (property->flags & DRM_MODE_PROP_BLOB) {
+				struct drm_property_blob *blob, *old_blob = NULL;
+				old_blob = drm_property_blob_find(dev, propvals->values[i]);
+				blob = drm_property_create_blob(dev, val, blob_data);
+				if (!blob)
+					return -ENOMEM;
+				propvals->values[i] = blob->base.id;
+				if (old_blob)
+					drm_property_destroy_blob(dev, old_blob);
+			} else {
+				propvals->values[i] = val;
+			}
 			return 0;
 		}
 	}
@@ -3473,7 +3498,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;
 		}
 	}
@@ -3694,27 +3719,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;
 }
@@ -3806,7 +3839,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;
 }
 
@@ -3820,7 +3855,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;
 }
@@ -3835,7 +3871,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;
 }
@@ -3951,7 +3988,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 142f6bd..97b0d84 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -451,7 +451,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 31d872d..69160d9 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -1655,7 +1655,8 @@ cdv_intel_dp_set_property(struct drm_connector *connector,
 	struct cdv_intel_dp *intel_dp = encoder->dev_priv;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			&connector->propvals, property, val, blob_data);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index f0e4d78..f71e513 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -184,7 +184,8 @@ static int cdv_hdmi_set_property(struct drm_connector *connector,
 			return 0;
 
 		if (drm_object_property_set_value(&connector->base,
-							property, value))
+							&connector->propvals,
+							property, value, blob_data))
 			return -1;
 
 		centre = (curValue == DRM_MODE_SCALE_NO_SCALE) ||
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index f9ee300..76f7508 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 2318a46..17f2c19 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 3442e44..d1a3a0c 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 724b525..8191309 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 562e575..e9f6eb7 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -9494,8 +9494,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 e4c0b10..50f2260 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3281,7 +3281,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 8e160b0..8c8b7d3 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1055,7 +1055,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 4e36ee99..83bec46 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2064,7 +2064,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;
 
@@ -2141,7 +2142,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;
 
@@ -2153,7 +2156,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;
 
@@ -2165,7 +2170,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;
 
@@ -2177,7 +2184,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 05ee0d5..f7a7bf4 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1297,7 +1297,8 @@ static void intel_tv_find_better_format(struct drm_connector *connector)
 
 	intel_tv->tv_format = tv_mode->name;
 	drm_object_property_set_value(&connector->base,
-		connector->dev->mode_config.tv_mode_property, i);
+		&connector->propvals,
+		connector->dev->mode_config.tv_mode_property, i, NULL);
 }
 
 /**
@@ -1455,7 +1456,8 @@ intel_tv_set_property(struct drm_connector *connector,
 	int ret = 0;
 	bool changed = false;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = drm_object_property_set_value(&connector->base,
+			 &connector->propvals, property, val, blob_data);
 	if (ret < 0)
 		goto out;
 
diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c
index 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 3351e72..8948c66 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 d3c9161..3dca538 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -579,12 +579,14 @@ static void dev_lastclose(struct drm_device *dev)
 		 */
 		for (i = 0; i < priv->num_crtcs; i++) {
 			drm_object_property_set_value(&priv->crtcs[i]->base,
-					priv->rotation_prop, 0);
+					&priv->crtcs[i]->propvals,
+					priv->rotation_prop, 0, NULL);
 		}
 
 		for (i = 0; i < priv->num_planes; i++) {
 			drm_object_property_set_value(&priv->planes[i]->base,
-					priv->rotation_prop, 0);
+					&priv->planes[i]->propvals,
+					priv->rotation_prop, 0, NULL);
 		}
 	}
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index 289048d..503bac9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -111,7 +111,8 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
 
 	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
 	drm_object_property_set_value(&connector->base,
-		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+		&connector->propvals,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
 
 	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
 	if (ret < 0)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index ccfe64c..c2d0054 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -76,7 +76,8 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
 
 	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
 	drm_object_property_set_value(&connector->base,
-		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+		&connector->propvals,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
 
 	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
 	if (ret < 0)
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index faf176b..90e023a 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -741,8 +741,8 @@ int shmob_drm_connector_create(struct shmob_drm_device *sdev,
 	connector->encoder = encoder;
 
 	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-	drm_object_property_set_value(&connector->base,
-		sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+	drm_object_property_set_value(&connector->base, &connector->propvals,
+		sdev->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF, NULL);
 
 	return 0;
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index b940a29..9caf434 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];
 };
 
@@ -361,6 +366,7 @@ struct drm_crtc {
 	void *helper_private;
 
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 };
 
 
@@ -520,6 +526,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_* */
 
@@ -603,6 +610,7 @@ struct drm_plane {
 	const struct drm_plane_funcs *funcs;
 
 	struct drm_object_properties properties;
+	struct drm_object_property_values propvals;
 
 	enum drm_plane_type type;
 };
@@ -969,8 +977,9 @@ static inline bool drm_property_type_valid(struct drm_property *property)
 }
 
 extern int drm_object_property_set_value(struct drm_mode_object *obj,
+					 struct drm_object_property_values *propvals,
 					 struct drm_property *property,
-					 uint64_t val);
+					 uint64_t val, void *blob_data);
 extern int drm_object_property_get_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t *value);
-- 
1.9.0

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

* [PATCH 08/17] drm: Allow drm_mode_object_find() to look up an object of any type
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (6 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 07/17] drm: split propvals out and blob property support Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 09/17] drm: Refactor object property check code Rob Clark
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 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>
Signed-off-by: Rob Clark <robdclark@gmail.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 e01c286..511d33e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -463,7 +463,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 9caf434..31ed7c6 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.9.0

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

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

* [PATCH 09/17] drm: Refactor object property check code
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (7 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 08/17] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 10/17] drm: allow FB's in drm_mode_object_find Rob Clark
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

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

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

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

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

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 511d33e..9b95601 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -3901,6 +3901,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,
 		struct drm_atomic_state *state,
@@ -3909,20 +3922,10 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
 {
 	struct drm_mode_object *arg_obj;
 	struct drm_property *property;
-	int i;
 
 	arg_obj = drm_mode_object_find(dev, obj_id, obj_type);
-	if (!arg_obj)
+	if (!(arg_obj && object_has_prop(arg_obj, prop_id)))
 		return -ENOENT;
-	if (!arg_obj->properties)
-		return -EINVAL;
-
-	for (i = 0; i < arg_obj->properties->count; i++)
-		if (arg_obj->properties->ids[i] == prop_id)
-			break;
-
-	if (i == arg_obj->properties->count)
-		return -EINVAL;
 
 	property = drm_property_find(dev, prop_id);
 	if (!property)
-- 
1.9.0

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

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

* [PATCH 10/17] drm: allow FB's in drm_mode_object_find
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (8 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 09/17] drm: Refactor object property check code Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26  8:39   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 11/17] drm: convert plane to properties/state Rob Clark
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

We do actually want to permit FB's in atomic case, since FB will be
looked up like any other object property value in a few code paths
(like property value validation).

So split out into an internal function without the WARN_ON() which
we can use in those special cases.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c | 33 ++++++++++++++++++++++-----------
 include/drm/drm_crtc.h     |  6 ------
 2 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 9b95601..48555724 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -443,6 +443,21 @@ void drm_mode_object_put(struct drm_device *dev,
 	mutex_unlock(&dev->mode_config.idr_mutex);
 }
 
+static struct drm_mode_object *_object_find(struct drm_device *dev,
+		uint32_t id, uint32_t type)
+{
+	struct drm_mode_object *obj = NULL;
+
+	mutex_lock(&dev->mode_config.idr_mutex);
+	obj = idr_find(&dev->mode_config.crtc_idr, id);
+	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
+	    (obj->id != id))
+		obj = NULL;
+	mutex_unlock(&dev->mode_config.idr_mutex);
+
+	return obj;
+}
+
 /**
  * drm_mode_object_find - look up a drm object with static lifetime
  * @dev: drm device
@@ -455,23 +470,19 @@ void drm_mode_object_put(struct drm_device *dev,
 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 		uint32_t id, uint32_t type)
 {
-	struct drm_mode_object *obj = NULL;
-
 	/* Framebuffers are reference counted and need their own lookup
 	 * function.*/
 	WARN_ON(type == DRM_MODE_OBJECT_FB);
-
-	mutex_lock(&dev->mode_config.idr_mutex);
-	obj = idr_find(&dev->mode_config.crtc_idr, id);
-	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
-	    (obj->id != id))
-		obj = NULL;
-	mutex_unlock(&dev->mode_config.idr_mutex);
-
-	return obj;
+	return _object_find(dev, id, type);
 }
 EXPORT_SYMBOL(drm_mode_object_find);
 
+static struct drm_mode_object *
+drm_property_get_obj(struct drm_property *property, uint64_t value)
+{
+	return _object_find(property->dev, value, property->values[0]);
+}
+
 /**
  * drm_framebuffer_init - initialize a framebuffer
  * @dev: DRM device
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 31ed7c6..547b75a 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1033,12 +1033,6 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
 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);
-- 
1.9.0

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

* [PATCH 11/17] drm: convert plane to properties/state
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (9 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 10/17] drm: allow FB's in drm_mode_object_find Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26  9:12   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 12/17] drm: convert crtc " Rob Clark
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

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

The same should be done for CRTC, encoder, and connector, but this
patch only includes the first part (plane).

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

diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 601ba9a..041ea89 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -7,6 +7,7 @@
  * published by the Free Software Foundation.
  */
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include "armada_crtc.h"
 #include "armada_drm.h"
 #include "armada_fb.h"
@@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane,
 {
 	struct armada_private *priv = plane->dev->dev_private;
 	struct armada_plane *dplane = drm_to_armada_plane(plane);
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
 	bool update_attr = false;
+	int ret = 0;
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == priv->colorkey_prop) {
 #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
@@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane,
 	} else if (property == priv->saturation_prop) {
 		dplane->prop.saturation = val;
 		update_attr = true;
+	} else {
+		ret = drm_plane_set_property(plane, pstate, property,
+				val, blob_data);
 	}
 
 	if (update_attr && dplane->base.crtc)
 		armada_ovl_update_attr(&dplane->prop,
 				       drm_to_armada_crtc(dplane->base.crtc));
 
-	return 0;
+	return ret;
 }
 
 static const struct drm_plane_funcs armada_plane_funcs = {
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 45df5e5..403ffc5 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -24,6 +24,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
+#include <drm/drm_plane_helper.h>
 
 /**
  * drm_atomic_begin - start a sequence of atomic updates
@@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 		uint32_t flags)
 {
 	struct drm_atomic_state *state;
+	int nplanes = dev->mode_config.num_total_plane;
 	int sz;
 	void *ptr;
 
 	sz = sizeof(*state);
+	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
 
 	ptr = kzalloc(sz, GFP_KERNEL);
 
@@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 	state->dev = dev;
 	state->flags = flags;
 
+	state->planes = ptr;
+	ptr = &state->planes[nplanes];
+
+	state->pstates = ptr;
+	ptr = &state->pstates[nplanes];
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_begin);
@@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event);
 int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
 	struct drm_atomic_state *a = state;
+	int nplanes = dev->mode_config.num_total_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		if (a->planes[i]) {
+			ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
+
 	a->acquire_ctx.frozen = true;
-	return 0;  /* for now */
+
+	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_check);
 
@@ -180,6 +201,18 @@ fail:
 static void commit_locks(struct drm_atomic_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
+	struct drm_device *dev = a->dev;
+	int nplanes = dev->mode_config.num_total_plane;
+	int i;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			plane->state->state = NULL;
+			drm_plane_destroy_state(plane, a->pstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	drm_modeset_drop_locks(&a->acquire_ctx);
 	ww_acquire_fini(ww_ctx);
@@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a,
 static int atomic_commit(struct drm_atomic_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
-	int ret = 0;
+	int nplanes = a->dev->mode_config.num_total_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
 
 	commit_locks(a, ww_ctx);
 
@@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref)
 }
 EXPORT_SYMBOL(_drm_atomic_state_free);
 
+int drm_atomic_plane_set_property(struct drm_plane *plane,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
+{
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
+}
+EXPORT_SYMBOL(drm_atomic_plane_set_property);
+
+static void init_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate, struct drm_atomic_state *state)
+{
+	/* snapshot current state: */
+	*pstate = *plane->state;
+	pstate->state = state;
+	if (pstate->fb)
+		drm_framebuffer_reference(pstate->fb);
+}
+
+struct drm_plane_state *
+drm_atomic_get_plane_state(struct drm_plane *plane,
+		struct drm_atomic_state *state)
+{
+	struct drm_atomic_state *a = state;
+	struct drm_plane_state *pstate;
+	int ret;
+
+	pstate = a->pstates[plane->id];
+
+	if (!pstate) {
+		struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
+
+		/* grab lock of current crtc.. if crtc is NULL then grab all: */
+		if (plane->state->crtc)
+			ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
+		else
+			ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
+		if (ret)
+			return ERR_PTR(ret);
+
+		pstate = drm_plane_create_state(plane);
+		if (!pstate)
+			return ERR_PTR(-ENOMEM);
+		init_plane_state(plane, pstate, state);
+		a->planes[plane->id] = plane;
+		a->pstates[plane->id] = pstate;
+	}
+
+	return pstate;
+}
+EXPORT_SYMBOL(drm_atomic_get_plane_state);
+
+static void
+swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a)
+{
+	struct drm_plane_state *pstate = a->pstates[plane->id];
+
+	/* clear transient state (only valid during atomic update): */
+	pstate->update_plane = false;
+	pstate->new_fb = false;
+
+	swap(plane->state, a->pstates[plane->id]);
+	plane->base.propvals = &plane->state->propvals;
+}
+
+/* For primary plane, if the driver implements ->page_flip(), then
+ * we can use that.  But drivers can now choose not to bother with
+ * implementing page_flip().
+ */
+static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate)
+{
+	struct drm_crtc *crtc = pstate->crtc;
+	return (plane == crtc->primary) && crtc->funcs->page_flip &&
+			!pstate->update_plane;
+}
+
+/* clear crtc/fb, ie. after disable_plane().  But takes care to keep
+ * the property state in sync.  Once we get rid of plane->crtc/fb ptrs
+ * and just use state, we can get rid of this fxn:
+ */
+static void
+reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate)
+{
+	struct drm_mode_config *config = &plane->dev->mode_config;
+	drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
+	drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
+	plane->crtc = NULL;
+	plane->fb = NULL;
+}
+
+static int
+commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
+{
+	struct drm_atomic_state *a = pstate->state;
+	struct drm_framebuffer *old_fb = plane->fb;
+	struct drm_framebuffer *fb = pstate->fb;
+	bool enabled = pstate->crtc && fb;
+	int ret = 0;
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	if (!enabled) {
+		if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+				(plane->funcs == &drm_primary_helper_funcs)) {
+			/* primary plane helpers don't like ->disable_plane()..
+			 * so this hack for now until someone comes up with
+			 * something better:
+			 */
+			ret = 0;
+		} else {
+			ret = plane->funcs->disable_plane(plane);
+			reset_plane(plane, pstate);
+		}
+	} else {
+		struct drm_crtc *crtc = pstate->crtc;
+		if (pstate->update_plane ||
+				(pstate->new_fb && !can_flip(plane, pstate))) {
+			ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
+					pstate->crtc_x, pstate->crtc_y,
+					pstate->crtc_w, pstate->crtc_h,
+					pstate->src_x,  pstate->src_y,
+					pstate->src_w,  pstate->src_h);
+			if (ret == 0) {
+				/*
+				 * For page_flip(), the driver does this, but for
+				 * update_plane() it doesn't.. hurray \o/
+				 */
+				plane->crtc = crtc;
+				plane->fb = fb;
+				fb = NULL;  /* don't unref */
+			}
+
+		} else if (pstate->new_fb) {
+			ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
+			if (ret == 0) {
+				/*
+				 * Warn if the driver hasn't properly updated the plane->fb
+				 * field to reflect that the new framebuffer is now used.
+				 * Failing to do so will screw with the reference counting
+				 * on framebuffers.
+				 */
+				WARN_ON(plane->fb != fb);
+				fb = NULL;  /* don't unref */
+			}
+		} else {
+			old_fb = NULL;
+			ret = 0;
+		}
+	}
+
+	if (ret) {
+		/* Keep the old fb, don't unref it. */
+		old_fb = NULL;
+	} else {
+		/* on success, update state and fb refcnting: */
+		/* NOTE: if we ensure no driver sets plane->state->fb = NULL
+		 * on disable, we can move this up a level and not duplicate
+		 * nearly the same thing for both update_plane and disable_plane
+		 * cases..  I leave it like this for now to be paranoid due to
+		 * the slightly different ordering in the two cases in the
+		 * original code.
+		 */
+		swap_plane_state(plane, pstate->state);
+	}
+
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+
+	return ret;
+}
 
 const struct drm_atomic_funcs drm_atomic_funcs = {
+		.check_plane_state  = drm_plane_check_state,
+		.commit_plane_state = commit_plane_state,
 };
 EXPORT_SYMBOL(drm_atomic_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 48555724..b556a31 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
+		void *state;
+
+		state = dev->driver->atomic_begin(dev, 0);
+		if (IS_ERR(state)) {
+			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
+			return;
+		}
+
+		/* TODO once CRTC is converted to state/properties, we can push the
+		 * locking down into drm_atomic_commit(), since that is where
+		 * the actual changes take place..
+		 */
 		drm_modeset_lock_all(dev);
 		/* remove from any CRTC */
 		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 
 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
 			if (plane->fb == fb)
-				drm_plane_force_disable(plane);
+				drm_plane_force_disable(plane, state);
 		}
+
+		/* just disabling stuff shouldn't fail, hopefully: */
+		if(dev->driver->atomic_check(dev, state))
+			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
+		else
+			dev->driver->atomic_commit(dev, state);
+
+		dev->driver->atomic_end(dev, state);
+
 		drm_modeset_unlock_all(dev);
 	}
 
@@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 			     const uint32_t *formats, uint32_t format_count,
 			     enum drm_plane_type type)
 {
+	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	/* this is now required: */
+	WARN_ON(!funcs->set_property);
+
 	drm_modeset_lock_all(dev);
 
 	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
 	if (ret)
 		goto out;
 
+	plane->funcs = funcs;
+	plane->state = drm_plane_create_state(plane);
 	plane->base.properties = &plane->properties;
-	plane->base.propvals = &plane->propvals;
+	plane->base.propvals = &plane->state->propvals;
 	plane->dev = dev;
-	plane->funcs = funcs;
 	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
 				      GFP_KERNEL);
 	if (!plane->format_types) {
@@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
 	plane->possible_crtcs = possible_crtcs;
 	plane->type = type;
 
-	list_add_tail(&plane->head, &dev->mode_config.plane_list);
-	dev->mode_config.num_total_plane++;
+	list_add_tail(&plane->head, &config->plane_list);
+	plane->id = config->num_total_plane;
+	config->num_total_plane++;
 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-		dev->mode_config.num_overlay_plane++;
+		config->num_overlay_plane++;
 
 	drm_object_attach_property(&plane->base,
-				   dev->mode_config.plane_type_property,
+				   config->plane_type_property,
 				   plane->type);
 
+	drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
+	drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_x, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_y, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_w, 0);
+	drm_object_attach_property(&plane->base, config->prop_src_h, 0);
+
  out:
 	drm_modeset_unlock_all(dev);
 
@@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane)
 	dev->mode_config.num_total_plane--;
 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
 		dev->mode_config.num_overlay_plane--;
+	drm_plane_destroy_state(plane, plane->state);
 	drm_modeset_unlock_all(dev);
 }
 EXPORT_SYMBOL(drm_plane_cleanup);
 
+int drm_plane_check_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	unsigned int fb_width, fb_height;
+	struct drm_framebuffer *fb = state->fb;
+	int i;
+
+	/* disabling the plane is allowed: */
+	if (!fb)
+		return 0;
+
+	fb_width = fb->width << 16;
+	fb_height = fb->height << 16;
+
+	/* Check whether this plane supports the fb pixel format. */
+	for (i = 0; i < plane->format_count; i++)
+		if (fb->pixel_format == plane->format_types[i])
+			break;
+	if (i == plane->format_count) {
+		DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
+		return -EINVAL;
+	}
+
+	/* Make sure source coordinates are inside the fb. */
+	if (state->src_w > fb_width ||
+			state->src_x > fb_width - state->src_w ||
+			state->src_h > fb_height ||
+			state->src_y > fb_height - state->src_h) {
+		DRM_DEBUG_KMS("Invalid source coordinates "
+			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+			      state->src_w >> 16,
+			      ((state->src_w & 0xffff) * 15625) >> 10,
+			      state->src_h >> 16,
+			      ((state->src_h & 0xffff) * 15625) >> 10,
+			      state->src_x >> 16,
+			      ((state->src_x & 0xffff) * 15625) >> 10,
+			      state->src_y >> 16,
+			      ((state->src_y & 0xffff) * 15625) >> 10);
+		return -ENOSPC;
+	}
+
+	/* Give drivers some help against integer overflows */
+	if (state->crtc_w > INT_MAX ||
+			state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
+			state->crtc_h > INT_MAX ||
+			state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
+		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+			      state->crtc_w, state->crtc_h,
+			      state->crtc_x, state->crtc_y);
+		return -ERANGE;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_check_state);
+
+void drm_plane_commit_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	plane->state = state;
+	plane->base.propvals = &state->propvals;
+}
+EXPORT_SYMBOL(drm_plane_commit_state);
+
+int drm_plane_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data)
+{
+	struct drm_device *dev = plane->dev;
+	struct drm_mode_config *config = &dev->mode_config;
+
+	drm_object_property_set_value(&plane->base,
+			&state->propvals, property, value, blob_data);
+
+	if (property == config->prop_fb_id) {
+		struct drm_framebuffer *old_fb = state->fb;
+		/*
+		 * NOTE: the ref to the fb could have been lost between
+		 * drm_property_change_is_valid() and now.  The upshot
+		 * is that drm_framebuffer_lookup() could return NULL
+		 * and we'd disable the plane.
+		 *
+		 * We *could* return an error in that case.  But if (for
+		 * example) _setcrtc() raced with _rmfb() and _rmfb()
+		 * came after, it would disable what was enabled in the
+		 * _setcrtc().  Which is the same end result that we get
+		 * here, just skipping briefly setting the mode.
+		 */
+		state->fb = drm_framebuffer_lookup(dev, value);
+		if (old_fb)
+			drm_framebuffer_unreference(old_fb);
+		state->new_fb = true;
+	} else if (property == config->prop_crtc_id) {
+		struct drm_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) {
+			struct drm_atomic_state *a = state->state;
+			int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
+			if (ret)
+				return ret;
+		}
+		state->crtc = crtc;
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_x) {
+		state->crtc_x = U642I64(value);
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_y) {
+		state->crtc_y = U642I64(value);
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_w) {
+		state->crtc_w = value;
+		state->update_plane = true;
+	} else if (property == config->prop_crtc_h) {
+		state->crtc_h = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_x) {
+		state->src_x = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_y) {
+		state->src_y = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_w) {
+		state->src_w = value;
+		state->update_plane = true;
+	} else if (property == config->prop_src_h) {
+		state->src_h = value;
+		state->update_plane = true;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_plane_set_property);
+
 /**
  * drm_plane_force_disable - Forcibly disable a plane
  * @plane: plane to disable
@@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
  * Used when the plane's current framebuffer is destroyed,
  * and when restoring fbdev mode.
  */
-void drm_plane_force_disable(struct drm_plane *plane)
+void drm_plane_force_disable(struct drm_plane *plane,
+		struct drm_atomic_state *state)
 {
-	struct drm_framebuffer *old_fb = plane->fb;
-	int ret;
+	struct drm_mode_config *config = &plane->dev->mode_config;
 
-	if (!old_fb)
-		return;
-
-	ret = plane->funcs->disable_plane(plane);
-	if (ret) {
-		DRM_ERROR("failed to disable plane with busy fb\n");
-		return;
-	}
-	/* disconnect the plane from the fb and crtc: */
-	__drm_framebuffer_unreference(old_fb);
-	plane->fb = NULL;
-	plane->crtc = NULL;
+	/* should turn off the crtc */
+	drm_mode_plane_set_obj_prop(plane, state,
+		config->prop_crtc_id, 0, NULL);
+	drm_mode_plane_set_obj_prop(plane, state,
+		config->prop_fb_id, 0, NULL);
 }
 EXPORT_SYMBOL(drm_plane_force_disable);
 
 static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
-	struct drm_property *edid;
-	struct drm_property *dpms;
+	struct drm_property *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;
 }
@@ -2140,20 +2369,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;
+	struct drm_atomic_state *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",
@@ -2161,104 +2389,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;
-		ret = plane->funcs->disable_plane(plane);
-		if (!ret) {
-			plane->crtc = NULL;
-			plane->fb = NULL;
-		} else {
-			old_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);
-	old_fb = plane->fb;
-	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) {
-		plane->crtc = crtc;
-		plane->fb = fb;
-		fb = NULL;
-	} else {
-		old_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;
 }
 
@@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
 	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
 }
 
-static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 					   struct drm_atomic_state *state, struct drm_property *property,
 					   uint64_t value, void *blob_data)
 {
@@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
 
-static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 				      struct drm_atomic_state *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
 
 	return ret;
 }
+EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
 
-static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
 				      struct drm_atomic_state *state, struct drm_property *property,
 				      uint64_t value, void *blob_data)
 {
@@ -3882,12 +4045,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,
 		struct drm_atomic_state *state, struct drm_property *property,
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 97b0d84..b73d3b0 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -286,13 +286,28 @@ 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)
 		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
-			drm_plane_force_disable(plane);
+			drm_plane_force_disable(plane, state);
+
+	/* just disabling stuff shouldn't fail, hopefully: */
+	if(dev->driver->atomic_check(dev, state))
+		DRM_ERROR("failed to restore fbdev mode\n");
+	else
+		dev->driver->atomic_commit(dev, state);
+
+	dev->driver->atomic_end(dev, state);
 
 	for (i = 0; i < fb_helper->crtc_count; i++) {
 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index d966afa..7a32383 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -26,6 +26,7 @@
 #include <linux/list.h>
 #include <drm/drmP.h>
 #include <drm/drm_rect.h>
+#include <drm/drm_atomic.h>
 
 #define SUBPIXEL_MASK 0xffff
 
@@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy);
 const struct drm_plane_funcs drm_primary_helper_funcs = {
 	.update_plane = drm_primary_helper_update,
 	.disable_plane = drm_primary_helper_disable,
+	.set_property = drm_atomic_plane_set_property,
 	.destroy = drm_primary_helper_destroy,
 };
 EXPORT_SYMBOL(drm_primary_helper_funcs);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 9da0935..8cf7442 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -10,6 +10,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 
 #include <drm/exynos_drm.h>
 #include "exynos_drm_drv.h"
@@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
 	struct drm_device *dev = plane->dev;
 	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 	struct exynos_drm_private *dev_priv = dev->dev_private;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == dev_priv->plane_zpos_property) {
 		exynos_plane->overlay.zpos = val;
 		return 0;
 	}
 
-	return -EINVAL;
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static struct drm_plane_funcs exynos_plane_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index c235546..3f742f5 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
 	.update_plane = intel_update_plane,
 	.disable_plane = intel_disable_plane,
 	.destroy = intel_destroy_plane,
+	.set_property = drm_atomic_plane_set_property,
 };
 
 static uint32_t ilk_plane_formats[] = {
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
index 8c064dc..4c92985 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
@@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static const struct drm_plane_funcs mdp4_plane_funcs = {
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 5cbf226..53cc8c6 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static const struct drm_plane_funcs mdp5_plane_funcs = {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index 577e6aa..97b48b5 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -24,6 +24,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
 
@@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane,
 		  uint64_t value, void *blob_data)
 {
 	struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == nv_plane->props.colorkey)
 		nv_plane->colorkey = value;
@@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane,
 	else if (property == nv_plane->props.iturbt_709)
 		nv_plane->iturbt_709 = value;
 	else
-		return -EINVAL;
+		return drm_plane_set_property(plane, pstate,
+				property, value, blob_data);
 
 	if (nv_plane->set_params)
 		nv_plane->set_params(nv_plane);
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 3dca538..da80bdc 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
 
 		for (i = 0; i < priv->num_planes; i++) {
 			drm_object_property_set_value(&priv->planes[i]->base,
-					&priv->planes[i]->propvals,
+					&priv->planes[i]->state->propvals,
 					priv->rotation_prop, 0, NULL);
 		}
 	}
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 2d3c975..a9acc58 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane,
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_private *priv = plane->dev->dev_private;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
 	int ret = -EINVAL;
 
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+
 	if (property == priv->rotation_prop) {
 		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->win.rotation = val;
@@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane,
 		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->info.zorder = val;
 		ret = apply(plane);
+	} else {
+		ret = drm_plane_set_property(plane, pstate, property,
+				val, blob_data);
 	}
 
 	return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
index 3a5d843..015c76a 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
@@ -14,6 +14,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
 {
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 	struct rcar_du_group *rgrp = rplane->group;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == rgrp->planes.alpha)
 		rcar_du_plane_set_alpha(rplane, value);
@@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
 	else if (property == rgrp->planes.zpos)
 		rcar_du_plane_set_zpos(rplane, value);
 	else
-		return -EINVAL;
+		return drm_plane_set_property(plane, pstate,
+				property, value, blob_data);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
index 060ae03..ccf03ea 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
@@ -14,6 +14,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane)
 static const struct drm_plane_funcs shmob_drm_plane_funcs = {
 	.update_plane = shmob_drm_plane_update,
 	.disable_plane = shmob_drm_plane_disable,
+	.set_property = drm_atomic_plane_set_property,
 	.destroy = shmob_drm_plane_destroy,
 };
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index ff72b81..78e93ec 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -68,7 +68,8 @@
  * struct drm_atomic_funcs - helper funcs used by the atomic helpers
  */
 struct drm_atomic_funcs {
-	int dummy; /* for now */
+	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
+	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
 };
 
 const extern struct drm_atomic_funcs drm_atomic_funcs;
@@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev,
 		struct drm_atomic_state *state);
 void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
 
+int drm_atomic_plane_set_property(struct drm_plane *plane,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data);
+struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
+		struct drm_atomic_state *state);
+
+static inline int
+drm_atomic_check_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			plane->dev->driver->atomic_funcs;
+	return funcs->check_plane_state(plane, pstate);
+}
+
+static inline int
+drm_atomic_commit_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			plane->dev->driver->atomic_funcs;
+	return funcs->commit_plane_state(plane, pstate);
+}
+
 /**
  * struct drm_atomic_state - the state object used by atomic helpers
  */
@@ -91,6 +116,8 @@ struct drm_atomic_state {
 	struct kref refcount;
 	struct drm_device *dev;
 	uint32_t flags;
+	struct drm_plane **planes;
+	struct drm_plane_state **pstates;
 
 	bool committed;
 	bool checked;       /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 547b75a..58309cc 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -569,7 +569,10 @@ 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_plane_state *(*create_state)(struct drm_plane *plane);
+	void (*destroy_state)(struct drm_plane *plane,
+			    struct drm_plane_state *pstate);
+	int (*set_property)(struct drm_plane *plane, 
 			    struct drm_atomic_state *state,
 			    struct drm_property *property, uint64_t val,
 			    void *blob_data);
@@ -582,6 +585,48 @@ enum drm_plane_type {
 };
 
 /**
+ * drm_plane_state - mutable plane state
+ * @update_plane: if full update_plane() is needed (vs pageflip)
+ * @new_fb: has the fb been changed
+ * @crtc: currently bound CRTC
+ * @fb: currently bound fb
+ * @crtc_x: left position of visible portion of plane on crtc
+ * @crtc_y: upper position of visible portion of plane on crtc
+ * @crtc_w: width of visible portion of plane on crtc
+ * @crtc_h: height of visible portion of plane on crtc
+ * @src_x: left position of visible portion of plane within
+ *   plane (in 16.16)
+ * @src_y: upper position of visible portion of plane within
+ *   plane (in 16.16)
+ * @src_w: width of visible portion of plane (in 16.16)
+ * @src_h: height of visible portion of plane (in 16.16)
+ * @propvals: property values
+ * @state: current global/toplevel state object (for atomic) while an
+ *    update is in progress, NULL otherwise.
+ */
+struct drm_plane_state {
+	bool update_plane      : 1;
+	bool new_fb            : 1;
+
+	struct drm_crtc *crtc;
+	struct drm_framebuffer *fb;
+
+	/* Signed dest location allows it to be partially off screen */
+	int32_t crtc_x, crtc_y;
+	uint32_t crtc_w, crtc_h;
+
+	/* Source values are 16.16 fixed point */
+	uint32_t src_x, src_y;
+	uint32_t src_h, src_w;
+
+	bool enabled;
+
+	struct drm_object_property_values propvals;
+
+	struct drm_atomic_state *state;
+};
+
+/**
  * drm_plane - central DRM plane control structure
  * @dev: DRM device this plane belongs to
  * @head: for list management
@@ -591,6 +636,8 @@ enum drm_plane_type {
  * @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
  * @type: type of plane (overlay, primary, cursor)
@@ -608,10 +655,17 @@ struct drm_plane {
 	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;
 
 	enum drm_plane_type type;
 };
@@ -807,8 +861,20 @@ struct drm_mode_config {
 	bool poll_running;
 	struct delayed_work output_poll_work;
 
-	/* pointers to standard properties */
+	/* just so blob properties can always be in a list: */
 	struct list_head property_blob_list;
+
+	/* pointers to standard properties */
+	struct drm_property *prop_src_x;
+	struct drm_property *prop_src_y;
+	struct drm_property *prop_src_w;
+	struct drm_property *prop_src_h;
+	struct drm_property *prop_crtc_x;
+	struct drm_property *prop_crtc_y;
+	struct drm_property *prop_crtc_w;
+	struct drm_property *prop_crtc_h;
+	struct drm_property *prop_fb_id;
+	struct drm_property *prop_crtc_id;
 	struct drm_property *edid_property;
 	struct drm_property *dpms_property;
 	struct drm_property *plane_type_property;
@@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev,
 			  const uint32_t *formats, uint32_t format_count,
 			  bool is_primary);
 extern void drm_plane_cleanup(struct drm_plane *plane);
-extern void drm_plane_force_disable(struct drm_plane *plane);
+extern void drm_plane_force_disable(struct drm_plane *plane,
+		struct drm_atomic_state *state);
 extern int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 				   int x, int y,
 				   const struct drm_display_mode *mode,
 				   const struct drm_framebuffer *fb);
+extern int drm_plane_check_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+extern void drm_plane_commit_state(struct drm_plane *plane,
+		struct drm_plane_state *state);
+extern int drm_plane_set_property(struct drm_plane *plane,
+		struct drm_plane_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data);
 
 extern void drm_encoder_cleanup(struct drm_encoder *encoder);
 
@@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj,
 extern int drm_object_property_get_value(struct drm_mode_object *obj,
 					 struct drm_property *property,
 					 uint64_t *value);
+
+int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
+					   struct drm_atomic_state *state, struct drm_property *property,
+					   uint64_t value, void *blob_data);
+int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
+				      struct drm_atomic_state *state, struct drm_property *property,
+				      uint64_t value, void *blob_data);
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+				      struct drm_atomic_state *state, struct drm_property *property,
+				      uint64_t value, void *blob_data);
+
 extern int drm_framebuffer_init(struct drm_device *dev,
 				struct drm_framebuffer *fb,
 				const struct drm_framebuffer_funcs *funcs);
@@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
 	return mo ? obj_to_blob(mo) : NULL;
 }
 
+static inline struct drm_plane_state *
+drm_plane_create_state(struct drm_plane *plane)
+{
+	if (plane->funcs->create_state)
+		return plane->funcs->create_state(plane);
+	return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
+}
+
+static inline void
+drm_plane_destroy_state(struct drm_plane *plane,
+		struct drm_plane_state *pstate)
+{
+	if (pstate->fb)
+		drm_framebuffer_unreference(pstate->fb);
+	if (plane->funcs->destroy_state)
+		plane->funcs->destroy_state(plane, pstate);
+	else
+		kfree(pstate);
+}
+
 /* Plane list iterator for legacy (overlay only) planes. */
 #define drm_for_each_legacy_plane(plane, planelist) \
 	list_for_each_entry(plane, planelist, head) \
-- 
1.9.0

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

* [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (10 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 11/17] drm: convert plane to properties/state Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26  9:31   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 13/17] drm: push locking down into restore_fbdev_mode Rob Clark
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

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

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/armada/armada_crtc.c       |  11 +-
 drivers/gpu/drm/ast/ast_mode.c             |   1 +
 drivers/gpu/drm/cirrus/cirrus_mode.c       |   1 +
 drivers/gpu/drm/drm_atomic.c               | 231 ++++++++++-
 drivers/gpu/drm/drm_crtc.c                 | 598 ++++++++++++++++++-----------
 drivers/gpu/drm/drm_fb_helper.c            |   2 +-
 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/mdp/mdp4/mdp4_crtc.c   |   6 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c   |   6 +-
 drivers/gpu/drm/nouveau/dispnv04/crtc.c    |   1 +
 drivers/gpu/drm/nouveau/nv50_display.c     |   1 +
 drivers/gpu/drm/omapdrm/omap_crtc.c        |  12 +-
 drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
 drivers/gpu/drm/qxl/qxl_display.c          |   2 +
 drivers/gpu/drm/radeon/radeon_display.c    |   2 +
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c     |   2 +
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c  |   2 +
 drivers/gpu/drm/tilcdc/tilcdc_crtc.c       |   1 +
 drivers/gpu/drm/udl/udl_modeset.c          |   2 +
 drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c        |   1 +
 drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c       |   1 +
 include/drm/drm_atomic.h                   |  29 ++
 include/drm/drm_crtc.h                     | 103 ++++-
 27 files changed, 785 insertions(+), 243 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 7d3c649..6237af4 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -9,6 +9,7 @@
 #include <linux/clk.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include "armada_crtc.h"
 #include "armada_drm.h"
 #include "armada_fb.h"
@@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 {
 	struct armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
 	bool update_csc = false;
+	int ret = 0;
+
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
 
 	if (property == priv->csc_yuv_prop) {
 		dcrtc->csc_yuv_mode = val;
@@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 	} else if (property == priv->csc_rgb_prop) {
 		dcrtc->csc_rgb_mode = val;
 		update_csc = true;
+	} else {
+		ret = drm_crtc_set_property(crtc, cstate, property,
+				val, blob_data);
 	}
 
 	if (update_csc) {
@@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
 		writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
 	}
 
-	return 0;
+	return ret;
 }
 
 static struct drm_crtc_funcs armada_crtc_funcs = {
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 114aee9..c08e0e1 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
 	.cursor_move = ast_cursor_move,
 	.reset = ast_crtc_reset,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.gamma_set = ast_crtc_gamma_set,
 	.destroy = ast_crtc_destroy,
 };
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index 49332c5..3c4428c 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc)
 static const struct drm_crtc_funcs cirrus_crtc_funcs = {
 	.gamma_set = cirrus_crtc_gamma_set,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = cirrus_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 403ffc5..863a0fe 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 {
 	struct drm_atomic_state *state;
 	int nplanes = dev->mode_config.num_total_plane;
+	int ncrtcs  = dev->mode_config.num_crtc;
 	int sz;
 	void *ptr;
 
 	sz = sizeof(*state);
 	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
+	sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
 
 	ptr = kzalloc(sz, GFP_KERNEL);
 
@@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
 	state->pstates = ptr;
 	ptr = &state->pstates[nplanes];
 
+	state->crtcs = ptr;
+	ptr = &state->crtcs[ncrtcs];
+
+	state->cstates = ptr;
+	ptr = &state->cstates[ncrtcs];
+
 	return state;
 }
 EXPORT_SYMBOL(drm_atomic_begin);
@@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
 		struct drm_atomic_state *state, struct drm_mode_object *obj,
 		struct drm_pending_vblank_event *event)
 {
-	return -EINVAL;  /* for now */
+	switch (obj->type) {
+	case DRM_MODE_OBJECT_CRTC: {
+		struct drm_crtc_state *cstate =
+			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
+		if (IS_ERR(cstate))
+			return PTR_ERR(cstate);
+		cstate->event = event;
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_set_event);
 
@@ -111,6 +130,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 {
 	struct drm_atomic_state *a = state;
 	int nplanes = dev->mode_config.num_total_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
 	int i, ret = 0;
 
 	for (i = 0; i < nplanes; i++) {
@@ -120,6 +140,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
 				break;
 		}
 	}
+	for (i = 0; i < ncrtcs; i++) {
+		if (a->crtcs[i]) {
+			ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]);
+			if (ret)
+				break;
+		}
+	}
 
 	a->acquire_ctx.frozen = true;
 
@@ -203,6 +230,7 @@ static void commit_locks(struct drm_atomic_state *a,
 {
 	struct drm_device *dev = a->dev;
 	int nplanes = dev->mode_config.num_total_plane;
+	int ncrtcs = dev->mode_config.num_crtc;
 	int i;
 
 	for (i = 0; i < nplanes; i++) {
@@ -213,6 +241,14 @@ static void commit_locks(struct drm_atomic_state *a,
 		}
 	}
 
+	for (i = 0; i < ncrtcs; i++) {
+		struct drm_crtc *crtc = a->crtcs[i];
+		if (crtc) {
+			crtc->state->state = NULL;
+			drm_crtc_destroy_state(crtc, a->cstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	drm_modeset_drop_locks(&a->acquire_ctx);
 	ww_acquire_fini(ww_ctx);
@@ -223,8 +259,18 @@ static int atomic_commit(struct drm_atomic_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
 	int nplanes = a->dev->mode_config.num_total_plane;
+	int ncrtcs = a->dev->mode_config.num_crtc;
 	int i, ret = 0;
 
+	for (i = 0; i < ncrtcs; i++) {
+		struct drm_crtc *crtc = a->crtcs[i];
+		if (crtc) {
+			ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]);
+			if (ret)
+				break;
+		}
+	}
+
 	for (i = 0; i < nplanes; i++) {
 		struct drm_plane *plane = a->planes[i];
 		if (plane) {
@@ -403,6 +449,7 @@ static int
 commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 {
 	struct drm_atomic_state *a = pstate->state;
+	struct drm_crtc_state *cstate = NULL;
 	struct drm_framebuffer *old_fb = plane->fb;
 	struct drm_framebuffer *fb = pstate->fb;
 	bool enabled = pstate->crtc && fb;
@@ -425,8 +472,11 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 		}
 	} else {
 		struct drm_crtc *crtc = pstate->crtc;
+		cstate = drm_atomic_get_crtc_state(crtc, pstate->state);
 		if (pstate->update_plane ||
 				(pstate->new_fb && !can_flip(plane, pstate))) {
+/* TODO pass event to update_plane().. */
+WARN_ON(cstate->event);
 			ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
 					pstate->crtc_x, pstate->crtc_y,
 					pstate->crtc_w, pstate->crtc_h,
@@ -443,7 +493,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 			}
 
 		} else if (pstate->new_fb) {
-			ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
+			ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags);
 			if (ret == 0) {
 				/*
 				 * Warn if the driver hasn't properly updated the plane->fb
@@ -473,9 +523,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 		 * original code.
 		 */
 		swap_plane_state(plane, pstate->state);
+		if (cstate)
+			cstate->event = NULL;
 	}
 
-
 	if (fb)
 		drm_framebuffer_unreference(fb);
 	if (old_fb)
@@ -484,8 +535,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
 	return ret;
 }
 
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data)
+{
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+}
+EXPORT_SYMBOL(drm_atomic_crtc_set_property);
+
+static void init_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate, struct drm_atomic_state *state)
+{
+	/* snapshot current state: */
+	*cstate = *crtc->state;
+	cstate->state = state;
+
+	if (cstate->connector_ids) {
+		int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]);
+		cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL);
+	}
+
+	/* this should never happen.. but make sure! */
+	WARN_ON(cstate->event);
+	cstate->event = NULL;
+}
+
+struct drm_crtc_state *
+drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
+{
+	struct drm_crtc_state *cstate;
+	int ret;
+
+	cstate = a->cstates[crtc->id];
+
+	if (!cstate) {
+		ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
+		if (ret)
+			return ERR_PTR(ret);
+
+		cstate = drm_crtc_create_state(crtc);
+		if (!cstate)
+			return ERR_PTR(-ENOMEM);
+		init_crtc_state(crtc, cstate, a);
+		a->crtcs[crtc->id] = crtc;
+		a->cstates[crtc->id] = cstate;
+
+		/* we'll need it later, so make sure we have state
+		 * for primary plane too:
+		 */
+		drm_atomic_get_plane_state(crtc->primary, a);
+	}
+	return cstate;
+}
+EXPORT_SYMBOL(drm_atomic_get_crtc_state);
+
+static void
+swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
+{
+	struct drm_crtc_state *cstate = a->cstates[crtc->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->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 int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_plane_state *pstate =
+			drm_atomic_get_plane_state(crtc->primary, cstate->state);
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_connector **connector_set = get_connector_set(crtc->dev,
+			cstate->connector_ids, cstate->num_connector_ids);
+	struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate);
+	struct drm_mode_set set = {
+			.crtc = crtc,
+			.x = pstate->src_x >> 16,
+			.y = pstate->src_y >> 16,
+			.mode = mode,
+			.num_connectors = cstate->num_connector_ids,
+			.connectors = connector_set,
+			.fb = fb,
+	};
+	int ret;
+
+	if (IS_ERR(mode)) {
+		ret = PTR_ERR(mode);
+		return ret;
+	}
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	ret = drm_mode_set_config_internal(&set);
+	if (!ret) {
+		swap_crtc_state(crtc, cstate->state);
+		pstate->new_fb = pstate->update_plane = false;
+	}
+
+	if (fb)
+		drm_framebuffer_unreference(fb);
+
+	kfree(connector_set);
+	if (mode)
+		drm_mode_destroy(dev, mode);
+	return ret;
+}
+
+static int
+commit_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	struct drm_plane_state *pstate =
+			drm_atomic_get_plane_state(crtc->primary, cstate->state);
+	int ret = -EINVAL;
+
+	if (cstate->set_config)
+		return set_config(crtc, cstate);
+
+	if (!pstate->fb) {
+		/* disable */
+		struct drm_mode_set set = {
+				.crtc = crtc,
+				.fb = NULL,
+		};
+
+		ret = drm_mode_set_config_internal(&set);
+		if (!ret) {
+			swap_crtc_state(crtc, cstate->state);
+		}
+	}
+
+	return ret;
+}
+
 const struct drm_atomic_funcs drm_atomic_funcs = {
 		.check_plane_state  = drm_plane_check_state,
 		.commit_plane_state = commit_plane_state,
+
+		.check_crtc_state   = drm_crtc_check_state,
+		.commit_crtc_state  = commit_crtc_state,
 };
 EXPORT_SYMBOL(drm_atomic_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b556a31..e14d517 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -689,10 +689,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
 	struct drm_device *dev = fb->dev;
-	struct drm_crtc *crtc;
 	struct drm_plane *plane;
-	struct drm_mode_set set;
-	int ret;
 
 	WARN_ON(!list_empty(&fb->filp_head));
 
@@ -712,7 +709,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
-		void *state;
+		struct drm_atomic_state *state;
 
 		state = dev->driver->atomic_begin(dev, 0);
 		if (IS_ERR(state)) {
@@ -720,24 +717,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 			return;
 		}
 
-		/* TODO once CRTC is converted to state/properties, we can push the
-		 * locking down into drm_atomic_commit(), since that is where
-		 * the actual changes take place..
-		 */
-		drm_modeset_lock_all(dev);
-		/* remove from any CRTC */
-		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-			if (crtc->primary->fb == fb) {
-				/* should turn off the crtc */
-				memset(&set, 0, sizeof(struct drm_mode_set));
-				set.crtc = crtc;
-				set.fb = NULL;
-				ret = drm_mode_set_config_internal(&set);
-				if (ret)
-					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
-			}
-		}
-
+		/* remove from any plane */
 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
 			if (plane->fb == fb)
 				drm_plane_force_disable(plane, state);
@@ -750,8 +730,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);
@@ -782,9 +760,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
 
+	/* this is now required: */
+	WARN_ON(!funcs->set_property);
+
 	crtc->dev = dev;
 	crtc->funcs = funcs;
-	crtc->invert_dimensions = false;
+	crtc->state = drm_crtc_create_state(crtc);
+	crtc->state->invert_dimensions = false;
 
 	drm_modeset_lock_all(dev);
 	drm_modeset_lock_init(&crtc->mutex);
@@ -796,14 +778,17 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 		goto out;
 
 	crtc->base.properties = &crtc->properties;
-	crtc->base.propvals = &crtc->propvals;
+	crtc->base.propvals = &crtc->state->propvals;
 
 	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
 	dev->mode_config.num_crtc++;
 
 	crtc->primary = primary;
 	if (primary)
-		primary->possible_crtcs = 1 << drm_crtc_index(crtc);
+		primary->possible_crtcs = 1 << crtc->id;
+
+	drm_object_attach_property(&crtc->base, config->prop_mode, 0);
+	drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
 
  out:
 	drm_modeset_unlock_all(dev);
@@ -832,31 +817,245 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 	drm_mode_object_put(dev, &crtc->base);
 	list_del(&crtc->head);
 	dev->mode_config.num_crtc--;
+
+	drm_crtc_destroy_state(crtc, crtc->state);
 }
 EXPORT_SYMBOL(drm_crtc_cleanup);
 
-/**
- * drm_crtc_index - find the index of a registered CRTC
- * @crtc: CRTC to find index for
- *
- * Given a registered CRTC, return the index of that CRTC within a DRM
- * device's list of CRTCs.
- */
-unsigned int drm_crtc_index(struct drm_crtc *crtc)
+/* get display-mode from user-mode */
+struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
 {
-	unsigned int index = 0;
-	struct drm_crtc *tmp;
+	struct drm_display_mode *mode = NULL;
+	if (cstate->mode_valid) {
+		struct drm_device *dev = crtc->dev;
+		int ret;
 
-	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
-		if (tmp == crtc)
-			return index;
+		mode = drm_mode_create(dev);
+		if (!mode)
+			return ERR_PTR(-ENOMEM);
+
+		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);
+		}
 
-		index++;
+		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
 	}
+	return mode;
+}
 
-	BUG();
+static int connector_idx(struct drm_crtc_state *state,
+		uint32_t connector_id)
+{
+	int i;
+	for (i = 0; i < state->num_connector_ids; i++)
+		if (state->connector_ids[i] == connector_id)
+			return i;
+	return -1;
+}
+
+static int remove_connector(struct drm_crtc *ocrtc,
+		struct drm_crtc_state *ostate, struct drm_atomic_state *state,
+		int idx)
+{
+	struct drm_mode_config *config = &ocrtc->dev->mode_config;
+	uint32_t *new_connector_ids;
+	int a, b;
+
+	/* before deletion point: */
+	a = idx * sizeof(ostate->connector_ids[0]);
+
+	/* after deletion point: */
+	b = (ostate->num_connector_ids - 1 - idx) *
+			sizeof(ostate->connector_ids[0]);
+
+	new_connector_ids = kmalloc(a+b, GFP_KERNEL);
+	if (!new_connector_ids)
+		return -ENOMEM;
+
+	memcpy(new_connector_ids, ostate->connector_ids, a);
+	memcpy(&new_connector_ids[idx],
+			&ostate->connector_ids[idx + 1], b);
+
+	return drm_mode_crtc_set_obj_prop(ocrtc, state,
+		config->prop_connector_ids, a + b,
+		new_connector_ids);
 }
-EXPORT_SYMBOL(drm_crtc_index);
+
+static int check_connectors(struct drm_crtc *crtc,
+		struct drm_atomic_state *state, bool fix,
+		uint32_t *connector_ids, uint32_t num_connector_ids)
+{
+	struct drm_mode_config *config = &crtc->dev->mode_config;
+	struct drm_crtc *ocrtc; /* other connector */
+
+	list_for_each_entry(ocrtc, &config->crtc_list, head) {
+		struct drm_crtc_state *ostate; /* other state */
+		unsigned i;
+
+		if (ocrtc == crtc)
+			continue;
+
+		ostate = drm_atomic_get_crtc_state(crtc, state);
+		if (IS_ERR(ostate))
+			return PTR_ERR(ostate);
+
+		for (i = 0; i < num_connector_ids; i++) {
+			struct drm_connector *connector;
+			uint32_t cid = connector_ids[i];
+			int idx;
+
+retry:
+			idx = connector_idx(ostate, cid);
+			if (idx < 0)
+				continue;
+
+			if (fix) {
+				int ret = remove_connector(ocrtc,
+						ostate, state, idx);
+				if (ret)
+					return ret;
+				goto retry;
+			}
+
+			connector = drm_connector_find(crtc->dev, cid);
+			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n",
+					connector->base.id,
+					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_plane *primary = crtc->primary;
+	struct drm_plane_state *pstate =
+			drm_atomic_get_plane_state(primary, state->state);
+	struct drm_framebuffer *fb = pstate->fb;
+	int hdisplay, vdisplay;
+	struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state);
+	unsigned x, y;
+
+	if (IS_ERR(mode))
+		return PTR_ERR(mode);
+
+	/* disabling the crtc is allowed: */
+	if (!(fb && state->mode_valid))
+		return 0;
+
+	hdisplay = state->mode.hdisplay;
+	vdisplay = state->mode.vdisplay;
+
+	if (mode && drm_mode_is_stereo(mode)) {
+		struct drm_display_mode adjusted = *mode;
+
+		drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
+		hdisplay = adjusted.crtc_hdisplay;
+		vdisplay = adjusted.crtc_vdisplay;
+	}
+
+	if (state->invert_dimensions)
+		swap(hdisplay, vdisplay);
+
+	x = pstate->src_x >> 16;
+	y = pstate->src_y >> 16;
+
+	if (hdisplay > fb->width ||
+	    vdisplay > fb->height ||
+	    x > fb->width - hdisplay ||
+	    y > fb->height - vdisplay) {
+		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
+			      fb->width, fb->height, hdisplay, vdisplay,
+			      x, y, state->invert_dimensions ? " (inverted)" : "");
+		return -ENOSPC;
+	}
+
+	if (crtc->enabled && !state->set_config) {
+		if (primary->state->fb->pixel_format != fb->pixel_format) {
+			DRM_DEBUG_KMS("Page flip is not allowed to "
+					"change frame buffer format.\n");
+			return -EINVAL;
+		}
+	}
+
+	if (state->num_connector_ids == 0) {
+		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
+		return -EINVAL;
+	}
+
+	if (state->connectors_change) {
+		int ret = check_connectors(crtc, state->state, false,
+				state->connector_ids, state->num_connector_ids);
+		if (ret)
+			return ret;
+	}
+
+	if (mode)
+		drm_mode_destroy(crtc->dev, mode);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_crtc_check_state);
+
+void drm_crtc_commit_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state)
+{
+	crtc->state = state;
+	crtc->base.propvals = &state->propvals;
+}
+EXPORT_SYMBOL(drm_crtc_commit_state);
+
+int drm_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_crtc_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_mode_config *config = &dev->mode_config;
+
+	/* grab primary plane state now, to ensure locks are held, etc. */
+	drm_atomic_get_plane_state(crtc->primary, state->state);
+
+	drm_object_property_set_value(&crtc->base,
+			&state->propvals, property, value, blob_data);
+
+	if (property == config->prop_mode) {
+		if (!blob_data) {
+			memset(&state->mode, 0, sizeof(state->mode));
+			state->mode_valid = false;
+		} else {
+			/* check size: */
+			if (value < sizeof(struct drm_mode_modeinfo))
+				return -EINVAL;
+			state->mode = *(struct drm_mode_modeinfo *)blob_data;
+			state->mode_valid = true;
+		}
+		state->set_config = true;
+	} else if (property == config->prop_connector_ids) {
+		/* if connector-id's changing, we need to have all the locks: */
+		struct drm_atomic_state *a = state->state;
+		int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx);
+		if (ret)
+			return ret;
+		state->connectors_change = true;
+		state->num_connector_ids = value / sizeof(state->connector_ids[0]);
+		kfree(state->connector_ids);
+		state->connector_ids = blob_data;
+		state->set_config = true;
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_crtc_set_property);
 
 /*
  * drm_mode_remove - remove and free a mode
@@ -1239,6 +1438,10 @@ int drm_plane_check_state(struct drm_plane *plane,
 	if (!fb)
 		return 0;
 
+	/* we'll need this later during commit: */
+	if (state->crtc)
+		drm_atomic_get_crtc_state(state->crtc, state->state);
+
 	fb_width = fb->width << 16;
 	fb_height = fb->height << 16;
 
@@ -1465,6 +1668,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;
 }
 
@@ -1754,7 +1967,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)
@@ -2001,8 +2214,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
 		goto out;
 	}
 
-	crtc_resp->x = crtc->x;
-	crtc_resp->y = crtc->y;
+	crtc_resp->x = crtc->primary->state->src_x >> 16;
+	crtc_resp->y = crtc->primary->state->src_y >> 16;
 	crtc_resp->gamma_size = crtc->gamma_size;
 	if (crtc->primary->fb)
 		crtc_resp->fb_id = crtc->primary->fb->base.id;
@@ -2495,7 +2708,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 		vdisplay = adjusted.crtc_vdisplay;
 	}
 
-	if (crtc->invert_dimensions)
+	if (crtc->state->invert_dimensions)
 		swap(hdisplay, vdisplay);
 
 	if (hdisplay > fb->width ||
@@ -2504,7 +2717,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 	    y > fb->height - vdisplay) {
 		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
 			      fb->width, fb->height, hdisplay, vdisplay, x, y,
-			      crtc->invert_dimensions ? " (inverted)" : "");
+			      crtc->state->invert_dimensions ? " (inverted)" : "");
 		return -ENOSPC;
 	}
 
@@ -2531,22 +2744,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;
+	struct drm_atomic_state *state = NULL;
 	int ret;
 	int i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 
-	/* For some reason crtc x/y offsets are signed internally. */
-	if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
-		return -ERANGE;
-
-	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);
@@ -2564,55 +2770,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 				ret = -EINVAL;
 				goto out;
 			}
-			fb = crtc->primary->fb;
-			/* Make refcounting symmetric with the lookup path. */
-			drm_framebuffer_reference(fb);
+			fb_id = crtc->primary->fb->base.id;
 		} else {
-			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
-			if (!fb) {
-				DRM_DEBUG_KMS("Unknown FB ID%d\n",
-						crtc_req->fb_id);
-				ret = -ENOENT;
-				goto out;
-			}
-		}
-
-		mode = drm_mode_create(dev);
-		if (!mode) {
-			ret = -ENOMEM;
-			goto out;
+			fb_id = crtc_req->fb_id;
 		}
-
-		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) {
@@ -2620,52 +2786,65 @@ 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_plane_set_obj_prop(crtc->primary, state,
+			config->prop_crtc_id, crtc->base.id, NULL) ||
+		drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_fb_id, fb_id, NULL) ||
+		drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_src_x, crtc_req->x << 16, NULL) ||
+		drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_src_y, crtc_req->y << 16, 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;
 }
 
@@ -4028,9 +4207,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;
 }
@@ -4424,6 +4600,51 @@ out:
 	return ret;
 }
 
+static struct drm_pending_vblank_event *create_vblank_event(
+		struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
+{
+	struct drm_pending_vblank_event *e = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (file_priv->event_space < sizeof e->event) {
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		goto out;
+	}
+	file_priv->event_space -= sizeof e->event;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	e = kzalloc(sizeof *e, GFP_KERNEL);
+	if (e == NULL) {
+		spin_lock_irqsave(&dev->event_lock, flags);
+		file_priv->event_space += sizeof e->event;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		goto out;
+	}
+
+	e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
+	e->event.base.length = sizeof e->event;
+	e->event.user_data = user_data;
+	e->base.event = &e->event.base;
+	e->base.file_priv = file_priv;
+	e->base.destroy =
+		(void (*) (struct drm_pending_event *)) kfree;
+
+out:
+	return e;
+}
+
+static void destroy_vblank_event(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_pending_vblank_event *e)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	file_priv->event_space += sizeof e->event;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+	kfree(e);
+}
+
 /**
  * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
  * @dev: DRM device
@@ -4446,10 +4667,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 			     void *data, struct drm_file *file_priv)
 {
 	struct drm_mode_crtc_page_flip *page_flip = data;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_crtc *crtc;
-	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
 	struct drm_pending_vblank_event *e = NULL;
-	unsigned long flags;
+	struct drm_atomic_state *state;
 	int ret = -EINVAL;
 
 	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -4463,92 +4684,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if (!crtc)
 		return -ENOENT;
 
-	drm_modeset_lock(&crtc->mutex, NULL);
-	if (crtc->primary->fb == NULL) {
-		/* The framebuffer is currently unbound, presumably
-		 * due to a hotplug event, that userspace has not
-		 * yet discovered.
-		 */
-		ret = -EBUSY;
-		goto out;
-	}
-
-	if (crtc->funcs->page_flip == NULL)
-		goto out;
-
-	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
-	if (!fb) {
-		ret = -ENOENT;
-		goto out;
-	}
-
-	ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
-	if (ret)
-		goto out;
-
-	if (crtc->primary->fb->pixel_format != fb->pixel_format) {
-		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
-		ret = -EINVAL;
-		goto out;
-	}
+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->primary->fb;
-	ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
-	if (ret) {
-		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-			spin_lock_irqsave(&dev->event_lock, flags);
-			file_priv->event_space += sizeof e->event;
-			spin_unlock_irqrestore(&dev->event_lock, flags);
-			kfree(e);
-		}
-		/* Keep the old fb, don't unref it. */
-		old_fb = NULL;
-	} else {
-		/*
-		 * Warn if the driver hasn't properly updated the crtc->fb
-		 * field to reflect that the new framebuffer is now used.
-		 * Failing to do so will screw with the reference counting
-		 * on framebuffers.
-		 */
-		WARN_ON(crtc->primary->fb != fb);
-		/* Unref only the old framebuffer. */
-		fb = NULL;
-	}
+	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+			config->prop_fb_id, page_flip->fb_id, NULL);
+	if (ret)
+		goto out;
 
-out:
-	if (fb)
-		drm_framebuffer_unreference(fb);
-	if (old_fb)
-		drm_framebuffer_unreference(old_fb);
-	drm_modeset_unlock(&crtc->mutex);
+	ret = dev->driver->atomic_check(dev, state);
+	if (ret)
+		goto out;
+
+	ret = dev->driver->atomic_commit(dev, state);
 
+out:
+	if (ret && e)
+		destroy_vblank_event(dev, file_priv, e);
+	dev->driver->atomic_end(dev, state);
+	if (ret == -EDEADLK)
+		goto retry;
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index b73d3b0..4669e69 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -286,7 +286,7 @@ 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;
+	struct drm_atomic_state *state;
 	int i;
 
 	drm_warn_on_modeset_not_all_locked(dev);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 2a56973..f3c7e77 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -14,6 +14,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 
 #include "exynos_drm_crtc.h"
 #include "exynos_drm_drv.h"
@@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
 	struct drm_device *dev = crtc->dev;
 	struct exynos_drm_private *dev_priv = dev->dev_private;
 	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
 
 	if (property == dev_priv->crtc_mode_property) {
 		enum exynos_crtc_mode mode = val;
@@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
 		return 0;
 	}
 
-	return -EINVAL;
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
 }
 
 static struct drm_crtc_funcs exynos_crtc_funcs = {
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
index 6672732..5b6eee9 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
 	.cursor_move = gma_crtc_cursor_move,
 	.gamma_set = gma_crtc_gamma_set,
 	.set_config = gma_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = gma_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index 87b50ba..79b5692 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = {
 	.cursor_move = gma_crtc_cursor_move,
 	.gamma_set = gma_crtc_gamma_set,
 	.set_config = gma_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = gma_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e9f6eb7..53b996f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10430,6 +10430,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_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 a034ed4..ba9bd91 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = {
 	.cursor_move = mga_crtc_cursor_move,
 	.gamma_set = mga_crtc_gamma_set,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = mga_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index 7cf0f78..d0d8befd 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -471,8 +471,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
 }
 
 #define CURSOR_WIDTH 64
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 771390b..7f4ee99 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -389,8 +389,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc,
 		struct drm_atomic_state *state, struct drm_property *property,
 		uint64_t val, void *blob_data)
 {
-	// XXX
-	return -EINVAL;
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
+	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
 }
 
 static const struct drm_crtc_funcs mdp5_crtc_funcs = {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 41be342..9e24632 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
 	.cursor_move = nv04_crtc_cursor_move,
 	.gamma_set = nv_crtc_gamma_set,
 	.set_config = nouveau_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = nouveau_crtc_page_flip,
 	.destroy = nv_crtc_destroy,
 };
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 58af547..ecbffeb 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1329,6 +1329,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
 	.cursor_move = nv50_crtc_cursor_move,
 	.gamma_set = nv50_crtc_gamma_set,
 	.set_config = nouveau_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = nv50_crtc_destroy,
 	.page_flip = nouveau_crtc_page_flip,
 };
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index a75934d..772687b 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct omap_drm_private *priv = crtc->dev->dev_private;
+	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+	int ret;
+
+	if (IS_ERR(cstate))
+		return PTR_ERR(cstate);
 
 	if (property == priv->rotation_prop) {
-		crtc->invert_dimensions =
+		cstate->invert_dimensions =
 				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
 	}
 
-	return omap_plane_set_property(omap_crtc->plane, state,
+	ret = omap_plane_set_property(omap_crtc->plane, state,
 			property, val, blob_data);
+	if (ret)
+		ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+	return ret;
 }
 
 static const struct drm_crtc_funcs omap_crtc_funcs = {
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index da80bdc..3f64c47 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev)
 		 */
 		for (i = 0; i < priv->num_crtcs; i++) {
 			drm_object_property_set_value(&priv->crtcs[i]->base,
-					&priv->crtcs[i]->propvals,
+					&priv->crtcs[i]->state->propvals,
 					priv->rotation_prop, 0, NULL);
 		}
 
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index b54c970..25896a9 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -29,6 +29,7 @@
 #include "qxl_drv.h"
 #include "qxl_object.h"
 #include "drm_crtc_helper.h"
+#include "drm_atomic.h"
 
 static bool qxl_head_enabled(struct qxl_head *head)
 {
@@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
 	.cursor_set2 = qxl_crtc_cursor_set2,
 	.cursor_move = qxl_crtc_cursor_move,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = qxl_crtc_destroy,
 };
 
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 8d99d5e..cc86aac 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -32,6 +32,7 @@
 
 #include <linux/pm_runtime.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_edid.h>
 
 #include <linux/gcd.h>
@@ -546,6 +547,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = {
 	.cursor_move = radeon_crtc_cursor_move,
 	.gamma_set = radeon_crtc_gamma_set,
 	.set_config = radeon_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.destroy = radeon_crtc_destroy,
 	.page_flip = radeon_crtc_page_flip,
 };
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 299267d..f5a3d55 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -17,6 +17,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
 static const struct drm_crtc_funcs crtc_funcs = {
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = rcar_du_crtc_page_flip,
 };
 
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index 90e023a..0a5280c 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -17,6 +17,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
@@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
 static const struct drm_crtc_funcs crtc_funcs = {
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_crtc_helper_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = shmob_drm_crtc_page_flip,
 };
 
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index 92839ba..b07f116 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
 		.destroy        = tilcdc_crtc_destroy,
 		.set_config     = drm_crtc_helper_set_config,
+		.set_property   = drm_atomic_crtc_set_property,
 		.page_flip      = tilcdc_crtc_page_flip,
 };
 
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index cddc4fc..36d0116 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -14,6 +14,7 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
 #include "udl_drv.h"
 
 /*
@@ -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_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 b2b9bd2..0313b00 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
 	.cursor_move = vmw_du_crtc_cursor_move,
 	.gamma_set = vmw_du_crtc_gamma_set,
 	.destroy = vmw_ldu_crtc_destroy,
+	.set_property = drm_atomic_crtc_set_property,
 	.set_config = vmw_ldu_crtc_set_config,
 };
 
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index a95d3a0..b723e09 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
 	.gamma_set = vmw_du_crtc_gamma_set,
 	.destroy = vmw_sou_crtc_destroy,
 	.set_config = vmw_sou_crtc_set_config,
+	.set_property = drm_atomic_crtc_set_property,
 	.page_flip = vmw_du_page_flip,
 };
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 78e93ec..7946b7f 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -70,6 +70,9 @@
 struct drm_atomic_funcs {
 	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
 	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
+
+	int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
+	int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
 };
 
 const extern struct drm_atomic_funcs drm_atomic_funcs;
@@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane,
 	return funcs->commit_plane_state(plane, pstate);
 }
 
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_atomic_state *state, struct drm_property *property,
+		uint64_t val, void *blob_data);
+struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc,
+		struct drm_atomic_state *state);
+
+static inline int
+drm_atomic_check_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			crtc->dev->driver->atomic_funcs;
+	return funcs->check_crtc_state(crtc, cstate);
+}
+
+static inline int
+drm_atomic_commit_crtc_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	const struct drm_atomic_funcs *funcs =
+			crtc->dev->driver->atomic_funcs;
+	return funcs->commit_crtc_state(crtc, cstate);
+}
+
 /**
  * struct drm_atomic_state - the state object used by atomic helpers
  */
@@ -118,6 +145,8 @@ struct drm_atomic_state {
 	uint32_t flags;
 	struct drm_plane **planes;
 	struct drm_plane_state **pstates;
+	struct drm_crtc **crtcs;
+	struct drm_crtc_state **cstates;
 
 	bool committed;
 	bool checked;       /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 58309cc..2fbf13a 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -284,6 +284,10 @@ struct drm_crtc_funcs {
 			 struct drm_pending_vblank_event *event,
 			 uint32_t flags);
 
+	struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc);
+	void (*destroy_state)(struct drm_crtc *crtc,
+			    struct drm_crtc_state *cstate);
+
 	int (*set_property)(struct drm_crtc *crtc,
 			    struct drm_atomic_state *state,
 			    struct drm_property *property, uint64_t val,
@@ -291,21 +295,52 @@ struct drm_crtc_funcs {
 };
 
 /**
+ * drm_crtc_state - mutable crtc state
+ * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
+ *    invert the width/height of the crtc.  This is used if the driver
+ *    is performing 90 or 270 degree rotated scanout
+ * @mode_valid: a valid mode has been set
+ * @set_config: needs modeset (crtc->set_config())
+ * @connectors_change: the connector-ids array has changed
+ * @num_connector_ids: the number of connector-ids
+ * @connector_ids: array of connector ids
+ * @mode: current mode timings
+ * @event: pending pageflip event
+ * @propvals: property values
+ * @state: current global/toplevel state object (for atomic) while an
+ *    update is in progress, NULL otherwise.
+ */
+struct drm_crtc_state {
+	bool invert_dimensions : 1;
+	bool mode_valid        : 1;
+
+	/* transient state, only valid during atomic operation: */
+	bool set_config        : 1;
+	bool connectors_change : 1;
+
+	uint8_t num_connector_ids;
+	uint32_t *connector_ids;
+	struct drm_mode_modeinfo mode;
+
+	struct drm_pending_vblank_event *event;
+
+	struct drm_object_property_values propvals;
+
+	struct drm_atomic_state *state;
+};
+
+/**
  * drm_crtc - central CRTC control structure
  * @dev: parent DRM device
  * @head: list management
+ * @id: CRTC number, 0..n
  * @mutex: per-CRTC locking
  * @base: base KMS object for ID tracking etc.
  * @primary: primary plane for this CRTC
  * @cursor: cursor plane for this CRTC
+ * @state: the mutable state
  * @enabled: is this CRTC enabled?
- * @mode: current mode timings
  * @hwmode: mode timings as programmed to hw regs
- * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
- *    invert the width/height of the crtc.  This is used if the driver
- *    is performing 90 or 270 degree rotated scanout
- * @x: x position on screen
- * @y: y position on screen
  * @funcs: CRTC control functions
  * @gamma_size: size of gamma ramp
  * @gamma_store: gamma ramp values
@@ -322,6 +357,8 @@ struct drm_crtc {
 	struct drm_device *dev;
 	struct list_head head;
 
+	int id;
+
 	/**
 	 * crtc mutex
 	 *
@@ -337,23 +374,19 @@ struct drm_crtc {
 	struct drm_plane *primary;
 	struct drm_plane *cursor;
 
+	struct drm_crtc_state *state;
+
 	/* Temporary tracking of the old fb while a modeset is ongoing. Used
 	 * by drm_mode_set_config_internal to implement correct refcounting. */
 	struct drm_framebuffer *old_fb;
 
 	bool enabled;
 
-	/* Requested mode from modesetting. */
-	struct drm_display_mode mode;
-
 	/* Programmed mode in hw, after adjustments for encoders,
 	 * crtc, panel scaling etc. Needed for timestamping etc.
 	 */
 	struct drm_display_mode hwmode;
 
-	bool invert_dimensions;
-
-	int x, y;
 	const struct drm_crtc_funcs *funcs;
 
 	/* CRTC gamma size for reporting to userspace */
@@ -367,9 +400,15 @@ struct drm_crtc {
 	void *helper_private;
 
 	struct drm_object_properties properties;
-	struct drm_object_property_values propvals;
-};
 
+	/* These are (temporary) duplicate information from what is in the
+	 * drm_crtc_state struct..  keeping duplicate copy here makes the
+	 * switch to atomic far less intrusive.  Once all the drivers and
+	 * the crtc/fb helpers are updated, then we can remove these:
+	 */
+	int x, y;
+	struct drm_display_mode mode;
+};
 
 /**
  * drm_connector_funcs - control connectors on a given device
@@ -875,6 +914,8 @@ struct drm_mode_config {
 	struct drm_property *prop_crtc_h;
 	struct drm_property *prop_fb_id;
 	struct drm_property *prop_crtc_id;
+	struct drm_property *prop_connector_ids;
+	struct drm_property *prop_mode;
 	struct drm_property *edid_property;
 	struct drm_property *dpms_property;
 	struct drm_property *plane_type_property;
@@ -935,7 +976,8 @@ extern int drm_crtc_init(struct drm_device *dev,
 			 struct drm_crtc *crtc,
 			 const struct drm_crtc_funcs *funcs);
 extern void drm_crtc_cleanup(struct drm_crtc *crtc);
-extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
+struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate);
 
 /**
  * drm_crtc_mask - find the mask of a registered CRTC
@@ -946,9 +988,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
  */
 static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
 {
-	return 1 << drm_crtc_index(crtc);
+	return 1 << crtc->id;
 }
 
+extern int drm_crtc_check_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state);
+extern void drm_crtc_commit_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *state);
+extern int drm_crtc_set_property(struct drm_crtc *crtc,
+		struct drm_crtc_state *state,
+		struct drm_property *property,
+		uint64_t value, void *blob_data);
+
 extern void drm_connector_ida_init(void);
 extern void drm_connector_ida_destroy(void);
 extern int drm_connector_init(struct drm_device *dev,
@@ -1024,6 +1075,7 @@ 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 void drm_mode_group_destroy(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);
@@ -1251,6 +1303,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
 	return mo ? obj_to_blob(mo) : NULL;
 }
 
+static inline struct drm_crtc_state *
+drm_crtc_create_state(struct drm_crtc *crtc)
+{
+	if (crtc->funcs->create_state)
+		return crtc->funcs->create_state(crtc);
+	return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL);
+}
+
+static inline void
+drm_crtc_destroy_state(struct drm_crtc *crtc,
+		struct drm_crtc_state *cstate)
+{
+	kfree(cstate->connector_ids);
+	if (crtc->funcs->destroy_state)
+		crtc->funcs->destroy_state(crtc, cstate);
+	else
+		kfree(cstate);
+}
+
 static inline struct drm_plane_state *
 drm_plane_create_state(struct drm_plane *plane)
 {
-- 
1.9.0

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

* [PATCH 13/17] drm: push locking down into restore_fbdev_mode
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (11 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 12/17] drm: convert crtc " Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26  9:34   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 14/17] drm/msm: add atomic support Rob Clark
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 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.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/armada/armada_fbdev.c     |  4 +---
 drivers/gpu/drm/drm_fb_cma_helper.c       |  9 ++-------
 drivers/gpu/drm/drm_fb_helper.c           | 17 ++++++++---------
 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 ++-
 10 files changed, 21 insertions(+), 44 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 948cb14..9976125 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev)
 {
 	struct armada_private *priv = dev->dev_private;
 
-	drm_modeset_lock_all(dev);
 	if (priv->fbdev)
-		drm_fb_helper_restore_fbdev_mode(priv->fbdev);
-	drm_modeset_unlock_all(dev);
+		drm_fb_helper_restore_fbdev_mode(priv->fbdev, false);
 }
 
 void armada_fbdev_fini(struct drm_device *dev)
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 65540b7..ccb7a9ac 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 4669e69..3815e1a 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)
 	struct drm_atomic_state *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;
@@ -355,7 +357,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
 			continue;
 		}
 
-		ret = drm_fb_helper_restore_fbdev_mode(helper);
+		ret = drm_fb_helper_restore_fbdev_mode(helper, true);
 		if (ret)
 			error = true;
 
@@ -840,7 +842,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var);
 int drm_fb_helper_set_par(struct fb_info *info)
 {
 	struct drm_fb_helper *fb_helper = info->par;
-	struct drm_device *dev = fb_helper->dev;
 	struct fb_var_screeninfo *var = &info->var;
 
 	if (var->pixclock != 0) {
@@ -848,9 +849,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
 		return -EINVAL;
 	}
 
-	drm_modeset_lock_all(dev);
-	drm_fb_helper_restore_fbdev_mode(fb_helper);
-	drm_modeset_unlock_all(dev);
+	drm_fb_helper_restore_fbdev_mode(fb_helper, false);
 
 	if (fb_helper->delayed_hotplug) {
 		fb_helper->delayed_hotplug = false;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 2371716..486b21f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -375,7 +375,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 0180292..72a35e3 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -112,11 +112,9 @@ static void psb_driver_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 fce4a0d..28afd69 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -683,11 +683,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
 	if (!dev_priv->fbdev)
 		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 46e0f29..d24ca45 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -382,11 +382,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(int irq, void *arg)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 3f64c47..ad082f4 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -590,9 +590,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 f7fca09..85aae37 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
 
 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);
 }
 
 static void tegra_fb_output_poll_changed(struct drm_device *drm)
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 6e622f7..99598a0 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.9.0

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

* [PATCH 14/17] drm/msm: add atomic support
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (12 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 13/17] drm: push locking down into restore_fbdev_mode Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26 17:54   ` Daniel Vetter
  2014-05-24 18:30 ` [PATCH 15/17] drm: spiff out FB refcnting traces Rob Clark
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

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

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 5e1e6b0..dd12f56 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -27,6 +27,7 @@ msm-y := \
 	mdp/mdp5/mdp5_kms.o \
 	mdp/mdp5/mdp5_plane.o \
 	mdp/mdp5/mdp5_smp.o \
+	msm_atomic.o \
 	msm_drv.o \
 	msm_fb.o \
 	msm_gem.o \
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index d0d8befd..2fa6b75 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -52,7 +52,6 @@ struct mdp4_crtc {
 
 	/* if there is a pending flip, these will be non-null: */
 	struct drm_pending_vblank_event *event;
-	struct msm_fence_cb pageflip_cb;
 
 #define PENDING_CURSOR 0x1
 #define PENDING_FLIP   0x2
@@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
 	mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank);
 }
 
-static void crtc_flush(struct drm_crtc *crtc)
+void mdp4_crtc_flush(struct drm_crtc *crtc)
 {
+	struct msm_drm_private *priv = crtc->dev->dev_private;
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct mdp4_kms *mdp4_kms = get_kms(crtc);
 	uint32_t i, flush = 0;
 
+	if (priv->pending_crtcs & (1 << crtc->id))
+		return;
+
 	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
 		struct drm_plane *plane = mdp4_crtc->planes[i];
 		if (plane) {
@@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
 	 * so that we can safely queue unref to current fb (ie. next
 	 * vblank we know hw is done w/ previous scanout_fb).
 	 */
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 
 	if (mdp4_crtc->scanout_fb)
 		drm_flip_work_queue(&mdp4_crtc->unref_fb_work,
@@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
-static void pageflip_cb(struct msm_fence_cb *cb)
-{
-	struct mdp4_crtc *mdp4_crtc =
-		container_of(cb, struct mdp4_crtc, pageflip_cb);
-	struct drm_crtc *crtc = &mdp4_crtc->base;
-	struct drm_framebuffer *fb = crtc->primary->fb;
-
-	if (!fb)
-		return;
-
-	drm_framebuffer_reference(fb);
-	mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
-	update_scanout(crtc, fb);
-}
-
 static void unref_fb_worker(struct drm_flip_work *work, void *val)
 {
 	struct mdp4_crtc *mdp4_crtc =
@@ -406,7 +394,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));
 }
@@ -448,23 +436,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
 {
 	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
-	struct drm_gem_object *obj;
 	unsigned long flags;
 
+	spin_lock_irqsave(&dev->event_lock, flags);
 	if (mdp4_crtc->event) {
 		dev_err(dev->dev, "already pending flip!\n");
+		spin_unlock_irqrestore(&dev->event_lock, flags);
 		return -EBUSY;
 	}
 
-	obj = msm_framebuffer_bo(new_fb, 0);
-
-	spin_lock_irqsave(&dev->event_lock, flags);
 	mdp4_crtc->event = event;
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	update_fb(crtc, new_fb);
 
-	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
+
+	/* grab extra ref for update_scanout() */
+	drm_framebuffer_reference(new_fb);
+	mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb);
+	update_scanout(crtc, new_fb);
+
+	return 0;
 }
 
 static int mdp4_crtc_set_property(struct drm_crtc *crtc,
@@ -598,7 +590,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 	mdp4_crtc->cursor.y = y;
 	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
 
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 	request_pending(crtc, PENDING_CURSOR);
 
 	return 0;
@@ -635,8 +627,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	pending = atomic_xchg(&mdp4_crtc->pending, 0);
 
 	if (pending & PENDING_FLIP) {
-		complete_flip(crtc, NULL);
-		drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
+		if (priv->pending_crtcs & (1 << crtc->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) {
@@ -650,7 +647,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);
 	struct drm_crtc *crtc = &mdp4_crtc->base;
 	DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
-	crtc_flush(crtc);
+	mdp4_crtc_flush(crtc);
 }
 
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
@@ -730,7 +727,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)
@@ -792,8 +789,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
 	ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
 			"unref cursor", unref_cursor_worker);
 
-	INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
-
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);
 	drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
index 0bb4faa..af0bfb1 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
@@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
 	return mdp4_dtv_round_pixclk(encoder, rate);
 }
 
+static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc)
+{
+	mdp4_crtc_flush(crtc);
+}
+
 static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
 {
 	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
@@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = {
 		.disable_vblank  = mdp4_disable_vblank,
 		.get_format      = mdp_get_format,
 		.round_pixclk    = mdp4_round_pixclk,
+		.flush           = mdp4_flush,
 		.preclose        = mdp4_preclose,
 		.destroy         = mdp4_destroy,
 	},
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
index 715520c5..834454c 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
@@ -184,6 +184,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane);
 struct drm_plane *mdp4_plane_init(struct drm_device *dev,
 		enum mdp4_pipe pipe_id, bool private_plane);
 
+void mdp4_crtc_flush(struct drm_crtc *crtc);
 uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
 void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
index 4c92985..f35848d 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
@@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
 
 	mdp4_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
-
-	drm_framebuffer_reference(fb);
-
 	return mdp4_plane_mode_set(plane, crtc, fb,
 			crtc_x, crtc_y, crtc_w, crtc_h,
 			src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 7f4ee99..0e74317 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -35,7 +35,6 @@ struct mdp5_crtc {
 
 	/* if there is a pending flip, these will be non-null: */
 	struct drm_pending_vblank_event *event;
-	struct msm_fence_cb pageflip_cb;
 
 #define PENDING_CURSOR 0x1
 #define PENDING_FLIP   0x2
@@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
 	mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
 }
 
-static void crtc_flush(struct drm_crtc *crtc)
+void mdp5_crtc_flush(struct drm_crtc *crtc)
 {
+	struct msm_drm_private *priv = crtc->dev->dev_private;
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct mdp5_kms *mdp5_kms = get_kms(crtc);
 	int id = mdp5_crtc->id;
 	uint32_t i, flush = 0;
 
+	if (priv->pending_crtcs & (1 << crtc->id))
+		return;
+
 	for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
 		struct drm_plane *plane = mdp5_crtc->planes[i];
 		if (plane) {
@@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
 	 * so that we can safely queue unref to current fb (ie. next
 	 * vblank we know hw is done w/ previous scanout_fb).
 	 */
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 
 	if (mdp5_crtc->scanout_fb)
 		drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
@@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
 	}
 }
 
-static void pageflip_cb(struct msm_fence_cb *cb)
-{
-	struct mdp5_crtc *mdp5_crtc =
-		container_of(cb, struct mdp5_crtc, pageflip_cb);
-	struct drm_crtc *crtc = &mdp5_crtc->base;
-	struct drm_framebuffer *fb = mdp5_crtc->fb;
-
-	if (!fb)
-		return;
-
-	drm_framebuffer_reference(fb);
-	mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
-	update_scanout(crtc, fb);
-}
-
 static void unref_fb_worker(struct drm_flip_work *work, void *val)
 {
 	struct mdp5_crtc *mdp5_crtc =
@@ -324,7 +312,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc)
 static void mdp5_crtc_commit(struct drm_crtc *crtc)
 {
 	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 	/* drop the ref to mdp clk's that we got in prepare: */
 	mdp5_disable(get_kms(crtc));
 }
@@ -366,23 +354,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
 {
 	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
 	struct drm_device *dev = crtc->dev;
-	struct drm_gem_object *obj;
 	unsigned long flags;
 
+	spin_lock_irqsave(&dev->event_lock, flags);
 	if (mdp5_crtc->event) {
 		dev_err(dev->dev, "already pending flip!\n");
+		spin_unlock_irqrestore(&dev->event_lock, flags);
 		return -EBUSY;
 	}
 
-	obj = msm_framebuffer_bo(new_fb, 0);
-
-	spin_lock_irqsave(&dev->event_lock, flags);
 	mdp5_crtc->event = event;
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	update_fb(crtc, new_fb);
 
-	return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb);
+	/* grab extra ref for update_scanout() */
+	drm_framebuffer_reference(new_fb);
+	mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb);
+	update_scanout(crtc, new_fb);
+
+	return 0;
 }
 
 static int mdp5_crtc_set_property(struct drm_crtc *crtc,
@@ -424,8 +415,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	pending = atomic_xchg(&mdp5_crtc->pending, 0);
 
 	if (pending & PENDING_FLIP) {
-		complete_flip(crtc, NULL);
-		drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
+		if (priv->pending_crtcs & (1 << crtc->id)) {
+			/* our update hasn't been flushed yet: */
+			request_pending(crtc, PENDING_FLIP);
+		} else {
+			complete_flip(crtc, NULL);
+			drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
+		}
 	}
 }
 
@@ -434,7 +430,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
 	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err);
 	struct drm_crtc *crtc = &mdp5_crtc->base;
 	DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 }
 
 uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
@@ -501,7 +497,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
 			MDP5_CTL_OP_MODE(MODE_NONE) |
 			MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
 
-	crtc_flush(crtc);
+	mdp5_crtc_flush(crtc);
 }
 
 static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
@@ -517,7 +513,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
 	mdp5_crtc->planes[pipe_id] = plane;
 	blend_setup(crtc);
 	if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
-		crtc_flush(crtc);
+		mdp5_crtc_flush(crtc);
 }
 
 void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
@@ -563,8 +559,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
 	if (ret)
 		goto fail;
 
-	INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
-
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
 	drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
 
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index ee8446c..01c3d70 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -91,6 +91,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
 	return rate;
 }
 
+static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc)
+{
+	mdp5_crtc_flush(crtc);
+}
+
 static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
 {
 	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
@@ -118,6 +123,7 @@ static const struct mdp_kms_funcs kms_funcs = {
 		.disable_vblank  = mdp5_disable_vblank,
 		.get_format      = mdp_get_format,
 		.round_pixclk    = mdp5_round_pixclk,
+		.flush           = mdp5_flush,
 		.preclose        = mdp5_preclose,
 		.destroy         = mdp5_destroy,
 	},
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index c8b1a25..18b031b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -197,8 +197,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
 struct drm_plane *mdp5_plane_init(struct drm_device *dev,
 		enum mdp5_pipe pipe, bool private_plane);
 
+void mdp5_crtc_flush(struct drm_crtc *crtc);
 uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
-
 void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
 void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
 		enum mdp5_intf intf_id);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 53cc8c6..f1bf3c2 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
 
 	mdp5_plane->enabled = true;
 
-	if (plane->fb)
-		drm_framebuffer_unreference(plane->fb);
-
-	drm_framebuffer_reference(fb);
-
 	return mdp5_plane_mode_set(plane, crtc, fb,
 			crtc_x, crtc_y, crtc_w, crtc_h,
 			src_x, src_y, src_w, src_h);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
new file mode 100644
index 0000000..b231377
--- /dev/null
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+#include "msm_gem.h"
+
+struct msm_async_commit {
+	struct drm_atomic_state *state;
+	uint32_t fence;
+	struct msm_fence_cb fence_cb;
+};
+
+static void fence_cb(struct msm_fence_cb *cb);
+static int commit_sync(struct drm_device *dev, void *state, bool unlocked);
+
+static struct msm_async_commit *new_commit(struct drm_atomic_state *state)
+{
+	struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+	if (!c)
+		return NULL;
+
+	drm_atomic_state_reference(state);
+	c->state = state;
+	INIT_FENCE_CB(&c->fence_cb, fence_cb);
+
+	return c;
+}
+static void free_commit(struct msm_async_commit *c)
+{
+	drm_atomic_state_unreference(c->state);
+	kfree(c);
+}
+
+static void fence_cb(struct msm_fence_cb *cb)
+{
+	struct msm_async_commit *c =
+			container_of(cb, struct msm_async_commit, fence_cb);
+	commit_sync(c->state->dev, c->state, true);
+	free_commit(c);
+}
+
+static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc,
+		struct drm_framebuffer *fb)
+{
+	struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0);
+	c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
+}
+
+static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb)
+{
+	// XXX TODO wait..
+	return 0;
+}
+
+#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb)
+
+static int commit_sync(struct drm_device *dev, void *state, bool unlocked)
+{
+	struct drm_atomic_state *a = state;
+	struct msm_drm_private *priv = dev->dev_private;
+	struct msm_kms *kms = priv->kms;
+	int ncrtcs = dev->mode_config.num_crtc;
+	uint32_t pending_crtcs = 0;
+	int i, ret;
+
+	for (i = 0; i < ncrtcs; i++)
+		if (a->crtcs[i])
+			pending_crtcs |= (1 << a->crtcs[i]->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_commit_unlocked(dev, state);
+	else
+		ret = drm_atomic_commit(dev, state);
+
+	mutex_lock(&dev->struct_mutex);
+	priv->pending_crtcs &= ~pending_crtcs;
+	mutex_unlock(&dev->struct_mutex);
+
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ncrtcs; i++)
+		if (a->crtcs[i])
+			kms->funcs->flush(kms, a->crtcs[i]);
+
+	return 0;
+}
+
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state)
+{
+	struct drm_atomic_state *a = state;
+	int nplanes = dev->mode_config.num_total_plane;
+	int i;
+
+	if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) {
+		/* non-block mode: defer commit until fb's are ready */
+		struct msm_async_commit *c = new_commit(state);
+
+		if (!c)
+			return -ENOMEM;
+
+		for (i = 0; i < nplanes; i++)
+			if (pending_fb(a->pstates[i]))
+				add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb);
+
+		return msm_queue_fence_cb(dev, &c->fence_cb, c->fence);
+	} else {
+		/* blocking mode: wait until fb's are ready */
+		int ret = 0;
+
+		for (i = 0; i < nplanes && !ret; i++)
+			if (pending_fb(a->pstates[i]))
+				ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb);
+
+		if (ret)
+			return ret;
+
+		return commit_sync(dev, state, false);
+	}
+}
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index d24ca45..bcee218 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -600,6 +600,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)
 {
@@ -815,7 +835,7 @@ static struct drm_driver msm_driver = {
 	.atomic_begin       = drm_atomic_begin,
 	.atomic_set_event   = drm_atomic_set_event,
 	.atomic_check       = drm_atomic_check,
-	.atomic_commit      = drm_atomic_commit,
+	.atomic_commit      = msm_atomic_commit,
 	.atomic_end         = drm_atomic_end,
 	.atomic_funcs       = &drm_atomic_funcs,
 #ifdef CONFIG_DEBUG_FS
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index afc990e..5e1e7da 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -110,6 +110,9 @@ struct msm_drm_private {
 	unsigned int num_connectors;
 	struct drm_connector *connectors[8];
 
+	/* crtc's pending atomic update: */
+	uint32_t pending_crtcs;
+
 	/* VRAM carveout, used when no IOMMU: */
 	struct {
 		unsigned long size;
@@ -143,11 +146,15 @@ int msm_register_mmu(struct drm_device *dev, struct msm_mmu *mmu);
 
 int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
 		struct timespec *timeout);
+int msm_queue_fence_cb(struct drm_device *dev,
+		struct msm_fence_cb *cb, uint32_t fence);
 void msm_update_fence(struct drm_device *dev, uint32_t fence);
 
 int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 		struct drm_file *file);
 
+int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state);
+
 int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj);
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index bb8026d..90cb138 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -392,23 +392,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,
@@ -447,12 +435,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 3246bb4..41a60cb 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -70,6 +70,19 @@ static inline bool is_active(struct msm_gem_object *msm_obj)
 	return msm_obj->gpu != NULL;
 }
 
+static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj,
+		uint32_t op)
+{
+	uint32_t fence = 0;
+
+	if (op & MSM_PREP_READ)
+		fence = msm_obj->write_fence;
+	if (op & MSM_PREP_WRITE)
+		fence = max(fence, msm_obj->read_fence);
+
+	return fence;
+}
+
 #define MAX_CMDS 4
 
 /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 0643774..91ddffc 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -42,6 +42,7 @@ struct msm_kms_funcs {
 	const struct msm_format *(*get_format)(struct msm_kms *kms, uint32_t format);
 	long (*round_pixclk)(struct msm_kms *kms, unsigned long rate,
 			struct drm_encoder *encoder);
+	void (*flush)(struct msm_kms *kms, struct drm_crtc *crtc);
 	/* cleanup: */
 	void (*preclose)(struct msm_kms *kms, struct drm_file *file);
 	void (*destroy)(struct msm_kms *kms);
-- 
1.9.0

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

* [PATCH 15/17] drm: spiff out FB refcnting traces
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (13 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 14/17] drm/msm: add atomic support Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 16/17] drm: more conservative locking Rob Clark
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

I find myself making this change locally whenever debugging FB reference
counting.  Which seems a bit silly.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index e14d517..972af76 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -584,7 +584,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup);
  */
 void drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-	DRM_DEBUG("FB ID: %d\n", fb->base.id);
+	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
 	kref_put(&fb->refcount, drm_framebuffer_free);
 }
 EXPORT_SYMBOL(drm_framebuffer_unreference);
@@ -597,7 +597,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference);
  */
 void drm_framebuffer_reference(struct drm_framebuffer *fb)
 {
-	DRM_DEBUG("FB ID: %d\n", fb->base.id);
+	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
 	kref_get(&fb->refcount);
 }
 EXPORT_SYMBOL(drm_framebuffer_reference);
@@ -609,7 +609,7 @@ static void drm_framebuffer_free_bug(struct kref *kref)
 
 static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
 {
-	DRM_DEBUG("FB ID: %d\n", fb->base.id);
+	DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, fb->refcount.refcount.counter);
 	kref_put(&fb->refcount, drm_framebuffer_free_bug);
 }
 
-- 
1.9.0

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

* [PATCH 16/17] drm: more conservative locking
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (14 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 15/17] drm: spiff out FB refcnting traces Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-24 18:30 ` [PATCH 17/17] drm: Fix up the atomic legacy paths so they work Rob Clark
  2014-05-26 10:40 ` [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Daniel Vetter
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

TODO possibly just squash this back into "convert crtc/plane to
atomic"..

This reintroduces drm_modeset_lock_all() in a places that had not been
converted to more fine grained locking (setcrtc, setplane, etc).  This
makes the locking equivalent to before.

Note that since mode_config.mutex is a drm_modeset_lock, the lock is
automatically dropped at the end of the atomic update.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c      | 21 +++++++++++++++++++++
 drivers/gpu/drm/drm_fb_helper.c | 18 +++++++++++++++++-
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 972af76..4e3b527 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -709,14 +709,22 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (atomic_read(&fb->refcount.refcount) > 1) {
+		struct drm_mode_config *config = &dev->mode_config;
 		struct drm_atomic_state *state;
+		int ret;
 
+retry:
 		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;
 		}
 
+		ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) ||
+				drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+		if (ret)
+			goto out;
+
 		/* remove from any plane */
 		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
 			if (plane->fb == fb)
@@ -729,7 +737,10 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
 		else
 			dev->driver->atomic_commit(dev, state);
 
+out:
 		dev->driver->atomic_end(dev, state);
+		if (ret == -EDEADLK)
+			goto retry;
 	}
 
 	drm_framebuffer_unreference(fb);
@@ -2595,6 +2606,11 @@ retry:
 	if (IS_ERR(state))
 		return PTR_ERR(state);
 
+	ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) ||
+			drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
+		goto out;
+
 	plane = drm_plane_find(dev, plane_req->plane_id);
 	if (!plane) {
 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
@@ -2810,6 +2826,11 @@ retry:
 	if (IS_ERR(state))
 		return PTR_ERR(state);
 
+	ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) ||
+			drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
+		goto out;
+
 	/* 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:
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 3815e1a..4dc22cf 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -36,6 +36,7 @@
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -287,11 +288,13 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 		bool lockless)
 {
 	struct drm_device *dev = fb_helper->dev;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_plane *plane;
 	bool error = false;
 	struct drm_atomic_state *state;
-	int i;
+	int ret, i;
 
+retry:
 	state = dev->driver->atomic_begin(dev, lockless ?
 			DRM_MODE_ATOMIC_NOLOCK : 0);
 	if (IS_ERR(state)) {
@@ -299,6 +302,11 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 		return true;
 	}
 
+	ret = drm_modeset_lock(&config->mutex, &state->acquire_ctx) ||
+			drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+	if (ret)
+		goto out;
+
 	list_for_each_entry(plane, &dev->mode_config.plane_list, head)
 		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
 			drm_plane_force_disable(plane, state);
@@ -309,7 +317,12 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 	else
 		dev->driver->atomic_commit(dev, state);
 
+out:
 	dev->driver->atomic_end(dev, state);
+	if (ret == -EDEADLK)
+		goto retry;
+
+	drm_modeset_lock_all(dev);
 
 	for (i = 0; i < fb_helper->crtc_count; i++) {
 		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
@@ -326,6 +339,9 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper,
 		if (ret)
 			error = true;
 	}
+
+	drm_modeset_unlock_all(dev);
+
 	return error;
 }
 EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
-- 
1.9.0

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

* [PATCH 17/17] drm: Fix up the atomic legacy paths so they work
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (15 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 16/17] drm: more conservative locking Rob Clark
@ 2014-05-24 18:30 ` Rob Clark
  2014-05-26 10:40 ` [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Daniel Vetter
  17 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-24 18:30 UTC (permalink / raw)
  To: dri-devel

From: Sean Paul <seanpaul@chromium.org>

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

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

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 863a0fe..79fbb2c 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -613,6 +613,7 @@ swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
 	/* clear transient state (only valid during atomic update): */
 	cstate->set_config = false;
 	cstate->connectors_change = false;
+	cstate->commit_state = false;
 
 	swap(crtc->state, a->cstates[crtc->id]);
 	crtc->base.propvals = &crtc->state->propvals;
@@ -687,6 +688,9 @@ commit_crtc_state(struct drm_crtc *crtc,
 			drm_atomic_get_plane_state(crtc->primary, cstate->state);
 	int ret = -EINVAL;
 
+	if (!cstate->commit_state)
+		return 0;
+
 	if (cstate->set_config)
 		return set_config(crtc, cstate);
 
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4e3b527..03dddbb 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -792,7 +792,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 	crtc->base.propvals = &crtc->state->propvals;
 
 	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
-	dev->mode_config.num_crtc++;
+	crtc->id = dev->mode_config.num_crtc++;
 
 	crtc->primary = primary;
 	if (primary)
@@ -910,7 +910,7 @@ static int check_connectors(struct drm_crtc *crtc,
 		if (ocrtc == crtc)
 			continue;
 
-		ostate = drm_atomic_get_crtc_state(crtc, state);
+		ostate = drm_atomic_get_crtc_state(ocrtc, state);
 		if (IS_ERR(ostate))
 			return PTR_ERR(ostate);
 
@@ -939,7 +939,6 @@ retry:
 			return -EINVAL;
 		}
 	}
-
 	return 0;
 }
 
@@ -961,6 +960,10 @@ int drm_crtc_check_state(struct drm_crtc *crtc,
 	if (!(fb && state->mode_valid))
 		return 0;
 
+	/* We're not committing this state, ignore */
+	if (!state->commit_state)
+		return 0;
+
 	hdisplay = state->mode.hdisplay;
 	vdisplay = state->mode.vdisplay;
 
@@ -1034,6 +1037,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc,
 	/* grab primary plane state now, to ensure locks are held, etc. */
 	drm_atomic_get_plane_state(crtc->primary, state->state);
 
+	state->commit_state = true;
 	drm_object_property_set_value(&crtc->base,
 			&state->propvals, property, value, blob_data);
 
@@ -1045,6 +1049,7 @@ int drm_crtc_set_property(struct drm_crtc *crtc,
 			/* check size: */
 			if (value < sizeof(struct drm_mode_modeinfo))
 				return -EINVAL;
+
 			state->mode = *(struct drm_mode_modeinfo *)blob_data;
 			state->mode_valid = true;
 		}
@@ -1538,6 +1543,7 @@ int drm_plane_set_property(struct drm_plane *plane,
 	} 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.
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 2fbf13a..009b76f 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -317,6 +317,7 @@ struct drm_crtc_state {
 	/* transient state, only valid during atomic operation: */
 	bool set_config        : 1;
 	bool connectors_change : 1;
+	bool commit_state      : 1;
 
 	uint8_t num_connector_ids;
 	uint32_t *connector_ids;
-- 
1.9.0

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-24 18:30 ` [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex Rob Clark
@ 2014-05-25 22:10   ` Daniel Vetter
  2014-05-25 23:16     ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-25 22:10 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
> @@ -8002,7 +8002,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);


This is pretty much the reason why I think switching the
mode_config.mutex to a ww_mutex is a bad idea: This call here nests
within the mode_config.mutex and so must be acquired. Wiring the
acquire context through everything is going to be fairly horrible,
especially since you must be able to bail out when trying to lock with
an axquire context.

My original design behind the crtc->mutex and mode_config.mutex split
was that as long as the connector->crtc links didn't change you can
get away with the crtc lock. setplane made a bit a mess out of this,
but strictly speaking as long as you acquire all crtc locks involved
in a potential plane switch (which ww_mtuxes can do) it'll be fine.
Since noticing whether any connector properties change should be
doable upfront I think we should try _really_ hard to keep the
mode_config.mutex a plain mutex which wraps all the more fine-grained
locks and is a catch-all for everything else but crtcs/planes.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-25 22:10   ` Daniel Vetter
@ 2014-05-25 23:16     ` Rob Clark
  2014-05-26  8:23       ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-25 23:16 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
>> @@ -8002,7 +8002,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);
>
>
> This is pretty much the reason why I think switching the
> mode_config.mutex to a ww_mutex is a bad idea: This call here nests
> within the mode_config.mutex and so must be acquired. Wiring the
> acquire context through everything is going to be fairly horrible,
> especially since you must be able to bail out when trying to lock with
> an axquire context.

which is the call-path to here from mode_config.mutex?  Is it possible
to just move the locking to a higher level for a
drm_modeset_lock_all()?

> My original design behind the crtc->mutex and mode_config.mutex split
> was that as long as the connector->crtc links didn't change you can
> get away with the crtc lock. setplane made a bit a mess out of this,
> but strictly speaking as long as you acquire all crtc locks involved
> in a potential plane switch (which ww_mtuxes can do) it'll be fine.
> Since noticing whether any connector properties change should be
> doable upfront I think we should try _really_ hard to keep the
> mode_config.mutex a plain mutex which wraps all the more fine-grained
> locks and is a catch-all for everything else but crtcs/planes.

That is basically how it was in one of the earlier iterations of
atomic.. but didn't hold mode_config.mutex in a lot of places where it
was previously held.. and while I do want to make locking more fine
grained I didn't want to try and do it at the same time as landing all
these other changes.

BR,
-R

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

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-25 23:16     ` Rob Clark
@ 2014-05-26  8:23       ` Daniel Vetter
  2014-05-26 11:56         ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  8:23 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
> On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
> >> @@ -8002,7 +8002,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);
> >
> >
> > This is pretty much the reason why I think switching the
> > mode_config.mutex to a ww_mutex is a bad idea: This call here nests
> > within the mode_config.mutex and so must be acquired. Wiring the
> > acquire context through everything is going to be fairly horrible,
> > especially since you must be able to bail out when trying to lock with
> > an axquire context.
> 
> which is the call-path to here from mode_config.mutex?  Is it possible
> to just move the locking to a higher level for a
> drm_modeset_lock_all()?

Connector probing. And the entire point of crtc locks was to _not_ block
all screen updates while we poke for a new edid or do load balancing. If
you want to test this you need a gen3/4 with tv-out (native, not through
sdvo) or a gen2 or i915g/gm with vga.

> > My original design behind the crtc->mutex and mode_config.mutex split
> > was that as long as the connector->crtc links didn't change you can
> > get away with the crtc lock. setplane made a bit a mess out of this,
> > but strictly speaking as long as you acquire all crtc locks involved
> > in a potential plane switch (which ww_mtuxes can do) it'll be fine.
> > Since noticing whether any connector properties change should be
> > doable upfront I think we should try _really_ hard to keep the
> > mode_config.mutex a plain mutex which wraps all the more fine-grained
> > locks and is a catch-all for everything else but crtcs/planes.
> 
> That is basically how it was in one of the earlier iterations of
> atomic.. but didn't hold mode_config.mutex in a lot of places where it
> was previously held.. and while I do want to make locking more fine
> grained I didn't want to try and do it at the same time as landing all
> these other changes.

Hm, maybe we should land the locking stuff first? I.e. just convert
crtc->mutex into a ww_mutex, and then add more fine-grained locking to
e.g. setplane by only grabbing the locks of the involved crtcs with the
w/w logic. We might need an additional plane mutex to make it work. Iirc
Ville had some patches for just this.

I'm arguing this since locking at the current interface I have a really
hard time seeing how we're going to implement this in i915. Still reading
around though.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 04/17] drm: add object property type
  2014-05-24 18:30 ` [PATCH 04/17] drm: add object property type Rob Clark
@ 2014-05-26  8:29   ` Daniel Vetter
  2014-05-26  8:33     ` Daniel Vetter
  2014-05-26 11:06     ` Rob Clark
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  8:29 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:13PM -0400, Rob Clark wrote:
> 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.
> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> ---
>  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 dd10e4c..c049ba7 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -3142,6 +3142,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)
> @@ -3162,6 +3164,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);
> @@ -3299,6 +3304,23 @@ 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);
> +
>  /**
>   * drm_property_add_enum - add a possible value to an enumeration property
>   * @property: enumeration property to change
> @@ -3319,14 +3341,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
>  {
>  	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)) {
> @@ -3509,10 +3533,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++;
>  	}
> @@ -3534,7 +3559,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;
> @@ -3556,7 +3582,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;
> @@ -3712,19 +3738,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;

Ok, so this usage is indeed safe for framebuffers since you only compare
the pointer against NULL. But you can't ever access this pointer since it
might disappear any time after you've dropped the idr lock. I think what
you actually want here is a new interface drm_mode_object_exists which
just returns a bool.

That won't expose any risky things to callers and we can still keep the
restriction that calling drm_mode_object_get for framebuffers is a BUG.
-Daniel

>  	} else {
>  		int i;
>  		for (i = 0; i < property->num_values; i++)
> @@ -3815,9 +3847,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, struct drm_atomic_state *state,
> -		struct drm_property *property, uint64_t value, void *blob_data)
> +static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t value, void *blob_data)
>  {
>  	if (drm_property_change_is_valid(property, value)) {
>  		switch (obj->type) {
> @@ -3831,6 +3863,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;
> @@ -3866,7 +3900,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
>  		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 83e0f88..65bfb8b 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -191,6 +191,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;
>  };
> @@ -941,6 +942,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
>  
>  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);
> @@ -974,6 +992,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);
> @@ -990,6 +1010,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.9.0
> 

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

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

* Re: [PATCH 04/17] drm: add object property type
  2014-05-26  8:29   ` Daniel Vetter
@ 2014-05-26  8:33     ` Daniel Vetter
  2014-05-26 11:06     ` Rob Clark
  1 sibling, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  8:33 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 10:29:47AM +0200, Daniel Vetter wrote:
> On Sat, May 24, 2014 at 02:30:13PM -0400, Rob Clark wrote:
> > +	} 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;
> 
> Ok, so this usage is indeed safe for framebuffers since you only compare
> the pointer against NULL. But you can't ever access this pointer since it
> might disappear any time after you've dropped the idr lock. I think what
> you actually want here is a new interface drm_mode_object_exists which
> just returns a bool.
> 
> That won't expose any risky things to callers and we can still keep the
> restriction that calling drm_mode_object_get for framebuffers is a BUG.

s/object_get/object_find/ I've meant ofc.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 06/17] drm: helpers to find mode objects
  2014-05-24 18:30 ` [PATCH 06/17] drm: helpers to find mode objects Rob Clark
@ 2014-05-26  8:37   ` Daniel Vetter
  2014-05-26  8:55     ` Daniel Vetter
  2014-05-26 11:12     ` Rob Clark
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  8:37 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:15PM -0400, Rob Clark wrote:
> Add a few more useful helpers to find mode objects.
> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>

There's a pile more in drivers for these. I like this, but imo we should
also convert drivers while at it. Also, docbook is missing. And since it's
a cleanup I think it's better to do this first, directly on top of
drm-next. The patch itself looks good.
-Daniel

> ---
>  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 fa86fba..bd12185 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -1736,7 +1736,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))
> @@ -1744,13 +1743,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;
> @@ -1804,7 +1801,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;
> @@ -1828,13 +1824,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>  
>  	drm_modeset_lock(&dev->mode_config.mutex, NULL);
>  
> -	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;
>  
> @@ -1949,7 +1943,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;
>  
> @@ -1957,13 +1950,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;
> @@ -2061,7 +2052,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;
> @@ -2070,13 +2060,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;
> @@ -2129,7 +2117,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;
> @@ -2144,14 +2131,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) {
> @@ -2168,15 +2153,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) {
> @@ -2363,7 +2346,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;
> @@ -2381,14 +2363,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) {
> @@ -2471,15 +2451,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));
> @@ -2511,7 +2489,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;
>  
> @@ -2521,12 +2498,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) {
> @@ -3522,7 +3498,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;
> @@ -3541,12 +3516,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)) {
> @@ -3676,7 +3650,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;
> @@ -3686,12 +3659,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;
> @@ -3898,7 +3870,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
>  		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;
>  
> @@ -3915,11 +3886,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
>  	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);
> @@ -4126,7 +4095,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;
> @@ -4136,12 +4104,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;
> @@ -4200,7 +4167,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;
> @@ -4210,12 +4176,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) {
> @@ -4268,7 +4233,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;
> @@ -4282,10 +4246,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->primary->fb == NULL) {
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index cd4a61a..b940a29 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -1112,6 +1112,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)
>  {
> @@ -1128,6 +1137,30 @@ 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;
> +}
> +
>  /* Plane list iterator for legacy (overlay only) planes. */
>  #define drm_for_each_legacy_plane(plane, planelist) \
>  	list_for_each_entry(plane, planelist, head) \
> -- 
> 1.9.0
> 

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

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

* Re: [PATCH 10/17] drm: allow FB's in drm_mode_object_find
  2014-05-24 18:30 ` [PATCH 10/17] drm: allow FB's in drm_mode_object_find Rob Clark
@ 2014-05-26  8:39   ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  8:39 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:19PM -0400, Rob Clark wrote:
> We do actually want to permit FB's in atomic case, since FB will be
> looked up like any other object property value in a few code paths
> (like property value validation).
> 
> So split out into an internal function without the WARN_ON() which
> we can use in those special cases.
> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>

See my other mail, but Nack. Imo you want a drm_mode_object_exists
instead, which just returns a bool. Gives you want you want and is safe.
-Daniel

> ---
>  drivers/gpu/drm/drm_crtc.c | 33 ++++++++++++++++++++++-----------
>  include/drm/drm_crtc.h     |  6 ------
>  2 files changed, 22 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 9b95601..48555724 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -443,6 +443,21 @@ void drm_mode_object_put(struct drm_device *dev,
>  	mutex_unlock(&dev->mode_config.idr_mutex);
>  }
>  
> +static struct drm_mode_object *_object_find(struct drm_device *dev,
> +		uint32_t id, uint32_t type)
> +{
> +	struct drm_mode_object *obj = NULL;
> +
> +	mutex_lock(&dev->mode_config.idr_mutex);
> +	obj = idr_find(&dev->mode_config.crtc_idr, id);
> +	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
> +	    (obj->id != id))
> +		obj = NULL;
> +	mutex_unlock(&dev->mode_config.idr_mutex);
> +
> +	return obj;
> +}
> +
>  /**
>   * drm_mode_object_find - look up a drm object with static lifetime
>   * @dev: drm device
> @@ -455,23 +470,19 @@ void drm_mode_object_put(struct drm_device *dev,
>  struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
>  		uint32_t id, uint32_t type)
>  {
> -	struct drm_mode_object *obj = NULL;
> -
>  	/* Framebuffers are reference counted and need their own lookup
>  	 * function.*/
>  	WARN_ON(type == DRM_MODE_OBJECT_FB);
> -
> -	mutex_lock(&dev->mode_config.idr_mutex);
> -	obj = idr_find(&dev->mode_config.crtc_idr, id);
> -	if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) ||
> -	    (obj->id != id))
> -		obj = NULL;
> -	mutex_unlock(&dev->mode_config.idr_mutex);
> -
> -	return obj;
> +	return _object_find(dev, id, type);
>  }
>  EXPORT_SYMBOL(drm_mode_object_find);
>  
> +static struct drm_mode_object *
> +drm_property_get_obj(struct drm_property *property, uint64_t value)
> +{
> +	return _object_find(property->dev, value, property->values[0]);
> +}
> +
>  /**
>   * drm_framebuffer_init - initialize a framebuffer
>   * @dev: DRM device
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 31ed7c6..547b75a 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -1033,12 +1033,6 @@ extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
>  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);
> -- 
> 1.9.0
> 

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

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

* Re: [PATCH 06/17] drm: helpers to find mode objects
  2014-05-26  8:37   ` Daniel Vetter
@ 2014-05-26  8:55     ` Daniel Vetter
  2014-05-26 11:12     ` Rob Clark
  1 sibling, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  8:55 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 10:37 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 02:30:15PM -0400, Rob Clark wrote:
>> Add a few more useful helpers to find mode objects.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
> There's a pile more in drivers for these. I like this, but imo we should
> also convert drivers while at it. Also, docbook is missing. And since it's
> a cleanup I think it's better to do this first, directly on top of
> drm-next. The patch itself looks good.

Aside: This is a pretty perfect case for coccinelle since it'll match
object_find users no matter how crazily they're split up. And you can
remove the unused local variable with it too (but that part is a bit
more fuzz).
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 11/17] drm: convert plane to properties/state
  2014-05-24 18:30 ` [PATCH 11/17] drm: convert plane to properties/state Rob Clark
@ 2014-05-26  9:12   ` Daniel Vetter
  2014-05-26 11:32     ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  9:12 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

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

Imo s/plane->id/plane->index/ since plane_id can be too easily confused
with the mode object id used for the idr lookup. We already have
drm_crtc_index, so this is established convention already.

A few comments below.
 
> ---
>  drivers/gpu/drm/armada/armada_overlay.c    |  11 +-
>  drivers/gpu/drm/drm_atomic.c               | 225 ++++++++++++++-
>  drivers/gpu/drm/drm_crtc.c                 | 433 ++++++++++++++++++++---------
>  drivers/gpu/drm/drm_fb_helper.c            |  17 +-
>  drivers/gpu/drm/drm_plane_helper.c         |   2 +
>  drivers/gpu/drm/exynos/exynos_drm_plane.c  |   7 +-
>  drivers/gpu/drm/i915/intel_sprite.c        |   1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c  |   6 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c  |   6 +-
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c |   8 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c       |   7 +
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c    |   8 +-
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c |   2 +
>  include/drm/drm_atomic.h                   |  29 +-
>  include/drm/drm_crtc.h                     | 114 +++++++-
>  16 files changed, 725 insertions(+), 153 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
> index 601ba9a..041ea89 100644
> --- a/drivers/gpu/drm/armada/armada_overlay.c
> +++ b/drivers/gpu/drm/armada/armada_overlay.c
> @@ -7,6 +7,7 @@
>   * published by the Free Software Foundation.
>   */
>  #include <drm/drmP.h>
> +#include <drm/drm_atomic.h>
>  #include "armada_crtc.h"
>  #include "armada_drm.h"
>  #include "armada_fb.h"
> @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane,
>  {
>  	struct armada_private *priv = plane->dev->dev_private;
>  	struct armada_plane *dplane = drm_to_armada_plane(plane);
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>  	bool update_attr = false;
> +	int ret = 0;
> +
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
>  
>  	if (property == priv->colorkey_prop) {
>  #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
> @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane,
>  	} else if (property == priv->saturation_prop) {
>  		dplane->prop.saturation = val;
>  		update_attr = true;
> +	} else {
> +		ret = drm_plane_set_property(plane, pstate, property,
> +				val, blob_data);
>  	}
>  
>  	if (update_attr && dplane->base.crtc)
>  		armada_ovl_update_attr(&dplane->prop,
>  				       drm_to_armada_crtc(dplane->base.crtc));
>  
> -	return 0;
> +	return ret;
>  }
>  
>  static const struct drm_plane_funcs armada_plane_funcs = {
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 45df5e5..403ffc5 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -24,6 +24,7 @@
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_atomic.h>
> +#include <drm/drm_plane_helper.h>
>  
>  /**
>   * drm_atomic_begin - start a sequence of atomic updates
> @@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
>  		uint32_t flags)
>  {
>  	struct drm_atomic_state *state;
> +	int nplanes = dev->mode_config.num_total_plane;
>  	int sz;
>  	void *ptr;
>  
>  	sz = sizeof(*state);
> +	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
>  
>  	ptr = kzalloc(sz, GFP_KERNEL);
>  
> @@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
>  	state->dev = dev;
>  	state->flags = flags;
>  
> +	state->planes = ptr;
> +	ptr = &state->planes[nplanes];
> +
> +	state->pstates = ptr;
> +	ptr = &state->pstates[nplanes];
> +
>  	return state;
>  }
>  EXPORT_SYMBOL(drm_atomic_begin);
> @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event);
>  int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
>  {
>  	struct drm_atomic_state *a = state;
> +	int nplanes = dev->mode_config.num_total_plane;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < nplanes; i++) {
> +		if (a->planes[i]) {
> +			ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
> +			if (ret)
> +				break;
> +		}
> +	}
> +
>  	a->acquire_ctx.frozen = true;
> -	return 0;  /* for now */
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_atomic_check);
>  
> @@ -180,6 +201,18 @@ fail:
>  static void commit_locks(struct drm_atomic_state *a,
>  		struct ww_acquire_ctx *ww_ctx)
>  {
> +	struct drm_device *dev = a->dev;
> +	int nplanes = dev->mode_config.num_total_plane;
> +	int i;
> +
> +	for (i = 0; i < nplanes; i++) {
> +		struct drm_plane *plane = a->planes[i];
> +		if (plane) {
> +			plane->state->state = NULL;
> +			drm_plane_destroy_state(plane, a->pstates[i]);
> +		}
> +	}
> +
>  	/* and properly release them (clear in_atomic, remove from list): */
>  	drm_modeset_drop_locks(&a->acquire_ctx);
>  	ww_acquire_fini(ww_ctx);
> @@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a,
>  static int atomic_commit(struct drm_atomic_state *a,
>  		struct ww_acquire_ctx *ww_ctx)
>  {
> -	int ret = 0;
> +	int nplanes = a->dev->mode_config.num_total_plane;
> +	int i, ret = 0;
> +
> +	for (i = 0; i < nplanes; i++) {
> +		struct drm_plane *plane = a->planes[i];
> +		if (plane) {
> +			ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
> +			if (ret)
> +				break;
> +		}
> +	}
>  
>  	commit_locks(a, ww_ctx);
>  
> @@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref)
>  }
>  EXPORT_SYMBOL(_drm_atomic_state_free);
>  
> +int drm_atomic_plane_set_property(struct drm_plane *plane,
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
> +{
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
> +	return drm_plane_set_property(plane, pstate, property, val, blob_data);
> +}
> +EXPORT_SYMBOL(drm_atomic_plane_set_property);
> +
> +static void init_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate, struct drm_atomic_state *state)
> +{
> +	/* snapshot current state: */
> +	*pstate = *plane->state;
> +	pstate->state = state;
> +	if (pstate->fb)
> +		drm_framebuffer_reference(pstate->fb);
> +}
> +
> +struct drm_plane_state *
> +drm_atomic_get_plane_state(struct drm_plane *plane,
> +		struct drm_atomic_state *state)
> +{
> +	struct drm_atomic_state *a = state;
> +	struct drm_plane_state *pstate;
> +	int ret;
> +
> +	pstate = a->pstates[plane->id];
> +
> +	if (!pstate) {
> +		struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
> +
> +		/* grab lock of current crtc.. if crtc is NULL then grab all: */
> +		if (plane->state->crtc)

Looking here looks fishy - who's preventing someone else from touching
plane->state while we don't yet hold any locks? Or do I miss something big
here?

> +			ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
> +		else
> +			ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
> +		if (ret)
> +			return ERR_PTR(ret);
> +
> +		pstate = drm_plane_create_state(plane);
> +		if (!pstate)
> +			return ERR_PTR(-ENOMEM);
> +		init_plane_state(plane, pstate, state);
> +		a->planes[plane->id] = plane;
> +		a->pstates[plane->id] = pstate;
> +	}
> +
> +	return pstate;
> +}
> +EXPORT_SYMBOL(drm_atomic_get_plane_state);
> +
> +static void
> +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a)
> +{
> +	struct drm_plane_state *pstate = a->pstates[plane->id];
> +
> +	/* clear transient state (only valid during atomic update): */
> +	pstate->update_plane = false;
> +	pstate->new_fb = false;
> +
> +	swap(plane->state, a->pstates[plane->id]);
> +	plane->base.propvals = &plane->state->propvals;
> +}
> +
> +/* For primary plane, if the driver implements ->page_flip(), then
> + * we can use that.  But drivers can now choose not to bother with
> + * implementing page_flip().
> + */
> +static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate)
> +{
> +	struct drm_crtc *crtc = pstate->crtc;
> +	return (plane == crtc->primary) && crtc->funcs->page_flip &&
> +			!pstate->update_plane;
> +}
> +
> +/* clear crtc/fb, ie. after disable_plane().  But takes care to keep
> + * the property state in sync.  Once we get rid of plane->crtc/fb ptrs
> + * and just use state, we can get rid of this fxn:
> + */
> +static void
> +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate)
> +{
> +	struct drm_mode_config *config = &plane->dev->mode_config;
> +	drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
> +	drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
> +	plane->crtc = NULL;
> +	plane->fb = NULL;
> +}
> +
> +static int
> +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
> +{
> +	struct drm_atomic_state *a = pstate->state;
> +	struct drm_framebuffer *old_fb = plane->fb;
> +	struct drm_framebuffer *fb = pstate->fb;
> +	bool enabled = pstate->crtc && fb;
> +	int ret = 0;
> +
> +	if (fb)
> +		drm_framebuffer_reference(fb);
> +
> +	if (!enabled) {
> +		if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> +				(plane->funcs == &drm_primary_helper_funcs)) {
> +			/* primary plane helpers don't like ->disable_plane()..
> +			 * so this hack for now until someone comes up with
> +			 * something better:
> +			 */

Imo that's something we could check for in ->check and reject early.

> +			ret = 0;
> +		} else {
> +			ret = plane->funcs->disable_plane(plane);
> +			reset_plane(plane, pstate);
> +		}
> +	} else {
> +		struct drm_crtc *crtc = pstate->crtc;
> +		if (pstate->update_plane ||
> +				(pstate->new_fb && !can_flip(plane, pstate))) {
> +			ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
> +					pstate->crtc_x, pstate->crtc_y,
> +					pstate->crtc_w, pstate->crtc_h,
> +					pstate->src_x,  pstate->src_y,
> +					pstate->src_w,  pstate->src_h);
> +			if (ret == 0) {
> +				/*
> +				 * For page_flip(), the driver does this, but for
> +				 * update_plane() it doesn't.. hurray \o/
> +				 */

Imo a patch to unify this first wouldn't hurt ...

> +				plane->crtc = crtc;
> +				plane->fb = fb;
> +				fb = NULL;  /* don't unref */
> +			}
> +
> +		} else if (pstate->new_fb) {
> +			ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
> +			if (ret == 0) {
> +				/*
> +				 * Warn if the driver hasn't properly updated the plane->fb
> +				 * field to reflect that the new framebuffer is now used.
> +				 * Failing to do so will screw with the reference counting
> +				 * on framebuffers.
> +				 */
> +				WARN_ON(plane->fb != fb);
> +				fb = NULL;  /* don't unref */
> +			}
> +		} else {
> +			old_fb = NULL;
> +			ret = 0;
> +		}
> +	}

This entire logic here kinda raises the question about transitioning
drivers to the atomic interfaces. For modeset operations it might work
fairly well since everything but i915 uses the crtc helpers and so can be
converted fairly easily.

But doing nuclear pageflips, even more so if we want completion events is
an entire new deal. No idea how that should work really.

> +
> +	if (ret) {
> +		/* Keep the old fb, don't unref it. */
> +		old_fb = NULL;
> +	} else {
> +		/* on success, update state and fb refcnting: */
> +		/* NOTE: if we ensure no driver sets plane->state->fb = NULL
> +		 * on disable, we can move this up a level and not duplicate
> +		 * nearly the same thing for both update_plane and disable_plane
> +		 * cases..  I leave it like this for now to be paranoid due to
> +		 * the slightly different ordering in the two cases in the
> +		 * original code.
> +		 */
> +		swap_plane_state(plane, pstate->state);
> +	}
> +
> +
> +	if (fb)
> +		drm_framebuffer_unreference(fb);
> +	if (old_fb)
> +		drm_framebuffer_unreference(old_fb);
> +
> +	return ret;
> +}
>  
>  const struct drm_atomic_funcs drm_atomic_funcs = {
> +		.check_plane_state  = drm_plane_check_state,
> +		.commit_plane_state = commit_plane_state,
>  };
>  EXPORT_SYMBOL(drm_atomic_funcs);
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 48555724..b556a31 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  	 * in this manner.
>  	 */
>  	if (atomic_read(&fb->refcount.refcount) > 1) {
> +		void *state;
> +
> +		state = dev->driver->atomic_begin(dev, 0);
> +		if (IS_ERR(state)) {
> +			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
> +			return;
> +		}
> +
> +		/* TODO once CRTC is converted to state/properties, we can push the
> +		 * locking down into drm_atomic_commit(), since that is where
> +		 * the actual changes take place..
> +		 */
>  		drm_modeset_lock_all(dev);
>  		/* remove from any CRTC */
>  		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> @@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  
>  		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
>  			if (plane->fb == fb)
> -				drm_plane_force_disable(plane);
> +				drm_plane_force_disable(plane, state);
>  		}
> +
> +		/* just disabling stuff shouldn't fail, hopefully: */
> +		if(dev->driver->atomic_check(dev, state))
> +			DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
> +		else
> +			dev->driver->atomic_commit(dev, state);
> +
> +		dev->driver->atomic_end(dev, state);
> +
>  		drm_modeset_unlock_all(dev);
>  	}
>  
> @@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
>  			     const uint32_t *formats, uint32_t format_count,
>  			     enum drm_plane_type type)
>  {
> +	struct drm_mode_config *config = &dev->mode_config;
>  	int ret;
>  
> +	/* this is now required: */
> +	WARN_ON(!funcs->set_property);
> +
>  	drm_modeset_lock_all(dev);
>  
>  	ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
>  	if (ret)
>  		goto out;
>  
> +	plane->funcs = funcs;
> +	plane->state = drm_plane_create_state(plane);
>  	plane->base.properties = &plane->properties;
> -	plane->base.propvals = &plane->propvals;
> +	plane->base.propvals = &plane->state->propvals;
>  	plane->dev = dev;
> -	plane->funcs = funcs;
>  	plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
>  				      GFP_KERNEL);
>  	if (!plane->format_types) {
> @@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
>  	plane->possible_crtcs = possible_crtcs;
>  	plane->type = type;
>  
> -	list_add_tail(&plane->head, &dev->mode_config.plane_list);
> -	dev->mode_config.num_total_plane++;
> +	list_add_tail(&plane->head, &config->plane_list);
> +	plane->id = config->num_total_plane;
> +	config->num_total_plane++;
>  	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
> -		dev->mode_config.num_overlay_plane++;
> +		config->num_overlay_plane++;
>  
>  	drm_object_attach_property(&plane->base,
> -				   dev->mode_config.plane_type_property,
> +				   config->plane_type_property,
>  				   plane->type);
>  
> +	drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
> +	drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_x, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_y, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_w, 0);
> +	drm_object_attach_property(&plane->base, config->prop_src_h, 0);
> +
>   out:
>  	drm_modeset_unlock_all(dev);
>  
> @@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane)
>  	dev->mode_config.num_total_plane--;
>  	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
>  		dev->mode_config.num_overlay_plane--;
> +	drm_plane_destroy_state(plane, plane->state);
>  	drm_modeset_unlock_all(dev);
>  }
>  EXPORT_SYMBOL(drm_plane_cleanup);
>  
> +int drm_plane_check_state(struct drm_plane *plane,
> +		struct drm_plane_state *state)
> +{
> +	unsigned int fb_width, fb_height;
> +	struct drm_framebuffer *fb = state->fb;
> +	int i;
> +
> +	/* disabling the plane is allowed: */
> +	if (!fb)
> +		return 0;
> +
> +	fb_width = fb->width << 16;
> +	fb_height = fb->height << 16;
> +
> +	/* Check whether this plane supports the fb pixel format. */
> +	for (i = 0; i < plane->format_count; i++)
> +		if (fb->pixel_format == plane->format_types[i])
> +			break;
> +	if (i == plane->format_count) {
> +		DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
> +		return -EINVAL;
> +	}
> +
> +	/* Make sure source coordinates are inside the fb. */
> +	if (state->src_w > fb_width ||
> +			state->src_x > fb_width - state->src_w ||
> +			state->src_h > fb_height ||
> +			state->src_y > fb_height - state->src_h) {
> +		DRM_DEBUG_KMS("Invalid source coordinates "
> +			      "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
> +			      state->src_w >> 16,
> +			      ((state->src_w & 0xffff) * 15625) >> 10,
> +			      state->src_h >> 16,
> +			      ((state->src_h & 0xffff) * 15625) >> 10,
> +			      state->src_x >> 16,
> +			      ((state->src_x & 0xffff) * 15625) >> 10,
> +			      state->src_y >> 16,
> +			      ((state->src_y & 0xffff) * 15625) >> 10);
> +		return -ENOSPC;
> +	}
> +
> +	/* Give drivers some help against integer overflows */
> +	if (state->crtc_w > INT_MAX ||
> +			state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
> +			state->crtc_h > INT_MAX ||
> +			state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
> +		DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
> +			      state->crtc_w, state->crtc_h,
> +			      state->crtc_x, state->crtc_y);
> +		return -ERANGE;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_check_state);
> +
> +void drm_plane_commit_state(struct drm_plane *plane,
> +		struct drm_plane_state *state)
> +{
> +	plane->state = state;
> +	plane->base.propvals = &state->propvals;
> +}
> +EXPORT_SYMBOL(drm_plane_commit_state);
> +
> +int drm_plane_set_property(struct drm_plane *plane,
> +		struct drm_plane_state *state,
> +		struct drm_property *property,
> +		uint64_t value, void *blob_data)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct drm_mode_config *config = &dev->mode_config;
> +
> +	drm_object_property_set_value(&plane->base,
> +			&state->propvals, property, value, blob_data);
> +
> +	if (property == config->prop_fb_id) {
> +		struct drm_framebuffer *old_fb = state->fb;
> +		/*
> +		 * NOTE: the ref to the fb could have been lost between
> +		 * drm_property_change_is_valid() and now.  The upshot
> +		 * is that drm_framebuffer_lookup() could return NULL
> +		 * and we'd disable the plane.
> +		 *
> +		 * We *could* return an error in that case.  But if (for
> +		 * example) _setcrtc() raced with _rmfb() and _rmfb()
> +		 * came after, it would disable what was enabled in the
> +		 * _setcrtc().  Which is the same end result that we get
> +		 * here, just skipping briefly setting the mode.
> +		 */
> +		state->fb = drm_framebuffer_lookup(dev, value);
> +		if (old_fb)
> +			drm_framebuffer_unreference(old_fb);
> +		state->new_fb = true;
> +	} else if (property == config->prop_crtc_id) {
> +		struct drm_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) {
> +			struct drm_atomic_state *a = state->state;
> +			int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
> +			if (ret)
> +				return ret;
> +		}
> +		state->crtc = crtc;
> +		state->update_plane = true;
> +	} else if (property == config->prop_crtc_x) {
> +		state->crtc_x = U642I64(value);
> +		state->update_plane = true;
> +	} else if (property == config->prop_crtc_y) {
> +		state->crtc_y = U642I64(value);
> +		state->update_plane = true;
> +	} else if (property == config->prop_crtc_w) {
> +		state->crtc_w = value;
> +		state->update_plane = true;
> +	} else if (property == config->prop_crtc_h) {
> +		state->crtc_h = value;
> +		state->update_plane = true;
> +	} else if (property == config->prop_src_x) {
> +		state->src_x = value;
> +		state->update_plane = true;
> +	} else if (property == config->prop_src_y) {
> +		state->src_y = value;
> +		state->update_plane = true;
> +	} else if (property == config->prop_src_w) {
> +		state->src_w = value;
> +		state->update_plane = true;
> +	} else if (property == config->prop_src_h) {
> +		state->src_h = value;
> +		state->update_plane = true;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_plane_set_property);
> +
>  /**
>   * drm_plane_force_disable - Forcibly disable a plane
>   * @plane: plane to disable
> @@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
>   * Used when the plane's current framebuffer is destroyed,
>   * and when restoring fbdev mode.
>   */
> -void drm_plane_force_disable(struct drm_plane *plane)
> +void drm_plane_force_disable(struct drm_plane *plane,
> +		struct drm_atomic_state *state)
>  {
> -	struct drm_framebuffer *old_fb = plane->fb;
> -	int ret;
> +	struct drm_mode_config *config = &plane->dev->mode_config;
>  
> -	if (!old_fb)
> -		return;
> -
> -	ret = plane->funcs->disable_plane(plane);
> -	if (ret) {
> -		DRM_ERROR("failed to disable plane with busy fb\n");
> -		return;
> -	}
> -	/* disconnect the plane from the fb and crtc: */
> -	__drm_framebuffer_unreference(old_fb);
> -	plane->fb = NULL;
> -	plane->crtc = NULL;
> +	/* should turn off the crtc */
> +	drm_mode_plane_set_obj_prop(plane, state,
> +		config->prop_crtc_id, 0, NULL);
> +	drm_mode_plane_set_obj_prop(plane, state,
> +		config->prop_fb_id, 0, NULL);
>  }
>  EXPORT_SYMBOL(drm_plane_force_disable);
>  
>  static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
>  {
> -	struct drm_property *edid;
> -	struct drm_property *dpms;
> +	struct drm_property *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;
>  }
> @@ -2140,20 +2369,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;
> +	struct drm_atomic_state *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",
> @@ -2161,104 +2389,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;
> -		ret = plane->funcs->disable_plane(plane);
> -		if (!ret) {
> -			plane->crtc = NULL;
> -			plane->fb = NULL;
> -		} else {
> -			old_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);
> -	old_fb = plane->fb;
> -	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) {
> -		plane->crtc = crtc;
> -		plane->fb = fb;
> -		fb = NULL;
> -	} else {
> -		old_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;
>  }
>  
> @@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
>  	return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
>  }
>  
> -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>  					   struct drm_atomic_state *state, struct drm_property *property,
>  					   uint64_t value, void *blob_data)
>  {
> @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
>  
> -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>  				      struct drm_atomic_state *state, struct drm_property *property,
>  				      uint64_t value, void *blob_data)
>  {
> @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>  
>  	return ret;
>  }
> +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
>  
> -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>  				      struct drm_atomic_state *state, struct drm_property *property,
>  				      uint64_t value, void *blob_data)
>  {
> @@ -3882,12 +4045,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,
>  		struct drm_atomic_state *state, struct drm_property *property,
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 97b0d84..b73d3b0 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -286,13 +286,28 @@ 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)
>  		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
> -			drm_plane_force_disable(plane);
> +			drm_plane_force_disable(plane, state);
> +
> +	/* just disabling stuff shouldn't fail, hopefully: */
> +	if(dev->driver->atomic_check(dev, state))
> +		DRM_ERROR("failed to restore fbdev mode\n");
> +	else
> +		dev->driver->atomic_commit(dev, state);
> +
> +	dev->driver->atomic_end(dev, state);
>  
>  	for (i = 0; i < fb_helper->crtc_count; i++) {
>  		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> index d966afa..7a32383 100644
> --- a/drivers/gpu/drm/drm_plane_helper.c
> +++ b/drivers/gpu/drm/drm_plane_helper.c
> @@ -26,6 +26,7 @@
>  #include <linux/list.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_rect.h>
> +#include <drm/drm_atomic.h>
>  
>  #define SUBPIXEL_MASK 0xffff
>  
> @@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy);
>  const struct drm_plane_funcs drm_primary_helper_funcs = {
>  	.update_plane = drm_primary_helper_update,
>  	.disable_plane = drm_primary_helper_disable,
> +	.set_property = drm_atomic_plane_set_property,
>  	.destroy = drm_primary_helper_destroy,
>  };
>  EXPORT_SYMBOL(drm_primary_helper_funcs);
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> index 9da0935..8cf7442 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> @@ -10,6 +10,7 @@
>   */
>  
>  #include <drm/drmP.h>
> +#include <drm/drm_atomic.h>
>  
>  #include <drm/exynos_drm.h>
>  #include "exynos_drm_drv.h"
> @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>  	struct drm_device *dev = plane->dev;
>  	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>  	struct exynos_drm_private *dev_priv = dev->dev_private;
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> +
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
>  
>  	if (property == dev_priv->plane_zpos_property) {
>  		exynos_plane->overlay.zpos = val;
>  		return 0;
>  	}
>  
> -	return -EINVAL;
> +	return drm_plane_set_property(plane, pstate, property, val, blob_data);
>  }

Imo the interfaces here are a bit wonky - most drivers have the exact same
structure of allocating a plane state object if it's not there and setting
the property. Imo we should push this down a bit and have type-specific
set_prop interfaces which take the state-specific state object. Core
properties would be fully handled in the core. Which means that driver
don't need to implement set_prop callbacks if they don't have any special
properties on top of the core stuff. Much less boilerplate that way.

This would also give us a strong incentive to have common properties for
e.g. blending since we could simply pimp the core with them, and leave
drivers to just register the properties if they support them. If they have
additional restrictions they only need to implement the ->check hook,
which has the awesome advantage that they can deal with a real structure
instead of abstract prop arrays.

E.g. we could add a plane->supports_blending bool which would
enabled/disable the default blending/Z-order properties for updating.

>  
>  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 c235546..3f742f5 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
>  	.update_plane = intel_update_plane,
>  	.disable_plane = intel_disable_plane,
>  	.destroy = intel_destroy_plane,
> +	.set_property = drm_atomic_plane_set_property,
>  };
>  
>  static uint32_t ilk_plane_formats[] = {
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> index 8c064dc..4c92985 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane,
>  		struct drm_atomic_state *state, struct drm_property *property,
>  		uint64_t val, void *blob_data)
>  {
> -	// XXX
> -	return -EINVAL;
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
> +	return drm_plane_set_property(plane, pstate, property, val, blob_data);
>  }
>  
>  static const struct drm_plane_funcs mdp4_plane_funcs = {
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> index 5cbf226..53cc8c6 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane,
>  		struct drm_atomic_state *state, struct drm_property *property,
>  		uint64_t val, void *blob_data)
>  {
> -	// XXX
> -	return -EINVAL;
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
> +	return drm_plane_set_property(plane, pstate, property, val, blob_data);
>  }
>  
>  static const struct drm_plane_funcs mdp5_plane_funcs = {
> diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> index 577e6aa..97b48b5 100644
> --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> @@ -24,6 +24,7 @@
>   */
>  
>  #include <drm/drmP.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_fourcc.h>
>  
> @@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane,
>  		  uint64_t value, void *blob_data)
>  {
>  	struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> +
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
>  
>  	if (property == nv_plane->props.colorkey)
>  		nv_plane->colorkey = value;
> @@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane,
>  	else if (property == nv_plane->props.iturbt_709)
>  		nv_plane->iturbt_709 = value;
>  	else
> -		return -EINVAL;
> +		return drm_plane_set_property(plane, pstate,
> +				property, value, blob_data);
>  
>  	if (nv_plane->set_params)
>  		nv_plane->set_params(nv_plane);
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index 3dca538..da80bdc 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
>  
>  		for (i = 0; i < priv->num_planes; i++) {
>  			drm_object_property_set_value(&priv->planes[i]->base,
> -					&priv->planes[i]->propvals,
> +					&priv->planes[i]->state->propvals,
>  					priv->rotation_prop, 0, NULL);
>  		}
>  	}
> diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
> index 2d3c975..a9acc58 100644
> --- a/drivers/gpu/drm/omapdrm/omap_plane.c
> +++ b/drivers/gpu/drm/omapdrm/omap_plane.c
> @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane,
>  {
>  	struct omap_plane *omap_plane = to_omap_plane(plane);
>  	struct omap_drm_private *priv = plane->dev->dev_private;
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>  	int ret = -EINVAL;
>  
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
> +
>  	if (property == priv->rotation_prop) {
>  		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
>  		omap_plane->win.rotation = val;
> @@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane,
>  		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
>  		omap_plane->info.zorder = val;
>  		ret = apply(plane);
> +	} else {
> +		ret = drm_plane_set_property(plane, pstate, property,
> +				val, blob_data);
>  	}
>  
>  	return ret;
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> index 3a5d843..015c76a 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> @@ -14,6 +14,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
>  {
>  	struct rcar_du_plane *rplane = to_rcar_plane(plane);
>  	struct rcar_du_group *rgrp = rplane->group;
> +	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> +
> +	if (IS_ERR(pstate))
> +		return PTR_ERR(pstate);
>  
>  	if (property == rgrp->planes.alpha)
>  		rcar_du_plane_set_alpha(rplane, value);
> @@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
>  	else if (property == rgrp->planes.zpos)
>  		rcar_du_plane_set_zpos(rplane, value);
>  	else
> -		return -EINVAL;
> +		return drm_plane_set_property(plane, pstate,
> +				property, value, blob_data);
>  
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> index 060ae03..ccf03ea 100644
> --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> @@ -14,6 +14,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane)
>  static const struct drm_plane_funcs shmob_drm_plane_funcs = {
>  	.update_plane = shmob_drm_plane_update,
>  	.disable_plane = shmob_drm_plane_disable,
> +	.set_property = drm_atomic_plane_set_property,
>  	.destroy = shmob_drm_plane_destroy,
>  };
>  
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index ff72b81..78e93ec 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -68,7 +68,8 @@
>   * struct drm_atomic_funcs - helper funcs used by the atomic helpers
>   */
>  struct drm_atomic_funcs {
> -	int dummy; /* for now */
> +	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
> +	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
>  };
>  
>  const extern struct drm_atomic_funcs drm_atomic_funcs;
> @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev,
>  		struct drm_atomic_state *state);
>  void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
>  
> +int drm_atomic_plane_set_property(struct drm_plane *plane,
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data);
> +struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
> +		struct drm_atomic_state *state);
> +
> +static inline int
> +drm_atomic_check_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate)
> +{
> +	const struct drm_atomic_funcs *funcs =
> +			plane->dev->driver->atomic_funcs;
> +	return funcs->check_plane_state(plane, pstate);
> +}
> +
> +static inline int
> +drm_atomic_commit_plane_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate)
> +{
> +	const struct drm_atomic_funcs *funcs =
> +			plane->dev->driver->atomic_funcs;
> +	return funcs->commit_plane_state(plane, pstate);
> +}
> +
>  /**
>   * struct drm_atomic_state - the state object used by atomic helpers
>   */
> @@ -91,6 +116,8 @@ struct drm_atomic_state {
>  	struct kref refcount;
>  	struct drm_device *dev;
>  	uint32_t flags;
> +	struct drm_plane **planes;
> +	struct drm_plane_state **pstates;
>  
>  	bool committed;
>  	bool checked;       /* just for debugging */
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 547b75a..58309cc 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -569,7 +569,10 @@ 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_plane_state *(*create_state)(struct drm_plane *plane);
> +	void (*destroy_state)(struct drm_plane *plane,
> +			    struct drm_plane_state *pstate);
> +	int (*set_property)(struct drm_plane *plane, 
>  			    struct drm_atomic_state *state,
>  			    struct drm_property *property, uint64_t val,
>  			    void *blob_data);
> @@ -582,6 +585,48 @@ enum drm_plane_type {
>  };
>  
>  /**
> + * drm_plane_state - mutable plane state
> + * @update_plane: if full update_plane() is needed (vs pageflip)
> + * @new_fb: has the fb been changed
> + * @crtc: currently bound CRTC
> + * @fb: currently bound fb
> + * @crtc_x: left position of visible portion of plane on crtc
> + * @crtc_y: upper position of visible portion of plane on crtc
> + * @crtc_w: width of visible portion of plane on crtc
> + * @crtc_h: height of visible portion of plane on crtc
> + * @src_x: left position of visible portion of plane within
> + *   plane (in 16.16)
> + * @src_y: upper position of visible portion of plane within
> + *   plane (in 16.16)
> + * @src_w: width of visible portion of plane (in 16.16)
> + * @src_h: height of visible portion of plane (in 16.16)
> + * @propvals: property values
> + * @state: current global/toplevel state object (for atomic) while an
> + *    update is in progress, NULL otherwise.
> + */
> +struct drm_plane_state {
> +	bool update_plane      : 1;
> +	bool new_fb            : 1;
> +
> +	struct drm_crtc *crtc;
> +	struct drm_framebuffer *fb;
> +
> +	/* Signed dest location allows it to be partially off screen */
> +	int32_t crtc_x, crtc_y;
> +	uint32_t crtc_w, crtc_h;
> +
> +	/* Source values are 16.16 fixed point */
> +	uint32_t src_x, src_y;
> +	uint32_t src_h, src_w;
> +
> +	bool enabled;
> +
> +	struct drm_object_property_values propvals;
> +
> +	struct drm_atomic_state *state;
> +};
> +
> +/**
>   * drm_plane - central DRM plane control structure
>   * @dev: DRM device this plane belongs to
>   * @head: for list management
> @@ -591,6 +636,8 @@ enum drm_plane_type {
>   * @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
>   * @type: type of plane (overlay, primary, cursor)
> @@ -608,10 +655,17 @@ struct drm_plane {
>  	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;
>  
>  	enum drm_plane_type type;
>  };
> @@ -807,8 +861,20 @@ struct drm_mode_config {
>  	bool poll_running;
>  	struct delayed_work output_poll_work;
>  
> -	/* pointers to standard properties */
> +	/* just so blob properties can always be in a list: */
>  	struct list_head property_blob_list;
> +
> +	/* pointers to standard properties */
> +	struct drm_property *prop_src_x;
> +	struct drm_property *prop_src_y;
> +	struct drm_property *prop_src_w;
> +	struct drm_property *prop_src_h;
> +	struct drm_property *prop_crtc_x;
> +	struct drm_property *prop_crtc_y;
> +	struct drm_property *prop_crtc_w;
> +	struct drm_property *prop_crtc_h;
> +	struct drm_property *prop_fb_id;
> +	struct drm_property *prop_crtc_id;
>  	struct drm_property *edid_property;
>  	struct drm_property *dpms_property;
>  	struct drm_property *plane_type_property;
> @@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev,
>  			  const uint32_t *formats, uint32_t format_count,
>  			  bool is_primary);
>  extern void drm_plane_cleanup(struct drm_plane *plane);
> -extern void drm_plane_force_disable(struct drm_plane *plane);
> +extern void drm_plane_force_disable(struct drm_plane *plane,
> +		struct drm_atomic_state *state);
>  extern int drm_crtc_check_viewport(const struct drm_crtc *crtc,
>  				   int x, int y,
>  				   const struct drm_display_mode *mode,
>  				   const struct drm_framebuffer *fb);
> +extern int drm_plane_check_state(struct drm_plane *plane,
> +		struct drm_plane_state *state);
> +extern void drm_plane_commit_state(struct drm_plane *plane,
> +		struct drm_plane_state *state);
> +extern int drm_plane_set_property(struct drm_plane *plane,
> +		struct drm_plane_state *state,
> +		struct drm_property *property,
> +		uint64_t value, void *blob_data);
>  
>  extern void drm_encoder_cleanup(struct drm_encoder *encoder);
>  
> @@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj,
>  extern int drm_object_property_get_value(struct drm_mode_object *obj,
>  					 struct drm_property *property,
>  					 uint64_t *value);
> +
> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> +					   struct drm_atomic_state *state, struct drm_property *property,
> +					   uint64_t value, void *blob_data);
> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> +				      struct drm_atomic_state *state, struct drm_property *property,
> +				      uint64_t value, void *blob_data);
> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> +				      struct drm_atomic_state *state, struct drm_property *property,
> +				      uint64_t value, void *blob_data);
> +
>  extern int drm_framebuffer_init(struct drm_device *dev,
>  				struct drm_framebuffer *fb,
>  				const struct drm_framebuffer_funcs *funcs);
> @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
>  	return mo ? obj_to_blob(mo) : NULL;
>  }
>  
> +static inline struct drm_plane_state *
> +drm_plane_create_state(struct drm_plane *plane)
> +{
> +	if (plane->funcs->create_state)
> +		return plane->funcs->create_state(plane);
> +	return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
> +}
> +
> +static inline void
> +drm_plane_destroy_state(struct drm_plane *plane,
> +		struct drm_plane_state *pstate)
> +{
> +	if (pstate->fb)
> +		drm_framebuffer_unreference(pstate->fb);
> +	if (plane->funcs->destroy_state)
> +		plane->funcs->destroy_state(plane, pstate);
> +	else
> +		kfree(pstate);
> +}
> +
>  /* Plane list iterator for legacy (overlay only) planes. */
>  #define drm_for_each_legacy_plane(plane, planelist) \
>  	list_for_each_entry(plane, planelist, head) \
> -- 
> 1.9.0
> 

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

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-24 18:30 ` [PATCH 12/17] drm: convert crtc " Rob Clark
@ 2014-05-26  9:31   ` Daniel Vetter
  2014-05-26 11:35     ` Rob Clark
  2014-05-26 15:23     ` Ville Syrjälä
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  9:31 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark 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.
> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>

Same comments about interface design as for the plane patch apply here.
One additional comment below.

> ---
>  drivers/gpu/drm/armada/armada_crtc.c       |  11 +-
>  drivers/gpu/drm/ast/ast_mode.c             |   1 +
>  drivers/gpu/drm/cirrus/cirrus_mode.c       |   1 +
>  drivers/gpu/drm/drm_atomic.c               | 231 ++++++++++-
>  drivers/gpu/drm/drm_crtc.c                 | 598 ++++++++++++++++++-----------
>  drivers/gpu/drm/drm_fb_helper.c            |   2 +-
>  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/mdp/mdp4/mdp4_crtc.c   |   6 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c   |   6 +-
>  drivers/gpu/drm/nouveau/dispnv04/crtc.c    |   1 +
>  drivers/gpu/drm/nouveau/nv50_display.c     |   1 +
>  drivers/gpu/drm/omapdrm/omap_crtc.c        |  12 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
>  drivers/gpu/drm/qxl/qxl_display.c          |   2 +
>  drivers/gpu/drm/radeon/radeon_display.c    |   2 +
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c     |   2 +
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c  |   2 +
>  drivers/gpu/drm/tilcdc/tilcdc_crtc.c       |   1 +
>  drivers/gpu/drm/udl/udl_modeset.c          |   2 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c        |   1 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c       |   1 +
>  include/drm/drm_atomic.h                   |  29 ++
>  include/drm/drm_crtc.h                     | 103 ++++-
>  27 files changed, 785 insertions(+), 243 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
> index 7d3c649..6237af4 100644
> --- a/drivers/gpu/drm/armada/armada_crtc.c
> +++ b/drivers/gpu/drm/armada/armada_crtc.c
> @@ -9,6 +9,7 @@
>  #include <linux/clk.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include "armada_crtc.h"
>  #include "armada_drm.h"
>  #include "armada_fb.h"
> @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
>  {
>  	struct armada_private *priv = crtc->dev->dev_private;
>  	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
> +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
>  	bool update_csc = false;
> +	int ret = 0;
> +
> +	if (IS_ERR(cstate))
> +		return PTR_ERR(cstate);
>  
>  	if (property == priv->csc_yuv_prop) {
>  		dcrtc->csc_yuv_mode = val;
> @@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
>  	} else if (property == priv->csc_rgb_prop) {
>  		dcrtc->csc_rgb_mode = val;
>  		update_csc = true;
> +	} else {
> +		ret = drm_crtc_set_property(crtc, cstate, property,
> +				val, blob_data);
>  	}
>  
>  	if (update_csc) {
> @@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
>  		writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
>  	}
>  
> -	return 0;
> +	return ret;
>  }
>  
>  static struct drm_crtc_funcs armada_crtc_funcs = {
> diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
> index 114aee9..c08e0e1 100644
> --- a/drivers/gpu/drm/ast/ast_mode.c
> +++ b/drivers/gpu/drm/ast/ast_mode.c
> @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
>  	.cursor_move = ast_cursor_move,
>  	.reset = ast_crtc_reset,
>  	.set_config = drm_crtc_helper_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.gamma_set = ast_crtc_gamma_set,
>  	.destroy = ast_crtc_destroy,
>  };
> diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
> index 49332c5..3c4428c 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_mode.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
> @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc)
>  static const struct drm_crtc_funcs cirrus_crtc_funcs = {
>  	.gamma_set = cirrus_crtc_gamma_set,
>  	.set_config = drm_crtc_helper_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = cirrus_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 403ffc5..863a0fe 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
>  {
>  	struct drm_atomic_state *state;
>  	int nplanes = dev->mode_config.num_total_plane;
> +	int ncrtcs  = dev->mode_config.num_crtc;
>  	int sz;
>  	void *ptr;
>  
>  	sz = sizeof(*state);
>  	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
> +	sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
>  
>  	ptr = kzalloc(sz, GFP_KERNEL);
>  
> @@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
>  	state->pstates = ptr;
>  	ptr = &state->pstates[nplanes];
>  
> +	state->crtcs = ptr;
> +	ptr = &state->crtcs[ncrtcs];
> +
> +	state->cstates = ptr;
> +	ptr = &state->cstates[ncrtcs];
> +
>  	return state;
>  }
>  EXPORT_SYMBOL(drm_atomic_begin);
> @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
>  		struct drm_atomic_state *state, struct drm_mode_object *obj,
>  		struct drm_pending_vblank_event *event)
>  {
> -	return -EINVAL;  /* for now */
> +	switch (obj->type) {
> +	case DRM_MODE_OBJECT_CRTC: {
> +		struct drm_crtc_state *cstate =
> +			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
> +		if (IS_ERR(cstate))
> +			return PTR_ERR(cstate);
> +		cstate->event = event;
> +		return 0;
> +	}
> +	default:
> +		return -EINVAL;
> +	}

Hm, I think if we only want completion events on crtcs (which I agree on)
then we should make the set_event interface more specific by passing
struct drm_crtc * and only call it for crtcs.

>  }
>  EXPORT_SYMBOL(drm_atomic_set_event);
>  
> @@ -111,6 +130,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
>  {
>  	struct drm_atomic_state *a = state;
>  	int nplanes = dev->mode_config.num_total_plane;
> +	int ncrtcs = dev->mode_config.num_crtc;
>  	int i, ret = 0;
>  
>  	for (i = 0; i < nplanes; i++) {
> @@ -120,6 +140,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
>  				break;
>  		}
>  	}
> +	for (i = 0; i < ncrtcs; i++) {
> +		if (a->crtcs[i]) {
> +			ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]);
> +			if (ret)
> +				break;
> +		}
> +	}
>  
>  	a->acquire_ctx.frozen = true;
>  
> @@ -203,6 +230,7 @@ static void commit_locks(struct drm_atomic_state *a,
>  {
>  	struct drm_device *dev = a->dev;
>  	int nplanes = dev->mode_config.num_total_plane;
> +	int ncrtcs = dev->mode_config.num_crtc;
>  	int i;
>  
>  	for (i = 0; i < nplanes; i++) {
> @@ -213,6 +241,14 @@ static void commit_locks(struct drm_atomic_state *a,
>  		}
>  	}
>  
> +	for (i = 0; i < ncrtcs; i++) {
> +		struct drm_crtc *crtc = a->crtcs[i];
> +		if (crtc) {
> +			crtc->state->state = NULL;
> +			drm_crtc_destroy_state(crtc, a->cstates[i]);
> +		}
> +	}
> +
>  	/* and properly release them (clear in_atomic, remove from list): */
>  	drm_modeset_drop_locks(&a->acquire_ctx);
>  	ww_acquire_fini(ww_ctx);
> @@ -223,8 +259,18 @@ static int atomic_commit(struct drm_atomic_state *a,
>  		struct ww_acquire_ctx *ww_ctx)
>  {
>  	int nplanes = a->dev->mode_config.num_total_plane;
> +	int ncrtcs = a->dev->mode_config.num_crtc;
>  	int i, ret = 0;
>  
> +	for (i = 0; i < ncrtcs; i++) {
> +		struct drm_crtc *crtc = a->crtcs[i];
> +		if (crtc) {
> +			ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]);
> +			if (ret)
> +				break;
> +		}
> +	}
> +
>  	for (i = 0; i < nplanes; i++) {
>  		struct drm_plane *plane = a->planes[i];
>  		if (plane) {
> @@ -403,6 +449,7 @@ static int
>  commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>  {
>  	struct drm_atomic_state *a = pstate->state;
> +	struct drm_crtc_state *cstate = NULL;
>  	struct drm_framebuffer *old_fb = plane->fb;
>  	struct drm_framebuffer *fb = pstate->fb;
>  	bool enabled = pstate->crtc && fb;
> @@ -425,8 +472,11 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>  		}
>  	} else {
>  		struct drm_crtc *crtc = pstate->crtc;
> +		cstate = drm_atomic_get_crtc_state(crtc, pstate->state);
>  		if (pstate->update_plane ||
>  				(pstate->new_fb && !can_flip(plane, pstate))) {
> +/* TODO pass event to update_plane().. */
> +WARN_ON(cstate->event);
>  			ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
>  					pstate->crtc_x, pstate->crtc_y,
>  					pstate->crtc_w, pstate->crtc_h,
> @@ -443,7 +493,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>  			}
>  
>  		} else if (pstate->new_fb) {
> -			ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
> +			ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags);
>  			if (ret == 0) {
>  				/*
>  				 * Warn if the driver hasn't properly updated the plane->fb
> @@ -473,9 +523,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>  		 * original code.
>  		 */
>  		swap_plane_state(plane, pstate->state);
> +		if (cstate)
> +			cstate->event = NULL;
>  	}
>  
> -
>  	if (fb)
>  		drm_framebuffer_unreference(fb);
>  	if (old_fb)
> @@ -484,8 +535,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>  	return ret;
>  }
>  
> +int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data)
> +{
> +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +	if (IS_ERR(cstate))
> +		return PTR_ERR(cstate);
> +	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
> +}
> +EXPORT_SYMBOL(drm_atomic_crtc_set_property);
> +
> +static void init_crtc_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate, struct drm_atomic_state *state)
> +{
> +	/* snapshot current state: */
> +	*cstate = *crtc->state;
> +	cstate->state = state;
> +
> +	if (cstate->connector_ids) {
> +		int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]);
> +		cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL);
> +	}
> +
> +	/* this should never happen.. but make sure! */
> +	WARN_ON(cstate->event);
> +	cstate->event = NULL;
> +}
> +
> +struct drm_crtc_state *
> +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
> +{
> +	struct drm_crtc_state *cstate;
> +	int ret;
> +
> +	cstate = a->cstates[crtc->id];
> +
> +	if (!cstate) {
> +		ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
> +		if (ret)
> +			return ERR_PTR(ret);
> +
> +		cstate = drm_crtc_create_state(crtc);
> +		if (!cstate)
> +			return ERR_PTR(-ENOMEM);
> +		init_crtc_state(crtc, cstate, a);
> +		a->crtcs[crtc->id] = crtc;
> +		a->cstates[crtc->id] = cstate;
> +
> +		/* we'll need it later, so make sure we have state
> +		 * for primary plane too:
> +		 */
> +		drm_atomic_get_plane_state(crtc->primary, a);

I haven't figured out why. With primary planes I don't really see a need
for this. If we need it to implement the legacy setcrtc interface, then
that should be done there, not here.

> +	}
> +	return cstate;
> +}
> +EXPORT_SYMBOL(drm_atomic_get_crtc_state);
> +
> +static void
> +swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
> +{
> +	struct drm_crtc_state *cstate = a->cstates[crtc->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->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 int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct drm_plane_state *pstate =
> +			drm_atomic_get_plane_state(crtc->primary, cstate->state);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	struct drm_connector **connector_set = get_connector_set(crtc->dev,
> +			cstate->connector_ids, cstate->num_connector_ids);
> +	struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate);
> +	struct drm_mode_set set = {
> +			.crtc = crtc,
> +			.x = pstate->src_x >> 16,
> +			.y = pstate->src_y >> 16,
> +			.mode = mode,
> +			.num_connectors = cstate->num_connector_ids,
> +			.connectors = connector_set,
> +			.fb = fb,
> +	};
> +	int ret;
> +
> +	if (IS_ERR(mode)) {
> +		ret = PTR_ERR(mode);
> +		return ret;
> +	}
> +
> +	if (fb)
> +		drm_framebuffer_reference(fb);
> +
> +	ret = drm_mode_set_config_internal(&set);
> +	if (!ret) {
> +		swap_crtc_state(crtc, cstate->state);
> +		pstate->new_fb = pstate->update_plane = false;
> +	}
> +
> +	if (fb)
> +		drm_framebuffer_unreference(fb);
> +
> +	kfree(connector_set);
> +	if (mode)
> +		drm_mode_destroy(dev, mode);
> +	return ret;
> +}
> +
> +static int
> +commit_crtc_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate)
> +{
> +	struct drm_plane_state *pstate =
> +			drm_atomic_get_plane_state(crtc->primary, cstate->state);
> +	int ret = -EINVAL;
> +
> +	if (cstate->set_config)
> +		return set_config(crtc, cstate);
> +
> +	if (!pstate->fb) {
> +		/* disable */
> +		struct drm_mode_set set = {
> +				.crtc = crtc,
> +				.fb = NULL,
> +		};
> +
> +		ret = drm_mode_set_config_internal(&set);
> +		if (!ret) {
> +			swap_crtc_state(crtc, cstate->state);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
>  const struct drm_atomic_funcs drm_atomic_funcs = {
>  		.check_plane_state  = drm_plane_check_state,
>  		.commit_plane_state = commit_plane_state,
> +
> +		.check_crtc_state   = drm_crtc_check_state,
> +		.commit_crtc_state  = commit_crtc_state,
>  };
>  EXPORT_SYMBOL(drm_atomic_funcs);
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index b556a31..e14d517 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -689,10 +689,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
>  void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  {
>  	struct drm_device *dev = fb->dev;
> -	struct drm_crtc *crtc;
>  	struct drm_plane *plane;
> -	struct drm_mode_set set;
> -	int ret;
>  
>  	WARN_ON(!list_empty(&fb->filp_head));
>  
> @@ -712,7 +709,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  	 * in this manner.
>  	 */
>  	if (atomic_read(&fb->refcount.refcount) > 1) {
> -		void *state;
> +		struct drm_atomic_state *state;
>  
>  		state = dev->driver->atomic_begin(dev, 0);
>  		if (IS_ERR(state)) {
> @@ -720,24 +717,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  			return;
>  		}
>  
> -		/* TODO once CRTC is converted to state/properties, we can push the
> -		 * locking down into drm_atomic_commit(), since that is where
> -		 * the actual changes take place..
> -		 */
> -		drm_modeset_lock_all(dev);
> -		/* remove from any CRTC */
> -		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> -			if (crtc->primary->fb == fb) {
> -				/* should turn off the crtc */
> -				memset(&set, 0, sizeof(struct drm_mode_set));
> -				set.crtc = crtc;
> -				set.fb = NULL;
> -				ret = drm_mode_set_config_internal(&set);
> -				if (ret)
> -					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
> -			}
> -		}
> -
> +		/* remove from any plane */
>  		list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
>  			if (plane->fb == fb)
>  				drm_plane_force_disable(plane, state);
> @@ -750,8 +730,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);
> @@ -782,9 +760,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  	struct drm_mode_config *config = &dev->mode_config;
>  	int ret;
>  
> +	/* this is now required: */
> +	WARN_ON(!funcs->set_property);
> +
>  	crtc->dev = dev;
>  	crtc->funcs = funcs;
> -	crtc->invert_dimensions = false;
> +	crtc->state = drm_crtc_create_state(crtc);
> +	crtc->state->invert_dimensions = false;
>  
>  	drm_modeset_lock_all(dev);
>  	drm_modeset_lock_init(&crtc->mutex);
> @@ -796,14 +778,17 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>  		goto out;
>  
>  	crtc->base.properties = &crtc->properties;
> -	crtc->base.propvals = &crtc->propvals;
> +	crtc->base.propvals = &crtc->state->propvals;
>  
>  	list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
>  	dev->mode_config.num_crtc++;
>  
>  	crtc->primary = primary;
>  	if (primary)
> -		primary->possible_crtcs = 1 << drm_crtc_index(crtc);
> +		primary->possible_crtcs = 1 << crtc->id;
> +
> +	drm_object_attach_property(&crtc->base, config->prop_mode, 0);
> +	drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
>  
>   out:
>  	drm_modeset_unlock_all(dev);
> @@ -832,31 +817,245 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
>  	drm_mode_object_put(dev, &crtc->base);
>  	list_del(&crtc->head);
>  	dev->mode_config.num_crtc--;
> +
> +	drm_crtc_destroy_state(crtc, crtc->state);
>  }
>  EXPORT_SYMBOL(drm_crtc_cleanup);
>  
> -/**
> - * drm_crtc_index - find the index of a registered CRTC
> - * @crtc: CRTC to find index for
> - *
> - * Given a registered CRTC, return the index of that CRTC within a DRM
> - * device's list of CRTCs.
> - */
> -unsigned int drm_crtc_index(struct drm_crtc *crtc)
> +/* get display-mode from user-mode */
> +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate)
>  {
> -	unsigned int index = 0;
> -	struct drm_crtc *tmp;
> +	struct drm_display_mode *mode = NULL;
> +	if (cstate->mode_valid) {
> +		struct drm_device *dev = crtc->dev;
> +		int ret;
>  
> -	list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
> -		if (tmp == crtc)
> -			return index;
> +		mode = drm_mode_create(dev);
> +		if (!mode)
> +			return ERR_PTR(-ENOMEM);
> +
> +		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);
> +		}
>  
> -		index++;
> +		drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
>  	}
> +	return mode;
> +}
>  
> -	BUG();
> +static int connector_idx(struct drm_crtc_state *state,
> +		uint32_t connector_id)
> +{
> +	int i;
> +	for (i = 0; i < state->num_connector_ids; i++)
> +		if (state->connector_ids[i] == connector_id)
> +			return i;
> +	return -1;
> +}
> +
> +static int remove_connector(struct drm_crtc *ocrtc,
> +		struct drm_crtc_state *ostate, struct drm_atomic_state *state,
> +		int idx)
> +{
> +	struct drm_mode_config *config = &ocrtc->dev->mode_config;
> +	uint32_t *new_connector_ids;
> +	int a, b;
> +
> +	/* before deletion point: */
> +	a = idx * sizeof(ostate->connector_ids[0]);
> +
> +	/* after deletion point: */
> +	b = (ostate->num_connector_ids - 1 - idx) *
> +			sizeof(ostate->connector_ids[0]);
> +
> +	new_connector_ids = kmalloc(a+b, GFP_KERNEL);
> +	if (!new_connector_ids)
> +		return -ENOMEM;
> +
> +	memcpy(new_connector_ids, ostate->connector_ids, a);
> +	memcpy(&new_connector_ids[idx],
> +			&ostate->connector_ids[idx + 1], b);
> +
> +	return drm_mode_crtc_set_obj_prop(ocrtc, state,
> +		config->prop_connector_ids, a + b,
> +		new_connector_ids);
>  }
> -EXPORT_SYMBOL(drm_crtc_index);
> +
> +static int check_connectors(struct drm_crtc *crtc,
> +		struct drm_atomic_state *state, bool fix,
> +		uint32_t *connector_ids, uint32_t num_connector_ids)
> +{
> +	struct drm_mode_config *config = &crtc->dev->mode_config;
> +	struct drm_crtc *ocrtc; /* other connector */
> +
> +	list_for_each_entry(ocrtc, &config->crtc_list, head) {
> +		struct drm_crtc_state *ostate; /* other state */
> +		unsigned i;
> +
> +		if (ocrtc == crtc)
> +			continue;
> +
> +		ostate = drm_atomic_get_crtc_state(crtc, state);
> +		if (IS_ERR(ostate))
> +			return PTR_ERR(ostate);
> +
> +		for (i = 0; i < num_connector_ids; i++) {
> +			struct drm_connector *connector;
> +			uint32_t cid = connector_ids[i];
> +			int idx;
> +
> +retry:
> +			idx = connector_idx(ostate, cid);
> +			if (idx < 0)
> +				continue;
> +
> +			if (fix) {
> +				int ret = remove_connector(ocrtc,
> +						ostate, state, idx);
> +				if (ret)
> +					return ret;
> +				goto retry;
> +			}
> +
> +			connector = drm_connector_find(crtc->dev, cid);
> +			DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n",
> +					connector->base.id,
> +					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_plane *primary = crtc->primary;
> +	struct drm_plane_state *pstate =
> +			drm_atomic_get_plane_state(primary, state->state);
> +	struct drm_framebuffer *fb = pstate->fb;
> +	int hdisplay, vdisplay;
> +	struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state);
> +	unsigned x, y;
> +
> +	if (IS_ERR(mode))
> +		return PTR_ERR(mode);
> +
> +	/* disabling the crtc is allowed: */
> +	if (!(fb && state->mode_valid))
> +		return 0;
> +
> +	hdisplay = state->mode.hdisplay;
> +	vdisplay = state->mode.vdisplay;
> +
> +	if (mode && drm_mode_is_stereo(mode)) {
> +		struct drm_display_mode adjusted = *mode;
> +
> +		drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
> +		hdisplay = adjusted.crtc_hdisplay;
> +		vdisplay = adjusted.crtc_vdisplay;
> +	}
> +
> +	if (state->invert_dimensions)
> +		swap(hdisplay, vdisplay);
> +
> +	x = pstate->src_x >> 16;
> +	y = pstate->src_y >> 16;
> +
> +	if (hdisplay > fb->width ||
> +	    vdisplay > fb->height ||
> +	    x > fb->width - hdisplay ||
> +	    y > fb->height - vdisplay) {
> +		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
> +			      fb->width, fb->height, hdisplay, vdisplay,
> +			      x, y, state->invert_dimensions ? " (inverted)" : "");
> +		return -ENOSPC;
> +	}
> +
> +	if (crtc->enabled && !state->set_config) {
> +		if (primary->state->fb->pixel_format != fb->pixel_format) {
> +			DRM_DEBUG_KMS("Page flip is not allowed to "
> +					"change frame buffer format.\n");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (state->num_connector_ids == 0) {
> +		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
> +		return -EINVAL;
> +	}
> +
> +	if (state->connectors_change) {
> +		int ret = check_connectors(crtc, state->state, false,
> +				state->connector_ids, state->num_connector_ids);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (mode)
> +		drm_mode_destroy(crtc->dev, mode);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_crtc_check_state);
> +
> +void drm_crtc_commit_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *state)
> +{
> +	crtc->state = state;
> +	crtc->base.propvals = &state->propvals;
> +}
> +EXPORT_SYMBOL(drm_crtc_commit_state);
> +
> +int drm_crtc_set_property(struct drm_crtc *crtc,
> +		struct drm_crtc_state *state,
> +		struct drm_property *property,
> +		uint64_t value, void *blob_data)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct drm_mode_config *config = &dev->mode_config;
> +
> +	/* grab primary plane state now, to ensure locks are held, etc. */
> +	drm_atomic_get_plane_state(crtc->primary, state->state);
> +
> +	drm_object_property_set_value(&crtc->base,
> +			&state->propvals, property, value, blob_data);
> +
> +	if (property == config->prop_mode) {
> +		if (!blob_data) {
> +			memset(&state->mode, 0, sizeof(state->mode));
> +			state->mode_valid = false;
> +		} else {
> +			/* check size: */
> +			if (value < sizeof(struct drm_mode_modeinfo))
> +				return -EINVAL;
> +			state->mode = *(struct drm_mode_modeinfo *)blob_data;
> +			state->mode_valid = true;
> +		}
> +		state->set_config = true;
> +	} else if (property == config->prop_connector_ids) {
> +		/* if connector-id's changing, we need to have all the locks: */
> +		struct drm_atomic_state *a = state->state;
> +		int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx);
> +		if (ret)
> +			return ret;
> +		state->connectors_change = true;
> +		state->num_connector_ids = value / sizeof(state->connector_ids[0]);
> +		kfree(state->connector_ids);
> +		state->connector_ids = blob_data;
> +		state->set_config = true;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_crtc_set_property);
>  
>  /*
>   * drm_mode_remove - remove and free a mode
> @@ -1239,6 +1438,10 @@ int drm_plane_check_state(struct drm_plane *plane,
>  	if (!fb)
>  		return 0;
>  
> +	/* we'll need this later during commit: */
> +	if (state->crtc)
> +		drm_atomic_get_crtc_state(state->crtc, state->state);
> +
>  	fb_width = fb->width << 16;
>  	fb_height = fb->height << 16;
>  
> @@ -1465,6 +1668,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;
>  }
>  
> @@ -1754,7 +1967,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)
> @@ -2001,8 +2214,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
>  		goto out;
>  	}
>  
> -	crtc_resp->x = crtc->x;
> -	crtc_resp->y = crtc->y;
> +	crtc_resp->x = crtc->primary->state->src_x >> 16;
> +	crtc_resp->y = crtc->primary->state->src_y >> 16;
>  	crtc_resp->gamma_size = crtc->gamma_size;
>  	if (crtc->primary->fb)
>  		crtc_resp->fb_id = crtc->primary->fb->base.id;
> @@ -2495,7 +2708,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
>  		vdisplay = adjusted.crtc_vdisplay;
>  	}
>  
> -	if (crtc->invert_dimensions)
> +	if (crtc->state->invert_dimensions)
>  		swap(hdisplay, vdisplay);
>  
>  	if (hdisplay > fb->width ||
> @@ -2504,7 +2717,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
>  	    y > fb->height - vdisplay) {
>  		DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
>  			      fb->width, fb->height, hdisplay, vdisplay, x, y,
> -			      crtc->invert_dimensions ? " (inverted)" : "");
> +			      crtc->state->invert_dimensions ? " (inverted)" : "");
>  		return -ENOSPC;
>  	}
>  
> @@ -2531,22 +2744,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;
> +	struct drm_atomic_state *state = NULL;
>  	int ret;
>  	int i;
>  
>  	if (!drm_core_check_feature(dev, DRIVER_MODESET))
>  		return -EINVAL;
>  
> -	/* For some reason crtc x/y offsets are signed internally. */
> -	if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
> -		return -ERANGE;
> -
> -	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);
> @@ -2564,55 +2770,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
>  				ret = -EINVAL;
>  				goto out;
>  			}
> -			fb = crtc->primary->fb;
> -			/* Make refcounting symmetric with the lookup path. */
> -			drm_framebuffer_reference(fb);
> +			fb_id = crtc->primary->fb->base.id;
>  		} else {
> -			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
> -			if (!fb) {
> -				DRM_DEBUG_KMS("Unknown FB ID%d\n",
> -						crtc_req->fb_id);
> -				ret = -ENOENT;
> -				goto out;
> -			}
> -		}
> -
> -		mode = drm_mode_create(dev);
> -		if (!mode) {
> -			ret = -ENOMEM;
> -			goto out;
> +			fb_id = crtc_req->fb_id;
>  		}
> -
> -		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) {
> @@ -2620,52 +2786,65 @@ 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_plane_set_obj_prop(crtc->primary, state,
> +			config->prop_crtc_id, crtc->base.id, NULL) ||
> +		drm_mode_plane_set_obj_prop(crtc->primary, state,
> +			config->prop_fb_id, fb_id, NULL) ||
> +		drm_mode_plane_set_obj_prop(crtc->primary, state,
> +			config->prop_src_x, crtc_req->x << 16, NULL) ||
> +		drm_mode_plane_set_obj_prop(crtc->primary, state,
> +			config->prop_src_y, crtc_req->y << 16, 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;
>  }
>  
> @@ -4028,9 +4207,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;
>  }
> @@ -4424,6 +4600,51 @@ out:
>  	return ret;
>  }
>  
> +static struct drm_pending_vblank_event *create_vblank_event(
> +		struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
> +{
> +	struct drm_pending_vblank_event *e = NULL;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	if (file_priv->event_space < sizeof e->event) {
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +		goto out;
> +	}
> +	file_priv->event_space -= sizeof e->event;
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +	e = kzalloc(sizeof *e, GFP_KERNEL);
> +	if (e == NULL) {
> +		spin_lock_irqsave(&dev->event_lock, flags);
> +		file_priv->event_space += sizeof e->event;
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +		goto out;
> +	}
> +
> +	e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
> +	e->event.base.length = sizeof e->event;
> +	e->event.user_data = user_data;
> +	e->base.event = &e->event.base;
> +	e->base.file_priv = file_priv;
> +	e->base.destroy =
> +		(void (*) (struct drm_pending_event *)) kfree;
> +
> +out:
> +	return e;
> +}
> +
> +static void destroy_vblank_event(struct drm_device *dev,
> +		struct drm_file *file_priv, struct drm_pending_vblank_event *e)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	file_priv->event_space += sizeof e->event;
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +	kfree(e);
> +}
> +
>  /**
>   * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
>   * @dev: DRM device
> @@ -4446,10 +4667,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>  			     void *data, struct drm_file *file_priv)
>  {
>  	struct drm_mode_crtc_page_flip *page_flip = data;
> +	struct drm_mode_config *config = &dev->mode_config;
>  	struct drm_crtc *crtc;
> -	struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>  	struct drm_pending_vblank_event *e = NULL;
> -	unsigned long flags;
> +	struct drm_atomic_state *state;
>  	int ret = -EINVAL;
>  
>  	if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
> @@ -4463,92 +4684,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>  	if (!crtc)
>  		return -ENOENT;
>  
> -	drm_modeset_lock(&crtc->mutex, NULL);
> -	if (crtc->primary->fb == NULL) {
> -		/* The framebuffer is currently unbound, presumably
> -		 * due to a hotplug event, that userspace has not
> -		 * yet discovered.
> -		 */
> -		ret = -EBUSY;
> -		goto out;
> -	}
> -
> -	if (crtc->funcs->page_flip == NULL)
> -		goto out;
> -
> -	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
> -	if (!fb) {
> -		ret = -ENOENT;
> -		goto out;
> -	}
> -
> -	ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
> -	if (ret)
> -		goto out;
> -
> -	if (crtc->primary->fb->pixel_format != fb->pixel_format) {
> -		DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
> -		ret = -EINVAL;
> -		goto out;
> -	}
> +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->primary->fb;
> -	ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
> -	if (ret) {
> -		if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
> -			spin_lock_irqsave(&dev->event_lock, flags);
> -			file_priv->event_space += sizeof e->event;
> -			spin_unlock_irqrestore(&dev->event_lock, flags);
> -			kfree(e);
> -		}
> -		/* Keep the old fb, don't unref it. */
> -		old_fb = NULL;
> -	} else {
> -		/*
> -		 * Warn if the driver hasn't properly updated the crtc->fb
> -		 * field to reflect that the new framebuffer is now used.
> -		 * Failing to do so will screw with the reference counting
> -		 * on framebuffers.
> -		 */
> -		WARN_ON(crtc->primary->fb != fb);
> -		/* Unref only the old framebuffer. */
> -		fb = NULL;
> -	}
> +	ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
> +			config->prop_fb_id, page_flip->fb_id, NULL);
> +	if (ret)
> +		goto out;
>  
> -out:
> -	if (fb)
> -		drm_framebuffer_unreference(fb);
> -	if (old_fb)
> -		drm_framebuffer_unreference(old_fb);
> -	drm_modeset_unlock(&crtc->mutex);
> +	ret = dev->driver->atomic_check(dev, state);
> +	if (ret)
> +		goto out;
> +
> +	ret = dev->driver->atomic_commit(dev, state);
>  
> +out:
> +	if (ret && e)
> +		destroy_vblank_event(dev, file_priv, e);
> +	dev->driver->atomic_end(dev, state);
> +	if (ret == -EDEADLK)
> +		goto retry;
>  	return ret;
>  }
>  
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index b73d3b0..4669e69 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -286,7 +286,7 @@ 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;
> +	struct drm_atomic_state *state;
>  	int i;
>  
>  	drm_warn_on_modeset_not_all_locked(dev);
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> index 2a56973..f3c7e77 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> @@ -14,6 +14,7 @@
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  
>  #include "exynos_drm_crtc.h"
>  #include "exynos_drm_drv.h"
> @@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
>  	struct drm_device *dev = crtc->dev;
>  	struct exynos_drm_private *dev_priv = dev->dev_private;
>  	struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
> +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +
> +	if (IS_ERR(cstate))
> +		return PTR_ERR(cstate);
>  
>  	if (property == dev_priv->crtc_mode_property) {
>  		enum exynos_crtc_mode mode = val;
> @@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
>  		return 0;
>  	}
>  
> -	return -EINVAL;
> +	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
>  }
>  
>  static struct drm_crtc_funcs exynos_crtc_funcs = {
> diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
> index 6672732..5b6eee9 100644
> --- a/drivers/gpu/drm/gma500/cdv_intel_display.c
> +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
> @@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
>  	.cursor_move = gma_crtc_cursor_move,
>  	.gamma_set = gma_crtc_gamma_set,
>  	.set_config = gma_crtc_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = gma_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
> index 87b50ba..79b5692 100644
> --- a/drivers/gpu/drm/gma500/psb_intel_display.c
> +++ b/drivers/gpu/drm/gma500/psb_intel_display.c
> @@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = {
>  	.cursor_move = gma_crtc_cursor_move,
>  	.gamma_set = gma_crtc_gamma_set,
>  	.set_config = gma_crtc_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = gma_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index e9f6eb7..53b996f 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -10430,6 +10430,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_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 a034ed4..ba9bd91 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_mode.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
> @@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = {
>  	.cursor_move = mga_crtc_cursor_move,
>  	.gamma_set = mga_crtc_gamma_set,
>  	.set_config = drm_crtc_helper_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = mga_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> index 7cf0f78..d0d8befd 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> @@ -471,8 +471,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc,
>  		struct drm_atomic_state *state, struct drm_property *property,
>  		uint64_t val, void *blob_data)
>  {
> -	// XXX
> -	return -EINVAL;
> +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +	if (IS_ERR(cstate))
> +		return PTR_ERR(cstate);
> +	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
>  }
>  
>  #define CURSOR_WIDTH 64
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> index 771390b..7f4ee99 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> @@ -389,8 +389,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc,
>  		struct drm_atomic_state *state, struct drm_property *property,
>  		uint64_t val, void *blob_data)
>  {
> -	// XXX
> -	return -EINVAL;
> +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +	if (IS_ERR(cstate))
> +		return PTR_ERR(cstate);
> +	return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
>  }
>  
>  static const struct drm_crtc_funcs mdp5_crtc_funcs = {
> diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
> index 41be342..9e24632 100644
> --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
> +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
> @@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
>  	.cursor_move = nv04_crtc_cursor_move,
>  	.gamma_set = nv_crtc_gamma_set,
>  	.set_config = nouveau_crtc_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.page_flip = nouveau_crtc_page_flip,
>  	.destroy = nv_crtc_destroy,
>  };
> diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
> index 58af547..ecbffeb 100644
> --- a/drivers/gpu/drm/nouveau/nv50_display.c
> +++ b/drivers/gpu/drm/nouveau/nv50_display.c
> @@ -1329,6 +1329,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
>  	.cursor_move = nv50_crtc_cursor_move,
>  	.gamma_set = nv50_crtc_gamma_set,
>  	.set_config = nouveau_crtc_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = nv50_crtc_destroy,
>  	.page_flip = nouveau_crtc_page_flip,
>  };
> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
> index a75934d..772687b 100644
> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
> @@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
>  {
>  	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>  	struct omap_drm_private *priv = crtc->dev->dev_private;
> +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +	int ret;
> +
> +	if (IS_ERR(cstate))
> +		return PTR_ERR(cstate);
>  
>  	if (property == priv->rotation_prop) {
> -		crtc->invert_dimensions =
> +		cstate->invert_dimensions =
>  				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
>  	}
>  
> -	return omap_plane_set_property(omap_crtc->plane, state,
> +	ret = omap_plane_set_property(omap_crtc->plane, state,
>  			property, val, blob_data);
> +	if (ret)
> +		ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data);
> +	return ret;
>  }
>  
>  static const struct drm_crtc_funcs omap_crtc_funcs = {
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index da80bdc..3f64c47 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev)
>  		 */
>  		for (i = 0; i < priv->num_crtcs; i++) {
>  			drm_object_property_set_value(&priv->crtcs[i]->base,
> -					&priv->crtcs[i]->propvals,
> +					&priv->crtcs[i]->state->propvals,
>  					priv->rotation_prop, 0, NULL);
>  		}
>  
> diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
> index b54c970..25896a9 100644
> --- a/drivers/gpu/drm/qxl/qxl_display.c
> +++ b/drivers/gpu/drm/qxl/qxl_display.c
> @@ -29,6 +29,7 @@
>  #include "qxl_drv.h"
>  #include "qxl_object.h"
>  #include "drm_crtc_helper.h"
> +#include "drm_atomic.h"
>  
>  static bool qxl_head_enabled(struct qxl_head *head)
>  {
> @@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
>  	.cursor_set2 = qxl_crtc_cursor_set2,
>  	.cursor_move = qxl_crtc_cursor_move,
>  	.set_config = drm_crtc_helper_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = qxl_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
> index 8d99d5e..cc86aac 100644
> --- a/drivers/gpu/drm/radeon/radeon_display.c
> +++ b/drivers/gpu/drm/radeon/radeon_display.c
> @@ -32,6 +32,7 @@
>  
>  #include <linux/pm_runtime.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_edid.h>
>  
>  #include <linux/gcd.h>
> @@ -546,6 +547,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = {
>  	.cursor_move = radeon_crtc_cursor_move,
>  	.gamma_set = radeon_crtc_gamma_set,
>  	.set_config = radeon_crtc_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.destroy = radeon_crtc_destroy,
>  	.page_flip = radeon_crtc_page_flip,
>  };
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> index 299267d..f5a3d55 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> @@ -17,6 +17,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
>  static const struct drm_crtc_funcs crtc_funcs = {
>  	.destroy = drm_crtc_cleanup,
>  	.set_config = drm_crtc_helper_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.page_flip = rcar_du_crtc_page_flip,
>  };
>  
> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> index 90e023a..0a5280c 100644
> --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> @@ -17,6 +17,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
>  static const struct drm_crtc_funcs crtc_funcs = {
>  	.destroy = drm_crtc_cleanup,
>  	.set_config = drm_crtc_helper_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.page_flip = shmob_drm_crtc_page_flip,
>  };
>  
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
> index 92839ba..b07f116 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
> @@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
>  static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
>  		.destroy        = tilcdc_crtc_destroy,
>  		.set_config     = drm_crtc_helper_set_config,
> +		.set_property   = drm_atomic_crtc_set_property,
>  		.page_flip      = tilcdc_crtc_page_flip,
>  };
>  
> diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
> index cddc4fc..36d0116 100644
> --- a/drivers/gpu/drm/udl/udl_modeset.c
> +++ b/drivers/gpu/drm/udl/udl_modeset.c
> @@ -14,6 +14,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include "udl_drv.h"
>  
>  /*
> @@ -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_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 b2b9bd2..0313b00 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> @@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
>  	.cursor_move = vmw_du_crtc_cursor_move,
>  	.gamma_set = vmw_du_crtc_gamma_set,
>  	.destroy = vmw_ldu_crtc_destroy,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.set_config = vmw_ldu_crtc_set_config,
>  };
>  
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> index a95d3a0..b723e09 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> @@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
>  	.gamma_set = vmw_du_crtc_gamma_set,
>  	.destroy = vmw_sou_crtc_destroy,
>  	.set_config = vmw_sou_crtc_set_config,
> +	.set_property = drm_atomic_crtc_set_property,
>  	.page_flip = vmw_du_page_flip,
>  };
>  
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index 78e93ec..7946b7f 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -70,6 +70,9 @@
>  struct drm_atomic_funcs {
>  	int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
>  	int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
> +
> +	int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
> +	int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
>  };
>  
>  const extern struct drm_atomic_funcs drm_atomic_funcs;
> @@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane,
>  	return funcs->commit_plane_state(plane, pstate);
>  }
>  
> +int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
> +		struct drm_atomic_state *state, struct drm_property *property,
> +		uint64_t val, void *blob_data);
> +struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc,
> +		struct drm_atomic_state *state);
> +
> +static inline int
> +drm_atomic_check_crtc_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate)
> +{
> +	const struct drm_atomic_funcs *funcs =
> +			crtc->dev->driver->atomic_funcs;
> +	return funcs->check_crtc_state(crtc, cstate);
> +}
> +
> +static inline int
> +drm_atomic_commit_crtc_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate)
> +{
> +	const struct drm_atomic_funcs *funcs =
> +			crtc->dev->driver->atomic_funcs;
> +	return funcs->commit_crtc_state(crtc, cstate);
> +}
> +
>  /**
>   * struct drm_atomic_state - the state object used by atomic helpers
>   */
> @@ -118,6 +145,8 @@ struct drm_atomic_state {
>  	uint32_t flags;
>  	struct drm_plane **planes;
>  	struct drm_plane_state **pstates;
> +	struct drm_crtc **crtcs;
> +	struct drm_crtc_state **cstates;
>  
>  	bool committed;
>  	bool checked;       /* just for debugging */
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 58309cc..2fbf13a 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -284,6 +284,10 @@ struct drm_crtc_funcs {
>  			 struct drm_pending_vblank_event *event,
>  			 uint32_t flags);
>  
> +	struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc);
> +	void (*destroy_state)(struct drm_crtc *crtc,
> +			    struct drm_crtc_state *cstate);
> +
>  	int (*set_property)(struct drm_crtc *crtc,
>  			    struct drm_atomic_state *state,
>  			    struct drm_property *property, uint64_t val,
> @@ -291,21 +295,52 @@ struct drm_crtc_funcs {
>  };
>  
>  /**
> + * drm_crtc_state - mutable crtc state
> + * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
> + *    invert the width/height of the crtc.  This is used if the driver
> + *    is performing 90 or 270 degree rotated scanout
> + * @mode_valid: a valid mode has been set
> + * @set_config: needs modeset (crtc->set_config())
> + * @connectors_change: the connector-ids array has changed
> + * @num_connector_ids: the number of connector-ids
> + * @connector_ids: array of connector ids
> + * @mode: current mode timings
> + * @event: pending pageflip event
> + * @propvals: property values
> + * @state: current global/toplevel state object (for atomic) while an
> + *    update is in progress, NULL otherwise.
> + */
> +struct drm_crtc_state {
> +	bool invert_dimensions : 1;
> +	bool mode_valid        : 1;
> +
> +	/* transient state, only valid during atomic operation: */
> +	bool set_config        : 1;
> +	bool connectors_change : 1;
> +
> +	uint8_t num_connector_ids;
> +	uint32_t *connector_ids;
> +	struct drm_mode_modeinfo mode;
> +
> +	struct drm_pending_vblank_event *event;
> +
> +	struct drm_object_property_values propvals;
> +
> +	struct drm_atomic_state *state;
> +};
> +
> +/**
>   * drm_crtc - central CRTC control structure
>   * @dev: parent DRM device
>   * @head: list management
> + * @id: CRTC number, 0..n
>   * @mutex: per-CRTC locking
>   * @base: base KMS object for ID tracking etc.
>   * @primary: primary plane for this CRTC
>   * @cursor: cursor plane for this CRTC
> + * @state: the mutable state
>   * @enabled: is this CRTC enabled?
> - * @mode: current mode timings
>   * @hwmode: mode timings as programmed to hw regs
> - * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
> - *    invert the width/height of the crtc.  This is used if the driver
> - *    is performing 90 or 270 degree rotated scanout
> - * @x: x position on screen
> - * @y: y position on screen
>   * @funcs: CRTC control functions
>   * @gamma_size: size of gamma ramp
>   * @gamma_store: gamma ramp values
> @@ -322,6 +357,8 @@ struct drm_crtc {
>  	struct drm_device *dev;
>  	struct list_head head;
>  
> +	int id;
> +
>  	/**
>  	 * crtc mutex
>  	 *
> @@ -337,23 +374,19 @@ struct drm_crtc {
>  	struct drm_plane *primary;
>  	struct drm_plane *cursor;
>  
> +	struct drm_crtc_state *state;
> +
>  	/* Temporary tracking of the old fb while a modeset is ongoing. Used
>  	 * by drm_mode_set_config_internal to implement correct refcounting. */
>  	struct drm_framebuffer *old_fb;
>  
>  	bool enabled;
>  
> -	/* Requested mode from modesetting. */
> -	struct drm_display_mode mode;
> -
>  	/* Programmed mode in hw, after adjustments for encoders,
>  	 * crtc, panel scaling etc. Needed for timestamping etc.
>  	 */
>  	struct drm_display_mode hwmode;
>  
> -	bool invert_dimensions;
> -
> -	int x, y;
>  	const struct drm_crtc_funcs *funcs;
>  
>  	/* CRTC gamma size for reporting to userspace */
> @@ -367,9 +400,15 @@ struct drm_crtc {
>  	void *helper_private;
>  
>  	struct drm_object_properties properties;
> -	struct drm_object_property_values propvals;
> -};
>  
> +	/* These are (temporary) duplicate information from what is in the
> +	 * drm_crtc_state struct..  keeping duplicate copy here makes the
> +	 * switch to atomic far less intrusive.  Once all the drivers and
> +	 * the crtc/fb helpers are updated, then we can remove these:
> +	 */
> +	int x, y;
> +	struct drm_display_mode mode;
> +};
>  
>  /**
>   * drm_connector_funcs - control connectors on a given device
> @@ -875,6 +914,8 @@ struct drm_mode_config {
>  	struct drm_property *prop_crtc_h;
>  	struct drm_property *prop_fb_id;
>  	struct drm_property *prop_crtc_id;
> +	struct drm_property *prop_connector_ids;
> +	struct drm_property *prop_mode;
>  	struct drm_property *edid_property;
>  	struct drm_property *dpms_property;
>  	struct drm_property *plane_type_property;
> @@ -935,7 +976,8 @@ extern int drm_crtc_init(struct drm_device *dev,
>  			 struct drm_crtc *crtc,
>  			 const struct drm_crtc_funcs *funcs);
>  extern void drm_crtc_cleanup(struct drm_crtc *crtc);
> -extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
> +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate);
>  
>  /**
>   * drm_crtc_mask - find the mask of a registered CRTC
> @@ -946,9 +988,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
>   */
>  static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
>  {
> -	return 1 << drm_crtc_index(crtc);
> +	return 1 << crtc->id;
>  }
>  
> +extern int drm_crtc_check_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *state);
> +extern void drm_crtc_commit_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *state);
> +extern int drm_crtc_set_property(struct drm_crtc *crtc,
> +		struct drm_crtc_state *state,
> +		struct drm_property *property,
> +		uint64_t value, void *blob_data);
> +
>  extern void drm_connector_ida_init(void);
>  extern void drm_connector_ida_destroy(void);
>  extern int drm_connector_init(struct drm_device *dev,
> @@ -1024,6 +1075,7 @@ 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 void drm_mode_group_destroy(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);
> @@ -1251,6 +1303,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
>  	return mo ? obj_to_blob(mo) : NULL;
>  }
>  
> +static inline struct drm_crtc_state *
> +drm_crtc_create_state(struct drm_crtc *crtc)
> +{
> +	if (crtc->funcs->create_state)
> +		return crtc->funcs->create_state(crtc);
> +	return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL);
> +}
> +
> +static inline void
> +drm_crtc_destroy_state(struct drm_crtc *crtc,
> +		struct drm_crtc_state *cstate)
> +{
> +	kfree(cstate->connector_ids);
> +	if (crtc->funcs->destroy_state)
> +		crtc->funcs->destroy_state(crtc, cstate);
> +	else
> +		kfree(cstate);
> +}
> +
>  static inline struct drm_plane_state *
>  drm_plane_create_state(struct drm_plane *plane)
>  {
> -- 
> 1.9.0
> 

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

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

* Re: [PATCH 13/17] drm: push locking down into restore_fbdev_mode
  2014-05-24 18:30 ` [PATCH 13/17] drm: push locking down into restore_fbdev_mode Rob Clark
@ 2014-05-26  9:34   ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26  9:34 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:22PM -0400, Rob Clark wrote:
> 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.
> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>

Yeah, makes sense. But I don't like the bool lockless interface,
especially since the only user of it is also in the fb helper.

Imo much better to add a static function with __ prefix internal to
drm_fb_helper.c which is lockless and give drivers less rope to hang
themselves. Since I just can't think of a good reason why they want to not
have locking when force-restoring the fbdev emulation.

With that changed this is Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> ---
>  drivers/gpu/drm/armada/armada_fbdev.c     |  4 +---
>  drivers/gpu/drm/drm_fb_cma_helper.c       |  9 ++-------
>  drivers/gpu/drm/drm_fb_helper.c           | 17 ++++++++---------
>  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 ++-
>  10 files changed, 21 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
> index 948cb14..9976125 100644
> --- a/drivers/gpu/drm/armada/armada_fbdev.c
> +++ b/drivers/gpu/drm/armada/armada_fbdev.c
> @@ -181,10 +181,8 @@ void armada_fbdev_lastclose(struct drm_device *dev)
>  {
>  	struct armada_private *priv = dev->dev_private;
>  
> -	drm_modeset_lock_all(dev);
>  	if (priv->fbdev)
> -		drm_fb_helper_restore_fbdev_mode(priv->fbdev);
> -	drm_modeset_unlock_all(dev);
> +		drm_fb_helper_restore_fbdev_mode(priv->fbdev, false);
>  }
>  
>  void armada_fbdev_fini(struct drm_device *dev)
> diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
> index 65540b7..ccb7a9ac 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 4669e69..3815e1a 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)
>  	struct drm_atomic_state *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;
> @@ -355,7 +357,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
>  			continue;
>  		}
>  
> -		ret = drm_fb_helper_restore_fbdev_mode(helper);
> +		ret = drm_fb_helper_restore_fbdev_mode(helper, true);
>  		if (ret)
>  			error = true;
>  
> @@ -840,7 +842,6 @@ EXPORT_SYMBOL(drm_fb_helper_check_var);
>  int drm_fb_helper_set_par(struct fb_info *info)
>  {
>  	struct drm_fb_helper *fb_helper = info->par;
> -	struct drm_device *dev = fb_helper->dev;
>  	struct fb_var_screeninfo *var = &info->var;
>  
>  	if (var->pixclock != 0) {
> @@ -848,9 +849,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
>  		return -EINVAL;
>  	}
>  
> -	drm_modeset_lock_all(dev);
> -	drm_fb_helper_restore_fbdev_mode(fb_helper);
> -	drm_modeset_unlock_all(dev);
> +	drm_fb_helper_restore_fbdev_mode(fb_helper, false);
>  
>  	if (fb_helper->delayed_hotplug) {
>  		fb_helper->delayed_hotplug = false;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> index 2371716..486b21f 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> @@ -375,7 +375,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 0180292..72a35e3 100644
> --- a/drivers/gpu/drm/gma500/psb_drv.c
> +++ b/drivers/gpu/drm/gma500/psb_drv.c
> @@ -112,11 +112,9 @@ static void psb_driver_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 fce4a0d..28afd69 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -683,11 +683,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
>  	if (!dev_priv->fbdev)
>  		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 46e0f29..d24ca45 100644
> --- a/drivers/gpu/drm/msm/msm_drv.c
> +++ b/drivers/gpu/drm/msm/msm_drv.c
> @@ -382,11 +382,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(int irq, void *arg)
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index 3f64c47..ad082f4 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -590,9 +590,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 f7fca09..85aae37 100644
> --- a/drivers/gpu/drm/tegra/fb.c
> +++ b/drivers/gpu/drm/tegra/fb.c
> @@ -346,11 +346,8 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
>  
>  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);
>  }
>  
>  static void tegra_fb_output_poll_changed(struct drm_device *drm)
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index 6e622f7..99598a0 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.9.0
> 

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

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

* Re: [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip
  2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
                   ` (16 preceding siblings ...)
  2014-05-24 18:30 ` [PATCH 17/17] drm: Fix up the atomic legacy paths so they work Rob Clark
@ 2014-05-26 10:40 ` Daniel Vetter
  2014-05-26 12:48   ` Rob Clark
  17 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 10:40 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
> One more time, with feeling..
> 
> Previous revision of series:
> http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
> 
> And if you prefer, in git form:
> http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion
> git://people.freedesktop.org/~robclark/linux cold-fusion
> 
> This series does not include the actual atomic ioctl, but it does
> include all the needed infrastructure changes.
> 
> Compared to previous revision, I've split out drm_modeset_acquire_ctx
> from drm_atomic_state, so that we can ww acquire mode_config and crtc
> locks outside of atomic transactions.  (Which keeps lockdep happy wrt.
> drm_modeset_lock_all().)  I also got to the point in juggling things
> around where I wanted to let the compiler type checking do it's job,
> so 's/void *state/struct drm_atomic_state *state/g'.
> 
> At this point, I've tested this on i915 (few different generation
> laptops), radeon, and msm.  And of course w/ ww debug (deadlock in-
> jection) enabled.  So I think all the locking related paths should
> be covered.
> 
> I believe Thierry has tested a slighly older revision on tegra.  I
> would of course appreciate more testing on other drivers for which I
> don't have the hw.  But I think it is pretty much ready to go.  I do
> still owe some docs updates, I will send some patches for that in
> the near future, but didn't want to hold up giving others a chance
> to start banging on this.

Ok, I've done a fairly cursory look at this only and tried to concentrate
on grasping the high-level details. Patches contain a bunch of comments on
the details, but here is the big stuff.

First things first there's a bunch of prep work and refactoring sprinkled
throughout the series. Imo we should pick those out and rebase them on top
of drm-next asap to get them in before the actual interfaces. I hope I've
catched them all and commented everywhere about what imo is still missing
for merging. With that out of the way the real atomic review below.

I like the overall design. It will be a royal pain to convert i915 over to
this since thus far we've simply tucked away the staged state into
obj->new_foo pointers. Which means we get to change the entire driver
again ;-)

But I think the interfaces to driver and overall api design need some good
polish:

- Imo way too much is done in driver callbacks, which then again call
  helpers. Parsing of the core properties for plane/crtcs should be done
  in the core imo. This way drivers only need to register ->set_prop
  callbacks if there's a driver-specific property they support.

- Imo those obj->set_prop callbacks should take the object-specific
  drm_obj_state object, not the global atomic state structure. Will lead
  to _much_ less lookup code duplicated all over the place. If we then
  also add a state->obj pointer we could also ditch that parameter. Ofc
  obj would be specific to the state at hand.

- One downside of that is that drivers won't be able to interfere the
  allocation step any more. I think we'd need an additional
  ->alloc_atomic_state call so that drivers can still easily subclass some
  objects with their own type.

- There's imo a bit a confusion between what's helper and what's driver
  api. My big gripe here is with the set_prop stuff which imo really
  should be core and not helpers. The default ->commit/check
  implementations otoh should be more clearly delineated as helper
  implementations that drivers can/should overwrite. I think we should
  split drm_atomic.c into drm_atomic.c (with the official pieces) and
  drm_atomic_helper.c (with the suggested standard/transitional
  implemenations for commit/check). Helper functions should have the
  drm_atomic_helper_ prefix.

- I also think we should split the vfuncs like we do with the crtc
  helpers. Imo the core interface should just be an ->alloc_state and
  ->set_prop on all kms objects, plus a global ->prep/check/commit at the
  driver level. The object-specific ->check/commit hooks otoh should be
  part of the atomic helper library and in some separate
  atomic_helper_funcs structure. Imo we could either extend the sturcture
  used by the crtc helpers (hackish imo) or change the type of that to
  make it clear it's for the crtc helpers and add a new pointer for atomic
  helpers. E.g. in i915 I expect that we'll implement our very own
  ->check/commit hooks using our own compute_config infrastructure. Maybe
  we could switch to the ->check hooks of the atomic helper eventually,
  but that will be a lot of work. And in any case we won't ever switch to
  the ->commit hook as you have it in your helpers since for truly atomic
  flips/modesets we need to interleave all the updates of all objects in
  various phases.

The patches are also rather big, which makes them a bit a pain to review.
Matt's approach for the primary planes was much easier:
1) Add the new core interfaces for the new world.
2) Implement helpers for drivers to transition and convert drivers.
3) Implement ioctls using the new driver interfaces.

That approach would also help a lot in figuring out what's helper code and
so optional, and what's core stuff.

The other big problem I see still is with the locking:

- Imo switching mode_config.mutex to a ww_mutex won't work. I know that
  the magic rules of w/w locking are _really_ tempting. But I don't think
  it will actually solve anything and instead only cause havoc.

- I think we need ww mutexes for crtcs, but they will be fairly useless
  without ww mutexes on plane. Not for i915, but for all those drivers
  which can switch planes around between different crtcs. Imo we should do
  this as a first step (before getting all the atomic interfaces in), and
  pimp the set_plane locking a bit already like I've described in some
  mail.

- There's a pile of state that's currently not really protected by
  anything in your patches. One piece is all the obj->state properties.
  Another one is state detected at runtime like e.g. whether a hdmi sink
  supports audio or what kind of link bw parameters a dp sink supports.
  Thus far this was all protected by mode_config.mutex, but we can't take
  this one in the generic atomic ioctls for pretty obvious reasons.

  I think we need a new lock here, e.g. dev->mode_config.state_lock. It's
  going to be painful, since we need to roll this out over _all_ driver
  callbacks which can change the meaning of some property. E.g. all the
  ->detect callbacks in i915. The important part is that no one is allowed
  to do any costly operations why holding this lock (i.e. anything like
  reading EDIDs or doing load-detect), it is only for updating/reading
  this state.

- To avoid nasties with locking inversion between the plain
  mode_config.mutex, the state_mutex and the various ww mutexes we need to
  rather completely rework the locking sequence for atomic updates
  compared to what you have. Since in the driver's detect callbacks we
  first grab the mode_config.mutex and then potentiall ww mutexes (only
  i915) and maybe also the state_mutex (for updating autodetected
  properties). But for atomic updates we first need to grab the
  state_mutex (so that we can get at the current state) and then ww
  mutexes and the mode_config.mutex. And for the later we're not even good
  at predicting the order we want to acquire them.

  Stage 1: Create the new state
  We grab _just_ the mode_config.state_mutex and allocate all the state
  objects. This will allow us to grab a copy of the current state of
  everything without races or inconsistencies.

  As a consequence ->set_prop hooks may not touch anything outside of the
  date/state protected by the state_mutex.

  Stage 2: check/commit stage
  We drop the state_lock since it would nest the wrong way round again
  with mode_config.mutex. We should have a complete copy of all state
  already, and if something races against us (2nd ioctl or a output probe
  call) we don't really care.

  Only then will the check/commit hooks grab all the locks. For the
  mode_config.mutex we could add a bit to the global state the we need it
  (e.g. when the connector->crtc links change or for some other
  driver-private need).

  That only leaves races with a 2nd concurrent atomic modeset ioctl. Which
  can be avoided by grabbing yet another lock, which we drop after the
  driver has acquired all real locks. For that we could mandate the
  ->check callback and require that it grabs all ww mutexes plus the
  mode_config.mutex.

For implementing this madness (if we agree it's the right approach) we can
go step-by-step:
- Convert crtc->mutex to a ww mutex.
- Add plane ww mutexes, make set_plane locking more fine-grained.
- Add the mode_config.state_lock, roll it out across all driver's ->detect
  callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
- Use all this correctly in the atomic stuff.

Ok, that's the two big comments on this work from my side. Now reality
check: How much off the mark am I?

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

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

* Re: [PATCH 04/17] drm: add object property type
  2014-05-26  8:29   ` Daniel Vetter
  2014-05-26  8:33     ` Daniel Vetter
@ 2014-05-26 11:06     ` Rob Clark
  1 sibling, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-26 11:06 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 4:29 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 02:30:13PM -0400, Rob Clark wrote:
>> 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.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>> ---
>>  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 dd10e4c..c049ba7 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -3142,6 +3142,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)
>> @@ -3162,6 +3164,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);
>> @@ -3299,6 +3304,23 @@ 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);
>> +
>>  /**
>>   * drm_property_add_enum - add a possible value to an enumeration property
>>   * @property: enumeration property to change
>> @@ -3319,14 +3341,16 @@ int drm_property_add_enum(struct drm_property *property, int index,
>>  {
>>       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)) {
>> @@ -3509,10 +3533,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++;
>>       }
>> @@ -3534,7 +3559,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;
>> @@ -3556,7 +3582,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;
>> @@ -3712,19 +3738,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;
>
> Ok, so this usage is indeed safe for framebuffers since you only compare
> the pointer against NULL. But you can't ever access this pointer since it
> might disappear any time after you've dropped the idr lock. I think what
> you actually want here is a new interface drm_mode_object_exists which
> just returns a bool.

Hmm, I think I'll just drop drm_property_get_obj() and call directly
_object_find() here, I think.  Unless we come up with other spots that
need a drm_mode_object_exists().  That would avoid potential future
misunderstandings.

BR,
-R

> That won't expose any risky things to callers and we can still keep the
> restriction that calling drm_mode_object_get for framebuffers is a BUG.
> -Daniel
>
>>       } else {
>>               int i;
>>               for (i = 0; i < property->num_values; i++)
>> @@ -3815,9 +3847,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, struct drm_atomic_state *state,
>> -             struct drm_property *property, uint64_t value, void *blob_data)
>> +static int drm_mode_set_obj_prop(struct drm_mode_object *obj,
>> +             struct drm_atomic_state *state, struct drm_property *property,
>> +             uint64_t value, void *blob_data)
>>  {
>>       if (drm_property_change_is_valid(property, value)) {
>>               switch (obj->type) {
>> @@ -3831,6 +3863,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;
>> @@ -3866,7 +3900,7 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
>>               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 83e0f88..65bfb8b 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -191,6 +191,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;
>>  };
>> @@ -941,6 +942,23 @@ extern void drm_mode_config_cleanup(struct drm_device *dev);
>>
>>  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);
>> @@ -974,6 +992,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);
>> @@ -990,6 +1010,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.9.0
>>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 06/17] drm: helpers to find mode objects
  2014-05-26  8:37   ` Daniel Vetter
  2014-05-26  8:55     ` Daniel Vetter
@ 2014-05-26 11:12     ` Rob Clark
  1 sibling, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-26 11:12 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 4:37 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 02:30:15PM -0400, Rob Clark wrote:
>> Add a few more useful helpers to find mode objects.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
> There's a pile more in drivers for these. I like this, but imo we should
> also convert drivers while at it. Also, docbook is missing. And since it's
> a cleanup I think it's better to do this first, directly on top of
> drm-next. The patch itself looks good.

Yeah, the intent was to go back and add missing docbook as follow-up
patches, to try to get this into a shape where it could be merged
earlier.

I can have a go at coccinelle too for follow-up patches.

BR,
-R

> -Daniel
>
>> ---
>>  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 fa86fba..bd12185 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -1736,7 +1736,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))
>> @@ -1744,13 +1743,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;
>> @@ -1804,7 +1801,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;
>> @@ -1828,13 +1824,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
>>
>>       drm_modeset_lock(&dev->mode_config.mutex, NULL);
>>
>> -     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;
>>
>> @@ -1949,7 +1943,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;
>>
>> @@ -1957,13 +1950,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;
>> @@ -2061,7 +2052,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;
>> @@ -2070,13 +2060,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;
>> @@ -2129,7 +2117,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;
>> @@ -2144,14 +2131,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) {
>> @@ -2168,15 +2153,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) {
>> @@ -2363,7 +2346,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;
>> @@ -2381,14 +2363,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) {
>> @@ -2471,15 +2451,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));
>> @@ -2511,7 +2489,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;
>>
>> @@ -2521,12 +2498,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) {
>> @@ -3522,7 +3498,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;
>> @@ -3541,12 +3516,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)) {
>> @@ -3676,7 +3650,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;
>> @@ -3686,12 +3659,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;
>> @@ -3898,7 +3870,6 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
>>               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;
>>
>> @@ -3915,11 +3886,9 @@ static int drm_mode_set_obj_prop_id(struct drm_device *dev,
>>       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);
>> @@ -4126,7 +4095,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;
>> @@ -4136,12 +4104,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;
>> @@ -4200,7 +4167,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;
>> @@ -4210,12 +4176,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) {
>> @@ -4268,7 +4233,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;
>> @@ -4282,10 +4246,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->primary->fb == NULL) {
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index cd4a61a..b940a29 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -1112,6 +1112,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)
>>  {
>> @@ -1128,6 +1137,30 @@ 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;
>> +}
>> +
>>  /* Plane list iterator for legacy (overlay only) planes. */
>>  #define drm_for_each_legacy_plane(plane, planelist) \
>>       list_for_each_entry(plane, planelist, head) \
>> --
>> 1.9.0
>>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 11/17] drm: convert plane to properties/state
  2014-05-26  9:12   ` Daniel Vetter
@ 2014-05-26 11:32     ` Rob Clark
  2014-05-26 14:52       ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 11:32 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 5:12 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 02:30:20PM -0400, Rob Clark wrote:
>> Break the mutable state of a plane out into a separate structure
>> and use atomic properties mechanism to set plane attributes.  This
>> makes it easier to have some helpers for plane->set_property()
>> and for checking for invalid params.  The idea is that individual
>> drivers can wrap the state struct in their own struct which adds
>> driver specific parameters, for easy build-up of state across
>> multiple set_property() calls and for easy atomic commit or roll-
>> back.
>>
>> The same should be done for CRTC, encoder, and connector, but this
>> patch only includes the first part (plane).
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
> Imo s/plane->id/plane->index/ since plane_id can be too easily confused
> with the mode object id used for the idr lookup. We already have
> drm_crtc_index, so this is established convention already.

fair enough..  I suppose I could do the same in crtc.

> A few comments below.
>
>> ---
>>  drivers/gpu/drm/armada/armada_overlay.c    |  11 +-
>>  drivers/gpu/drm/drm_atomic.c               | 225 ++++++++++++++-
>>  drivers/gpu/drm/drm_crtc.c                 | 433 ++++++++++++++++++++---------
>>  drivers/gpu/drm/drm_fb_helper.c            |  17 +-
>>  drivers/gpu/drm/drm_plane_helper.c         |   2 +
>>  drivers/gpu/drm/exynos/exynos_drm_plane.c  |   7 +-
>>  drivers/gpu/drm/i915/intel_sprite.c        |   1 +
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c  |   6 +-
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c  |   6 +-
>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c |   8 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c       |   7 +
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c    |   8 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c |   2 +
>>  include/drm/drm_atomic.h                   |  29 +-
>>  include/drm/drm_crtc.h                     | 114 +++++++-
>>  16 files changed, 725 insertions(+), 153 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
>> index 601ba9a..041ea89 100644
>> --- a/drivers/gpu/drm/armada/armada_overlay.c
>> +++ b/drivers/gpu/drm/armada/armada_overlay.c
>> @@ -7,6 +7,7 @@
>>   * published by the Free Software Foundation.
>>   */
>>  #include <drm/drmP.h>
>> +#include <drm/drm_atomic.h>
>>  #include "armada_crtc.h"
>>  #include "armada_drm.h"
>>  #include "armada_fb.h"
>> @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane,
>>  {
>>       struct armada_private *priv = plane->dev->dev_private;
>>       struct armada_plane *dplane = drm_to_armada_plane(plane);
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>>       bool update_attr = false;
>> +     int ret = 0;
>> +
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>>
>>       if (property == priv->colorkey_prop) {
>>  #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
>> @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane,
>>       } else if (property == priv->saturation_prop) {
>>               dplane->prop.saturation = val;
>>               update_attr = true;
>> +     } else {
>> +             ret = drm_plane_set_property(plane, pstate, property,
>> +                             val, blob_data);
>>       }
>>
>>       if (update_attr && dplane->base.crtc)
>>               armada_ovl_update_attr(&dplane->prop,
>>                                      drm_to_armada_crtc(dplane->base.crtc));
>>
>> -     return 0;
>> +     return ret;
>>  }
>>
>>  static const struct drm_plane_funcs armada_plane_funcs = {
>> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
>> index 45df5e5..403ffc5 100644
>> --- a/drivers/gpu/drm/drm_atomic.c
>> +++ b/drivers/gpu/drm/drm_atomic.c
>> @@ -24,6 +24,7 @@
>>
>>  #include <drm/drmP.h>
>>  #include <drm/drm_atomic.h>
>> +#include <drm/drm_plane_helper.h>
>>
>>  /**
>>   * drm_atomic_begin - start a sequence of atomic updates
>> @@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
>>               uint32_t flags)
>>  {
>>       struct drm_atomic_state *state;
>> +     int nplanes = dev->mode_config.num_total_plane;
>>       int sz;
>>       void *ptr;
>>
>>       sz = sizeof(*state);
>> +     sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
>>
>>       ptr = kzalloc(sz, GFP_KERNEL);
>>
>> @@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
>>       state->dev = dev;
>>       state->flags = flags;
>>
>> +     state->planes = ptr;
>> +     ptr = &state->planes[nplanes];
>> +
>> +     state->pstates = ptr;
>> +     ptr = &state->pstates[nplanes];
>> +
>>       return state;
>>  }
>>  EXPORT_SYMBOL(drm_atomic_begin);
>> @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event);
>>  int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
>>  {
>>       struct drm_atomic_state *a = state;
>> +     int nplanes = dev->mode_config.num_total_plane;
>> +     int i, ret = 0;
>> +
>> +     for (i = 0; i < nplanes; i++) {
>> +             if (a->planes[i]) {
>> +                     ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
>> +                     if (ret)
>> +                             break;
>> +             }
>> +     }
>> +
>>       a->acquire_ctx.frozen = true;
>> -     return 0;  /* for now */
>> +
>> +     return ret;
>>  }
>>  EXPORT_SYMBOL(drm_atomic_check);
>>
>> @@ -180,6 +201,18 @@ fail:
>>  static void commit_locks(struct drm_atomic_state *a,
>>               struct ww_acquire_ctx *ww_ctx)
>>  {
>> +     struct drm_device *dev = a->dev;
>> +     int nplanes = dev->mode_config.num_total_plane;
>> +     int i;
>> +
>> +     for (i = 0; i < nplanes; i++) {
>> +             struct drm_plane *plane = a->planes[i];
>> +             if (plane) {
>> +                     plane->state->state = NULL;
>> +                     drm_plane_destroy_state(plane, a->pstates[i]);
>> +             }
>> +     }
>> +
>>       /* and properly release them (clear in_atomic, remove from list): */
>>       drm_modeset_drop_locks(&a->acquire_ctx);
>>       ww_acquire_fini(ww_ctx);
>> @@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a,
>>  static int atomic_commit(struct drm_atomic_state *a,
>>               struct ww_acquire_ctx *ww_ctx)
>>  {
>> -     int ret = 0;
>> +     int nplanes = a->dev->mode_config.num_total_plane;
>> +     int i, ret = 0;
>> +
>> +     for (i = 0; i < nplanes; i++) {
>> +             struct drm_plane *plane = a->planes[i];
>> +             if (plane) {
>> +                     ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
>> +                     if (ret)
>> +                             break;
>> +             }
>> +     }
>>
>>       commit_locks(a, ww_ctx);
>>
>> @@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref)
>>  }
>>  EXPORT_SYMBOL(_drm_atomic_state_free);
>>
>> +int drm_atomic_plane_set_property(struct drm_plane *plane,
>> +             struct drm_atomic_state *state, struct drm_property *property,
>> +             uint64_t val, void *blob_data)
>> +{
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
>> +}
>> +EXPORT_SYMBOL(drm_atomic_plane_set_property);
>> +
>> +static void init_plane_state(struct drm_plane *plane,
>> +             struct drm_plane_state *pstate, struct drm_atomic_state *state)
>> +{
>> +     /* snapshot current state: */
>> +     *pstate = *plane->state;
>> +     pstate->state = state;
>> +     if (pstate->fb)
>> +             drm_framebuffer_reference(pstate->fb);
>> +}
>> +
>> +struct drm_plane_state *
>> +drm_atomic_get_plane_state(struct drm_plane *plane,
>> +             struct drm_atomic_state *state)
>> +{
>> +     struct drm_atomic_state *a = state;
>> +     struct drm_plane_state *pstate;
>> +     int ret;
>> +
>> +     pstate = a->pstates[plane->id];
>> +
>> +     if (!pstate) {
>> +             struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
>> +
>> +             /* grab lock of current crtc.. if crtc is NULL then grab all: */
>> +             if (plane->state->crtc)
>
> Looking here looks fishy - who's preventing someone else from touching
> plane->state while we don't yet hold any locks? Or do I miss something big
> here?

Yeah, probably should hoist that out into a local variable to avoid
problems on transition to null crtc.  I don't think it can happen at
the moment, but when we start to relax locking it could

>> +                     ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
>> +             else
>> +                     ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
>> +             if (ret)
>> +                     return ERR_PTR(ret);
>> +
>> +             pstate = drm_plane_create_state(plane);
>> +             if (!pstate)
>> +                     return ERR_PTR(-ENOMEM);
>> +             init_plane_state(plane, pstate, state);
>> +             a->planes[plane->id] = plane;
>> +             a->pstates[plane->id] = pstate;
>> +     }
>> +
>> +     return pstate;
>> +}
>> +EXPORT_SYMBOL(drm_atomic_get_plane_state);
>> +
>> +static void
>> +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a)
>> +{
>> +     struct drm_plane_state *pstate = a->pstates[plane->id];
>> +
>> +     /* clear transient state (only valid during atomic update): */
>> +     pstate->update_plane = false;
>> +     pstate->new_fb = false;
>> +
>> +     swap(plane->state, a->pstates[plane->id]);
>> +     plane->base.propvals = &plane->state->propvals;
>> +}
>> +
>> +/* For primary plane, if the driver implements ->page_flip(), then
>> + * we can use that.  But drivers can now choose not to bother with
>> + * implementing page_flip().
>> + */
>> +static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate)
>> +{
>> +     struct drm_crtc *crtc = pstate->crtc;
>> +     return (plane == crtc->primary) && crtc->funcs->page_flip &&
>> +                     !pstate->update_plane;
>> +}
>> +
>> +/* clear crtc/fb, ie. after disable_plane().  But takes care to keep
>> + * the property state in sync.  Once we get rid of plane->crtc/fb ptrs
>> + * and just use state, we can get rid of this fxn:
>> + */
>> +static void
>> +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate)
>> +{
>> +     struct drm_mode_config *config = &plane->dev->mode_config;
>> +     drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
>> +     drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
>> +     plane->crtc = NULL;
>> +     plane->fb = NULL;
>> +}
>> +
>> +static int
>> +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>> +{
>> +     struct drm_atomic_state *a = pstate->state;
>> +     struct drm_framebuffer *old_fb = plane->fb;
>> +     struct drm_framebuffer *fb = pstate->fb;
>> +     bool enabled = pstate->crtc && fb;
>> +     int ret = 0;
>> +
>> +     if (fb)
>> +             drm_framebuffer_reference(fb);
>> +
>> +     if (!enabled) {
>> +             if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
>> +                             (plane->funcs == &drm_primary_helper_funcs)) {
>> +                     /* primary plane helpers don't like ->disable_plane()..
>> +                      * so this hack for now until someone comes up with
>> +                      * something better:
>> +                      */
>
> Imo that's something we could check for in ->check and reject early.

well, we aren't actually treating it as a failure.  If we did want to
treat it as an error, then yes, we should fail it in ->check().  I'm
going to let someone who actually uses primary planes helpers decide
how they want it and change it if necessary.

>> +                     ret = 0;
>> +             } else {
>> +                     ret = plane->funcs->disable_plane(plane);
>> +                     reset_plane(plane, pstate);
>> +             }
>> +     } else {
>> +             struct drm_crtc *crtc = pstate->crtc;
>> +             if (pstate->update_plane ||
>> +                             (pstate->new_fb && !can_flip(plane, pstate))) {
>> +                     ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
>> +                                     pstate->crtc_x, pstate->crtc_y,
>> +                                     pstate->crtc_w, pstate->crtc_h,
>> +                                     pstate->src_x,  pstate->src_y,
>> +                                     pstate->src_w,  pstate->src_h);
>> +                     if (ret == 0) {
>> +                             /*
>> +                              * For page_flip(), the driver does this, but for
>> +                              * update_plane() it doesn't.. hurray \o/
>> +                              */
>
> Imo a patch to unify this first wouldn't hurt ...

I was kinda leaning more towards introducing a new API with unified
behaviour, and deprecating the old eventually.  Doing it all at once
and not having someone who is more expert in each different drivers,
seems like too big a chance to introduce problems.

I kinda want to more to an api that is more like atomic_commit() on a
per object basis (ie. {crtc,plane,etc}->atomic_commit()).  Then driver
can hook in to the commit process at device level
(dev->atomic_commit()) and/or per-object level as needed.

So yes, I want to unify.. but by building on top of atomic and
deprecating the old.

>
>> +                             plane->crtc = crtc;
>> +                             plane->fb = fb;
>> +                             fb = NULL;  /* don't unref */
>> +                     }
>> +
>> +             } else if (pstate->new_fb) {
>> +                     ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
>> +                     if (ret == 0) {
>> +                             /*
>> +                              * Warn if the driver hasn't properly updated the plane->fb
>> +                              * field to reflect that the new framebuffer is now used.
>> +                              * Failing to do so will screw with the reference counting
>> +                              * on framebuffers.
>> +                              */
>> +                             WARN_ON(plane->fb != fb);
>> +                             fb = NULL;  /* don't unref */
>> +                     }
>> +             } else {
>> +                     old_fb = NULL;
>> +                     ret = 0;
>> +             }
>> +     }
>
> This entire logic here kinda raises the question about transitioning
> drivers to the atomic interfaces. For modeset operations it might work
> fairly well since everything but i915 uses the crtc helpers and so can be
> converted fairly easily.
>
> But doing nuclear pageflips, even more so if we want completion events is
> an entire new deal. No idea how that should work really.

Currently we only have completion events on CRTCs, but we are going to
need no APIs internally for completion events on planes.  Which might
end up being the motivation for plane->atomic_commit() API.

At an rate, that should not hold up this patchset, or even adding
atomic ioctl itself (as long as we don't expose new events yet, which
is something we could leave out of the first version).


>> +
>> +     if (ret) {
>> +             /* Keep the old fb, don't unref it. */
>> +             old_fb = NULL;
>> +     } else {
>> +             /* on success, update state and fb refcnting: */
>> +             /* NOTE: if we ensure no driver sets plane->state->fb = NULL
>> +              * on disable, we can move this up a level and not duplicate
>> +              * nearly the same thing for both update_plane and disable_plane
>> +              * cases..  I leave it like this for now to be paranoid due to
>> +              * the slightly different ordering in the two cases in the
>> +              * original code.
>> +              */
>> +             swap_plane_state(plane, pstate->state);
>> +     }
>> +
>> +
>> +     if (fb)
>> +             drm_framebuffer_unreference(fb);
>> +     if (old_fb)
>> +             drm_framebuffer_unreference(old_fb);
>> +
>> +     return ret;
>> +}
>>
>>  const struct drm_atomic_funcs drm_atomic_funcs = {
>> +             .check_plane_state  = drm_plane_check_state,
>> +             .commit_plane_state = commit_plane_state,
>>  };
>>  EXPORT_SYMBOL(drm_atomic_funcs);
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index 48555724..b556a31 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>>        * in this manner.
>>        */
>>       if (atomic_read(&fb->refcount.refcount) > 1) {
>> +             void *state;
>> +
>> +             state = dev->driver->atomic_begin(dev, 0);
>> +             if (IS_ERR(state)) {
>> +                     DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
>> +                     return;
>> +             }
>> +
>> +             /* TODO once CRTC is converted to state/properties, we can push the
>> +              * locking down into drm_atomic_commit(), since that is where
>> +              * the actual changes take place..
>> +              */
>>               drm_modeset_lock_all(dev);
>>               /* remove from any CRTC */
>>               list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>> @@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>>
>>               list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
>>                       if (plane->fb == fb)
>> -                             drm_plane_force_disable(plane);
>> +                             drm_plane_force_disable(plane, state);
>>               }
>> +
>> +             /* just disabling stuff shouldn't fail, hopefully: */
>> +             if(dev->driver->atomic_check(dev, state))
>> +                     DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
>> +             else
>> +                     dev->driver->atomic_commit(dev, state);
>> +
>> +             dev->driver->atomic_end(dev, state);
>> +
>>               drm_modeset_unlock_all(dev);
>>       }
>>
>> @@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
>>                            const uint32_t *formats, uint32_t format_count,
>>                            enum drm_plane_type type)
>>  {
>> +     struct drm_mode_config *config = &dev->mode_config;
>>       int ret;
>>
>> +     /* this is now required: */
>> +     WARN_ON(!funcs->set_property);
>> +
>>       drm_modeset_lock_all(dev);
>>
>>       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
>>       if (ret)
>>               goto out;
>>
>> +     plane->funcs = funcs;
>> +     plane->state = drm_plane_create_state(plane);
>>       plane->base.properties = &plane->properties;
>> -     plane->base.propvals = &plane->propvals;
>> +     plane->base.propvals = &plane->state->propvals;
>>       plane->dev = dev;
>> -     plane->funcs = funcs;
>>       plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
>>                                     GFP_KERNEL);
>>       if (!plane->format_types) {
>> @@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
>>       plane->possible_crtcs = possible_crtcs;
>>       plane->type = type;
>>
>> -     list_add_tail(&plane->head, &dev->mode_config.plane_list);
>> -     dev->mode_config.num_total_plane++;
>> +     list_add_tail(&plane->head, &config->plane_list);
>> +     plane->id = config->num_total_plane;
>> +     config->num_total_plane++;
>>       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
>> -             dev->mode_config.num_overlay_plane++;
>> +             config->num_overlay_plane++;
>>
>>       drm_object_attach_property(&plane->base,
>> -                                dev->mode_config.plane_type_property,
>> +                                config->plane_type_property,
>>                                  plane->type);
>>
>> +     drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_x, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_y, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_w, 0);
>> +     drm_object_attach_property(&plane->base, config->prop_src_h, 0);
>> +
>>   out:
>>       drm_modeset_unlock_all(dev);
>>
>> @@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane)
>>       dev->mode_config.num_total_plane--;
>>       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
>>               dev->mode_config.num_overlay_plane--;
>> +     drm_plane_destroy_state(plane, plane->state);
>>       drm_modeset_unlock_all(dev);
>>  }
>>  EXPORT_SYMBOL(drm_plane_cleanup);
>>
>> +int drm_plane_check_state(struct drm_plane *plane,
>> +             struct drm_plane_state *state)
>> +{
>> +     unsigned int fb_width, fb_height;
>> +     struct drm_framebuffer *fb = state->fb;
>> +     int i;
>> +
>> +     /* disabling the plane is allowed: */
>> +     if (!fb)
>> +             return 0;
>> +
>> +     fb_width = fb->width << 16;
>> +     fb_height = fb->height << 16;
>> +
>> +     /* Check whether this plane supports the fb pixel format. */
>> +     for (i = 0; i < plane->format_count; i++)
>> +             if (fb->pixel_format == plane->format_types[i])
>> +                     break;
>> +     if (i == plane->format_count) {
>> +             DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Make sure source coordinates are inside the fb. */
>> +     if (state->src_w > fb_width ||
>> +                     state->src_x > fb_width - state->src_w ||
>> +                     state->src_h > fb_height ||
>> +                     state->src_y > fb_height - state->src_h) {
>> +             DRM_DEBUG_KMS("Invalid source coordinates "
>> +                           "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
>> +                           state->src_w >> 16,
>> +                           ((state->src_w & 0xffff) * 15625) >> 10,
>> +                           state->src_h >> 16,
>> +                           ((state->src_h & 0xffff) * 15625) >> 10,
>> +                           state->src_x >> 16,
>> +                           ((state->src_x & 0xffff) * 15625) >> 10,
>> +                           state->src_y >> 16,
>> +                           ((state->src_y & 0xffff) * 15625) >> 10);
>> +             return -ENOSPC;
>> +     }
>> +
>> +     /* Give drivers some help against integer overflows */
>> +     if (state->crtc_w > INT_MAX ||
>> +                     state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
>> +                     state->crtc_h > INT_MAX ||
>> +                     state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
>> +             DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
>> +                           state->crtc_w, state->crtc_h,
>> +                           state->crtc_x, state->crtc_y);
>> +             return -ERANGE;
>> +     }
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(drm_plane_check_state);
>> +
>> +void drm_plane_commit_state(struct drm_plane *plane,
>> +             struct drm_plane_state *state)
>> +{
>> +     plane->state = state;
>> +     plane->base.propvals = &state->propvals;
>> +}
>> +EXPORT_SYMBOL(drm_plane_commit_state);
>> +
>> +int drm_plane_set_property(struct drm_plane *plane,
>> +             struct drm_plane_state *state,
>> +             struct drm_property *property,
>> +             uint64_t value, void *blob_data)
>> +{
>> +     struct drm_device *dev = plane->dev;
>> +     struct drm_mode_config *config = &dev->mode_config;
>> +
>> +     drm_object_property_set_value(&plane->base,
>> +                     &state->propvals, property, value, blob_data);
>> +
>> +     if (property == config->prop_fb_id) {
>> +             struct drm_framebuffer *old_fb = state->fb;
>> +             /*
>> +              * NOTE: the ref to the fb could have been lost between
>> +              * drm_property_change_is_valid() and now.  The upshot
>> +              * is that drm_framebuffer_lookup() could return NULL
>> +              * and we'd disable the plane.
>> +              *
>> +              * We *could* return an error in that case.  But if (for
>> +              * example) _setcrtc() raced with _rmfb() and _rmfb()
>> +              * came after, it would disable what was enabled in the
>> +              * _setcrtc().  Which is the same end result that we get
>> +              * here, just skipping briefly setting the mode.
>> +              */
>> +             state->fb = drm_framebuffer_lookup(dev, value);
>> +             if (old_fb)
>> +                     drm_framebuffer_unreference(old_fb);
>> +             state->new_fb = true;
>> +     } else if (property == config->prop_crtc_id) {
>> +             struct drm_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) {
>> +                     struct drm_atomic_state *a = state->state;
>> +                     int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
>> +                     if (ret)
>> +                             return ret;
>> +             }
>> +             state->crtc = crtc;
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_crtc_x) {
>> +             state->crtc_x = U642I64(value);
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_crtc_y) {
>> +             state->crtc_y = U642I64(value);
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_crtc_w) {
>> +             state->crtc_w = value;
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_crtc_h) {
>> +             state->crtc_h = value;
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_src_x) {
>> +             state->src_x = value;
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_src_y) {
>> +             state->src_y = value;
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_src_w) {
>> +             state->src_w = value;
>> +             state->update_plane = true;
>> +     } else if (property == config->prop_src_h) {
>> +             state->src_h = value;
>> +             state->update_plane = true;
>> +     } else {
>> +             return -EINVAL;
>> +     }
>> +
>> +     return 0;
>> +}
>> +EXPORT_SYMBOL(drm_plane_set_property);
>> +
>>  /**
>>   * drm_plane_force_disable - Forcibly disable a plane
>>   * @plane: plane to disable
>> @@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
>>   * Used when the plane's current framebuffer is destroyed,
>>   * and when restoring fbdev mode.
>>   */
>> -void drm_plane_force_disable(struct drm_plane *plane)
>> +void drm_plane_force_disable(struct drm_plane *plane,
>> +             struct drm_atomic_state *state)
>>  {
>> -     struct drm_framebuffer *old_fb = plane->fb;
>> -     int ret;
>> +     struct drm_mode_config *config = &plane->dev->mode_config;
>>
>> -     if (!old_fb)
>> -             return;
>> -
>> -     ret = plane->funcs->disable_plane(plane);
>> -     if (ret) {
>> -             DRM_ERROR("failed to disable plane with busy fb\n");
>> -             return;
>> -     }
>> -     /* disconnect the plane from the fb and crtc: */
>> -     __drm_framebuffer_unreference(old_fb);
>> -     plane->fb = NULL;
>> -     plane->crtc = NULL;
>> +     /* should turn off the crtc */
>> +     drm_mode_plane_set_obj_prop(plane, state,
>> +             config->prop_crtc_id, 0, NULL);
>> +     drm_mode_plane_set_obj_prop(plane, state,
>> +             config->prop_fb_id, 0, NULL);
>>  }
>>  EXPORT_SYMBOL(drm_plane_force_disable);
>>
>>  static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
>>  {
>> -     struct drm_property *edid;
>> -     struct drm_property *dpms;
>> +     struct drm_property *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;
>>  }
>> @@ -2140,20 +2369,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;
>> +     struct drm_atomic_state *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",
>> @@ -2161,104 +2389,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;
>> -             ret = plane->funcs->disable_plane(plane);
>> -             if (!ret) {
>> -                     plane->crtc = NULL;
>> -                     plane->fb = NULL;
>> -             } else {
>> -                     old_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);
>> -     old_fb = plane->fb;
>> -     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) {
>> -             plane->crtc = crtc;
>> -             plane->fb = fb;
>> -             fb = NULL;
>> -     } else {
>> -             old_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;
>>  }
>>
>> @@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
>>       return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
>>  }
>>
>> -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>>                                          struct drm_atomic_state *state, struct drm_property *property,
>>                                          uint64_t value, void *blob_data)
>>  {
>> @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>>
>>       return ret;
>>  }
>> +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
>>
>> -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>>                                     struct drm_atomic_state *state, struct drm_property *property,
>>                                     uint64_t value, void *blob_data)
>>  {
>> @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>>
>>       return ret;
>>  }
>> +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
>>
>> -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>>                                     struct drm_atomic_state *state, struct drm_property *property,
>>                                     uint64_t value, void *blob_data)
>>  {
>> @@ -3882,12 +4045,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,
>>               struct drm_atomic_state *state, struct drm_property *property,
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
>> index 97b0d84..b73d3b0 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -286,13 +286,28 @@ 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)
>>               if (plane->type != DRM_PLANE_TYPE_PRIMARY)
>> -                     drm_plane_force_disable(plane);
>> +                     drm_plane_force_disable(plane, state);
>> +
>> +     /* just disabling stuff shouldn't fail, hopefully: */
>> +     if(dev->driver->atomic_check(dev, state))
>> +             DRM_ERROR("failed to restore fbdev mode\n");
>> +     else
>> +             dev->driver->atomic_commit(dev, state);
>> +
>> +     dev->driver->atomic_end(dev, state);
>>
>>       for (i = 0; i < fb_helper->crtc_count; i++) {
>>               struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
>> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
>> index d966afa..7a32383 100644
>> --- a/drivers/gpu/drm/drm_plane_helper.c
>> +++ b/drivers/gpu/drm/drm_plane_helper.c
>> @@ -26,6 +26,7 @@
>>  #include <linux/list.h>
>>  #include <drm/drmP.h>
>>  #include <drm/drm_rect.h>
>> +#include <drm/drm_atomic.h>
>>
>>  #define SUBPIXEL_MASK 0xffff
>>
>> @@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy);
>>  const struct drm_plane_funcs drm_primary_helper_funcs = {
>>       .update_plane = drm_primary_helper_update,
>>       .disable_plane = drm_primary_helper_disable,
>> +     .set_property = drm_atomic_plane_set_property,
>>       .destroy = drm_primary_helper_destroy,
>>  };
>>  EXPORT_SYMBOL(drm_primary_helper_funcs);
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> index 9da0935..8cf7442 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> @@ -10,6 +10,7 @@
>>   */
>>
>>  #include <drm/drmP.h>
>> +#include <drm/drm_atomic.h>
>>
>>  #include <drm/exynos_drm.h>
>>  #include "exynos_drm_drv.h"
>> @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>>       struct drm_device *dev = plane->dev;
>>       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>       struct exynos_drm_private *dev_priv = dev->dev_private;
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>> +
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>>
>>       if (property == dev_priv->plane_zpos_property) {
>>               exynos_plane->overlay.zpos = val;
>>               return 0;
>>       }
>>
>> -     return -EINVAL;
>> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>  }
>
> Imo the interfaces here are a bit wonky - most drivers have the exact same
> structure of allocating a plane state object if it's not there and setting
> the property. Imo we should push this down a bit and have type-specific
> set_prop interfaces which take the state-specific state object. Core
> properties would be fully handled in the core. Which means that driver
> don't need to implement set_prop callbacks if they don't have any special
> properties on top of the core stuff. Much less boilerplate that way.

Drivers which do not have any custom properties already just directly
use drm_{plane,crtc}_set_property().

Basically, a vanilla driver with no planes, no custom properties, and
no inter-crtc dependencies could plug in helpers everywhere a be
"fully atomic"(TM).

BR,
-R

> This would also give us a strong incentive to have common properties for
> e.g. blending since we could simply pimp the core with them, and leave
> drivers to just register the properties if they support them. If they have
> additional restrictions they only need to implement the ->check hook,
> which has the awesome advantage that they can deal with a real structure
> instead of abstract prop arrays.
>
> E.g. we could add a plane->supports_blending bool which would
> enabled/disable the default blending/Z-order properties for updating.
>
>>
>>  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 c235546..3f742f5 100644
>> --- a/drivers/gpu/drm/i915/intel_sprite.c
>> +++ b/drivers/gpu/drm/i915/intel_sprite.c
>> @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
>>       .update_plane = intel_update_plane,
>>       .disable_plane = intel_disable_plane,
>>       .destroy = intel_destroy_plane,
>> +     .set_property = drm_atomic_plane_set_property,
>>  };
>>
>>  static uint32_t ilk_plane_formats[] = {
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
>> index 8c064dc..4c92985 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
>> @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane,
>>               struct drm_atomic_state *state, struct drm_property *property,
>>               uint64_t val, void *blob_data)
>>  {
>> -     // XXX
>> -     return -EINVAL;
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>  }
>>
>>  static const struct drm_plane_funcs mdp4_plane_funcs = {
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
>> index 5cbf226..53cc8c6 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
>> @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane,
>>               struct drm_atomic_state *state, struct drm_property *property,
>>               uint64_t val, void *blob_data)
>>  {
>> -     // XXX
>> -     return -EINVAL;
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>  }
>>
>>  static const struct drm_plane_funcs mdp5_plane_funcs = {
>> diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
>> index 577e6aa..97b48b5 100644
>> --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
>> +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
>> @@ -24,6 +24,7 @@
>>   */
>>
>>  #include <drm/drmP.h>
>> +#include <drm/drm_atomic.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_fourcc.h>
>>
>> @@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane,
>>                 uint64_t value, void *blob_data)
>>  {
>>       struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>> +
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>>
>>       if (property == nv_plane->props.colorkey)
>>               nv_plane->colorkey = value;
>> @@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane,
>>       else if (property == nv_plane->props.iturbt_709)
>>               nv_plane->iturbt_709 = value;
>>       else
>> -             return -EINVAL;
>> +             return drm_plane_set_property(plane, pstate,
>> +                             property, value, blob_data);
>>
>>       if (nv_plane->set_params)
>>               nv_plane->set_params(nv_plane);
>> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
>> index 3dca538..da80bdc 100644
>> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
>> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
>> @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
>>
>>               for (i = 0; i < priv->num_planes; i++) {
>>                       drm_object_property_set_value(&priv->planes[i]->base,
>> -                                     &priv->planes[i]->propvals,
>> +                                     &priv->planes[i]->state->propvals,
>>                                       priv->rotation_prop, 0, NULL);
>>               }
>>       }
>> diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
>> index 2d3c975..a9acc58 100644
>> --- a/drivers/gpu/drm/omapdrm/omap_plane.c
>> +++ b/drivers/gpu/drm/omapdrm/omap_plane.c
>> @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane,
>>  {
>>       struct omap_plane *omap_plane = to_omap_plane(plane);
>>       struct omap_drm_private *priv = plane->dev->dev_private;
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>>       int ret = -EINVAL;
>>
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>> +
>>       if (property == priv->rotation_prop) {
>>               DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
>>               omap_plane->win.rotation = val;
>> @@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane,
>>               DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
>>               omap_plane->info.zorder = val;
>>               ret = apply(plane);
>> +     } else {
>> +             ret = drm_plane_set_property(plane, pstate, property,
>> +                             val, blob_data);
>>       }
>>
>>       return ret;
>> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
>> index 3a5d843..015c76a 100644
>> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
>> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
>> @@ -14,6 +14,7 @@
>>  #include <drm/drmP.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_atomic.h>
>>  #include <drm/drm_fb_cma_helper.h>
>>  #include <drm/drm_gem_cma_helper.h>
>>
>> @@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
>>  {
>>       struct rcar_du_plane *rplane = to_rcar_plane(plane);
>>       struct rcar_du_group *rgrp = rplane->group;
>> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>> +
>> +     if (IS_ERR(pstate))
>> +             return PTR_ERR(pstate);
>>
>>       if (property == rgrp->planes.alpha)
>>               rcar_du_plane_set_alpha(rplane, value);
>> @@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
>>       else if (property == rgrp->planes.zpos)
>>               rcar_du_plane_set_zpos(rplane, value);
>>       else
>> -             return -EINVAL;
>> +             return drm_plane_set_property(plane, pstate,
>> +                             property, value, blob_data);
>>
>>       return 0;
>>  }
>> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
>> index 060ae03..ccf03ea 100644
>> --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
>> +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
>> @@ -14,6 +14,7 @@
>>  #include <drm/drmP.h>
>>  #include <drm/drm_crtc.h>
>>  #include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_atomic.h>
>>  #include <drm/drm_fb_cma_helper.h>
>>  #include <drm/drm_gem_cma_helper.h>
>>
>> @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane)
>>  static const struct drm_plane_funcs shmob_drm_plane_funcs = {
>>       .update_plane = shmob_drm_plane_update,
>>       .disable_plane = shmob_drm_plane_disable,
>> +     .set_property = drm_atomic_plane_set_property,
>>       .destroy = shmob_drm_plane_destroy,
>>  };
>>
>> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
>> index ff72b81..78e93ec 100644
>> --- a/include/drm/drm_atomic.h
>> +++ b/include/drm/drm_atomic.h
>> @@ -68,7 +68,8 @@
>>   * struct drm_atomic_funcs - helper funcs used by the atomic helpers
>>   */
>>  struct drm_atomic_funcs {
>> -     int dummy; /* for now */
>> +     int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
>> +     int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
>>  };
>>
>>  const extern struct drm_atomic_funcs drm_atomic_funcs;
>> @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev,
>>               struct drm_atomic_state *state);
>>  void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
>>
>> +int drm_atomic_plane_set_property(struct drm_plane *plane,
>> +             struct drm_atomic_state *state, struct drm_property *property,
>> +             uint64_t val, void *blob_data);
>> +struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
>> +             struct drm_atomic_state *state);
>> +
>> +static inline int
>> +drm_atomic_check_plane_state(struct drm_plane *plane,
>> +             struct drm_plane_state *pstate)
>> +{
>> +     const struct drm_atomic_funcs *funcs =
>> +                     plane->dev->driver->atomic_funcs;
>> +     return funcs->check_plane_state(plane, pstate);
>> +}
>> +
>> +static inline int
>> +drm_atomic_commit_plane_state(struct drm_plane *plane,
>> +             struct drm_plane_state *pstate)
>> +{
>> +     const struct drm_atomic_funcs *funcs =
>> +                     plane->dev->driver->atomic_funcs;
>> +     return funcs->commit_plane_state(plane, pstate);
>> +}
>> +
>>  /**
>>   * struct drm_atomic_state - the state object used by atomic helpers
>>   */
>> @@ -91,6 +116,8 @@ struct drm_atomic_state {
>>       struct kref refcount;
>>       struct drm_device *dev;
>>       uint32_t flags;
>> +     struct drm_plane **planes;
>> +     struct drm_plane_state **pstates;
>>
>>       bool committed;
>>       bool checked;       /* just for debugging */
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index 547b75a..58309cc 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -569,7 +569,10 @@ 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_plane_state *(*create_state)(struct drm_plane *plane);
>> +     void (*destroy_state)(struct drm_plane *plane,
>> +                         struct drm_plane_state *pstate);
>> +     int (*set_property)(struct drm_plane *plane,
>>                           struct drm_atomic_state *state,
>>                           struct drm_property *property, uint64_t val,
>>                           void *blob_data);
>> @@ -582,6 +585,48 @@ enum drm_plane_type {
>>  };
>>
>>  /**
>> + * drm_plane_state - mutable plane state
>> + * @update_plane: if full update_plane() is needed (vs pageflip)
>> + * @new_fb: has the fb been changed
>> + * @crtc: currently bound CRTC
>> + * @fb: currently bound fb
>> + * @crtc_x: left position of visible portion of plane on crtc
>> + * @crtc_y: upper position of visible portion of plane on crtc
>> + * @crtc_w: width of visible portion of plane on crtc
>> + * @crtc_h: height of visible portion of plane on crtc
>> + * @src_x: left position of visible portion of plane within
>> + *   plane (in 16.16)
>> + * @src_y: upper position of visible portion of plane within
>> + *   plane (in 16.16)
>> + * @src_w: width of visible portion of plane (in 16.16)
>> + * @src_h: height of visible portion of plane (in 16.16)
>> + * @propvals: property values
>> + * @state: current global/toplevel state object (for atomic) while an
>> + *    update is in progress, NULL otherwise.
>> + */
>> +struct drm_plane_state {
>> +     bool update_plane      : 1;
>> +     bool new_fb            : 1;
>> +
>> +     struct drm_crtc *crtc;
>> +     struct drm_framebuffer *fb;
>> +
>> +     /* Signed dest location allows it to be partially off screen */
>> +     int32_t crtc_x, crtc_y;
>> +     uint32_t crtc_w, crtc_h;
>> +
>> +     /* Source values are 16.16 fixed point */
>> +     uint32_t src_x, src_y;
>> +     uint32_t src_h, src_w;
>> +
>> +     bool enabled;
>> +
>> +     struct drm_object_property_values propvals;
>> +
>> +     struct drm_atomic_state *state;
>> +};
>> +
>> +/**
>>   * drm_plane - central DRM plane control structure
>>   * @dev: DRM device this plane belongs to
>>   * @head: for list management
>> @@ -591,6 +636,8 @@ enum drm_plane_type {
>>   * @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
>>   * @type: type of plane (overlay, primary, cursor)
>> @@ -608,10 +655,17 @@ struct drm_plane {
>>       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;
>>
>>       enum drm_plane_type type;
>>  };
>> @@ -807,8 +861,20 @@ struct drm_mode_config {
>>       bool poll_running;
>>       struct delayed_work output_poll_work;
>>
>> -     /* pointers to standard properties */
>> +     /* just so blob properties can always be in a list: */
>>       struct list_head property_blob_list;
>> +
>> +     /* pointers to standard properties */
>> +     struct drm_property *prop_src_x;
>> +     struct drm_property *prop_src_y;
>> +     struct drm_property *prop_src_w;
>> +     struct drm_property *prop_src_h;
>> +     struct drm_property *prop_crtc_x;
>> +     struct drm_property *prop_crtc_y;
>> +     struct drm_property *prop_crtc_w;
>> +     struct drm_property *prop_crtc_h;
>> +     struct drm_property *prop_fb_id;
>> +     struct drm_property *prop_crtc_id;
>>       struct drm_property *edid_property;
>>       struct drm_property *dpms_property;
>>       struct drm_property *plane_type_property;
>> @@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev,
>>                         const uint32_t *formats, uint32_t format_count,
>>                         bool is_primary);
>>  extern void drm_plane_cleanup(struct drm_plane *plane);
>> -extern void drm_plane_force_disable(struct drm_plane *plane);
>> +extern void drm_plane_force_disable(struct drm_plane *plane,
>> +             struct drm_atomic_state *state);
>>  extern int drm_crtc_check_viewport(const struct drm_crtc *crtc,
>>                                  int x, int y,
>>                                  const struct drm_display_mode *mode,
>>                                  const struct drm_framebuffer *fb);
>> +extern int drm_plane_check_state(struct drm_plane *plane,
>> +             struct drm_plane_state *state);
>> +extern void drm_plane_commit_state(struct drm_plane *plane,
>> +             struct drm_plane_state *state);
>> +extern int drm_plane_set_property(struct drm_plane *plane,
>> +             struct drm_plane_state *state,
>> +             struct drm_property *property,
>> +             uint64_t value, void *blob_data);
>>
>>  extern void drm_encoder_cleanup(struct drm_encoder *encoder);
>>
>> @@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj,
>>  extern int drm_object_property_get_value(struct drm_mode_object *obj,
>>                                        struct drm_property *property,
>>                                        uint64_t *value);
>> +
>> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
>> +                                        struct drm_atomic_state *state, struct drm_property *property,
>> +                                        uint64_t value, void *blob_data);
>> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>> +                                   struct drm_atomic_state *state, struct drm_property *property,
>> +                                   uint64_t value, void *blob_data);
>> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>> +                                   struct drm_atomic_state *state, struct drm_property *property,
>> +                                   uint64_t value, void *blob_data);
>> +
>>  extern int drm_framebuffer_init(struct drm_device *dev,
>>                               struct drm_framebuffer *fb,
>>                               const struct drm_framebuffer_funcs *funcs);
>> @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
>>       return mo ? obj_to_blob(mo) : NULL;
>>  }
>>
>> +static inline struct drm_plane_state *
>> +drm_plane_create_state(struct drm_plane *plane)
>> +{
>> +     if (plane->funcs->create_state)
>> +             return plane->funcs->create_state(plane);
>> +     return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
>> +}
>> +
>> +static inline void
>> +drm_plane_destroy_state(struct drm_plane *plane,
>> +             struct drm_plane_state *pstate)
>> +{
>> +     if (pstate->fb)
>> +             drm_framebuffer_unreference(pstate->fb);
>> +     if (plane->funcs->destroy_state)
>> +             plane->funcs->destroy_state(plane, pstate);
>> +     else
>> +             kfree(pstate);
>> +}
>> +
>>  /* Plane list iterator for legacy (overlay only) planes. */
>>  #define drm_for_each_legacy_plane(plane, planelist) \
>>       list_for_each_entry(plane, planelist, head) \
>> --
>> 1.9.0
>>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26  9:31   ` Daniel Vetter
@ 2014-05-26 11:35     ` Rob Clark
  2014-05-26 14:56       ` Daniel Vetter
  2014-05-26 15:23     ` Ville Syrjälä
  1 sibling, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 11:35 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 5:31 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> +struct drm_crtc_state *
>> +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
>> +{
>> +     struct drm_crtc_state *cstate;
>> +     int ret;
>> +
>> +     cstate = a->cstates[crtc->id];
>> +
>> +     if (!cstate) {
>> +             ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
>> +             if (ret)
>> +                     return ERR_PTR(ret);
>> +
>> +             cstate = drm_crtc_create_state(crtc);
>> +             if (!cstate)
>> +                     return ERR_PTR(-ENOMEM);
>> +             init_crtc_state(crtc, cstate, a);
>> +             a->crtcs[crtc->id] = crtc;
>> +             a->cstates[crtc->id] = cstate;
>> +
>> +             /* we'll need it later, so make sure we have state
>> +              * for primary plane too:
>> +              */
>> +             drm_atomic_get_plane_state(crtc->primary, a);
>
> I haven't figured out why. With primary planes I don't really see a need
> for this. If we need it to implement the legacy setcrtc interface, then
> that should be done there, not here.


well, if you sort out how to disable primary helper plane, then yes,
you are right :-)

see commit_crtc_state()

BR,
-R

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26  8:23       ` Daniel Vetter
@ 2014-05-26 11:56         ` Rob Clark
  2014-05-26 14:35           ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 11:56 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
>> On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
>> >> @@ -8002,7 +8002,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);
>> >
>> >
>> > This is pretty much the reason why I think switching the
>> > mode_config.mutex to a ww_mutex is a bad idea: This call here nests
>> > within the mode_config.mutex and so must be acquired. Wiring the
>> > acquire context through everything is going to be fairly horrible,
>> > especially since you must be able to bail out when trying to lock with
>> > an axquire context.
>>
>> which is the call-path to here from mode_config.mutex?  Is it possible
>> to just move the locking to a higher level for a
>> drm_modeset_lock_all()?
>
> Connector probing. And the entire point of crtc locks was to _not_ block
> all screen updates while we poke for a new edid or do load balancing. If
> you want to test this you need a gen3/4 with tv-out (native, not through
> sdvo) or a gen2 or i915g/gm with vga.


hmm, I guess I'm still not quite seeing the issue.  For non-atomic
paths, we are grabbing mode_config and/or crtc mutex as bare mutexes
in same spots as we did before.  So if it worked before without
nested_lock stuff it should still work now.

BR,
-R

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

* Re: [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip
  2014-05-26 10:40 ` [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Daniel Vetter
@ 2014-05-26 12:48   ` Rob Clark
  2014-05-26 15:24     ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 12:48 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
>> One more time, with feeling..
>>
>> Previous revision of series:
>> http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
>>
>> And if you prefer, in git form:
>> http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion
>> git://people.freedesktop.org/~robclark/linux cold-fusion
>>
>> This series does not include the actual atomic ioctl, but it does
>> include all the needed infrastructure changes.
>>
>> Compared to previous revision, I've split out drm_modeset_acquire_ctx
>> from drm_atomic_state, so that we can ww acquire mode_config and crtc
>> locks outside of atomic transactions.  (Which keeps lockdep happy wrt.
>> drm_modeset_lock_all().)  I also got to the point in juggling things
>> around where I wanted to let the compiler type checking do it's job,
>> so 's/void *state/struct drm_atomic_state *state/g'.
>>
>> At this point, I've tested this on i915 (few different generation
>> laptops), radeon, and msm.  And of course w/ ww debug (deadlock in-
>> jection) enabled.  So I think all the locking related paths should
>> be covered.
>>
>> I believe Thierry has tested a slighly older revision on tegra.  I
>> would of course appreciate more testing on other drivers for which I
>> don't have the hw.  But I think it is pretty much ready to go.  I do
>> still owe some docs updates, I will send some patches for that in
>> the near future, but didn't want to hold up giving others a chance
>> to start banging on this.
>
> Ok, I've done a fairly cursory look at this only and tried to concentrate
> on grasping the high-level details. Patches contain a bunch of comments on
> the details, but here is the big stuff.
>
> First things first there's a bunch of prep work and refactoring sprinkled
> throughout the series. Imo we should pick those out and rebase them on top
> of drm-next asap to get them in before the actual interfaces. I hope I've
> catched them all and commented everywhere about what imo is still missing
> for merging. With that out of the way the real atomic review below.
>
> I like the overall design. It will be a royal pain to convert i915 over to
> this since thus far we've simply tucked away the staged state into
> obj->new_foo pointers. Which means we get to change the entire driver
> again ;-)
>
> But I think the interfaces to driver and overall api design need some good
> polish:
>
> - Imo way too much is done in driver callbacks, which then again call
>   helpers. Parsing of the core properties for plane/crtcs should be done
>   in the core imo. This way drivers only need to register ->set_prop
>   callbacks if there's a driver-specific property they support.

Pretty universally, the approach I took was to provide default fxns
(for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let
drivers wrap them as needed.  We might need to tweak
drm_atomic_begin() a bit for the first driver that needs to wrap
drm_atomic_state, but I guess we can tackle that as the need arises.

> - Imo those obj->set_prop callbacks should take the object-specific
>   drm_obj_state object, not the global atomic state structure. Will lead
>   to _much_ less lookup code duplicated all over the place. If we then
>   also add a state->obj pointer we could also ditch that parameter. Ofc
>   obj would be specific to the state at hand.

The problem is if those callbacks need to take other driver shared
locks.. that plus initially I was trying to keep state as opaque as
possible (in case sooner or later i915 decided to re-implement :-P)

Eventually I decided keeping state opaque was too much pita, and that
if driver wanted to do something different, it should instead
subclass.  So I guess these days they could chase [pc]state->state to
get back at the global state.

> - One downside of that is that drivers won't be able to interfere the
>   allocation step any more. I think we'd need an additional
>   ->alloc_atomic_state call so that drivers can still easily subclass some
>   objects with their own type.

the ->create_state() is already split out as a vfunc for planes and
crtcs.  Which I think should be enough to cover most of what drivers
need.  If driver needs to subclass global state too, then we need to
split up drm_atomic_begin(), but I guess that should be a relatively
small change that we can make when it becomes needed.

> - There's imo a bit a confusion between what's helper and what's driver
>   api. My big gripe here is with the set_prop stuff which imo really
>   should be core and not helpers. The default ->commit/check
>   implementations otoh should be more clearly delineated as helper
>   implementations that drivers can/should overwrite. I think we should
>   split drm_atomic.c into drm_atomic.c (with the official pieces) and
>   drm_atomic_helper.c (with the suggested standard/transitional
>   implemenations for commit/check). Helper functions should have the
>   drm_atomic_helper_ prefix.

Hmm, I think nearly *everything* could be overridden.. maybe it could
be more clear what to override vs outright replace.  But otoh I'm not
quite sure yet what drivers will need to override (other than some
obvious ones, which are already broken out into vfuncs, like
{plane,crtc}->create_state() to handle driver custom properties).

So I expect some tweaks as other drivers start adding native atomic
support.  I basically added what I needed for msm, and what I thought
was pretty safe bet that other drivers would need.  This at least
gives us something other drivers could start trying to use.  Then see
what is missing and add it as we go.  At least that was my line of
thinking.

> - I also think we should split the vfuncs like we do with the crtc
>   helpers. Imo the core interface should just be an ->alloc_state and
>   ->set_prop on all kms objects, plus a global ->prep/check/commit at the
>   driver level. The object-specific ->check/commit hooks otoh should be
>   part of the atomic helper library and in some separate
>   atomic_helper_funcs structure.

this is in fact the way it is, although 'struct drm_atomic_funcs'
maybe should have "helper" in the name.

>  Imo we could either extend the sturcture
>   used by the crtc helpers (hackish imo) or change the type of that to
>   make it clear it's for the crtc helpers and add a new pointer for atomic
>   helpers. E.g. in i915 I expect that we'll implement our very own
>   ->check/commit hooks using our own compute_config infrastructure. Maybe
>   we could switch to the ->check hooks of the atomic helper eventually,
>   but that will be a lot of work. And in any case we won't ever switch to
>   the ->commit hook as you have it in your helpers since for truly atomic
>   flips/modesets we need to interleave all the updates of all objects in
>   various phases.

not sure if it would work for you, but in msm I have a per-crtc "don't
actually commit yet" bit, which gets set for each crtc involved in
atomic update.  Although in my case it is mainly a matter of not
touching the "GO" bits until everything is setup..

If not, I guess it shouldn't be a big deal to, for example, split up
atomic_commit() into part that did the locking dance and then call
vfunc ->atomic_apply_state() or something like that.

Basically figure out where you need to hook in for i915 as you add
native atomic support.  By the time you and maybe one or two other
drivers have added atomic support we should have it pretty well nailed
down.  But until then, there might be some small adjustments
here/there.

> The patches are also rather big, which makes them a bit a pain to review.
> Matt's approach for the primary planes was much easier:
> 1) Add the new core interfaces for the new world.
> 2) Implement helpers for drivers to transition and convert drivers.
> 3) Implement ioctls using the new driver interfaces.

not so far different from what I did.  Although I suppose I could have
split up adding atomic support for plane/crtc vs converting over
existing ioctls to use it.  Not sure if it is worth doing at this
stage or not.  Not really sure that it would help in sorting out what
is helper vs not.

> That approach would also help a lot in figuring out what's helper code and
> so optional, and what's core stuff.
>
> The other big problem I see still is with the locking:
>
> - Imo switching mode_config.mutex to a ww_mutex won't work. I know that
>   the magic rules of w/w locking are _really_ tempting. But I don't think
>   it will actually solve anything and instead only cause havoc.

are you actually getting lockdep splats?  If so pls send them my way
and I'll have a look.

In non-atomic cases we are still using these as bare mutex's (other
than the one special case where we needed nested_lock before).  So I
think if it worked before, it should continue to work.

> - I think we need ww mutexes for crtcs, but they will be fairly useless
>   without ww mutexes on plane. Not for i915, but for all those drivers
>   which can switch planes around between different crtcs. Imo we should do
>   this as a first step (before getting all the atomic interfaces in), and
>   pimp the set_plane locking a bit already like I've described in some
>   mail.
>
> - There's a pile of state that's currently not really protected by
>   anything in your patches. One piece is all the obj->state properties.
>   Another one is state detected at runtime like e.g. whether a hdmi sink
>   supports audio or what kind of link bw parameters a dp sink supports.
>   Thus far this was all protected by mode_config.mutex, but we can't take
>   this one in the generic atomic ioctls for pretty obvious reasons.

we are still taking mode_config.mutex in places where we did before
(setcrtc, setplane, etc).  So it shouldn't be a problem *yet*.

Runtime internal params about hw state, as opposed to things userspace
is asking for, should perhaps not be in obj->state?  Or maybe I'm
misunderstanding you.

>   I think we need a new lock here, e.g. dev->mode_config.state_lock. It's
>   going to be painful, since we need to roll this out over _all_ driver
>   callbacks which can change the meaning of some property. E.g. all the
>   ->detect callbacks in i915. The important part is that no one is allowed
>   to do any costly operations why holding this lock (i.e. anything like
>   reading EDIDs or doing load-detect), it is only for updating/reading
>   this state.
>
> - To avoid nasties with locking inversion between the plain
>   mode_config.mutex, the state_mutex and the various ww mutexes we need to
>   rather completely rework the locking sequence for atomic updates
>   compared to what you have. Since in the driver's detect callbacks we
>   first grab the mode_config.mutex and then potentiall ww mutexes (only
>   i915) and maybe also the state_mutex (for updating autodetected
>   properties). But for atomic updates we first need to grab the
>   state_mutex (so that we can get at the current state) and then ww
>   mutexes and the mode_config.mutex. And for the later we're not even good
>   at predicting the order we want to acquire them.

so I think I need to understand the call sequence for the detect thing
in i915, since atm I'm not quite sure what the problem is, and why it
worked before..

>   Stage 1: Create the new state
>   We grab _just_ the mode_config.state_mutex and allocate all the state
>   objects. This will allow us to grab a copy of the current state of
>   everything without races or inconsistencies.
>
>   As a consequence ->set_prop hooks may not touch anything outside of the
>   date/state protected by the state_mutex.
>
>   Stage 2: check/commit stage
>   We drop the state_lock since it would nest the wrong way round again
>   with mode_config.mutex. We should have a complete copy of all state
>   already, and if something races against us (2nd ioctl or a output probe
>   call) we don't really care.
>
>   Only then will the check/commit hooks grab all the locks. For the
>   mode_config.mutex we could add a bit to the global state the we need it
>   (e.g. when the connector->crtc links change or for some other
>   driver-private need).
>
>   That only leaves races with a 2nd concurrent atomic modeset ioctl. Which
>   can be avoided by grabbing yet another lock, which we drop after the
>   driver has acquired all real locks. For that we could mandate the
>   ->check callback and require that it grabs all ww mutexes plus the
>   mode_config.mutex.
>
> For implementing this madness (if we agree it's the right approach) we can
> go step-by-step:
> - Convert crtc->mutex to a ww mutex.
> - Add plane ww mutexes, make set_plane locking more fine-grained.
> - Add the mode_config.state_lock, roll it out across all driver's ->detect
>   callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
> - Use all this correctly in the atomic stuff.
>
> Ok, that's the two big comments on this work from my side. Now reality
> check: How much off the mark am I?

well, I think in summary, it is (a) might need to split few things up
or re-arrange things slightly as i915 and other drivers start adding
native atomic support.. but this doesn't scare me too much, and I
think we can take it as we go.

And (b) possible locking fun.. this I think actually does need to be
sorted first.  Although I'm not quite sure I understand why it is
actually a problem.

BR,
-R

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

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 11:56         ` Rob Clark
@ 2014-05-26 14:35           ` Daniel Vetter
  2014-05-26 14:36             ` Daniel Vetter
  2014-05-26 15:04             ` Rob Clark
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 14:35 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 07:56:50AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
> >> On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> >> > On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
> >> >> @@ -8002,7 +8002,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);
> >> >
> >> >
> >> > This is pretty much the reason why I think switching the
> >> > mode_config.mutex to a ww_mutex is a bad idea: This call here nests
> >> > within the mode_config.mutex and so must be acquired. Wiring the
> >> > acquire context through everything is going to be fairly horrible,
> >> > especially since you must be able to bail out when trying to lock with
> >> > an axquire context.
> >>
> >> which is the call-path to here from mode_config.mutex?  Is it possible
> >> to just move the locking to a higher level for a
> >> drm_modeset_lock_all()?
> >
> > Connector probing. And the entire point of crtc locks was to _not_ block
> > all screen updates while we poke for a new edid or do load balancing. If
> > you want to test this you need a gen3/4 with tv-out (native, not through
> > sdvo) or a gen2 or i915g/gm with vga.
> 
> 
> hmm, I guess I'm still not quite seeing the issue.  For non-atomic
> paths, we are grabbing mode_config and/or crtc mutex as bare mutexes
> in same spots as we did before.  So if it worked before without
> nested_lock stuff it should still work now.

Thread A is doing output probing.

				Thread B is doing atomic modeset

Grabs mode_config.mutex

				Grabs crtc_A->ww_mutex

Tries to grab crtc_A->ww_mutex,
blocks since normal ww_mutex_lock

				Tries to grab mode_config.mutex with ww
				acuiquire context, blocks since current
				holder hasn't acquired the mutex with a ww
				ticket

-> Deadlock.

You really can't mix lock nesting with w/w and lock nesting with a static
hierarchy. It's all or nothing.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 14:35           ` Daniel Vetter
@ 2014-05-26 14:36             ` Daniel Vetter
  2014-05-26 15:04             ` Rob Clark
  1 sibling, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 14:36 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 04:35:04PM +0200, Daniel Vetter wrote:
> On Mon, May 26, 2014 at 07:56:50AM -0400, Rob Clark wrote:
> > On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > > On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
> > >> On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > >> > On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
> > >> >> @@ -8002,7 +8002,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);
> > >> >
> > >> >
> > >> > This is pretty much the reason why I think switching the
> > >> > mode_config.mutex to a ww_mutex is a bad idea: This call here nests
> > >> > within the mode_config.mutex and so must be acquired. Wiring the
> > >> > acquire context through everything is going to be fairly horrible,
> > >> > especially since you must be able to bail out when trying to lock with
> > >> > an axquire context.
> > >>
> > >> which is the call-path to here from mode_config.mutex?  Is it possible
> > >> to just move the locking to a higher level for a
> > >> drm_modeset_lock_all()?
> > >
> > > Connector probing. And the entire point of crtc locks was to _not_ block
> > > all screen updates while we poke for a new edid or do load balancing. If
> > > you want to test this you need a gen3/4 with tv-out (native, not through
> > > sdvo) or a gen2 or i915g/gm with vga.
> > 
> > 
> > hmm, I guess I'm still not quite seeing the issue.  For non-atomic
> > paths, we are grabbing mode_config and/or crtc mutex as bare mutexes
> > in same spots as we did before.  So if it worked before without
> > nested_lock stuff it should still work now.
> 
> Thread A is doing output probing.
> 
> 				Thread B is doing atomic modeset
> 
> Grabs mode_config.mutex
> 
> 				Grabs crtc_A->ww_mutex
> 
> Tries to grab crtc_A->ww_mutex,
> blocks since normal ww_mutex_lock
.. blocks since normal ww_mutex_lock without acquire ticket.

Otherwise it's a bit unclear.
-Daniel


> 
> 				Tries to grab mode_config.mutex with ww
> 				acuiquire context, blocks since current
> 				holder hasn't acquired the mutex with a ww
> 				ticket
> 
> -> Deadlock.
> 
> You really can't mix lock nesting with w/w and lock nesting with a static
> hierarchy. It's all or nothing.
> -Daniel
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

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

* Re: [PATCH 11/17] drm: convert plane to properties/state
  2014-05-26 11:32     ` Rob Clark
@ 2014-05-26 14:52       ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 14:52 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 07:32:13AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 5:12 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Sat, May 24, 2014 at 02:30:20PM -0400, Rob Clark wrote:
> >> Break the mutable state of a plane out into a separate structure
> >> and use atomic properties mechanism to set plane attributes.  This
> >> makes it easier to have some helpers for plane->set_property()
> >> and for checking for invalid params.  The idea is that individual
> >> drivers can wrap the state struct in their own struct which adds
> >> driver specific parameters, for easy build-up of state across
> >> multiple set_property() calls and for easy atomic commit or roll-
> >> back.
> >>
> >> The same should be done for CRTC, encoder, and connector, but this
> >> patch only includes the first part (plane).
> >>
> >> Signed-off-by: Rob Clark <robdclark@gmail.com>
> >
> > Imo s/plane->id/plane->index/ since plane_id can be too easily confused
> > with the mode object id used for the idr lookup. We already have
> > drm_crtc_index, so this is established convention already.
> 
> fair enough..  I suppose I could do the same in crtc.

I've forgotten to mention: We then should replace drm_crtc_index with
crtc->index since that helper is redundant.

> > A few comments below.
> >
> >> ---
> >>  drivers/gpu/drm/armada/armada_overlay.c    |  11 +-
> >>  drivers/gpu/drm/drm_atomic.c               | 225 ++++++++++++++-
> >>  drivers/gpu/drm/drm_crtc.c                 | 433 ++++++++++++++++++++---------
> >>  drivers/gpu/drm/drm_fb_helper.c            |  17 +-
> >>  drivers/gpu/drm/drm_plane_helper.c         |   2 +
> >>  drivers/gpu/drm/exynos/exynos_drm_plane.c  |   7 +-
> >>  drivers/gpu/drm/i915/intel_sprite.c        |   1 +
> >>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c  |   6 +-
> >>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c  |   6 +-
> >>  drivers/gpu/drm/nouveau/dispnv04/overlay.c |   8 +-
> >>  drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
> >>  drivers/gpu/drm/omapdrm/omap_plane.c       |   7 +
> >>  drivers/gpu/drm/rcar-du/rcar_du_plane.c    |   8 +-
> >>  drivers/gpu/drm/shmobile/shmob_drm_plane.c |   2 +
> >>  include/drm/drm_atomic.h                   |  29 +-
> >>  include/drm/drm_crtc.h                     | 114 +++++++-
> >>  16 files changed, 725 insertions(+), 153 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
> >> index 601ba9a..041ea89 100644
> >> --- a/drivers/gpu/drm/armada/armada_overlay.c
> >> +++ b/drivers/gpu/drm/armada/armada_overlay.c
> >> @@ -7,6 +7,7 @@
> >>   * published by the Free Software Foundation.
> >>   */
> >>  #include <drm/drmP.h>
> >> +#include <drm/drm_atomic.h>
> >>  #include "armada_crtc.h"
> >>  #include "armada_drm.h"
> >>  #include "armada_fb.h"
> >> @@ -288,7 +289,12 @@ static int armada_plane_set_property(struct drm_plane *plane,
> >>  {
> >>       struct armada_private *priv = plane->dev->dev_private;
> >>       struct armada_plane *dplane = drm_to_armada_plane(plane);
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >>       bool update_attr = false;
> >> +     int ret = 0;
> >> +
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >>
> >>       if (property == priv->colorkey_prop) {
> >>  #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
> >> @@ -342,13 +348,16 @@ static int armada_plane_set_property(struct drm_plane *plane,
> >>       } else if (property == priv->saturation_prop) {
> >>               dplane->prop.saturation = val;
> >>               update_attr = true;
> >> +     } else {
> >> +             ret = drm_plane_set_property(plane, pstate, property,
> >> +                             val, blob_data);
> >>       }
> >>
> >>       if (update_attr && dplane->base.crtc)
> >>               armada_ovl_update_attr(&dplane->prop,
> >>                                      drm_to_armada_crtc(dplane->base.crtc));
> >>
> >> -     return 0;
> >> +     return ret;
> >>  }
> >>
> >>  static const struct drm_plane_funcs armada_plane_funcs = {
> >> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> >> index 45df5e5..403ffc5 100644
> >> --- a/drivers/gpu/drm/drm_atomic.c
> >> +++ b/drivers/gpu/drm/drm_atomic.c
> >> @@ -24,6 +24,7 @@
> >>
> >>  #include <drm/drmP.h>
> >>  #include <drm/drm_atomic.h>
> >> +#include <drm/drm_plane_helper.h>
> >>
> >>  /**
> >>   * drm_atomic_begin - start a sequence of atomic updates
> >> @@ -46,10 +47,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
> >>               uint32_t flags)
> >>  {
> >>       struct drm_atomic_state *state;
> >> +     int nplanes = dev->mode_config.num_total_plane;
> >>       int sz;
> >>       void *ptr;
> >>
> >>       sz = sizeof(*state);
> >> +     sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
> >>
> >>       ptr = kzalloc(sz, GFP_KERNEL);
> >>
> >> @@ -65,6 +68,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
> >>       state->dev = dev;
> >>       state->flags = flags;
> >>
> >> +     state->planes = ptr;
> >> +     ptr = &state->planes[nplanes];
> >> +
> >> +     state->pstates = ptr;
> >> +     ptr = &state->pstates[nplanes];
> >> +
> >>       return state;
> >>  }
> >>  EXPORT_SYMBOL(drm_atomic_begin);
> >> @@ -101,8 +110,20 @@ EXPORT_SYMBOL(drm_atomic_set_event);
> >>  int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
> >>  {
> >>       struct drm_atomic_state *a = state;
> >> +     int nplanes = dev->mode_config.num_total_plane;
> >> +     int i, ret = 0;
> >> +
> >> +     for (i = 0; i < nplanes; i++) {
> >> +             if (a->planes[i]) {
> >> +                     ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]);
> >> +                     if (ret)
> >> +                             break;
> >> +             }
> >> +     }
> >> +
> >>       a->acquire_ctx.frozen = true;
> >> -     return 0;  /* for now */
> >> +
> >> +     return ret;
> >>  }
> >>  EXPORT_SYMBOL(drm_atomic_check);
> >>
> >> @@ -180,6 +201,18 @@ fail:
> >>  static void commit_locks(struct drm_atomic_state *a,
> >>               struct ww_acquire_ctx *ww_ctx)
> >>  {
> >> +     struct drm_device *dev = a->dev;
> >> +     int nplanes = dev->mode_config.num_total_plane;
> >> +     int i;
> >> +
> >> +     for (i = 0; i < nplanes; i++) {
> >> +             struct drm_plane *plane = a->planes[i];
> >> +             if (plane) {
> >> +                     plane->state->state = NULL;
> >> +                     drm_plane_destroy_state(plane, a->pstates[i]);
> >> +             }
> >> +     }
> >> +
> >>       /* and properly release them (clear in_atomic, remove from list): */
> >>       drm_modeset_drop_locks(&a->acquire_ctx);
> >>       ww_acquire_fini(ww_ctx);
> >> @@ -189,7 +222,17 @@ static void commit_locks(struct drm_atomic_state *a,
> >>  static int atomic_commit(struct drm_atomic_state *a,
> >>               struct ww_acquire_ctx *ww_ctx)
> >>  {
> >> -     int ret = 0;
> >> +     int nplanes = a->dev->mode_config.num_total_plane;
> >> +     int i, ret = 0;
> >> +
> >> +     for (i = 0; i < nplanes; i++) {
> >> +             struct drm_plane *plane = a->planes[i];
> >> +             if (plane) {
> >> +                     ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
> >> +                     if (ret)
> >> +                             break;
> >> +             }
> >> +     }
> >>
> >>       commit_locks(a, ww_ctx);
> >>
> >> @@ -264,7 +307,185 @@ void _drm_atomic_state_free(struct kref *kref)
> >>  }
> >>  EXPORT_SYMBOL(_drm_atomic_state_free);
> >>
> >> +int drm_atomic_plane_set_property(struct drm_plane *plane,
> >> +             struct drm_atomic_state *state, struct drm_property *property,
> >> +             uint64_t val, void *blob_data)
> >> +{
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
> >> +}
> >> +EXPORT_SYMBOL(drm_atomic_plane_set_property);
> >> +
> >> +static void init_plane_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *pstate, struct drm_atomic_state *state)
> >> +{
> >> +     /* snapshot current state: */
> >> +     *pstate = *plane->state;
> >> +     pstate->state = state;
> >> +     if (pstate->fb)
> >> +             drm_framebuffer_reference(pstate->fb);
> >> +}
> >> +
> >> +struct drm_plane_state *
> >> +drm_atomic_get_plane_state(struct drm_plane *plane,
> >> +             struct drm_atomic_state *state)
> >> +{
> >> +     struct drm_atomic_state *a = state;
> >> +     struct drm_plane_state *pstate;
> >> +     int ret;
> >> +
> >> +     pstate = a->pstates[plane->id];
> >> +
> >> +     if (!pstate) {
> >> +             struct drm_modeset_acquire_ctx *ctx = &a->acquire_ctx;
> >> +
> >> +             /* grab lock of current crtc.. if crtc is NULL then grab all: */
> >> +             if (plane->state->crtc)
> >
> > Looking here looks fishy - who's preventing someone else from touching
> > plane->state while we don't yet hold any locks? Or do I miss something big
> > here?
> 
> Yeah, probably should hoist that out into a local variable to avoid
> problems on transition to null crtc.  I don't think it can happen at
> the moment, but when we start to relax locking it could

So what if some manages to race a 2nd atomic modeset and flips out the
plane->state from underneath you're now chasing a freed structure?

Trying to be clever and attempting to do this without locks is imo just
not worth the (review) pain to keep it race-free.

> 
> >> +                     ret = drm_modeset_lock(&plane->state->crtc->mutex, ctx);
> >> +             else
> >> +                     ret = drm_modeset_lock_all_crtcs(plane->dev, ctx);
> >> +             if (ret)
> >> +                     return ERR_PTR(ret);
> >> +
> >> +             pstate = drm_plane_create_state(plane);
> >> +             if (!pstate)
> >> +                     return ERR_PTR(-ENOMEM);
> >> +             init_plane_state(plane, pstate, state);
> >> +             a->planes[plane->id] = plane;
> >> +             a->pstates[plane->id] = pstate;
> >> +     }
> >> +
> >> +     return pstate;
> >> +}
> >> +EXPORT_SYMBOL(drm_atomic_get_plane_state);
> >> +
> >> +static void
> >> +swap_plane_state(struct drm_plane *plane, struct drm_atomic_state *a)
> >> +{
> >> +     struct drm_plane_state *pstate = a->pstates[plane->id];
> >> +
> >> +     /* clear transient state (only valid during atomic update): */
> >> +     pstate->update_plane = false;
> >> +     pstate->new_fb = false;
> >> +
> >> +     swap(plane->state, a->pstates[plane->id]);
> >> +     plane->base.propvals = &plane->state->propvals;
> >> +}
> >> +
> >> +/* For primary plane, if the driver implements ->page_flip(), then
> >> + * we can use that.  But drivers can now choose not to bother with
> >> + * implementing page_flip().
> >> + */
> >> +static bool can_flip(struct drm_plane *plane, struct drm_plane_state *pstate)
> >> +{
> >> +     struct drm_crtc *crtc = pstate->crtc;
> >> +     return (plane == crtc->primary) && crtc->funcs->page_flip &&
> >> +                     !pstate->update_plane;
> >> +}
> >> +
> >> +/* clear crtc/fb, ie. after disable_plane().  But takes care to keep
> >> + * the property state in sync.  Once we get rid of plane->crtc/fb ptrs
> >> + * and just use state, we can get rid of this fxn:
> >> + */
> >> +static void
> >> +reset_plane(struct drm_plane *plane, struct drm_plane_state *pstate)
> >> +{
> >> +     struct drm_mode_config *config = &plane->dev->mode_config;
> >> +     drm_plane_set_property(plane, pstate, config->prop_fb_id, 0, NULL);
> >> +     drm_plane_set_property(plane, pstate, config->prop_crtc_id, 0, NULL);
> >> +     plane->crtc = NULL;
> >> +     plane->fb = NULL;
> >> +}
> >> +
> >> +static int
> >> +commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
> >> +{
> >> +     struct drm_atomic_state *a = pstate->state;
> >> +     struct drm_framebuffer *old_fb = plane->fb;
> >> +     struct drm_framebuffer *fb = pstate->fb;
> >> +     bool enabled = pstate->crtc && fb;
> >> +     int ret = 0;
> >> +
> >> +     if (fb)
> >> +             drm_framebuffer_reference(fb);
> >> +
> >> +     if (!enabled) {
> >> +             if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
> >> +                             (plane->funcs == &drm_primary_helper_funcs)) {
> >> +                     /* primary plane helpers don't like ->disable_plane()..
> >> +                      * so this hack for now until someone comes up with
> >> +                      * something better:
> >> +                      */
> >
> > Imo that's something we could check for in ->check and reject early.
> 
> well, we aren't actually treating it as a failure.  If we did want to
> treat it as an error, then yes, we should fail it in ->check().  I'm
> going to let someone who actually uses primary planes helpers decide
> how they want it and change it if necessary.

Atm all drivers use the primary plane helpers. And I guess for a long time
most will stick to them, so I think we should have an answer here.
Rejecting the modeset sounds like the right one, since after all arbitrary
driver restrictions is what we have the ->check hook for.

> >> +                     ret = 0;
> >> +             } else {
> >> +                     ret = plane->funcs->disable_plane(plane);
> >> +                     reset_plane(plane, pstate);
> >> +             }
> >> +     } else {
> >> +             struct drm_crtc *crtc = pstate->crtc;
> >> +             if (pstate->update_plane ||
> >> +                             (pstate->new_fb && !can_flip(plane, pstate))) {
> >> +                     ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
> >> +                                     pstate->crtc_x, pstate->crtc_y,
> >> +                                     pstate->crtc_w, pstate->crtc_h,
> >> +                                     pstate->src_x,  pstate->src_y,
> >> +                                     pstate->src_w,  pstate->src_h);
> >> +                     if (ret == 0) {
> >> +                             /*
> >> +                              * For page_flip(), the driver does this, but for
> >> +                              * update_plane() it doesn't.. hurray \o/
> >> +                              */
> >
> > Imo a patch to unify this first wouldn't hurt ...
> 
> I was kinda leaning more towards introducing a new API with unified
> behaviour, and deprecating the old eventually.  Doing it all at once
> and not having someone who is more expert in each different drivers,
> seems like too big a chance to introduce problems.
> 
> I kinda want to more to an api that is more like atomic_commit() on a
> per object basis (ie. {crtc,plane,etc}->atomic_commit()).  Then driver
> can hook in to the commit process at device level
> (dev->atomic_commit()) and/or per-object level as needed.
> 
> So yes, I want to unify.. but by building on top of atomic and
> deprecating the old.

I only meant to unify the refcounting and updating rules for obj->fb and
similar. Like I've done for update_plane and set_crtc as part of Matt's
primary plane work. Unifying all the flip interfaces is seriously out of
scope ;-)

Also I really don't think you can make atomic pageflips work in a generic
fashion with per-obj callbacks. After all the entire point is to have an
atomic, global update, and the means (or lack thereof) the hw provides to
make this happen will be different everywhere.

Atomic modesets (i.e. "do we have sufficient plls for this output config")
are a bit different and for those it might work. But that should clearly
be part of some helper library.

Yet another reason why imo it's really important to make a clear
disdinction between atomic modesets (which we can mostly do for everyone
using crtc helpers) and atomic/nuclear pageflips (for which we completely
lack the unified driver interface and for which a per-obj ->commit hook is
imo useless).

> >> +                             plane->crtc = crtc;
> >> +                             plane->fb = fb;
> >> +                             fb = NULL;  /* don't unref */
> >> +                     }
> >> +
> >> +             } else if (pstate->new_fb) {
> >> +                     ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
> >> +                     if (ret == 0) {
> >> +                             /*
> >> +                              * Warn if the driver hasn't properly updated the plane->fb
> >> +                              * field to reflect that the new framebuffer is now used.
> >> +                              * Failing to do so will screw with the reference counting
> >> +                              * on framebuffers.
> >> +                              */
> >> +                             WARN_ON(plane->fb != fb);
> >> +                             fb = NULL;  /* don't unref */
> >> +                     }
> >> +             } else {
> >> +                     old_fb = NULL;
> >> +                     ret = 0;
> >> +             }
> >> +     }
> >
> > This entire logic here kinda raises the question about transitioning
> > drivers to the atomic interfaces. For modeset operations it might work
> > fairly well since everything but i915 uses the crtc helpers and so can be
> > converted fairly easily.
> >
> > But doing nuclear pageflips, even more so if we want completion events is
> > an entire new deal. No idea how that should work really.
> 
> Currently we only have completion events on CRTCs, but we are going to
> need no APIs internally for completion events on planes.  Which might
> end up being the motivation for plane->atomic_commit() API.
> 
> At an rate, that should not hold up this patchset, or even adding
> atomic ioctl itself (as long as we don't expose new events yet, which
> is something we could leave out of the first version).

Hm, what's the use-case for per-plane completion events? Userspace can
always multiplex it when they want, and one single nuclear pageflip
certainly shouldn't result in different vblank events for different planes
on the same crtc. Otherwise it's just not atomic enough ;-)

Imo the pain we gain in making this generic (I've commented about this in
other places) doesn't justify the possible gain. Especially since I don't
see a real use case.

I think we should have a NUCLEAR_FLIP option in the atomic ioctl which
requests that:
- all updates happen on one vblank
- all updates happen asynchronously

and promises that the update only concerns one crtc. All drivers without
support for it (i.e. atm everything, hopefully soonish i915 will have
support for this) would then reject this.

Which means that for the start atomic updates would only work for
modesets, but I also think that's about as good as it gets without
driver-specific work anyway.

> >> +
> >> +     if (ret) {
> >> +             /* Keep the old fb, don't unref it. */
> >> +             old_fb = NULL;
> >> +     } else {
> >> +             /* on success, update state and fb refcnting: */
> >> +             /* NOTE: if we ensure no driver sets plane->state->fb = NULL
> >> +              * on disable, we can move this up a level and not duplicate
> >> +              * nearly the same thing for both update_plane and disable_plane
> >> +              * cases..  I leave it like this for now to be paranoid due to
> >> +              * the slightly different ordering in the two cases in the
> >> +              * original code.
> >> +              */
> >> +             swap_plane_state(plane, pstate->state);
> >> +     }
> >> +
> >> +
> >> +     if (fb)
> >> +             drm_framebuffer_unreference(fb);
> >> +     if (old_fb)
> >> +             drm_framebuffer_unreference(old_fb);
> >> +
> >> +     return ret;
> >> +}
> >>
> >>  const struct drm_atomic_funcs drm_atomic_funcs = {
> >> +             .check_plane_state  = drm_plane_check_state,
> >> +             .commit_plane_state = commit_plane_state,
> >>  };
> >>  EXPORT_SYMBOL(drm_atomic_funcs);
> >> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> >> index 48555724..b556a31 100644
> >> --- a/drivers/gpu/drm/drm_crtc.c
> >> +++ b/drivers/gpu/drm/drm_crtc.c
> >> @@ -712,6 +712,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
> >>        * in this manner.
> >>        */
> >>       if (atomic_read(&fb->refcount.refcount) > 1) {
> >> +             void *state;
> >> +
> >> +             state = dev->driver->atomic_begin(dev, 0);
> >> +             if (IS_ERR(state)) {
> >> +                     DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
> >> +                     return;
> >> +             }
> >> +
> >> +             /* TODO once CRTC is converted to state/properties, we can push the
> >> +              * locking down into drm_atomic_commit(), since that is where
> >> +              * the actual changes take place..
> >> +              */
> >>               drm_modeset_lock_all(dev);
> >>               /* remove from any CRTC */
> >>               list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> >> @@ -728,8 +740,17 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
> >>
> >>               list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
> >>                       if (plane->fb == fb)
> >> -                             drm_plane_force_disable(plane);
> >> +                             drm_plane_force_disable(plane, state);
> >>               }
> >> +
> >> +             /* just disabling stuff shouldn't fail, hopefully: */
> >> +             if(dev->driver->atomic_check(dev, state))
> >> +                     DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n");
> >> +             else
> >> +                     dev->driver->atomic_commit(dev, state);
> >> +
> >> +             dev->driver->atomic_end(dev, state);
> >> +
> >>               drm_modeset_unlock_all(dev);
> >>       }
> >>
> >> @@ -1090,18 +1111,23 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
> >>                            const uint32_t *formats, uint32_t format_count,
> >>                            enum drm_plane_type type)
> >>  {
> >> +     struct drm_mode_config *config = &dev->mode_config;
> >>       int ret;
> >>
> >> +     /* this is now required: */
> >> +     WARN_ON(!funcs->set_property);
> >> +
> >>       drm_modeset_lock_all(dev);
> >>
> >>       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
> >>       if (ret)
> >>               goto out;
> >>
> >> +     plane->funcs = funcs;
> >> +     plane->state = drm_plane_create_state(plane);
> >>       plane->base.properties = &plane->properties;
> >> -     plane->base.propvals = &plane->propvals;
> >> +     plane->base.propvals = &plane->state->propvals;
> >>       plane->dev = dev;
> >> -     plane->funcs = funcs;
> >>       plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
> >>                                     GFP_KERNEL);
> >>       if (!plane->format_types) {
> >> @@ -1116,15 +1142,27 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
> >>       plane->possible_crtcs = possible_crtcs;
> >>       plane->type = type;
> >>
> >> -     list_add_tail(&plane->head, &dev->mode_config.plane_list);
> >> -     dev->mode_config.num_total_plane++;
> >> +     list_add_tail(&plane->head, &config->plane_list);
> >> +     plane->id = config->num_total_plane;
> >> +     config->num_total_plane++;
> >>       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
> >> -             dev->mode_config.num_overlay_plane++;
> >> +             config->num_overlay_plane++;
> >>
> >>       drm_object_attach_property(&plane->base,
> >> -                                dev->mode_config.plane_type_property,
> >> +                                config->plane_type_property,
> >>                                  plane->type);
> >>
> >> +     drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_src_x, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_src_y, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_src_w, 0);
> >> +     drm_object_attach_property(&plane->base, config->prop_src_h, 0);
> >> +
> >>   out:
> >>       drm_modeset_unlock_all(dev);
> >>
> >> @@ -1185,10 +1223,151 @@ void drm_plane_cleanup(struct drm_plane *plane)
> >>       dev->mode_config.num_total_plane--;
> >>       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
> >>               dev->mode_config.num_overlay_plane--;
> >> +     drm_plane_destroy_state(plane, plane->state);
> >>       drm_modeset_unlock_all(dev);
> >>  }
> >>  EXPORT_SYMBOL(drm_plane_cleanup);
> >>
> >> +int drm_plane_check_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *state)
> >> +{
> >> +     unsigned int fb_width, fb_height;
> >> +     struct drm_framebuffer *fb = state->fb;
> >> +     int i;
> >> +
> >> +     /* disabling the plane is allowed: */
> >> +     if (!fb)
> >> +             return 0;
> >> +
> >> +     fb_width = fb->width << 16;
> >> +     fb_height = fb->height << 16;
> >> +
> >> +     /* Check whether this plane supports the fb pixel format. */
> >> +     for (i = 0; i < plane->format_count; i++)
> >> +             if (fb->pixel_format == plane->format_types[i])
> >> +                     break;
> >> +     if (i == plane->format_count) {
> >> +             DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     /* Make sure source coordinates are inside the fb. */
> >> +     if (state->src_w > fb_width ||
> >> +                     state->src_x > fb_width - state->src_w ||
> >> +                     state->src_h > fb_height ||
> >> +                     state->src_y > fb_height - state->src_h) {
> >> +             DRM_DEBUG_KMS("Invalid source coordinates "
> >> +                           "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
> >> +                           state->src_w >> 16,
> >> +                           ((state->src_w & 0xffff) * 15625) >> 10,
> >> +                           state->src_h >> 16,
> >> +                           ((state->src_h & 0xffff) * 15625) >> 10,
> >> +                           state->src_x >> 16,
> >> +                           ((state->src_x & 0xffff) * 15625) >> 10,
> >> +                           state->src_y >> 16,
> >> +                           ((state->src_y & 0xffff) * 15625) >> 10);
> >> +             return -ENOSPC;
> >> +     }
> >> +
> >> +     /* Give drivers some help against integer overflows */
> >> +     if (state->crtc_w > INT_MAX ||
> >> +                     state->crtc_x > INT_MAX - (int32_t) state->crtc_w ||
> >> +                     state->crtc_h > INT_MAX ||
> >> +                     state->crtc_y > INT_MAX - (int32_t) state->crtc_h) {
> >> +             DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
> >> +                           state->crtc_w, state->crtc_h,
> >> +                           state->crtc_x, state->crtc_y);
> >> +             return -ERANGE;
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +EXPORT_SYMBOL(drm_plane_check_state);
> >> +
> >> +void drm_plane_commit_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *state)
> >> +{
> >> +     plane->state = state;
> >> +     plane->base.propvals = &state->propvals;
> >> +}
> >> +EXPORT_SYMBOL(drm_plane_commit_state);
> >> +
> >> +int drm_plane_set_property(struct drm_plane *plane,
> >> +             struct drm_plane_state *state,
> >> +             struct drm_property *property,
> >> +             uint64_t value, void *blob_data)
> >> +{
> >> +     struct drm_device *dev = plane->dev;
> >> +     struct drm_mode_config *config = &dev->mode_config;
> >> +
> >> +     drm_object_property_set_value(&plane->base,
> >> +                     &state->propvals, property, value, blob_data);
> >> +
> >> +     if (property == config->prop_fb_id) {
> >> +             struct drm_framebuffer *old_fb = state->fb;
> >> +             /*
> >> +              * NOTE: the ref to the fb could have been lost between
> >> +              * drm_property_change_is_valid() and now.  The upshot
> >> +              * is that drm_framebuffer_lookup() could return NULL
> >> +              * and we'd disable the plane.
> >> +              *
> >> +              * We *could* return an error in that case.  But if (for
> >> +              * example) _setcrtc() raced with _rmfb() and _rmfb()
> >> +              * came after, it would disable what was enabled in the
> >> +              * _setcrtc().  Which is the same end result that we get
> >> +              * here, just skipping briefly setting the mode.
> >> +              */
> >> +             state->fb = drm_framebuffer_lookup(dev, value);
> >> +             if (old_fb)
> >> +                     drm_framebuffer_unreference(old_fb);
> >> +             state->new_fb = true;
> >> +     } else if (property == config->prop_crtc_id) {
> >> +             struct drm_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) {
> >> +                     struct drm_atomic_state *a = state->state;
> >> +                     int ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
> >> +                     if (ret)
> >> +                             return ret;
> >> +             }
> >> +             state->crtc = crtc;
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_crtc_x) {
> >> +             state->crtc_x = U642I64(value);
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_crtc_y) {
> >> +             state->crtc_y = U642I64(value);
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_crtc_w) {
> >> +             state->crtc_w = value;
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_crtc_h) {
> >> +             state->crtc_h = value;
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_src_x) {
> >> +             state->src_x = value;
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_src_y) {
> >> +             state->src_y = value;
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_src_w) {
> >> +             state->src_w = value;
> >> +             state->update_plane = true;
> >> +     } else if (property == config->prop_src_h) {
> >> +             state->src_h = value;
> >> +             state->update_plane = true;
> >> +     } else {
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +EXPORT_SYMBOL(drm_plane_set_property);
> >> +
> >>  /**
> >>   * drm_plane_force_disable - Forcibly disable a plane
> >>   * @plane: plane to disable
> >> @@ -1198,43 +1377,93 @@ EXPORT_SYMBOL(drm_plane_cleanup);
> >>   * Used when the plane's current framebuffer is destroyed,
> >>   * and when restoring fbdev mode.
> >>   */
> >> -void drm_plane_force_disable(struct drm_plane *plane)
> >> +void drm_plane_force_disable(struct drm_plane *plane,
> >> +             struct drm_atomic_state *state)
> >>  {
> >> -     struct drm_framebuffer *old_fb = plane->fb;
> >> -     int ret;
> >> +     struct drm_mode_config *config = &plane->dev->mode_config;
> >>
> >> -     if (!old_fb)
> >> -             return;
> >> -
> >> -     ret = plane->funcs->disable_plane(plane);
> >> -     if (ret) {
> >> -             DRM_ERROR("failed to disable plane with busy fb\n");
> >> -             return;
> >> -     }
> >> -     /* disconnect the plane from the fb and crtc: */
> >> -     __drm_framebuffer_unreference(old_fb);
> >> -     plane->fb = NULL;
> >> -     plane->crtc = NULL;
> >> +     /* should turn off the crtc */
> >> +     drm_mode_plane_set_obj_prop(plane, state,
> >> +             config->prop_crtc_id, 0, NULL);
> >> +     drm_mode_plane_set_obj_prop(plane, state,
> >> +             config->prop_fb_id, 0, NULL);
> >>  }
> >>  EXPORT_SYMBOL(drm_plane_force_disable);
> >>
> >>  static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
> >>  {
> >> -     struct drm_property *edid;
> >> -     struct drm_property *dpms;
> >> +     struct drm_property *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;
> >>  }
> >> @@ -2140,20 +2369,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;
> >> +     struct drm_atomic_state *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",
> >> @@ -2161,104 +2389,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;
> >> -             ret = plane->funcs->disable_plane(plane);
> >> -             if (!ret) {
> >> -                     plane->crtc = NULL;
> >> -                     plane->fb = NULL;
> >> -             } else {
> >> -                     old_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);
> >> -     old_fb = plane->fb;
> >> -     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) {
> >> -             plane->crtc = crtc;
> >> -             plane->fb = fb;
> >> -             fb = NULL;
> >> -     } else {
> >> -             old_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;
> >>  }
> >>
> >> @@ -3833,7 +3994,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
> >>       return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
> >>  }
> >>
> >> -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> >> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> >>                                          struct drm_atomic_state *state, struct drm_property *property,
> >>                                          uint64_t value, void *blob_data)
> >>  {
> >> @@ -3856,8 +4017,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> >>
> >>       return ret;
> >>  }
> >> +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop);
> >>
> >> -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> >> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> >>                                     struct drm_atomic_state *state, struct drm_property *property,
> >>                                     uint64_t value, void *blob_data)
> >>  {
> >> @@ -3872,8 +4034,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> >>
> >>       return ret;
> >>  }
> >> +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop);
> >>
> >> -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> >> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> >>                                     struct drm_atomic_state *state, struct drm_property *property,
> >>                                     uint64_t value, void *blob_data)
> >>  {
> >> @@ -3882,12 +4045,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,
> >>               struct drm_atomic_state *state, struct drm_property *property,
> >> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> >> index 97b0d84..b73d3b0 100644
> >> --- a/drivers/gpu/drm/drm_fb_helper.c
> >> +++ b/drivers/gpu/drm/drm_fb_helper.c
> >> @@ -286,13 +286,28 @@ 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)
> >>               if (plane->type != DRM_PLANE_TYPE_PRIMARY)
> >> -                     drm_plane_force_disable(plane);
> >> +                     drm_plane_force_disable(plane, state);
> >> +
> >> +     /* just disabling stuff shouldn't fail, hopefully: */
> >> +     if(dev->driver->atomic_check(dev, state))
> >> +             DRM_ERROR("failed to restore fbdev mode\n");
> >> +     else
> >> +             dev->driver->atomic_commit(dev, state);
> >> +
> >> +     dev->driver->atomic_end(dev, state);
> >>
> >>       for (i = 0; i < fb_helper->crtc_count; i++) {
> >>               struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
> >> diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
> >> index d966afa..7a32383 100644
> >> --- a/drivers/gpu/drm/drm_plane_helper.c
> >> +++ b/drivers/gpu/drm/drm_plane_helper.c
> >> @@ -26,6 +26,7 @@
> >>  #include <linux/list.h>
> >>  #include <drm/drmP.h>
> >>  #include <drm/drm_rect.h>
> >> +#include <drm/drm_atomic.h>
> >>
> >>  #define SUBPIXEL_MASK 0xffff
> >>
> >> @@ -234,6 +235,7 @@ EXPORT_SYMBOL(drm_primary_helper_destroy);
> >>  const struct drm_plane_funcs drm_primary_helper_funcs = {
> >>       .update_plane = drm_primary_helper_update,
> >>       .disable_plane = drm_primary_helper_disable,
> >> +     .set_property = drm_atomic_plane_set_property,
> >>       .destroy = drm_primary_helper_destroy,
> >>  };
> >>  EXPORT_SYMBOL(drm_primary_helper_funcs);
> >> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> >> index 9da0935..8cf7442 100644
> >> --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
> >> +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> >> @@ -10,6 +10,7 @@
> >>   */
> >>
> >>  #include <drm/drmP.h>
> >> +#include <drm/drm_atomic.h>
> >>
> >>  #include <drm/exynos_drm.h>
> >>  #include "exynos_drm_drv.h"
> >> @@ -220,13 +221,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
> >>       struct drm_device *dev = plane->dev;
> >>       struct exynos_plane *exynos_plane = to_exynos_plane(plane);
> >>       struct exynos_drm_private *dev_priv = dev->dev_private;
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >> +
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >>
> >>       if (property == dev_priv->plane_zpos_property) {
> >>               exynos_plane->overlay.zpos = val;
> >>               return 0;
> >>       }
> >>
> >> -     return -EINVAL;
> >> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
> >>  }
> >
> > Imo the interfaces here are a bit wonky - most drivers have the exact same
> > structure of allocating a plane state object if it's not there and setting
> > the property. Imo we should push this down a bit and have type-specific
> > set_prop interfaces which take the state-specific state object. Core
> > properties would be fully handled in the core. Which means that driver
> > don't need to implement set_prop callbacks if they don't have any special
> > properties on top of the core stuff. Much less boilerplate that way.
> 
> Drivers which do not have any custom properties already just directly
> use drm_{plane,crtc}_set_property().
> 
> Basically, a vanilla driver with no planes, no custom properties, and
> no inter-crtc dependencies could plug in helpers everywhere a be
> "fully atomic"(TM).

My critique is two-fold:
- It looks like the core property parsing is helper code which can be
  overriden. We very much don't want that, since we want to expose a
  common set of kms operations that work everywhere.

- Since the callbacks are not object specific there's a bit too much
  casting going on for my taste.

- For drivers that do have driver props we could move the allocation error
  handling into the core. Which is imo a Very Good Thing(tm).

Cheers, Daniel
> 
> BR,
> -R
> 
> > This would also give us a strong incentive to have common properties for
> > e.g. blending since we could simply pimp the core with them, and leave
> > drivers to just register the properties if they support them. If they have
> > additional restrictions they only need to implement the ->check hook,
> > which has the awesome advantage that they can deal with a real structure
> > instead of abstract prop arrays.
> >
> > E.g. we could add a plane->supports_blending bool which would
> > enabled/disable the default blending/Z-order properties for updating.
> >
> >>
> >>  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 c235546..3f742f5 100644
> >> --- a/drivers/gpu/drm/i915/intel_sprite.c
> >> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> >> @@ -1190,6 +1190,7 @@ static const struct drm_plane_funcs intel_plane_funcs = {
> >>       .update_plane = intel_update_plane,
> >>       .disable_plane = intel_disable_plane,
> >>       .destroy = intel_destroy_plane,
> >> +     .set_property = drm_atomic_plane_set_property,
> >>  };
> >>
> >>  static uint32_t ilk_plane_formats[] = {
> >> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> >> index 8c064dc..4c92985 100644
> >> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> >> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> >> @@ -88,8 +88,10 @@ int mdp4_plane_set_property(struct drm_plane *plane,
> >>               struct drm_atomic_state *state, struct drm_property *property,
> >>               uint64_t val, void *blob_data)
> >>  {
> >> -     // XXX
> >> -     return -EINVAL;
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
> >>  }
> >>
> >>  static const struct drm_plane_funcs mdp4_plane_funcs = {
> >> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> >> index 5cbf226..53cc8c6 100644
> >> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> >> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> >> @@ -103,8 +103,10 @@ int mdp5_plane_set_property(struct drm_plane *plane,
> >>               struct drm_atomic_state *state, struct drm_property *property,
> >>               uint64_t val, void *blob_data)
> >>  {
> >> -     // XXX
> >> -     return -EINVAL;
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >> +     return drm_plane_set_property(plane, pstate, property, val, blob_data);
> >>  }
> >>
> >>  static const struct drm_plane_funcs mdp5_plane_funcs = {
> >> diff --git a/drivers/gpu/drm/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> >> index 577e6aa..97b48b5 100644
> >> --- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> >> +++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
> >> @@ -24,6 +24,7 @@
> >>   */
> >>
> >>  #include <drm/drmP.h>
> >> +#include <drm/drm_atomic.h>
> >>  #include <drm/drm_crtc.h>
> >>  #include <drm/drm_fourcc.h>
> >>
> >> @@ -226,6 +227,10 @@ nv_set_property(struct drm_plane *plane,
> >>                 uint64_t value, void *blob_data)
> >>  {
> >>       struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >> +
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >>
> >>       if (property == nv_plane->props.colorkey)
> >>               nv_plane->colorkey = value;
> >> @@ -240,7 +245,8 @@ nv_set_property(struct drm_plane *plane,
> >>       else if (property == nv_plane->props.iturbt_709)
> >>               nv_plane->iturbt_709 = value;
> >>       else
> >> -             return -EINVAL;
> >> +             return drm_plane_set_property(plane, pstate,
> >> +                             property, value, blob_data);
> >>
> >>       if (nv_plane->set_params)
> >>               nv_plane->set_params(nv_plane);
> >> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> >> index 3dca538..da80bdc 100644
> >> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> >> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> >> @@ -585,7 +585,7 @@ static void dev_lastclose(struct drm_device *dev)
> >>
> >>               for (i = 0; i < priv->num_planes; i++) {
> >>                       drm_object_property_set_value(&priv->planes[i]->base,
> >> -                                     &priv->planes[i]->propvals,
> >> +                                     &priv->planes[i]->state->propvals,
> >>                                       priv->rotation_prop, 0, NULL);
> >>               }
> >>       }
> >> diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
> >> index 2d3c975..a9acc58 100644
> >> --- a/drivers/gpu/drm/omapdrm/omap_plane.c
> >> +++ b/drivers/gpu/drm/omapdrm/omap_plane.c
> >> @@ -341,8 +341,12 @@ int omap_plane_set_property(struct drm_plane *plane,
> >>  {
> >>       struct omap_plane *omap_plane = to_omap_plane(plane);
> >>       struct omap_drm_private *priv = plane->dev->dev_private;
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >>       int ret = -EINVAL;
> >>
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >> +
> >>       if (property == priv->rotation_prop) {
> >>               DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
> >>               omap_plane->win.rotation = val;
> >> @@ -351,6 +355,9 @@ int omap_plane_set_property(struct drm_plane *plane,
> >>               DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
> >>               omap_plane->info.zorder = val;
> >>               ret = apply(plane);
> >> +     } else {
> >> +             ret = drm_plane_set_property(plane, pstate, property,
> >> +                             val, blob_data);
> >>       }
> >>
> >>       return ret;
> >> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> >> index 3a5d843..015c76a 100644
> >> --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> >> +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c
> >> @@ -14,6 +14,7 @@
> >>  #include <drm/drmP.h>
> >>  #include <drm/drm_crtc.h>
> >>  #include <drm/drm_crtc_helper.h>
> >> +#include <drm/drm_atomic.h>
> >>  #include <drm/drm_fb_cma_helper.h>
> >>  #include <drm/drm_gem_cma_helper.h>
> >>
> >> @@ -404,6 +405,10 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
> >>  {
> >>       struct rcar_du_plane *rplane = to_rcar_plane(plane);
> >>       struct rcar_du_group *rgrp = rplane->group;
> >> +     struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
> >> +
> >> +     if (IS_ERR(pstate))
> >> +             return PTR_ERR(pstate);
> >>
> >>       if (property == rgrp->planes.alpha)
> >>               rcar_du_plane_set_alpha(rplane, value);
> >> @@ -412,7 +417,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane,
> >>       else if (property == rgrp->planes.zpos)
> >>               rcar_du_plane_set_zpos(rplane, value);
> >>       else
> >> -             return -EINVAL;
> >> +             return drm_plane_set_property(plane, pstate,
> >> +                             property, value, blob_data);
> >>
> >>       return 0;
> >>  }
> >> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> >> index 060ae03..ccf03ea 100644
> >> --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> >> +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c
> >> @@ -14,6 +14,7 @@
> >>  #include <drm/drmP.h>
> >>  #include <drm/drm_crtc.h>
> >>  #include <drm/drm_crtc_helper.h>
> >> +#include <drm/drm_atomic.h>
> >>  #include <drm/drm_fb_cma_helper.h>
> >>  #include <drm/drm_gem_cma_helper.h>
> >>
> >> @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane)
> >>  static const struct drm_plane_funcs shmob_drm_plane_funcs = {
> >>       .update_plane = shmob_drm_plane_update,
> >>       .disable_plane = shmob_drm_plane_disable,
> >> +     .set_property = drm_atomic_plane_set_property,
> >>       .destroy = shmob_drm_plane_destroy,
> >>  };
> >>
> >> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> >> index ff72b81..78e93ec 100644
> >> --- a/include/drm/drm_atomic.h
> >> +++ b/include/drm/drm_atomic.h
> >> @@ -68,7 +68,8 @@
> >>   * struct drm_atomic_funcs - helper funcs used by the atomic helpers
> >>   */
> >>  struct drm_atomic_funcs {
> >> -     int dummy; /* for now */
> >> +     int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
> >> +     int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
> >>  };
> >>
> >>  const extern struct drm_atomic_funcs drm_atomic_funcs;
> >> @@ -84,6 +85,30 @@ int drm_atomic_commit_unlocked(struct drm_device *dev,
> >>               struct drm_atomic_state *state);
> >>  void drm_atomic_end(struct drm_device *dev, struct drm_atomic_state *state);
> >>
> >> +int drm_atomic_plane_set_property(struct drm_plane *plane,
> >> +             struct drm_atomic_state *state, struct drm_property *property,
> >> +             uint64_t val, void *blob_data);
> >> +struct drm_plane_state *drm_atomic_get_plane_state(struct drm_plane *plane,
> >> +             struct drm_atomic_state *state);
> >> +
> >> +static inline int
> >> +drm_atomic_check_plane_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *pstate)
> >> +{
> >> +     const struct drm_atomic_funcs *funcs =
> >> +                     plane->dev->driver->atomic_funcs;
> >> +     return funcs->check_plane_state(plane, pstate);
> >> +}
> >> +
> >> +static inline int
> >> +drm_atomic_commit_plane_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *pstate)
> >> +{
> >> +     const struct drm_atomic_funcs *funcs =
> >> +                     plane->dev->driver->atomic_funcs;
> >> +     return funcs->commit_plane_state(plane, pstate);
> >> +}
> >> +
> >>  /**
> >>   * struct drm_atomic_state - the state object used by atomic helpers
> >>   */
> >> @@ -91,6 +116,8 @@ struct drm_atomic_state {
> >>       struct kref refcount;
> >>       struct drm_device *dev;
> >>       uint32_t flags;
> >> +     struct drm_plane **planes;
> >> +     struct drm_plane_state **pstates;
> >>
> >>       bool committed;
> >>       bool checked;       /* just for debugging */
> >> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> >> index 547b75a..58309cc 100644
> >> --- a/include/drm/drm_crtc.h
> >> +++ b/include/drm/drm_crtc.h
> >> @@ -569,7 +569,10 @@ 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_plane_state *(*create_state)(struct drm_plane *plane);
> >> +     void (*destroy_state)(struct drm_plane *plane,
> >> +                         struct drm_plane_state *pstate);
> >> +     int (*set_property)(struct drm_plane *plane,
> >>                           struct drm_atomic_state *state,
> >>                           struct drm_property *property, uint64_t val,
> >>                           void *blob_data);
> >> @@ -582,6 +585,48 @@ enum drm_plane_type {
> >>  };
> >>
> >>  /**
> >> + * drm_plane_state - mutable plane state
> >> + * @update_plane: if full update_plane() is needed (vs pageflip)
> >> + * @new_fb: has the fb been changed
> >> + * @crtc: currently bound CRTC
> >> + * @fb: currently bound fb
> >> + * @crtc_x: left position of visible portion of plane on crtc
> >> + * @crtc_y: upper position of visible portion of plane on crtc
> >> + * @crtc_w: width of visible portion of plane on crtc
> >> + * @crtc_h: height of visible portion of plane on crtc
> >> + * @src_x: left position of visible portion of plane within
> >> + *   plane (in 16.16)
> >> + * @src_y: upper position of visible portion of plane within
> >> + *   plane (in 16.16)
> >> + * @src_w: width of visible portion of plane (in 16.16)
> >> + * @src_h: height of visible portion of plane (in 16.16)
> >> + * @propvals: property values
> >> + * @state: current global/toplevel state object (for atomic) while an
> >> + *    update is in progress, NULL otherwise.
> >> + */
> >> +struct drm_plane_state {
> >> +     bool update_plane      : 1;
> >> +     bool new_fb            : 1;
> >> +
> >> +     struct drm_crtc *crtc;
> >> +     struct drm_framebuffer *fb;
> >> +
> >> +     /* Signed dest location allows it to be partially off screen */
> >> +     int32_t crtc_x, crtc_y;
> >> +     uint32_t crtc_w, crtc_h;
> >> +
> >> +     /* Source values are 16.16 fixed point */
> >> +     uint32_t src_x, src_y;
> >> +     uint32_t src_h, src_w;
> >> +
> >> +     bool enabled;
> >> +
> >> +     struct drm_object_property_values propvals;
> >> +
> >> +     struct drm_atomic_state *state;
> >> +};
> >> +
> >> +/**
> >>   * drm_plane - central DRM plane control structure
> >>   * @dev: DRM device this plane belongs to
> >>   * @head: for list management
> >> @@ -591,6 +636,8 @@ enum drm_plane_type {
> >>   * @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
> >>   * @type: type of plane (overlay, primary, cursor)
> >> @@ -608,10 +655,17 @@ struct drm_plane {
> >>       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;
> >>
> >>       enum drm_plane_type type;
> >>  };
> >> @@ -807,8 +861,20 @@ struct drm_mode_config {
> >>       bool poll_running;
> >>       struct delayed_work output_poll_work;
> >>
> >> -     /* pointers to standard properties */
> >> +     /* just so blob properties can always be in a list: */
> >>       struct list_head property_blob_list;
> >> +
> >> +     /* pointers to standard properties */
> >> +     struct drm_property *prop_src_x;
> >> +     struct drm_property *prop_src_y;
> >> +     struct drm_property *prop_src_w;
> >> +     struct drm_property *prop_src_h;
> >> +     struct drm_property *prop_crtc_x;
> >> +     struct drm_property *prop_crtc_y;
> >> +     struct drm_property *prop_crtc_w;
> >> +     struct drm_property *prop_crtc_h;
> >> +     struct drm_property *prop_fb_id;
> >> +     struct drm_property *prop_crtc_id;
> >>       struct drm_property *edid_property;
> >>       struct drm_property *dpms_property;
> >>       struct drm_property *plane_type_property;
> >> @@ -930,11 +996,20 @@ extern int drm_plane_init(struct drm_device *dev,
> >>                         const uint32_t *formats, uint32_t format_count,
> >>                         bool is_primary);
> >>  extern void drm_plane_cleanup(struct drm_plane *plane);
> >> -extern void drm_plane_force_disable(struct drm_plane *plane);
> >> +extern void drm_plane_force_disable(struct drm_plane *plane,
> >> +             struct drm_atomic_state *state);
> >>  extern int drm_crtc_check_viewport(const struct drm_crtc *crtc,
> >>                                  int x, int y,
> >>                                  const struct drm_display_mode *mode,
> >>                                  const struct drm_framebuffer *fb);
> >> +extern int drm_plane_check_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *state);
> >> +extern void drm_plane_commit_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *state);
> >> +extern int drm_plane_set_property(struct drm_plane *plane,
> >> +             struct drm_plane_state *state,
> >> +             struct drm_property *property,
> >> +             uint64_t value, void *blob_data);
> >>
> >>  extern void drm_encoder_cleanup(struct drm_encoder *encoder);
> >>
> >> @@ -984,6 +1059,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj,
> >>  extern int drm_object_property_get_value(struct drm_mode_object *obj,
> >>                                        struct drm_property *property,
> >>                                        uint64_t *value);
> >> +
> >> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector,
> >> +                                        struct drm_atomic_state *state, struct drm_property *property,
> >> +                                        uint64_t value, void *blob_data);
> >> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
> >> +                                   struct drm_atomic_state *state, struct drm_property *property,
> >> +                                   uint64_t value, void *blob_data);
> >> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
> >> +                                   struct drm_atomic_state *state, struct drm_property *property,
> >> +                                   uint64_t value, void *blob_data);
> >> +
> >>  extern int drm_framebuffer_init(struct drm_device *dev,
> >>                               struct drm_framebuffer *fb,
> >>                               const struct drm_framebuffer_funcs *funcs);
> >> @@ -1165,6 +1251,26 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
> >>       return mo ? obj_to_blob(mo) : NULL;
> >>  }
> >>
> >> +static inline struct drm_plane_state *
> >> +drm_plane_create_state(struct drm_plane *plane)
> >> +{
> >> +     if (plane->funcs->create_state)
> >> +             return plane->funcs->create_state(plane);
> >> +     return kzalloc(sizeof(struct drm_plane_state), GFP_KERNEL);
> >> +}
> >> +
> >> +static inline void
> >> +drm_plane_destroy_state(struct drm_plane *plane,
> >> +             struct drm_plane_state *pstate)
> >> +{
> >> +     if (pstate->fb)
> >> +             drm_framebuffer_unreference(pstate->fb);
> >> +     if (plane->funcs->destroy_state)
> >> +             plane->funcs->destroy_state(plane, pstate);
> >> +     else
> >> +             kfree(pstate);
> >> +}
> >> +
> >>  /* Plane list iterator for legacy (overlay only) planes. */
> >>  #define drm_for_each_legacy_plane(plane, planelist) \
> >>       list_for_each_entry(plane, planelist, head) \
> >> --
> >> 1.9.0
> >>
> >
> > --
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26 11:35     ` Rob Clark
@ 2014-05-26 14:56       ` Daniel Vetter
  2014-05-26 15:15         ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 14:56 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 07:35:45AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 5:31 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> >> +struct drm_crtc_state *
> >> +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
> >> +{
> >> +     struct drm_crtc_state *cstate;
> >> +     int ret;
> >> +
> >> +     cstate = a->cstates[crtc->id];
> >> +
> >> +     if (!cstate) {
> >> +             ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
> >> +             if (ret)
> >> +                     return ERR_PTR(ret);
> >> +
> >> +             cstate = drm_crtc_create_state(crtc);
> >> +             if (!cstate)
> >> +                     return ERR_PTR(-ENOMEM);
> >> +             init_crtc_state(crtc, cstate, a);
> >> +             a->crtcs[crtc->id] = crtc;
> >> +             a->cstates[crtc->id] = cstate;
> >> +
> >> +             /* we'll need it later, so make sure we have state
> >> +              * for primary plane too:
> >> +              */
> >> +             drm_atomic_get_plane_state(crtc->primary, a);
> >
> > I haven't figured out why. With primary planes I don't really see a need
> > for this. If we need it to implement the legacy setcrtc interface, then
> > that should be done there, not here.
> 
> 
> well, if you sort out how to disable primary helper plane, then yes,
> you are right :-)
> 
> see commit_crtc_state()

Imo bail when we have a crtc with NULL primary plane in crtc_commit (and
wont disable the entire crtc ofc).

Also I think your current commit_crtc should be pushed down into the crtc
helpers - it's not going to do any good for i915. But I guess until we
have a real user of the atomic interface (i.e. updates more than 1 crtc)
like the fb helper code we don't need this yet.

But as soon as we update more than one crtc we _must_ push it down into
the crtc helpers or the i915 machinery - if you try to enable crtcs which
need resources from crtcs which aren't yet off this is simply going to
fail.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 14:35           ` Daniel Vetter
  2014-05-26 14:36             ` Daniel Vetter
@ 2014-05-26 15:04             ` Rob Clark
  2014-05-26 15:07               ` Daniel Vetter
  1 sibling, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 15:04 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Mon, May 26, 2014 at 10:35 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, May 26, 2014 at 07:56:50AM -0400, Rob Clark wrote:
>> On Mon, May 26, 2014 at 4:23 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Sun, May 25, 2014 at 07:16:43PM -0400, Rob Clark wrote:
>> >> On Sun, May 25, 2014 at 6:10 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> >> > On Sat, May 24, 2014 at 8:30 PM, Rob Clark <robdclark@gmail.com> wrote:
>> >> >> @@ -8002,7 +8002,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);
>> >> >
>> >> >
>> >> > This is pretty much the reason why I think switching the
>> >> > mode_config.mutex to a ww_mutex is a bad idea: This call here nests
>> >> > within the mode_config.mutex and so must be acquired. Wiring the
>> >> > acquire context through everything is going to be fairly horrible,
>> >> > especially since you must be able to bail out when trying to lock with
>> >> > an axquire context.
>> >>
>> >> which is the call-path to here from mode_config.mutex?  Is it possible
>> >> to just move the locking to a higher level for a
>> >> drm_modeset_lock_all()?
>> >
>> > Connector probing. And the entire point of crtc locks was to _not_ block
>> > all screen updates while we poke for a new edid or do load balancing. If
>> > you want to test this you need a gen3/4 with tv-out (native, not through
>> > sdvo) or a gen2 or i915g/gm with vga.
>>
>>
>> hmm, I guess I'm still not quite seeing the issue.  For non-atomic
>> paths, we are grabbing mode_config and/or crtc mutex as bare mutexes
>> in same spots as we did before.  So if it worked before without
>> nested_lock stuff it should still work now.
>
> Thread A is doing output probing.
>
>                                 Thread B is doing atomic modeset
>
> Grabs mode_config.mutex
>
>                                 Grabs crtc_A->ww_mutex
>
> Tries to grab crtc_A->ww_mutex,
> blocks since normal ww_mutex_lock
>
>                                 Tries to grab mode_config.mutex with ww
>                                 acuiquire context, blocks since current
>                                 holder hasn't acquired the mutex with a ww
>                                 ticket


hmm, ok, I had thought this case, thread B would get -EDEADLK because
lock was held, and not his acquire ctx.  If that is not the case, then
I would propose this:

All places doing things the old way, must grab mode_config.mutex first
currently.  And we use mode_config.mutex to protect
mode_config.acquire_ctx.  So all the lower spots grabbing individual
crtc mutexes can safely use mode_config.acquire_ctx.

Then the only headache is propagating -EDEADLK up the call stack.  If
we are lucky, the all already propagate -EINTR, etc.

BR,
-R

>
> -> Deadlock.
>
> You really can't mix lock nesting with w/w and lock nesting with a static
> hierarchy. It's all or nothing.
> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 15:04             ` Rob Clark
@ 2014-05-26 15:07               ` Daniel Vetter
  2014-05-26 15:20                 ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 15:07 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
> hmm, ok, I had thought this case, thread B would get -EDEADLK because
> lock was held, and not his acquire ctx.  If that is not the case, then
> I would propose this:
>
> All places doing things the old way, must grab mode_config.mutex first
> currently.  And we use mode_config.mutex to protect
> mode_config.acquire_ctx.  So all the lower spots grabbing individual
> crtc mutexes can safely use mode_config.acquire_ctx.
>
> Then the only headache is propagating -EDEADLK up the call stack.  If
> we are lucky, the all already propagate -EINTR, etc.

The output poll work most definitely doesn't propagate -EINTR. Like
I've said, this will be painful. And imo doing this also makes the kms
locking into quite a mess overall.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26 14:56       ` Daniel Vetter
@ 2014-05-26 15:15         ` Rob Clark
  0 siblings, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-26 15:15 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 10:56 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, May 26, 2014 at 07:35:45AM -0400, Rob Clark wrote:
>> On Mon, May 26, 2014 at 5:31 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> >> +struct drm_crtc_state *
>> >> +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
>> >> +{
>> >> +     struct drm_crtc_state *cstate;
>> >> +     int ret;
>> >> +
>> >> +     cstate = a->cstates[crtc->id];
>> >> +
>> >> +     if (!cstate) {
>> >> +             ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
>> >> +             if (ret)
>> >> +                     return ERR_PTR(ret);
>> >> +
>> >> +             cstate = drm_crtc_create_state(crtc);
>> >> +             if (!cstate)
>> >> +                     return ERR_PTR(-ENOMEM);
>> >> +             init_crtc_state(crtc, cstate, a);
>> >> +             a->crtcs[crtc->id] = crtc;
>> >> +             a->cstates[crtc->id] = cstate;
>> >> +
>> >> +             /* we'll need it later, so make sure we have state
>> >> +              * for primary plane too:
>> >> +              */
>> >> +             drm_atomic_get_plane_state(crtc->primary, a);
>> >
>> > I haven't figured out why. With primary planes I don't really see a need
>> > for this. If we need it to implement the legacy setcrtc interface, then
>> > that should be done there, not here.
>>
>>
>> well, if you sort out how to disable primary helper plane, then yes,
>> you are right :-)
>>
>> see commit_crtc_state()
>
> Imo bail when we have a crtc with NULL primary plane in crtc_commit (and
> wont disable the entire crtc ofc).
>
> Also I think your current commit_crtc should be pushed down into the crtc
> helpers - it's not going to do any good for i915. But I guess until we
> have a real user of the atomic interface (i.e. updates more than 1 crtc)
> like the fb helper code we don't need this yet.

it is in helpers (if we rename drm_atomic_funcs ->
drm_atomic_helper_funcs, which is basically what it is).

I suspect what you actually want is to split up atomic_commit() so you
can do the loop over all the planes and crtcs internally.

BR,
-R

> But as soon as we update more than one crtc we _must_ push it down into
> the crtc helpers or the i915 machinery - if you try to enable crtcs which
> need resources from crtcs which aren't yet off this is simply going to
> fail.
> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 15:07               ` Daniel Vetter
@ 2014-05-26 15:20                 ` Rob Clark
  2014-05-26 15:35                   ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 15:20 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, May 26, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>> hmm, ok, I had thought this case, thread B would get -EDEADLK because
>> lock was held, and not his acquire ctx.  If that is not the case, then
>> I would propose this:
>>
>> All places doing things the old way, must grab mode_config.mutex first
>> currently.  And we use mode_config.mutex to protect
>> mode_config.acquire_ctx.  So all the lower spots grabbing individual
>> crtc mutexes can safely use mode_config.acquire_ctx.
>>
>> Then the only headache is propagating -EDEADLK up the call stack.  If
>> we are lucky, the all already propagate -EINTR, etc.
>
> The output poll work most definitely doesn't propagate -EINTR. Like
> I've said, this will be painful. And imo doing this also makes the kms
> locking into quite a mess overall.

Well, we could hold mode_config.mutex as a traditional mutex around
atomic operations.  What you loose out would be now _NONBLOCK
operations could conceivable call into driver paths without
mode_config.mutex held.  This was the advantage of converting
mode_config.mutex as well.  Granted, it is slightly theoretical
because until we expose atomic ioctl it would only apply to page_flip
(which was not holding mode_config.mutex).  And we also want to get
rid of mode_config.mutex in these paths too.

Otoh, if we want to make locking more fine grained, more use of
ww_mutex seems like the best way.  And if that means adding a return
value to a fxn here/there and propagating errors properly, maybe we
should just go ahead and do that.  It sounds like the right long term
solution anyways.

BR,
-R

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

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26  9:31   ` Daniel Vetter
  2014-05-26 11:35     ` Rob Clark
@ 2014-05-26 15:23     ` Ville Syrjälä
  2014-05-26 15:37       ` Daniel Vetter
  1 sibling, 1 reply; 69+ messages in thread
From: Ville Syrjälä @ 2014-05-26 15:23 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
> On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark 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.
> > 
> > Signed-off-by: Rob Clark <robdclark@gmail.com>
> 
> Same comments about interface design as for the plane patch apply here.
> One additional comment below.
> 
> > ---
> >  drivers/gpu/drm/armada/armada_crtc.c       |  11 +-
> >  drivers/gpu/drm/ast/ast_mode.c             |   1 +
> >  drivers/gpu/drm/cirrus/cirrus_mode.c       |   1 +
> >  drivers/gpu/drm/drm_atomic.c               | 231 ++++++++++-
> >  drivers/gpu/drm/drm_crtc.c                 | 598 ++++++++++++++++++-----------
> >  drivers/gpu/drm/drm_fb_helper.c            |   2 +-
> >  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/mdp/mdp4/mdp4_crtc.c   |   6 +-
> >  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c   |   6 +-
> >  drivers/gpu/drm/nouveau/dispnv04/crtc.c    |   1 +
> >  drivers/gpu/drm/nouveau/nv50_display.c     |   1 +
> >  drivers/gpu/drm/omapdrm/omap_crtc.c        |  12 +-
> >  drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
> >  drivers/gpu/drm/qxl/qxl_display.c          |   2 +
> >  drivers/gpu/drm/radeon/radeon_display.c    |   2 +
> >  drivers/gpu/drm/rcar-du/rcar_du_crtc.c     |   2 +
> >  drivers/gpu/drm/shmobile/shmob_drm_crtc.c  |   2 +
> >  drivers/gpu/drm/tilcdc/tilcdc_crtc.c       |   1 +
> >  drivers/gpu/drm/udl/udl_modeset.c          |   2 +
> >  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c        |   1 +
> >  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c       |   1 +
> >  include/drm/drm_atomic.h                   |  29 ++
> >  include/drm/drm_crtc.h                     | 103 ++++-
> >  27 files changed, 785 insertions(+), 243 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
> > index 7d3c649..6237af4 100644
> > --- a/drivers/gpu/drm/armada/armada_crtc.c
> > +++ b/drivers/gpu/drm/armada/armada_crtc.c
> > @@ -9,6 +9,7 @@
> >  #include <linux/clk.h>
> >  #include <drm/drmP.h>
> >  #include <drm/drm_crtc_helper.h>
> > +#include <drm/drm_atomic.h>
> >  #include "armada_crtc.h"
> >  #include "armada_drm.h"
> >  #include "armada_fb.h"
> > @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
> >  {
> >  	struct armada_private *priv = crtc->dev->dev_private;
> >  	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
> > +	struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> >  	bool update_csc = false;
> > +	int ret = 0;
> > +
> > +	if (IS_ERR(cstate))
> > +		return PTR_ERR(cstate);
> >  
> >  	if (property == priv->csc_yuv_prop) {
> >  		dcrtc->csc_yuv_mode = val;
> > @@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
> >  	} else if (property == priv->csc_rgb_prop) {
> >  		dcrtc->csc_rgb_mode = val;
> >  		update_csc = true;
> > +	} else {
> > +		ret = drm_crtc_set_property(crtc, cstate, property,
> > +				val, blob_data);
> >  	}
> >  
> >  	if (update_csc) {
> > @@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
> >  		writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
> >  	}
> >  
> > -	return 0;
> > +	return ret;
> >  }
> >  
> >  static struct drm_crtc_funcs armada_crtc_funcs = {
> > diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
> > index 114aee9..c08e0e1 100644
> > --- a/drivers/gpu/drm/ast/ast_mode.c
> > +++ b/drivers/gpu/drm/ast/ast_mode.c
> > @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
> >  	.cursor_move = ast_cursor_move,
> >  	.reset = ast_crtc_reset,
> >  	.set_config = drm_crtc_helper_set_config,
> > +	.set_property = drm_atomic_crtc_set_property,
> >  	.gamma_set = ast_crtc_gamma_set,
> >  	.destroy = ast_crtc_destroy,
> >  };
> > diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
> > index 49332c5..3c4428c 100644
> > --- a/drivers/gpu/drm/cirrus/cirrus_mode.c
> > +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
> > @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc)
> >  static const struct drm_crtc_funcs cirrus_crtc_funcs = {
> >  	.gamma_set = cirrus_crtc_gamma_set,
> >  	.set_config = drm_crtc_helper_set_config,
> > +	.set_property = drm_atomic_crtc_set_property,
> >  	.destroy = cirrus_crtc_destroy,
> >  };
> >  
> > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> > index 403ffc5..863a0fe 100644
> > --- a/drivers/gpu/drm/drm_atomic.c
> > +++ b/drivers/gpu/drm/drm_atomic.c
> > @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
> >  {
> >  	struct drm_atomic_state *state;
> >  	int nplanes = dev->mode_config.num_total_plane;
> > +	int ncrtcs  = dev->mode_config.num_crtc;
> >  	int sz;
> >  	void *ptr;
> >  
> >  	sz = sizeof(*state);
> >  	sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
> > +	sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
> >  
> >  	ptr = kzalloc(sz, GFP_KERNEL);
> >  
> > @@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
> >  	state->pstates = ptr;
> >  	ptr = &state->pstates[nplanes];
> >  
> > +	state->crtcs = ptr;
> > +	ptr = &state->crtcs[ncrtcs];
> > +
> > +	state->cstates = ptr;
> > +	ptr = &state->cstates[ncrtcs];
> > +
> >  	return state;
> >  }
> >  EXPORT_SYMBOL(drm_atomic_begin);
> > @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
> >  		struct drm_atomic_state *state, struct drm_mode_object *obj,
> >  		struct drm_pending_vblank_event *event)
> >  {
> > -	return -EINVAL;  /* for now */
> > +	switch (obj->type) {
> > +	case DRM_MODE_OBJECT_CRTC: {
> > +		struct drm_crtc_state *cstate =
> > +			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
> > +		if (IS_ERR(cstate))
> > +			return PTR_ERR(cstate);
> > +		cstate->event = event;
> > +		return 0;
> > +	}
> > +	default:
> > +		return -EINVAL;
> > +	}
> 
> Hm, I think if we only want completion events on crtcs (which I agree on)

I don't. Unless you have a nice way of passing some kind of "fbs now
available for rendering" list back to userland in the single crtc
event. Last time I looked making the drm event stuff deal with
variable length events looked more painful than just adding per
plane events. But I must admit that I didn't really try to do it.

-- 
Ville Syrjälä
Intel OTC

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

* Re: [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip
  2014-05-26 12:48   ` Rob Clark
@ 2014-05-26 15:24     ` Daniel Vetter
  2014-05-26 16:12       ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 15:24 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 08:48:52AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
> >> One more time, with feeling..
> >>
> >> Previous revision of series:
> >> http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
> >>
> >> And if you prefer, in git form:
> >> http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion
> >> git://people.freedesktop.org/~robclark/linux cold-fusion
> >>
> >> This series does not include the actual atomic ioctl, but it does
> >> include all the needed infrastructure changes.
> >>
> >> Compared to previous revision, I've split out drm_modeset_acquire_ctx
> >> from drm_atomic_state, so that we can ww acquire mode_config and crtc
> >> locks outside of atomic transactions.  (Which keeps lockdep happy wrt.
> >> drm_modeset_lock_all().)  I also got to the point in juggling things
> >> around where I wanted to let the compiler type checking do it's job,
> >> so 's/void *state/struct drm_atomic_state *state/g'.
> >>
> >> At this point, I've tested this on i915 (few different generation
> >> laptops), radeon, and msm.  And of course w/ ww debug (deadlock in-
> >> jection) enabled.  So I think all the locking related paths should
> >> be covered.
> >>
> >> I believe Thierry has tested a slighly older revision on tegra.  I
> >> would of course appreciate more testing on other drivers for which I
> >> don't have the hw.  But I think it is pretty much ready to go.  I do
> >> still owe some docs updates, I will send some patches for that in
> >> the near future, but didn't want to hold up giving others a chance
> >> to start banging on this.
> >
> > Ok, I've done a fairly cursory look at this only and tried to concentrate
> > on grasping the high-level details. Patches contain a bunch of comments on
> > the details, but here is the big stuff.
> >
> > First things first there's a bunch of prep work and refactoring sprinkled
> > throughout the series. Imo we should pick those out and rebase them on top
> > of drm-next asap to get them in before the actual interfaces. I hope I've
> > catched them all and commented everywhere about what imo is still missing
> > for merging. With that out of the way the real atomic review below.
> >
> > I like the overall design. It will be a royal pain to convert i915 over to
> > this since thus far we've simply tucked away the staged state into
> > obj->new_foo pointers. Which means we get to change the entire driver
> > again ;-)
> >
> > But I think the interfaces to driver and overall api design need some good
> > polish:
> >
> > - Imo way too much is done in driver callbacks, which then again call
> >   helpers. Parsing of the core properties for plane/crtcs should be done
> >   in the core imo. This way drivers only need to register ->set_prop
> >   callbacks if there's a driver-specific property they support.
> 
> Pretty universally, the approach I took was to provide default fxns
> (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let
> drivers wrap them as needed.  We might need to tweak
> drm_atomic_begin() a bit for the first driver that needs to wrap
> drm_atomic_state, but I guess we can tackle that as the need arises.
> 
> > - Imo those obj->set_prop callbacks should take the object-specific
> >   drm_obj_state object, not the global atomic state structure. Will lead
> >   to _much_ less lookup code duplicated all over the place. If we then
> >   also add a state->obj pointer we could also ditch that parameter. Ofc
> >   obj would be specific to the state at hand.
> 
> The problem is if those callbacks need to take other driver shared
> locks.. that plus initially I was trying to keep state as opaque as
> possible (in case sooner or later i915 decided to re-implement :-P)
> 
> Eventually I decided keeping state opaque was too much pita, and that
> if driver wanted to do something different, it should instead
> subclass.  So I guess these days they could chase [pc]state->state to
> get back at the global state.

I don't think keeping state opaque is a feature. Imo we _want_ to have
some common structures to track the common set of properties, for
otherwise insanity will rule. Drivers can add whatever they whish by
subclassing the existing state structs and add whatever they need to track
their state. I don't have any intentions to implement something
newfangled/different for i915, but instead want to port our current
infrastructure over to this.

Having clear prepare&check (we call it compute) and commit semantics is
imo the right way to do kms. Think of i915 as the poc vehicle that gets to
pay the bill of rewriting the driver twice.

So I'm advocating to move even further avoid from void* everywhere than
you've moved already.

Wrt helpers I think at least for modeset operations we can pimp the crtc
helpers and mostly reuse the hooks we already have, maybe augmented with
per-object ->check hooks. But for nuclear pageflips I really don't see a
chance but a single driver-gloabl ->commit (and fwiw check) hook. Maybe
per-crtc at best, but that will already run into planes changing crtcs.

> > - One downside of that is that drivers won't be able to interfere the
> >   allocation step any more. I think we'd need an additional
> >   ->alloc_atomic_state call so that drivers can still easily subclass some
> >   objects with their own type.
> 
> the ->create_state() is already split out as a vfunc for planes and
> crtcs.  Which I think should be enough to cover most of what drivers
> need.  If driver needs to subclass global state too, then we need to
> split up drm_atomic_begin(), but I guess that should be a relatively
> small change that we can make when it becomes needed.

I guess i915 will need it, to keep track of shared resources like plls.
Atm we carry them around in dev_priv, without any precompute/commit
semantics. Still something that needs to be fixed ...

Wrt ->create_state: I simply missed that in all the patches. But if you
have that I don't quite understand why the various ->set_property
callbacks have checks whether the object-specific state exists already and
allocate it if not through the get_plane_state and similar functions. Imo
that kind of stuff should be handled in the core. Error handling code is
really hard to get correct ime, and we depend upon it's correctness here
(at least with the current locking scheme) for ww mutexes to work.

> > - There's imo a bit a confusion between what's helper and what's driver
> >   api. My big gripe here is with the set_prop stuff which imo really
> >   should be core and not helpers. The default ->commit/check
> >   implementations otoh should be more clearly delineated as helper
> >   implementations that drivers can/should overwrite. I think we should
> >   split drm_atomic.c into drm_atomic.c (with the official pieces) and
> >   drm_atomic_helper.c (with the suggested standard/transitional
> >   implemenations for commit/check). Helper functions should have the
> >   drm_atomic_helper_ prefix.
> 
> Hmm, I think nearly *everything* could be overridden.. maybe it could
> be more clear what to override vs outright replace.  But otoh I'm not
> quite sure yet what drivers will need to override (other than some
> obvious ones, which are already broken out into vfuncs, like
> {plane,crtc}->create_state() to handle driver custom properties).

Like I've said above we should imo try to unify the world a bit more
again. And for the state tracking that should be possible, so I don't see
a need to hand drivers too much rope.

The check/commit stuff otoh should be fully overrideable, since i915 will
need that.

> So I expect some tweaks as other drivers start adding native atomic
> support.  I basically added what I needed for msm, and what I thought
> was pretty safe bet that other drivers would need.  This at least
> gives us something other drivers could start trying to use.  Then see
> what is missing and add it as we go.  At least that was my line of
> thinking.

Like I've said I don't think there's much value beyong making crtc helpers
atomic capable for modeset changes. Atomic pageflips pretty much need
driver-specific magic (less so if there's a "GO" bit like on sane
hardware).

> > - I also think we should split the vfuncs like we do with the crtc
> >   helpers. Imo the core interface should just be an ->alloc_state and
> >   ->set_prop on all kms objects, plus a global ->prep/check/commit at the
> >   driver level. The object-specific ->check/commit hooks otoh should be
> >   part of the atomic helper library and in some separate
> >   atomic_helper_funcs structure.
> 
> this is in fact the way it is, although 'struct drm_atomic_funcs'
> maybe should have "helper" in the name.

Yeah, I'm mostly just asking for sprinkling helper all over the stuff that
clearly is helper code. And moving everything else into the core and
making it non-overrideable.

> >  Imo we could either extend the sturcture
> >   used by the crtc helpers (hackish imo) or change the type of that to
> >   make it clear it's for the crtc helpers and add a new pointer for atomic
> >   helpers. E.g. in i915 I expect that we'll implement our very own
> >   ->check/commit hooks using our own compute_config infrastructure. Maybe
> >   we could switch to the ->check hooks of the atomic helper eventually,
> >   but that will be a lot of work. And in any case we won't ever switch to
> >   the ->commit hook as you have it in your helpers since for truly atomic
> >   flips/modesets we need to interleave all the updates of all objects in
> >   various phases.
> 
> not sure if it would work for you, but in msm I have a per-crtc "don't
> actually commit yet" bit, which gets set for each crtc involved in
> atomic update.  Although in my case it is mainly a matter of not
> touching the "GO" bits until everything is setup..
> 
> If not, I guess it shouldn't be a big deal to, for example, split up
> atomic_commit() into part that did the locking dance and then call
> vfunc ->atomic_apply_state() or something like that.
> 
> Basically figure out where you need to hook in for i915 as you add
> native atomic support.  By the time you and maybe one or two other
> drivers have added atomic support we should have it pretty well nailed
> down.  But until then, there might be some small adjustments
> here/there.

For modeset changes we need to munge everything through the full state
precompute and then commit to hw machinery. Not yet fully there (since the
shared dpll stuff doesn't work yet). For nuclear pageflips we're only just
starting to merge all of Ville's infrastructure, but will probably be the
same. So imo the only hand-off point between the core and drm is the
single, global ->commit. ->check would be the identical code, except for
the final commit.

All the ->set_prop callbacks would do nothing else but store data into
structures.

> > The patches are also rather big, which makes them a bit a pain to review.
> > Matt's approach for the primary planes was much easier:
> > 1) Add the new core interfaces for the new world.
> > 2) Implement helpers for drivers to transition and convert drivers.
> > 3) Implement ioctls using the new driver interfaces.
> 
> not so far different from what I did.  Although I suppose I could have
> split up adding atomic support for plane/crtc vs converting over
> existing ioctls to use it.  Not sure if it is worth doing at this
> stage or not.  Not really sure that it would help in sorting out what
> is helper vs not.

It's mostly for reviewing - silly me got lost countless times in your
patches.

> > That approach would also help a lot in figuring out what's helper code and
> > so optional, and what's core stuff.
> >
> > The other big problem I see still is with the locking:
> >
> > - Imo switching mode_config.mutex to a ww_mutex won't work. I know that
> >   the magic rules of w/w locking are _really_ tempting. But I don't think
> >   it will actually solve anything and instead only cause havoc.
> 
> are you actually getting lockdep splats?  If so pls send them my way
> and I'll have a look.
> 
> In non-atomic cases we are still using these as bare mutex's (other
> than the one special case where we needed nested_lock before).  So I
> think if it worked before, it should continue to work.

Didn't run it, but pretty sure. I think the other mail conversation
cleared that up - mixing w/w nesting and static nesting just don't work:
Static nesting means locks are filed into different (static) groups, w/w
nesting means you have one single locking class, but ordered dynamically
with the backoff magic.

> > - I think we need ww mutexes for crtcs, but they will be fairly useless
> >   without ww mutexes on plane. Not for i915, but for all those drivers
> >   which can switch planes around between different crtcs. Imo we should do
> >   this as a first step (before getting all the atomic interfaces in), and
> >   pimp the set_plane locking a bit already like I've described in some
> >   mail.
> >
> > - There's a pile of state that's currently not really protected by
> >   anything in your patches. One piece is all the obj->state properties.
> >   Another one is state detected at runtime like e.g. whether a hdmi sink
> >   supports audio or what kind of link bw parameters a dp sink supports.
> >   Thus far this was all protected by mode_config.mutex, but we can't take
> >   this one in the generic atomic ioctls for pretty obvious reasons.
> 
> we are still taking mode_config.mutex in places where we did before
> (setcrtc, setplane, etc).  So it shouldn't be a problem *yet*.
> 
> Runtime internal params about hw state, as opposed to things userspace
> is asking for, should perhaps not be in obj->state?  Or maybe I'm
> misunderstanding you.

Afaict we copy obj->state and ->set_prop (at least in i915) also looks at
a bunch of other connector state. And at that point we don't hold
mode_config.mutex, and I couldn't spot anything else. Thus far all the
->set_prop stuff _did_ hold mode_config.mutex.

> >   I think we need a new lock here, e.g. dev->mode_config.state_lock. It's
> >   going to be painful, since we need to roll this out over _all_ driver
> >   callbacks which can change the meaning of some property. E.g. all the
> >   ->detect callbacks in i915. The important part is that no one is allowed
> >   to do any costly operations why holding this lock (i.e. anything like
> >   reading EDIDs or doing load-detect), it is only for updating/reading
> >   this state.
> >
> > - To avoid nasties with locking inversion between the plain
> >   mode_config.mutex, the state_mutex and the various ww mutexes we need to
> >   rather completely rework the locking sequence for atomic updates
> >   compared to what you have. Since in the driver's detect callbacks we
> >   first grab the mode_config.mutex and then potentiall ww mutexes (only
> >   i915) and maybe also the state_mutex (for updating autodetected
> >   properties). But for atomic updates we first need to grab the
> >   state_mutex (so that we can get at the current state) and then ww
> >   mutexes and the mode_config.mutex. And for the later we're not even good
> >   at predicting the order we want to acquire them.
> 
> so I think I need to understand the call sequence for the detect thing
> in i915, since atm I'm not quite sure what the problem is, and why it
> worked before..
> 
> >   Stage 1: Create the new state
> >   We grab _just_ the mode_config.state_mutex and allocate all the state
> >   objects. This will allow us to grab a copy of the current state of
> >   everything without races or inconsistencies.
> >
> >   As a consequence ->set_prop hooks may not touch anything outside of the
> >   date/state protected by the state_mutex.
> >
> >   Stage 2: check/commit stage
> >   We drop the state_lock since it would nest the wrong way round again
> >   with mode_config.mutex. We should have a complete copy of all state
> >   already, and if something races against us (2nd ioctl or a output probe
> >   call) we don't really care.
> >
> >   Only then will the check/commit hooks grab all the locks. For the
> >   mode_config.mutex we could add a bit to the global state the we need it
> >   (e.g. when the connector->crtc links change or for some other
> >   driver-private need).
> >
> >   That only leaves races with a 2nd concurrent atomic modeset ioctl. Which
> >   can be avoided by grabbing yet another lock, which we drop after the
> >   driver has acquired all real locks. For that we could mandate the
> >   ->check callback and require that it grabs all ww mutexes plus the
> >   mode_config.mutex.
> >
> > For implementing this madness (if we agree it's the right approach) we can
> > go step-by-step:
> > - Convert crtc->mutex to a ww mutex.
> > - Add plane ww mutexes, make set_plane locking more fine-grained.
> > - Add the mode_config.state_lock, roll it out across all driver's ->detect
> >   callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
> > - Use all this correctly in the atomic stuff.
> >
> > Ok, that's the two big comments on this work from my side. Now reality
> > check: How much off the mark am I?
> 
> well, I think in summary, it is (a) might need to split few things up
> or re-arrange things slightly as i915 and other drivers start adding
> native atomic support.. but this doesn't scare me too much, and I
> think we can take it as we go.

The problem I see here is massive churn over drivers. If we try to make
set_prop hooks and similar stuff optional we'd avoid that, and would
actually gain flexibility with fixing the interfaces down the road. If we
enforce default callbacks for everyone that will only bloat diffs.

> And (b) possible locking fun.. this I think actually does need to be
> sorted first.  Although I'm not quite sure I understand why it is
> actually a problem.

Plan B for this might be to go full bore on ww mutexes and also protect
connectors with them. Then atomic updates would never have a need for
mode_config.mutex, and the role of that lock would be restricted to some
detect fun. Less complicated locking scheme, but even more code to audit.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 15:20                 ` Rob Clark
@ 2014-05-26 15:35                   ` Daniel Vetter
  2014-05-26 15:49                     ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 15:35 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 11:20:49AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Mon, May 26, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
> >> hmm, ok, I had thought this case, thread B would get -EDEADLK because
> >> lock was held, and not his acquire ctx.  If that is not the case, then
> >> I would propose this:
> >>
> >> All places doing things the old way, must grab mode_config.mutex first
> >> currently.  And we use mode_config.mutex to protect
> >> mode_config.acquire_ctx.  So all the lower spots grabbing individual
> >> crtc mutexes can safely use mode_config.acquire_ctx.
> >>
> >> Then the only headache is propagating -EDEADLK up the call stack.  If
> >> we are lucky, the all already propagate -EINTR, etc.
> >
> > The output poll work most definitely doesn't propagate -EINTR. Like
> > I've said, this will be painful. And imo doing this also makes the kms
> > locking into quite a mess overall.
> 
> Well, we could hold mode_config.mutex as a traditional mutex around
> atomic operations.  What you loose out would be now _NONBLOCK
> operations could conceivable call into driver paths without
> mode_config.mutex held.  This was the advantage of converting
> mode_config.mutex as well.  Granted, it is slightly theoretical
> because until we expose atomic ioctl it would only apply to page_flip
> (which was not holding mode_config.mutex).  And we also want to get
> rid of mode_config.mutex in these paths too.
> 
> Otoh, if we want to make locking more fine grained, more use of
> ww_mutex seems like the best way.  And if that means adding a return
> value to a fxn here/there and propagating errors properly, maybe we
> should just go ahead and do that.  It sounds like the right long term
> solution anyways.

Yeah, I'm starting to lean towards trying to elide mode_config.mutex
completely from the atomic paths (and modesets in general). I think the
only bits we really need is adding ww mutexes to planes _and_ to
connectors. The atomic would _only_ ever acquire ww mutexes, and we would
be able to guarante that most of them are only held short times so that we
don't need to bother with trylocking them for NONBLOCK. That should simply
the atomic logic a bit I hope.

So that, and a full subsystem audit unfortunately :(
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26 15:23     ` Ville Syrjälä
@ 2014-05-26 15:37       ` Daniel Vetter
  2014-05-26 15:42         ` Rob Clark
  2014-05-26 15:46         ` Ville Syrjälä
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 15:37 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: dri-devel

On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
> On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
> > On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
> > > @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
> > >  		struct drm_atomic_state *state, struct drm_mode_object *obj,
> > >  		struct drm_pending_vblank_event *event)
> > >  {
> > > -	return -EINVAL;  /* for now */
> > > +	switch (obj->type) {
> > > +	case DRM_MODE_OBJECT_CRTC: {
> > > +		struct drm_crtc_state *cstate =
> > > +			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
> > > +		if (IS_ERR(cstate))
> > > +			return PTR_ERR(cstate);
> > > +		cstate->event = event;
> > > +		return 0;
> > > +	}
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > 
> > Hm, I think if we only want completion events on crtcs (which I agree on)
> 
> I don't. Unless you have a nice way of passing some kind of "fbs now
> available for rendering" list back to userland in the single crtc
> event. Last time I looked making the drm event stuff deal with
> variable length events looked more painful than just adding per
> plane events. But I must admit that I didn't really try to do it.

Hm, why can't userspace keep a list of fb ids involved in a given crtc
pageflip around and index those with the cookie we pass around?

I really don't see why the kernel has to implement a half-baked event
multiplexer (which just copies the same thing n times), while userspace is
perfectly capable of doing that itself?

Now if the timestamps would be genuinely different then I'd agree, but
then that's not an atomic update.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26 15:37       ` Daniel Vetter
@ 2014-05-26 15:42         ` Rob Clark
  2014-05-26 15:46         ` Ville Syrjälä
  1 sibling, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-26 15:42 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 11:37 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
>> On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
>> > On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
>> > > @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
>> > >           struct drm_atomic_state *state, struct drm_mode_object *obj,
>> > >           struct drm_pending_vblank_event *event)
>> > >  {
>> > > - return -EINVAL;  /* for now */
>> > > + switch (obj->type) {
>> > > + case DRM_MODE_OBJECT_CRTC: {
>> > > +         struct drm_crtc_state *cstate =
>> > > +                 drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
>> > > +         if (IS_ERR(cstate))
>> > > +                 return PTR_ERR(cstate);
>> > > +         cstate->event = event;
>> > > +         return 0;
>> > > + }
>> > > + default:
>> > > +         return -EINVAL;
>> > > + }
>> >
>> > Hm, I think if we only want completion events on crtcs (which I agree on)
>>
>> I don't. Unless you have a nice way of passing some kind of "fbs now
>> available for rendering" list back to userland in the single crtc
>> event. Last time I looked making the drm event stuff deal with
>> variable length events looked more painful than just adding per
>> plane events. But I must admit that I didn't really try to do it.
>
> Hm, why can't userspace keep a list of fb ids involved in a given crtc
> pageflip around and index those with the cookie we pass around?
>
> I really don't see why the kernel has to implement a half-baked event
> multiplexer (which just copies the same thing n times), while userspace is
> perfectly capable of doing that itself?
>
> Now if the timestamps would be genuinely different then I'd agree, but
> then that's not an atomic update.

I do kinda think userspace would want to know somehow if an fb is
going to be really late and explicitly submit an older fb.  If you
were resizing, for example, I don't think the driver could make the
call to leave one plane displaying an older fb.

But I do think we can bikeshed about that in parallel with getting the
inital atomic infrastructure merged.

BR,
-R

> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26 15:37       ` Daniel Vetter
  2014-05-26 15:42         ` Rob Clark
@ 2014-05-26 15:46         ` Ville Syrjälä
  2014-05-26 16:12           ` Daniel Vetter
  1 sibling, 1 reply; 69+ messages in thread
From: Ville Syrjälä @ 2014-05-26 15:46 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Mon, May 26, 2014 at 05:37:57PM +0200, Daniel Vetter wrote:
> On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
> > On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
> > > On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
> > > > @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
> > > >  		struct drm_atomic_state *state, struct drm_mode_object *obj,
> > > >  		struct drm_pending_vblank_event *event)
> > > >  {
> > > > -	return -EINVAL;  /* for now */
> > > > +	switch (obj->type) {
> > > > +	case DRM_MODE_OBJECT_CRTC: {
> > > > +		struct drm_crtc_state *cstate =
> > > > +			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
> > > > +		if (IS_ERR(cstate))
> > > > +			return PTR_ERR(cstate);
> > > > +		cstate->event = event;
> > > > +		return 0;
> > > > +	}
> > > > +	default:
> > > > +		return -EINVAL;
> > > > +	}
> > > 
> > > Hm, I think if we only want completion events on crtcs (which I agree on)
> > 
> > I don't. Unless you have a nice way of passing some kind of "fbs now
> > available for rendering" list back to userland in the single crtc
> > event. Last time I looked making the drm event stuff deal with
> > variable length events looked more painful than just adding per
> > plane events. But I must admit that I didn't really try to do it.
> 
> Hm, why can't userspace keep a list of fb ids involved in a given crtc
> pageflip around and index those with the cookie we pass around?
> 
> I really don't see why the kernel has to implement a half-baked event
> multiplexer (which just copies the same thing n times), while userspace is
> perfectly capable of doing that itself?
> 
> Now if the timestamps would be genuinely different then I'd agree, but
> then that's not an atomic update.

User space can't do it if flips get issued faster than the vrefresh
rate. I still want my "moar fps" triple buffering mode.

-- 
Ville Syrjälä
Intel OTC

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 15:35                   ` Daniel Vetter
@ 2014-05-26 15:49                     ` Rob Clark
  2014-05-26 16:09                       ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 15:49 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel

On Mon, May 26, 2014 at 11:35 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, May 26, 2014 at 11:20:49AM -0400, Rob Clark wrote:
>> On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Mon, May 26, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
>> >> hmm, ok, I had thought this case, thread B would get -EDEADLK because
>> >> lock was held, and not his acquire ctx.  If that is not the case, then
>> >> I would propose this:
>> >>
>> >> All places doing things the old way, must grab mode_config.mutex first
>> >> currently.  And we use mode_config.mutex to protect
>> >> mode_config.acquire_ctx.  So all the lower spots grabbing individual
>> >> crtc mutexes can safely use mode_config.acquire_ctx.
>> >>
>> >> Then the only headache is propagating -EDEADLK up the call stack.  If
>> >> we are lucky, the all already propagate -EINTR, etc.
>> >
>> > The output poll work most definitely doesn't propagate -EINTR. Like
>> > I've said, this will be painful. And imo doing this also makes the kms
>> > locking into quite a mess overall.
>>
>> Well, we could hold mode_config.mutex as a traditional mutex around
>> atomic operations.  What you loose out would be now _NONBLOCK
>> operations could conceivable call into driver paths without
>> mode_config.mutex held.  This was the advantage of converting
>> mode_config.mutex as well.  Granted, it is slightly theoretical
>> because until we expose atomic ioctl it would only apply to page_flip
>> (which was not holding mode_config.mutex).  And we also want to get
>> rid of mode_config.mutex in these paths too.
>>
>> Otoh, if we want to make locking more fine grained, more use of
>> ww_mutex seems like the best way.  And if that means adding a return
>> value to a fxn here/there and propagating errors properly, maybe we
>> should just go ahead and do that.  It sounds like the right long term
>> solution anyways.
>
> Yeah, I'm starting to lean towards trying to elide mode_config.mutex
> completely from the atomic paths (and modesets in general). I think the
> only bits we really need is adding ww mutexes to planes _and_ to
> connectors. The atomic would _only_ ever acquire ww mutexes, and we would
> be able to guarante that most of them are only held short times so that we
> don't need to bother with trylocking them for NONBLOCK. That should simply
> the atomic logic a bit I hope.
>
> So that, and a full subsystem audit unfortunately :(

well, getting rid of any global lock from atomic path is, I hope, the
eventual goal.  But we are *really* late already with atomic, and too
many other things backing up on top of this.  I suppose I should have
a closer look at the detect paths, to just see how hairy the
alternative of adding error propagation would be.  Although maybe not
today (it is supposed to be a holiday here.. and I've already spent
enough of the weekend on atomic ;-))

Just to get *something* merged to unblock driver work and maybe get
people starting to think about plumbing this through userspace (xrandr
atomic modeset, for example), maybe the way to go is just revert
mode_config.mutex to a traditional mutex for now.  The weird cases
only start to come up with real atomic ioctls, and we could use a
driver flag to opt-in to that on a per driver basis.

BR,
-R

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

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

* Re: [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex
  2014-05-26 15:49                     ` Rob Clark
@ 2014-05-26 16:09                       ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 16:09 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Mon, May 26, 2014 at 11:49:59AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 11:35 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Mon, May 26, 2014 at 11:20:49AM -0400, Rob Clark wrote:
> >> On Mon, May 26, 2014 at 11:07 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> >> > On Mon, May 26, 2014 at 5:04 PM, Rob Clark <robdclark@gmail.com> wrote:
> >> >> hmm, ok, I had thought this case, thread B would get -EDEADLK because
> >> >> lock was held, and not his acquire ctx.  If that is not the case, then
> >> >> I would propose this:
> >> >>
> >> >> All places doing things the old way, must grab mode_config.mutex first
> >> >> currently.  And we use mode_config.mutex to protect
> >> >> mode_config.acquire_ctx.  So all the lower spots grabbing individual
> >> >> crtc mutexes can safely use mode_config.acquire_ctx.
> >> >>
> >> >> Then the only headache is propagating -EDEADLK up the call stack.  If
> >> >> we are lucky, the all already propagate -EINTR, etc.
> >> >
> >> > The output poll work most definitely doesn't propagate -EINTR. Like
> >> > I've said, this will be painful. And imo doing this also makes the kms
> >> > locking into quite a mess overall.
> >>
> >> Well, we could hold mode_config.mutex as a traditional mutex around
> >> atomic operations.  What you loose out would be now _NONBLOCK
> >> operations could conceivable call into driver paths without
> >> mode_config.mutex held.  This was the advantage of converting
> >> mode_config.mutex as well.  Granted, it is slightly theoretical
> >> because until we expose atomic ioctl it would only apply to page_flip
> >> (which was not holding mode_config.mutex).  And we also want to get
> >> rid of mode_config.mutex in these paths too.
> >>
> >> Otoh, if we want to make locking more fine grained, more use of
> >> ww_mutex seems like the best way.  And if that means adding a return
> >> value to a fxn here/there and propagating errors properly, maybe we
> >> should just go ahead and do that.  It sounds like the right long term
> >> solution anyways.
> >
> > Yeah, I'm starting to lean towards trying to elide mode_config.mutex
> > completely from the atomic paths (and modesets in general). I think the
> > only bits we really need is adding ww mutexes to planes _and_ to
> > connectors. The atomic would _only_ ever acquire ww mutexes, and we would
> > be able to guarante that most of them are only held short times so that we
> > don't need to bother with trylocking them for NONBLOCK. That should simply
> > the atomic logic a bit I hope.
> >
> > So that, and a full subsystem audit unfortunately :(
> 
> well, getting rid of any global lock from atomic path is, I hope, the
> eventual goal.  But we are *really* late already with atomic, and too
> many other things backing up on top of this.  I suppose I should have
> a closer look at the detect paths, to just see how hairy the
> alternative of adding error propagation would be.  Although maybe not
> today (it is supposed to be a holiday here.. and I've already spent
> enough of the weekend on atomic ;-))
> 
> Just to get *something* merged to unblock driver work and maybe get
> people starting to think about plumbing this through userspace (xrandr
> atomic modeset, for example), maybe the way to go is just revert
> mode_config.mutex to a traditional mutex for now.  The weird cases
> only start to come up with real atomic ioctls, and we could use a
> driver flag to opt-in to that on a per driver basis.

This will result in people raving about regressions, which from a
maintainer pov I'll reject. If we want to chicken out of this question I'd
vote for an drm_modeset_lock_all for the atomic paths, which also isn't
really helpful. I don't really think there's an easy answer.

Wrt unblocking driver work: In i915 we keep on plunging ahead without the
interface stuff thus far. And wrt unblocking everything else: I hear a lot
of people screaming about the lack of atomic updates, but at least within
Intel not many people throwing resources at it. So my impression is that
it can't be that bad really.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip
  2014-05-26 15:24     ` Daniel Vetter
@ 2014-05-26 16:12       ` Rob Clark
  2014-05-26 17:36         ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-26 16:12 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 11:24 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Mon, May 26, 2014 at 08:48:52AM -0400, Rob Clark wrote:
>> On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
>> >> One more time, with feeling..
>> >>
>> >> Previous revision of series:
>> >> http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
>> >>
>> >> And if you prefer, in git form:
>> >> http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion
>> >> git://people.freedesktop.org/~robclark/linux cold-fusion
>> >>
>> >> This series does not include the actual atomic ioctl, but it does
>> >> include all the needed infrastructure changes.
>> >>
>> >> Compared to previous revision, I've split out drm_modeset_acquire_ctx
>> >> from drm_atomic_state, so that we can ww acquire mode_config and crtc
>> >> locks outside of atomic transactions.  (Which keeps lockdep happy wrt.
>> >> drm_modeset_lock_all().)  I also got to the point in juggling things
>> >> around where I wanted to let the compiler type checking do it's job,
>> >> so 's/void *state/struct drm_atomic_state *state/g'.
>> >>
>> >> At this point, I've tested this on i915 (few different generation
>> >> laptops), radeon, and msm.  And of course w/ ww debug (deadlock in-
>> >> jection) enabled.  So I think all the locking related paths should
>> >> be covered.
>> >>
>> >> I believe Thierry has tested a slighly older revision on tegra.  I
>> >> would of course appreciate more testing on other drivers for which I
>> >> don't have the hw.  But I think it is pretty much ready to go.  I do
>> >> still owe some docs updates, I will send some patches for that in
>> >> the near future, but didn't want to hold up giving others a chance
>> >> to start banging on this.
>> >
>> > Ok, I've done a fairly cursory look at this only and tried to concentrate
>> > on grasping the high-level details. Patches contain a bunch of comments on
>> > the details, but here is the big stuff.
>> >
>> > First things first there's a bunch of prep work and refactoring sprinkled
>> > throughout the series. Imo we should pick those out and rebase them on top
>> > of drm-next asap to get them in before the actual interfaces. I hope I've
>> > catched them all and commented everywhere about what imo is still missing
>> > for merging. With that out of the way the real atomic review below.
>> >
>> > I like the overall design. It will be a royal pain to convert i915 over to
>> > this since thus far we've simply tucked away the staged state into
>> > obj->new_foo pointers. Which means we get to change the entire driver
>> > again ;-)
>> >
>> > But I think the interfaces to driver and overall api design need some good
>> > polish:
>> >
>> > - Imo way too much is done in driver callbacks, which then again call
>> >   helpers. Parsing of the core properties for plane/crtcs should be done
>> >   in the core imo. This way drivers only need to register ->set_prop
>> >   callbacks if there's a driver-specific property they support.
>>
>> Pretty universally, the approach I took was to provide default fxns
>> (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let
>> drivers wrap them as needed.  We might need to tweak
>> drm_atomic_begin() a bit for the first driver that needs to wrap
>> drm_atomic_state, but I guess we can tackle that as the need arises.
>>
>> > - Imo those obj->set_prop callbacks should take the object-specific
>> >   drm_obj_state object, not the global atomic state structure. Will lead
>> >   to _much_ less lookup code duplicated all over the place. If we then
>> >   also add a state->obj pointer we could also ditch that parameter. Ofc
>> >   obj would be specific to the state at hand.
>>
>> The problem is if those callbacks need to take other driver shared
>> locks.. that plus initially I was trying to keep state as opaque as
>> possible (in case sooner or later i915 decided to re-implement :-P)
>>
>> Eventually I decided keeping state opaque was too much pita, and that
>> if driver wanted to do something different, it should instead
>> subclass.  So I guess these days they could chase [pc]state->state to
>> get back at the global state.
>
> I don't think keeping state opaque is a feature. Imo we _want_ to have
> some common structures to track the common set of properties, for
> otherwise insanity will rule. Drivers can add whatever they whish by
> subclassing the existing state structs and add whatever they need to track
> their state. I don't have any intentions to implement something
> newfangled/different for i915, but instead want to port our current
> infrastructure over to this.
>
> Having clear prepare&check (we call it compute) and commit semantics is
> imo the right way to do kms. Think of i915 as the poc vehicle that gets to
> pay the bill of rewriting the driver twice.
>
> So I'm advocating to move even further avoid from void* everywhere than
> you've moved already.
>
> Wrt helpers I think at least for modeset operations we can pimp the crtc
> helpers and mostly reuse the hooks we already have, maybe augmented with
> per-object ->check hooks. But for nuclear pageflips I really don't see a
> chance but a single driver-gloabl ->commit (and fwiw check) hook. Maybe
> per-crtc at best, but that will already run into planes changing crtcs.

The per-object hooks will work with at least three display controller
blocks that I know of (two in msm, and omapdrm), so I guess it is not
*that* uncommon.

We'll need to split up an existing fxn or two so that other drivers
can do an all-at-once commit more easily.  That seems like a fairly
small tweak, and I think it would be ok to merge along w/ i915 atomic
support.

>> > - One downside of that is that drivers won't be able to interfere the
>> >   allocation step any more. I think we'd need an additional
>> >   ->alloc_atomic_state call so that drivers can still easily subclass some
>> >   objects with their own type.
>>
>> the ->create_state() is already split out as a vfunc for planes and
>> crtcs.  Which I think should be enough to cover most of what drivers
>> need.  If driver needs to subclass global state too, then we need to
>> split up drm_atomic_begin(), but I guess that should be a relatively
>> small change that we can make when it becomes needed.
>
> I guess i915 will need it, to keep track of shared resources like plls.
> Atm we carry them around in dev_priv, without any precompute/commit
> semantics. Still something that needs to be fixed ...

it would seem tempting to put in i915_atomic_state, but then how does
it work with multiple parallel but independent atomic updates, or
should that not be allowed?

> Wrt ->create_state: I simply missed that in all the patches. But if you
> have that I don't quite understand why the various ->set_property
> callbacks have checks whether the object-specific state exists already and
> allocate it if not through the get_plane_state and similar functions. Imo
> that kind of stuff should be handled in the core. Error handling code is
> really hard to get correct ime, and we depend upon it's correctness here
> (at least with the current locking scheme) for ww mutexes to work.

Well, the current approach gave the driver more flexibility about what
locks to grab, etc.. although I suppose some of this might be less
needed now with primary planes.  Formerly I had to fwd some properties
from crtc to my own internal primary plane, which required the
flexibility that the current approach gives.

>> > - There's imo a bit a confusion between what's helper and what's driver
>> >   api. My big gripe here is with the set_prop stuff which imo really
>> >   should be core and not helpers. The default ->commit/check
>> >   implementations otoh should be more clearly delineated as helper
>> >   implementations that drivers can/should overwrite. I think we should
>> >   split drm_atomic.c into drm_atomic.c (with the official pieces) and
>> >   drm_atomic_helper.c (with the suggested standard/transitional
>> >   implemenations for commit/check). Helper functions should have the
>> >   drm_atomic_helper_ prefix.
>>
>> Hmm, I think nearly *everything* could be overridden.. maybe it could
>> be more clear what to override vs outright replace.  But otoh I'm not
>> quite sure yet what drivers will need to override (other than some
>> obvious ones, which are already broken out into vfuncs, like
>> {plane,crtc}->create_state() to handle driver custom properties).
>
> Like I've said above we should imo try to unify the world a bit more
> again. And for the state tracking that should be possible, so I don't see
> a need to hand drivers too much rope.
>
> The check/commit stuff otoh should be fully overrideable, since i915 will
> need that.
>
>> So I expect some tweaks as other drivers start adding native atomic
>> support.  I basically added what I needed for msm, and what I thought
>> was pretty safe bet that other drivers would need.  This at least
>> gives us something other drivers could start trying to use.  Then see
>> what is missing and add it as we go.  At least that was my line of
>> thinking.
>
> Like I've said I don't think there's much value beyong making crtc helpers
> atomic capable for modeset changes. Atomic pageflips pretty much need
> driver-specific magic (less so if there's a "GO" bit like on sane
> hardware).

right, there is a very tiny bit of driver magic in msm_atomic_commit()..

anyways, I don't disagree that we probably need to add something for
some drivers to be able to hook in to ->atomic_commit() after locking
magic but before loop over planes/crtcs.  I think that would simply be
addition to the api, ie. new fxn ptr a driver could populate.  I
wouldn't need it for msm.

>> > - I also think we should split the vfuncs like we do with the crtc
>> >   helpers. Imo the core interface should just be an ->alloc_state and
>> >   ->set_prop on all kms objects, plus a global ->prep/check/commit at the
>> >   driver level. The object-specific ->check/commit hooks otoh should be
>> >   part of the atomic helper library and in some separate
>> >   atomic_helper_funcs structure.
>>
>> this is in fact the way it is, although 'struct drm_atomic_funcs'
>> maybe should have "helper" in the name.
>
> Yeah, I'm mostly just asking for sprinkling helper all over the stuff that
> clearly is helper code. And moving everything else into the core and
> making it non-overrideable.
>
>> >  Imo we could either extend the sturcture
>> >   used by the crtc helpers (hackish imo) or change the type of that to
>> >   make it clear it's for the crtc helpers and add a new pointer for atomic
>> >   helpers. E.g. in i915 I expect that we'll implement our very own
>> >   ->check/commit hooks using our own compute_config infrastructure. Maybe
>> >   we could switch to the ->check hooks of the atomic helper eventually,
>> >   but that will be a lot of work. And in any case we won't ever switch to
>> >   the ->commit hook as you have it in your helpers since for truly atomic
>> >   flips/modesets we need to interleave all the updates of all objects in
>> >   various phases.
>>
>> not sure if it would work for you, but in msm I have a per-crtc "don't
>> actually commit yet" bit, which gets set for each crtc involved in
>> atomic update.  Although in my case it is mainly a matter of not
>> touching the "GO" bits until everything is setup..
>>
>> If not, I guess it shouldn't be a big deal to, for example, split up
>> atomic_commit() into part that did the locking dance and then call
>> vfunc ->atomic_apply_state() or something like that.
>>
>> Basically figure out where you need to hook in for i915 as you add
>> native atomic support.  By the time you and maybe one or two other
>> drivers have added atomic support we should have it pretty well nailed
>> down.  But until then, there might be some small adjustments
>> here/there.
>
> For modeset changes we need to munge everything through the full state
> precompute and then commit to hw machinery. Not yet fully there (since the
> shared dpll stuff doesn't work yet). For nuclear pageflips we're only just
> starting to merge all of Ville's infrastructure, but will probably be the
> same. So imo the only hand-off point between the core and drm is the
> single, global ->commit. ->check would be the identical code, except for
> the final commit.
>
> All the ->set_prop callbacks would do nothing else but store data into
> structures.

I suspect I'll want to see which core properties change eventually..
when I have a chance to implement YUV and scaling, I'd want to flag
that scaling/csc coefficients need updating, etc.

It doesn't seem unreasonable to pass plane/crtc state rather than
global state.  (Although we would need connector_state.. which is
something I've not found a good use for yet.)  But other than that,
I'd kinda like to leave the ->set_property() as it is.

BR,
-R

>
>> > The patches are also rather big, which makes them a bit a pain to review.
>> > Matt's approach for the primary planes was much easier:
>> > 1) Add the new core interfaces for the new world.
>> > 2) Implement helpers for drivers to transition and convert drivers.
>> > 3) Implement ioctls using the new driver interfaces.
>>
>> not so far different from what I did.  Although I suppose I could have
>> split up adding atomic support for plane/crtc vs converting over
>> existing ioctls to use it.  Not sure if it is worth doing at this
>> stage or not.  Not really sure that it would help in sorting out what
>> is helper vs not.
>
> It's mostly for reviewing - silly me got lost countless times in your
> patches.
>
>> > That approach would also help a lot in figuring out what's helper code and
>> > so optional, and what's core stuff.
>> >
>> > The other big problem I see still is with the locking:
>> >
>> > - Imo switching mode_config.mutex to a ww_mutex won't work. I know that
>> >   the magic rules of w/w locking are _really_ tempting. But I don't think
>> >   it will actually solve anything and instead only cause havoc.
>>
>> are you actually getting lockdep splats?  If so pls send them my way
>> and I'll have a look.
>>
>> In non-atomic cases we are still using these as bare mutex's (other
>> than the one special case where we needed nested_lock before).  So I
>> think if it worked before, it should continue to work.
>
> Didn't run it, but pretty sure. I think the other mail conversation
> cleared that up - mixing w/w nesting and static nesting just don't work:
> Static nesting means locks are filed into different (static) groups, w/w
> nesting means you have one single locking class, but ordered dynamically
> with the backoff magic.
>
>> > - I think we need ww mutexes for crtcs, but they will be fairly useless
>> >   without ww mutexes on plane. Not for i915, but for all those drivers
>> >   which can switch planes around between different crtcs. Imo we should do
>> >   this as a first step (before getting all the atomic interfaces in), and
>> >   pimp the set_plane locking a bit already like I've described in some
>> >   mail.
>> >
>> > - There's a pile of state that's currently not really protected by
>> >   anything in your patches. One piece is all the obj->state properties.
>> >   Another one is state detected at runtime like e.g. whether a hdmi sink
>> >   supports audio or what kind of link bw parameters a dp sink supports.
>> >   Thus far this was all protected by mode_config.mutex, but we can't take
>> >   this one in the generic atomic ioctls for pretty obvious reasons.
>>
>> we are still taking mode_config.mutex in places where we did before
>> (setcrtc, setplane, etc).  So it shouldn't be a problem *yet*.
>>
>> Runtime internal params about hw state, as opposed to things userspace
>> is asking for, should perhaps not be in obj->state?  Or maybe I'm
>> misunderstanding you.
>
> Afaict we copy obj->state and ->set_prop (at least in i915) also looks at
> a bunch of other connector state. And at that point we don't hold
> mode_config.mutex, and I couldn't spot anything else. Thus far all the
> ->set_prop stuff _did_ hold mode_config.mutex.
>
>> >   I think we need a new lock here, e.g. dev->mode_config.state_lock. It's
>> >   going to be painful, since we need to roll this out over _all_ driver
>> >   callbacks which can change the meaning of some property. E.g. all the
>> >   ->detect callbacks in i915. The important part is that no one is allowed
>> >   to do any costly operations why holding this lock (i.e. anything like
>> >   reading EDIDs or doing load-detect), it is only for updating/reading
>> >   this state.
>> >
>> > - To avoid nasties with locking inversion between the plain
>> >   mode_config.mutex, the state_mutex and the various ww mutexes we need to
>> >   rather completely rework the locking sequence for atomic updates
>> >   compared to what you have. Since in the driver's detect callbacks we
>> >   first grab the mode_config.mutex and then potentiall ww mutexes (only
>> >   i915) and maybe also the state_mutex (for updating autodetected
>> >   properties). But for atomic updates we first need to grab the
>> >   state_mutex (so that we can get at the current state) and then ww
>> >   mutexes and the mode_config.mutex. And for the later we're not even good
>> >   at predicting the order we want to acquire them.
>>
>> so I think I need to understand the call sequence for the detect thing
>> in i915, since atm I'm not quite sure what the problem is, and why it
>> worked before..
>>
>> >   Stage 1: Create the new state
>> >   We grab _just_ the mode_config.state_mutex and allocate all the state
>> >   objects. This will allow us to grab a copy of the current state of
>> >   everything without races or inconsistencies.
>> >
>> >   As a consequence ->set_prop hooks may not touch anything outside of the
>> >   date/state protected by the state_mutex.
>> >
>> >   Stage 2: check/commit stage
>> >   We drop the state_lock since it would nest the wrong way round again
>> >   with mode_config.mutex. We should have a complete copy of all state
>> >   already, and if something races against us (2nd ioctl or a output probe
>> >   call) we don't really care.
>> >
>> >   Only then will the check/commit hooks grab all the locks. For the
>> >   mode_config.mutex we could add a bit to the global state the we need it
>> >   (e.g. when the connector->crtc links change or for some other
>> >   driver-private need).
>> >
>> >   That only leaves races with a 2nd concurrent atomic modeset ioctl. Which
>> >   can be avoided by grabbing yet another lock, which we drop after the
>> >   driver has acquired all real locks. For that we could mandate the
>> >   ->check callback and require that it grabs all ww mutexes plus the
>> >   mode_config.mutex.
>> >
>> > For implementing this madness (if we agree it's the right approach) we can
>> > go step-by-step:
>> > - Convert crtc->mutex to a ww mutex.
>> > - Add plane ww mutexes, make set_plane locking more fine-grained.
>> > - Add the mode_config.state_lock, roll it out across all driver's ->detect
>> >   callbacks and add it to modeset_lock_all (so that set_prop calls dtrt).
>> > - Use all this correctly in the atomic stuff.
>> >
>> > Ok, that's the two big comments on this work from my side. Now reality
>> > check: How much off the mark am I?
>>
>> well, I think in summary, it is (a) might need to split few things up
>> or re-arrange things slightly as i915 and other drivers start adding
>> native atomic support.. but this doesn't scare me too much, and I
>> think we can take it as we go.
>
> The problem I see here is massive churn over drivers. If we try to make
> set_prop hooks and similar stuff optional we'd avoid that, and would
> actually gain flexibility with fixing the interfaces down the road. If we
> enforce default callbacks for everyone that will only bloat diffs.
>
>> And (b) possible locking fun.. this I think actually does need to be
>> sorted first.  Although I'm not quite sure I understand why it is
>> actually a problem.
>
> Plan B for this might be to go full bore on ww mutexes and also protect
> connectors with them. Then atomic updates would never have a need for
> mode_config.mutex, and the role of that lock would be restricted to some
> detect fun. Less complicated locking scheme, but even more code to audit.
> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 12/17] drm: convert crtc to properties/state
  2014-05-26 15:46         ` Ville Syrjälä
@ 2014-05-26 16:12           ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 16:12 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: dri-devel

On Mon, May 26, 2014 at 06:46:30PM +0300, Ville Syrjälä wrote:
> On Mon, May 26, 2014 at 05:37:57PM +0200, Daniel Vetter wrote:
> > On Mon, May 26, 2014 at 06:23:05PM +0300, Ville Syrjälä wrote:
> > > On Mon, May 26, 2014 at 11:31:10AM +0200, Daniel Vetter wrote:
> > > > On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
> > > > > @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
> > > > >  		struct drm_atomic_state *state, struct drm_mode_object *obj,
> > > > >  		struct drm_pending_vblank_event *event)
> > > > >  {
> > > > > -	return -EINVAL;  /* for now */
> > > > > +	switch (obj->type) {
> > > > > +	case DRM_MODE_OBJECT_CRTC: {
> > > > > +		struct drm_crtc_state *cstate =
> > > > > +			drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
> > > > > +		if (IS_ERR(cstate))
> > > > > +			return PTR_ERR(cstate);
> > > > > +		cstate->event = event;
> > > > > +		return 0;
> > > > > +	}
> > > > > +	default:
> > > > > +		return -EINVAL;
> > > > > +	}
> > > > 
> > > > Hm, I think if we only want completion events on crtcs (which I agree on)
> > > 
> > > I don't. Unless you have a nice way of passing some kind of "fbs now
> > > available for rendering" list back to userland in the single crtc
> > > event. Last time I looked making the drm event stuff deal with
> > > variable length events looked more painful than just adding per
> > > plane events. But I must admit that I didn't really try to do it.
> > 
> > Hm, why can't userspace keep a list of fb ids involved in a given crtc
> > pageflip around and index those with the cookie we pass around?
> > 
> > I really don't see why the kernel has to implement a half-baked event
> > multiplexer (which just copies the same thing n times), while userspace is
> > perfectly capable of doing that itself?
> > 
> > Now if the timestamps would be genuinely different then I'd agree, but
> > then that's not an atomic update.
> 
> User space can't do it if flips get issued faster than the vrefresh
> rate. I still want my "moar fps" triple buffering mode.

Hm right, I forget about this every time it comes up. Imo we should
postpone this to when it actually gets implemented - usualy preemptive
generalism just ends up hurting everyone for now real good. For the
current logic we could ditch all the indirection and callbacks really and
simply require that the ->commit hook sends out the crtc even if it's
there.

And even if we add per-plane events we don't need new hooks imo. Just
adding an event pointer to the plane state structure and firing off those
events together with the crtc one in drivers should be good enough.

The proposed set_event callback plus implementation otoh are serious
overkill.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip
  2014-05-26 16:12       ` Rob Clark
@ 2014-05-26 17:36         ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 17:36 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 6:12 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Mon, May 26, 2014 at 11:24 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> On Mon, May 26, 2014 at 08:48:52AM -0400, Rob Clark wrote:
>>> On Mon, May 26, 2014 at 6:40 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>>> > On Sat, May 24, 2014 at 02:30:09PM -0400, Rob Clark wrote:
>>> >> One more time, with feeling..
>>> >>
>>> >> Previous revision of series:
>>> >> http://lists.freedesktop.org/archives/dri-devel/2014-March/055806.html
>>> >>
>>> >> And if you prefer, in git form:
>>> >> http://cgit.freedesktop.org/~robclark/linux/log/?h=cold-fusion
>>> >> git://people.freedesktop.org/~robclark/linux cold-fusion
>>> >>
>>> >> This series does not include the actual atomic ioctl, but it does
>>> >> include all the needed infrastructure changes.
>>> >>
>>> >> Compared to previous revision, I've split out drm_modeset_acquire_ctx
>>> >> from drm_atomic_state, so that we can ww acquire mode_config and crtc
>>> >> locks outside of atomic transactions.  (Which keeps lockdep happy wrt.
>>> >> drm_modeset_lock_all().)  I also got to the point in juggling things
>>> >> around where I wanted to let the compiler type checking do it's job,
>>> >> so 's/void *state/struct drm_atomic_state *state/g'.
>>> >>
>>> >> At this point, I've tested this on i915 (few different generation
>>> >> laptops), radeon, and msm.  And of course w/ ww debug (deadlock in-
>>> >> jection) enabled.  So I think all the locking related paths should
>>> >> be covered.
>>> >>
>>> >> I believe Thierry has tested a slighly older revision on tegra.  I
>>> >> would of course appreciate more testing on other drivers for which I
>>> >> don't have the hw.  But I think it is pretty much ready to go.  I do
>>> >> still owe some docs updates, I will send some patches for that in
>>> >> the near future, but didn't want to hold up giving others a chance
>>> >> to start banging on this.
>>> >
>>> > Ok, I've done a fairly cursory look at this only and tried to concentrate
>>> > on grasping the high-level details. Patches contain a bunch of comments on
>>> > the details, but here is the big stuff.
>>> >
>>> > First things first there's a bunch of prep work and refactoring sprinkled
>>> > throughout the series. Imo we should pick those out and rebase them on top
>>> > of drm-next asap to get them in before the actual interfaces. I hope I've
>>> > catched them all and commented everywhere about what imo is still missing
>>> > for merging. With that out of the way the real atomic review below.
>>> >
>>> > I like the overall design. It will be a royal pain to convert i915 over to
>>> > this since thus far we've simply tucked away the staged state into
>>> > obj->new_foo pointers. Which means we get to change the entire driver
>>> > again ;-)
>>> >
>>> > But I think the interfaces to driver and overall api design need some good
>>> > polish:
>>> >
>>> > - Imo way too much is done in driver callbacks, which then again call
>>> >   helpers. Parsing of the core properties for plane/crtcs should be done
>>> >   in the core imo. This way drivers only need to register ->set_prop
>>> >   callbacks if there's a driver-specific property they support.
>>>
>>> Pretty universally, the approach I took was to provide default fxns
>>> (for ->atomic_xyz(), ->set_property(), ->create_state(), etc), and let
>>> drivers wrap them as needed.  We might need to tweak
>>> drm_atomic_begin() a bit for the first driver that needs to wrap
>>> drm_atomic_state, but I guess we can tackle that as the need arises.
>>>
>>> > - Imo those obj->set_prop callbacks should take the object-specific
>>> >   drm_obj_state object, not the global atomic state structure. Will lead
>>> >   to _much_ less lookup code duplicated all over the place. If we then
>>> >   also add a state->obj pointer we could also ditch that parameter. Ofc
>>> >   obj would be specific to the state at hand.
>>>
>>> The problem is if those callbacks need to take other driver shared
>>> locks.. that plus initially I was trying to keep state as opaque as
>>> possible (in case sooner or later i915 decided to re-implement :-P)
>>>
>>> Eventually I decided keeping state opaque was too much pita, and that
>>> if driver wanted to do something different, it should instead
>>> subclass.  So I guess these days they could chase [pc]state->state to
>>> get back at the global state.
>>
>> I don't think keeping state opaque is a feature. Imo we _want_ to have
>> some common structures to track the common set of properties, for
>> otherwise insanity will rule. Drivers can add whatever they whish by
>> subclassing the existing state structs and add whatever they need to track
>> their state. I don't have any intentions to implement something
>> newfangled/different for i915, but instead want to port our current
>> infrastructure over to this.
>>
>> Having clear prepare&check (we call it compute) and commit semantics is
>> imo the right way to do kms. Think of i915 as the poc vehicle that gets to
>> pay the bill of rewriting the driver twice.
>>
>> So I'm advocating to move even further avoid from void* everywhere than
>> you've moved already.
>>
>> Wrt helpers I think at least for modeset operations we can pimp the crtc
>> helpers and mostly reuse the hooks we already have, maybe augmented with
>> per-object ->check hooks. But for nuclear pageflips I really don't see a
>> chance but a single driver-gloabl ->commit (and fwiw check) hook. Maybe
>> per-crtc at best, but that will already run into planes changing crtcs.
>
> The per-object hooks will work with at least three display controller
> blocks that I know of (two in msm, and omapdrm), so I guess it is not
> *that* uncommon.
>
> We'll need to split up an existing fxn or two so that other drivers
> can do an all-at-once commit more easily.  That seems like a fairly
> small tweak, and I think it would be ok to merge along w/ i915 atomic
> support.

I'm working on a reply to your msm patch. I think it's better we'll
move that discussion over there, where we have more context with
actual code.

>>> > - One downside of that is that drivers won't be able to interfere the
>>> >   allocation step any more. I think we'd need an additional
>>> >   ->alloc_atomic_state call so that drivers can still easily subclass some
>>> >   objects with their own type.
>>>
>>> the ->create_state() is already split out as a vfunc for planes and
>>> crtcs.  Which I think should be enough to cover most of what drivers
>>> need.  If driver needs to subclass global state too, then we need to
>>> split up drm_atomic_begin(), but I guess that should be a relatively
>>> small change that we can make when it becomes needed.
>>
>> I guess i915 will need it, to keep track of shared resources like plls.
>> Atm we carry them around in dev_priv, without any precompute/commit
>> semantics. Still something that needs to be fixed ...
>
> it would seem tempting to put in i915_atomic_state, but then how does
> it work with multiple parallel but independent atomic updates, or
> should that not be allowed?

Once we've entered the check/commit hooks all locks should have been
acquired (or will get acquired at the beginning of the check hook), so
there's no bothersome parallelism I think. Wrt the i915 implementation
I think the approach I'll try is:

1. In the set_prop stage _only_ store data in the <obj>_state
structures and nowhere else, not touching any data reachable from a
2nd thread.

2. In the check hook put all the state into <obj>->new_state pointers.
At that point we hold all the locks so can do that.

3. Run the ->compute_config hooks over all relevant objects. We need
to change all the code from directly looking into its object's
structure to go through the new_state indirection (e.g. for figuring
out whether we should enable hdmi audio or similar stuff).

4. Bail out and undo al new_foo pointer changes when only checking,
run the full hw commit code when doing a real atomic update.

That approach should hug the current implementation closely wrt the
->new_foo handling, but still tightly integrate with the free-floating
state construction atomic wants. The conversion will not be pretty
though :(

>> Wrt ->create_state: I simply missed that in all the patches. But if you
>> have that I don't quite understand why the various ->set_property
>> callbacks have checks whether the object-specific state exists already and
>> allocate it if not through the get_plane_state and similar functions. Imo
>> that kind of stuff should be handled in the core. Error handling code is
>> really hard to get correct ime, and we depend upon it's correctness here
>> (at least with the current locking scheme) for ww mutexes to work.
>
> Well, the current approach gave the driver more flexibility about what
> locks to grab, etc.. although I suppose some of this might be less
> needed now with primary planes.  Formerly I had to fwd some properties
> from crtc to my own internal primary plane, which required the
> flexibility that the current approach gives.

I think if there's any private ww mutex locks drivers need to acquire,
they should do that in their check/commit hook. Not while parsing
properties. From that point on it's all under the control of the
driver, so might not even need a full ww mutex, but a plain lock that
nests within all the kms ww mutexes might be good enough.

>>> > - There's imo a bit a confusion between what's helper and what's driver
>>> >   api. My big gripe here is with the set_prop stuff which imo really
>>> >   should be core and not helpers. The default ->commit/check
>>> >   implementations otoh should be more clearly delineated as helper
>>> >   implementations that drivers can/should overwrite. I think we should
>>> >   split drm_atomic.c into drm_atomic.c (with the official pieces) and
>>> >   drm_atomic_helper.c (with the suggested standard/transitional
>>> >   implemenations for commit/check). Helper functions should have the
>>> >   drm_atomic_helper_ prefix.
>>>
>>> Hmm, I think nearly *everything* could be overridden.. maybe it could
>>> be more clear what to override vs outright replace.  But otoh I'm not
>>> quite sure yet what drivers will need to override (other than some
>>> obvious ones, which are already broken out into vfuncs, like
>>> {plane,crtc}->create_state() to handle driver custom properties).
>>
>> Like I've said above we should imo try to unify the world a bit more
>> again. And for the state tracking that should be possible, so I don't see
>> a need to hand drivers too much rope.
>>
>> The check/commit stuff otoh should be fully overrideable, since i915 will
>> need that.
>>
>>> So I expect some tweaks as other drivers start adding native atomic
>>> support.  I basically added what I needed for msm, and what I thought
>>> was pretty safe bet that other drivers would need.  This at least
>>> gives us something other drivers could start trying to use.  Then see
>>> what is missing and add it as we go.  At least that was my line of
>>> thinking.
>>
>> Like I've said I don't think there's much value beyong making crtc helpers
>> atomic capable for modeset changes. Atomic pageflips pretty much need
>> driver-specific magic (less so if there's a "GO" bit like on sane
>> hardware).
>
> right, there is a very tiny bit of driver magic in msm_atomic_commit()..
>
> anyways, I don't disagree that we probably need to add something for
> some drivers to be able to hook in to ->atomic_commit() after locking
> magic but before loop over planes/crtcs.  I think that would simply be
> addition to the api, ie. new fxn ptr a driver could populate.  I
> wouldn't need it for msm.

If you mean new functions for the atomic helper, I agree (and don't
really care). If you mean new functions for the core->driver interface
(i.e. what i915 gets to implement on it's own) then if the
check/commit hooks really should carry all the information we need. In
the end this boils down to my request to more clearly separate
core->driver interface from helper code.

>>> > - I also think we should split the vfuncs like we do with the crtc
>>> >   helpers. Imo the core interface should just be an ->alloc_state and
>>> >   ->set_prop on all kms objects, plus a global ->prep/check/commit at the
>>> >   driver level. The object-specific ->check/commit hooks otoh should be
>>> >   part of the atomic helper library and in some separate
>>> >   atomic_helper_funcs structure.
>>>
>>> this is in fact the way it is, although 'struct drm_atomic_funcs'
>>> maybe should have "helper" in the name.
>>
>> Yeah, I'm mostly just asking for sprinkling helper all over the stuff that
>> clearly is helper code. And moving everything else into the core and
>> making it non-overrideable.
>>
>>> >  Imo we could either extend the sturcture
>>> >   used by the crtc helpers (hackish imo) or change the type of that to
>>> >   make it clear it's for the crtc helpers and add a new pointer for atomic
>>> >   helpers. E.g. in i915 I expect that we'll implement our very own
>>> >   ->check/commit hooks using our own compute_config infrastructure. Maybe
>>> >   we could switch to the ->check hooks of the atomic helper eventually,
>>> >   but that will be a lot of work. And in any case we won't ever switch to
>>> >   the ->commit hook as you have it in your helpers since for truly atomic
>>> >   flips/modesets we need to interleave all the updates of all objects in
>>> >   various phases.
>>>
>>> not sure if it would work for you, but in msm I have a per-crtc "don't
>>> actually commit yet" bit, which gets set for each crtc involved in
>>> atomic update.  Although in my case it is mainly a matter of not
>>> touching the "GO" bits until everything is setup..
>>>
>>> If not, I guess it shouldn't be a big deal to, for example, split up
>>> atomic_commit() into part that did the locking dance and then call
>>> vfunc ->atomic_apply_state() or something like that.
>>>
>>> Basically figure out where you need to hook in for i915 as you add
>>> native atomic support.  By the time you and maybe one or two other
>>> drivers have added atomic support we should have it pretty well nailed
>>> down.  But until then, there might be some small adjustments
>>> here/there.
>>
>> For modeset changes we need to munge everything through the full state
>> precompute and then commit to hw machinery. Not yet fully there (since the
>> shared dpll stuff doesn't work yet). For nuclear pageflips we're only just
>> starting to merge all of Ville's infrastructure, but will probably be the
>> same. So imo the only hand-off point between the core and drm is the
>> single, global ->commit. ->check would be the identical code, except for
>> the final commit.
>>
>> All the ->set_prop callbacks would do nothing else but store data into
>> structures.
>
> I suspect I'll want to see which core properties change eventually..
> when I have a chance to implement YUV and scaling, I'd want to flag
> that scaling/csc coefficients need updating, etc.

Hm, my idea was that drivers would implement any state diffing in
their ->check hooks. Only then we
- hold all the locks, so know nothing relevant will change
- have the entire picture of all states (and the often depend on each
another in funny ways).

So imo all the set_prop code should _only_ deal with creating the
in-kernel representation of the desired state, not with checking it or
computing a whole lot of derived date (like which parts need to be
updated in hw).

> It doesn't seem unreasonable to pass plane/crtc state rather than
> global state.  (Although we would need connector_state.. which is
> something I've not found a good use for yet.)  But other than that,
> I'd kinda like to leave the ->set_property() as it is.

Oh, we'll need that definitely. At least on i915 we have a pile of
connector properties which we need to track somehow, and most of those
need a full modeset to put them into the hw (mostly disable display
pipe, frob hw, reenable display pipe with the same state).
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-24 18:30 ` [PATCH 14/17] drm/msm: add atomic support Rob Clark
@ 2014-05-26 17:54   ` Daniel Vetter
  2014-05-27 15:58     ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-26 17:54 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel

On Sat, May 24, 2014 at 02:30:23PM -0400, Rob Clark wrote:
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> ---
>  drivers/gpu/drm/msm/Makefile              |   1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c  |  57 ++++++------
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c   |   6 ++
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h   |   1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c |   5 --
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c  |  56 ++++++------
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c   |   6 ++
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h   |   2 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c |   5 --
>  drivers/gpu/drm/msm/msm_atomic.c          | 141 ++++++++++++++++++++++++++++++
>  drivers/gpu/drm/msm/msm_drv.c             |  22 ++++-
>  drivers/gpu/drm/msm/msm_drv.h             |   7 ++
>  drivers/gpu/drm/msm/msm_gem.c             |  24 +----
>  drivers/gpu/drm/msm/msm_gem.h             |  13 +++
>  drivers/gpu/drm/msm/msm_kms.h             |   1 +
>  15 files changed, 253 insertions(+), 94 deletions(-)
>  create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
> 
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 5e1e6b0..dd12f56 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -27,6 +27,7 @@ msm-y := \
>  	mdp/mdp5/mdp5_kms.o \
>  	mdp/mdp5/mdp5_plane.o \
>  	mdp/mdp5/mdp5_smp.o \
> +	msm_atomic.o \
>  	msm_drv.o \
>  	msm_fb.o \
>  	msm_gem.o \
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> index d0d8befd..2fa6b75 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> @@ -52,7 +52,6 @@ struct mdp4_crtc {
>  
>  	/* if there is a pending flip, these will be non-null: */
>  	struct drm_pending_vblank_event *event;
> -	struct msm_fence_cb pageflip_cb;
>  
>  #define PENDING_CURSOR 0x1
>  #define PENDING_FLIP   0x2
> @@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
>  	mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank);
>  }
>  
> -static void crtc_flush(struct drm_crtc *crtc)
> +void mdp4_crtc_flush(struct drm_crtc *crtc)
>  {
> +	struct msm_drm_private *priv = crtc->dev->dev_private;
>  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
>  	struct mdp4_kms *mdp4_kms = get_kms(crtc);
>  	uint32_t i, flush = 0;
>  
> +	if (priv->pending_crtcs & (1 << crtc->id))
> +		return;
> +
>  	for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
>  		struct drm_plane *plane = mdp4_crtc->planes[i];
>  		if (plane) {
> @@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
>  	 * so that we can safely queue unref to current fb (ie. next
>  	 * vblank we know hw is done w/ previous scanout_fb).
>  	 */
> -	crtc_flush(crtc);
> +	mdp4_crtc_flush(crtc);
>  
>  	if (mdp4_crtc->scanout_fb)
>  		drm_flip_work_queue(&mdp4_crtc->unref_fb_work,
> @@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
>  	spin_unlock_irqrestore(&dev->event_lock, flags);
>  }
>  
> -static void pageflip_cb(struct msm_fence_cb *cb)
> -{
> -	struct mdp4_crtc *mdp4_crtc =
> -		container_of(cb, struct mdp4_crtc, pageflip_cb);
> -	struct drm_crtc *crtc = &mdp4_crtc->base;
> -	struct drm_framebuffer *fb = crtc->primary->fb;
> -
> -	if (!fb)
> -		return;
> -
> -	drm_framebuffer_reference(fb);
> -	mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
> -	update_scanout(crtc, fb);
> -}
> -
>  static void unref_fb_worker(struct drm_flip_work *work, void *val)
>  {
>  	struct mdp4_crtc *mdp4_crtc =
> @@ -406,7 +394,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));
>  }
> @@ -448,23 +436,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
>  {
>  	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
>  	struct drm_device *dev = crtc->dev;
> -	struct drm_gem_object *obj;
>  	unsigned long flags;
>  
> +	spin_lock_irqsave(&dev->event_lock, flags);
>  	if (mdp4_crtc->event) {
>  		dev_err(dev->dev, "already pending flip!\n");
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
>  		return -EBUSY;
>  	}
>  
> -	obj = msm_framebuffer_bo(new_fb, 0);
> -
> -	spin_lock_irqsave(&dev->event_lock, flags);
>  	mdp4_crtc->event = event;
>  	spin_unlock_irqrestore(&dev->event_lock, flags);
>  
>  	update_fb(crtc, new_fb);
>  
> -	return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
> +
> +	/* grab extra ref for update_scanout() */
> +	drm_framebuffer_reference(new_fb);
> +	mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb);
> +	update_scanout(crtc, new_fb);
> +
> +	return 0;
>  }
>  
>  static int mdp4_crtc_set_property(struct drm_crtc *crtc,
> @@ -598,7 +590,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>  	mdp4_crtc->cursor.y = y;
>  	spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
>  
> -	crtc_flush(crtc);
> +	mdp4_crtc_flush(crtc);
>  	request_pending(crtc, PENDING_CURSOR);
>  
>  	return 0;
> @@ -635,8 +627,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
>  	pending = atomic_xchg(&mdp4_crtc->pending, 0);
>  
>  	if (pending & PENDING_FLIP) {
> -		complete_flip(crtc, NULL);
> -		drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
> +		if (priv->pending_crtcs & (1 << crtc->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) {
> @@ -650,7 +647,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
>  	struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);
>  	struct drm_crtc *crtc = &mdp4_crtc->base;
>  	DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
> -	crtc_flush(crtc);
> +	mdp4_crtc_flush(crtc);
>  }
>  
>  uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
> @@ -730,7 +727,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)
> @@ -792,8 +789,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
>  	ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
>  			"unref cursor", unref_cursor_worker);
>  
> -	INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
> -
>  	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);
>  	drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
>  
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
> index 0bb4faa..af0bfb1 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
> @@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
>  	return mdp4_dtv_round_pixclk(encoder, rate);
>  }
>  
> +static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc)
> +{
> +	mdp4_crtc_flush(crtc);
> +}
> +
>  static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
>  {
>  	struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
> @@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = {
>  		.disable_vblank  = mdp4_disable_vblank,
>  		.get_format      = mdp_get_format,
>  		.round_pixclk    = mdp4_round_pixclk,
> +		.flush           = mdp4_flush,
>  		.preclose        = mdp4_preclose,
>  		.destroy         = mdp4_destroy,
>  	},
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
> index 715520c5..834454c 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
> @@ -184,6 +184,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane);
>  struct drm_plane *mdp4_plane_init(struct drm_device *dev,
>  		enum mdp4_pipe pipe_id, bool private_plane);
>  
> +void mdp4_crtc_flush(struct drm_crtc *crtc);
>  uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
>  void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
>  void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> index 4c92985..f35848d 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
> @@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
>  
>  	mdp4_plane->enabled = true;
>  
> -	if (plane->fb)
> -		drm_framebuffer_unreference(plane->fb);
> -
> -	drm_framebuffer_reference(fb);
> -
>  	return mdp4_plane_mode_set(plane, crtc, fb,
>  			crtc_x, crtc_y, crtc_w, crtc_h,
>  			src_x, src_y, src_w, src_h);
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> index 7f4ee99..0e74317 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> @@ -35,7 +35,6 @@ struct mdp5_crtc {
>  
>  	/* if there is a pending flip, these will be non-null: */
>  	struct drm_pending_vblank_event *event;
> -	struct msm_fence_cb pageflip_cb;
>  
>  #define PENDING_CURSOR 0x1
>  #define PENDING_FLIP   0x2
> @@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
>  	mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
>  }
>  
> -static void crtc_flush(struct drm_crtc *crtc)
> +void mdp5_crtc_flush(struct drm_crtc *crtc)
>  {
> +	struct msm_drm_private *priv = crtc->dev->dev_private;
>  	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
>  	struct mdp5_kms *mdp5_kms = get_kms(crtc);
>  	int id = mdp5_crtc->id;
>  	uint32_t i, flush = 0;
>  
> +	if (priv->pending_crtcs & (1 << crtc->id))
> +		return;
> +
>  	for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
>  		struct drm_plane *plane = mdp5_crtc->planes[i];
>  		if (plane) {
> @@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
>  	 * so that we can safely queue unref to current fb (ie. next
>  	 * vblank we know hw is done w/ previous scanout_fb).
>  	 */
> -	crtc_flush(crtc);
> +	mdp5_crtc_flush(crtc);
>  
>  	if (mdp5_crtc->scanout_fb)
>  		drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
> @@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
>  	}
>  }
>  
> -static void pageflip_cb(struct msm_fence_cb *cb)
> -{
> -	struct mdp5_crtc *mdp5_crtc =
> -		container_of(cb, struct mdp5_crtc, pageflip_cb);
> -	struct drm_crtc *crtc = &mdp5_crtc->base;
> -	struct drm_framebuffer *fb = mdp5_crtc->fb;
> -
> -	if (!fb)
> -		return;
> -
> -	drm_framebuffer_reference(fb);
> -	mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
> -	update_scanout(crtc, fb);
> -}
> -
>  static void unref_fb_worker(struct drm_flip_work *work, void *val)
>  {
>  	struct mdp5_crtc *mdp5_crtc =
> @@ -324,7 +312,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc)
>  static void mdp5_crtc_commit(struct drm_crtc *crtc)
>  {
>  	mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
> -	crtc_flush(crtc);
> +	mdp5_crtc_flush(crtc);
>  	/* drop the ref to mdp clk's that we got in prepare: */
>  	mdp5_disable(get_kms(crtc));
>  }
> @@ -366,23 +354,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
>  {
>  	struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
>  	struct drm_device *dev = crtc->dev;
> -	struct drm_gem_object *obj;
>  	unsigned long flags;
>  
> +	spin_lock_irqsave(&dev->event_lock, flags);
>  	if (mdp5_crtc->event) {
>  		dev_err(dev->dev, "already pending flip!\n");
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
>  		return -EBUSY;
>  	}
>  
> -	obj = msm_framebuffer_bo(new_fb, 0);
> -
> -	spin_lock_irqsave(&dev->event_lock, flags);
>  	mdp5_crtc->event = event;
>  	spin_unlock_irqrestore(&dev->event_lock, flags);
>  
>  	update_fb(crtc, new_fb);
>  
> -	return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb);
> +	/* grab extra ref for update_scanout() */
> +	drm_framebuffer_reference(new_fb);
> +	mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb);
> +	update_scanout(crtc, new_fb);
> +
> +	return 0;
>  }
>  
>  static int mdp5_crtc_set_property(struct drm_crtc *crtc,
> @@ -424,8 +415,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
>  	pending = atomic_xchg(&mdp5_crtc->pending, 0);
>  
>  	if (pending & PENDING_FLIP) {
> -		complete_flip(crtc, NULL);
> -		drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
> +		if (priv->pending_crtcs & (1 << crtc->id)) {
> +			/* our update hasn't been flushed yet: */
> +			request_pending(crtc, PENDING_FLIP);
> +		} else {
> +			complete_flip(crtc, NULL);
> +			drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
> +		}
>  	}
>  }
>  
> @@ -434,7 +430,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
>  	struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err);
>  	struct drm_crtc *crtc = &mdp5_crtc->base;
>  	DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
> -	crtc_flush(crtc);
> +	mdp5_crtc_flush(crtc);
>  }
>  
>  uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
> @@ -501,7 +497,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
>  			MDP5_CTL_OP_MODE(MODE_NONE) |
>  			MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
>  
> -	crtc_flush(crtc);
> +	mdp5_crtc_flush(crtc);
>  }
>  
>  static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
> @@ -517,7 +513,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
>  	mdp5_crtc->planes[pipe_id] = plane;
>  	blend_setup(crtc);
>  	if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
> -		crtc_flush(crtc);
> +		mdp5_crtc_flush(crtc);
>  }
>  
>  void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
> @@ -563,8 +559,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
>  	if (ret)
>  		goto fail;
>  
> -	INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
> -
>  	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
>  	drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
>  
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
> index ee8446c..01c3d70 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
> @@ -91,6 +91,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
>  	return rate;
>  }
>  
> +static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc)
> +{
> +	mdp5_crtc_flush(crtc);
> +}
> +
>  static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
>  {
>  	struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
> @@ -118,6 +123,7 @@ static const struct mdp_kms_funcs kms_funcs = {
>  		.disable_vblank  = mdp5_disable_vblank,
>  		.get_format      = mdp_get_format,
>  		.round_pixclk    = mdp5_round_pixclk,
> +		.flush           = mdp5_flush,
>  		.preclose        = mdp5_preclose,
>  		.destroy         = mdp5_destroy,
>  	},
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
> index c8b1a25..18b031b 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
> @@ -197,8 +197,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
>  struct drm_plane *mdp5_plane_init(struct drm_device *dev,
>  		enum mdp5_pipe pipe, bool private_plane);
>  
> +void mdp5_crtc_flush(struct drm_crtc *crtc);
>  uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
> -
>  void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
>  void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
>  		enum mdp5_intf intf_id);
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> index 53cc8c6..f1bf3c2 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
> @@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
>  
>  	mdp5_plane->enabled = true;
>  
> -	if (plane->fb)
> -		drm_framebuffer_unreference(plane->fb);
> -
> -	drm_framebuffer_reference(fb);
> -
>  	return mdp5_plane_mode_set(plane, crtc, fb,
>  			crtc_x, crtc_y, crtc_w, crtc_h,
>  			src_x, src_y, src_w, src_h);
> diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
> new file mode 100644
> index 0000000..b231377
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/msm_atomic.c
> @@ -0,0 +1,141 @@
> +/*
> + * Copyright (C) 2013 Red Hat
> + * Author: Rob Clark <robdclark@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "msm_drv.h"
> +#include "msm_kms.h"
> +#include "msm_gem.h"
> +
> +struct msm_async_commit {
> +	struct drm_atomic_state *state;
> +	uint32_t fence;
> +	struct msm_fence_cb fence_cb;
> +};
> +
> +static void fence_cb(struct msm_fence_cb *cb);
> +static int commit_sync(struct drm_device *dev, void *state, bool unlocked);
> +
> +static struct msm_async_commit *new_commit(struct drm_atomic_state *state)
> +{
> +	struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
> +
> +	if (!c)
> +		return NULL;
> +
> +	drm_atomic_state_reference(state);
> +	c->state = state;
> +	INIT_FENCE_CB(&c->fence_cb, fence_cb);
> +
> +	return c;
> +}
> +static void free_commit(struct msm_async_commit *c)
> +{
> +	drm_atomic_state_unreference(c->state);
> +	kfree(c);
> +}
> +
> +static void fence_cb(struct msm_fence_cb *cb)
> +{
> +	struct msm_async_commit *c =
> +			container_of(cb, struct msm_async_commit, fence_cb);
> +	commit_sync(c->state->dev, c->state, true);
> +	free_commit(c);
> +}
> +
> +static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc,
> +		struct drm_framebuffer *fb)
> +{
> +	struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0);
> +	c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
> +}
> +
> +static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb)
> +{
> +	// XXX TODO wait..
> +	return 0;
> +}
> +
> +#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb)
> +
> +static int commit_sync(struct drm_device *dev, void *state, bool unlocked)
> +{
> +	struct drm_atomic_state *a = state;
> +	struct msm_drm_private *priv = dev->dev_private;
> +	struct msm_kms *kms = priv->kms;
> +	int ncrtcs = dev->mode_config.num_crtc;
> +	uint32_t pending_crtcs = 0;
> +	int i, ret;
> +
> +	for (i = 0; i < ncrtcs; i++)
> +		if (a->crtcs[i])
> +			pending_crtcs |= (1 << a->crtcs[i]->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_commit_unlocked(dev, state);
> +	else
> +		ret = drm_atomic_commit(dev, state);
> +
> +	mutex_lock(&dev->struct_mutex);
> +	priv->pending_crtcs &= ~pending_crtcs;
> +	mutex_unlock(&dev->struct_mutex);
> +
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < ncrtcs; i++)
> +		if (a->crtcs[i])
> +			kms->funcs->flush(kms, a->crtcs[i]);
> +
> +	return 0;
> +}
> +
> +int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state)
> +{
> +	struct drm_atomic_state *a = state;
> +	int nplanes = dev->mode_config.num_total_plane;
> +	int i;
> +
> +	if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) {
> +		/* non-block mode: defer commit until fb's are ready */
> +		struct msm_async_commit *c = new_commit(state);
> +
> +		if (!c)
> +			return -ENOMEM;
> +
> +		for (i = 0; i < nplanes; i++)
> +			if (pending_fb(a->pstates[i]))
> +				add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb);
> +
> +		return msm_queue_fence_cb(dev, &c->fence_cb, c->fence);
> +	} else {
> +		/* blocking mode: wait until fb's are ready */
> +		int ret = 0;
> +
> +		for (i = 0; i < nplanes && !ret; i++)
> +			if (pending_fb(a->pstates[i]))
> +				ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb);
> +
> +		if (ret)
> +			return ret;
> +
> +		return commit_sync(dev, state, false);
> +	}
> +}

Ok, I think I should have read your msm implementation a _lot_ earlier.
Explains your desing choices neatly.

Two observations:

- A GO bit makes nuclear pageflips ridiculously easy to implement,
  presuming the hardware actually works. And it's the sane model, so imo a
  good one to wrap the atomic helpers around.

  But reality is often a lot more ugly, especially if you're employed by
  Intel. Which is why I'm harping so much on this helpers-vs-core
  interface issues ... We really need the full state transition in one
  piece to do anything useful.

- msm doesn't have any resource sharing going on for modeset operations
  (which I mean lighting up crtcs to feed pixel streams to connectors).
  Which means the simplistic "loop over all crtcs and call the old
  ->setcrtc" approach actually works.

  The problem I see here is that if you have hardware with more elaborate
  needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e.
  a GO bit) then your current atomic helpers will make it rather hard to
  mix this. So I think we should pimp the crtc helpers a bit to be atomic
  compliant (i.e. kill all outputs first before enabling anything new) and
  try to integrate this with the atomic helpers used for GO bit style
  updates.

  i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers
  anymore. But the radone eyefinity (or whatever the damn thing is called)
  I have lying around here fits the bill: It has 5 DP+ outputs but only 2
  dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI
  screens atomically and it should all pan out.

  But since your current helpers just loop over all crtcs without any
  regard to ordering constraints this will fall over if the 2 HDMI outputs
  get enabled before the 3 DP outputs get disabled all disabled.

So with all that in mind I have 3 sanity checks for the atomic interfaces
and helpers:

1. The atomic helpers should make enabling sane hw with a GO bit easy for
nuclear pageflips. Benchmark would be sane hw like msm or omap.

2. It should cooperate with the crtc helpers such that hw with some shared
resources (like dplls) works for atomic modesets. That pretty much means
"disable everything (including crtc/connetor pipelines that only changed
some parts) before enabling anything". Benchmark would be a platform with
shared dplls.

3. The core->driver interface should be powerful enough to support
insanity like i915, but no more. Which means all the code that's share
(i.e. the set_prop code I've been harping all over the place about) should
be done in the core, without any means for drivers to override. Currently
the drm core also takes care of a bunch of things like basic locking and
refcounting, and that's kinda the spirit for this. i915 is the obvious
benchmark here.

I think we can roll this out piecemeal (and it's probably better to do it
that way), but I also think that until we've resolved the requirements of
2&3 we should try to minimize subsystem wide changes as much as possible
by making them opt-in and the vfuncs optional.

If you compare this approach with my review for Matt's universal plane
patches it's exactly the same song.

I hope this elaboration of my thinking clarifies all my review comments a
bit and explains what I'm aiming for.

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

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-26 17:54   ` Daniel Vetter
@ 2014-05-27 15:58     ` Rob Clark
  2014-05-27 17:50       ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-27 15:58 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, May 24, 2014 at 02:30:23PM -0400, Rob Clark wrote:
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>> ---
>>  drivers/gpu/drm/msm/Makefile              |   1 +
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c  |  57 ++++++------
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c   |   6 ++
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h   |   1 +
>>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c |   5 --
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c  |  56 ++++++------
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c   |   6 ++
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h   |   2 +-
>>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c |   5 --
>>  drivers/gpu/drm/msm/msm_atomic.c          | 141 ++++++++++++++++++++++++++++++
>>  drivers/gpu/drm/msm/msm_drv.c             |  22 ++++-
>>  drivers/gpu/drm/msm/msm_drv.h             |   7 ++
>>  drivers/gpu/drm/msm/msm_gem.c             |  24 +----
>>  drivers/gpu/drm/msm/msm_gem.h             |  13 +++
>>  drivers/gpu/drm/msm/msm_kms.h             |   1 +
>>  15 files changed, 253 insertions(+), 94 deletions(-)
>>  create mode 100644 drivers/gpu/drm/msm/msm_atomic.c
>>
>> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
>> index 5e1e6b0..dd12f56 100644
>> --- a/drivers/gpu/drm/msm/Makefile
>> +++ b/drivers/gpu/drm/msm/Makefile
>> @@ -27,6 +27,7 @@ msm-y := \
>>       mdp/mdp5/mdp5_kms.o \
>>       mdp/mdp5/mdp5_plane.o \
>>       mdp/mdp5/mdp5_smp.o \
>> +     msm_atomic.o \
>>       msm_drv.o \
>>       msm_fb.o \
>>       msm_gem.o \
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
>> index d0d8befd..2fa6b75 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
>> @@ -52,7 +52,6 @@ struct mdp4_crtc {
>>
>>       /* if there is a pending flip, these will be non-null: */
>>       struct drm_pending_vblank_event *event;
>> -     struct msm_fence_cb pageflip_cb;
>>
>>  #define PENDING_CURSOR 0x1
>>  #define PENDING_FLIP   0x2
>> @@ -93,12 +92,16 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
>>       mdp_irq_register(&get_kms(crtc)->base, &mdp4_crtc->vblank);
>>  }
>>
>> -static void crtc_flush(struct drm_crtc *crtc)
>> +void mdp4_crtc_flush(struct drm_crtc *crtc)
>>  {
>> +     struct msm_drm_private *priv = crtc->dev->dev_private;
>>       struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
>>       struct mdp4_kms *mdp4_kms = get_kms(crtc);
>>       uint32_t i, flush = 0;
>>
>> +     if (priv->pending_crtcs & (1 << crtc->id))
>> +             return;
>> +
>>       for (i = 0; i < ARRAY_SIZE(mdp4_crtc->planes); i++) {
>>               struct drm_plane *plane = mdp4_crtc->planes[i];
>>               if (plane) {
>> @@ -142,7 +145,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
>>        * so that we can safely queue unref to current fb (ie. next
>>        * vblank we know hw is done w/ previous scanout_fb).
>>        */
>> -     crtc_flush(crtc);
>> +     mdp4_crtc_flush(crtc);
>>
>>       if (mdp4_crtc->scanout_fb)
>>               drm_flip_work_queue(&mdp4_crtc->unref_fb_work,
>> @@ -177,21 +180,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
>>       spin_unlock_irqrestore(&dev->event_lock, flags);
>>  }
>>
>> -static void pageflip_cb(struct msm_fence_cb *cb)
>> -{
>> -     struct mdp4_crtc *mdp4_crtc =
>> -             container_of(cb, struct mdp4_crtc, pageflip_cb);
>> -     struct drm_crtc *crtc = &mdp4_crtc->base;
>> -     struct drm_framebuffer *fb = crtc->primary->fb;
>> -
>> -     if (!fb)
>> -             return;
>> -
>> -     drm_framebuffer_reference(fb);
>> -     mdp4_plane_set_scanout(mdp4_crtc->plane, fb);
>> -     update_scanout(crtc, fb);
>> -}
>> -
>>  static void unref_fb_worker(struct drm_flip_work *work, void *val)
>>  {
>>       struct mdp4_crtc *mdp4_crtc =
>> @@ -406,7 +394,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));
>>  }
>> @@ -448,23 +436,27 @@ static int mdp4_crtc_page_flip(struct drm_crtc *crtc,
>>  {
>>       struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
>>       struct drm_device *dev = crtc->dev;
>> -     struct drm_gem_object *obj;
>>       unsigned long flags;
>>
>> +     spin_lock_irqsave(&dev->event_lock, flags);
>>       if (mdp4_crtc->event) {
>>               dev_err(dev->dev, "already pending flip!\n");
>> +             spin_unlock_irqrestore(&dev->event_lock, flags);
>>               return -EBUSY;
>>       }
>>
>> -     obj = msm_framebuffer_bo(new_fb, 0);
>> -
>> -     spin_lock_irqsave(&dev->event_lock, flags);
>>       mdp4_crtc->event = event;
>>       spin_unlock_irqrestore(&dev->event_lock, flags);
>>
>>       update_fb(crtc, new_fb);
>>
>> -     return msm_gem_queue_inactive_cb(obj, &mdp4_crtc->pageflip_cb);
>> +
>> +     /* grab extra ref for update_scanout() */
>> +     drm_framebuffer_reference(new_fb);
>> +     mdp4_plane_set_scanout(mdp4_crtc->plane, new_fb);
>> +     update_scanout(crtc, new_fb);
>> +
>> +     return 0;
>>  }
>>
>>  static int mdp4_crtc_set_property(struct drm_crtc *crtc,
>> @@ -598,7 +590,7 @@ static int mdp4_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
>>       mdp4_crtc->cursor.y = y;
>>       spin_unlock_irqrestore(&mdp4_crtc->cursor.lock, flags);
>>
>> -     crtc_flush(crtc);
>> +     mdp4_crtc_flush(crtc);
>>       request_pending(crtc, PENDING_CURSOR);
>>
>>       return 0;
>> @@ -635,8 +627,13 @@ static void mdp4_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
>>       pending = atomic_xchg(&mdp4_crtc->pending, 0);
>>
>>       if (pending & PENDING_FLIP) {
>> -             complete_flip(crtc, NULL);
>> -             drm_flip_work_commit(&mdp4_crtc->unref_fb_work, priv->wq);
>> +             if (priv->pending_crtcs & (1 << crtc->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) {
>> @@ -650,7 +647,7 @@ static void mdp4_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
>>       struct mdp4_crtc *mdp4_crtc = container_of(irq, struct mdp4_crtc, err);
>>       struct drm_crtc *crtc = &mdp4_crtc->base;
>>       DBG("%s: error: %08x", mdp4_crtc->name, irqstatus);
>> -     crtc_flush(crtc);
>> +     mdp4_crtc_flush(crtc);
>>  }
>>
>>  uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc)
>> @@ -730,7 +727,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)
>> @@ -792,8 +789,6 @@ struct drm_crtc *mdp4_crtc_init(struct drm_device *dev,
>>       ret = drm_flip_work_init(&mdp4_crtc->unref_cursor_work, 64,
>>                       "unref cursor", unref_cursor_worker);
>>
>> -     INIT_FENCE_CB(&mdp4_crtc->pageflip_cb, pageflip_cb);
>> -
>>       drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp4_crtc_funcs);
>>       drm_crtc_helper_add(crtc, &mdp4_crtc_helper_funcs);
>>
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
>> index 0bb4faa..af0bfb1 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
>> @@ -131,6 +131,11 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
>>       return mdp4_dtv_round_pixclk(encoder, rate);
>>  }
>>
>> +static void mdp4_flush(struct msm_kms *kms, struct drm_crtc *crtc)
>> +{
>> +     mdp4_crtc_flush(crtc);
>> +}
>> +
>>  static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file)
>>  {
>>       struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
>> @@ -162,6 +167,7 @@ static const struct mdp_kms_funcs kms_funcs = {
>>               .disable_vblank  = mdp4_disable_vblank,
>>               .get_format      = mdp_get_format,
>>               .round_pixclk    = mdp4_round_pixclk,
>> +             .flush           = mdp4_flush,
>>               .preclose        = mdp4_preclose,
>>               .destroy         = mdp4_destroy,
>>       },
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
>> index 715520c5..834454c 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
>> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h
>> @@ -184,6 +184,7 @@ enum mdp4_pipe mdp4_plane_pipe(struct drm_plane *plane);
>>  struct drm_plane *mdp4_plane_init(struct drm_device *dev,
>>               enum mdp4_pipe pipe_id, bool private_plane);
>>
>> +void mdp4_crtc_flush(struct drm_crtc *crtc);
>>  uint32_t mdp4_crtc_vblank(struct drm_crtc *crtc);
>>  void mdp4_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
>>  void mdp4_crtc_set_config(struct drm_crtc *crtc, uint32_t config);
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
>> index 4c92985..f35848d 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
>> @@ -48,11 +48,6 @@ static int mdp4_plane_update(struct drm_plane *plane,
>>
>>       mdp4_plane->enabled = true;
>>
>> -     if (plane->fb)
>> -             drm_framebuffer_unreference(plane->fb);
>> -
>> -     drm_framebuffer_reference(fb);
>> -
>>       return mdp4_plane_mode_set(plane, crtc, fb,
>>                       crtc_x, crtc_y, crtc_w, crtc_h,
>>                       src_x, src_y, src_w, src_h);
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
>> index 7f4ee99..0e74317 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
>> @@ -35,7 +35,6 @@ struct mdp5_crtc {
>>
>>       /* if there is a pending flip, these will be non-null: */
>>       struct drm_pending_vblank_event *event;
>> -     struct msm_fence_cb pageflip_cb;
>>
>>  #define PENDING_CURSOR 0x1
>>  #define PENDING_FLIP   0x2
>> @@ -73,13 +72,17 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
>>       mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
>>  }
>>
>> -static void crtc_flush(struct drm_crtc *crtc)
>> +void mdp5_crtc_flush(struct drm_crtc *crtc)
>>  {
>> +     struct msm_drm_private *priv = crtc->dev->dev_private;
>>       struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
>>       struct mdp5_kms *mdp5_kms = get_kms(crtc);
>>       int id = mdp5_crtc->id;
>>       uint32_t i, flush = 0;
>>
>> +     if (priv->pending_crtcs & (1 << crtc->id))
>> +             return;
>> +
>>       for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
>>               struct drm_plane *plane = mdp5_crtc->planes[i];
>>               if (plane) {
>> @@ -124,7 +127,7 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
>>        * so that we can safely queue unref to current fb (ie. next
>>        * vblank we know hw is done w/ previous scanout_fb).
>>        */
>> -     crtc_flush(crtc);
>> +     mdp5_crtc_flush(crtc);
>>
>>       if (mdp5_crtc->scanout_fb)
>>               drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
>> @@ -165,21 +168,6 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file)
>>       }
>>  }
>>
>> -static void pageflip_cb(struct msm_fence_cb *cb)
>> -{
>> -     struct mdp5_crtc *mdp5_crtc =
>> -             container_of(cb, struct mdp5_crtc, pageflip_cb);
>> -     struct drm_crtc *crtc = &mdp5_crtc->base;
>> -     struct drm_framebuffer *fb = mdp5_crtc->fb;
>> -
>> -     if (!fb)
>> -             return;
>> -
>> -     drm_framebuffer_reference(fb);
>> -     mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
>> -     update_scanout(crtc, fb);
>> -}
>> -
>>  static void unref_fb_worker(struct drm_flip_work *work, void *val)
>>  {
>>       struct mdp5_crtc *mdp5_crtc =
>> @@ -324,7 +312,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc)
>>  static void mdp5_crtc_commit(struct drm_crtc *crtc)
>>  {
>>       mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
>> -     crtc_flush(crtc);
>> +     mdp5_crtc_flush(crtc);
>>       /* drop the ref to mdp clk's that we got in prepare: */
>>       mdp5_disable(get_kms(crtc));
>>  }
>> @@ -366,23 +354,26 @@ static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
>>  {
>>       struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
>>       struct drm_device *dev = crtc->dev;
>> -     struct drm_gem_object *obj;
>>       unsigned long flags;
>>
>> +     spin_lock_irqsave(&dev->event_lock, flags);
>>       if (mdp5_crtc->event) {
>>               dev_err(dev->dev, "already pending flip!\n");
>> +             spin_unlock_irqrestore(&dev->event_lock, flags);
>>               return -EBUSY;
>>       }
>>
>> -     obj = msm_framebuffer_bo(new_fb, 0);
>> -
>> -     spin_lock_irqsave(&dev->event_lock, flags);
>>       mdp5_crtc->event = event;
>>       spin_unlock_irqrestore(&dev->event_lock, flags);
>>
>>       update_fb(crtc, new_fb);
>>
>> -     return msm_gem_queue_inactive_cb(obj, &mdp5_crtc->pageflip_cb);
>> +     /* grab extra ref for update_scanout() */
>> +     drm_framebuffer_reference(new_fb);
>> +     mdp5_plane_set_scanout(mdp5_crtc->plane, new_fb);
>> +     update_scanout(crtc, new_fb);
>> +
>> +     return 0;
>>  }
>>
>>  static int mdp5_crtc_set_property(struct drm_crtc *crtc,
>> @@ -424,8 +415,13 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
>>       pending = atomic_xchg(&mdp5_crtc->pending, 0);
>>
>>       if (pending & PENDING_FLIP) {
>> -             complete_flip(crtc, NULL);
>> -             drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
>> +             if (priv->pending_crtcs & (1 << crtc->id)) {
>> +                     /* our update hasn't been flushed yet: */
>> +                     request_pending(crtc, PENDING_FLIP);
>> +             } else {
>> +                     complete_flip(crtc, NULL);
>> +                     drm_flip_work_commit(&mdp5_crtc->unref_fb_work, priv->wq);
>> +             }
>>       }
>>  }
>>
>> @@ -434,7 +430,7 @@ static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
>>       struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err);
>>       struct drm_crtc *crtc = &mdp5_crtc->base;
>>       DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
>> -     crtc_flush(crtc);
>> +     mdp5_crtc_flush(crtc);
>>  }
>>
>>  uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
>> @@ -501,7 +497,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
>>                       MDP5_CTL_OP_MODE(MODE_NONE) |
>>                       MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
>>
>> -     crtc_flush(crtc);
>> +     mdp5_crtc_flush(crtc);
>>  }
>>
>>  static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
>> @@ -517,7 +513,7 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
>>       mdp5_crtc->planes[pipe_id] = plane;
>>       blend_setup(crtc);
>>       if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
>> -             crtc_flush(crtc);
>> +             mdp5_crtc_flush(crtc);
>>  }
>>
>>  void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
>> @@ -563,8 +559,6 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
>>       if (ret)
>>               goto fail;
>>
>> -     INIT_FENCE_CB(&mdp5_crtc->pageflip_cb, pageflip_cb);
>> -
>>       drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs);
>>       drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs);
>>
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
>> index ee8446c..01c3d70 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
>> @@ -91,6 +91,11 @@ static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
>>       return rate;
>>  }
>>
>> +static void mdp5_flush(struct msm_kms *kms, struct drm_crtc *crtc)
>> +{
>> +     mdp5_crtc_flush(crtc);
>> +}
>> +
>>  static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
>>  {
>>       struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
>> @@ -118,6 +123,7 @@ static const struct mdp_kms_funcs kms_funcs = {
>>               .disable_vblank  = mdp5_disable_vblank,
>>               .get_format      = mdp_get_format,
>>               .round_pixclk    = mdp5_round_pixclk,
>> +             .flush           = mdp5_flush,
>>               .preclose        = mdp5_preclose,
>>               .destroy         = mdp5_destroy,
>>       },
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
>> index c8b1a25..18b031b 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
>> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
>> @@ -197,8 +197,8 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
>>  struct drm_plane *mdp5_plane_init(struct drm_device *dev,
>>               enum mdp5_pipe pipe, bool private_plane);
>>
>> +void mdp5_crtc_flush(struct drm_crtc *crtc);
>>  uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);
>> -
>>  void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
>>  void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
>>               enum mdp5_intf intf_id);
>> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
>> index 53cc8c6..f1bf3c2 100644
>> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
>> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
>> @@ -48,11 +48,6 @@ static int mdp5_plane_update(struct drm_plane *plane,
>>
>>       mdp5_plane->enabled = true;
>>
>> -     if (plane->fb)
>> -             drm_framebuffer_unreference(plane->fb);
>> -
>> -     drm_framebuffer_reference(fb);
>> -
>>       return mdp5_plane_mode_set(plane, crtc, fb,
>>                       crtc_x, crtc_y, crtc_w, crtc_h,
>>                       src_x, src_y, src_w, src_h);
>> diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
>> new file mode 100644
>> index 0000000..b231377
>> --- /dev/null
>> +++ b/drivers/gpu/drm/msm/msm_atomic.c
>> @@ -0,0 +1,141 @@
>> +/*
>> + * Copyright (C) 2013 Red Hat
>> + * Author: Rob Clark <robdclark@gmail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include "msm_drv.h"
>> +#include "msm_kms.h"
>> +#include "msm_gem.h"
>> +
>> +struct msm_async_commit {
>> +     struct drm_atomic_state *state;
>> +     uint32_t fence;
>> +     struct msm_fence_cb fence_cb;
>> +};
>> +
>> +static void fence_cb(struct msm_fence_cb *cb);
>> +static int commit_sync(struct drm_device *dev, void *state, bool unlocked);
>> +
>> +static struct msm_async_commit *new_commit(struct drm_atomic_state *state)
>> +{
>> +     struct msm_async_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
>> +
>> +     if (!c)
>> +             return NULL;
>> +
>> +     drm_atomic_state_reference(state);
>> +     c->state = state;
>> +     INIT_FENCE_CB(&c->fence_cb, fence_cb);
>> +
>> +     return c;
>> +}
>> +static void free_commit(struct msm_async_commit *c)
>> +{
>> +     drm_atomic_state_unreference(c->state);
>> +     kfree(c);
>> +}
>> +
>> +static void fence_cb(struct msm_fence_cb *cb)
>> +{
>> +     struct msm_async_commit *c =
>> +                     container_of(cb, struct msm_async_commit, fence_cb);
>> +     commit_sync(c->state->dev, c->state, true);
>> +     free_commit(c);
>> +}
>> +
>> +static void add_fb(struct msm_async_commit *c, struct drm_crtc *crtc,
>> +             struct drm_framebuffer *fb)
>> +{
>> +     struct drm_gem_object *obj = msm_framebuffer_bo(fb, 0);
>> +     c->fence = max(c->fence, msm_gem_fence(to_msm_bo(obj), MSM_PREP_READ));
>> +}
>> +
>> +static int wait_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb)
>> +{
>> +     // XXX TODO wait..
>> +     return 0;
>> +}
>> +
>> +#define pending_fb(state) ((state) && (state)->fb && (state)->new_fb)
>> +
>> +static int commit_sync(struct drm_device *dev, void *state, bool unlocked)
>> +{
>> +     struct drm_atomic_state *a = state;
>> +     struct msm_drm_private *priv = dev->dev_private;
>> +     struct msm_kms *kms = priv->kms;
>> +     int ncrtcs = dev->mode_config.num_crtc;
>> +     uint32_t pending_crtcs = 0;
>> +     int i, ret;
>> +
>> +     for (i = 0; i < ncrtcs; i++)
>> +             if (a->crtcs[i])
>> +                     pending_crtcs |= (1 << a->crtcs[i]->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_commit_unlocked(dev, state);
>> +     else
>> +             ret = drm_atomic_commit(dev, state);
>> +
>> +     mutex_lock(&dev->struct_mutex);
>> +     priv->pending_crtcs &= ~pending_crtcs;
>> +     mutex_unlock(&dev->struct_mutex);
>> +
>> +     if (ret)
>> +             return ret;
>> +
>> +     for (i = 0; i < ncrtcs; i++)
>> +             if (a->crtcs[i])
>> +                     kms->funcs->flush(kms, a->crtcs[i]);
>> +
>> +     return 0;
>> +}
>> +
>> +int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state)
>> +{
>> +     struct drm_atomic_state *a = state;
>> +     int nplanes = dev->mode_config.num_total_plane;
>> +     int i;
>> +
>> +     if (a->flags & DRM_MODE_ATOMIC_NONBLOCK) {
>> +             /* non-block mode: defer commit until fb's are ready */
>> +             struct msm_async_commit *c = new_commit(state);
>> +
>> +             if (!c)
>> +                     return -ENOMEM;
>> +
>> +             for (i = 0; i < nplanes; i++)
>> +                     if (pending_fb(a->pstates[i]))
>> +                             add_fb(c, a->pstates[i]->crtc, a->pstates[i]->fb);
>> +
>> +             return msm_queue_fence_cb(dev, &c->fence_cb, c->fence);
>> +     } else {
>> +             /* blocking mode: wait until fb's are ready */
>> +             int ret = 0;
>> +
>> +             for (i = 0; i < nplanes && !ret; i++)
>> +                     if (pending_fb(a->pstates[i]))
>> +                             ret = wait_fb(a->pstates[i]->crtc, a->pstates[i]->fb);
>> +
>> +             if (ret)
>> +                     return ret;
>> +
>> +             return commit_sync(dev, state, false);
>> +     }
>> +}
>
> Ok, I think I should have read your msm implementation a _lot_ earlier.
> Explains your desing choices neatly.
>
> Two observations:
>
> - A GO bit makes nuclear pageflips ridiculously easy to implement,
>   presuming the hardware actually works. And it's the sane model, so imo a
>   good one to wrap the atomic helpers around.
>
>   But reality is often a lot more ugly, especially if you're employed by
>   Intel. Which is why I'm harping so much on this helpers-vs-core
>   interface issues ... We really need the full state transition in one
>   piece to do anything useful.
>
> - msm doesn't have any resource sharing going on for modeset operations
>   (which I mean lighting up crtcs to feed pixel streams to connectors).
>   Which means the simplistic "loop over all crtcs and call the old
>   ->setcrtc" approach actually works.

we do actually have some shared resources on mdp5 generation (the
"smp" blocks, basically a shared buffer which we need to allocate fifo
space from for each plane)..

I'm mostly ignoring this at the moment, because we don't support
enough crtc's yet to run out of smp blocks.  But hooking in at the
current ->atomic_commit() would be enough for me, I think.  Tbh, it is
something that should be handled at the ->atomic_check() stage so I
hadn't given much though to ->atomic_commit() stage.

That all said, I've no problem with adding one more hook, after
->atomic_commit() and lock magic, before loop over planes/crtcs, so
drivers that need to can replace the loops and do their own thing.
Current state should be reasonably good for sane hw.  I'm just waiting
for patches for i915 to add whatever it needs.

>   The problem I see here is that if you have hardware with more elaborate
>   needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e.
>   a GO bit) then your current atomic helpers will make it rather hard to
>   mix this. So I think we should pimp the crtc helpers a bit to be atomic
>   compliant (i.e. kill all outputs first before enabling anything new) and
>   try to integrate this with the atomic helpers used for GO bit style
>   updates.

Not really, I don't think.  You can still do whatever shared resource
setup in ->atomic_commit().  For drivers which want to defer commit
(ie. doing commit after fb's ready from cpu, rather than from gpu), it
would probably be more convenient to split up atomic_commit() so
drivers have a post lock hook.  I think it is just a few line patch,
and I think that it probably isn't really needed until i915 is ready.
I'm pretty sure we can change this to do what i915 needs, while at the
same time keeping it simple for 'GO bit' drivers.

The bit about crtc helpers, I'll have to think about.  I'm not sure
that we want to require that 'atomic' (modeset or pageflip) completely
*replaces* current state.  So disabling unrelated crtcs doesn't seem
like the right thing.  But we have some time to decide about the
semantics of an atomic modeset before we expose to userspace.

>   i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers
>   anymore. But the radone eyefinity (or whatever the damn thing is called)
>   I have lying around here fits the bill: It has 5 DP+ outputs but only 2
>   dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI
>   screens atomically and it should all pan out.
>
>   But since your current helpers just loop over all crtcs without any
>   regard to ordering constraints this will fall over if the 2 HDMI outputs
>   get enabled before the 3 DP outputs get disabled all disabled.

the driver should have rejected the config before it gets to commit
stage, if it were an impossible config.

> So with all that in mind I have 3 sanity checks for the atomic interfaces
> and helpers:
>
> 1. The atomic helpers should make enabling sane hw with a GO bit easy for
> nuclear pageflips. Benchmark would be sane hw like msm or omap.
>
> 2. It should cooperate with the crtc helpers such that hw with some shared
> resources (like dplls) works for atomic modesets. That pretty much means
> "disable everything (including crtc/connetor pipelines that only changed
> some parts) before enabling anything". Benchmark would be a platform with
> shared dplls.

btw, not something I'd thought about before, but shared dplls seem
common enough, that it is something core could help with.  (Assuming
they are all related to pixel clk and not some derived thing that core
wouldn't know about.)

I think we do need to decide what partial state updates me in the
context of modeset or pageflip.  I kinda think the right thing is
different for modeset vs pageflip.  Maybe we want to define an atomic
flag to mean "disable/discard everything else"..  at any rate, we only
need to sort that before exposing the API to userspace.

> 3. The core->driver interface should be powerful enough to support
> insanity like i915, but no more. Which means all the code that's share
> (i.e. the set_prop code I've been harping all over the place about) should
> be done in the core, without any means for drivers to override. Currently
> the drm core also takes care of a bunch of things like basic locking and
> refcounting, and that's kinda the spirit for this. i915 is the obvious
> benchmark here.

The more I think about it, the more I think we should leave set_prop
as it is (although possibly with drm_atomic_state ->
drm_{plane,crtc,etc}_state).

In the past, before primary plane, I really needed this.  And I expect
having a convenient way to 'sniff' changing properties as they go by
will be useful in some cases.

> I think we can roll this out piecemeal (and it's probably better to do it
> that way), but I also think that until we've resolved the requirements of
> 2&3 we should try to minimize subsystem wide changes as much as possible
> by making them opt-in and the vfuncs optional.

the new mandatory vfuncs don't amount to too much churn.. not as much
as the extra param for crtc lock/unlock fxns.  Maybe if I was planning
on keeping this alive on a branch for so long in the beginning I would
have arranged a few things slightly different, but meh.

A very early iteration of atomic preserved the old pageflip, setcrtc,
setplane, etc paths for drivers not implementing atomic fxns.  But
that was at least a bit ugly.  I like the current approach since the
code paths are very much the same for atomic and non-atomic.

BR,
-R

> If you compare this approach with my review for Matt's universal plane
> patches it's exactly the same song.
>
> I hope this elaboration of my thinking clarifies all my review comments a
> bit and explains what I'm aiming for.
>
> Cheers, Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 15:58     ` Rob Clark
@ 2014-05-27 17:50       ` Daniel Vetter
  2014-05-27 18:48         ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-27 17:50 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
> On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > Ok, I think I should have read your msm implementation a _lot_ earlier.
> > Explains your desing choices neatly.
> >
> > Two observations:
> >
> > - A GO bit makes nuclear pageflips ridiculously easy to implement,
> >   presuming the hardware actually works. And it's the sane model, so imo a
> >   good one to wrap the atomic helpers around.
> >
> >   But reality is often a lot more ugly, especially if you're employed by
> >   Intel. Which is why I'm harping so much on this helpers-vs-core
> >   interface issues ... We really need the full state transition in one
> >   piece to do anything useful.
> >
> > - msm doesn't have any resource sharing going on for modeset operations
> >   (which I mean lighting up crtcs to feed pixel streams to connectors).
> >   Which means the simplistic "loop over all crtcs and call the old
> >   ->setcrtc" approach actually works.
> 
> we do actually have some shared resources on mdp5 generation (the
> "smp" blocks, basically a shared buffer which we need to allocate fifo
> space from for each plane)..
> 
> I'm mostly ignoring this at the moment, because we don't support
> enough crtc's yet to run out of smp blocks.  But hooking in at the
> current ->atomic_commit() would be enough for me, I think.  Tbh, it is
> something that should be handled at the ->atomic_check() stage so I
> hadn't given much though to ->atomic_commit() stage.
> 
> That all said, I've no problem with adding one more hook, after
> ->atomic_commit() and lock magic, before loop over planes/crtcs, so
> drivers that need to can replace the loops and do their own thing.
> Current state should be reasonably good for sane hw.  I'm just waiting
> for patches for i915 to add whatever it needs.

I don't think that will be enough for you. Example, slightly hypothetical:

- Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen.
  Together they just barely fit into your fifo space.

- Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one.
  Presume different outputs here so that no implicit output stealing
  happens upon mode switching.

Atomic switch should work, but don't since if you just loop over crtcs you
have the intermediate stage where you try to drive 2 giant screens, which
will run out of fifo resources. And I think it's really common to have
such implicit resource sharing, maybe except for the most beefy desktop
gpu which simply can't run out of memory bw with today's screen.

So afaics you really need to push a bit of smarts into the crtc helpers to
make atomic possible, which then leaves you with interaction issues
between the atomic stuff for GO bit capable hw for plane updates and the
modeset ordering hell.

> >   The problem I see here is that if you have hardware with more elaborate
> >   needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e.
> >   a GO bit) then your current atomic helpers will make it rather hard to
> >   mix this. So I think we should pimp the crtc helpers a bit to be atomic
> >   compliant (i.e. kill all outputs first before enabling anything new) and
> >   try to integrate this with the atomic helpers used for GO bit style
> >   updates.
> 
> Not really, I don't think.  You can still do whatever shared resource
> setup in ->atomic_commit().  For drivers which want to defer commit
> (ie. doing commit after fb's ready from cpu, rather than from gpu), it
> would probably be more convenient to split up atomic_commit() so
> drivers have a post lock hook.  I think it is just a few line patch,
> and I think that it probably isn't really needed until i915 is ready.
> I'm pretty sure we can change this to do what i915 needs, while at the
> same time keeping it simple for 'GO bit' drivers.
>
> The bit about crtc helpers, I'll have to think about.  I'm not sure
> that we want to require that 'atomic' (modeset or pageflip) completely
> *replaces* current state.  So disabling unrelated crtcs doesn't seem
> like the right thing.  But we have some time to decide about the
> semantics of an atomic modeset before we expose to userspace.

I'm not talking about replacing unrelated crtcs. It's also not about
underspecified state or about enabling/changing global resources.

It is _only_ about ordering of the various operations: If both the
current and the desired new configuration are at the limit of what the hw
can do you can't switch to the new configuration by looping over all
crtcs. The fact that this doesn't work is the entire point of atomic
modesets. And if we have helpers which aren't cut out for the task at hand
for any hw where we need to have atomic modesets to make such changes
possible without userspace going nuts, then the helpers are imo simply not
useful

> >   i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers
> >   anymore. But the radone eyefinity (or whatever the damn thing is called)
> >   I have lying around here fits the bill: It has 5 DP+ outputs but only 2
> >   dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI
> >   screens atomically and it should all pan out.
> >
> >   But since your current helpers just loop over all crtcs without any
> >   regard to ordering constraints this will fall over if the 2 HDMI outputs
> >   get enabled before the 3 DP outputs get disabled all disabled.
> 
> the driver should have rejected the config before it gets to commit
> stage, if it were an impossible config.

The configuration _is_ possible. We simply have to be somewhat careful
with transitioning to it, since not _all_ intermediate states are
possible. Your current helpers presume that's the case, which afaik isn't
the case on any hw where we have global limits. For modesets.

Nuclear pageflips are a completely different thing, as long as your hw has
a GO bit.

> > So with all that in mind I have 3 sanity checks for the atomic interfaces
> > and helpers:
> >
> > 1. The atomic helpers should make enabling sane hw with a GO bit easy for
> > nuclear pageflips. Benchmark would be sane hw like msm or omap.
> >
> > 2. It should cooperate with the crtc helpers such that hw with some shared
> > resources (like dplls) works for atomic modesets. That pretty much means
> > "disable everything (including crtc/connetor pipelines that only changed
> > some parts) before enabling anything". Benchmark would be a platform with
> > shared dplls.
> 
> btw, not something I'd thought about before, but shared dplls seem
> common enough, that it is something core could help with.  (Assuming
> they are all related to pixel clk and not some derived thing that core
> wouldn't know about.)

Yup, that's what I'm trying to say ;-) But it was just an example, I
believe that atm your helpers don't help for any shared modeset resources
at all.

> I think we do need to decide what partial state updates me in the
> context of modeset or pageflip.  I kinda think the right thing is
> different for modeset vs pageflip.  Maybe we want to define an atomic
> flag to mean "disable/discard everything else"..  at any rate, we only
> need to sort that before exposing the API to userspace.

Yeah, I still strongly support this split in the api itself. For i915 my
plan is to have separate configuration structures for modeset state and
pageflip/plane config state. When we do an atomic modeset we compute both
(maybe with some shortcut if we know that the pipe config didn't change at
all). Then comes the big switch:

- If we have the same pipe config, we simply to a vblank synce nuclear
  flip to the new config.

- If pipe configs change it will be much more elaborate:

  1. Do a vblank synced nuclear flip to black on all pipes that need to go
  off (whether they get disabled or reconfigured doesn't matter for now).

  2. Disable pipes.

  3. Commit new state on the sw side.

  4. Enable all pipes with the new config, but only scanning out black.

  5. Do a vblank-synced nuclear flip to the new config. This would also be
  the one that would signale the drm events that the atomic update
  completed.

  For fastboot we might need some hacks to fuse this all together, e.g for
  some panel fitter changes we don't need to disable the pipe completely.
  But that's the advanced stuff really.

I think modelling the crtc helpers after this model could work. But that
means that the crtc helpers and the nuclear flip atomic helpers for GO
bit capable hw need to be rather tightly integrated, while still allowing
drivers to override the nuclear flip parts.

> > 3. The core->driver interface should be powerful enough to support
> > insanity like i915, but no more. Which means all the code that's share
> > (i.e. the set_prop code I've been harping all over the place about) should
> > be done in the core, without any means for drivers to override. Currently
> > the drm core also takes care of a bunch of things like basic locking and
> > refcounting, and that's kinda the spirit for this. i915 is the obvious
> > benchmark here.
> 
> The more I think about it, the more I think we should leave set_prop
> as it is (although possibly with drm_atomic_state ->
> drm_{plane,crtc,etc}_state).
> 
> In the past, before primary plane, I really needed this.  And I expect
> having a convenient way to 'sniff' changing properties as they go by
> will be useful in some cases.

I actually really like the addition of the state object to ->set_prop. At
least for i915 (which already has a fair pile of additional properties)
that looks like the perfect way to prep the stage.

But at least for the transition we should keep the impact minimal. So no
manadatory callbacks and don't enforce the usage of the state object until
the drm core is fully converted to always follow a set_prop with a
->commit. Since currently we have internal mode_set calls in all i915
set_prop functions and we need to convert them all. But we can't do that
until all the core stuff uses the atomic interface for all legacy ioctl
ops.

> > I think we can roll this out piecemeal (and it's probably better to do it
> > that way), but I also think that until we've resolved the requirements of
> > 2&3 we should try to minimize subsystem wide changes as much as possible
> > by making them opt-in and the vfuncs optional.
> 
> the new mandatory vfuncs don't amount to too much churn.. not as much
> as the extra param for crtc lock/unlock fxns.  Maybe if I was planning
> on keeping this alive on a branch for so long in the beginning I would
> have arranged a few things slightly different, but meh.
> 
> A very early iteration of atomic preserved the old pageflip, setcrtc,
> setplane, etc paths for drivers not implementing atomic fxns.  But
> that was at least a bit ugly.  I like the current approach since the
> code paths are very much the same for atomic and non-atomic.

I'm not advertising for keeping the old driver interfaces, that would be
much worse. I'm only saying that we'll likely change this a few times, and
we will transition fairly slowly anyway. Reducing the overall churn and
especially reducing the amount of code we might need to fix up if we
change our minds seems like a good approach to me.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 17:50       ` Daniel Vetter
@ 2014-05-27 18:48         ` Rob Clark
  2014-05-27 19:26           ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-27 18:48 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 1:50 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
>> On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > Ok, I think I should have read your msm implementation a _lot_ earlier.
>> > Explains your desing choices neatly.
>> >
>> > Two observations:
>> >
>> > - A GO bit makes nuclear pageflips ridiculously easy to implement,
>> >   presuming the hardware actually works. And it's the sane model, so imo a
>> >   good one to wrap the atomic helpers around.
>> >
>> >   But reality is often a lot more ugly, especially if you're employed by
>> >   Intel. Which is why I'm harping so much on this helpers-vs-core
>> >   interface issues ... We really need the full state transition in one
>> >   piece to do anything useful.
>> >
>> > - msm doesn't have any resource sharing going on for modeset operations
>> >   (which I mean lighting up crtcs to feed pixel streams to connectors).
>> >   Which means the simplistic "loop over all crtcs and call the old
>> >   ->setcrtc" approach actually works.
>>
>> we do actually have some shared resources on mdp5 generation (the
>> "smp" blocks, basically a shared buffer which we need to allocate fifo
>> space from for each plane)..
>>
>> I'm mostly ignoring this at the moment, because we don't support
>> enough crtc's yet to run out of smp blocks.  But hooking in at the
>> current ->atomic_commit() would be enough for me, I think.  Tbh, it is
>> something that should be handled at the ->atomic_check() stage so I
>> hadn't given much though to ->atomic_commit() stage.
>>
>> That all said, I've no problem with adding one more hook, after
>> ->atomic_commit() and lock magic, before loop over planes/crtcs, so
>> drivers that need to can replace the loops and do their own thing.
>> Current state should be reasonably good for sane hw.  I'm just waiting
>> for patches for i915 to add whatever it needs.
>
> I don't think that will be enough for you. Example, slightly hypothetical:
>
> - Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen.
>   Together they just barely fit into your fifo space.
>
> - Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one.
>   Presume different outputs here so that no implicit output stealing
>   happens upon mode switching.
>
> Atomic switch should work, but don't since if you just loop over crtcs you
> have the intermediate stage where you try to drive 2 giant screens, which
> will run out of fifo resources. And I think it's really common to have
> such implicit resource sharing, maybe except for the most beefy desktop
> gpu which simply can't run out of memory bw with today's screen.

Well, this situation is a bit problematic in other similar cases..
moving plane between crtc's which needs to wait on a vblank is another
vaguely similar case.  I was kinda thinking of punting on that one
(ie. -EBUSY and userspace tries again on next frame).  Maybe for
modeset that doesn't work out so well, since frame N+1 you'll still be
at configuration A and have the same problem.

Would be kinda nice if helpers could order things according to what
decreases resource requirements vs what increases.  Although we could
probably get a lot of mileage out of just making the 'atomic loop over
things helper' apply config for crtcs/planes which will be disabled
first, before the ones which will be enabled at the end of the commit.
 Hmm.

Either way, if you have to replace 'atomic loop over things helper'
with your own i915 specific thing, it shouldn't make much difference
to you.  And I don't really think we need to solve all the worlds
problems in the first version.  But seems like there could be some
value in making helpers a bit more aware of shared resource
constraints.

> So afaics you really need to push a bit of smarts into the crtc helpers to
> make atomic possible, which then leaves you with interaction issues
> between the atomic stuff for GO bit capable hw for plane updates and the
> modeset ordering hell.
>
>> >   The problem I see here is that if you have hardware with more elaborate
>> >   needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e.
>> >   a GO bit) then your current atomic helpers will make it rather hard to
>> >   mix this. So I think we should pimp the crtc helpers a bit to be atomic
>> >   compliant (i.e. kill all outputs first before enabling anything new) and
>> >   try to integrate this with the atomic helpers used for GO bit style
>> >   updates.
>>
>> Not really, I don't think.  You can still do whatever shared resource
>> setup in ->atomic_commit().  For drivers which want to defer commit
>> (ie. doing commit after fb's ready from cpu, rather than from gpu), it
>> would probably be more convenient to split up atomic_commit() so
>> drivers have a post lock hook.  I think it is just a few line patch,
>> and I think that it probably isn't really needed until i915 is ready.
>> I'm pretty sure we can change this to do what i915 needs, while at the
>> same time keeping it simple for 'GO bit' drivers.
>>
>> The bit about crtc helpers, I'll have to think about.  I'm not sure
>> that we want to require that 'atomic' (modeset or pageflip) completely
>> *replaces* current state.  So disabling unrelated crtcs doesn't seem
>> like the right thing.  But we have some time to decide about the
>> semantics of an atomic modeset before we expose to userspace.
>
> I'm not talking about replacing unrelated crtcs. It's also not about
> underspecified state or about enabling/changing global resources.
>
> It is _only_ about ordering of the various operations: If both the
> current and the desired new configuration are at the limit of what the hw
> can do you can't switch to the new configuration by looping over all
> crtcs. The fact that this doesn't work is the entire point of atomic
> modesets. And if we have helpers which aren't cut out for the task at hand
> for any hw where we need to have atomic modesets to make such changes
> possible without userspace going nuts, then the helpers are imo simply not
> useful
>
>> >   i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers
>> >   anymore. But the radone eyefinity (or whatever the damn thing is called)
>> >   I have lying around here fits the bill: It has 5 DP+ outputs but only 2
>> >   dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI
>> >   screens atomically and it should all pan out.
>> >
>> >   But since your current helpers just loop over all crtcs without any
>> >   regard to ordering constraints this will fall over if the 2 HDMI outputs
>> >   get enabled before the 3 DP outputs get disabled all disabled.
>>
>> the driver should have rejected the config before it gets to commit
>> stage, if it were an impossible config.
>
> The configuration _is_ possible. We simply have to be somewhat careful
> with transitioning to it, since not _all_ intermediate states are
> possible. Your current helpers presume that's the case, which afaik isn't
> the case on any hw where we have global limits. For modesets.
>
> Nuclear pageflips are a completely different thing, as long as your hw has
> a GO bit.
>
>> > So with all that in mind I have 3 sanity checks for the atomic interfaces
>> > and helpers:
>> >
>> > 1. The atomic helpers should make enabling sane hw with a GO bit easy for
>> > nuclear pageflips. Benchmark would be sane hw like msm or omap.
>> >
>> > 2. It should cooperate with the crtc helpers such that hw with some shared
>> > resources (like dplls) works for atomic modesets. That pretty much means
>> > "disable everything (including crtc/connetor pipelines that only changed
>> > some parts) before enabling anything". Benchmark would be a platform with
>> > shared dplls.
>>
>> btw, not something I'd thought about before, but shared dplls seem
>> common enough, that it is something core could help with.  (Assuming
>> they are all related to pixel clk and not some derived thing that core
>> wouldn't know about.)
>
> Yup, that's what I'm trying to say ;-) But it was just an example, I
> believe that atm your helpers don't help for any shared modeset resources
> at all.

no, not at all (other than the ww_mutex stuff which should be useful
for shared resources and more fine grained locking within driver).  It
wasn't really a goal.

But having some knowledge about shared resources seems like it could
make core helpers more useful when I get closer to exploiting the
limits of the hw I have..  I suspect i915 is just further down that
path than the other drivers.

>> I think we do need to decide what partial state updates me in the
>> context of modeset or pageflip.  I kinda think the right thing is
>> different for modeset vs pageflip.  Maybe we want to define an atomic
>> flag to mean "disable/discard everything else"..  at any rate, we only
>> need to sort that before exposing the API to userspace.
>
> Yeah, I still strongly support this split in the api itself. For i915 my
> plan is to have separate configuration structures for modeset state and
> pageflip/plane config state. When we do an atomic modeset we compute both
> (maybe with some shortcut if we know that the pipe config didn't change at
> all). Then comes the big switch:
>
> - If we have the same pipe config, we simply to a vblank synce nuclear
>   flip to the new config.
>
> - If pipe configs change it will be much more elaborate:
>
>   1. Do a vblank synced nuclear flip to black on all pipes that need to go
>   off (whether they get disabled or reconfigured doesn't matter for now).
>
>   2. Disable pipes.
>
>   3. Commit new state on the sw side.
>
>   4. Enable all pipes with the new config, but only scanning out black.
>
>   5. Do a vblank-synced nuclear flip to the new config. This would also be
>   the one that would signale the drm events that the atomic update
>   completed.
>
>   For fastboot we might need some hacks to fuse this all together, e.g for
>   some panel fitter changes we don't need to disable the pipe completely.
>   But that's the advanced stuff really.
>
> I think modelling the crtc helpers after this model could work. But that
> means that the crtc helpers and the nuclear flip atomic helpers for GO
> bit capable hw need to be rather tightly integrated, while still allowing
> drivers to override the nuclear flip parts.
>
>> > 3. The core->driver interface should be powerful enough to support
>> > insanity like i915, but no more. Which means all the code that's share
>> > (i.e. the set_prop code I've been harping all over the place about) should
>> > be done in the core, without any means for drivers to override. Currently
>> > the drm core also takes care of a bunch of things like basic locking and
>> > refcounting, and that's kinda the spirit for this. i915 is the obvious
>> > benchmark here.
>>
>> The more I think about it, the more I think we should leave set_prop
>> as it is (although possibly with drm_atomic_state ->
>> drm_{plane,crtc,etc}_state).
>>
>> In the past, before primary plane, I really needed this.  And I expect
>> having a convenient way to 'sniff' changing properties as they go by
>> will be useful in some cases.
>
> I actually really like the addition of the state object to ->set_prop. At
> least for i915 (which already has a fair pile of additional properties)
> that looks like the perfect way to prep the stage.
>
> But at least for the transition we should keep the impact minimal. So no
> manadatory callbacks and don't enforce the usage of the state object until
> the drm core is fully converted to always follow a set_prop with a
> ->commit. Since currently we have internal mode_set calls in all i915
> set_prop functions and we need to convert them all. But we can't do that
> until all the core stuff uses the atomic interface for all legacy ioctl
> ops.
>

fwiw, at least all set_prop ioctl stuff from core follows up with a
atomic_commit now.  There are one or two more places doing mode_set
un-atomically.  And I'm not sure if you call set_prop anywhere
internally from i915.

BR,
-R

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 18:48         ` Rob Clark
@ 2014-05-27 19:26           ` Daniel Vetter
  2014-05-27 20:06             ` Rob Clark
  0 siblings, 1 reply; 69+ messages in thread
From: Daniel Vetter @ 2014-05-27 19:26 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 02:48:41PM -0400, Rob Clark wrote:
> On Tue, May 27, 2014 at 1:50 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
> >> On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> >> > Ok, I think I should have read your msm implementation a _lot_ earlier.
> >> > Explains your desing choices neatly.
> >> >
> >> > Two observations:
> >> >
> >> > - A GO bit makes nuclear pageflips ridiculously easy to implement,
> >> >   presuming the hardware actually works. And it's the sane model, so imo a
> >> >   good one to wrap the atomic helpers around.
> >> >
> >> >   But reality is often a lot more ugly, especially if you're employed by
> >> >   Intel. Which is why I'm harping so much on this helpers-vs-core
> >> >   interface issues ... We really need the full state transition in one
> >> >   piece to do anything useful.
> >> >
> >> > - msm doesn't have any resource sharing going on for modeset operations
> >> >   (which I mean lighting up crtcs to feed pixel streams to connectors).
> >> >   Which means the simplistic "loop over all crtcs and call the old
> >> >   ->setcrtc" approach actually works.
> >>
> >> we do actually have some shared resources on mdp5 generation (the
> >> "smp" blocks, basically a shared buffer which we need to allocate fifo
> >> space from for each plane)..
> >>
> >> I'm mostly ignoring this at the moment, because we don't support
> >> enough crtc's yet to run out of smp blocks.  But hooking in at the
> >> current ->atomic_commit() would be enough for me, I think.  Tbh, it is
> >> something that should be handled at the ->atomic_check() stage so I
> >> hadn't given much though to ->atomic_commit() stage.
> >>
> >> That all said, I've no problem with adding one more hook, after
> >> ->atomic_commit() and lock magic, before loop over planes/crtcs, so
> >> drivers that need to can replace the loops and do their own thing.
> >> Current state should be reasonably good for sane hw.  I'm just waiting
> >> for patches for i915 to add whatever it needs.
> >
> > I don't think that will be enough for you. Example, slightly hypothetical:
> >
> > - Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen.
> >   Together they just barely fit into your fifo space.
> >
> > - Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one.
> >   Presume different outputs here so that no implicit output stealing
> >   happens upon mode switching.
> >
> > Atomic switch should work, but don't since if you just loop over crtcs you
> > have the intermediate stage where you try to drive 2 giant screens, which
> > will run out of fifo resources. And I think it's really common to have
> > such implicit resource sharing, maybe except for the most beefy desktop
> > gpu which simply can't run out of memory bw with today's screen.
> 
> Well, this situation is a bit problematic in other similar cases..
> moving plane between crtc's which needs to wait on a vblank is another
> vaguely similar case.  I was kinda thinking of punting on that one
> (ie. -EBUSY and userspace tries again on next frame).  Maybe for
> modeset that doesn't work out so well, since frame N+1 you'll still be
> at configuration A and have the same problem.
> 
> Would be kinda nice if helpers could order things according to what
> decreases resource requirements vs what increases.  Although we could
> probably get a lot of mileage out of just making the 'atomic loop over
> things helper' apply config for crtcs/planes which will be disabled
> first, before the ones which will be enabled at the end of the commit.
>  Hmm.

Yeah, that one should go a long way, but only for modeset changes. If we
do this for plane updates it will look _really_ bad ;-)

So for the "move plane from crtc to crtc" I think we need a separate step.
Presuming we don't have any modeset operations we should be able to group
all the plane updates per crtc. This ofc presumes a sane hw with GO bit.
Then the helper could figure out which crtc it needs to nuclear flip first
to be able to move the plane.

At least with the current atomic ioctl proposal we can't reject this with
-EBUSY since with a full modeset (which takes longer than one vblank
anyway) it's possible. Otoh we _should_ reject it when userspace expects a
vblank synced update.

Which is another reason for why I think we really should have a flag
somewhere for vblank synced atomic updates vs. atomic updates which take
longer than one vblank and might even involved a few msec of blank screens
for switching.

> Either way, if you have to replace 'atomic loop over things helper'
> with your own i915 specific thing, it shouldn't make much difference
> to you.  And I don't really think we need to solve all the worlds
> problems in the first version.  But seems like there could be some
> value in making helpers a bit more aware of shared resource
> constraints.

This isn't about i915, but about all the drivers using crtc helpers.
Correct ordering of the crtc helper hooks should pretty much solve this,
without the need to track global resources (i.e. disable everything before
we start enabling). At least for modeset-like atomic updates.

i915 will roll their own, but not because atomic updates aren't possible
with the crtc helpers, but because the crtc helpers are inadequate for our
hw. For modeset updates atomic or not doesn't factor in here.

And imo if we can make the crtc helpers work, we should do that. Otherwise
there won't be a whole lot of use behind the atomic modeset updates imo.

> > So afaics you really need to push a bit of smarts into the crtc helpers to
> > make atomic possible, which then leaves you with interaction issues
> > between the atomic stuff for GO bit capable hw for plane updates and the
> > modeset ordering hell.
> >
> >> >   The problem I see here is that if you have hardware with more elaborate
> >> >   needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e.
> >> >   a GO bit) then your current atomic helpers will make it rather hard to
> >> >   mix this. So I think we should pimp the crtc helpers a bit to be atomic
> >> >   compliant (i.e. kill all outputs first before enabling anything new) and
> >> >   try to integrate this with the atomic helpers used for GO bit style
> >> >   updates.
> >>
> >> Not really, I don't think.  You can still do whatever shared resource
> >> setup in ->atomic_commit().  For drivers which want to defer commit
> >> (ie. doing commit after fb's ready from cpu, rather than from gpu), it
> >> would probably be more convenient to split up atomic_commit() so
> >> drivers have a post lock hook.  I think it is just a few line patch,
> >> and I think that it probably isn't really needed until i915 is ready.
> >> I'm pretty sure we can change this to do what i915 needs, while at the
> >> same time keeping it simple for 'GO bit' drivers.
> >>
> >> The bit about crtc helpers, I'll have to think about.  I'm not sure
> >> that we want to require that 'atomic' (modeset or pageflip) completely
> >> *replaces* current state.  So disabling unrelated crtcs doesn't seem
> >> like the right thing.  But we have some time to decide about the
> >> semantics of an atomic modeset before we expose to userspace.
> >
> > I'm not talking about replacing unrelated crtcs. It's also not about
> > underspecified state or about enabling/changing global resources.
> >
> > It is _only_ about ordering of the various operations: If both the
> > current and the desired new configuration are at the limit of what the hw
> > can do you can't switch to the new configuration by looping over all
> > crtcs. The fact that this doesn't work is the entire point of atomic
> > modesets. And if we have helpers which aren't cut out for the task at hand
> > for any hw where we need to have atomic modesets to make such changes
> > possible without userspace going nuts, then the helpers are imo simply not
> > useful
> >
> >> >   i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers
> >> >   anymore. But the radone eyefinity (or whatever the damn thing is called)
> >> >   I have lying around here fits the bill: It has 5 DP+ outputs but only 2
> >> >   dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI
> >> >   screens atomically and it should all pan out.
> >> >
> >> >   But since your current helpers just loop over all crtcs without any
> >> >   regard to ordering constraints this will fall over if the 2 HDMI outputs
> >> >   get enabled before the 3 DP outputs get disabled all disabled.
> >>
> >> the driver should have rejected the config before it gets to commit
> >> stage, if it were an impossible config.
> >
> > The configuration _is_ possible. We simply have to be somewhat careful
> > with transitioning to it, since not _all_ intermediate states are
> > possible. Your current helpers presume that's the case, which afaik isn't
> > the case on any hw where we have global limits. For modesets.
> >
> > Nuclear pageflips are a completely different thing, as long as your hw has
> > a GO bit.
> >
> >> > So with all that in mind I have 3 sanity checks for the atomic interfaces
> >> > and helpers:
> >> >
> >> > 1. The atomic helpers should make enabling sane hw with a GO bit easy for
> >> > nuclear pageflips. Benchmark would be sane hw like msm or omap.
> >> >
> >> > 2. It should cooperate with the crtc helpers such that hw with some shared
> >> > resources (like dplls) works for atomic modesets. That pretty much means
> >> > "disable everything (including crtc/connetor pipelines that only changed
> >> > some parts) before enabling anything". Benchmark would be a platform with
> >> > shared dplls.
> >>
> >> btw, not something I'd thought about before, but shared dplls seem
> >> common enough, that it is something core could help with.  (Assuming
> >> they are all related to pixel clk and not some derived thing that core
> >> wouldn't know about.)
> >
> > Yup, that's what I'm trying to say ;-) But it was just an example, I
> > believe that atm your helpers don't help for any shared modeset resources
> > at all.
> 
> no, not at all (other than the ww_mutex stuff which should be useful
> for shared resources and more fine grained locking within driver).  It
> wasn't really a goal.
> 
> But having some knowledge about shared resources seems like it could
> make core helpers more useful when I get closer to exploiting the
> limits of the hw I have..  I suspect i915 is just further down that
> path than the other drivers.

I'm repeating myself, but simply ordering updates correctly should already
solve it. At least if the driver provides checks to make sure the new
config doesn't go over limits (e.g. by counting plls or required fifo
space). If we don't have that, the helpers are imo not sufficiently
validated as generally useful. And I have seen _way_ too much single use
code in the drm core from the old ums/dri1 days.

> >> I think we do need to decide what partial state updates me in the
> >> context of modeset or pageflip.  I kinda think the right thing is
> >> different for modeset vs pageflip.  Maybe we want to define an atomic
> >> flag to mean "disable/discard everything else"..  at any rate, we only
> >> need to sort that before exposing the API to userspace.
> >
> > Yeah, I still strongly support this split in the api itself. For i915 my
> > plan is to have separate configuration structures for modeset state and
> > pageflip/plane config state. When we do an atomic modeset we compute both
> > (maybe with some shortcut if we know that the pipe config didn't change at
> > all). Then comes the big switch:
> >
> > - If we have the same pipe config, we simply to a vblank synce nuclear
> >   flip to the new config.
> >
> > - If pipe configs change it will be much more elaborate:
> >
> >   1. Do a vblank synced nuclear flip to black on all pipes that need to go
> >   off (whether they get disabled or reconfigured doesn't matter for now).
> >
> >   2. Disable pipes.
> >
> >   3. Commit new state on the sw side.
> >
> >   4. Enable all pipes with the new config, but only scanning out black.
> >
> >   5. Do a vblank-synced nuclear flip to the new config. This would also be
> >   the one that would signale the drm events that the atomic update
> >   completed.
> >
> >   For fastboot we might need some hacks to fuse this all together, e.g for
> >   some panel fitter changes we don't need to disable the pipe completely.
> >   But that's the advanced stuff really.
> >
> > I think modelling the crtc helpers after this model could work. But that
> > means that the crtc helpers and the nuclear flip atomic helpers for GO
> > bit capable hw need to be rather tightly integrated, while still allowing
> > drivers to override the nuclear flip parts.
> >
> >> > 3. The core->driver interface should be powerful enough to support
> >> > insanity like i915, but no more. Which means all the code that's share
> >> > (i.e. the set_prop code I've been harping all over the place about) should
> >> > be done in the core, without any means for drivers to override. Currently
> >> > the drm core also takes care of a bunch of things like basic locking and
> >> > refcounting, and that's kinda the spirit for this. i915 is the obvious
> >> > benchmark here.
> >>
> >> The more I think about it, the more I think we should leave set_prop
> >> as it is (although possibly with drm_atomic_state ->
> >> drm_{plane,crtc,etc}_state).
> >>
> >> In the past, before primary plane, I really needed this.  And I expect
> >> having a convenient way to 'sniff' changing properties as they go by
> >> will be useful in some cases.
> >
> > I actually really like the addition of the state object to ->set_prop. At
> > least for i915 (which already has a fair pile of additional properties)
> > that looks like the perfect way to prep the stage.
> >
> > But at least for the transition we should keep the impact minimal. So no
> > manadatory callbacks and don't enforce the usage of the state object until
> > the drm core is fully converted to always follow a set_prop with a
> > ->commit. Since currently we have internal mode_set calls in all i915
> > set_prop functions and we need to convert them all. But we can't do that
> > until all the core stuff uses the atomic interface for all legacy ioctl
> > ops.
> >
> 
> fwiw, at least all set_prop ioctl stuff from core follows up with a
> atomic_commit now.  There are one or two more places doing mode_set
> un-atomically.  And I'm not sure if you call set_prop anywhere
> internally from i915.

We do modesets internally, but as long as those aren't externally visible
(which they shouldn't be if we grab locks before checking the state) it
should be all fine. Also from my pov the ->set_prop stuff is just
interface to construct the in-kernel representation of the desired config.
The real magic happens in the check/commit hooks (which is the same level
i915-internal modeset changes happens at).

I think one excellent use-case we get for free (almost) without the ioctl
would be fbcon. It very much wants to do an atomic update, so converting
that over to the atomic interface would be good imo.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 19:26           ` Daniel Vetter
@ 2014-05-27 20:06             ` Rob Clark
  2014-05-27 22:09               ` Daniel Vetter
  0 siblings, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-27 20:06 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, May 27, 2014 at 02:48:41PM -0400, Rob Clark wrote:
>> On Tue, May 27, 2014 at 1:50 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Tue, May 27, 2014 at 11:58:33AM -0400, Rob Clark wrote:
>> >> On Mon, May 26, 2014 at 1:54 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> >> > Ok, I think I should have read your msm implementation a _lot_ earlier.
>> >> > Explains your desing choices neatly.
>> >> >
>> >> > Two observations:
>> >> >
>> >> > - A GO bit makes nuclear pageflips ridiculously easy to implement,
>> >> >   presuming the hardware actually works. And it's the sane model, so imo a
>> >> >   good one to wrap the atomic helpers around.
>> >> >
>> >> >   But reality is often a lot more ugly, especially if you're employed by
>> >> >   Intel. Which is why I'm harping so much on this helpers-vs-core
>> >> >   interface issues ... We really need the full state transition in one
>> >> >   piece to do anything useful.
>> >> >
>> >> > - msm doesn't have any resource sharing going on for modeset operations
>> >> >   (which I mean lighting up crtcs to feed pixel streams to connectors).
>> >> >   Which means the simplistic "loop over all crtcs and call the old
>> >> >   ->setcrtc" approach actually works.
>> >>
>> >> we do actually have some shared resources on mdp5 generation (the
>> >> "smp" blocks, basically a shared buffer which we need to allocate fifo
>> >> space from for each plane)..
>> >>
>> >> I'm mostly ignoring this at the moment, because we don't support
>> >> enough crtc's yet to run out of smp blocks.  But hooking in at the
>> >> current ->atomic_commit() would be enough for me, I think.  Tbh, it is
>> >> something that should be handled at the ->atomic_check() stage so I
>> >> hadn't given much though to ->atomic_commit() stage.
>> >>
>> >> That all said, I've no problem with adding one more hook, after
>> >> ->atomic_commit() and lock magic, before loop over planes/crtcs, so
>> >> drivers that need to can replace the loops and do their own thing.
>> >> Current state should be reasonably good for sane hw.  I'm just waiting
>> >> for patches for i915 to add whatever it needs.
>> >
>> > I don't think that will be enough for you. Example, slightly hypothetical:
>> >
>> > - Configuration A: crtc 0 displays a small screen, crtc 1 a giant screen.
>> >   Together they just barely fit into your fifo space.
>> >
>> > - Configuration B: crtc 0 drives a giant screen, crtc 1 a tiny one.
>> >   Presume different outputs here so that no implicit output stealing
>> >   happens upon mode switching.
>> >
>> > Atomic switch should work, but don't since if you just loop over crtcs you
>> > have the intermediate stage where you try to drive 2 giant screens, which
>> > will run out of fifo resources. And I think it's really common to have
>> > such implicit resource sharing, maybe except for the most beefy desktop
>> > gpu which simply can't run out of memory bw with today's screen.
>>
>> Well, this situation is a bit problematic in other similar cases..
>> moving plane between crtc's which needs to wait on a vblank is another
>> vaguely similar case.  I was kinda thinking of punting on that one
>> (ie. -EBUSY and userspace tries again on next frame).  Maybe for
>> modeset that doesn't work out so well, since frame N+1 you'll still be
>> at configuration A and have the same problem.
>>
>> Would be kinda nice if helpers could order things according to what
>> decreases resource requirements vs what increases.  Although we could
>> probably get a lot of mileage out of just making the 'atomic loop over
>> things helper' apply config for crtcs/planes which will be disabled
>> first, before the ones which will be enabled at the end of the commit.
>>  Hmm.
>
> Yeah, that one should go a long way, but only for modeset changes. If we
> do this for plane updates it will look _really_ bad ;-)
>
> So for the "move plane from crtc to crtc" I think we need a separate step.
> Presuming we don't have any modeset operations we should be able to group
> all the plane updates per crtc. This ofc presumes a sane hw with GO bit.
> Then the helper could figure out which crtc it needs to nuclear flip first
> to be able to move the plane.
>
> At least with the current atomic ioctl proposal we can't reject this with
> -EBUSY since with a full modeset (which takes longer than one vblank
> anyway) it's possible. Otoh we _should_ reject it when userspace expects a
> vblank synced update.
>
> Which is another reason for why I think we really should have a flag
> somewhere for vblank synced atomic updates vs. atomic updates which take
> longer than one vblank and might even involved a few msec of blank screens
> for switching.

Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
should hang so much off of that one flag.

>> Either way, if you have to replace 'atomic loop over things helper'
>> with your own i915 specific thing, it shouldn't make much difference
>> to you.  And I don't really think we need to solve all the worlds
>> problems in the first version.  But seems like there could be some
>> value in making helpers a bit more aware of shared resource
>> constraints.
>
> This isn't about i915, but about all the drivers using crtc helpers.
> Correct ordering of the crtc helper hooks should pretty much solve this,
> without the need to track global resources (i.e. disable everything before
> we start enabling). At least for modeset-like atomic updates.
>
> i915 will roll their own, but not because atomic updates aren't possible
> with the crtc helpers, but because the crtc helpers are inadequate for our
> hw. For modeset updates atomic or not doesn't factor in here.
>
> And imo if we can make the crtc helpers work, we should do that. Otherwise
> there won't be a whole lot of use behind the atomic modeset updates imo.
>
>> > So afaics you really need to push a bit of smarts into the crtc helpers to
>> > make atomic possible, which then leaves you with interaction issues
>> > between the atomic stuff for GO bit capable hw for plane updates and the
>> > modeset ordering hell.
>> >
>> >> >   The problem I see here is that if you have hardware with more elaborate
>> >> >   needs (e.g. shared dplls), but otherwise sanity for plane updates (i.e.
>> >> >   a GO bit) then your current atomic helpers will make it rather hard to
>> >> >   mix this. So I think we should pimp the crtc helpers a bit to be atomic
>> >> >   compliant (i.e. kill all outputs first before enabling anything new) and
>> >> >   try to integrate this with the atomic helpers used for GO bit style
>> >> >   updates.
>> >>
>> >> Not really, I don't think.  You can still do whatever shared resource
>> >> setup in ->atomic_commit().  For drivers which want to defer commit
>> >> (ie. doing commit after fb's ready from cpu, rather than from gpu), it
>> >> would probably be more convenient to split up atomic_commit() so
>> >> drivers have a post lock hook.  I think it is just a few line patch,
>> >> and I think that it probably isn't really needed until i915 is ready.
>> >> I'm pretty sure we can change this to do what i915 needs, while at the
>> >> same time keeping it simple for 'GO bit' drivers.
>> >>
>> >> The bit about crtc helpers, I'll have to think about.  I'm not sure
>> >> that we want to require that 'atomic' (modeset or pageflip) completely
>> >> *replaces* current state.  So disabling unrelated crtcs doesn't seem
>> >> like the right thing.  But we have some time to decide about the
>> >> semantics of an atomic modeset before we expose to userspace.
>> >
>> > I'm not talking about replacing unrelated crtcs. It's also not about
>> > underspecified state or about enabling/changing global resources.
>> >
>> > It is _only_ about ordering of the various operations: If both the
>> > current and the desired new configuration are at the limit of what the hw
>> > can do you can't switch to the new configuration by looping over all
>> > crtcs. The fact that this doesn't work is the entire point of atomic
>> > modesets. And if we have helpers which aren't cut out for the task at hand
>> > for any hw where we need to have atomic modesets to make such changes
>> > possible without userspace going nuts, then the helpers are imo simply not
>> > useful
>> >
>> >> >   i915 has dpll sharing on ivb/hsw, but doesn't use the the crtc helpers
>> >> >   anymore. But the radone eyefinity (or whatever the damn thing is called)
>> >> >   I have lying around here fits the bill: It has 5 DP+ outputs but only 2
>> >> >   dplls. So you can drive e.g. 3 DP screens and then switch to 2 HDMI
>> >> >   screens atomically and it should all pan out.
>> >> >
>> >> >   But since your current helpers just loop over all crtcs without any
>> >> >   regard to ordering constraints this will fall over if the 2 HDMI outputs
>> >> >   get enabled before the 3 DP outputs get disabled all disabled.
>> >>
>> >> the driver should have rejected the config before it gets to commit
>> >> stage, if it were an impossible config.
>> >
>> > The configuration _is_ possible. We simply have to be somewhat careful
>> > with transitioning to it, since not _all_ intermediate states are
>> > possible. Your current helpers presume that's the case, which afaik isn't
>> > the case on any hw where we have global limits. For modesets.
>> >
>> > Nuclear pageflips are a completely different thing, as long as your hw has
>> > a GO bit.
>> >
>> >> > So with all that in mind I have 3 sanity checks for the atomic interfaces
>> >> > and helpers:
>> >> >
>> >> > 1. The atomic helpers should make enabling sane hw with a GO bit easy for
>> >> > nuclear pageflips. Benchmark would be sane hw like msm or omap.
>> >> >
>> >> > 2. It should cooperate with the crtc helpers such that hw with some shared
>> >> > resources (like dplls) works for atomic modesets. That pretty much means
>> >> > "disable everything (including crtc/connetor pipelines that only changed
>> >> > some parts) before enabling anything". Benchmark would be a platform with
>> >> > shared dplls.
>> >>
>> >> btw, not something I'd thought about before, but shared dplls seem
>> >> common enough, that it is something core could help with.  (Assuming
>> >> they are all related to pixel clk and not some derived thing that core
>> >> wouldn't know about.)
>> >
>> > Yup, that's what I'm trying to say ;-) But it was just an example, I
>> > believe that atm your helpers don't help for any shared modeset resources
>> > at all.
>>
>> no, not at all (other than the ww_mutex stuff which should be useful
>> for shared resources and more fine grained locking within driver).  It
>> wasn't really a goal.
>>
>> But having some knowledge about shared resources seems like it could
>> make core helpers more useful when I get closer to exploiting the
>> limits of the hw I have..  I suspect i915 is just further down that
>> path than the other drivers.
>
> I'm repeating myself, but simply ordering updates correctly should already
> solve it. At least if the driver provides checks to make sure the new
> config doesn't go over limits (e.g. by counting plls or required fifo
> space). If we don't have that, the helpers are imo not sufficiently
> validated as generally useful. And I have seen _way_ too much single use
> code in the drm core from the old ums/dri1 days.
>
>> >> I think we do need to decide what partial state updates me in the
>> >> context of modeset or pageflip.  I kinda think the right thing is
>> >> different for modeset vs pageflip.  Maybe we want to define an atomic
>> >> flag to mean "disable/discard everything else"..  at any rate, we only
>> >> need to sort that before exposing the API to userspace.
>> >
>> > Yeah, I still strongly support this split in the api itself. For i915 my
>> > plan is to have separate configuration structures for modeset state and
>> > pageflip/plane config state. When we do an atomic modeset we compute both
>> > (maybe with some shortcut if we know that the pipe config didn't change at
>> > all). Then comes the big switch:
>> >
>> > - If we have the same pipe config, we simply to a vblank synce nuclear
>> >   flip to the new config.
>> >
>> > - If pipe configs change it will be much more elaborate:
>> >
>> >   1. Do a vblank synced nuclear flip to black on all pipes that need to go
>> >   off (whether they get disabled or reconfigured doesn't matter for now).
>> >
>> >   2. Disable pipes.
>> >
>> >   3. Commit new state on the sw side.
>> >
>> >   4. Enable all pipes with the new config, but only scanning out black.
>> >
>> >   5. Do a vblank-synced nuclear flip to the new config. This would also be
>> >   the one that would signale the drm events that the atomic update
>> >   completed.
>> >
>> >   For fastboot we might need some hacks to fuse this all together, e.g for
>> >   some panel fitter changes we don't need to disable the pipe completely.
>> >   But that's the advanced stuff really.
>> >
>> > I think modelling the crtc helpers after this model could work. But that
>> > means that the crtc helpers and the nuclear flip atomic helpers for GO
>> > bit capable hw need to be rather tightly integrated, while still allowing
>> > drivers to override the nuclear flip parts.
>> >
>> >> > 3. The core->driver interface should be powerful enough to support
>> >> > insanity like i915, but no more. Which means all the code that's share
>> >> > (i.e. the set_prop code I've been harping all over the place about) should
>> >> > be done in the core, without any means for drivers to override. Currently
>> >> > the drm core also takes care of a bunch of things like basic locking and
>> >> > refcounting, and that's kinda the spirit for this. i915 is the obvious
>> >> > benchmark here.
>> >>
>> >> The more I think about it, the more I think we should leave set_prop
>> >> as it is (although possibly with drm_atomic_state ->
>> >> drm_{plane,crtc,etc}_state).
>> >>
>> >> In the past, before primary plane, I really needed this.  And I expect
>> >> having a convenient way to 'sniff' changing properties as they go by
>> >> will be useful in some cases.
>> >
>> > I actually really like the addition of the state object to ->set_prop. At
>> > least for i915 (which already has a fair pile of additional properties)
>> > that looks like the perfect way to prep the stage.
>> >
>> > But at least for the transition we should keep the impact minimal. So no
>> > manadatory callbacks and don't enforce the usage of the state object until
>> > the drm core is fully converted to always follow a set_prop with a
>> > ->commit. Since currently we have internal mode_set calls in all i915
>> > set_prop functions and we need to convert them all. But we can't do that
>> > until all the core stuff uses the atomic interface for all legacy ioctl
>> > ops.
>> >
>>
>> fwiw, at least all set_prop ioctl stuff from core follows up with a
>> atomic_commit now.  There are one or two more places doing mode_set
>> un-atomically.  And I'm not sure if you call set_prop anywhere
>> internally from i915.
>
> We do modesets internally, but as long as those aren't externally visible
> (which they shouldn't be if we grab locks before checking the state) it
> should be all fine. Also from my pov the ->set_prop stuff is just
> interface to construct the in-kernel representation of the desired config.
> The real magic happens in the check/commit hooks (which is the same level
> i915-internal modeset changes happens at).
>
> I think one excellent use-case we get for free (almost) without the ioctl
> would be fbcon. It very much wants to do an atomic update, so converting
> that over to the atomic interface would be good imo.

Yes, iirc the remaining non-atomic paths are fbcon related.  In
principle it should be a simple matter to increment the refcnt on
fbcon state object and re-apply it.  Although at the moment we keep
track of *how* to apply the state (ie. page_flip vs set_config, etc)
as the state object is built up.. which isn't very conducive to
re-committing an existing state object.  Which is part of the reason I
wanted to deprecate the various existing
->page_flip/->update_plane/->set_config/etc and introduce per object
->commit()'s.  (Which could either be called by helpers, or called
internally by driver or completely ignored by driver)

I've been a bit reluctant so far to do too much additional refactoring
on top of atomic, since I'm about at the limit of what I have time to
repeatedly rebase each kernel version.  This is why I'm a bit anxious
to start merging some of atomic, even if it doesn't do absolutely
everything yet.

BR,
-R

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

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 20:06             ` Rob Clark
@ 2014-05-27 22:09               ` Daniel Vetter
  2014-05-27 23:32                 ` Rob Clark
  2014-05-27 23:47                 ` Rob Clark
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-27 22:09 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:

[snip]

> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
> should hang so much off of that one flag.

Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
want non-blocking modesets.

[snip]

> > I think one excellent use-case we get for free (almost) without the ioctl
> > would be fbcon. It very much wants to do an atomic update, so converting
> > that over to the atomic interface would be good imo.
> 
> Yes, iirc the remaining non-atomic paths are fbcon related.  In
> principle it should be a simple matter to increment the refcnt on
> fbcon state object and re-apply it.  Although at the moment we keep
> track of *how* to apply the state (ie. page_flip vs set_config, etc)
> as the state object is built up.. which isn't very conducive to
> re-committing an existing state object.  Which is part of the reason I
> wanted to deprecate the various existing
> ->page_flip/->update_plane/->set_config/etc and introduce per object
> ->commit()'s.  (Which could either be called by helpers, or called
> internally by driver or completely ignored by driver)

Yeah, I think the approach in here with a few helpers to bend atomic
->commit to the old hooks (somewhat-ish) is good. And with the crtc
helpers we should be able to move most drivers away from the old hooks
quickly. The exception will be pageflips/cursors, but that requires a lot
of driver-specific work, and probably first a full conversion to universal
planes (which atm don't support everything even for the primary plane due
to the arbitrary restriction to rgbx8888).

> I've been a bit reluctant so far to do too much additional refactoring
> on top of atomic, since I'm about at the limit of what I have time to
> repeatedly rebase each kernel version.  This is why I'm a bit anxious
> to start merging some of atomic, even if it doesn't do absolutely
> everything yet.

I understand that, which is why I suggested a bunch of things to split out
already so we can get them in.

On top of that I think with the split-up mode_config.mutex like I've just
proposed in an RFC we have a clear path for the locking issues, too. So
could go ahead an merge the w/w conversion, too.

That leaves the set_prop refactoring which is still under discussion.
Those three pieces hopefully help a lot with reducing rebasing pain.

On top of that I think we should look at cutting away functional pieces of
the conversion, e.g. ignore planes at first and only look at atomic
modeset. Or ignore atomic modesets and only look at plane updates,
rejecting anything that changes connectors or crtcs. By cutting out slices
we should be able to get patches into shape step-by-step.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 22:09               ` Daniel Vetter
@ 2014-05-27 23:32                 ` Rob Clark
  2014-05-28 13:21                   ` Daniel Vetter
  2014-05-27 23:47                 ` Rob Clark
  1 sibling, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-27 23:32 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
>> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>
> [snip]
>
>> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
>> should hang so much off of that one flag.
>
> Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
> want non-blocking modesets.

random-diverging-from-original-topic-thought.. seems like userspace
just wants a deadline/timeout (hopefully in units of vblanks).. at
least to the level of "I want this to happen on the next vblank (after
rendering completes), or give me an error" vs "I want this to complete
atomically even if it takes a few extra vblanks for things to sort
out"..

I guess that amounts to what you mean by VBLANK_SYNCED flag, but
VBLANKED_SYNCED might not be a good name.. at least for some hw all
you can do is vblank sync'd..

BR,
-R

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 22:09               ` Daniel Vetter
  2014-05-27 23:32                 ` Rob Clark
@ 2014-05-27 23:47                 ` Rob Clark
  2014-05-28 13:32                   ` Daniel Vetter
  1 sibling, 1 reply; 69+ messages in thread
From: Rob Clark @ 2014-05-27 23:47 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
>> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>
> [snip]
>
>> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
>> should hang so much off of that one flag.
>
> Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
> want non-blocking modesets.
>
> [snip]
>
>> > I think one excellent use-case we get for free (almost) without the ioctl
>> > would be fbcon. It very much wants to do an atomic update, so converting
>> > that over to the atomic interface would be good imo.
>>
>> Yes, iirc the remaining non-atomic paths are fbcon related.  In
>> principle it should be a simple matter to increment the refcnt on
>> fbcon state object and re-apply it.  Although at the moment we keep
>> track of *how* to apply the state (ie. page_flip vs set_config, etc)
>> as the state object is built up.. which isn't very conducive to
>> re-committing an existing state object.  Which is part of the reason I
>> wanted to deprecate the various existing
>> ->page_flip/->update_plane/->set_config/etc and introduce per object
>> ->commit()'s.  (Which could either be called by helpers, or called
>> internally by driver or completely ignored by driver)
>
> Yeah, I think the approach in here with a few helpers to bend atomic
> ->commit to the old hooks (somewhat-ish) is good. And with the crtc
> helpers we should be able to move most drivers away from the old hooks
> quickly. The exception will be pageflips/cursors, but that requires a lot
> of driver-specific work, and probably first a full conversion to universal
> planes (which atm don't support everything even for the primary plane due
> to the arbitrary restriction to rgbx8888).

btw, I guess/hope most SoC drivers (ie. the ones that want to do crazy
things with overlays) would be moving rapidly away from using primary
plane helpers.  I think native primary planes fits better most of the
non-trivial mobile display controller blocks..

>> I've been a bit reluctant so far to do too much additional refactoring
>> on top of atomic, since I'm about at the limit of what I have time to
>> repeatedly rebase each kernel version.  This is why I'm a bit anxious
>> to start merging some of atomic, even if it doesn't do absolutely
>> everything yet.
>
> I understand that, which is why I suggested a bunch of things to split out
> already so we can get them in.
>
> On top of that I think with the split-up mode_config.mutex like I've just
> proposed in an RFC we have a clear path for the locking issues, too. So
> could go ahead an merge the w/w conversion, too.

I suppose now that I split modeset acquire ctx out into it's own thing
(originally it was inline w/ drm_atomic_state), I could reshuffle the
patches and move ww_mutex stuff below atomic.. will take a bit of
patch surgery since some parts of that patch would have to move to
later patches, but I can sort it out

> That leaves the set_prop refactoring which is still under discussion.
> Those three pieces hopefully help a lot with reducing rebasing pain.
>
> On top of that I think we should look at cutting away functional pieces of
> the conversion, e.g. ignore planes at first and only look at atomic
> modeset. Or ignore atomic modesets and only look at plane updates,
> rejecting anything that changes connectors or crtcs. By cutting out slices
> we should be able to get patches into shape step-by-step.

hmm, well, not sure how much value it is splitting up like that.  I
suppose the only-plane approach would sidestep/punt some issues.  But
once locking and set_prop are in place / agreed, it isn't that much
more from a churn perspective.

tbh, I think the worse problems are when we actually expose atomic
ioctl to userspace.  Right now we are rather restricted in the updates
triggered via atomic in that there are fixed well defined entry points
to atomic.  Ie. only pageflip() sets _NONBLOCK flag, and it does not
set mode property, so you won't ever see a _NONBLOCK modeset.

BR,
-R

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

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 23:32                 ` Rob Clark
@ 2014-05-28 13:21                   ` Daniel Vetter
  2014-05-28 14:14                     ` Ville Syrjälä
  2014-05-28 15:19                     ` Rob Clark
  0 siblings, 2 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-28 13:21 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
> On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
> >> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> >
> > [snip]
> >
> >> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
> >> should hang so much off of that one flag.
> >
> > Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
> > want non-blocking modesets.
> 
> random-diverging-from-original-topic-thought.. seems like userspace
> just wants a deadline/timeout (hopefully in units of vblanks).. at
> least to the level of "I want this to happen on the next vblank (after
> rendering completes), or give me an error" vs "I want this to complete
> atomically even if it takes a few extra vblanks for things to sort
> out"..
> 
> I guess that amounts to what you mean by VBLANK_SYNCED flag, but
> VBLANKED_SYNCED might not be a good name.. at least for some hw all
> you can do is vblank sync'd..

Hm, we might as well go full monty and allow userspace to request a
specific vblank counter. Would be useful for e.g. queuing up a bunch of
video frames, which some hw can do. But that would then require cancelling
of existing flips, so I guess for now a simple VBLANK_SYNCED flag which
emulates pageflip behaviour should be good enough.

That would also be useful if userspace attempts an atomic update on
drivers which only support atomic modeset (through the crtc helpers), but
can't do vblank synced updates. Then they could easily reject those. After
all current drivers also often lack a pageflip implementation ...
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-27 23:47                 ` Rob Clark
@ 2014-05-28 13:32                   ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-28 13:32 UTC (permalink / raw)
  To: Rob Clark; +Cc: dri-devel@lists.freedesktop.org

On Tue, May 27, 2014 at 07:47:42PM -0400, Rob Clark wrote:
> On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
> >> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> >
> > [snip]
> >
> >> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
> >> should hang so much off of that one flag.
> >
> > Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
> > want non-blocking modesets.
> >
> > [snip]
> >
> >> > I think one excellent use-case we get for free (almost) without the ioctl
> >> > would be fbcon. It very much wants to do an atomic update, so converting
> >> > that over to the atomic interface would be good imo.
> >>
> >> Yes, iirc the remaining non-atomic paths are fbcon related.  In
> >> principle it should be a simple matter to increment the refcnt on
> >> fbcon state object and re-apply it.  Although at the moment we keep
> >> track of *how* to apply the state (ie. page_flip vs set_config, etc)
> >> as the state object is built up.. which isn't very conducive to
> >> re-committing an existing state object.  Which is part of the reason I
> >> wanted to deprecate the various existing
> >> ->page_flip/->update_plane/->set_config/etc and introduce per object
> >> ->commit()'s.  (Which could either be called by helpers, or called
> >> internally by driver or completely ignored by driver)
> >
> > Yeah, I think the approach in here with a few helpers to bend atomic
> > ->commit to the old hooks (somewhat-ish) is good. And with the crtc
> > helpers we should be able to move most drivers away from the old hooks
> > quickly. The exception will be pageflips/cursors, but that requires a lot
> > of driver-specific work, and probably first a full conversion to universal
> > planes (which atm don't support everything even for the primary plane due
> > to the arbitrary restriction to rgbx8888).
> 
> btw, I guess/hope most SoC drivers (ie. the ones that want to do crazy
> things with overlays) would be moving rapidly away from using primary
> plane helpers.  I think native primary planes fits better most of the
> non-trivial mobile display controller blocks..

Fully agreed. See my comment about the VBLANK_SYNCED flag, I think without
a driver implementation we should simply aggressively reject such updates.
Same with disabling the primary plane, at least until we've reworked the
interfaces a bit.

> >> I've been a bit reluctant so far to do too much additional refactoring
> >> on top of atomic, since I'm about at the limit of what I have time to
> >> repeatedly rebase each kernel version.  This is why I'm a bit anxious
> >> to start merging some of atomic, even if it doesn't do absolutely
> >> everything yet.
> >
> > I understand that, which is why I suggested a bunch of things to split out
> > already so we can get them in.
> >
> > On top of that I think with the split-up mode_config.mutex like I've just
> > proposed in an RFC we have a clear path for the locking issues, too. So
> > could go ahead an merge the w/w conversion, too.
> 
> I suppose now that I split modeset acquire ctx out into it's own thing
> (originally it was inline w/ drm_atomic_state), I could reshuffle the
> patches and move ww_mutex stuff below atomic.. will take a bit of
> patch surgery since some parts of that patch would have to move to
> later patches, but I can sort it out

below = earlier in the series or later? I'll assume earlier since that
what git log shows ;-)

As mentioned on irc I think we could also throw per-plane locks into the
mix to really validate the locking side of this. One thing we didn't
discuss that much in the last few emails is accessing the obj->state data
and other stuff which is autodetect (e.g. whether the sink is audio
capable or not). I still think we need one additional plain mutex to
protect that. It would sit between the mode_config.mutex and all the ww
mutexes in the locking hierarchy.

But we can look at that once the mode_config.mutex mess is untangled and
clear.

> > That leaves the set_prop refactoring which is still under discussion.
> > Those three pieces hopefully help a lot with reducing rebasing pain.
> >
> > On top of that I think we should look at cutting away functional pieces of
> > the conversion, e.g. ignore planes at first and only look at atomic
> > modeset. Or ignore atomic modesets and only look at plane updates,
> > rejecting anything that changes connectors or crtcs. By cutting out slices
> > we should be able to get patches into shape step-by-step.
> 
> hmm, well, not sure how much value it is splitting up like that.  I
> suppose the only-plane approach would sidestep/punt some issues.  But
> once locking and set_prop are in place / agreed, it isn't that much
> more from a churn perspective.

Hm, could be that it's not worth it, was just an idea.

> tbh, I think the worse problems are when we actually expose atomic
> ioctl to userspace.  Right now we are rather restricted in the updates
> triggered via atomic in that there are fixed well defined entry points
> to atomic.  Ie. only pageflip() sets _NONBLOCK flag, and it does not
> set mode property, so you won't ever see a _NONBLOCK modeset.

I guess we could lift limits step-by-step, e.g. reject any NONBLOCK
updates if they change crtc/connector properties. And reject any NONBLOCK
which isn't also vsynced.

Wrt earlier testing fbcon should give us basic in-kernel validation, since
it really wants to kill all the planes and everything and put a new config
into place in one go. Another good one would be suspend/resume. It needs
vt-switchless resume enabled (which is a per-driver knob and only i915 has
it for now, but it's easy to do). Then we could suspend/resume a full X
session with overlays, cursors and all and see whether the atomic engine
can cope and restore it all.

Once that's proven the ioctl shouldn't be that bad really any more. For
i915 I'll demand lots of tests for corner-cases, but the easiest to
implement workload might be the vt restore in the ddx. If we can do that
with atomic you can fire up a bunch of ridiculous (and conflicting) X
configs with cursors and overlays and the vt-switch between them like mad.
That should give us good testing I hope.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-28 13:21                   ` Daniel Vetter
@ 2014-05-28 14:14                     ` Ville Syrjälä
  2014-05-28 14:50                       ` Daniel Vetter
  2014-05-28 15:19                     ` Rob Clark
  1 sibling, 1 reply; 69+ messages in thread
From: Ville Syrjälä @ 2014-05-28 14:14 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Wed, May 28, 2014 at 03:21:41PM +0200, Daniel Vetter wrote:
> On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
> > On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > > On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
> > >> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > >
> > > [snip]
> > >
> > >> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
> > >> should hang so much off of that one flag.
> > >
> > > Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
> > > want non-blocking modesets.
> > 
> > random-diverging-from-original-topic-thought.. seems like userspace
> > just wants a deadline/timeout (hopefully in units of vblanks).. at
> > least to the level of "I want this to happen on the next vblank (after
> > rendering completes), or give me an error" vs "I want this to complete
> > atomically even if it takes a few extra vblanks for things to sort
> > out"..
> > 
> > I guess that amounts to what you mean by VBLANK_SYNCED flag, but
> > VBLANKED_SYNCED might not be a good name.. at least for some hw all
> > you can do is vblank sync'd..
> 
> Hm, we might as well go full monty and allow userspace to request a
> specific vblank counter. Would be useful for e.g. queuing up a bunch of
> video frames, which some hw can do. But that would then require cancelling
> of existing flips,

The hard part there would be rolling back the user visible state. But
if we were to disallow it for anything but simple page flips, we could
rather easily keep the fb pointers around in the structure that tracks
the progress of the operation.

Otherwise cancelling should be trivial when using mmio instead of the
cs.

But we could certainly do the target vblank thing without allowing
multiple queued flips initially, and without a cancel capability.

However using vblank counter is a bit problematic with multiple crtcs.
But I guess userspace can try to do the something_else->vbl conversion
itself if it wants to use some other units.

There's also the question whether we should allow a target vbl count for
each crtc, or just one of them.

We could make it so that you can specify the target vblank count only
for a single crtc, and the rest of the crtcs will flip soon before or
soon after. The reason for allowing the "soon before" case is because
it'll make the implementation much simpler. We just have to perform
all the register writes somewhere between 'target_vbl-1 - target_vbl'
of the specified crtc. The order in which the flips actually happen
then depends on the vblank period and phase of each crtc.

If user space wants target counts for all crtcs, it could issue separate
nuclear flips for each crtc, although that does raise the issue that we
can't check the entire target state, so we can't guarantee that all of
the nuclear flips will succeed. So that's a bit bad.

So I guess we could allow a target vbl count for all the crtcs, just
need to convey that information inside another array in the ioctl to
avoid imposing a limit in the number of crtcs. But then there's the
question what happens if only a subset of the involved crtcs have a
target count. Return an error? Or just pick one of the crtcs that
did get a target vblank and flip the rest at the same time? I guess
the latter option is better to allow one crtc to act as the master
clock for the whole thing, and the rest just hop along as best they
can.

-- 
Ville Syrjälä
Intel OTC

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-28 14:14                     ` Ville Syrjälä
@ 2014-05-28 14:50                       ` Daniel Vetter
  0 siblings, 0 replies; 69+ messages in thread
From: Daniel Vetter @ 2014-05-28 14:50 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: dri-devel@lists.freedesktop.org

On Wed, May 28, 2014 at 05:14:21PM +0300, Ville Syrjälä wrote:
> On Wed, May 28, 2014 at 03:21:41PM +0200, Daniel Vetter wrote:
> > On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
> > > On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > > > On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
> > > >> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> > > >
> > > > [snip]
> > > >
> > > >> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
> > > >> should hang so much off of that one flag.
> > > >
> > > > Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
> > > > want non-blocking modesets.
> > > 
> > > random-diverging-from-original-topic-thought.. seems like userspace
> > > just wants a deadline/timeout (hopefully in units of vblanks).. at
> > > least to the level of "I want this to happen on the next vblank (after
> > > rendering completes), or give me an error" vs "I want this to complete
> > > atomically even if it takes a few extra vblanks for things to sort
> > > out"..
> > > 
> > > I guess that amounts to what you mean by VBLANK_SYNCED flag, but
> > > VBLANKED_SYNCED might not be a good name.. at least for some hw all
> > > you can do is vblank sync'd..
> > 
> > Hm, we might as well go full monty and allow userspace to request a
> > specific vblank counter. Would be useful for e.g. queuing up a bunch of
> > video frames, which some hw can do. But that would then require cancelling
> > of existing flips,
> 
> The hard part there would be rolling back the user visible state. But
> if we were to disallow it for anything but simple page flips, we could
> rather easily keep the fb pointers around in the structure that tracks
> the progress of the operation.
> 
> Otherwise cancelling should be trivial when using mmio instead of the
> cs.
> 
> But we could certainly do the target vblank thing without allowing
> multiple queued flips initially, and without a cancel capability.
> 
> However using vblank counter is a bit problematic with multiple crtcs.
> But I guess userspace can try to do the something_else->vbl conversion
> itself if it wants to use some other units.
> 
> There's also the question whether we should allow a target vbl count for
> each crtc, or just one of them.
> 
> We could make it so that you can specify the target vblank count only
> for a single crtc, and the rest of the crtcs will flip soon before or
> soon after. The reason for allowing the "soon before" case is because
> it'll make the implementation much simpler. We just have to perform
> all the register writes somewhere between 'target_vbl-1 - target_vbl'
> of the specified crtc. The order in which the flips actually happen
> then depends on the vblank period and phase of each crtc.
> 
> If user space wants target counts for all crtcs, it could issue separate
> nuclear flips for each crtc, although that does raise the issue that we
> can't check the entire target state, so we can't guarantee that all of
> the nuclear flips will succeed. So that's a bit bad.
> 
> So I guess we could allow a target vbl count for all the crtcs, just
> need to convey that information inside another array in the ioctl to
> avoid imposing a limit in the number of crtcs. But then there's the
> question what happens if only a subset of the involved crtcs have a
> target count. Return an error? Or just pick one of the crtcs that
> did get a target vblank and flip the rest at the same time? I guess
> the latter option is better to allow one crtc to act as the master
> clock for the whole thing, and the rest just hop along as best they
> can.

I guess if userspace asks for a target count on more than one crtc we'd
reject the request. That leaves a bit a window unfortunately where
userspace partially submitted a request and then the kernel starts to tell
it that it's too late. But then I don't think we can really avoid this
situation without going completely nuts on the implementation side.

So a best effort (maybe with the guarantee that if we're too late it wont
happen so that userspace could try to render the next frame and display
that for smoothness) approach should be good enough here.

Anyway, let's not get too distracted with fancy ideas before we have the
basics ;-)

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

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

* Re: [PATCH 14/17] drm/msm: add atomic support
  2014-05-28 13:21                   ` Daniel Vetter
  2014-05-28 14:14                     ` Ville Syrjälä
@ 2014-05-28 15:19                     ` Rob Clark
  1 sibling, 0 replies; 69+ messages in thread
From: Rob Clark @ 2014-05-28 15:19 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel@lists.freedesktop.org

On Wed, May 28, 2014 at 9:21 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Tue, May 27, 2014 at 07:32:46PM -0400, Rob Clark wrote:
>> On Tue, May 27, 2014 at 6:09 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> > On Tue, May 27, 2014 at 04:06:28PM -0400, Rob Clark wrote:
>> >> On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> >
>> > [snip]
>> >
>> >> Well, there was the NONBLOCK atomic flag.. I'm not entirely sure if we
>> >> should hang so much off of that one flag.
>> >
>> > Yeah, a separate VBLANK_SYNCED might be useful. Apparently people also
>> > want non-blocking modesets.
>>
>> random-diverging-from-original-topic-thought.. seems like userspace
>> just wants a deadline/timeout (hopefully in units of vblanks).. at
>> least to the level of "I want this to happen on the next vblank (after
>> rendering completes), or give me an error" vs "I want this to complete
>> atomically even if it takes a few extra vblanks for things to sort
>> out"..
>>
>> I guess that amounts to what you mean by VBLANK_SYNCED flag, but
>> VBLANKED_SYNCED might not be a good name.. at least for some hw all
>> you can do is vblank sync'd..
>
> Hm, we might as well go full monty and allow userspace to request a
> specific vblank counter. Would be useful for e.g. queuing up a bunch of
> video frames, which some hw can do. But that would then require cancelling
> of existing flips, so I guess for now a simple VBLANK_SYNCED flag which
> emulates pageflip behaviour should be good enough.

The full on vblank counter and queue stuff up could be interesting..
not entirely sure how we'd manage ->atomic_check() against a not yet
applied base state.  I suppose it is just a matter of copying the
previous-state values for new state objects from the not-yet applied
state, rather than {plane,crtc,etc}->state..

And since state objects are refcnt'd I guess forming a linked list out
of 'em would not be hard.

Probably we should not allow queuing in the beginning.  But does kinda
seem like some magic is possible to handle this.

> That would also be useful if userspace attempts an atomic update on
> drivers which only support atomic modeset (through the crtc helpers), but
> can't do vblank synced updates. Then they could easily reject those. After
> all current drivers also often lack a pageflip implementation ...

yeah.. but I guess this mainly the server chips and old stuff?  I
wonder how many drivers support multiple crtcs but not pageflip?

BR,
-R

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

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

end of thread, other threads:[~2014-05-28 15:19 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-24 18:30 [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Rob Clark
2014-05-24 18:30 ` [PATCH 01/17] drm: fix typo Rob Clark
2014-05-24 18:30 ` [PATCH 02/17] drm: add atomic fxns Rob Clark
2014-05-24 18:30 ` [PATCH 03/17] drm: convert crtc and mode_config to ww_mutex Rob Clark
2014-05-25 22:10   ` Daniel Vetter
2014-05-25 23:16     ` Rob Clark
2014-05-26  8:23       ` Daniel Vetter
2014-05-26 11:56         ` Rob Clark
2014-05-26 14:35           ` Daniel Vetter
2014-05-26 14:36             ` Daniel Vetter
2014-05-26 15:04             ` Rob Clark
2014-05-26 15:07               ` Daniel Vetter
2014-05-26 15:20                 ` Rob Clark
2014-05-26 15:35                   ` Daniel Vetter
2014-05-26 15:49                     ` Rob Clark
2014-05-26 16:09                       ` Daniel Vetter
2014-05-24 18:30 ` [PATCH 04/17] drm: add object property type Rob Clark
2014-05-26  8:29   ` Daniel Vetter
2014-05-26  8:33     ` Daniel Vetter
2014-05-26 11:06     ` Rob Clark
2014-05-24 18:30 ` [PATCH 05/17] drm: add signed-range " Rob Clark
2014-05-24 18:30 ` [PATCH 06/17] drm: helpers to find mode objects Rob Clark
2014-05-26  8:37   ` Daniel Vetter
2014-05-26  8:55     ` Daniel Vetter
2014-05-26 11:12     ` Rob Clark
2014-05-24 18:30 ` [PATCH 07/17] drm: split propvals out and blob property support Rob Clark
2014-05-24 18:30 ` [PATCH 08/17] drm: Allow drm_mode_object_find() to look up an object of any type Rob Clark
2014-05-24 18:30 ` [PATCH 09/17] drm: Refactor object property check code Rob Clark
2014-05-24 18:30 ` [PATCH 10/17] drm: allow FB's in drm_mode_object_find Rob Clark
2014-05-26  8:39   ` Daniel Vetter
2014-05-24 18:30 ` [PATCH 11/17] drm: convert plane to properties/state Rob Clark
2014-05-26  9:12   ` Daniel Vetter
2014-05-26 11:32     ` Rob Clark
2014-05-26 14:52       ` Daniel Vetter
2014-05-24 18:30 ` [PATCH 12/17] drm: convert crtc " Rob Clark
2014-05-26  9:31   ` Daniel Vetter
2014-05-26 11:35     ` Rob Clark
2014-05-26 14:56       ` Daniel Vetter
2014-05-26 15:15         ` Rob Clark
2014-05-26 15:23     ` Ville Syrjälä
2014-05-26 15:37       ` Daniel Vetter
2014-05-26 15:42         ` Rob Clark
2014-05-26 15:46         ` Ville Syrjälä
2014-05-26 16:12           ` Daniel Vetter
2014-05-24 18:30 ` [PATCH 13/17] drm: push locking down into restore_fbdev_mode Rob Clark
2014-05-26  9:34   ` Daniel Vetter
2014-05-24 18:30 ` [PATCH 14/17] drm/msm: add atomic support Rob Clark
2014-05-26 17:54   ` Daniel Vetter
2014-05-27 15:58     ` Rob Clark
2014-05-27 17:50       ` Daniel Vetter
2014-05-27 18:48         ` Rob Clark
2014-05-27 19:26           ` Daniel Vetter
2014-05-27 20:06             ` Rob Clark
2014-05-27 22:09               ` Daniel Vetter
2014-05-27 23:32                 ` Rob Clark
2014-05-28 13:21                   ` Daniel Vetter
2014-05-28 14:14                     ` Ville Syrjälä
2014-05-28 14:50                       ` Daniel Vetter
2014-05-28 15:19                     ` Rob Clark
2014-05-27 23:47                 ` Rob Clark
2014-05-28 13:32                   ` Daniel Vetter
2014-05-24 18:30 ` [PATCH 15/17] drm: spiff out FB refcnting traces Rob Clark
2014-05-24 18:30 ` [PATCH 16/17] drm: more conservative locking Rob Clark
2014-05-24 18:30 ` [PATCH 17/17] drm: Fix up the atomic legacy paths so they work Rob Clark
2014-05-26 10:40 ` [PATCH 00/17] prepare for atomic/nuclear modeset/pageflip Daniel Vetter
2014-05-26 12:48   ` Rob Clark
2014-05-26 15:24     ` Daniel Vetter
2014-05-26 16:12       ` Rob Clark
2014-05-26 17:36         ` Daniel Vetter

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.