Skip to contentSkip to conversation

prepare for atomic/nuclear modeset/pageflip

PATCH In review Rob Clark 3 participants 69 messages May 24, 2014 – May 28, 2014 Page 2 of 2

Subsystems (20)
lore
Share & tools
lore
author
#44 lore raw

PATCH 13/17 drm: push locking down into restore_fbdev_mode

In reply to Rob Clark

All the call-sites save one need locking. By pushing it down and adding a lockless flag, we can use the new spiffy atomic ww_mutex crtc locking and simplify all the call-sites.

---
 drivers/gpu/drm/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(-)
drivers/gpu/drm/armada/armada_fbdev.c +1 -3
index 948cb14..9976125 100644
@@ -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)
drivers/gpu/drm/drm_fb_cma_helper.c +2 -7
index 65540b7..ccb7a9ac 100644
@@ -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);
 
drivers/gpu/drm/drm_fb_helper.c +8 -9
index 4669e69..3815e1a 100644
@@ -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;
drivers/gpu/drm/exynos/exynos_drm_fbdev.c +1 -3
index 2371716..486b21f 100644
@@ -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);
 }
drivers/gpu/drm/gma500/psb_drv.c +1 -3
index 0180292..72a35e3 100644
@@ -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;
 }
drivers/gpu/drm/i915/intel_fbdev.c +1 -5
index fce4a0d..28afd69 100644
@@ -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);
 }
drivers/gpu/drm/msm/msm_drv.c +2 -5
index 46e0f29..d24ca45 100644
@@ -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)
drivers/gpu/drm/omapdrm/omap_drv.c +1 -3
index 3f64c47..ad082f4 100644
@@ -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");
 }
drivers/gpu/drm/tegra/fb.c +2 -5
index f7fca09..85aae37 100644
@@ -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)
include/drm/drm_fb_helper.h +2 -1
index 6e622f7..99598a0 100644
@@ -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,
Signature · 1 line
1.9.0
#45 lore raw

Re: [PATCH 13/17] drm: push locking down into restore_fbdev_mode

Quoted 5 lines · Rob Clark

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.

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 <[email protected]>

Quoted 225 lines
---
 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(-)
drivers/gpu/drm/armada/armada_fbdev.c +1 -3
index 948cb14..9976125 100644
@@ -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)
drivers/gpu/drm/drm_fb_cma_helper.c +2 -7
index 65540b7..ccb7a9ac 100644
@@ -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);
 
drivers/gpu/drm/drm_fb_helper.c +8 -9
index 4669e69..3815e1a 100644
@@ -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;
drivers/gpu/drm/exynos/exynos_drm_fbdev.c +1 -3
index 2371716..486b21f 100644
@@ -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);
 }
drivers/gpu/drm/gma500/psb_drv.c +1 -3
index 0180292..72a35e3 100644
@@ -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;
 }
drivers/gpu/drm/i915/intel_fbdev.c +1 -5
index fce4a0d..28afd69 100644
@@ -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);
 }
drivers/gpu/drm/msm/msm_drv.c +2 -5
index 46e0f29..d24ca45 100644
@@ -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)
drivers/gpu/drm/omapdrm/omap_drv.c +1 -3
index 3f64c47..ad082f4 100644
@@ -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");
 }
drivers/gpu/drm/tegra/fb.c +2 -5
index f7fca09..85aae37 100644
@@ -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)
include/drm/drm_fb_helper.h +2 -1
index 6e622f7..99598a0 100644
@@ -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,
Signature · 1 line
1.9.0
Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#46 lore raw

PATCH 14/17 drm/msm: add atomic support

In reply to Rob Clark
---
 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
drivers/gpu/drm/msm/Makefile +1 -0
index 5e1e6b0..dd12f56 100644
@@ -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 \
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +26 -31

140 lines not on this page · raw message

drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +6 -0
index 0bb4faa..af0bfb1 100644
@@ -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,
 	},
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +1 -0
index 715520c5..834454c 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +0 -5
index 4c92985..f35848d 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +25 -31

140 lines not on this page · raw message

drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +6 -0
index ee8446c..01c3d70 100644
@@ -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,
 	},
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +1 -1
index c8b1a25..18b031b 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +0 -5
index 53cc8c6..f1bf3c2 100644
@@ -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);
drivers/gpu/drm/msm/msm_atomic.c new file +141 -0

141 lines not on this page · raw message

drivers/gpu/drm/msm/msm_drv.c +21 -1
index d24ca45..bcee218 100644
@@ -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
drivers/gpu/drm/msm/msm_drv.h +7 -0
index afc990e..5e1e7da 100644
@@ -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);
drivers/gpu/drm/msm/msm_gem.c +4 -20
index bb8026d..90cb138 100644
@@ -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;
 
drivers/gpu/drm/msm/msm_gem.h +13 -0
index 3246bb4..41a60cb 100644
@@ -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,
drivers/gpu/drm/msm/msm_kms.h +1 -0
index 0643774..91ddffc 100644
@@ -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);
Signature · 1 line
1.9.0
#47 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 585 lines · Rob Clark
---
 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
drivers/gpu/drm/msm/Makefile +1 -0
index 5e1e6b0..dd12f56 100644
@@ -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 \
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +26 -31
index d0d8befd..2fa6b75 100644
@@ -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);
 
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +6 -0
index 0bb4faa..af0bfb1 100644
@@ -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,
 	},
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +1 -0
index 715520c5..834454c 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +0 -5
index 4c92985..f35848d 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +25 -31
index 7f4ee99..0e74317 100644
@@ -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);
 
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +6 -0
index ee8446c..01c3d70 100644
@@ -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,
 	},
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +1 -1
index c8b1a25..18b031b 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +0 -5
index 53cc8c6..f1bf3c2 100644
@@ -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);
drivers/gpu/drm/msm/msm_atomic.c new file +131 -0
new file mode 100644
index 0000000..b231377
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email protected]>
+ *
+ * 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;
+

drivers/gpu/drm/msm/msm_atomic.c line 141

Quoted 10 lines
drivers/gpu/drm/msm/msm_atomic.c new file +10 -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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#48 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 611 lines · Daniel Vetter
Quoted 585 lines · Rob Clark
---
 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
drivers/gpu/drm/msm/Makefile +1 -0
index 5e1e6b0..dd12f56 100644
@@ -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 \
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +26 -31
index d0d8befd..2fa6b75 100644
@@ -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);
 
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +6 -0
index 0bb4faa..af0bfb1 100644
@@ -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,
      },
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +1 -0
index 715520c5..834454c 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +0 -5
index 4c92985..f35848d 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +25 -31
index 7f4ee99..0e74317 100644
@@ -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);
 
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +6 -0
index ee8446c..01c3d70 100644
@@ -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,
      },
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h +1 -1
index c8b1a25..18b031b 100644
@@ -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);
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +0 -5
index 53cc8c6..f1bf3c2 100644
@@ -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);
drivers/gpu/drm/msm/msm_atomic.c new file +131 -0
new file mode 100644
index 0000000..b231377
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <[email protected]>
+ *
+ * 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;
+

drivers/gpu/drm/msm/msm_atomic.c line 141

Quoted 10 lines
drivers/gpu/drm/msm/msm_atomic.c new file +10 -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.

Quoted 4 lines

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

Quoted 7 lines

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.

Quoted 9 lines

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.

Quoted 11 lines

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.

Quoted 7 lines

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.

Quoted 4 lines

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

Quoted 11 lines

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

#49 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 30 lines · Rob Clark
Quoted 14 lines · Daniel Vetter

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.

Quoted 4 lines

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

Quoted 5 lines

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.

Quoted 17 lines
Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 6 lines
Quoted 6 lines

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.

Quoted 6 lines
Quoted 3 lines

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.

Quoted 12 lines
Quoted 11 lines

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.

Quoted 4 lines

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.

Quoted 5 lines

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.

Quoted 8 lines
Quoted 7 lines

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.

Quoted 7 lines

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.

Quoted 10 lines
Quoted 4 lines

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.

Quoted 4 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#50 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 46 lines · Daniel Vetter
Quoted 30 lines · Rob Clark
Quoted 14 lines · Daniel Vetter

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.

Quoted 4 lines

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

Quoted 5 lines

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.

Quoted 5 lines

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.

Quoted 73 lines

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.

Quoted 17 lines
Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 6 lines
Quoted 6 lines

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.

Quoted 6 lines
Quoted 3 lines

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.

Quoted 12 lines
Quoted 11 lines

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.

Quoted 8 lines
Quoted 4 lines

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.

Quoted 59 lines
Quoted 5 lines

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.

Quoted 8 lines
Quoted 7 lines

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.

Quoted 7 lines

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.

Quoted 8 lines

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

#51 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 60 lines · Rob Clark
Quoted 46 lines · Daniel Vetter
Quoted 30 lines · Rob Clark
Quoted 14 lines · Daniel Vetter

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.

Quoted 4 lines

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

Quoted 5 lines

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.

Quoted 5 lines

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.

Quoted 6 lines

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.

Quoted 6 lines

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.

Quoted 82 lines
Quoted 73 lines

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.

Quoted 17 lines
Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 6 lines
Quoted 6 lines

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.

Quoted 6 lines
Quoted 3 lines

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.

Quoted 12 lines
Quoted 11 lines

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.

Quoted 8 lines
Quoted 4 lines

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.

Quoted 8 lines

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.

Quoted 68 lines
Quoted 59 lines
Quoted 5 lines

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.

Quoted 8 lines
Quoted 7 lines

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.

Quoted 7 lines

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.

Quoted 8 lines

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.

Quoted 4 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#52 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 82 lines · Daniel Vetter
Quoted 60 lines · Rob Clark
Quoted 46 lines · Daniel Vetter
Quoted 30 lines · Rob Clark
Quoted 14 lines · Daniel Vetter

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.

Quoted 4 lines

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

Quoted 5 lines

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.

Quoted 5 lines

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.

Quoted 6 lines

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.

Quoted 4 lines

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.

Quoted 197 lines
Quoted 6 lines

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.

Quoted 82 lines
Quoted 73 lines

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.

Quoted 17 lines
Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 6 lines
Quoted 6 lines

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.

Quoted 6 lines
Quoted 3 lines

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.

Quoted 12 lines
Quoted 11 lines

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.

Quoted 8 lines
Quoted 4 lines

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.

Quoted 8 lines

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.

Quoted 68 lines
Quoted 59 lines
Quoted 5 lines

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.

Quoted 8 lines
Quoted 7 lines

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.

Quoted 7 lines

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.

Quoted 8 lines

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.

Quoted 4 lines

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

Quoted 3 lines

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

Quoted 5 lines

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

#53 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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]

Quoted 4 lines
Quoted 3 lines

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.

Quoted 10 lines

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

Quoted 5 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#54 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 10 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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

#55 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 19 lines · Rob Clark
Quoted 10 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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

Quoted 3 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
#56 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 20 lines · Daniel Vetter
Quoted 19 lines · Rob Clark
Quoted 10 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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

Quoted 8 lines
Quoted 3 lines

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.

Signature · 2 lines
Ville Syrjälä
Intel OTC
#57 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 61 lines · Ville Syrjälä
Quoted 20 lines · Daniel Vetter
Quoted 19 lines · Rob Clark
Quoted 10 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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

Quoted 8 lines
Quoted 3 lines

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.

Quoted 9 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#58 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

In reply to Daniel Vetter
Quoted 24 lines · Daniel Vetter
Quoted 19 lines · Rob Clark
Quoted 10 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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

Quoted 3 lines

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

Quoted 5 lines

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.

Quoted 4 lines

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

Quoted 5 lines

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

author
#59 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

In reply to Daniel Vetter
Quoted 28 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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]

Quoted 4 lines
Quoted 3 lines

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.

Quoted 10 lines

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)

Quoted 7 lines

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

Quoted 6 lines
Quoted 5 lines

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.

Quoted 6 lines

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

Quoted 8 lines

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

Quoted 5 lines

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

#60 lore raw

Re: [PATCH 14/17] drm/msm: add atomic support

Quoted 37 lines · Rob Clark
Quoted 28 lines · Daniel Vetter
Quoted 1 line · Rob Clark

On Tue, May 27, 2014 at 3:26 PM, Daniel Vetter <[email protected]> wrote:

[snip]

Quoted 2 lines

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]

Quoted 4 lines
Quoted 3 lines

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.

Quoted 10 lines

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)

Quoted 7 lines

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

Quoted 4 lines

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.

Quoted 13 lines
Quoted 6 lines
Quoted 5 lines

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.

Quoted 6 lines

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.

Quoted 5 lines

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.

Quoted 9 lines
Quoted 8 lines

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.

Quoted 4 lines

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.

Quoted 5 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#61 lore raw

PATCH 15/17 drm: spiff out FB refcnting traces

In reply to Rob Clark

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

---
 drivers/gpu/drm/drm_crtc.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpu/drm/drm_crtc.c +3 -3
index e14d517..972af76 100644
@@ -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);
 }
 
Signature · 1 line
1.9.0
author
#62 lore raw

PATCH 16/17 drm: more conservative locking

In reply to Rob Clark

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.

---
 drivers/gpu/drm/drm_crtc.c      | 21 +++++++++++++++++++++
 drivers/gpu/drm/drm_fb_helper.c | 18 +++++++++++++++++-
 2 files changed, 38 insertions(+), 1 deletion(-)
drivers/gpu/drm/drm_crtc.c +21 -0
index 972af76..4e3b527 100644
@@ -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:
drivers/gpu/drm/drm_fb_helper.c +17 -1
index 3815e1a..4dc22cf 100644
@@ -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);
Signature · 1 line
1.9.0
author
#63 lore raw

PATCH 17/17 drm: Fix up the atomic legacy paths so they work

In reply to Rob Clark

From: Sean Paul <[email protected]>

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

Change-Id Icd864ac52da9c973202f3ac4629824ef5eec2ac9
---
 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(-)
drivers/gpu/drm/drm_atomic.c +4 -0
index 863a0fe..79fbb2c 100644
@@ -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);
 
drivers/gpu/drm/drm_crtc.c +9 -3
index 4e3b527..03dddbb 100644
@@ -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.
include/drm/drm_crtc.h +1 -0
index 2fbf13a..009b76f 100644
@@ -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;
Signature · 1 line
1.9.0
#64 lore raw
In reply to Rob Clark
Quoted 24 lines · Rob Clark

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.

Quoted 6 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#65 lore raw
Quoted 47 lines · Daniel Vetter
Quoted 24 lines · Rob Clark

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.

Quoted 6 lines

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

Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 4 lines

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

Quoted 9 lines

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

Quoted 6 lines

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

Quoted 10 lines

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.

Quoted 5 lines

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.

Quoted 8 lines

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.

Quoted 7 lines

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

Quoted 6 lines

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

Quoted 8 lines

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.

Quoted 10 lines

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

Quoted 33 lines

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.

Quoted 2 lines

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

Quoted 6 lines

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

#66 lore raw
Quoted 68 lines · Rob Clark
Quoted 47 lines · Daniel Vetter
Quoted 24 lines · Rob Clark

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.

Quoted 6 lines

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

Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 8 lines

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.

Quoted 10 lines
Quoted 4 lines

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

Quoted 10 lines
Quoted 9 lines

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

Quoted 5 lines

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.

Quoted 6 lines

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

Quoted 9 lines
Quoted 6 lines

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

Quoted 20 lines
Quoted 10 lines

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.

Quoted 5 lines

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.

Quoted 11 lines
Quoted 5 lines

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.

Quoted 9 lines
Quoted 8 lines

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.

Quoted 6 lines

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.

Quoted 14 lines
Quoted 7 lines

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

Quoted 6 lines

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

Quoted 6 lines

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.

Quoted 56 lines
Quoted 8 lines

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.

Quoted 10 lines

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

Quoted 33 lines

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.

Quoted 7 lines
Quoted 2 lines

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.

Quoted 3 lines

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

Signature · 3 lines
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
author
#67 lore raw
Quoted 90 lines · Daniel Vetter
Quoted 68 lines · Rob Clark
Quoted 47 lines · Daniel Vetter
Quoted 24 lines · Rob Clark

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.

Quoted 6 lines

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

Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 8 lines

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.

Quoted 8 lines

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.

Quoted 11 lines
Quoted 10 lines
Quoted 4 lines

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

Quoted 3 lines

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?

Quoted 7 lines

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.

Quoted 30 lines
Quoted 10 lines
Quoted 9 lines

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

Quoted 5 lines

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.

Quoted 6 lines

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.

Quoted 4 lines

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.

Quoted 48 lines
Quoted 9 lines
Quoted 6 lines

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

Quoted 20 lines
Quoted 10 lines

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.

Quoted 5 lines

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.

Quoted 2 lines

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

Quoted 146 lines
Quoted 7 lines
Quoted 6 lines

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.

Quoted 5 lines

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.

Quoted 9 lines
Quoted 8 lines

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.

Quoted 6 lines

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.

Quoted 14 lines
Quoted 7 lines

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

Quoted 6 lines

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

Quoted 6 lines

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.

Quoted 56 lines
Quoted 8 lines

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.

Quoted 10 lines

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

Quoted 33 lines

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.

Quoted 7 lines
Quoted 2 lines

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.

Quoted 3 lines

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

#68 lore raw
Quoted 100 lines · Rob Clark
Quoted 90 lines · Daniel Vetter
Quoted 68 lines · Rob Clark
Quoted 47 lines · Daniel Vetter
Quoted 24 lines · Rob Clark

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.

Quoted 6 lines

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

Quoted 7 lines

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.

Quoted 5 lines

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

Quoted 8 lines

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.

Quoted 8 lines

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.

Quoted 8 lines

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.

Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
Quoted 4 lines

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

Quoted 7 lines
Quoted 3 lines

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 :(

Quoted 8 lines
Quoted 7 lines

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.

Quoted 5 lines

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.

Quoted 35 lines
Quoted 30 lines
Quoted 10 lines
Quoted 9 lines

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

Quoted 5 lines

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.

Quoted 6 lines

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.

Quoted 4 lines

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

Quoted 7 lines

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.

Quoted 48 lines
Quoted 48 lines
Quoted 9 lines
Quoted 6 lines

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

Quoted 20 lines
Quoted 10 lines

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.

Quoted 5 lines

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.

Quoted 6 lines
Quoted 2 lines

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

Quoted 4 lines

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

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