aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2014-06-27 11:19:22 -0400
committerDave Airlie <airlied@redhat.com>2014-07-07 21:31:13 -0400
commit50c3dc970a09b3b60422a58934cc27a413288bab (patch)
treeab272064113a5ee43a378f81ad6af401acfa46eb
parent0eff2a24d66cfa84ed803782c8614b4538126394 (diff)
drm/fb-helper: Fix hpd vs. initial config races
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> Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c11
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c3
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h1
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c4
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 5aab5ab8b584..6cd687a20cd5 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1609,8 +1609,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
1609 * either the output polling work or a work item launched from the driver's 1609 * either the output polling work or a work item launched from the driver's
1610 * hotplug interrupt). 1610 * hotplug interrupt).
1611 * 1611 *
1612 * Note that the driver must ensure that this is only called _after_ the fb has 1612 * Note that drivers may call this even before calling
1613 * been fully set up, i.e. after the call to drm_fb_helper_initial_config. 1613 * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
1614 * for a race-free fbcon setup and will make sure that the fbdev emulation will
1615 * not miss any hotplug events.
1614 * 1616 *
1615 * RETURNS: 1617 * RETURNS:
1616 * 0 on success and a non-zero error code otherwise. 1618 * 0 on success and a non-zero error code otherwise.
@@ -1620,11 +1622,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1620 struct drm_device *dev = fb_helper->dev; 1622 struct drm_device *dev = fb_helper->dev;
1621 u32 max_width, max_height; 1623 u32 max_width, max_height;
1622 1624
1623 if (!fb_helper->fb)
1624 return 0;
1625
1626 mutex_lock(&fb_helper->dev->mode_config.mutex); 1625 mutex_lock(&fb_helper->dev->mode_config.mutex);
1627 if (!drm_fb_helper_is_bound(fb_helper)) { 1626 if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
1628 fb_helper->delayed_hotplug = true; 1627 fb_helper->delayed_hotplug = true;
1629 mutex_unlock(&fb_helper->dev->mode_config.mutex); 1628 mutex_unlock(&fb_helper->dev->mode_config.mutex);
1630 return 0; 1629 return 0;
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 6c656392d67d..cac9265f9757 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)
1375 */ 1375 */
1376 intel_fbdev_initial_config(dev); 1376 intel_fbdev_initial_config(dev);
1377 1377
1378 /* Only enable hotplug handling once the fbdev is fully set up. */
1379 dev_priv->enable_hotplug_processing = true;
1380
1381 drm_kms_helper_poll_init(dev); 1378 drm_kms_helper_poll_init(dev);
1382 1379
1383 return 0; 1380 return 0;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 651e65e051c0..d935ab3718e1 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -527,7 +527,6 @@ static int i915_drm_freeze(struct drm_device *dev)
527 } 527 }
528 528
529 drm_irq_uninstall(dev); 529 drm_irq_uninstall(dev);
530 dev_priv->enable_hotplug_processing = false;
531 530
532 intel_disable_gt_powersave(dev); 531 intel_disable_gt_powersave(dev);
533 532
@@ -655,7 +654,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
655 * notifications. 654 * notifications.
656 * */ 655 * */
657 intel_hpd_init(dev); 656 intel_hpd_init(dev);
658 dev_priv->enable_hotplug_processing = true;
659 /* Config may have changed between suspend and resume */ 657 /* Config may have changed between suspend and resume */
660 drm_helper_hpd_irq_event(dev); 658 drm_helper_hpd_irq_event(dev);
661 } 659 }
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index a47fbf60b781..5484f052d50c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1389,7 +1389,6 @@ struct drm_i915_private {
1389 u32 pipestat_irq_mask[I915_MAX_PIPES]; 1389 u32 pipestat_irq_mask[I915_MAX_PIPES];
1390 1390
1391 struct work_struct hotplug_work; 1391 struct work_struct hotplug_work;
1392 bool enable_hotplug_processing;
1393 struct { 1392 struct {
1394 unsigned long hpd_last_jiffies; 1393 unsigned long hpd_last_jiffies;
1395 int hpd_cnt; 1394 int hpd_cnt;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 267f069765ad..69a7960c36bb 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1109,10 +1109,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
1109 bool changed = false; 1109 bool changed = false;
1110 u32 hpd_event_bits; 1110 u32 hpd_event_bits;
1111 1111
1112 /* HPD irq before everything is fully set up. */
1113 if (!dev_priv->enable_hotplug_processing)
1114 return;
1115
1116 mutex_lock(&mode_config->mutex); 1112 mutex_lock(&mode_config->mutex);
1117 DRM_DEBUG_KMS("running encoder hotplug functions\n"); 1113 DRM_DEBUG_KMS("running encoder hotplug functions\n");
1118 1114