diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 44 | 
1 files changed, 40 insertions, 4 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f9bc3aaa90d0..c16cb025755e 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1893,9 +1893,17 @@ static void i9xx_pipestat_irq_ack(struct drm_i915_private *dev_priv,  		/*  		 * Clear the PIPE*STAT regs before the IIR +		 * +		 * Toggle the enable bits to make sure we get an +		 * edge in the ISR pipe event bit if we don't clear +		 * all the enabled status bits. Otherwise the edge +		 * triggered IIR on i965/g4x wouldn't notice that +		 * an interrupt is still pending.  		 */ -		if (pipe_stats[pipe]) -			I915_WRITE(reg, enable_mask | pipe_stats[pipe]); +		if (pipe_stats[pipe]) { +			I915_WRITE(reg, pipe_stats[pipe]); +			I915_WRITE(reg, enable_mask); +		}  	}  	spin_unlock(&dev_priv->irq_lock);  } @@ -1990,10 +1998,38 @@ static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv,  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;  }  |