Commit 50c3dc97 authored by Daniel Vetter's avatar Daniel Vetter Committed by Dave Airlie

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 53f1904b
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: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 0eff2a24
...@@ -1609,8 +1609,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); ...@@ -1609,8 +1609,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
* either the output polling work or a work item launched from the driver's * either the output polling work or a work item launched from the driver's
* hotplug interrupt). * hotplug interrupt).
* *
* Note that the driver must ensure that this is only called _after_ the fb has * Note that drivers may call this even before calling
* been fully set up, i.e. after the call to drm_fb_helper_initial_config. * 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: * RETURNS:
* 0 on success and a non-zero error code otherwise. * 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,11 +1622,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
struct drm_device *dev = fb_helper->dev; struct drm_device *dev = fb_helper->dev;
u32 max_width, max_height; u32 max_width, max_height;
if (!fb_helper->fb)
return 0;
mutex_lock(&fb_helper->dev->mode_config.mutex); 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; fb_helper->delayed_hotplug = true;
mutex_unlock(&fb_helper->dev->mode_config.mutex); mutex_unlock(&fb_helper->dev->mode_config.mutex);
return 0; return 0;
......
...@@ -1375,9 +1375,6 @@ static int i915_load_modeset_init(struct drm_device *dev) ...@@ -1375,9 +1375,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
*/ */
intel_fbdev_initial_config(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); drm_kms_helper_poll_init(dev);
return 0; return 0;
......
...@@ -527,7 +527,6 @@ static int i915_drm_freeze(struct drm_device *dev) ...@@ -527,7 +527,6 @@ static int i915_drm_freeze(struct drm_device *dev)
} }
drm_irq_uninstall(dev); drm_irq_uninstall(dev);
dev_priv->enable_hotplug_processing = false;
intel_disable_gt_powersave(dev); intel_disable_gt_powersave(dev);
...@@ -655,7 +654,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) ...@@ -655,7 +654,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
* notifications. * notifications.
* */ * */
intel_hpd_init(dev); intel_hpd_init(dev);
dev_priv->enable_hotplug_processing = true;
/* Config may have changed between suspend and resume */ /* Config may have changed between suspend and resume */
drm_helper_hpd_irq_event(dev); drm_helper_hpd_irq_event(dev);
} }
......
...@@ -1389,7 +1389,6 @@ struct drm_i915_private { ...@@ -1389,7 +1389,6 @@ struct drm_i915_private {
u32 pipestat_irq_mask[I915_MAX_PIPES]; u32 pipestat_irq_mask[I915_MAX_PIPES];
struct work_struct hotplug_work; struct work_struct hotplug_work;
bool enable_hotplug_processing;
struct { struct {
unsigned long hpd_last_jiffies; unsigned long hpd_last_jiffies;
int hpd_cnt; int hpd_cnt;
......
...@@ -1109,10 +1109,6 @@ static void i915_hotplug_work_func(struct work_struct *work) ...@@ -1109,10 +1109,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
bool changed = false; bool changed = false;
u32 hpd_event_bits; u32 hpd_event_bits;
/* HPD irq before everything is fully set up. */
if (!dev_priv->enable_hotplug_processing)
return;
mutex_lock(&mode_config->mutex); mutex_lock(&mode_config->mutex);
DRM_DEBUG_KMS("running encoder hotplug functions\n"); DRM_DEBUG_KMS("running encoder hotplug functions\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment