drm/i915/vlv: Disable HPD in valleyview_crt_detect_hotplug()
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@vger.kernel.org
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Lyude <cpaul@redhat.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
(cherry picked from commit b236d7c842
)
This commit is contained in:
parent
4c732e6ee9
commit
21842ea84f
|
@ -2957,6 +2957,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_init_work(struct drm_i915_private *dev_priv);
|
||||||
void intel_hpd_cancel_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_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 */
|
/* i915_irq.c */
|
||||||
static inline void i915_queue_hangcheck(struct drm_i915_private *dev_priv)
|
static inline void i915_queue_hangcheck(struct drm_i915_private *dev_priv)
|
||||||
|
|
|
@ -329,10 +329,25 @@ static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)
|
||||||
struct drm_device *dev = connector->dev;
|
struct drm_device *dev = connector->dev;
|
||||||
struct intel_crt *crt = intel_attached_crt(connector);
|
struct intel_crt *crt = intel_attached_crt(connector);
|
||||||
struct drm_i915_private *dev_priv = to_i915(dev);
|
struct drm_i915_private *dev_priv = to_i915(dev);
|
||||||
|
bool reenable_hpd;
|
||||||
u32 adpa;
|
u32 adpa;
|
||||||
bool ret;
|
bool ret;
|
||||||
u32 save_adpa;
|
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);
|
save_adpa = adpa = I915_READ(crt->adpa_reg);
|
||||||
DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);
|
DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);
|
||||||
|
|
||||||
|
@ -357,6 +372,9 @@ static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)
|
||||||
|
|
||||||
DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -510,3 +510,30 @@ void intel_hpd_cancel_work(struct drm_i915_private *dev_priv)
|
||||||
cancel_work_sync(&dev_priv->hotplug.hotplug_work);
|
cancel_work_sync(&dev_priv->hotplug.hotplug_work);
|
||||||
cancel_delayed_work_sync(&dev_priv->hotplug.reenable_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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue