| From 96a85cc517a9ee4ae5e8d7f5a36cba05023784eb Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com> |
| Date: Thu, 14 Jun 2018 20:56:25 +0300 |
| Subject: drm/i915: Fix hotplug irq ack on i965/g4x |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Ville Syrjälä <ville.syrjala@linux.intel.com> |
| |
| commit 96a85cc517a9ee4ae5e8d7f5a36cba05023784eb upstream. |
| |
| Just like with PIPESTAT, the edge triggered IIR on i965/g4x |
| also causes problems for hotplug interrupts. To make sure |
| we don't get the IIR port interrupt bit stuck low with the |
| ISR bit high we must force an edge in ISR. Unfortunately |
| we can't borrow the PIPESTAT trick and toggle the enable |
| bits in PORT_HOTPLUG_EN as that act itself generates hotplug |
| interrupts. Instead we just have to loop until we've cleared |
| PORT_HOTPLUG_STAT, or we just give up and WARN. |
| |
| v2: Don't frob with PORT_HOTPLUG_EN |
| |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> |
| Link: https://patchwork.freedesktop.org/patch/msgid/20180614175625.1615-1-ville.syrjala@linux.intel.com |
| Reviewed-by: Imre Deak <imre.deak@intel.com> |
| (cherry picked from commit 0ba7c51a6fd80a89236f6ceb52e63f8a7f62bfd3) |
| Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/gpu/drm/i915/i915_irq.c | 32 ++++++++++++++++++++++++++++++-- |
| 1 file changed, 30 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/gpu/drm/i915/i915_irq.c |
| +++ b/drivers/gpu/drm/i915/i915_irq.c |
| @@ -1684,10 +1684,38 @@ static void valleyview_pipestat_irq_hand |
| |
| static u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv) |
| { |
| - u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); |
| + u32 hotplug_status = 0, hotplug_status_mask; |
| + int i; |
| |
| - if (hotplug_status) |
| + if (IS_G4X(dev_priv) || |
| + IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) |
| + hotplug_status_mask = HOTPLUG_INT_STATUS_G4X | |
| + DP_AUX_CHANNEL_MASK_INT_STATUS_G4X; |
| + else |
| + hotplug_status_mask = HOTPLUG_INT_STATUS_I915; |
| + |
| + /* |
| + * We absolutely have to clear all the pending interrupt |
| + * bits in PORT_HOTPLUG_STAT. Otherwise the ISR port |
| + * interrupt bit won't have an edge, and the i965/g4x |
| + * edge triggered IIR will not notice that an interrupt |
| + * is still pending. We can't use PORT_HOTPLUG_EN to |
| + * guarantee the edge as the act of toggling the enable |
| + * bits can itself generate a new hotplug interrupt :( |
| + */ |
| + for (i = 0; i < 10; i++) { |
| + u32 tmp = I915_READ(PORT_HOTPLUG_STAT) & hotplug_status_mask; |
| + |
| + if (tmp == 0) |
| + return hotplug_status; |
| + |
| + hotplug_status |= tmp; |
| I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); |
| + } |
| + |
| + WARN_ONCE(1, |
| + "PORT_HOTPLUG_STAT did not clear (0x%08x)\n", |
| + I915_READ(PORT_HOTPLUG_STAT)); |
| |
| return hotplug_status; |
| } |