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.
+ * It doesn't set the bit in iir again, but it still produces
+ * interrupts (for non-MSI).
+ */
+ 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);
+ 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);
+ 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 */
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);
}
* stray interrupts.
*/
iir = new_iir;
- } while (iir != 0);
+ }
- return IRQ_HANDLED;
+ return ret;
}
static int i915_emit_irq(struct drm_device * dev)