summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/i915_irq.c
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2008-07-29 12:10:39 -0700
committerDave Airlie <airlied@linux.ie>2008-10-18 07:10:10 +1000
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 4a2de789734..24d11ed5bbc 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -33,6 +33,31 @@
#define MAX_NOPID ((u32)~0)
+/** These are the interrupts used by the driver */
+#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \
+ I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
+
+static inline void
+i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ if ((dev_priv->irq_mask_reg & mask) != 0) {
+ dev_priv->irq_mask_reg &= ~mask;
+ I915_WRITE(IMR, dev_priv->irq_mask_reg);
+ (void) I915_READ(IMR);
+ }
+}
+
+static inline void
+i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)
+{
+ if ((dev_priv->irq_mask_reg & mask) != mask) {
+ dev_priv->irq_mask_reg |= mask;
+ I915_WRITE(IMR, dev_priv->irq_mask_reg);
+ (void) I915_READ(IMR);
+ }
+}
+
/**
* Emit blits for scheduled buffer swaps.
*
@@ -229,46 +254,50 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- u16 temp;
u32 pipea_stats, pipeb_stats;
+ u32 iir;
pipea_stats = I915_READ(PIPEASTAT);
pipeb_stats = I915_READ(PIPEBSTAT);
- temp = I915_READ16(IIR);
-
- temp &= (I915_USER_INTERRUPT |
- I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
- I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT);
+ if (dev->pdev->msi_enabled)
+ I915_WRITE(IMR, ~0);
+ iir = I915_READ(IIR);
- DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp);
+ DRM_DEBUG("iir=%08x\n", iir);
- if (temp == 0)
+ if (iir == 0) {
+ if (dev->pdev->msi_enabled) {
+ I915_WRITE(IMR, dev_priv->irq_mask_reg);
+ (void) I915_READ(IMR);
+ }
return IRQ_NONE;
+ }
- I915_WRITE16(IIR, temp);
- (void) I915_READ16(IIR);
- DRM_READMEMORYBARRIER();
+ I915_WRITE(IIR, iir);
+ if (dev->pdev->msi_enabled)
+ I915_WRITE(IMR, dev_priv->irq_mask_reg);
+ (void) I915_READ(IIR); /* Flush posted writes */
dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
- if (temp & I915_USER_INTERRUPT)
+ if (iir & I915_USER_INTERRUPT)
DRM_WAKEUP(&dev_priv->irq_queue);
- if (temp & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
- I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
+ if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
+ I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) {
int vblank_pipe = dev_priv->vblank_pipe;
if ((vblank_pipe &
(DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
== (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
- if (temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
+ if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
atomic_inc(&dev->vbl_received);
- if (temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
+ if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)
atomic_inc(&dev->vbl_received2);
- } else if (((temp & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
+ } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) &&
(vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
- ((temp & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
+ ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) &&
(vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
atomic_inc(&dev->vbl_received);
@@ -314,6 +343,27 @@ static int i915_emit_irq(struct drm_device * dev)
return dev_priv->counter;
}
+static void i915_user_irq_get(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ spin_lock(&dev_priv->user_irq_lock);
+ if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1))
+ i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+ spin_unlock(&dev_priv->user_irq_lock);
+}
+
+static void i915_user_irq_put(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+ spin_lock(&dev_priv->user_irq_lock);
+ BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
+ if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0))
+ i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+ spin_unlock(&dev_priv->user_irq_lock);
+}
+
static int i915_wait_irq(struct drm_device * dev, int irq_nr)
{
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)
DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
READ_BREADCRUMB(dev_priv));
- if (READ_BREADCRUMB(dev_priv) >= irq_nr)
+ if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
+ dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return 0;
+ }
dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;
+ i915_user_irq_get(dev);
DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ,
READ_BREADCRUMB(dev_priv) >= irq_nr);
+ i915_user_irq_put(dev);
if (ret == -EBUSY) {
DRM_ERROR("EBUSY -- rec: %d emitted: %d\n",
@@ -413,20 +467,6 @@ int i915_irq_wait(struct drm_device *dev, void *data,
return i915_wait_irq(dev, irqwait->irq_seq);
}
-static void i915_enable_interrupt (struct drm_device *dev)
-{
- drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- u16 flag;
-
- flag = 0;
- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
- flag |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
- if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
- flag |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
-
- I915_WRITE16(IER, I915_USER_INTERRUPT | flag);
-}
-
/* Set the vblank monitor pipe
*/
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,
{
drm_i915_private_t *dev_priv = dev->dev_private;
drm_i915_vblank_pipe_t *pipe = data;
+ u32 enable_mask = 0, disable_mask = 0;
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
@@ -445,9 +486,20 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
return -EINVAL;
}
- dev_priv->vblank_pipe = pipe->pipe;
+ if (pipe->pipe & DRM_I915_VBLANK_PIPE_A)
+ enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
+ else
+ disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
+
+ if (pipe->pipe & DRM_I915_VBLANK_PIPE_B)
+ enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+ else
+ disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
- i915_enable_interrupt (dev);
+ i915_enable_irq(dev_priv, enable_mask);
+ i915_disable_irq(dev_priv, disable_mask);
+
+ dev_priv->vblank_pipe = pipe->pipe;
return 0;
}
@@ -464,7 +516,7 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
return -EINVAL;
}
- flag = I915_READ(IER);
+ flag = I915_READ(IMR);
pipe->pipe = 0;
if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT)
pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
@@ -586,9 +638,9 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
- I915_WRITE16(HWSTAM, 0xfffe);
- I915_WRITE16(IMR, 0x0);
- I915_WRITE16(IER, 0x0);
+ I915_WRITE(HWSTAM, 0xfffe);
+ I915_WRITE(IMR, 0x0);
+ I915_WRITE(IER, 0x0);
}
void i915_driver_irq_postinstall(struct drm_device * dev)
@@ -601,7 +653,18 @@ void i915_driver_irq_postinstall(struct drm_device * dev)
if (!dev_priv->vblank_pipe)
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A;
- i915_enable_interrupt(dev);
+
+ /* Set initial unmasked IRQs to just the selected vblank pipes. */
+ dev_priv->irq_mask_reg = ~0;
+ if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
+ dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT;
+ if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
+ dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
+
+ I915_WRITE(IMR, dev_priv->irq_mask_reg);
+ I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
+ (void) I915_READ(IER);
+
DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
}
@@ -613,10 +676,10 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
if (!dev_priv)
return;
- I915_WRITE16(HWSTAM, 0xffff);
- I915_WRITE16(IMR, 0xffff);
- I915_WRITE16(IER, 0x0);
+ I915_WRITE(HWSTAM, 0xffff);
+ I915_WRITE(IMR, 0xffff);
+ I915_WRITE(IER, 0x0);
- temp = I915_READ16(IIR);
- I915_WRITE16(IIR, temp);
+ temp = I915_READ(IIR);
+ I915_WRITE(IIR, temp);
}