diff options
author | Eric Anholt <eric@anholt.net> | 2008-07-29 15:10:39 -0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2008-10-17 17:10:10 -0400 |
commit | ed4cb4142b242d8090d3811d5eb4abf6aa985bc8 (patch) | |
tree | 5d19db6ffec95b18654bbde820ae8ff8995f856e /drivers/gpu/drm/i915/i915_irq.c | |
parent | 585fb111348f7cdc30c6a1b903987612ddeafb23 (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.c | 153 |
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 | |||
41 | static inline void | ||
42 | i915_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 | |||
51 | static inline void | ||
52 | i915_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 | ||
346 | static 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 | |||
356 | static 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 | |||
317 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) | 367 | static 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 | ||
416 | static 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 | */ |
432 | int i915_vblank_pipe_set(struct drm_device *dev, void *data, | 472 | int 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 | ||
594 | void i915_driver_irq_postinstall(struct drm_device * dev) | 646 | void 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 | } |