Because we write pipestat before iir, it's possible that a pipestat
interrupt will occur between the pipestat write and the iir write. This
leaves pipestat with an interrupt status not visible in iir. This may cause
an interrupt flood as we never clear the pipestat event.
Signed-off-by: Keith Packard <[EMAIL PROTECTED]>
---
drivers/gpu/drm/i915/i915_irq.c | 61 ++++++++++++++++++++++++++------------
1 files changed, 42 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 7965043..879a696 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -170,37 +170,60 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 iir, new_iir;
u32 pipea_stats, pipeb_stats;
+ u32 vblank_status;
+ u32 vblank_enable;
int vblank = 0;
unsigned long irqflags;
+ int irq_received;
+ int ret = IRQ_NONE;
atomic_inc(&dev_priv->irq_received);
iir = I915_READ(IIR);
- if (iir == 0)
- return IRQ_NONE;
+ if (IS_I965G(dev)) {
+ vblank_status = I915_START_VBLANK_INTERRUPT_STATUS;
+ vblank_enable = PIPE_START_VBLANK_INTERRUPT_ENABLE;
+ } else {
+ vblank_status = I915_VBLANK_INTERRUPT_STATUS;
+ vblank_enable = I915_VBLANK_INTERRUPT_ENABLE;
+ }
- do {
- pipea_stats = 0;
- pipeb_stats = 0;
+ for (;;) {
+ irq_received = iir != 0;
+
+ /* Can't rely on pipestat interrupt bit in iir as it might
+ * have been cleared after the pipestat interrupt was received
+ */
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ pipea_stats = I915_READ(PIPEASTAT);
+ pipeb_stats = I915_READ(PIPEBSTAT);
/*
* Clear the PIPE(A|B)STAT regs before the IIR
*/
- if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
- spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
- pipea_stats = I915_READ(PIPEASTAT);
+ if (pipea_stats & 0x8000ffff) {
I915_WRITE(PIPEASTAT, pipea_stats);
- spin_unlock_irqrestore(&dev_priv->user_irq_lock,
- irqflags);
+ WARN((iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) == 0 &&
+ (pipea_stats & vblank_enable) != 0 &&
+ (pipea_stats & vblank_status) != 0,
+ "Pipe A vblank event not in IIR\n");
+ irq_received = 1;
}
- if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
- spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
- pipeb_stats = I915_READ(PIPEBSTAT);
+ if (pipeb_stats & 0x8000ffff) {
I915_WRITE(PIPEBSTAT, pipeb_stats);
- spin_unlock_irqrestore(&dev_priv->user_irq_lock,
- irqflags);
+ WARN((iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) == 0 &&
+ (pipeb_stats & vblank_enable) &&
+ (pipeb_stats & vblank_status) != 0,
+ "Pipe B vblank event not in IIR\n");
+ irq_received = 1;
}
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
+
+ if (!irq_received)
+ break;
+
+ ret = IRQ_HANDLED;
I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -214,12 +237,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
DRM_WAKEUP(&dev_priv->irq_queue);
}
- if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) {
+ if (pipea_stats & vblank_status) {
vblank++;
drm_handle_vblank(dev, 0);
}
- if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) {
+ if (pipeb_stats & vblank_status) {
vblank++;
drm_handle_vblank(dev, 1);
}
@@ -244,9 +267,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
* stray interrupts.
*/
iir = new_iir;
- } while (iir != 0 && dev->pdev->msi_enabled);
+ }
- return IRQ_HANDLED;
+ return ret;
}
static int i915_emit_irq(struct drm_device * dev)
--
1.5.6.5
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel