--- a/drivers/gpu/drm/i915/intel_hotplug.c +++ b/drivers/gpu/drm/i915/intel_hotplug.c @@ -336,11 +336,10 @@ /* * Handle hotplug events outside the interrupt handler proper. */ -static void i915_hotplug_work_func(struct work_struct *work) +static void i915_hotplug_work_func_x(struct work_struct *work, + struct drm_i915_private *dev_priv, + struct drm_device *dev) { - struct drm_i915_private *dev_priv = - container_of(work, struct drm_i915_private, hotplug.hotplug_work); - struct drm_device *dev = &dev_priv->drm; struct intel_connector *intel_connector; struct intel_encoder *intel_encoder; struct drm_connector *connector; @@ -353,7 +352,7 @@ spin_lock_irq(&dev_priv->irq_lock); - hpd_event_bits = dev_priv->hotplug.event_bits; + hpd_event_bits = dev_priv->hotplug.event_bits | dev_priv->hotplug.recheck_event_bits; dev_priv->hotplug.event_bits = 0; /* Disable hotplug on connectors that hit an irq storm. */ @@ -379,8 +378,40 @@ drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); - if (changed) + if (changed) { + dev_priv->hotplug.recheck_event_bits = 0; drm_kms_helper_hotplug_event(dev); + } else { + if (hpd_event_bits && !dev_priv->hotplug.recheck_event_bits) { + unsigned long delay = msecs_to_jiffies(2000); + schedule_delayed_work(&dev_priv->hotplug.recheck_hotplug, delay); + } + + if (hpd_event_bits != dev_priv->hotplug.recheck_event_bits) + dev_priv->hotplug.recheck_event_bits |= hpd_event_bits; + } +} + +static void i915_hotplug_work_func(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, struct drm_i915_private, hotplug.hotplug_work); + struct drm_device *dev = &dev_priv->drm; + + i915_hotplug_work_func_x(work, dev_priv, dev); +} + +static void i915_hotplug_recheck_func(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, typeof(*dev_priv), + hotplug.recheck_hotplug.work); + struct drm_device *dev = &dev_priv->drm; + + i915_hotplug_work_func_x(work, dev_priv, dev); + + /* re-try just once */ + dev_priv->hotplug.recheck_event_bits = 0; } @@ -604,6 +635,8 @@ INIT_WORK(&dev_priv->hotplug.poll_init_work, i915_hpd_poll_init_work); INIT_DELAYED_WORK(&dev_priv->hotplug.reenable_work, intel_hpd_irq_storm_reenable_work); + INIT_DELAYED_WORK(&dev_priv->hotplug.recheck_hotplug, + i915_hotplug_recheck_func); } void intel_hpd_cancel_work(struct drm_i915_private *dev_priv) @@ -620,6 +653,7 @@ cancel_work_sync(&dev_priv->hotplug.hotplug_work); cancel_work_sync(&dev_priv->hotplug.poll_init_work); cancel_delayed_work_sync(&dev_priv->hotplug.reenable_work); + cancel_delayed_work_sync(&dev_priv->hotplug.recheck_hotplug); } bool intel_hpd_disable(struct drm_i915_private *dev_priv, enum hpd_pin pin) --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -278,7 +278,9 @@ } state; } stats[HPD_NUM_PINS]; u32 event_bits; + u32 recheck_event_bits; struct delayed_work reenable_work; + struct delayed_work recheck_hotplug; struct intel_digital_port *irq_port[I915_MAX_PORTS]; u32 long_port_mask;