All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] drm/fb-helper: Fix hpd vs. initial config races
@ 2014-04-22 14:42 ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-samsung-soc, Daniel Vetter, linux-tegra, intel-gfx,
	linux-arm-kernel

From: Daniel Vetter <daniel.vetter@ffwll.ch>

Some drivers need to be able to have a perfect race-free fbcon setup.
Current drivers only enable hotplug processing after the call to
drm_fb_helper_initial_config which leaves a tiny but important race.

This race is especially noticable on embedded platforms where the
driver itself enables the voltage for the hdmi output, since only then
will monitors (after a bit of delay, as usual) respond by asserting
the hpd pin.

Most of the infrastructure is already there with the split-out
drm_fb_helper_init. And drm_fb_helper_initial_config already has all
the required locking to handle concurrent hpd events since

commit 53f1904bced78d7c00f5d874c662ec3ac85d0f9f
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date:   Thu Mar 20 14:26:35 2014 +0100

    drm/fb-helper: improve drm_fb_helper_initial_config locking

The only missing bit is making drm_fb_helper_hotplug_event save
against concurrent calls of drm_fb_helper_initial_config. The only
unprotected bit is the check for fb_helper->fb.

With that drivers can first initialize the fb helper, then enabel
hotplug processing and then set up the initial config all in a
completely race-free manner. Update kerneldoc and convert i915 as a
proof of concept.

Feature requested by Thierry since his tegra driver atm reliably boots
slowly enough to misses the hotplug event for an external hdmi screen,
but also reliably boots to quickly for the hpd pin to be asserted when
the fb helper calls into the hdmi ->detect function.

Cc: Thierry Reding <treding@nvidia.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_fb_helper.c | 11 +++++------
 drivers/gpu/drm/i915/i915_dma.c |  3 ---
 drivers/gpu/drm/i915/i915_drv.c |  2 --
 drivers/gpu/drm/i915/i915_drv.h |  1 -
 drivers/gpu/drm/i915/i915_irq.c |  4 ----
 5 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index e95ed5805f07..80ce92ab91f9 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1587,8 +1587,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  * either the output polling work or a work item launched from the driver's
  * hotplug interrupt).
  *
- * Note that the driver must ensure that this is only called _after_ the fb has
- * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
+ * Note that drivers may call this even before calling
+ * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
+ * for a race-free fbcon setup and will make sure that the fbdev emulation will
+ * not miss any hotplug events.
  *
  * RETURNS:
  * 0 on success and a non-zero error code otherwise.
@@ -1598,11 +1600,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	u32 max_width, max_height;
 
-	if (!fb_helper->fb)
-		return 0;
-
 	mutex_lock(&fb_helper->dev->mode_config.mutex);
-	if (!drm_fb_helper_is_bound(fb_helper)) {
+	if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
 		fb_helper->delayed_hotplug = true;
 		mutex_unlock(&fb_helper->dev->mode_config.mutex);
 		return 0;
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index cc0c6eded51c..89ba88d37ae1 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1375,9 +1375,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
 	 */
 	intel_fbdev_initial_config(dev);
 
-	/* Only enable hotplug handling once the fbdev is fully set up. */
-	dev_priv->enable_hotplug_processing = true;
-
 	drm_kms_helper_poll_init(dev);
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 254b3236200b..dee36a5b7fae 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -448,7 +448,6 @@ static int i915_drm_freeze(struct drm_device *dev)
 		cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
 
 		drm_irq_uninstall(dev);
-		dev_priv->enable_hotplug_processing = false;
 		/*
 		 * Disable CRTCs directly since we want to preserve sw state
 		 * for _thaw.
@@ -589,7 +588,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 		 * notifications.
 		 * */
 		intel_hpd_init(dev);
-		dev_priv->enable_hotplug_processing = true;
 		/* Config may have changed between suspend and resume */
 		intel_resume_hotplug(dev);
 	}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7d6acb401fd9..41094d6357b1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1321,7 +1321,6 @@ struct drm_i915_private {
 	u32 pipestat_irq_mask[I915_MAX_PIPES];
 
 	struct work_struct hotplug_work;
-	bool enable_hotplug_processing;
 	struct {
 		unsigned long hpd_last_jiffies;
 		int hpd_cnt;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index c828f5b78250..179588b2368f 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1003,10 +1003,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	bool changed = false;
 	u32 hpd_event_bits;
 
-	/* HPD irq before everything is fully set up. */
-	if (!dev_priv->enable_hotplug_processing)
-		return;
-
 	mutex_lock(&mode_config->mutex);
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
-- 
1.9.2

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

* [PATCH 1/4] drm/fb-helper: Fix hpd vs. initial config races
@ 2014-04-22 14:42 ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: linux-arm-kernel

From: Daniel Vetter <daniel.vetter@ffwll.ch>

Some drivers need to be able to have a perfect race-free fbcon setup.
Current drivers only enable hotplug processing after the call to
drm_fb_helper_initial_config which leaves a tiny but important race.

This race is especially noticable on embedded platforms where the
driver itself enables the voltage for the hdmi output, since only then
will monitors (after a bit of delay, as usual) respond by asserting
the hpd pin.

Most of the infrastructure is already there with the split-out
drm_fb_helper_init. And drm_fb_helper_initial_config already has all
the required locking to handle concurrent hpd events since

commit 53f1904bced78d7c00f5d874c662ec3ac85d0f9f
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date:   Thu Mar 20 14:26:35 2014 +0100

    drm/fb-helper: improve drm_fb_helper_initial_config locking

The only missing bit is making drm_fb_helper_hotplug_event save
against concurrent calls of drm_fb_helper_initial_config. The only
unprotected bit is the check for fb_helper->fb.

With that drivers can first initialize the fb helper, then enabel
hotplug processing and then set up the initial config all in a
completely race-free manner. Update kerneldoc and convert i915 as a
proof of concept.

Feature requested by Thierry since his tegra driver atm reliably boots
slowly enough to misses the hotplug event for an external hdmi screen,
but also reliably boots to quickly for the hpd pin to be asserted when
the fb helper calls into the hdmi ->detect function.

Cc: Thierry Reding <treding@nvidia.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_fb_helper.c | 11 +++++------
 drivers/gpu/drm/i915/i915_dma.c |  3 ---
 drivers/gpu/drm/i915/i915_drv.c |  2 --
 drivers/gpu/drm/i915/i915_drv.h |  1 -
 drivers/gpu/drm/i915/i915_irq.c |  4 ----
 5 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index e95ed5805f07..80ce92ab91f9 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1587,8 +1587,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  * either the output polling work or a work item launched from the driver's
  * hotplug interrupt).
  *
- * Note that the driver must ensure that this is only called _after_ the fb has
- * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
+ * Note that drivers may call this even before calling
+ * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
+ * for a race-free fbcon setup and will make sure that the fbdev emulation will
+ * not miss any hotplug events.
  *
  * RETURNS:
  * 0 on success and a non-zero error code otherwise.
@@ -1598,11 +1600,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	u32 max_width, max_height;
 
-	if (!fb_helper->fb)
-		return 0;
-
 	mutex_lock(&fb_helper->dev->mode_config.mutex);
-	if (!drm_fb_helper_is_bound(fb_helper)) {
+	if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
 		fb_helper->delayed_hotplug = true;
 		mutex_unlock(&fb_helper->dev->mode_config.mutex);
 		return 0;
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index cc0c6eded51c..89ba88d37ae1 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1375,9 +1375,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
 	 */
 	intel_fbdev_initial_config(dev);
 
-	/* Only enable hotplug handling once the fbdev is fully set up. */
-	dev_priv->enable_hotplug_processing = true;
-
 	drm_kms_helper_poll_init(dev);
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 254b3236200b..dee36a5b7fae 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -448,7 +448,6 @@ static int i915_drm_freeze(struct drm_device *dev)
 		cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work);
 
 		drm_irq_uninstall(dev);
-		dev_priv->enable_hotplug_processing = false;
 		/*
 		 * Disable CRTCs directly since we want to preserve sw state
 		 * for _thaw.
@@ -589,7 +588,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 		 * notifications.
 		 * */
 		intel_hpd_init(dev);
-		dev_priv->enable_hotplug_processing = true;
 		/* Config may have changed between suspend and resume */
 		intel_resume_hotplug(dev);
 	}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7d6acb401fd9..41094d6357b1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1321,7 +1321,6 @@ struct drm_i915_private {
 	u32 pipestat_irq_mask[I915_MAX_PIPES];
 
 	struct work_struct hotplug_work;
-	bool enable_hotplug_processing;
 	struct {
 		unsigned long hpd_last_jiffies;
 		int hpd_cnt;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index c828f5b78250..179588b2368f 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1003,10 +1003,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	bool changed = false;
 	u32 hpd_event_bits;
 
-	/* HPD irq before everything is fully set up. */
-	if (!dev_priv->enable_hotplug_processing)
-		return;
-
 	mutex_lock(&mode_config->mutex);
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
-- 
1.9.2

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

* [PATCH 2/4] drm: Constify struct drm_fb_helper_funcs
  2014-04-22 14:42 ` Thierry Reding
@ 2014-04-22 14:42     ` Thierry Reding
  -1 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
  Cc: Daniel Vetter, Imre Deak,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	intel-gfx-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

There's no need for this to be modifiable. Make it const so that it can
be put into the .rodata section.

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/gpu/drm/armada/armada_fbdev.c     | 2 +-
 drivers/gpu/drm/ast/ast_fb.c              | 2 +-
 drivers/gpu/drm/bochs/bochs_fbdev.c       | 2 +-
 drivers/gpu/drm/cirrus/cirrus_fbdev.c     | 2 +-
 drivers/gpu/drm/drm_fb_cma_helper.c       | 2 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 +-
 drivers/gpu/drm/gma500/framebuffer.c      | 2 +-
 drivers/gpu/drm/i915/intel_fbdev.c        | 2 +-
 drivers/gpu/drm/mgag200/mgag200_fb.c      | 2 +-
 drivers/gpu/drm/msm/msm_fbdev.c           | 2 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c   | 2 +-
 drivers/gpu/drm/omapdrm/omap_fbdev.c      | 2 +-
 drivers/gpu/drm/qxl/qxl_fb.c              | 2 +-
 drivers/gpu/drm/radeon/radeon_fb.c        | 2 +-
 drivers/gpu/drm/tegra/fb.c                | 2 +-
 drivers/gpu/drm/udl/udl_fb.c              | 2 +-
 include/drm/drm_fb_helper.h               | 2 +-
 17 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 948cb14c561e..21aa1261dba2 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
 	return ret;
 }
 
-static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
 	.gamma_set	= armada_drm_crtc_gamma_set,
 	.gamma_get	= armada_drm_crtc_gamma_get,
 	.fb_probe	= armada_fb_probe,
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index a28640f47c27..2113894e4ff8 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue = ast_crtc->lut_b[regno] << 8;
 }
 
-static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
 	.gamma_set = ast_fb_gamma_set,
 	.gamma_get = ast_fb_gamma_get,
 	.fb_probe = astfb_create,
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index 561b84474122..17e5c17f2730 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue  = regno;
 }
 
-static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
 	.gamma_set = bochs_fb_gamma_set,
 	.gamma_get = bochs_fb_gamma_get,
 	.fb_probe = bochsfb_create,
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index 32bbba0a787b..2bd0291168e4 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
 	return 0;
 }
 
-static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
 	.gamma_set = cirrus_crtc_fb_gamma_set,
 	.gamma_get = cirrus_crtc_fb_gamma_get,
 	.fb_probe = cirrusfb_create,
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 61b5a47ad239..b74f9e58b69d 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
+static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
 	.fb_probe = drm_fbdev_cma_create,
 };
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index addbf7536da4..7ccf04917f47 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -233,7 +233,7 @@ out:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
 	.fb_probe =	exynos_drm_fbdev_create,
 };
 
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index e7fcc148f333..76e4d777d01d 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
 	return psbfb_create(psb_fbdev, sizes);
 }
 
-static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
 	.gamma_set = psbfb_gamma_set,
 	.gamma_get = psbfb_gamma_get,
 	.fb_probe = psbfb_probe,
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index b4d44e62f0c7..336a89f2549e 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -429,7 +429,7 @@ out:
 	return true;
 }
 
-static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
 	.initial_config = intel_fb_initial_config,
 	.gamma_set = intel_crtc_fb_gamma_set,
 	.gamma_get = intel_crtc_fb_gamma_get,
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index 13b7dd83faa9..a4319aba9180 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
 	return 0;
 }
 
-static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
 	.gamma_set = mga_crtc_fb_gamma_set,
 	.gamma_get = mga_crtc_fb_gamma_get,
 	.fb_probe = mgag200fb_create,
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index 6c6d7d4c9b4e..841665862f2f 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -180,7 +180,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
 	DBG("fbdev: get gamma");
 }
 
-static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
 	.gamma_set = msm_crtc_fb_gamma_set,
 	.gamma_get = msm_crtc_fb_gamma_get,
 	.fb_probe = msm_fbdev_create,
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 64a42cfd3717..8e9c07b7fc89 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 	info->flags |= FBINFO_HWACCEL_DISABLED;
 }
 
-static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
 	.gamma_set = nouveau_fbcon_gamma_set,
 	.gamma_get = nouveau_fbcon_gamma_get,
 	.fb_probe = nouveau_fbcon_create,
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 1388ca7f87e8..4cb12083eb12 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -281,7 +281,7 @@ fail:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
 	.fb_probe = omap_fbdev_create,
 };
 
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index f437b30ce689..cf89614c72be 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
 	return 0;
 }
 
-static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
 	.fb_probe = qxl_fb_find_or_create_single,
 };
 
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 665ced3b7313..ad97afdbc4c7 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
 	return 0;
 }
 
-static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
 	.gamma_set = radeon_crtc_fb_gamma_set,
 	.gamma_get = radeon_crtc_fb_gamma_get,
 	.fb_probe = radeonfb_create,
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index f7fca09d4921..75bed72a9755 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -267,7 +267,7 @@ release:
 	return err;
 }
 
-static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
 	.fb_probe = tegra_fbdev_probe,
 };
 
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 377176372da8..0647c8cc368b 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -550,7 +550,7 @@ out:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
 	.fb_probe = udlfb_create,
 };
 
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 6e622f7d481d..e8c8eeb3a253 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -87,7 +87,7 @@ struct drm_fb_helper {
 	struct drm_fb_helper_crtc *crtc_info;
 	int connector_count;
 	struct drm_fb_helper_connector **connector_info;
-	struct drm_fb_helper_funcs *funcs;
+	const struct drm_fb_helper_funcs *funcs;
 	struct fb_info *fbdev;
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
-- 
1.9.2

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

* [PATCH 2/4] drm: Constify struct drm_fb_helper_funcs
@ 2014-04-22 14:42     ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

There's no need for this to be modifiable. Make it const so that it can
be put into the .rodata section.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/armada/armada_fbdev.c     | 2 +-
 drivers/gpu/drm/ast/ast_fb.c              | 2 +-
 drivers/gpu/drm/bochs/bochs_fbdev.c       | 2 +-
 drivers/gpu/drm/cirrus/cirrus_fbdev.c     | 2 +-
 drivers/gpu/drm/drm_fb_cma_helper.c       | 2 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 +-
 drivers/gpu/drm/gma500/framebuffer.c      | 2 +-
 drivers/gpu/drm/i915/intel_fbdev.c        | 2 +-
 drivers/gpu/drm/mgag200/mgag200_fb.c      | 2 +-
 drivers/gpu/drm/msm/msm_fbdev.c           | 2 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c   | 2 +-
 drivers/gpu/drm/omapdrm/omap_fbdev.c      | 2 +-
 drivers/gpu/drm/qxl/qxl_fb.c              | 2 +-
 drivers/gpu/drm/radeon/radeon_fb.c        | 2 +-
 drivers/gpu/drm/tegra/fb.c                | 2 +-
 drivers/gpu/drm/udl/udl_fb.c              | 2 +-
 include/drm/drm_fb_helper.h               | 2 +-
 17 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 948cb14c561e..21aa1261dba2 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
 	return ret;
 }
 
-static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
 	.gamma_set	= armada_drm_crtc_gamma_set,
 	.gamma_get	= armada_drm_crtc_gamma_get,
 	.fb_probe	= armada_fb_probe,
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index a28640f47c27..2113894e4ff8 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue = ast_crtc->lut_b[regno] << 8;
 }
 
-static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
 	.gamma_set = ast_fb_gamma_set,
 	.gamma_get = ast_fb_gamma_get,
 	.fb_probe = astfb_create,
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index 561b84474122..17e5c17f2730 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue  = regno;
 }
 
-static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
 	.gamma_set = bochs_fb_gamma_set,
 	.gamma_get = bochs_fb_gamma_get,
 	.fb_probe = bochsfb_create,
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index 32bbba0a787b..2bd0291168e4 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
 	return 0;
 }
 
-static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
 	.gamma_set = cirrus_crtc_fb_gamma_set,
 	.gamma_get = cirrus_crtc_fb_gamma_get,
 	.fb_probe = cirrusfb_create,
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 61b5a47ad239..b74f9e58b69d 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
+static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
 	.fb_probe = drm_fbdev_cma_create,
 };
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index addbf7536da4..7ccf04917f47 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -233,7 +233,7 @@ out:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
 	.fb_probe =	exynos_drm_fbdev_create,
 };
 
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index e7fcc148f333..76e4d777d01d 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
 	return psbfb_create(psb_fbdev, sizes);
 }
 
-static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
 	.gamma_set = psbfb_gamma_set,
 	.gamma_get = psbfb_gamma_get,
 	.fb_probe = psbfb_probe,
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index b4d44e62f0c7..336a89f2549e 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -429,7 +429,7 @@ out:
 	return true;
 }
 
-static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
 	.initial_config = intel_fb_initial_config,
 	.gamma_set = intel_crtc_fb_gamma_set,
 	.gamma_get = intel_crtc_fb_gamma_get,
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index 13b7dd83faa9..a4319aba9180 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
 	return 0;
 }
 
-static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
 	.gamma_set = mga_crtc_fb_gamma_set,
 	.gamma_get = mga_crtc_fb_gamma_get,
 	.fb_probe = mgag200fb_create,
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index 6c6d7d4c9b4e..841665862f2f 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -180,7 +180,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
 	DBG("fbdev: get gamma");
 }
 
-static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
 	.gamma_set = msm_crtc_fb_gamma_set,
 	.gamma_get = msm_crtc_fb_gamma_get,
 	.fb_probe = msm_fbdev_create,
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 64a42cfd3717..8e9c07b7fc89 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 	info->flags |= FBINFO_HWACCEL_DISABLED;
 }
 
-static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
 	.gamma_set = nouveau_fbcon_gamma_set,
 	.gamma_get = nouveau_fbcon_gamma_get,
 	.fb_probe = nouveau_fbcon_create,
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 1388ca7f87e8..4cb12083eb12 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -281,7 +281,7 @@ fail:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
 	.fb_probe = omap_fbdev_create,
 };
 
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index f437b30ce689..cf89614c72be 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
 	return 0;
 }
 
-static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
 	.fb_probe = qxl_fb_find_or_create_single,
 };
 
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 665ced3b7313..ad97afdbc4c7 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
 	return 0;
 }
 
-static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
 	.gamma_set = radeon_crtc_fb_gamma_set,
 	.gamma_get = radeon_crtc_fb_gamma_get,
 	.fb_probe = radeonfb_create,
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index f7fca09d4921..75bed72a9755 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -267,7 +267,7 @@ release:
 	return err;
 }
 
-static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
 	.fb_probe = tegra_fbdev_probe,
 };
 
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 377176372da8..0647c8cc368b 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -550,7 +550,7 @@ out:
 	return ret;
 }
 
-static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
 	.fb_probe = udlfb_create,
 };
 
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 6e622f7d481d..e8c8eeb3a253 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -87,7 +87,7 @@ struct drm_fb_helper {
 	struct drm_fb_helper_crtc *crtc_info;
 	int connector_count;
 	struct drm_fb_helper_connector **connector_info;
-	struct drm_fb_helper_funcs *funcs;
+	const struct drm_fb_helper_funcs *funcs;
 	struct fb_info *fbdev;
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
-- 
1.9.2

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

* [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
  2014-04-22 14:42 ` Thierry Reding
@ 2014-04-22 14:42   ` Thierry Reding
  -1 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: dri-devel
  Cc: linux-samsung-soc, Daniel Vetter, linux-tegra, intel-gfx,
	linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

To implement hotplug detection in a race-free manner, drivers must call
drm_kms_helper_poll_init() before hotplug events can be triggered. Such
events can be triggered right after any of the encoders or connectors
are initialized. At the same time, if the drm_fb_helper_hotplug_event()
helper is used by a driver, then the poll helper requires some parts of
the FB helper to be initialized to prevent a crash.

At the same time, drm_fb_helper_init() requires information that is not
necessarily available at such an early stage (number of CRTCs and
connectors), so it cannot be used yet.

Add a new helper, drm_fb_helper_prepare(), that initializes the bare
minimum needed to allow drm_kms_helper_poll_init() to execute and any
subsequent hotplug events to be processed properly.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/armada/armada_fbdev.c     |  2 +-
 drivers/gpu/drm/ast/ast_fb.c              |  4 ++-
 drivers/gpu/drm/bochs/bochs_fbdev.c       |  3 ++-
 drivers/gpu/drm/cirrus/cirrus_fbdev.c     |  4 ++-
 drivers/gpu/drm/drm_fb_cma_helper.c       |  3 ++-
 drivers/gpu/drm/drm_fb_helper.c           | 43 ++++++++++++++++++++++++-------
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  3 ++-
 drivers/gpu/drm/gma500/framebuffer.c      |  3 ++-
 drivers/gpu/drm/i915/intel_fbdev.c        |  3 ++-
 drivers/gpu/drm/mgag200/mgag200_fb.c      |  3 ++-
 drivers/gpu/drm/msm/msm_fbdev.c           |  2 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  3 ++-
 drivers/gpu/drm/omapdrm/omap_fbdev.c      |  2 +-
 drivers/gpu/drm/qxl/qxl_fb.c              |  5 +++-
 drivers/gpu/drm/radeon/radeon_fb.c        |  4 ++-
 drivers/gpu/drm/tegra/fb.c                |  4 +--
 drivers/gpu/drm/udl/udl_fb.c              |  3 ++-
 include/drm/drm_fb_helper.h               |  2 ++
 18 files changed, 68 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 21aa1261dba2..9437e11d5df1 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
 
 	priv->fbdev = fbh;
 
-	fbh->funcs = &armada_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, fbh, 1, 1);
 	if (ret) {
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index 2113894e4ff8..cba45c774552 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	ast->fbdev = afbdev;
-	afbdev->helper.funcs = &ast_fb_helper_funcs;
 	spin_lock_init(&afbdev->dirty_lock);
+
+	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, &afbdev->helper,
 				 1, 1);
 	if (ret) {
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index 17e5c17f2730..19cf3e9413b6 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
 {
 	int ret;
 
-	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
+	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
+			      &bochs_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
 				 1, 1);
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index 2bd0291168e4..2a135f253e29 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
 		return -ENOMEM;
 
 	cdev->mode_info.gfbdev = gfbdev;
-	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
 	spin_lock_init(&gfbdev->dirty_lock);
 
+	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
+			      &cirrus_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
 				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
 	if (ret) {
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index b74f9e58b69d..acbbd230e081 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
 	helper = &fbdev_cma->fb_helper;
 
+	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
 	if (ret < 0) {
 		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 80ce92ab91f9..7788f110fcbf 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
  *
- * Initialization is done as a three-step process with drm_fb_helper_init(),
- * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default behaviour can override the
- * second step with their own code.  Teardown is done with drm_fb_helper_fini().
+ * Initialization is done as a four-step process with drm_fb_helper_prepare(),
+ * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
+ * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
+ * default behaviour can override the second step with their own code.
+ * Teardown is done with drm_fb_helper_fini().
  *
  * At runtime drivers should restore the fbdev console by calling
  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
@@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 }
 
 /**
+ * drm_fb_helper_prepare - setup a drm_fb_helper structure
+ * @dev: DRM device
+ * @helper: driver-allocated fbdev helper structure to set up
+ * @funcs: pointer to structure of functions associate with this helper
+ *
+ * Sets up the bare minimum to make the framebuffer helper usable. This is
+ * useful to implement race-free initialization of the polling helpers. In
+ * that case a typical sequence would be:
+ *
+ *   - call drm_fb_helper_prepare()
+ *   - set dev->mode_config.funcs
+ *   - perform driver-specific setup
+ *   - call drm_kms_helper_poll_init()
+ *   - call drm_fb_helper_init()
+ *   - call drm_fb_helper_single_add_all_connectors()
+ *   - call drm_fb_helper_initial_config()
+ */
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs)
+{
+	INIT_LIST_HEAD(&helper->kernel_fb_list);
+	helper->funcs = funcs;
+	helper->dev = dev;
+}
+EXPORT_SYMBOL(drm_fb_helper_prepare);
+
+/**
  * drm_fb_helper_init - initialize a drm_fb_helper structure
  * @dev: drm device
  * @fb_helper: driver-allocated fbdev helper structure to initialize
@@ -513,8 +541,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
  * to allow driver writes more control over the exact init sequence.
  *
- * Drivers must set fb_helper->funcs before calling
- * drm_fb_helper_initial_config().
+ * Drivers must call drm_fb_helper_prepare() before calling this function.
  *
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
@@ -529,10 +556,6 @@ int drm_fb_helper_init(struct drm_device *dev,
 	if (!max_conn_count)
 		return -EINVAL;
 
-	fb_helper->dev = dev;
-
-	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
-
 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 	if (!fb_helper->crtc_info)
 		return -ENOMEM;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 7ccf04917f47..46443971d517 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -274,7 +274,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	private->fb_helper = helper = &fbdev->drm_fb_helper;
-	helper->funcs = &exynos_drm_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 
 	num_crtc = dev->mode_config.num_crtc;
 
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index 76e4d777d01d..d0dd3bea8aa5 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
 	}
 
 	dev_priv->fbdev = fbdev;
-	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
 
 	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
 							INTELFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 336a89f2549e..e7dd21029060 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -600,7 +600,8 @@ int intel_fbdev_init(struct drm_device *dev)
 	if (ifbdev == NULL)
 		return -ENOMEM;
 
-	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+	drm_fb_helper_probe(dev, &ifbdev->helper, &intel_fb_helper_funcs);
+
 	if (!intel_fbdev_init_bios(dev, ifbdev))
 		ifbdev->preferred_bpp = 32;
 
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index a4319aba9180..5451dc58eff1 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
 		return -ENOMEM;
 
 	mdev->mfbdev = mfbdev;
-	mfbdev->helper.funcs = &mga_fb_helper_funcs;
 	spin_lock_init(&mfbdev->dirty_lock);
 
+	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
 				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
 	if (ret)
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index 841665862f2f..745b1be4acf0 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -200,7 +200,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
 
 	helper = &fbdev->base;
 
-	helper->funcs = &msm_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 8e9c07b7fc89..afe706a20f97 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
 
 	fbcon->dev = dev;
 	drm->fbcon = fbcon;
-	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, &fbcon->helper,
 				 dev->mode_config.num_crtc, 4);
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 4cb12083eb12..8436c6857cda 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
 
 	helper = &fbdev->base;
 
-	helper->funcs = &omap_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index cf89614c72be..df567888bb1e 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
 
 	qfbdev->qdev = qdev;
 	qdev->mode_info.qfbdev = qfbdev;
-	qfbdev->helper.funcs = &qxl_fb_helper_funcs;
 	spin_lock_init(&qfbdev->delayed_ops_lock);
 	INIT_LIST_HEAD(&qfbdev->delayed_ops);
+
+	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
+			      &qxl_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
 				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
 				 QXLFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index ad97afdbc4c7..db598d712901 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
 
 	rfbdev->rdev = rdev;
 	rdev->mode_info.rfbdev = rfbdev;
-	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
+
+	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
+			      &radeon_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
 				 rdev->num_crtc,
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 75bed72a9755..2e3758542c89 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 					      unsigned int num_crtc,
 					      unsigned int max_connectors)
 {
-	struct drm_fb_helper *helper;
 	struct tegra_fbdev *fbdev;
 	int err;
 
@@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fbdev->base.funcs = &tegra_fb_helper_funcs;
-	helper = &fbdev->base;
+	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
 
 	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
 	if (err < 0) {
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 0647c8cc368b..d1da339843ca 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	udl->fbdev = ufbdev;
-	ufbdev->helper.funcs = &udl_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, &ufbdev->helper,
 				 1, 1);
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index e8c8eeb3a253..9b83c66a5f13 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -97,6 +97,8 @@ struct drm_fb_helper {
 	bool delayed_hotplug;
 };
 
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs);
 int drm_fb_helper_init(struct drm_device *dev,
 		       struct drm_fb_helper *helper, int crtc_count,
 		       int max_conn);
-- 
1.9.2

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

* [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
@ 2014-04-22 14:42   ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

To implement hotplug detection in a race-free manner, drivers must call
drm_kms_helper_poll_init() before hotplug events can be triggered. Such
events can be triggered right after any of the encoders or connectors
are initialized. At the same time, if the drm_fb_helper_hotplug_event()
helper is used by a driver, then the poll helper requires some parts of
the FB helper to be initialized to prevent a crash.

At the same time, drm_fb_helper_init() requires information that is not
necessarily available at such an early stage (number of CRTCs and
connectors), so it cannot be used yet.

Add a new helper, drm_fb_helper_prepare(), that initializes the bare
minimum needed to allow drm_kms_helper_poll_init() to execute and any
subsequent hotplug events to be processed properly.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/armada/armada_fbdev.c     |  2 +-
 drivers/gpu/drm/ast/ast_fb.c              |  4 ++-
 drivers/gpu/drm/bochs/bochs_fbdev.c       |  3 ++-
 drivers/gpu/drm/cirrus/cirrus_fbdev.c     |  4 ++-
 drivers/gpu/drm/drm_fb_cma_helper.c       |  3 ++-
 drivers/gpu/drm/drm_fb_helper.c           | 43 ++++++++++++++++++++++++-------
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  3 ++-
 drivers/gpu/drm/gma500/framebuffer.c      |  3 ++-
 drivers/gpu/drm/i915/intel_fbdev.c        |  3 ++-
 drivers/gpu/drm/mgag200/mgag200_fb.c      |  3 ++-
 drivers/gpu/drm/msm/msm_fbdev.c           |  2 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  3 ++-
 drivers/gpu/drm/omapdrm/omap_fbdev.c      |  2 +-
 drivers/gpu/drm/qxl/qxl_fb.c              |  5 +++-
 drivers/gpu/drm/radeon/radeon_fb.c        |  4 ++-
 drivers/gpu/drm/tegra/fb.c                |  4 +--
 drivers/gpu/drm/udl/udl_fb.c              |  3 ++-
 include/drm/drm_fb_helper.h               |  2 ++
 18 files changed, 68 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 21aa1261dba2..9437e11d5df1 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
 
 	priv->fbdev = fbh;
 
-	fbh->funcs = &armada_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, fbh, 1, 1);
 	if (ret) {
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index 2113894e4ff8..cba45c774552 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	ast->fbdev = afbdev;
-	afbdev->helper.funcs = &ast_fb_helper_funcs;
 	spin_lock_init(&afbdev->dirty_lock);
+
+	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, &afbdev->helper,
 				 1, 1);
 	if (ret) {
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index 17e5c17f2730..19cf3e9413b6 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
 {
 	int ret;
 
-	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
+	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
+			      &bochs_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
 				 1, 1);
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index 2bd0291168e4..2a135f253e29 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
 		return -ENOMEM;
 
 	cdev->mode_info.gfbdev = gfbdev;
-	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
 	spin_lock_init(&gfbdev->dirty_lock);
 
+	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
+			      &cirrus_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
 				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
 	if (ret) {
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index b74f9e58b69d..acbbd230e081 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
 	helper = &fbdev_cma->fb_helper;
 
+	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
 	if (ret < 0) {
 		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 80ce92ab91f9..7788f110fcbf 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
  *
- * Initialization is done as a three-step process with drm_fb_helper_init(),
- * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default behaviour can override the
- * second step with their own code.  Teardown is done with drm_fb_helper_fini().
+ * Initialization is done as a four-step process with drm_fb_helper_prepare(),
+ * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
+ * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
+ * default behaviour can override the second step with their own code.
+ * Teardown is done with drm_fb_helper_fini().
  *
  * At runtime drivers should restore the fbdev console by calling
  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
@@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 }
 
 /**
+ * drm_fb_helper_prepare - setup a drm_fb_helper structure
+ * @dev: DRM device
+ * @helper: driver-allocated fbdev helper structure to set up
+ * @funcs: pointer to structure of functions associate with this helper
+ *
+ * Sets up the bare minimum to make the framebuffer helper usable. This is
+ * useful to implement race-free initialization of the polling helpers. In
+ * that case a typical sequence would be:
+ *
+ *   - call drm_fb_helper_prepare()
+ *   - set dev->mode_config.funcs
+ *   - perform driver-specific setup
+ *   - call drm_kms_helper_poll_init()
+ *   - call drm_fb_helper_init()
+ *   - call drm_fb_helper_single_add_all_connectors()
+ *   - call drm_fb_helper_initial_config()
+ */
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs)
+{
+	INIT_LIST_HEAD(&helper->kernel_fb_list);
+	helper->funcs = funcs;
+	helper->dev = dev;
+}
+EXPORT_SYMBOL(drm_fb_helper_prepare);
+
+/**
  * drm_fb_helper_init - initialize a drm_fb_helper structure
  * @dev: drm device
  * @fb_helper: driver-allocated fbdev helper structure to initialize
@@ -513,8 +541,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
  * to allow driver writes more control over the exact init sequence.
  *
- * Drivers must set fb_helper->funcs before calling
- * drm_fb_helper_initial_config().
+ * Drivers must call drm_fb_helper_prepare() before calling this function.
  *
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
@@ -529,10 +556,6 @@ int drm_fb_helper_init(struct drm_device *dev,
 	if (!max_conn_count)
 		return -EINVAL;
 
-	fb_helper->dev = dev;
-
-	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
-
 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 	if (!fb_helper->crtc_info)
 		return -ENOMEM;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 7ccf04917f47..46443971d517 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -274,7 +274,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	private->fb_helper = helper = &fbdev->drm_fb_helper;
-	helper->funcs = &exynos_drm_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 
 	num_crtc = dev->mode_config.num_crtc;
 
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index 76e4d777d01d..d0dd3bea8aa5 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
 	}
 
 	dev_priv->fbdev = fbdev;
-	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
 
 	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
 							INTELFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 336a89f2549e..e7dd21029060 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -600,7 +600,8 @@ int intel_fbdev_init(struct drm_device *dev)
 	if (ifbdev == NULL)
 		return -ENOMEM;
 
-	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+	drm_fb_helper_probe(dev, &ifbdev->helper, &intel_fb_helper_funcs);
+
 	if (!intel_fbdev_init_bios(dev, ifbdev))
 		ifbdev->preferred_bpp = 32;
 
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index a4319aba9180..5451dc58eff1 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
 		return -ENOMEM;
 
 	mdev->mfbdev = mfbdev;
-	mfbdev->helper.funcs = &mga_fb_helper_funcs;
 	spin_lock_init(&mfbdev->dirty_lock);
 
+	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
 				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
 	if (ret)
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index 841665862f2f..745b1be4acf0 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -200,7 +200,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
 
 	helper = &fbdev->base;
 
-	helper->funcs = &msm_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 8e9c07b7fc89..afe706a20f97 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
 
 	fbcon->dev = dev;
 	drm->fbcon = fbcon;
-	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, &fbcon->helper,
 				 dev->mode_config.num_crtc, 4);
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 4cb12083eb12..8436c6857cda 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
 
 	helper = &fbdev->base;
 
-	helper->funcs = &omap_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index cf89614c72be..df567888bb1e 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
 
 	qfbdev->qdev = qdev;
 	qdev->mode_info.qfbdev = qfbdev;
-	qfbdev->helper.funcs = &qxl_fb_helper_funcs;
 	spin_lock_init(&qfbdev->delayed_ops_lock);
 	INIT_LIST_HEAD(&qfbdev->delayed_ops);
+
+	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
+			      &qxl_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
 				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
 				 QXLFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index ad97afdbc4c7..db598d712901 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
 
 	rfbdev->rdev = rdev;
 	rdev->mode_info.rfbdev = rfbdev;
-	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
+
+	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
+			      &radeon_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
 				 rdev->num_crtc,
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 75bed72a9755..2e3758542c89 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 					      unsigned int num_crtc,
 					      unsigned int max_connectors)
 {
-	struct drm_fb_helper *helper;
 	struct tegra_fbdev *fbdev;
 	int err;
 
@@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fbdev->base.funcs = &tegra_fb_helper_funcs;
-	helper = &fbdev->base;
+	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
 
 	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
 	if (err < 0) {
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 0647c8cc368b..d1da339843ca 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	udl->fbdev = ufbdev;
-	ufbdev->helper.funcs = &udl_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, &ufbdev->helper,
 				 1, 1);
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index e8c8eeb3a253..9b83c66a5f13 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -97,6 +97,8 @@ struct drm_fb_helper {
 	bool delayed_hotplug;
 };
 
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs);
 int drm_fb_helper_init(struct drm_device *dev,
 		       struct drm_fb_helper *helper, int crtc_count,
 		       int max_conn);
-- 
1.9.2

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

* [PATCH 4/4] drm/tegra: Implement race-free hotplug detection
  2014-04-22 14:42 ` Thierry Reding
@ 2014-04-22 14:42   ` Thierry Reding
  -1 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: dri-devel
  Cc: Daniel Vetter, Imre Deak, linux-arm-kernel, linux-samsung-soc,
	intel-gfx, linux-tegra

From: Thierry Reding <treding@nvidia.com>

A race condition currently exists on Tegra, where it can happen that a
monitor attached via HDMI isn't detected during the initial FB helper
setup, but the hotplug event happens too early to be processed by the
poll helpers because they haven't been initialized yet. This happens
because on some boards the HDMI driver can control the regulator that
supplies the +5V pin on the HDMI connector. Therefore depending on the
timing between the initialization of the HDMI driver and the rest of
DRM, it's possible that the monitor returns the hotplug signal right
within the window where we would miss it.

Unfortunately, drm_kms_helper_poll_init() will wreak havoc when called
before at least some parts of the FB helpers have been set up.

This commit fixes this by splitting out the minimum of initialization
required to make drm_kms_helper_poll_init() work into a separate
function that can be called early. It is then safe to move all of the
poll helper initialization to an earlier point in time (before the
HDMI output driver has a chance to enable the +5V supply). That way if
the hotplug signal is returned before the initial FB helper setup, the
monitor will be forcefully detected at that point, and if the hotplug
signal is returned after that it will be properly handled by the poll
helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.c |  8 ++++++--
 drivers/gpu/drm/tegra/drm.h |  1 +
 drivers/gpu/drm/tegra/fb.c  | 47 ++++++++++++++++++++++++++++++---------------
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 6f5b6e2f552e..4d36debe3de6 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -41,6 +41,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	drm_mode_config_init(drm);
 
+	err = tegra_drm_fb_prepare(drm);
+	if (err < 0)
+		return err;
+
+	drm_kms_helper_poll_init(drm);
+
 	err = host1x_device_init(device);
 	if (err < 0)
 		return err;
@@ -60,8 +66,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 	if (err < 0)
 		return err;
 
-	drm_kms_helper_poll_init(drm);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 126332c3ecbb..c2d9705de1f9 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -288,6 +288,7 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
 				    unsigned int index);
 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
 bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
+int tegra_drm_fb_prepare(struct drm_device *drm);
 extern int tegra_drm_fb_init(struct drm_device *drm);
 extern void tegra_drm_fb_exit(struct drm_device *drm);
 #ifdef CONFIG_DRM_TEGRA_FBDEV
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 2e3758542c89..70d0e07d353c 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -271,13 +271,9 @@ static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
 	.fb_probe = tegra_fbdev_probe,
 };
 
-static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
-					      unsigned int preferred_bpp,
-					      unsigned int num_crtc,
-					      unsigned int max_connectors)
+static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
 {
 	struct tegra_fbdev *fbdev;
-	int err;
 
 	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 	if (!fbdev) {
@@ -287,10 +283,21 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 
 	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
 
+	return fbdev;
+}
+
+static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
+			    unsigned int preferred_bpp,
+			    unsigned int num_crtc,
+			    unsigned int max_connectors)
+{
+	struct drm_device *drm = fbdev->base.dev;
+	int err;
+
 	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
 	if (err < 0) {
 		dev_err(drm->dev, "failed to initialize DRM FB helper\n");
-		goto free;
+		return err;
 	}
 
 	err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
@@ -299,21 +306,17 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 		goto fini;
 	}
 
-	drm_helper_disable_unused_functions(drm);
-
 	err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
 	if (err < 0) {
 		dev_err(drm->dev, "failed to set initial configuration\n");
 		goto fini;
 	}
 
-	return fbdev;
+	return 0;
 
 fini:
 	drm_fb_helper_fini(&fbdev->base);
-free:
-	kfree(fbdev);
-	return ERR_PTR(err);
+	return err;
 }
 
 static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
@@ -367,7 +370,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 #endif
 };
 
-int tegra_drm_fb_init(struct drm_device *drm)
+int tegra_drm_fb_prepare(struct drm_device *drm)
 {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	struct tegra_drm *tegra = drm->dev_private;
@@ -382,8 +385,7 @@ int tegra_drm_fb_init(struct drm_device *drm)
 	drm->mode_config.funcs = &tegra_drm_mode_funcs;
 
 #ifdef CONFIG_DRM_TEGRA_FBDEV
-	tegra->fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
-					  drm->mode_config.num_connector);
+	tegra->fbdev = tegra_fbdev_create(drm);
 	if (IS_ERR(tegra->fbdev))
 		return PTR_ERR(tegra->fbdev);
 #endif
@@ -391,6 +393,21 @@ int tegra_drm_fb_init(struct drm_device *drm)
 	return 0;
 }
 
+int tegra_drm_fb_init(struct drm_device *drm)
+{
+#ifdef CONFIG_DRM_TEGRA_FBDEV
+	struct tegra_drm *tegra = drm->dev_private;
+	int err;
+
+	err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
+			       drm->mode_config.num_connector);
+	if (err < 0)
+		return err;
+#endif
+
+	return 0;
+}
+
 void tegra_drm_fb_exit(struct drm_device *drm)
 {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
-- 
1.9.2

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

* [PATCH 4/4] drm/tegra: Implement race-free hotplug detection
@ 2014-04-22 14:42   ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-22 14:42 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

A race condition currently exists on Tegra, where it can happen that a
monitor attached via HDMI isn't detected during the initial FB helper
setup, but the hotplug event happens too early to be processed by the
poll helpers because they haven't been initialized yet. This happens
because on some boards the HDMI driver can control the regulator that
supplies the +5V pin on the HDMI connector. Therefore depending on the
timing between the initialization of the HDMI driver and the rest of
DRM, it's possible that the monitor returns the hotplug signal right
within the window where we would miss it.

Unfortunately, drm_kms_helper_poll_init() will wreak havoc when called
before at least some parts of the FB helpers have been set up.

This commit fixes this by splitting out the minimum of initialization
required to make drm_kms_helper_poll_init() work into a separate
function that can be called early. It is then safe to move all of the
poll helper initialization to an earlier point in time (before the
HDMI output driver has a chance to enable the +5V supply). That way if
the hotplug signal is returned before the initial FB helper setup, the
monitor will be forcefully detected at that point, and if the hotplug
signal is returned after that it will be properly handled by the poll
helpers.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/tegra/drm.c |  8 ++++++--
 drivers/gpu/drm/tegra/drm.h |  1 +
 drivers/gpu/drm/tegra/fb.c  | 47 ++++++++++++++++++++++++++++++---------------
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 6f5b6e2f552e..4d36debe3de6 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -41,6 +41,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 
 	drm_mode_config_init(drm);
 
+	err = tegra_drm_fb_prepare(drm);
+	if (err < 0)
+		return err;
+
+	drm_kms_helper_poll_init(drm);
+
 	err = host1x_device_init(device);
 	if (err < 0)
 		return err;
@@ -60,8 +66,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 	if (err < 0)
 		return err;
 
-	drm_kms_helper_poll_init(drm);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 126332c3ecbb..c2d9705de1f9 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -288,6 +288,7 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
 				    unsigned int index);
 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
 bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
+int tegra_drm_fb_prepare(struct drm_device *drm);
 extern int tegra_drm_fb_init(struct drm_device *drm);
 extern void tegra_drm_fb_exit(struct drm_device *drm);
 #ifdef CONFIG_DRM_TEGRA_FBDEV
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 2e3758542c89..70d0e07d353c 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -271,13 +271,9 @@ static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
 	.fb_probe = tegra_fbdev_probe,
 };
 
-static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
-					      unsigned int preferred_bpp,
-					      unsigned int num_crtc,
-					      unsigned int max_connectors)
+static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
 {
 	struct tegra_fbdev *fbdev;
-	int err;
 
 	fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
 	if (!fbdev) {
@@ -287,10 +283,21 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 
 	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
 
+	return fbdev;
+}
+
+static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
+			    unsigned int preferred_bpp,
+			    unsigned int num_crtc,
+			    unsigned int max_connectors)
+{
+	struct drm_device *drm = fbdev->base.dev;
+	int err;
+
 	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
 	if (err < 0) {
 		dev_err(drm->dev, "failed to initialize DRM FB helper\n");
-		goto free;
+		return err;
 	}
 
 	err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
@@ -299,21 +306,17 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 		goto fini;
 	}
 
-	drm_helper_disable_unused_functions(drm);
-
 	err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
 	if (err < 0) {
 		dev_err(drm->dev, "failed to set initial configuration\n");
 		goto fini;
 	}
 
-	return fbdev;
+	return 0;
 
 fini:
 	drm_fb_helper_fini(&fbdev->base);
-free:
-	kfree(fbdev);
-	return ERR_PTR(err);
+	return err;
 }
 
 static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
@@ -367,7 +370,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 #endif
 };
 
-int tegra_drm_fb_init(struct drm_device *drm)
+int tegra_drm_fb_prepare(struct drm_device *drm)
 {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
 	struct tegra_drm *tegra = drm->dev_private;
@@ -382,8 +385,7 @@ int tegra_drm_fb_init(struct drm_device *drm)
 	drm->mode_config.funcs = &tegra_drm_mode_funcs;
 
 #ifdef CONFIG_DRM_TEGRA_FBDEV
-	tegra->fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
-					  drm->mode_config.num_connector);
+	tegra->fbdev = tegra_fbdev_create(drm);
 	if (IS_ERR(tegra->fbdev))
 		return PTR_ERR(tegra->fbdev);
 #endif
@@ -391,6 +393,21 @@ int tegra_drm_fb_init(struct drm_device *drm)
 	return 0;
 }
 
+int tegra_drm_fb_init(struct drm_device *drm)
+{
+#ifdef CONFIG_DRM_TEGRA_FBDEV
+	struct tegra_drm *tegra = drm->dev_private;
+	int err;
+
+	err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
+			       drm->mode_config.num_connector);
+	if (err < 0)
+		return err;
+#endif
+
+	return 0;
+}
+
 void tegra_drm_fb_exit(struct drm_device *drm)
 {
 #ifdef CONFIG_DRM_TEGRA_FBDEV
-- 
1.9.2

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

* Re: [PATCH 2/4] drm: Constify struct drm_fb_helper_funcs
  2014-04-22 14:42     ` Thierry Reding
@ 2014-04-22 15:39       ` Daniel Vetter
  -1 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-22 15:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-samsung-soc, Daniel Vetter, dri-devel, linux-tegra,
	intel-gfx, linux-arm-kernel

On Tue, Apr 22, 2014 at 04:42:19PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> There's no need for this to be modifiable. Make it const so that it can
> be put into the .rodata section.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>


> ---
>  drivers/gpu/drm/armada/armada_fbdev.c     | 2 +-
>  drivers/gpu/drm/ast/ast_fb.c              | 2 +-
>  drivers/gpu/drm/bochs/bochs_fbdev.c       | 2 +-
>  drivers/gpu/drm/cirrus/cirrus_fbdev.c     | 2 +-
>  drivers/gpu/drm/drm_fb_cma_helper.c       | 2 +-
>  drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 +-
>  drivers/gpu/drm/gma500/framebuffer.c      | 2 +-
>  drivers/gpu/drm/i915/intel_fbdev.c        | 2 +-
>  drivers/gpu/drm/mgag200/mgag200_fb.c      | 2 +-
>  drivers/gpu/drm/msm/msm_fbdev.c           | 2 +-
>  drivers/gpu/drm/nouveau/nouveau_fbcon.c   | 2 +-
>  drivers/gpu/drm/omapdrm/omap_fbdev.c      | 2 +-
>  drivers/gpu/drm/qxl/qxl_fb.c              | 2 +-
>  drivers/gpu/drm/radeon/radeon_fb.c        | 2 +-
>  drivers/gpu/drm/tegra/fb.c                | 2 +-
>  drivers/gpu/drm/udl/udl_fb.c              | 2 +-
>  include/drm/drm_fb_helper.h               | 2 +-
>  17 files changed, 17 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
> index 948cb14c561e..21aa1261dba2 100644
> --- a/drivers/gpu/drm/armada/armada_fbdev.c
> +++ b/drivers/gpu/drm/armada/armada_fbdev.c
> @@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
>  	.gamma_set	= armada_drm_crtc_gamma_set,
>  	.gamma_get	= armada_drm_crtc_gamma_get,
>  	.fb_probe	= armada_fb_probe,
> diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
> index a28640f47c27..2113894e4ff8 100644
> --- a/drivers/gpu/drm/ast/ast_fb.c
> +++ b/drivers/gpu/drm/ast/ast_fb.c
> @@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
>  	*blue = ast_crtc->lut_b[regno] << 8;
>  }
>  
> -static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
>  	.gamma_set = ast_fb_gamma_set,
>  	.gamma_get = ast_fb_gamma_get,
>  	.fb_probe = astfb_create,
> diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
> index 561b84474122..17e5c17f2730 100644
> --- a/drivers/gpu/drm/bochs/bochs_fbdev.c
> +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
> @@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
>  	*blue  = regno;
>  }
>  
> -static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
>  	.gamma_set = bochs_fb_gamma_set,
>  	.gamma_get = bochs_fb_gamma_get,
>  	.fb_probe = bochsfb_create,
> diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> index 32bbba0a787b..2bd0291168e4 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> @@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
>  	.gamma_set = cirrus_crtc_fb_gamma_set,
>  	.gamma_get = cirrus_crtc_fb_gamma_get,
>  	.fb_probe = cirrusfb_create,
> diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
> index 61b5a47ad239..b74f9e58b69d 100644
> --- a/drivers/gpu/drm/drm_fb_cma_helper.c
> +++ b/drivers/gpu/drm/drm_fb_cma_helper.c
> @@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
> +static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
>  	.fb_probe = drm_fbdev_cma_create,
>  };
>  
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> index addbf7536da4..7ccf04917f47 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> @@ -233,7 +233,7 @@ out:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
>  	.fb_probe =	exynos_drm_fbdev_create,
>  };
>  
> diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
> index e7fcc148f333..76e4d777d01d 100644
> --- a/drivers/gpu/drm/gma500/framebuffer.c
> +++ b/drivers/gpu/drm/gma500/framebuffer.c
> @@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
>  	return psbfb_create(psb_fbdev, sizes);
>  }
>  
> -static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
>  	.gamma_set = psbfb_gamma_set,
>  	.gamma_get = psbfb_gamma_get,
>  	.fb_probe = psbfb_probe,
> diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
> index b4d44e62f0c7..336a89f2549e 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -429,7 +429,7 @@ out:
>  	return true;
>  }
>  
> -static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
>  	.initial_config = intel_fb_initial_config,
>  	.gamma_set = intel_crtc_fb_gamma_set,
>  	.gamma_get = intel_crtc_fb_gamma_get,
> diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
> index 13b7dd83faa9..a4319aba9180 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_fb.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
> @@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
>  	.gamma_set = mga_crtc_fb_gamma_set,
>  	.gamma_get = mga_crtc_fb_gamma_get,
>  	.fb_probe = mgag200fb_create,
> diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
> index 6c6d7d4c9b4e..841665862f2f 100644
> --- a/drivers/gpu/drm/msm/msm_fbdev.c
> +++ b/drivers/gpu/drm/msm/msm_fbdev.c
> @@ -180,7 +180,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
>  	DBG("fbdev: get gamma");
>  }
>  
> -static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
>  	.gamma_set = msm_crtc_fb_gamma_set,
>  	.gamma_get = msm_crtc_fb_gamma_get,
>  	.fb_probe = msm_fbdev_create,
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> index 64a42cfd3717..8e9c07b7fc89 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> @@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
>  	info->flags |= FBINFO_HWACCEL_DISABLED;
>  }
>  
> -static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
> +static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
>  	.gamma_set = nouveau_fbcon_gamma_set,
>  	.gamma_get = nouveau_fbcon_gamma_get,
>  	.fb_probe = nouveau_fbcon_create,
> diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> index 1388ca7f87e8..4cb12083eb12 100644
> --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
> +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> @@ -281,7 +281,7 @@ fail:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
>  	.fb_probe = omap_fbdev_create,
>  };
>  
> diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
> index f437b30ce689..cf89614c72be 100644
> --- a/drivers/gpu/drm/qxl/qxl_fb.c
> +++ b/drivers/gpu/drm/qxl/qxl_fb.c
> @@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
>  	.fb_probe = qxl_fb_find_or_create_single,
>  };
>  
> diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
> index 665ced3b7313..ad97afdbc4c7 100644
> --- a/drivers/gpu/drm/radeon/radeon_fb.c
> +++ b/drivers/gpu/drm/radeon/radeon_fb.c
> @@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
>  	.gamma_set = radeon_crtc_fb_gamma_set,
>  	.gamma_get = radeon_crtc_fb_gamma_get,
>  	.fb_probe = radeonfb_create,
> diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
> index f7fca09d4921..75bed72a9755 100644
> --- a/drivers/gpu/drm/tegra/fb.c
> +++ b/drivers/gpu/drm/tegra/fb.c
> @@ -267,7 +267,7 @@ release:
>  	return err;
>  }
>  
> -static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
>  	.fb_probe = tegra_fbdev_probe,
>  };
>  
> diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
> index 377176372da8..0647c8cc368b 100644
> --- a/drivers/gpu/drm/udl/udl_fb.c
> +++ b/drivers/gpu/drm/udl/udl_fb.c
> @@ -550,7 +550,7 @@ out:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
>  	.fb_probe = udlfb_create,
>  };
>  
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index 6e622f7d481d..e8c8eeb3a253 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -87,7 +87,7 @@ struct drm_fb_helper {
>  	struct drm_fb_helper_crtc *crtc_info;
>  	int connector_count;
>  	struct drm_fb_helper_connector **connector_info;
> -	struct drm_fb_helper_funcs *funcs;
> +	const struct drm_fb_helper_funcs *funcs;
>  	struct fb_info *fbdev;
>  	u32 pseudo_palette[17];
>  	struct list_head kernel_fb_list;
> -- 
> 1.9.2
> 

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

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

* [PATCH 2/4] drm: Constify struct drm_fb_helper_funcs
@ 2014-04-22 15:39       ` Daniel Vetter
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-22 15:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 22, 2014 at 04:42:19PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> There's no need for this to be modifiable. Make it const so that it can
> be put into the .rodata section.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>


> ---
>  drivers/gpu/drm/armada/armada_fbdev.c     | 2 +-
>  drivers/gpu/drm/ast/ast_fb.c              | 2 +-
>  drivers/gpu/drm/bochs/bochs_fbdev.c       | 2 +-
>  drivers/gpu/drm/cirrus/cirrus_fbdev.c     | 2 +-
>  drivers/gpu/drm/drm_fb_cma_helper.c       | 2 +-
>  drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 +-
>  drivers/gpu/drm/gma500/framebuffer.c      | 2 +-
>  drivers/gpu/drm/i915/intel_fbdev.c        | 2 +-
>  drivers/gpu/drm/mgag200/mgag200_fb.c      | 2 +-
>  drivers/gpu/drm/msm/msm_fbdev.c           | 2 +-
>  drivers/gpu/drm/nouveau/nouveau_fbcon.c   | 2 +-
>  drivers/gpu/drm/omapdrm/omap_fbdev.c      | 2 +-
>  drivers/gpu/drm/qxl/qxl_fb.c              | 2 +-
>  drivers/gpu/drm/radeon/radeon_fb.c        | 2 +-
>  drivers/gpu/drm/tegra/fb.c                | 2 +-
>  drivers/gpu/drm/udl/udl_fb.c              | 2 +-
>  include/drm/drm_fb_helper.h               | 2 +-
>  17 files changed, 17 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
> index 948cb14c561e..21aa1261dba2 100644
> --- a/drivers/gpu/drm/armada/armada_fbdev.c
> +++ b/drivers/gpu/drm/armada/armada_fbdev.c
> @@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
>  	.gamma_set	= armada_drm_crtc_gamma_set,
>  	.gamma_get	= armada_drm_crtc_gamma_get,
>  	.fb_probe	= armada_fb_probe,
> diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
> index a28640f47c27..2113894e4ff8 100644
> --- a/drivers/gpu/drm/ast/ast_fb.c
> +++ b/drivers/gpu/drm/ast/ast_fb.c
> @@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
>  	*blue = ast_crtc->lut_b[regno] << 8;
>  }
>  
> -static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
>  	.gamma_set = ast_fb_gamma_set,
>  	.gamma_get = ast_fb_gamma_get,
>  	.fb_probe = astfb_create,
> diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
> index 561b84474122..17e5c17f2730 100644
> --- a/drivers/gpu/drm/bochs/bochs_fbdev.c
> +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
> @@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
>  	*blue  = regno;
>  }
>  
> -static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
>  	.gamma_set = bochs_fb_gamma_set,
>  	.gamma_get = bochs_fb_gamma_get,
>  	.fb_probe = bochsfb_create,
> diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> index 32bbba0a787b..2bd0291168e4 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> @@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
>  	.gamma_set = cirrus_crtc_fb_gamma_set,
>  	.gamma_get = cirrus_crtc_fb_gamma_get,
>  	.fb_probe = cirrusfb_create,
> diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
> index 61b5a47ad239..b74f9e58b69d 100644
> --- a/drivers/gpu/drm/drm_fb_cma_helper.c
> +++ b/drivers/gpu/drm/drm_fb_cma_helper.c
> @@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
> +static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
>  	.fb_probe = drm_fbdev_cma_create,
>  };
>  
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> index addbf7536da4..7ccf04917f47 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> @@ -233,7 +233,7 @@ out:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
>  	.fb_probe =	exynos_drm_fbdev_create,
>  };
>  
> diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
> index e7fcc148f333..76e4d777d01d 100644
> --- a/drivers/gpu/drm/gma500/framebuffer.c
> +++ b/drivers/gpu/drm/gma500/framebuffer.c
> @@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
>  	return psbfb_create(psb_fbdev, sizes);
>  }
>  
> -static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
>  	.gamma_set = psbfb_gamma_set,
>  	.gamma_get = psbfb_gamma_get,
>  	.fb_probe = psbfb_probe,
> diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
> index b4d44e62f0c7..336a89f2549e 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -429,7 +429,7 @@ out:
>  	return true;
>  }
>  
> -static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
>  	.initial_config = intel_fb_initial_config,
>  	.gamma_set = intel_crtc_fb_gamma_set,
>  	.gamma_get = intel_crtc_fb_gamma_get,
> diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
> index 13b7dd83faa9..a4319aba9180 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_fb.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
> @@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
>  	.gamma_set = mga_crtc_fb_gamma_set,
>  	.gamma_get = mga_crtc_fb_gamma_get,
>  	.fb_probe = mgag200fb_create,
> diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
> index 6c6d7d4c9b4e..841665862f2f 100644
> --- a/drivers/gpu/drm/msm/msm_fbdev.c
> +++ b/drivers/gpu/drm/msm/msm_fbdev.c
> @@ -180,7 +180,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
>  	DBG("fbdev: get gamma");
>  }
>  
> -static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
>  	.gamma_set = msm_crtc_fb_gamma_set,
>  	.gamma_get = msm_crtc_fb_gamma_get,
>  	.fb_probe = msm_fbdev_create,
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> index 64a42cfd3717..8e9c07b7fc89 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> @@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
>  	info->flags |= FBINFO_HWACCEL_DISABLED;
>  }
>  
> -static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
> +static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
>  	.gamma_set = nouveau_fbcon_gamma_set,
>  	.gamma_get = nouveau_fbcon_gamma_get,
>  	.fb_probe = nouveau_fbcon_create,
> diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> index 1388ca7f87e8..4cb12083eb12 100644
> --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
> +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> @@ -281,7 +281,7 @@ fail:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
>  	.fb_probe = omap_fbdev_create,
>  };
>  
> diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
> index f437b30ce689..cf89614c72be 100644
> --- a/drivers/gpu/drm/qxl/qxl_fb.c
> +++ b/drivers/gpu/drm/qxl/qxl_fb.c
> @@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
>  	.fb_probe = qxl_fb_find_or_create_single,
>  };
>  
> diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
> index 665ced3b7313..ad97afdbc4c7 100644
> --- a/drivers/gpu/drm/radeon/radeon_fb.c
> +++ b/drivers/gpu/drm/radeon/radeon_fb.c
> @@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
>  	return 0;
>  }
>  
> -static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
>  	.gamma_set = radeon_crtc_fb_gamma_set,
>  	.gamma_get = radeon_crtc_fb_gamma_get,
>  	.fb_probe = radeonfb_create,
> diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
> index f7fca09d4921..75bed72a9755 100644
> --- a/drivers/gpu/drm/tegra/fb.c
> +++ b/drivers/gpu/drm/tegra/fb.c
> @@ -267,7 +267,7 @@ release:
>  	return err;
>  }
>  
> -static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
>  	.fb_probe = tegra_fbdev_probe,
>  };
>  
> diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
> index 377176372da8..0647c8cc368b 100644
> --- a/drivers/gpu/drm/udl/udl_fb.c
> +++ b/drivers/gpu/drm/udl/udl_fb.c
> @@ -550,7 +550,7 @@ out:
>  	return ret;
>  }
>  
> -static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
> +static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
>  	.fb_probe = udlfb_create,
>  };
>  
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index 6e622f7d481d..e8c8eeb3a253 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -87,7 +87,7 @@ struct drm_fb_helper {
>  	struct drm_fb_helper_crtc *crtc_info;
>  	int connector_count;
>  	struct drm_fb_helper_connector **connector_info;
> -	struct drm_fb_helper_funcs *funcs;
> +	const struct drm_fb_helper_funcs *funcs;
>  	struct fb_info *fbdev;
>  	u32 pseudo_palette[17];
>  	struct list_head kernel_fb_list;
> -- 
> 1.9.2
> 

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

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

* Re: [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
  2014-04-22 14:42   ` Thierry Reding
@ 2014-04-22 15:54     ` Daniel Vetter
  -1 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-22 15:54 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-samsung-soc, Daniel Vetter, dri-devel, linux-tegra,
	intel-gfx, linux-arm-kernel

On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> To implement hotplug detection in a race-free manner, drivers must call
> drm_kms_helper_poll_init() before hotplug events can be triggered. Such
> events can be triggered right after any of the encoders or connectors
> are initialized. At the same time, if the drm_fb_helper_hotplug_event()
> helper is used by a driver, then the poll helper requires some parts of
> the FB helper to be initialized to prevent a crash.
> 
> At the same time, drm_fb_helper_init() requires information that is not
> necessarily available at such an early stage (number of CRTCs and
> connectors), so it cannot be used yet.
> 
> Add a new helper, drm_fb_helper_prepare(), that initializes the bare
> minimum needed to allow drm_kms_helper_poll_init() to execute and any
> subsequent hotplug events to be processed properly.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Some bikeshed on the kerneldoc below, with that addressed this is

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/armada/armada_fbdev.c     |  2 +-
>  drivers/gpu/drm/ast/ast_fb.c              |  4 ++-
>  drivers/gpu/drm/bochs/bochs_fbdev.c       |  3 ++-
>  drivers/gpu/drm/cirrus/cirrus_fbdev.c     |  4 ++-
>  drivers/gpu/drm/drm_fb_cma_helper.c       |  3 ++-
>  drivers/gpu/drm/drm_fb_helper.c           | 43 ++++++++++++++++++++++++-------
>  drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  3 ++-
>  drivers/gpu/drm/gma500/framebuffer.c      |  3 ++-
>  drivers/gpu/drm/i915/intel_fbdev.c        |  3 ++-
>  drivers/gpu/drm/mgag200/mgag200_fb.c      |  3 ++-
>  drivers/gpu/drm/msm/msm_fbdev.c           |  2 +-
>  drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  3 ++-
>  drivers/gpu/drm/omapdrm/omap_fbdev.c      |  2 +-
>  drivers/gpu/drm/qxl/qxl_fb.c              |  5 +++-
>  drivers/gpu/drm/radeon/radeon_fb.c        |  4 ++-
>  drivers/gpu/drm/tegra/fb.c                |  4 +--
>  drivers/gpu/drm/udl/udl_fb.c              |  3 ++-
>  include/drm/drm_fb_helper.h               |  2 ++
>  18 files changed, 68 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
> index 21aa1261dba2..9437e11d5df1 100644
> --- a/drivers/gpu/drm/armada/armada_fbdev.c
> +++ b/drivers/gpu/drm/armada/armada_fbdev.c
> @@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
>  
>  	priv->fbdev = fbh;
>  
> -	fbh->funcs = &armada_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, fbh, 1, 1);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
> index 2113894e4ff8..cba45c774552 100644
> --- a/drivers/gpu/drm/ast/ast_fb.c
> +++ b/drivers/gpu/drm/ast/ast_fb.c
> @@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	ast->fbdev = afbdev;
> -	afbdev->helper.funcs = &ast_fb_helper_funcs;
>  	spin_lock_init(&afbdev->dirty_lock);
> +
> +	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(dev, &afbdev->helper,
>  				 1, 1);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
> index 17e5c17f2730..19cf3e9413b6 100644
> --- a/drivers/gpu/drm/bochs/bochs_fbdev.c
> +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
> @@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
>  {
>  	int ret;
>  
> -	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
> +	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
> +			      &bochs_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
>  				 1, 1);
> diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> index 2bd0291168e4..2a135f253e29 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> @@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
>  		return -ENOMEM;
>  
>  	cdev->mode_info.gfbdev = gfbdev;
> -	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
>  	spin_lock_init(&gfbdev->dirty_lock);
>  
> +	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
> +			      &cirrus_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
>  				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
> index b74f9e58b69d..acbbd230e081 100644
> --- a/drivers/gpu/drm/drm_fb_cma_helper.c
> +++ b/drivers/gpu/drm/drm_fb_cma_helper.c
> @@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
>  	helper = &fbdev_cma->fb_helper;
>  
> +	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
> +
>  	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
>  	if (ret < 0) {
>  		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 80ce92ab91f9..7788f110fcbf 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
>   * helper functions used by many drivers to implement the kernel mode setting
>   * interfaces.
>   *
> - * Initialization is done as a three-step process with drm_fb_helper_init(),
> - * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
> - * Drivers with fancier requirements than the default behaviour can override the
> - * second step with their own code.  Teardown is done with drm_fb_helper_fini().
> + * Initialization is done as a four-step process with drm_fb_helper_prepare(),
> + * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
> + * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
> + * default behaviour can override the second step with their own code.
> + * Teardown is done with drm_fb_helper_fini().
>   *
>   * At runtime drivers should restore the fbdev console by calling
>   * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
> @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>  }
>  
>  /**
> + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> + * @dev: DRM device
> + * @helper: driver-allocated fbdev helper structure to set up
> + * @funcs: pointer to structure of functions associate with this helper
> + *
> + * Sets up the bare minimum to make the framebuffer helper usable. This is
> + * useful to implement race-free initialization of the polling helpers. In
> + * that case a typical sequence would be:
> + *
> + *   - call drm_fb_helper_prepare()
> + *   - set dev->mode_config.funcs

This step is done in drm_fb_helper_prepare already.

> + *   - perform driver-specific setup
> + *   - call drm_kms_helper_poll_init()

Maybe mention that after this you can start to handle hpd events using the
probe helpers?

> + *   - call drm_fb_helper_init()
> + *   - call drm_fb_helper_single_add_all_connectors()
> + *   - call drm_fb_helper_initial_config()

Does this list look sane in the generated DocBook? Afaik kerneldoc
unfortunately lacks any form of markup, at least afaik :(

> + */
> +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> +			   const struct drm_fb_helper_funcs *funcs)
> +{
> +	INIT_LIST_HEAD(&helper->kernel_fb_list);
> +	helper->funcs = funcs;
> +	helper->dev = dev;
> +}
> +EXPORT_SYMBOL(drm_fb_helper_prepare);
> +
> +/**
>   * drm_fb_helper_init - initialize a drm_fb_helper structure
>   * @dev: drm device
>   * @fb_helper: driver-allocated fbdev helper structure to initialize
> @@ -513,8 +541,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>   * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
>   * to allow driver writes more control over the exact init sequence.
>   *
> - * Drivers must set fb_helper->funcs before calling
> - * drm_fb_helper_initial_config().
> + * Drivers must call drm_fb_helper_prepare() before calling this function.
>   *
>   * RETURNS:
>   * Zero if everything went ok, nonzero otherwise.
> @@ -529,10 +556,6 @@ int drm_fb_helper_init(struct drm_device *dev,
>  	if (!max_conn_count)
>  		return -EINVAL;
>  
> -	fb_helper->dev = dev;
> -
> -	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
> -
>  	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
>  	if (!fb_helper->crtc_info)
>  		return -ENOMEM;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> index 7ccf04917f47..46443971d517 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> @@ -274,7 +274,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	private->fb_helper = helper = &fbdev->drm_fb_helper;
> -	helper->funcs = &exynos_drm_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
>  
>  	num_crtc = dev->mode_config.num_crtc;
>  
> diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
> index 76e4d777d01d..d0dd3bea8aa5 100644
> --- a/drivers/gpu/drm/gma500/framebuffer.c
> +++ b/drivers/gpu/drm/gma500/framebuffer.c
> @@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
>  	}
>  
>  	dev_priv->fbdev = fbdev;
> -	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
>  
>  	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
>  							INTELFB_CONN_LIMIT);
> diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
> index 336a89f2549e..e7dd21029060 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -600,7 +600,8 @@ int intel_fbdev_init(struct drm_device *dev)
>  	if (ifbdev == NULL)
>  		return -ENOMEM;
>  
> -	ifbdev->helper.funcs = &intel_fb_helper_funcs;
> +	drm_fb_helper_probe(dev, &ifbdev->helper, &intel_fb_helper_funcs);
> +
>  	if (!intel_fbdev_init_bios(dev, ifbdev))
>  		ifbdev->preferred_bpp = 32;
>  
> diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
> index a4319aba9180..5451dc58eff1 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_fb.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
> @@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
>  		return -ENOMEM;
>  
>  	mdev->mfbdev = mfbdev;
> -	mfbdev->helper.funcs = &mga_fb_helper_funcs;
>  	spin_lock_init(&mfbdev->dirty_lock);
>  
> +	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
>  				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
>  	if (ret)
> diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
> index 841665862f2f..745b1be4acf0 100644
> --- a/drivers/gpu/drm/msm/msm_fbdev.c
> +++ b/drivers/gpu/drm/msm/msm_fbdev.c
> @@ -200,7 +200,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
>  
>  	helper = &fbdev->base;
>  
> -	helper->funcs = &msm_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, helper,
>  			priv->num_crtcs, priv->num_connectors);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> index 8e9c07b7fc89..afe706a20f97 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> @@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
>  
>  	fbcon->dev = dev;
>  	drm->fbcon = fbcon;
> -	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, &fbcon->helper,
>  				 dev->mode_config.num_crtc, 4);
> diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> index 4cb12083eb12..8436c6857cda 100644
> --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
> +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> @@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
>  
>  	helper = &fbdev->base;
>  
> -	helper->funcs = &omap_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, helper,
>  			priv->num_crtcs, priv->num_connectors);
> diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
> index cf89614c72be..df567888bb1e 100644
> --- a/drivers/gpu/drm/qxl/qxl_fb.c
> +++ b/drivers/gpu/drm/qxl/qxl_fb.c
> @@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
>  
>  	qfbdev->qdev = qdev;
>  	qdev->mode_info.qfbdev = qfbdev;
> -	qfbdev->helper.funcs = &qxl_fb_helper_funcs;
>  	spin_lock_init(&qfbdev->delayed_ops_lock);
>  	INIT_LIST_HEAD(&qfbdev->delayed_ops);
> +
> +	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
> +			      &qxl_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
>  				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
>  				 QXLFB_CONN_LIMIT);
> diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
> index ad97afdbc4c7..db598d712901 100644
> --- a/drivers/gpu/drm/radeon/radeon_fb.c
> +++ b/drivers/gpu/drm/radeon/radeon_fb.c
> @@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
>  
>  	rfbdev->rdev = rdev;
>  	rdev->mode_info.rfbdev = rfbdev;
> -	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
> +			      &radeon_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
>  				 rdev->num_crtc,
> diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
> index 75bed72a9755..2e3758542c89 100644
> --- a/drivers/gpu/drm/tegra/fb.c
> +++ b/drivers/gpu/drm/tegra/fb.c
> @@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
>  					      unsigned int num_crtc,
>  					      unsigned int max_connectors)
>  {
> -	struct drm_fb_helper *helper;
>  	struct tegra_fbdev *fbdev;
>  	int err;
>  
> @@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	fbdev->base.funcs = &tegra_fb_helper_funcs;
> -	helper = &fbdev->base;
> +	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
>  
>  	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
>  	if (err < 0) {
> diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
> index 0647c8cc368b..d1da339843ca 100644
> --- a/drivers/gpu/drm/udl/udl_fb.c
> +++ b/drivers/gpu/drm/udl/udl_fb.c
> @@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	udl->fbdev = ufbdev;
> -	ufbdev->helper.funcs = &udl_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, &ufbdev->helper,
>  				 1, 1);
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index e8c8eeb3a253..9b83c66a5f13 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -97,6 +97,8 @@ struct drm_fb_helper {
>  	bool delayed_hotplug;
>  };
>  
> +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> +			   const struct drm_fb_helper_funcs *funcs);
>  int drm_fb_helper_init(struct drm_device *dev,
>  		       struct drm_fb_helper *helper, int crtc_count,
>  		       int max_conn);
> -- 
> 1.9.2
> 

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

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

* [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
@ 2014-04-22 15:54     ` Daniel Vetter
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-22 15:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> To implement hotplug detection in a race-free manner, drivers must call
> drm_kms_helper_poll_init() before hotplug events can be triggered. Such
> events can be triggered right after any of the encoders or connectors
> are initialized. At the same time, if the drm_fb_helper_hotplug_event()
> helper is used by a driver, then the poll helper requires some parts of
> the FB helper to be initialized to prevent a crash.
> 
> At the same time, drm_fb_helper_init() requires information that is not
> necessarily available at such an early stage (number of CRTCs and
> connectors), so it cannot be used yet.
> 
> Add a new helper, drm_fb_helper_prepare(), that initializes the bare
> minimum needed to allow drm_kms_helper_poll_init() to execute and any
> subsequent hotplug events to be processed properly.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Some bikeshed on the kerneldoc below, with that addressed this is

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> ---
>  drivers/gpu/drm/armada/armada_fbdev.c     |  2 +-
>  drivers/gpu/drm/ast/ast_fb.c              |  4 ++-
>  drivers/gpu/drm/bochs/bochs_fbdev.c       |  3 ++-
>  drivers/gpu/drm/cirrus/cirrus_fbdev.c     |  4 ++-
>  drivers/gpu/drm/drm_fb_cma_helper.c       |  3 ++-
>  drivers/gpu/drm/drm_fb_helper.c           | 43 ++++++++++++++++++++++++-------
>  drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  3 ++-
>  drivers/gpu/drm/gma500/framebuffer.c      |  3 ++-
>  drivers/gpu/drm/i915/intel_fbdev.c        |  3 ++-
>  drivers/gpu/drm/mgag200/mgag200_fb.c      |  3 ++-
>  drivers/gpu/drm/msm/msm_fbdev.c           |  2 +-
>  drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  3 ++-
>  drivers/gpu/drm/omapdrm/omap_fbdev.c      |  2 +-
>  drivers/gpu/drm/qxl/qxl_fb.c              |  5 +++-
>  drivers/gpu/drm/radeon/radeon_fb.c        |  4 ++-
>  drivers/gpu/drm/tegra/fb.c                |  4 +--
>  drivers/gpu/drm/udl/udl_fb.c              |  3 ++-
>  include/drm/drm_fb_helper.h               |  2 ++
>  18 files changed, 68 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
> index 21aa1261dba2..9437e11d5df1 100644
> --- a/drivers/gpu/drm/armada/armada_fbdev.c
> +++ b/drivers/gpu/drm/armada/armada_fbdev.c
> @@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
>  
>  	priv->fbdev = fbh;
>  
> -	fbh->funcs = &armada_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, fbh, 1, 1);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
> index 2113894e4ff8..cba45c774552 100644
> --- a/drivers/gpu/drm/ast/ast_fb.c
> +++ b/drivers/gpu/drm/ast/ast_fb.c
> @@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	ast->fbdev = afbdev;
> -	afbdev->helper.funcs = &ast_fb_helper_funcs;
>  	spin_lock_init(&afbdev->dirty_lock);
> +
> +	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(dev, &afbdev->helper,
>  				 1, 1);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
> index 17e5c17f2730..19cf3e9413b6 100644
> --- a/drivers/gpu/drm/bochs/bochs_fbdev.c
> +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
> @@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
>  {
>  	int ret;
>  
> -	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
> +	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
> +			      &bochs_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
>  				 1, 1);
> diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> index 2bd0291168e4..2a135f253e29 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> @@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
>  		return -ENOMEM;
>  
>  	cdev->mode_info.gfbdev = gfbdev;
> -	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
>  	spin_lock_init(&gfbdev->dirty_lock);
>  
> +	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
> +			      &cirrus_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
>  				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
> index b74f9e58b69d..acbbd230e081 100644
> --- a/drivers/gpu/drm/drm_fb_cma_helper.c
> +++ b/drivers/gpu/drm/drm_fb_cma_helper.c
> @@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
>  	helper = &fbdev_cma->fb_helper;
>  
> +	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
> +
>  	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
>  	if (ret < 0) {
>  		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 80ce92ab91f9..7788f110fcbf 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
>   * helper functions used by many drivers to implement the kernel mode setting
>   * interfaces.
>   *
> - * Initialization is done as a three-step process with drm_fb_helper_init(),
> - * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
> - * Drivers with fancier requirements than the default behaviour can override the
> - * second step with their own code.  Teardown is done with drm_fb_helper_fini().
> + * Initialization is done as a four-step process with drm_fb_helper_prepare(),
> + * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
> + * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
> + * default behaviour can override the second step with their own code.
> + * Teardown is done with drm_fb_helper_fini().
>   *
>   * At runtime drivers should restore the fbdev console by calling
>   * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
> @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>  }
>  
>  /**
> + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> + * @dev: DRM device
> + * @helper: driver-allocated fbdev helper structure to set up
> + * @funcs: pointer to structure of functions associate with this helper
> + *
> + * Sets up the bare minimum to make the framebuffer helper usable. This is
> + * useful to implement race-free initialization of the polling helpers. In
> + * that case a typical sequence would be:
> + *
> + *   - call drm_fb_helper_prepare()
> + *   - set dev->mode_config.funcs

This step is done in drm_fb_helper_prepare already.

> + *   - perform driver-specific setup
> + *   - call drm_kms_helper_poll_init()

Maybe mention that after this you can start to handle hpd events using the
probe helpers?

> + *   - call drm_fb_helper_init()
> + *   - call drm_fb_helper_single_add_all_connectors()
> + *   - call drm_fb_helper_initial_config()

Does this list look sane in the generated DocBook? Afaik kerneldoc
unfortunately lacks any form of markup, at least afaik :(

> + */
> +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> +			   const struct drm_fb_helper_funcs *funcs)
> +{
> +	INIT_LIST_HEAD(&helper->kernel_fb_list);
> +	helper->funcs = funcs;
> +	helper->dev = dev;
> +}
> +EXPORT_SYMBOL(drm_fb_helper_prepare);
> +
> +/**
>   * drm_fb_helper_init - initialize a drm_fb_helper structure
>   * @dev: drm device
>   * @fb_helper: driver-allocated fbdev helper structure to initialize
> @@ -513,8 +541,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>   * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
>   * to allow driver writes more control over the exact init sequence.
>   *
> - * Drivers must set fb_helper->funcs before calling
> - * drm_fb_helper_initial_config().
> + * Drivers must call drm_fb_helper_prepare() before calling this function.
>   *
>   * RETURNS:
>   * Zero if everything went ok, nonzero otherwise.
> @@ -529,10 +556,6 @@ int drm_fb_helper_init(struct drm_device *dev,
>  	if (!max_conn_count)
>  		return -EINVAL;
>  
> -	fb_helper->dev = dev;
> -
> -	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
> -
>  	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
>  	if (!fb_helper->crtc_info)
>  		return -ENOMEM;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> index 7ccf04917f47..46443971d517 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> @@ -274,7 +274,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	private->fb_helper = helper = &fbdev->drm_fb_helper;
> -	helper->funcs = &exynos_drm_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
>  
>  	num_crtc = dev->mode_config.num_crtc;
>  
> diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
> index 76e4d777d01d..d0dd3bea8aa5 100644
> --- a/drivers/gpu/drm/gma500/framebuffer.c
> +++ b/drivers/gpu/drm/gma500/framebuffer.c
> @@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
>  	}
>  
>  	dev_priv->fbdev = fbdev;
> -	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
>  
>  	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
>  							INTELFB_CONN_LIMIT);
> diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
> index 336a89f2549e..e7dd21029060 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -600,7 +600,8 @@ int intel_fbdev_init(struct drm_device *dev)
>  	if (ifbdev == NULL)
>  		return -ENOMEM;
>  
> -	ifbdev->helper.funcs = &intel_fb_helper_funcs;
> +	drm_fb_helper_probe(dev, &ifbdev->helper, &intel_fb_helper_funcs);
> +
>  	if (!intel_fbdev_init_bios(dev, ifbdev))
>  		ifbdev->preferred_bpp = 32;
>  
> diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
> index a4319aba9180..5451dc58eff1 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_fb.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
> @@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
>  		return -ENOMEM;
>  
>  	mdev->mfbdev = mfbdev;
> -	mfbdev->helper.funcs = &mga_fb_helper_funcs;
>  	spin_lock_init(&mfbdev->dirty_lock);
>  
> +	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
>  				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
>  	if (ret)
> diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
> index 841665862f2f..745b1be4acf0 100644
> --- a/drivers/gpu/drm/msm/msm_fbdev.c
> +++ b/drivers/gpu/drm/msm/msm_fbdev.c
> @@ -200,7 +200,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
>  
>  	helper = &fbdev->base;
>  
> -	helper->funcs = &msm_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, helper,
>  			priv->num_crtcs, priv->num_connectors);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> index 8e9c07b7fc89..afe706a20f97 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> @@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
>  
>  	fbcon->dev = dev;
>  	drm->fbcon = fbcon;
> -	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, &fbcon->helper,
>  				 dev->mode_config.num_crtc, 4);
> diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> index 4cb12083eb12..8436c6857cda 100644
> --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
> +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> @@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
>  
>  	helper = &fbdev->base;
>  
> -	helper->funcs = &omap_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, helper,
>  			priv->num_crtcs, priv->num_connectors);
> diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
> index cf89614c72be..df567888bb1e 100644
> --- a/drivers/gpu/drm/qxl/qxl_fb.c
> +++ b/drivers/gpu/drm/qxl/qxl_fb.c
> @@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
>  
>  	qfbdev->qdev = qdev;
>  	qdev->mode_info.qfbdev = qfbdev;
> -	qfbdev->helper.funcs = &qxl_fb_helper_funcs;
>  	spin_lock_init(&qfbdev->delayed_ops_lock);
>  	INIT_LIST_HEAD(&qfbdev->delayed_ops);
> +
> +	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
> +			      &qxl_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
>  				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
>  				 QXLFB_CONN_LIMIT);
> diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
> index ad97afdbc4c7..db598d712901 100644
> --- a/drivers/gpu/drm/radeon/radeon_fb.c
> +++ b/drivers/gpu/drm/radeon/radeon_fb.c
> @@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
>  
>  	rfbdev->rdev = rdev;
>  	rdev->mode_info.rfbdev = rfbdev;
> -	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
> +			      &radeon_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
>  				 rdev->num_crtc,
> diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
> index 75bed72a9755..2e3758542c89 100644
> --- a/drivers/gpu/drm/tegra/fb.c
> +++ b/drivers/gpu/drm/tegra/fb.c
> @@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
>  					      unsigned int num_crtc,
>  					      unsigned int max_connectors)
>  {
> -	struct drm_fb_helper *helper;
>  	struct tegra_fbdev *fbdev;
>  	int err;
>  
> @@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	fbdev->base.funcs = &tegra_fb_helper_funcs;
> -	helper = &fbdev->base;
> +	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
>  
>  	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
>  	if (err < 0) {
> diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
> index 0647c8cc368b..d1da339843ca 100644
> --- a/drivers/gpu/drm/udl/udl_fb.c
> +++ b/drivers/gpu/drm/udl/udl_fb.c
> @@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	udl->fbdev = ufbdev;
> -	ufbdev->helper.funcs = &udl_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, &ufbdev->helper,
>  				 1, 1);
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index e8c8eeb3a253..9b83c66a5f13 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -97,6 +97,8 @@ struct drm_fb_helper {
>  	bool delayed_hotplug;
>  };
>  
> +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> +			   const struct drm_fb_helper_funcs *funcs);
>  int drm_fb_helper_init(struct drm_device *dev,
>  		       struct drm_fb_helper *helper, int crtc_count,
>  		       int max_conn);
> -- 
> 1.9.2
> 

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

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

* Re: [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
  2014-04-22 15:54     ` Daniel Vetter
@ 2014-04-23  7:14       ` Thierry Reding
  -1 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-23  7:14 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: linux-samsung-soc, Daniel Vetter, dri-devel, linux-tegra,
	intel-gfx, linux-arm-kernel


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

On Tue, Apr 22, 2014 at 05:54:06PM +0200, Daniel Vetter wrote:
> On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
[...]
> > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
[...]
> > @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
> >  }
> >  
> >  /**
> > + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> > + * @dev: DRM device
> > + * @helper: driver-allocated fbdev helper structure to set up
> > + * @funcs: pointer to structure of functions associate with this helper
> > + *
> > + * Sets up the bare minimum to make the framebuffer helper usable. This is
> > + * useful to implement race-free initialization of the polling helpers. In
> > + * that case a typical sequence would be:
> > + *
> > + *   - call drm_fb_helper_prepare()
> > + *   - set dev->mode_config.funcs
> 
> This step is done in drm_fb_helper_prepare already.

drm_fb_helper_prepare() sets fb_helper->funcs. dev->mode_config.funcs
needs to be set because it gets called by drm_kms_helper_hotplug_event()
which in turn is called by output_poll_execute(), which can be called at
any point after drm_kms_helper_poll_init(). It could be scheduled
immediately by drm_kms_helper_poll_enable().

I wonder if perhaps we should be wrapping that into a function as well.
Currently this is only documented in code by the drivers that call this.
But since it's only a single step it doesn't seem worth it. Perhaps if
we rolled the min/max_width/height into that function as well it would
be more worth it? That could be difficult to do since a couple of
drivers need to set those depending on the chipset generation.

> > + *   - perform driver-specific setup
> > + *   - call drm_kms_helper_poll_init()
> 
> Maybe mention that after this you can start to handle hpd events using the
> probe helpers?

Isn't that somewhat implied already? The poll helpers call directly the
dev->mode_config.funcs->output_poll_changed() function, which has
already been set up earlier.

> > + *   - call drm_fb_helper_init()
> > + *   - call drm_fb_helper_single_add_all_connectors()
> > + *   - call drm_fb_helper_initial_config()
> 
> Does this list look sane in the generated DocBook? Afaik kerneldoc
> unfortunately lacks any form of markup, at least afaik :(

I must admit I didn't check. I'll make sure I do that before sending out
the next version.

Thierry

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

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

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

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

* [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
@ 2014-04-23  7:14       ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-23  7:14 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Apr 22, 2014 at 05:54:06PM +0200, Daniel Vetter wrote:
> On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
[...]
> > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
[...]
> > @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
> >  }
> >  
> >  /**
> > + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> > + * @dev: DRM device
> > + * @helper: driver-allocated fbdev helper structure to set up
> > + * @funcs: pointer to structure of functions associate with this helper
> > + *
> > + * Sets up the bare minimum to make the framebuffer helper usable. This is
> > + * useful to implement race-free initialization of the polling helpers. In
> > + * that case a typical sequence would be:
> > + *
> > + *   - call drm_fb_helper_prepare()
> > + *   - set dev->mode_config.funcs
> 
> This step is done in drm_fb_helper_prepare already.

drm_fb_helper_prepare() sets fb_helper->funcs. dev->mode_config.funcs
needs to be set because it gets called by drm_kms_helper_hotplug_event()
which in turn is called by output_poll_execute(), which can be called at
any point after drm_kms_helper_poll_init(). It could be scheduled
immediately by drm_kms_helper_poll_enable().

I wonder if perhaps we should be wrapping that into a function as well.
Currently this is only documented in code by the drivers that call this.
But since it's only a single step it doesn't seem worth it. Perhaps if
we rolled the min/max_width/height into that function as well it would
be more worth it? That could be difficult to do since a couple of
drivers need to set those depending on the chipset generation.

> > + *   - perform driver-specific setup
> > + *   - call drm_kms_helper_poll_init()
> 
> Maybe mention that after this you can start to handle hpd events using the
> probe helpers?

Isn't that somewhat implied already? The poll helpers call directly the
dev->mode_config.funcs->output_poll_changed() function, which has
already been set up earlier.

> > + *   - call drm_fb_helper_init()
> > + *   - call drm_fb_helper_single_add_all_connectors()
> > + *   - call drm_fb_helper_initial_config()
> 
> Does this list look sane in the generated DocBook? Afaik kerneldoc
> unfortunately lacks any form of markup, at least afaik :(

I must admit I didn't check. I'll make sure I do that before sending out
the next version.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140423/dc7ba188/attachment.sig>

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

* Re: [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
  2014-04-23  7:14       ` Thierry Reding
@ 2014-04-23  7:35         ` Daniel Vetter
  -1 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-23  7:35 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-samsung-soc, Daniel Vetter, dri-devel, linux-tegra,
	intel-gfx, linux-arm-kernel

On Wed, Apr 23, 2014 at 09:14:41AM +0200, Thierry Reding wrote:
> On Tue, Apr 22, 2014 at 05:54:06PM +0200, Daniel Vetter wrote:
> > On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
> [...]
> > > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> [...]
> > > @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
> > >  }
> > >  
> > >  /**
> > > + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> > > + * @dev: DRM device
> > > + * @helper: driver-allocated fbdev helper structure to set up
> > > + * @funcs: pointer to structure of functions associate with this helper
> > > + *
> > > + * Sets up the bare minimum to make the framebuffer helper usable. This is
> > > + * useful to implement race-free initialization of the polling helpers. In
> > > + * that case a typical sequence would be:
> > > + *
> > > + *   - call drm_fb_helper_prepare()
> > > + *   - set dev->mode_config.funcs
> > 
> > This step is done in drm_fb_helper_prepare already.
> 
> drm_fb_helper_prepare() sets fb_helper->funcs. dev->mode_config.funcs
> needs to be set because it gets called by drm_kms_helper_hotplug_event()
> which in turn is called by output_poll_execute(), which can be called at
> any point after drm_kms_helper_poll_init(). It could be scheduled
> immediately by drm_kms_helper_poll_enable().
> 
> I wonder if perhaps we should be wrapping that into a function as well.
> Currently this is only documented in code by the drivers that call this.
> But since it's only a single step it doesn't seem worth it. Perhaps if
> we rolled the min/max_width/height into that function as well it would
> be more worth it? That could be difficult to do since a couple of
> drivers need to set those depending on the chipset generation.

Oh I've misread this step for the fb_helper->funcs assignment. Makes sense
and I don't think we need to augment your kerneldoc more to explain this.

> > > + *   - perform driver-specific setup

Hm, maybe clarify this as "initialize modeset objects like crtcs, encoders
and connectors"? Since that's the important part we need to get done here.

> > > + *   - call drm_kms_helper_poll_init()
> > 
> > Maybe mention that after this you can start to handle hpd events using the
> > probe helpers?
> 
> Isn't that somewhat implied already? The poll helpers call directly the
> dev->mode_config.funcs->output_poll_changed() function, which has
> already been set up earlier.

I've more meant that after this it's save for drivers to enable hpd
interrupts and start to process them. I.e.

	- enable interrupts and start processing hpd events

as an additional step to make it really cleear how it all fits together.
Otherwise driver authors are left wondering wtf this isn't just one
function call to do it all for them ;-)

> > > + *   - call drm_fb_helper_init()
> > > + *   - call drm_fb_helper_single_add_all_connectors()
> > > + *   - call drm_fb_helper_initial_config()
> > 
> > Does this list look sane in the generated DocBook? Afaik kerneldoc
> > unfortunately lacks any form of markup, at least afaik :(
> 
> I must admit I didn't check. I'll make sure I do that before sending out
> the next version.

If it looks ugly I'm ok as-is, just wondered. Kerneldoc is a bit
simplistic ime.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
@ 2014-04-23  7:35         ` Daniel Vetter
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-23  7:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 23, 2014 at 09:14:41AM +0200, Thierry Reding wrote:
> On Tue, Apr 22, 2014 at 05:54:06PM +0200, Daniel Vetter wrote:
> > On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
> [...]
> > > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> [...]
> > > @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
> > >  }
> > >  
> > >  /**
> > > + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> > > + * @dev: DRM device
> > > + * @helper: driver-allocated fbdev helper structure to set up
> > > + * @funcs: pointer to structure of functions associate with this helper
> > > + *
> > > + * Sets up the bare minimum to make the framebuffer helper usable. This is
> > > + * useful to implement race-free initialization of the polling helpers. In
> > > + * that case a typical sequence would be:
> > > + *
> > > + *   - call drm_fb_helper_prepare()
> > > + *   - set dev->mode_config.funcs
> > 
> > This step is done in drm_fb_helper_prepare already.
> 
> drm_fb_helper_prepare() sets fb_helper->funcs. dev->mode_config.funcs
> needs to be set because it gets called by drm_kms_helper_hotplug_event()
> which in turn is called by output_poll_execute(), which can be called at
> any point after drm_kms_helper_poll_init(). It could be scheduled
> immediately by drm_kms_helper_poll_enable().
> 
> I wonder if perhaps we should be wrapping that into a function as well.
> Currently this is only documented in code by the drivers that call this.
> But since it's only a single step it doesn't seem worth it. Perhaps if
> we rolled the min/max_width/height into that function as well it would
> be more worth it? That could be difficult to do since a couple of
> drivers need to set those depending on the chipset generation.

Oh I've misread this step for the fb_helper->funcs assignment. Makes sense
and I don't think we need to augment your kerneldoc more to explain this.

> > > + *   - perform driver-specific setup

Hm, maybe clarify this as "initialize modeset objects like crtcs, encoders
and connectors"? Since that's the important part we need to get done here.

> > > + *   - call drm_kms_helper_poll_init()
> > 
> > Maybe mention that after this you can start to handle hpd events using the
> > probe helpers?
> 
> Isn't that somewhat implied already? The poll helpers call directly the
> dev->mode_config.funcs->output_poll_changed() function, which has
> already been set up earlier.

I've more meant that after this it's save for drivers to enable hpd
interrupts and start to process them. I.e.

	- enable interrupts and start processing hpd events

as an additional step to make it really cleear how it all fits together.
Otherwise driver authors are left wondering wtf this isn't just one
function call to do it all for them ;-)

> > > + *   - call drm_fb_helper_init()
> > > + *   - call drm_fb_helper_single_add_all_connectors()
> > > + *   - call drm_fb_helper_initial_config()
> > 
> > Does this list look sane in the generated DocBook? Afaik kerneldoc
> > unfortunately lacks any form of markup, at least afaik :(
> 
> I must admit I didn't check. I'll make sure I do that before sending out
> the next version.

If it looks ugly I'm ok as-is, just wondered. Kerneldoc is a bit
simplistic ime.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

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

* [PATCH] drm: Introduce drm_fb_helper_prepare()
  2014-04-22 14:42   ` Thierry Reding
  (?)
  (?)
@ 2014-04-23 13:40   ` Thierry Reding
  2014-04-23 14:24     ` Daniel Vetter
  -1 siblings, 1 reply; 20+ messages in thread
From: Thierry Reding @ 2014-04-23 13:40 UTC (permalink / raw)
  To: dri-devel

From: Thierry Reding <treding@nvidia.com>

To implement hotplug detection in a race-free manner, drivers must call
drm_kms_helper_poll_init() before hotplug events can be triggered. Such
events can be triggered right after any of the encoders or connectors
are initialized. At the same time, if the drm_fb_helper_hotplug_event()
helper is used by a driver, then the poll helper requires some parts of
the FB helper to be initialized to prevent a crash.

At the same time, drm_fb_helper_init() requires information that is not
necessarily available at such an early stage (number of CRTCs and
connectors), so it cannot be used yet.

Add a new helper, drm_fb_helper_prepare(), that initializes the bare
minimum needed to allow drm_kms_helper_poll_init() to execute and any
subsequent hotplug events to be processed properly.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v2:
- improve kernel-doc (Daniel Vetter)

 drivers/gpu/drm/armada/armada_fbdev.c     |  2 +-
 drivers/gpu/drm/ast/ast_fb.c              |  4 ++-
 drivers/gpu/drm/bochs/bochs_fbdev.c       |  3 +-
 drivers/gpu/drm/cirrus/cirrus_fbdev.c     |  4 ++-
 drivers/gpu/drm/drm_fb_cma_helper.c       |  3 +-
 drivers/gpu/drm/drm_fb_helper.c           | 47 ++++++++++++++++++++++++-------
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  3 +-
 drivers/gpu/drm/gma500/framebuffer.c      |  3 +-
 drivers/gpu/drm/i915/intel_fbdev.c        |  3 +-
 drivers/gpu/drm/mgag200/mgag200_fb.c      |  3 +-
 drivers/gpu/drm/msm/msm_fbdev.c           |  2 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  3 +-
 drivers/gpu/drm/omapdrm/omap_fbdev.c      |  2 +-
 drivers/gpu/drm/qxl/qxl_fb.c              |  5 +++-
 drivers/gpu/drm/radeon/radeon_fb.c        |  4 ++-
 drivers/gpu/drm/tegra/fb.c                |  4 +--
 drivers/gpu/drm/udl/udl_fb.c              |  3 +-
 include/drm/drm_fb_helper.h               |  2 ++
 18 files changed, 72 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index 21aa1261dba2..9437e11d5df1 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
 
 	priv->fbdev = fbh;
 
-	fbh->funcs = &armada_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, fbh, 1, 1);
 	if (ret) {
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index 2113894e4ff8..cba45c774552 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	ast->fbdev = afbdev;
-	afbdev->helper.funcs = &ast_fb_helper_funcs;
 	spin_lock_init(&afbdev->dirty_lock);
+
+	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, &afbdev->helper,
 				 1, 1);
 	if (ret) {
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index 17e5c17f2730..19cf3e9413b6 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
 {
 	int ret;
 
-	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
+	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
+			      &bochs_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
 				 1, 1);
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index 2bd0291168e4..2a135f253e29 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
 		return -ENOMEM;
 
 	cdev->mode_info.gfbdev = gfbdev;
-	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
 	spin_lock_init(&gfbdev->dirty_lock);
 
+	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
+			      &cirrus_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
 				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
 	if (ret) {
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index b74f9e58b69d..acbbd230e081 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
 	helper = &fbdev_cma->fb_helper;
 
+	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
 	if (ret < 0) {
 		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 80ce92ab91f9..5e87b4b638db 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
  *
- * Initialization is done as a three-step process with drm_fb_helper_init(),
- * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default behaviour can override the
- * second step with their own code.  Teardown is done with drm_fb_helper_fini().
+ * Initialization is done as a four-step process with drm_fb_helper_prepare(),
+ * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
+ * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
+ * default behaviour can override the second step with their own code.
+ * Teardown is done with drm_fb_helper_fini().
  *
  * At runtime drivers should restore the fbdev console by calling
  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
@@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
  *
  * All other functions exported by the fb helper library can be used to
  * implement the fbdev driver interface by the driver.
+ *
+ * It is possible, though perhaps somewhat tricky, to implement race-free
+ * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
+ * helper must be called first to initialize the minimum required to make
+ * hotplug detection work. Drivers also need to make sure to properly set up
+ * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
+ * it is safe to enable interrupts and start processing hotplug events. At the
+ * same time, drivers should initialize all modeset objects such as CRTCs,
+ * encoders and connectors. To finish up the fbdev helper initialization, the
+ * drm_fb_helper_init() function is called. To probe for all attached displays
+ * and set up an initial configuration using the detected hardware, drivers
+ * should call drm_fb_helper_single_add_all_connectors() followed by
+ * drm_fb_helper_initial_config().
  */
 
 /**
@@ -502,6 +516,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 }
 
 /**
+ * drm_fb_helper_prepare - setup a drm_fb_helper structure
+ * @dev: DRM device
+ * @helper: driver-allocated fbdev helper structure to set up
+ * @funcs: pointer to structure of functions associate with this helper
+ *
+ * Sets up the bare minimum to make the framebuffer helper usable. This is
+ * useful to implement race-free initialization of the polling helpers.
+ */
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs)
+{
+	INIT_LIST_HEAD(&helper->kernel_fb_list);
+	helper->funcs = funcs;
+	helper->dev = dev;
+}
+EXPORT_SYMBOL(drm_fb_helper_prepare);
+
+/**
  * drm_fb_helper_init - initialize a drm_fb_helper structure
  * @dev: drm device
  * @fb_helper: driver-allocated fbdev helper structure to initialize
@@ -513,8 +545,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
  * to allow driver writes more control over the exact init sequence.
  *
- * Drivers must set fb_helper->funcs before calling
- * drm_fb_helper_initial_config().
+ * Drivers must call drm_fb_helper_prepare() before calling this function.
  *
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
@@ -529,10 +560,6 @@ int drm_fb_helper_init(struct drm_device *dev,
 	if (!max_conn_count)
 		return -EINVAL;
 
-	fb_helper->dev = dev;
-
-	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
-
 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 	if (!fb_helper->crtc_info)
 		return -ENOMEM;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 7ccf04917f47..46443971d517 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -274,7 +274,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	private->fb_helper = helper = &fbdev->drm_fb_helper;
-	helper->funcs = &exynos_drm_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 
 	num_crtc = dev->mode_config.num_crtc;
 
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index 76e4d777d01d..d0dd3bea8aa5 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
 	}
 
 	dev_priv->fbdev = fbdev;
-	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
 
 	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
 							INTELFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 336a89f2549e..735890c9ef5a 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -600,7 +600,8 @@ int intel_fbdev_init(struct drm_device *dev)
 	if (ifbdev == NULL)
 		return -ENOMEM;
 
-	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
+
 	if (!intel_fbdev_init_bios(dev, ifbdev))
 		ifbdev->preferred_bpp = 32;
 
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index a4319aba9180..5451dc58eff1 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
 		return -ENOMEM;
 
 	mdev->mfbdev = mfbdev;
-	mfbdev->helper.funcs = &mga_fb_helper_funcs;
 	spin_lock_init(&mfbdev->dirty_lock);
 
+	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
 				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
 	if (ret)
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index 841665862f2f..745b1be4acf0 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -200,7 +200,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
 
 	helper = &fbdev->base;
 
-	helper->funcs = &msm_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 8e9c07b7fc89..afe706a20f97 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
 
 	fbcon->dev = dev;
 	drm->fbcon = fbcon;
-	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, &fbcon->helper,
 				 dev->mode_config.num_crtc, 4);
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 4cb12083eb12..8436c6857cda 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
 
 	helper = &fbdev->base;
 
-	helper->funcs = &omap_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index cf89614c72be..df567888bb1e 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
 
 	qfbdev->qdev = qdev;
 	qdev->mode_info.qfbdev = qfbdev;
-	qfbdev->helper.funcs = &qxl_fb_helper_funcs;
 	spin_lock_init(&qfbdev->delayed_ops_lock);
 	INIT_LIST_HEAD(&qfbdev->delayed_ops);
+
+	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
+			      &qxl_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
 				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
 				 QXLFB_CONN_LIMIT);
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index ad97afdbc4c7..db598d712901 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
 
 	rfbdev->rdev = rdev;
 	rdev->mode_info.rfbdev = rfbdev;
-	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
+
+	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
+			      &radeon_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
 				 rdev->num_crtc,
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 75bed72a9755..2e3758542c89 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 					      unsigned int num_crtc,
 					      unsigned int max_connectors)
 {
-	struct drm_fb_helper *helper;
 	struct tegra_fbdev *fbdev;
 	int err;
 
@@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fbdev->base.funcs = &tegra_fb_helper_funcs;
-	helper = &fbdev->base;
+	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
 
 	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
 	if (err < 0) {
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 0647c8cc368b..d1da339843ca 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	udl->fbdev = ufbdev;
-	ufbdev->helper.funcs = &udl_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
 
 	ret = drm_fb_helper_init(dev, &ufbdev->helper,
 				 1, 1);
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index e8c8eeb3a253..9b83c66a5f13 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -97,6 +97,8 @@ struct drm_fb_helper {
 	bool delayed_hotplug;
 };
 
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs);
 int drm_fb_helper_init(struct drm_device *dev,
 		       struct drm_fb_helper *helper, int crtc_count,
 		       int max_conn);
-- 
1.9.2

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

* Re: [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
  2014-04-23  7:35         ` Daniel Vetter
@ 2014-04-23 13:44           ` Thierry Reding
  -1 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-23 13:44 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: linux-samsung-soc, Daniel Vetter, dri-devel, linux-tegra,
	intel-gfx, linux-arm-kernel


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

On Wed, Apr 23, 2014 at 09:35:48AM +0200, Daniel Vetter wrote:
> On Wed, Apr 23, 2014 at 09:14:41AM +0200, Thierry Reding wrote:
> > On Tue, Apr 22, 2014 at 05:54:06PM +0200, Daniel Vetter wrote:
> > > On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
> > [...]
> > > > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> > [...]
> > > > @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
> > > >  }
> > > >  
> > > >  /**
> > > > + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> > > > + * @dev: DRM device
> > > > + * @helper: driver-allocated fbdev helper structure to set up
> > > > + * @funcs: pointer to structure of functions associate with this helper
> > > > + *
> > > > + * Sets up the bare minimum to make the framebuffer helper usable. This is
> > > > + * useful to implement race-free initialization of the polling helpers. In
> > > > + * that case a typical sequence would be:
> > > > + *
> > > > + *   - call drm_fb_helper_prepare()
> > > > + *   - set dev->mode_config.funcs
> > > 
> > > This step is done in drm_fb_helper_prepare already.
> > 
> > drm_fb_helper_prepare() sets fb_helper->funcs. dev->mode_config.funcs
> > needs to be set because it gets called by drm_kms_helper_hotplug_event()
> > which in turn is called by output_poll_execute(), which can be called at
> > any point after drm_kms_helper_poll_init(). It could be scheduled
> > immediately by drm_kms_helper_poll_enable().
> > 
> > I wonder if perhaps we should be wrapping that into a function as well.
> > Currently this is only documented in code by the drivers that call this.
> > But since it's only a single step it doesn't seem worth it. Perhaps if
> > we rolled the min/max_width/height into that function as well it would
> > be more worth it? That could be difficult to do since a couple of
> > drivers need to set those depending on the chipset generation.
> 
> Oh I've misread this step for the fb_helper->funcs assignment. Makes sense
> and I don't think we need to augment your kerneldoc more to explain this.
> 
> > > > + *   - perform driver-specific setup
> 
> Hm, maybe clarify this as "initialize modeset objects like crtcs, encoders
> and connectors"? Since that's the important part we need to get done here.
> 
> > > > + *   - call drm_kms_helper_poll_init()
> > > 
> > > Maybe mention that after this you can start to handle hpd events using the
> > > probe helpers?
> > 
> > Isn't that somewhat implied already? The poll helpers call directly the
> > dev->mode_config.funcs->output_poll_changed() function, which has
> > already been set up earlier.
> 
> I've more meant that after this it's save for drivers to enable hpd
> interrupts and start to process them. I.e.
> 
> 	- enable interrupts and start processing hpd events
> 
> as an additional step to make it really cleear how it all fits together.
> Otherwise driver authors are left wondering wtf this isn't just one
> function call to do it all for them ;-)
> 
> > > > + *   - call drm_fb_helper_init()
> > > > + *   - call drm_fb_helper_single_add_all_connectors()
> > > > + *   - call drm_fb_helper_initial_config()
> > > 
> > > Does this list look sane in the generated DocBook? Afaik kerneldoc
> > > unfortunately lacks any form of markup, at least afaik :(
> > 
> > I must admit I didn't check. I'll make sure I do that before sending out
> > the next version.
> 
> If it looks ugly I'm ok as-is, just wondered. Kerneldoc is a bit
> simplistic ime.

In the second version I just sent out I ended up moving the description
of this sequence into the fbdev helper section rather than keeping it in
the description of the drm_fb_helper_prepare() function, since the new
function is really just a part of the whole sequence, so it seemed to
fit more nicely. And I've dropped the list formatting and turned it into
prose instead.

Thierry

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

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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* [PATCH 3/4] drm: Introduce drm_fb_helper_prepare()
@ 2014-04-23 13:44           ` Thierry Reding
  0 siblings, 0 replies; 20+ messages in thread
From: Thierry Reding @ 2014-04-23 13:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Apr 23, 2014 at 09:35:48AM +0200, Daniel Vetter wrote:
> On Wed, Apr 23, 2014 at 09:14:41AM +0200, Thierry Reding wrote:
> > On Tue, Apr 22, 2014 at 05:54:06PM +0200, Daniel Vetter wrote:
> > > On Tue, Apr 22, 2014 at 04:42:20PM +0200, Thierry Reding wrote:
> > [...]
> > > > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> > [...]
> > > > @@ -502,6 +503,33 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
> > > >  }
> > > >  
> > > >  /**
> > > > + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> > > > + * @dev: DRM device
> > > > + * @helper: driver-allocated fbdev helper structure to set up
> > > > + * @funcs: pointer to structure of functions associate with this helper
> > > > + *
> > > > + * Sets up the bare minimum to make the framebuffer helper usable. This is
> > > > + * useful to implement race-free initialization of the polling helpers. In
> > > > + * that case a typical sequence would be:
> > > > + *
> > > > + *   - call drm_fb_helper_prepare()
> > > > + *   - set dev->mode_config.funcs
> > > 
> > > This step is done in drm_fb_helper_prepare already.
> > 
> > drm_fb_helper_prepare() sets fb_helper->funcs. dev->mode_config.funcs
> > needs to be set because it gets called by drm_kms_helper_hotplug_event()
> > which in turn is called by output_poll_execute(), which can be called at
> > any point after drm_kms_helper_poll_init(). It could be scheduled
> > immediately by drm_kms_helper_poll_enable().
> > 
> > I wonder if perhaps we should be wrapping that into a function as well.
> > Currently this is only documented in code by the drivers that call this.
> > But since it's only a single step it doesn't seem worth it. Perhaps if
> > we rolled the min/max_width/height into that function as well it would
> > be more worth it? That could be difficult to do since a couple of
> > drivers need to set those depending on the chipset generation.
> 
> Oh I've misread this step for the fb_helper->funcs assignment. Makes sense
> and I don't think we need to augment your kerneldoc more to explain this.
> 
> > > > + *   - perform driver-specific setup
> 
> Hm, maybe clarify this as "initialize modeset objects like crtcs, encoders
> and connectors"? Since that's the important part we need to get done here.
> 
> > > > + *   - call drm_kms_helper_poll_init()
> > > 
> > > Maybe mention that after this you can start to handle hpd events using the
> > > probe helpers?
> > 
> > Isn't that somewhat implied already? The poll helpers call directly the
> > dev->mode_config.funcs->output_poll_changed() function, which has
> > already been set up earlier.
> 
> I've more meant that after this it's save for drivers to enable hpd
> interrupts and start to process them. I.e.
> 
> 	- enable interrupts and start processing hpd events
> 
> as an additional step to make it really cleear how it all fits together.
> Otherwise driver authors are left wondering wtf this isn't just one
> function call to do it all for them ;-)
> 
> > > > + *   - call drm_fb_helper_init()
> > > > + *   - call drm_fb_helper_single_add_all_connectors()
> > > > + *   - call drm_fb_helper_initial_config()
> > > 
> > > Does this list look sane in the generated DocBook? Afaik kerneldoc
> > > unfortunately lacks any form of markup, at least afaik :(
> > 
> > I must admit I didn't check. I'll make sure I do that before sending out
> > the next version.
> 
> If it looks ugly I'm ok as-is, just wondered. Kerneldoc is a bit
> simplistic ime.

In the second version I just sent out I ended up moving the description
of this sequence into the fbdev helper section rather than keeping it in
the description of the drm_fb_helper_prepare() function, since the new
function is really just a part of the whole sequence, so it seemed to
fit more nicely. And I've dropped the list formatting and turned it into
prose instead.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140423/317fadc2/attachment.sig>

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

* Re: [PATCH] drm: Introduce drm_fb_helper_prepare()
  2014-04-23 13:40   ` [PATCH] " Thierry Reding
@ 2014-04-23 14:24     ` Daniel Vetter
  0 siblings, 0 replies; 20+ messages in thread
From: Daniel Vetter @ 2014-04-23 14:24 UTC (permalink / raw)
  To: Thierry Reding; +Cc: dri-devel

On Wed, Apr 23, 2014 at 03:40:46PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> To implement hotplug detection in a race-free manner, drivers must call
> drm_kms_helper_poll_init() before hotplug events can be triggered. Such
> events can be triggered right after any of the encoders or connectors
> are initialized. At the same time, if the drm_fb_helper_hotplug_event()
> helper is used by a driver, then the poll helper requires some parts of
> the FB helper to be initialized to prevent a crash.
> 
> At the same time, drm_fb_helper_init() requires information that is not
> necessarily available at such an early stage (number of CRTCs and
> connectors), so it cannot be used yet.
> 
> Add a new helper, drm_fb_helper_prepare(), that initializes the bare
> minimum needed to allow drm_kms_helper_poll_init() to execute and any
> subsequent hotplug events to be processed properly.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v2:
> - improve kernel-doc (Daniel Vetter)
> 
>  drivers/gpu/drm/armada/armada_fbdev.c     |  2 +-
>  drivers/gpu/drm/ast/ast_fb.c              |  4 ++-
>  drivers/gpu/drm/bochs/bochs_fbdev.c       |  3 +-
>  drivers/gpu/drm/cirrus/cirrus_fbdev.c     |  4 ++-
>  drivers/gpu/drm/drm_fb_cma_helper.c       |  3 +-
>  drivers/gpu/drm/drm_fb_helper.c           | 47 ++++++++++++++++++++++++-------
>  drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  3 +-
>  drivers/gpu/drm/gma500/framebuffer.c      |  3 +-
>  drivers/gpu/drm/i915/intel_fbdev.c        |  3 +-
>  drivers/gpu/drm/mgag200/mgag200_fb.c      |  3 +-
>  drivers/gpu/drm/msm/msm_fbdev.c           |  2 +-
>  drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  3 +-
>  drivers/gpu/drm/omapdrm/omap_fbdev.c      |  2 +-
>  drivers/gpu/drm/qxl/qxl_fb.c              |  5 +++-
>  drivers/gpu/drm/radeon/radeon_fb.c        |  4 ++-
>  drivers/gpu/drm/tegra/fb.c                |  4 +--
>  drivers/gpu/drm/udl/udl_fb.c              |  3 +-
>  include/drm/drm_fb_helper.h               |  2 ++
>  18 files changed, 72 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
> index 21aa1261dba2..9437e11d5df1 100644
> --- a/drivers/gpu/drm/armada/armada_fbdev.c
> +++ b/drivers/gpu/drm/armada/armada_fbdev.c
> @@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
>  
>  	priv->fbdev = fbh;
>  
> -	fbh->funcs = &armada_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, fbh, 1, 1);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
> index 2113894e4ff8..cba45c774552 100644
> --- a/drivers/gpu/drm/ast/ast_fb.c
> +++ b/drivers/gpu/drm/ast/ast_fb.c
> @@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	ast->fbdev = afbdev;
> -	afbdev->helper.funcs = &ast_fb_helper_funcs;
>  	spin_lock_init(&afbdev->dirty_lock);
> +
> +	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(dev, &afbdev->helper,
>  				 1, 1);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
> index 17e5c17f2730..19cf3e9413b6 100644
> --- a/drivers/gpu/drm/bochs/bochs_fbdev.c
> +++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
> @@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
>  {
>  	int ret;
>  
> -	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
> +	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
> +			      &bochs_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
>  				 1, 1);
> diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> index 2bd0291168e4..2a135f253e29 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> @@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
>  		return -ENOMEM;
>  
>  	cdev->mode_info.gfbdev = gfbdev;
> -	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
>  	spin_lock_init(&gfbdev->dirty_lock);
>  
> +	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
> +			      &cirrus_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
>  				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
> index b74f9e58b69d..acbbd230e081 100644
> --- a/drivers/gpu/drm/drm_fb_cma_helper.c
> +++ b/drivers/gpu/drm/drm_fb_cma_helper.c
> @@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
>  	helper = &fbdev_cma->fb_helper;
>  
> +	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
> +
>  	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
>  	if (ret < 0) {
>  		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 80ce92ab91f9..5e87b4b638db 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
>   * helper functions used by many drivers to implement the kernel mode setting
>   * interfaces.
>   *
> - * Initialization is done as a three-step process with drm_fb_helper_init(),
> - * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
> - * Drivers with fancier requirements than the default behaviour can override the
> - * second step with their own code.  Teardown is done with drm_fb_helper_fini().
> + * Initialization is done as a four-step process with drm_fb_helper_prepare(),
> + * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
> + * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
> + * default behaviour can override the second step with their own code.

s/second/third/

Otherwise the new kerneldoc looks nice, so presuming you didn't touch the
code this is still (with the above fixed ofc)

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>

> + * Teardown is done with drm_fb_helper_fini().
>   *
>   * At runtime drivers should restore the fbdev console by calling
>   * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
> @@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
>   *
>   * All other functions exported by the fb helper library can be used to
>   * implement the fbdev driver interface by the driver.
> + *
> + * It is possible, though perhaps somewhat tricky, to implement race-free
> + * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
> + * helper must be called first to initialize the minimum required to make
> + * hotplug detection work. Drivers also need to make sure to properly set up
> + * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
> + * it is safe to enable interrupts and start processing hotplug events. At the
> + * same time, drivers should initialize all modeset objects such as CRTCs,
> + * encoders and connectors. To finish up the fbdev helper initialization, the
> + * drm_fb_helper_init() function is called. To probe for all attached displays
> + * and set up an initial configuration using the detected hardware, drivers
> + * should call drm_fb_helper_single_add_all_connectors() followed by
> + * drm_fb_helper_initial_config().
>   */
>  
>  /**
> @@ -502,6 +516,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>  }
>  
>  /**
> + * drm_fb_helper_prepare - setup a drm_fb_helper structure
> + * @dev: DRM device
> + * @helper: driver-allocated fbdev helper structure to set up
> + * @funcs: pointer to structure of functions associate with this helper
> + *
> + * Sets up the bare minimum to make the framebuffer helper usable. This is
> + * useful to implement race-free initialization of the polling helpers.
> + */
> +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> +			   const struct drm_fb_helper_funcs *funcs)
> +{
> +	INIT_LIST_HEAD(&helper->kernel_fb_list);
> +	helper->funcs = funcs;
> +	helper->dev = dev;
> +}
> +EXPORT_SYMBOL(drm_fb_helper_prepare);
> +
> +/**
>   * drm_fb_helper_init - initialize a drm_fb_helper structure
>   * @dev: drm device
>   * @fb_helper: driver-allocated fbdev helper structure to initialize
> @@ -513,8 +545,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>   * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
>   * to allow driver writes more control over the exact init sequence.
>   *
> - * Drivers must set fb_helper->funcs before calling
> - * drm_fb_helper_initial_config().
> + * Drivers must call drm_fb_helper_prepare() before calling this function.
>   *
>   * RETURNS:
>   * Zero if everything went ok, nonzero otherwise.
> @@ -529,10 +560,6 @@ int drm_fb_helper_init(struct drm_device *dev,
>  	if (!max_conn_count)
>  		return -EINVAL;
>  
> -	fb_helper->dev = dev;
> -
> -	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
> -
>  	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
>  	if (!fb_helper->crtc_info)
>  		return -ENOMEM;
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> index 7ccf04917f47..46443971d517 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
> @@ -274,7 +274,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	private->fb_helper = helper = &fbdev->drm_fb_helper;
> -	helper->funcs = &exynos_drm_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
>  
>  	num_crtc = dev->mode_config.num_crtc;
>  
> diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
> index 76e4d777d01d..d0dd3bea8aa5 100644
> --- a/drivers/gpu/drm/gma500/framebuffer.c
> +++ b/drivers/gpu/drm/gma500/framebuffer.c
> @@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
>  	}
>  
>  	dev_priv->fbdev = fbdev;
> -	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
>  
>  	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
>  							INTELFB_CONN_LIMIT);
> diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
> index 336a89f2549e..735890c9ef5a 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -600,7 +600,8 @@ int intel_fbdev_init(struct drm_device *dev)
>  	if (ifbdev == NULL)
>  		return -ENOMEM;
>  
> -	ifbdev->helper.funcs = &intel_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
> +
>  	if (!intel_fbdev_init_bios(dev, ifbdev))
>  		ifbdev->preferred_bpp = 32;
>  
> diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
> index a4319aba9180..5451dc58eff1 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_fb.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
> @@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
>  		return -ENOMEM;
>  
>  	mdev->mfbdev = mfbdev;
> -	mfbdev->helper.funcs = &mga_fb_helper_funcs;
>  	spin_lock_init(&mfbdev->dirty_lock);
>  
> +	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
>  				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
>  	if (ret)
> diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
> index 841665862f2f..745b1be4acf0 100644
> --- a/drivers/gpu/drm/msm/msm_fbdev.c
> +++ b/drivers/gpu/drm/msm/msm_fbdev.c
> @@ -200,7 +200,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
>  
>  	helper = &fbdev->base;
>  
> -	helper->funcs = &msm_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, helper,
>  			priv->num_crtcs, priv->num_connectors);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> index 8e9c07b7fc89..afe706a20f97 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
> @@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
>  
>  	fbcon->dev = dev;
>  	drm->fbcon = fbcon;
> -	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, &fbcon->helper,
>  				 dev->mode_config.num_crtc, 4);
> diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> index 4cb12083eb12..8436c6857cda 100644
> --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
> +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
> @@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
>  
>  	helper = &fbdev->base;
>  
> -	helper->funcs = &omap_fb_helper_funcs;
> +	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, helper,
>  			priv->num_crtcs, priv->num_connectors);
> diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
> index cf89614c72be..df567888bb1e 100644
> --- a/drivers/gpu/drm/qxl/qxl_fb.c
> +++ b/drivers/gpu/drm/qxl/qxl_fb.c
> @@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
>  
>  	qfbdev->qdev = qdev;
>  	qdev->mode_info.qfbdev = qfbdev;
> -	qfbdev->helper.funcs = &qxl_fb_helper_funcs;
>  	spin_lock_init(&qfbdev->delayed_ops_lock);
>  	INIT_LIST_HEAD(&qfbdev->delayed_ops);
> +
> +	drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
> +			      &qxl_fb_helper_funcs);
> +
>  	ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
>  				 qxl_num_crtc /* num_crtc - QXL supports just 1 */,
>  				 QXLFB_CONN_LIMIT);
> diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
> index ad97afdbc4c7..db598d712901 100644
> --- a/drivers/gpu/drm/radeon/radeon_fb.c
> +++ b/drivers/gpu/drm/radeon/radeon_fb.c
> @@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
>  
>  	rfbdev->rdev = rdev;
>  	rdev->mode_info.rfbdev = rfbdev;
> -	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
> +			      &radeon_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
>  				 rdev->num_crtc,
> diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
> index 75bed72a9755..2e3758542c89 100644
> --- a/drivers/gpu/drm/tegra/fb.c
> +++ b/drivers/gpu/drm/tegra/fb.c
> @@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
>  					      unsigned int num_crtc,
>  					      unsigned int max_connectors)
>  {
> -	struct drm_fb_helper *helper;
>  	struct tegra_fbdev *fbdev;
>  	int err;
>  
> @@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	fbdev->base.funcs = &tegra_fb_helper_funcs;
> -	helper = &fbdev->base;
> +	drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
>  
>  	err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
>  	if (err < 0) {
> diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
> index 0647c8cc368b..d1da339843ca 100644
> --- a/drivers/gpu/drm/udl/udl_fb.c
> +++ b/drivers/gpu/drm/udl/udl_fb.c
> @@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
>  		return -ENOMEM;
>  
>  	udl->fbdev = ufbdev;
> -	ufbdev->helper.funcs = &udl_fb_helper_funcs;
> +
> +	drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
>  
>  	ret = drm_fb_helper_init(dev, &ufbdev->helper,
>  				 1, 1);
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index e8c8eeb3a253..9b83c66a5f13 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -97,6 +97,8 @@ struct drm_fb_helper {
>  	bool delayed_hotplug;
>  };
>  
> +void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> +			   const struct drm_fb_helper_funcs *funcs);
>  int drm_fb_helper_init(struct drm_device *dev,
>  		       struct drm_fb_helper *helper, int crtc_count,
>  		       int max_conn);
> -- 
> 1.9.2
> 

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

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

end of thread, other threads:[~2014-04-23 14:24 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-22 14:42 [PATCH 1/4] drm/fb-helper: Fix hpd vs. initial config races Thierry Reding
2014-04-22 14:42 ` Thierry Reding
     [not found] ` <1398177741-28482-1-git-send-email-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-04-22 14:42   ` [PATCH 2/4] drm: Constify struct drm_fb_helper_funcs Thierry Reding
2014-04-22 14:42     ` Thierry Reding
2014-04-22 15:39     ` Daniel Vetter
2014-04-22 15:39       ` Daniel Vetter
2014-04-22 14:42 ` [PATCH 3/4] drm: Introduce drm_fb_helper_prepare() Thierry Reding
2014-04-22 14:42   ` Thierry Reding
2014-04-22 15:54   ` Daniel Vetter
2014-04-22 15:54     ` Daniel Vetter
2014-04-23  7:14     ` Thierry Reding
2014-04-23  7:14       ` Thierry Reding
2014-04-23  7:35       ` Daniel Vetter
2014-04-23  7:35         ` Daniel Vetter
2014-04-23 13:44         ` Thierry Reding
2014-04-23 13:44           ` Thierry Reding
2014-04-23 13:40   ` [PATCH] " Thierry Reding
2014-04-23 14:24     ` Daniel Vetter
2014-04-22 14:42 ` [PATCH 4/4] drm/tegra: Implement race-free hotplug detection Thierry Reding
2014-04-22 14:42   ` Thierry Reding

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