diff options
Diffstat (limited to 'shared-core/i915_irq.c')
-rw-r--r-- | shared-core/i915_irq.c | 99 |
1 files changed, 74 insertions, 25 deletions
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 2287cd0c..37d85f40 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -435,6 +435,28 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane) return count; } +/** + * Handler for user interrupts in process context (able to sleep, do VFS + * operations, etc. + * + * If another IRQ comes in while we're in this handler, it will still get put + * on the queue again to be rerun when we finish. + */ +void +i915_user_interrupt_handler(struct work_struct *work) +{ + drm_i915_private_t *dev_priv; + struct drm_device *dev; + + dev_priv = container_of(work, drm_i915_private_t, + user_interrupt_task); + dev = dev_priv->dev; + + mutex_lock(&dev->struct_mutex); + i915_gem_retire_requests(dev); + mutex_unlock(&dev->struct_mutex); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -485,13 +507,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - I915_WRITE(I915REG_INT_IDENTITY_R, iir); + I915_WRITE(I915REG_INT_IDENTITY_R, iir | I915_USER_INTERRUPT); (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted write */ if (iir & I915_USER_INTERRUPT) { DRM_WAKEUP(&dev_priv->irq_queue); #ifdef I915_HAVE_FENCE i915_fence_handler(dev); + schedule_work(&dev_priv->user_interrupt_task); #endif } @@ -526,8 +549,9 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv) { DRM_SPINLOCK(&dev_priv->user_irq_lock); if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ - dev_priv->irq_enable_reg |= I915_USER_INTERRUPT; - I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + dev_priv->irq_mask_reg &= ~I915_USER_INTERRUPT; + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + (void) I915_READ (I915REG_INT_ENABLE_R); } DRM_SPINUNLOCK(&dev_priv->user_irq_lock); @@ -536,15 +560,17 @@ void i915_user_irq_on(drm_i915_private_t *dev_priv) void i915_user_irq_off(drm_i915_private_t *dev_priv) { DRM_SPINLOCK(&dev_priv->user_irq_lock); + BUG_ON(dev_priv->user_irq_refcount <= 0); if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { - // dev_priv->irq_enable_reg &= ~USER_INT_FLAG; - // I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + dev_priv->irq_mask_reg |= I915_USER_INTERRUPT; + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + (void) I915_READ(I915REG_INT_MASK_R); } DRM_SPINUNLOCK(&dev_priv->user_irq_lock); } -static int i915_wait_irq(struct drm_device * dev, int irq_nr) +int i915_wait_irq(struct drm_device * dev, int irq_nr) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = 0; @@ -618,16 +644,17 @@ int i915_enable_vblank(struct drm_device *dev, int plane) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe = i915_get_pipe(dev, plane); u32 pipestat_reg = 0; + u32 mask_reg = 0; u32 pipestat; switch (pipe) { case 0: pipestat_reg = I915REG_PIPEASTAT; - dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; + mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; break; case 1: pipestat_reg = I915REG_PIPEBSTAT; - dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; + mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; break; default: DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", @@ -653,7 +680,11 @@ int i915_enable_vblank(struct drm_device *dev, int plane) I915_VBLANK_INTERRUPT_STATUS); I915_WRITE(pipestat_reg, pipestat); } - I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + DRM_SPINLOCK(&dev_priv->user_irq_lock); + dev_priv->irq_mask_reg &= ~mask_reg; + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + I915_READ(I915REG_INT_MASK_R); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); return 0; } @@ -663,16 +694,17 @@ void i915_disable_vblank(struct drm_device *dev, int plane) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe = i915_get_pipe(dev, plane); u32 pipestat_reg = 0; + u32 mask_reg = 0; u32 pipestat; switch (pipe) { case 0: pipestat_reg = I915REG_PIPEASTAT; - dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; + mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; break; case 1: pipestat_reg = I915REG_PIPEBSTAT; - dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; + mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; break; default: DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", @@ -680,7 +712,11 @@ void i915_disable_vblank(struct drm_device *dev, int plane) break; } - I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + DRM_SPINLOCK(&dev_priv->user_irq_lock); + dev_priv->irq_mask_reg |= mask_reg; + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + (void) I915_READ (I915REG_INT_MASK_R); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); if (pipestat_reg) { pipestat = I915_READ (pipestat_reg); @@ -692,6 +728,7 @@ void i915_disable_vblank(struct drm_device *dev, int plane) pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS | I915_VBLANK_INTERRUPT_STATUS); I915_WRITE(pipestat_reg, pipestat); + (void) I915_READ(pipestat_reg); } } @@ -699,12 +736,27 @@ static void i915_enable_interrupt (struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->irq_enable_reg |= I915_USER_INTERRUPT; - - I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + dev_priv->irq_mask_reg = (I915_USER_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); + I915_WRITE(I915REG_INT_MASK_R, dev_priv->irq_mask_reg); + I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_mask_reg); + (void) I915_READ (I915REG_INT_ENABLE_R); dev_priv->irq_enabled = 1; } +static void i915_disable_interrupt (struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + I915_WRITE(I915REG_HWSTAM, 0xffffffff); + I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); + I915_WRITE(I915REG_INT_ENABLE_R, 0); + I915_WRITE(I915REG_INT_IDENTITY_R, 0xffffffff); + (void) I915_READ (I915REG_INT_IDENTITY_R); + dev_priv->irq_enabled = 0; +} + /* Set the vblank monitor pipe */ int i915_vblank_pipe_set(struct drm_device *dev, void *data, @@ -910,9 +962,11 @@ void i915_driver_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - I915_WRITE16(I915REG_HWSTAM, 0xeffe); - I915_WRITE16(I915REG_INT_MASK_R, 0x0); - I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); + I915_WRITE(I915REG_HWSTAM, 0xffff); + I915_WRITE(I915REG_INT_ENABLE_R, 0x0); + I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); + I915_WRITE(I915REG_INT_IDENTITY_R, 0xffffffff); + (void) I915_READ(I915REG_INT_IDENTITY_R); } int i915_driver_irq_postinstall(struct drm_device * dev) @@ -926,7 +980,7 @@ int i915_driver_irq_postinstall(struct drm_device * dev) DRM_SPININIT(&dev_priv->user_irq_lock, "userirq"); dev_priv->user_irq_refcount = 0; - dev_priv->irq_enable_reg = 0; + dev_priv->irq_mask_reg = 0; ret = drm_vblank_init(dev, num_pipes); if (ret) @@ -953,15 +1007,10 @@ void i915_driver_irq_uninstall(struct drm_device * dev) if (!dev_priv) return; - dev_priv->irq_enabled = 0; - I915_WRITE(I915REG_HWSTAM, 0xffffffff); - I915_WRITE(I915REG_INT_MASK_R, 0xffffffff); - I915_WRITE(I915REG_INT_ENABLE_R, 0x0); + i915_disable_interrupt (dev); temp = I915_READ(I915REG_PIPEASTAT); I915_WRITE(I915REG_PIPEASTAT, temp); temp = I915_READ(I915REG_PIPEBSTAT); I915_WRITE(I915REG_PIPEBSTAT, temp); - temp = I915_READ(I915REG_INT_IDENTITY_R); - I915_WRITE(I915REG_INT_IDENTITY_R, temp); } |