aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915
diff options
context:
space:
mode:
authorKeith Packard <keithp@keithp.com>2008-11-19 17:03:05 -0500
committerDave Airlie <airlied@redhat.com>2008-11-24 18:28:28 -0500
commit05eff845a28499762075d3a72e238a31f4d2407c (patch)
tree72c9700869c065a5432129928d86409dab7d0d6c /drivers/gpu/drm/i915
parent2678d9d6964b29ecd1975870c7a850242b29bc5c (diff)
drm/i915: Always read pipestat in irq_handler
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 <keithp@keithp.com> Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c55
1 files changed, 36 insertions, 19 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 654d42fabec8..c3673581db58 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -170,37 +170,54 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
170 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; 170 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
171 u32 iir, new_iir; 171 u32 iir, new_iir;
172 u32 pipea_stats, pipeb_stats; 172 u32 pipea_stats, pipeb_stats;
173 u32 vblank_status;
174 u32 vblank_enable;
173 int vblank = 0; 175 int vblank = 0;
174 unsigned long irqflags; 176 unsigned long irqflags;
177 int irq_received;
178 int ret = IRQ_NONE;
175 179
176 atomic_inc(&dev_priv->irq_received); 180 atomic_inc(&dev_priv->irq_received);
177 181
178 iir = I915_READ(IIR); 182 iir = I915_READ(IIR);
179 183
180 if (iir == 0) 184 if (IS_I965G(dev)) {
181 return IRQ_NONE; 185 vblank_status = I915_START_VBLANK_INTERRUPT_STATUS;
186 vblank_enable = PIPE_START_VBLANK_INTERRUPT_ENABLE;
187 } else {
188 vblank_status = I915_VBLANK_INTERRUPT_STATUS;
189 vblank_enable = I915_VBLANK_INTERRUPT_ENABLE;
190 }
182 191
183 do { 192 for (;;) {
184 pipea_stats = 0; 193 irq_received = iir != 0;
185 pipeb_stats = 0; 194
195 /* Can't rely on pipestat interrupt bit in iir as it might
196 * have been cleared after the pipestat interrupt was received.
197 * It doesn't set the bit in iir again, but it still produces
198 * interrupts (for non-MSI).
199 */
200 spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
201 pipea_stats = I915_READ(PIPEASTAT);
202 pipeb_stats = I915_READ(PIPEBSTAT);
186 /* 203 /*
187 * Clear the PIPE(A|B)STAT regs before the IIR 204 * Clear the PIPE(A|B)STAT regs before the IIR
188 */ 205 */
189 if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { 206 if (pipea_stats & 0x8000ffff) {
190 spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
191 pipea_stats = I915_READ(PIPEASTAT);
192 I915_WRITE(PIPEASTAT, pipea_stats); 207 I915_WRITE(PIPEASTAT, pipea_stats);
193 spin_unlock_irqrestore(&dev_priv->user_irq_lock, 208 irq_received = 1;
194 irqflags);
195 } 209 }
196 210
197 if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { 211 if (pipeb_stats & 0x8000ffff) {
198 spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
199 pipeb_stats = I915_READ(PIPEBSTAT);
200 I915_WRITE(PIPEBSTAT, pipeb_stats); 212 I915_WRITE(PIPEBSTAT, pipeb_stats);
201 spin_unlock_irqrestore(&dev_priv->user_irq_lock, 213 irq_received = 1;
202 irqflags);
203 } 214 }
215 spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
216
217 if (!irq_received)
218 break;
219
220 ret = IRQ_HANDLED;
204 221
205 I915_WRITE(IIR, iir); 222 I915_WRITE(IIR, iir);
206 new_iir = I915_READ(IIR); /* Flush posted writes */ 223 new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -214,12 +231,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
214 DRM_WAKEUP(&dev_priv->irq_queue); 231 DRM_WAKEUP(&dev_priv->irq_queue);
215 } 232 }
216 233
217 if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { 234 if (pipea_stats & vblank_status) {
218 vblank++; 235 vblank++;
219 drm_handle_vblank(dev, 0); 236 drm_handle_vblank(dev, 0);
220 } 237 }
221 238
222 if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { 239 if (pipeb_stats & vblank_status) {
223 vblank++; 240 vblank++;
224 drm_handle_vblank(dev, 1); 241 drm_handle_vblank(dev, 1);
225 } 242 }
@@ -244,9 +261,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
244 * stray interrupts. 261 * stray interrupts.
245 */ 262 */
246 iir = new_iir; 263 iir = new_iir;
247 } while (iir != 0); 264 }
248 265
249 return IRQ_HANDLED; 266 return ret;
250} 267}
251 268
252static int i915_emit_irq(struct drm_device * dev) 269static int i915_emit_irq(struct drm_device * dev)