diff options
author | Keith Packard <keithp@keithp.com> | 2008-10-16 14:31:38 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2008-10-22 23:42:26 -0400 |
commit | e9d21d7f5ae1e625f3687d88bb50b00478c533ad (patch) | |
tree | 7df5011d358145f89564b42c38f58cab16f78ea7 /drivers/gpu/drm/i915/i915_irq.c | |
parent | 4b40893918203ee1a1f6a114316c2a19c072e9bd (diff) |
i915: Protect vblank IRQ reg access with spinlock
This uses the same spinlock as the user_irq code as it shares the same
register, ensuring that interrupt registers are updated atomically.
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/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 69 |
1 files changed, 40 insertions, 29 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index baae511c785b..671dd79d0aac 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -481,22 +481,24 @@ static int i915_emit_irq(struct drm_device * dev) | |||
481 | void i915_user_irq_get(struct drm_device *dev) | 481 | void i915_user_irq_get(struct drm_device *dev) |
482 | { | 482 | { |
483 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 483 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
484 | unsigned long irqflags; | ||
484 | 485 | ||
485 | spin_lock(&dev_priv->user_irq_lock); | 486 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
486 | if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) | 487 | if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) |
487 | i915_enable_irq(dev_priv, I915_USER_INTERRUPT); | 488 | i915_enable_irq(dev_priv, I915_USER_INTERRUPT); |
488 | spin_unlock(&dev_priv->user_irq_lock); | 489 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); |
489 | } | 490 | } |
490 | 491 | ||
491 | void i915_user_irq_put(struct drm_device *dev) | 492 | void i915_user_irq_put(struct drm_device *dev) |
492 | { | 493 | { |
493 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 494 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
495 | unsigned long irqflags; | ||
494 | 496 | ||
495 | spin_lock(&dev_priv->user_irq_lock); | 497 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
496 | BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); | 498 | BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); |
497 | if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) | 499 | if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) |
498 | i915_disable_irq(dev_priv, I915_USER_INTERRUPT); | 500 | i915_disable_irq(dev_priv, I915_USER_INTERRUPT); |
499 | spin_unlock(&dev_priv->user_irq_lock); | 501 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); |
500 | } | 502 | } |
501 | 503 | ||
502 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) | 504 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) |
@@ -584,33 +586,37 @@ int i915_enable_vblank(struct drm_device *dev, int plane) | |||
584 | int pipe = i915_get_pipe(dev, plane); | 586 | int pipe = i915_get_pipe(dev, plane); |
585 | u32 pipestat_reg = 0; | 587 | u32 pipestat_reg = 0; |
586 | u32 pipestat; | 588 | u32 pipestat; |
589 | u32 interrupt = 0; | ||
590 | unsigned long irqflags; | ||
587 | 591 | ||
588 | switch (pipe) { | 592 | switch (pipe) { |
589 | case 0: | 593 | case 0: |
590 | pipestat_reg = PIPEASTAT; | 594 | pipestat_reg = PIPEASTAT; |
591 | i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT); | 595 | interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; |
592 | break; | 596 | break; |
593 | case 1: | 597 | case 1: |
594 | pipestat_reg = PIPEBSTAT; | 598 | pipestat_reg = PIPEBSTAT; |
595 | i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); | 599 | interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; |
596 | break; | 600 | break; |
597 | default: | 601 | default: |
598 | DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", | 602 | DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", |
599 | pipe); | 603 | pipe); |
600 | break; | 604 | return 0; |
601 | } | 605 | } |
602 | 606 | ||
603 | if (pipestat_reg) { | 607 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
604 | pipestat = I915_READ(pipestat_reg); | 608 | pipestat = I915_READ(pipestat_reg); |
605 | if (IS_I965G(dev)) | 609 | if (IS_I965G(dev)) |
606 | pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; | 610 | pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; |
607 | else | 611 | else |
608 | pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; | 612 | pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; |
609 | /* Clear any stale interrupt status */ | 613 | /* Clear any stale interrupt status */ |
610 | pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | | 614 | pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | |
611 | PIPE_VBLANK_INTERRUPT_STATUS); | 615 | PIPE_VBLANK_INTERRUPT_STATUS); |
612 | I915_WRITE(pipestat_reg, pipestat); | 616 | I915_WRITE(pipestat_reg, pipestat); |
613 | } | 617 | (void) I915_READ(pipestat_reg); /* Posting read */ |
618 | i915_enable_irq(dev_priv, interrupt); | ||
619 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); | ||
614 | 620 | ||
615 | return 0; | 621 | return 0; |
616 | } | 622 | } |
@@ -621,31 +627,36 @@ void i915_disable_vblank(struct drm_device *dev, int plane) | |||
621 | int pipe = i915_get_pipe(dev, plane); | 627 | int pipe = i915_get_pipe(dev, plane); |
622 | u32 pipestat_reg = 0; | 628 | u32 pipestat_reg = 0; |
623 | u32 pipestat; | 629 | u32 pipestat; |
630 | u32 interrupt = 0; | ||
631 | unsigned long irqflags; | ||
624 | 632 | ||
625 | switch (pipe) { | 633 | switch (pipe) { |
626 | case 0: | 634 | case 0: |
627 | pipestat_reg = PIPEASTAT; | 635 | pipestat_reg = PIPEASTAT; |
628 | i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT); | 636 | interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; |
629 | break; | 637 | break; |
630 | case 1: | 638 | case 1: |
631 | pipestat_reg = PIPEBSTAT; | 639 | pipestat_reg = PIPEBSTAT; |
632 | i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); | 640 | interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; |
633 | break; | 641 | break; |
634 | default: | 642 | default: |
635 | DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", | 643 | DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", |
636 | pipe); | 644 | pipe); |
645 | return; | ||
637 | break; | 646 | break; |
638 | } | 647 | } |
639 | 648 | ||
640 | if (pipestat_reg) { | 649 | spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); |
641 | pipestat = I915_READ(pipestat_reg); | 650 | i915_disable_irq(dev_priv, interrupt); |
642 | pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | | 651 | pipestat = I915_READ(pipestat_reg); |
643 | PIPE_VBLANK_INTERRUPT_ENABLE); | 652 | pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | |
644 | /* Clear any stale interrupt status */ | 653 | PIPE_VBLANK_INTERRUPT_ENABLE); |
645 | pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | | 654 | /* Clear any stale interrupt status */ |
646 | PIPE_VBLANK_INTERRUPT_STATUS); | 655 | pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | |
647 | I915_WRITE(pipestat_reg, pipestat); | 656 | PIPE_VBLANK_INTERRUPT_STATUS); |
648 | } | 657 | I915_WRITE(pipestat_reg, pipestat); |
658 | (void) I915_READ(pipestat_reg); /* Posting read */ | ||
659 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); | ||
649 | } | 660 | } |
650 | 661 | ||
651 | /* Set the vblank monitor pipe | 662 | /* Set the vblank monitor pipe |