One of the things preventing us from using polling is the fact that
calling valleyview_crt_detect_hotplug() when there's a VGA cable
connected results in sending another hotplug. With polling enabled when
HPD is disabled, this results in a scenario like this:

- We enable power wells and reset the ADPA
- output_poll_exec does force probe on VGA, triggering a hpd
- HPD handler waits for poll to unlock dev->mode_config.mutex
- output_poll_exec shuts off the ADPA, unlocks dev->mode_config.mutex
- HPD handler runs, resets ADPA and brings us back to the start

This results in an endless irq storm getting sent from the ADPA
whenever a VGA connector gets detected in the middle of polling.

Somewhat based off of the "drm/i915: Disable CRT HPD around force
trigger" patch Ville Syrjälä sent a while back

Cc: stable at vger.kernel.org
Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
Signed-off-by: Lyude <cpaul at redhat.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |  2 ++
 drivers/gpu/drm/i915/intel_crt.c     | 18 ++++++++++++++++++
 drivers/gpu/drm/i915/intel_hotplug.c | 27 +++++++++++++++++++++++++++
 3 files changed, 47 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index a988a95..239a3ec 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2933,6 +2933,8 @@ void intel_hpd_init(struct drm_i915_private *dev_priv);
 void intel_hpd_init_work(struct drm_i915_private *dev_priv);
 void intel_hpd_cancel_work(struct drm_i915_private *dev_priv);
 bool intel_hpd_pin_to_port(enum hpd_pin pin, enum port *port);
+bool intel_hpd_disable(struct drm_i915_private *dev_priv, enum hpd_pin pin);
+void intel_hpd_enable(struct drm_i915_private *dev_priv, enum hpd_pin pin);

 /* i915_irq.c */
 void i915_queue_hangcheck(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 220be7a..a626793 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -327,10 +327,25 @@ static bool valleyview_crt_detect_hotplug(struct 
drm_connector *connector)
        struct drm_device *dev = connector->dev;
        struct intel_crt *crt = intel_attached_crt(connector);
        struct drm_i915_private *dev_priv = dev->dev_private;
+       bool reenable_hpd;
        u32 adpa;
        bool ret;
        u32 save_adpa;

+       /*
+        * Doing a force trigger causes a hpd interrupt to get sent, which can
+        * get us stuck in a loop if we're polling:
+        *  - We enable power wells and reset the ADPA
+        *  - output_poll_exec does force probe on VGA, triggering a hpd
+        *  - HPD handler waits for poll to unlock dev->mode_config.mutex
+        *  - output_poll_exec shuts off the ADPA, unlocks
+        *    dev->mode_config.mutex
+        *  - HPD handler runs, resets ADPA and brings us back to the start
+        *
+        * Just disable HPD interrupts here to prevent this
+        */
+       reenable_hpd = intel_hpd_disable(dev_priv, crt->base.hpd_pin);
+
        save_adpa = adpa = I915_READ(crt->adpa_reg);
        DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);

@@ -353,6 +368,9 @@ static bool valleyview_crt_detect_hotplug(struct 
drm_connector *connector)

        DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret);

+       if (reenable_hpd)
+               intel_hpd_enable(dev_priv, crt->base.hpd_pin);
+
        return ret;
 }

diff --git a/drivers/gpu/drm/i915/intel_hotplug.c 
b/drivers/gpu/drm/i915/intel_hotplug.c
index 38eeca7..ec3285f 100644
--- a/drivers/gpu/drm/i915/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/intel_hotplug.c
@@ -510,3 +510,30 @@ void intel_hpd_cancel_work(struct drm_i915_private 
*dev_priv)
        cancel_work_sync(&dev_priv->hotplug.hotplug_work);
        cancel_delayed_work_sync(&dev_priv->hotplug.reenable_work);
 }
+
+bool intel_hpd_disable(struct drm_i915_private *dev_priv, enum hpd_pin pin)
+{
+       bool ret = false;
+
+       if (pin == HPD_NONE)
+               return false;
+
+       spin_lock_irq(&dev_priv->irq_lock);
+       if (dev_priv->hotplug.stats[pin].state == HPD_ENABLED) {
+               dev_priv->hotplug.stats[pin].state = HPD_DISABLED;
+               ret = true;
+       }
+       spin_unlock_irq(&dev_priv->irq_lock);
+
+       return ret;
+}
+
+void intel_hpd_enable(struct drm_i915_private *dev_priv, enum hpd_pin pin)
+{
+       if (pin == HPD_NONE)
+               return;
+
+       spin_lock_irq(&dev_priv->irq_lock);
+       dev_priv->hotplug.stats[pin].state = HPD_ENABLED;
+       spin_unlock_irq(&dev_priv->irq_lock);
+}
-- 
2.5.5

Reply via email to