diff options
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 108 |
1 files changed, 61 insertions, 47 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ca3ed1833908..654d42fabec8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -168,69 +168,83 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
168 | { | 168 | { |
169 | struct drm_device *dev = (struct drm_device *) arg; | 169 | struct drm_device *dev = (struct drm_device *) arg; |
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; | 171 | u32 iir, new_iir; |
172 | u32 pipea_stats = 0, pipeb_stats = 0; | 172 | u32 pipea_stats, pipeb_stats; |
173 | int vblank = 0; | 173 | int vblank = 0; |
174 | unsigned long irqflags; | 174 | unsigned long irqflags; |
175 | 175 | ||
176 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); | ||
177 | atomic_inc(&dev_priv->irq_received); | 176 | atomic_inc(&dev_priv->irq_received); |
178 | 177 | ||
179 | if (dev->pdev->msi_enabled) | ||
180 | I915_WRITE(IMR, ~0); | ||
181 | iir = I915_READ(IIR); | 178 | iir = I915_READ(IIR); |
182 | 179 | ||
183 | if (iir == 0) { | 180 | if (iir == 0) |
184 | if (dev->pdev->msi_enabled) { | ||
185 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | ||
186 | (void) I915_READ(IMR); | ||
187 | } | ||
188 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); | ||
189 | return IRQ_NONE; | 181 | return IRQ_NONE; |
190 | } | ||
191 | |||
192 | /* | ||
193 | * Clear the PIPE(A|B)STAT regs before the IIR | ||
194 | */ | ||
195 | if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { | ||
196 | pipea_stats = I915_READ(PIPEASTAT); | ||
197 | I915_WRITE(PIPEASTAT, pipea_stats); | ||
198 | } | ||
199 | 182 | ||
200 | if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { | 183 | do { |
201 | pipeb_stats = I915_READ(PIPEBSTAT); | 184 | pipea_stats = 0; |
202 | I915_WRITE(PIPEBSTAT, pipeb_stats); | 185 | pipeb_stats = 0; |
203 | } | 186 | /* |
187 | * Clear the PIPE(A|B)STAT regs before the IIR | ||
188 | */ | ||
189 | if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { | ||
190 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); | ||
191 | pipea_stats = I915_READ(PIPEASTAT); | ||
192 | I915_WRITE(PIPEASTAT, pipea_stats); | ||
193 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, | ||
194 | irqflags); | ||
195 | } | ||
204 | 196 | ||
205 | I915_WRITE(IIR, iir); | 197 | if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { |
206 | if (dev->pdev->msi_enabled) | 198 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
207 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | 199 | pipeb_stats = I915_READ(PIPEBSTAT); |
208 | (void) I915_READ(IIR); /* Flush posted writes */ | 200 | I915_WRITE(PIPEBSTAT, pipeb_stats); |
201 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, | ||
202 | irqflags); | ||
203 | } | ||
209 | 204 | ||
210 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); | 205 | I915_WRITE(IIR, iir); |
206 | new_iir = I915_READ(IIR); /* Flush posted writes */ | ||
211 | 207 | ||
212 | if (dev_priv->sarea_priv) | 208 | if (dev_priv->sarea_priv) |
213 | dev_priv->sarea_priv->last_dispatch = | 209 | dev_priv->sarea_priv->last_dispatch = |
214 | READ_BREADCRUMB(dev_priv); | 210 | READ_BREADCRUMB(dev_priv); |
215 | 211 | ||
216 | if (iir & I915_USER_INTERRUPT) { | 212 | if (iir & I915_USER_INTERRUPT) { |
217 | dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); | 213 | dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); |
218 | DRM_WAKEUP(&dev_priv->irq_queue); | 214 | DRM_WAKEUP(&dev_priv->irq_queue); |
219 | } | 215 | } |
220 | 216 | ||
221 | if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { | 217 | if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { |
222 | vblank++; | 218 | vblank++; |
223 | drm_handle_vblank(dev, 0); | 219 | drm_handle_vblank(dev, 0); |
224 | } | 220 | } |
225 | 221 | ||
226 | if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { | 222 | if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { |
227 | vblank++; | 223 | vblank++; |
228 | drm_handle_vblank(dev, 1); | 224 | drm_handle_vblank(dev, 1); |
229 | } | 225 | } |
230 | 226 | ||
231 | if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || | 227 | if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || |
232 | (iir & I915_ASLE_INTERRUPT)) | 228 | (iir & I915_ASLE_INTERRUPT)) |
233 | opregion_asle_intr(dev); | 229 | opregion_asle_intr(dev); |
230 | |||
231 | /* With MSI, interrupts are only generated when iir | ||
232 | * transitions from zero to nonzero. If another bit got | ||
233 | * set while we were handling the existing iir bits, then | ||
234 | * we would never get another interrupt. | ||
235 | * | ||
236 | * This is fine on non-MSI as well, as if we hit this path | ||
237 | * we avoid exiting the interrupt handler only to generate | ||
238 | * another one. | ||
239 | * | ||
240 | * Note that for MSI this could cause a stray interrupt report | ||
241 | * if an interrupt landed in the time between writing IIR and | ||
242 | * the posting read. This should be rare enough to never | ||
243 | * trigger the 99% of 100,000 interrupts test for disabling | ||
244 | * stray interrupts. | ||
245 | */ | ||
246 | iir = new_iir; | ||
247 | } while (iir != 0); | ||
234 | 248 | ||
235 | return IRQ_HANDLED; | 249 | return IRQ_HANDLED; |
236 | } | 250 | } |