aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/i915_irq.c
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2008-07-29 15:10:39 -0400
committerDave Airlie <airlied@linux.ie>2008-10-17 17:10:10 -0400
commited4cb4142b242d8090d3811d5eb4abf6aa985bc8 (patch)
tree5d19db6ffec95b18654bbde820ae8ff8995f856e /drivers/gpu/drm/i915/i915_irq.c
parent585fb111348f7cdc30c6a1b903987612ddeafb23 (diff)
i915: Add support for MSI and interrupt mitigation.
Previous attempts at interrupt mitigation had been foiled by i915_wait_irq's failure to update the sarea seqno value when the status page indicated that the seqno had already been passed. MSI support has been seen to cut CPU costs by up to 40% in some workloads by avoiding other expensive interrupt handlers for frequent graphics interrupts. Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c153
1 files changed, 108 insertions, 45 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 4a2de7897344..24d11ed5bbc7 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -33,6 +33,31 @@
33 33
34#define MAX_NOPID ((u32)~0) 34#define MAX_NOPID ((u32)~0)
35 35
36/** These are the interrupts used by the driver */
37#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \
38 I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
39 I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
40
41static inline void
42i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
43{
44 if ((dev_priv->irq_mask_reg & mask) != 0) {
45 dev_priv->irq_mask_reg &= ~mask;
46 I915_WRITE(IMR, dev_priv->irq_mask_reg);
47 (void) I915_READ(IMR);
48 }
49}
50
51static inline void
52i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
53{
54 if ((dev_priv->irq_mask_reg & mask) != mask) {
55 dev_priv->irq_mask_reg |= mask;
56 I915_WRITE(IMR, dev_priv->irq_mask_reg);
57 (void) I915_READ(IMR);
58 }
59}
60
36/** 61/**
37 * Emit blits for scheduled buffer swaps. 62 * Emit blits for scheduled buffer swaps.
38 * 63 *
@@ -229,46 +254,50 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
229{ 254{
230 struct drm_device *dev = (struct drm_device *) arg; 255 struct drm_device *dev = (struct drm_device *) arg;
231 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; 256 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
232 u16 temp;
233 u32 pipea_stats, pipeb_stats; 257 u32 pipea_stats, pipeb_stats;
258 u32 iir;
234 259
235 pipea_stats = I915_READ(PIPEASTAT); 260 pipea_stats = I915_READ(PIPEASTAT);
236 pipeb_stats = I915_READ(PIPEBSTAT); 261 pipeb_stats = I915_READ(PIPEBSTAT);
237 262
238 temp = I915_READ16(IIR); 263 if (dev->pdev->msi_enabled)
239 264 I915_WRITE(IMR, ~0);
240 temp &= (I915_USER_INTERRUPT | 265 iir = I915_READ(IIR);
241 I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
242 I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT);
243 266
244 DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp); 267 DRM_DEBUG("iir=%08x\n", iir);
245 268
246 if (temp == 0) 269 if (iir == 0) {
270 if (dev->pdev->msi_enabled) {
271 I915_WRITE(IMR, dev_priv->irq_mask_reg);
272 (void) I915_READ(IMR);
273 }
247 return IRQ_NONE; 274 return IRQ_NONE;
275 }
248 276
249 I915_WRITE16(IIR, temp); 277 I915_WRITE(IIR, iir);
250 (void) I915_READ16(IIR); 278 if (dev->pdev->msi_enabled)
251 DRM_READMEMORYBARRIER(); 279 I915_WRITE(IMR, dev_priv->irq_mask_reg);
280 (void) I915_READ(IIR); /* Flush posted writes */
252 281
253 dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); 282 dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
254 283
255 if (temp & I915_USER_INTERRUPT) 284 if (iir & I915_USER_INTERRUPT)
256 DRM_WAKEUP(&dev_priv->irq_queue); 285 DRM_WAKEUP(&dev_priv->irq_queue);
257 286
258 if (temp & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | 287 if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
259 I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) { 288 I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
260 int vblank_pipe = dev_priv->vblank_pipe; 289 int vblank_pipe = dev_priv->vblank_pipe;
261 290
262 if ((vblank_pipe & 291 if ((vblank_pipe &
263 (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) 292 (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
264 == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { 293 == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
265 if (temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) 294 if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
266 atomic_inc(&dev->vbl_received); 295 atomic_inc(&dev->vbl_received);
267 if (temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) 296 if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
268 atomic_inc(&dev->vbl_received2); 297 atomic_inc(&dev->vbl_received2);
269 } else if (((temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) && 298 } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
270 (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || 299 (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
271 ((temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) && 300 ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
272 (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) 301 (vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
273 atomic_inc(&dev->vbl_received); 302 atomic_inc(&dev->vbl_received);
274 303
@@ -314,6 +343,27 @@ static int i915_emit_irq(struct drm_device * dev)
314 return dev_priv->counter; 343 return dev_priv->counter;
315} 344}
316 345
346static void i915_user_irq_get(struct drm_device *dev)
347{
348 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
349
350 spin_lock(&dev_priv->user_irq_lock);
351 if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
352 i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
353 spin_unlock(&dev_priv->user_irq_lock);
354}
355
356static void i915_user_irq_put(struct drm_device *dev)
357{
358 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
359
360 spin_lock(&dev_priv->user_irq_lock);
361 BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
362 if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
363 i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
364 spin_unlock(&dev_priv->user_irq_lock);
365}
366
317static int i915_wait_irq(struct drm_device * dev, int irq_nr) 367static int i915_wait_irq(struct drm_device * dev, int irq_nr)
318{ 368{
319 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; 369 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -322,13 +372,17 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
322 DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr, 372 DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
323 READ_BREADCRUMB(dev_priv)); 373 READ_BREADCRUMB(dev_priv));
324 374
325 if (READ_BREADCRUMB(dev_priv) >= irq_nr) 375 if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
376 dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
326 return 0; 377 return 0;
378 }
327 379
328 dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; 380 dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
329 381
382 i915_user_irq_get(dev);
330 DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, 383 DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
331 READ_BREADCRUMB(dev_priv) >= irq_nr); 384 READ_BREADCRUMB(dev_priv) >= irq_nr);
385 i915_user_irq_put(dev);
332 386
333 if (ret == -EBUSY) { 387 if (ret == -EBUSY) {
334 DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", 388 DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
@@ -413,20 +467,6 @@ int i915_irq_wait(struct drm_device *dev, void *data,
413 return i915_wait_irq(dev, irqwait->irq_seq); 467 return i915_wait_irq(dev, irqwait->irq_seq);
414} 468}
415 469
416static void i915_enable_interrupt (struct drm_device *dev)
417{
418 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
419 u16 flag;
420
421 flag = 0;
422 if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
423 flag |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
424 if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
425 flag |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
426
427 I915_WRITE16(IER, I915_USER_INTERRUPT | flag);
428}
429
430/* Set the vblank monitor pipe 470/* Set the vblank monitor pipe
431 */ 471 */
432int i915_vblank_pipe_set(struct drm_device *dev, void *data, 472int i915_vblank_pipe_set(struct drm_device *dev, void *data,
@@ -434,6 +474,7 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
434{ 474{
435 drm_i915_private_t *dev_priv = dev->dev_private; 475 drm_i915_private_t *dev_priv = dev->dev_private;
436 drm_i915_vblank_pipe_t *pipe = data; 476 drm_i915_vblank_pipe_t *pipe = data;
477 u32 enable_mask = 0, disable_mask = 0;
437 478
438 if (!dev_priv) { 479 if (!dev_priv) {
439 DRM_ERROR("called with no initialization\n"); 480 DRM_ERROR("called with no initialization\n");
@@ -445,9 +486,20 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
445 return -EINVAL; 486 return -EINVAL;
446 } 487 }
447 488
448 dev_priv->vblank_pipe = pipe->pipe; 489 if (pipe->pipe & DRM_I915_VBLANK_PIPE_A)
490 enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
491 else
492 disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
493
494 if (pipe->pipe & DRM_I915_VBLANK_PIPE_B)
495 enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
496 else
497 disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
449 498
450 i915_enable_interrupt (dev); 499 i915_enable_irq(dev_priv, enable_mask);
500 i915_disable_irq(dev_priv, disable_mask);
501
502 dev_priv->vblank_pipe = pipe->pipe;
451 503
452 return 0; 504 return 0;
453} 505}
@@ -464,7 +516,7 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
464 return -EINVAL; 516 return -EINVAL;
465 } 517 }
466 518
467 flag = I915_READ(IER); 519 flag = I915_READ(IMR);
468 pipe->pipe = 0; 520 pipe->pipe = 0;
469 if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) 521 if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
470 pipe->pipe |= DRM_I915_VBLANK_PIPE_A; 522 pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
@@ -586,9 +638,9 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
586{ 638{
587 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; 639 drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
588 640
589 I915_WRITE16(HWSTAM, 0xfffe); 641 I915_WRITE(HWSTAM, 0xfffe);
590 I915_WRITE16(IMR, 0x0); 642 I915_WRITE(IMR, 0x0);
591 I915_WRITE16(IER, 0x0); 643 I915_WRITE(IER, 0x0);
592} 644}
593 645
594void i915_driver_irq_postinstall(struct drm_device * dev) 646void i915_driver_irq_postinstall(struct drm_device * dev)
@@ -601,7 +653,18 @@ void i915_driver_irq_postinstall(struct drm_device * dev)
601 653
602 if (!dev_priv->vblank_pipe) 654 if (!dev_priv->vblank_pipe)
603 dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; 655 dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
604 i915_enable_interrupt(dev); 656
657 /* Set initial unmasked IRQs to just the selected vblank pipes. */
658 dev_priv->irq_mask_reg = ~0;
659 if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
660 dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
661 if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
662 dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
663
664 I915_WRITE(IMR, dev_priv->irq_mask_reg);
665 I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
666 (void) I915_READ(IER);
667
605 DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); 668 DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
606} 669}
607 670
@@ -613,10 +676,10 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
613 if (!dev_priv) 676 if (!dev_priv)
614 return; 677 return;
615 678
616 I915_WRITE16(HWSTAM, 0xffff); 679 I915_WRITE(HWSTAM, 0xffff);
617 I915_WRITE16(IMR, 0xffff); 680 I915_WRITE(IMR, 0xffff);
618 I915_WRITE16(IER, 0x0); 681 I915_WRITE(IER, 0x0);
619 682
620 temp = I915_READ16(IIR); 683 temp = I915_READ(IIR);
621 I915_WRITE16(IIR, temp); 684 I915_WRITE(IIR, temp);
622} 685}