diff options
Diffstat (limited to 'shared-core/i915_irq.c')
-rw-r--r-- | shared-core/i915_irq.c | 240 |
1 files changed, 127 insertions, 113 deletions
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 2d355688..bad5ae29 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -35,6 +35,33 @@ #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_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) + +static inline void +i915_enable_irq(struct drm_i915_private *dev_priv, uint32_t 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(struct drm_i915_private *dev_priv, uint32_t 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); + } +} + /** * i915_get_pipe - return the the pipe associated with a given plane * @dev: DRM device @@ -443,9 +470,12 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane) static struct drm_device *hotplug_dev; -/* - * This code is called in a more safe envirmoent to handle the hotplugs. - * Add code here for hotplug love to userspace. +/** + * 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. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void i915_hotplug_work_func(void *work) @@ -485,7 +515,6 @@ static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat) if (stat & SDVOC_HOTPLUG_INT_STATUS) { DRM_DEBUG("sDVOC event\n"); } - queue_work(dev_priv->wq, &hotplug); return 0; @@ -497,30 +526,29 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) struct drm_i915_master_private *master_priv; struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; u32 iir; - u32 pipea_stats = 0, pipeb_stats, tvdac; + u32 pipea_stats = 0, pipeb_stats = 0, tvdac; int hotplug = 0; int vblank = 0; - /* On i8xx/i915 hw the IIR and IER are 16bit on i9xx its 32bit */ - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - iir = I915_READ(IIR); - else - iir = I915_READ16(IIR); - - iir &= (dev_priv->irq_enable_reg | I915_USER_INTERRUPT); - + if (dev->pdev->msi_enabled) + I915_WRITE(IMR, ~0); + iir = I915_READ(IIR); #if 0 DRM_DEBUG("flag=%08x\n", iir); #endif + atomic_inc(&dev_priv->irq_received); if (iir == 0) { -#if 0 DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n", iir, I915_READ(IMR), I915_READ(IER), I915_READ(PIPEASTAT), I915_READ(PIPEBSTAT)); -#endif + if (dev->pdev->msi_enabled) { + I915_WRITE(IMR, + dev_priv->irq_mask_reg); + (void) I915_READ(IMR); + } return IRQ_NONE; } @@ -530,46 +558,29 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) */ if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { pipea_stats = I915_READ(PIPEASTAT); - if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| - PIPE_VBLANK_INTERRUPT_STATUS)) - { - vblank++; - drm_handle_vblank(dev, i915_get_plane(dev, 0)); - } - - /* This is a global event, and not a pipe A event */ - if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS) - hotplug = 1; - - if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS) { - hotplug = 1; - /* Toggle hotplug detection to clear hotplug status */ - tvdac = I915_READ(TV_DAC); - I915_WRITE(TV_DAC, tvdac & ~TVDAC_STATE_CHG_EN); - I915_WRITE(TV_DAC, tvdac | TVDAC_STATE_CHG_EN); - } - I915_WRITE(PIPEASTAT, pipea_stats); } if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { pipeb_stats = I915_READ(PIPEBSTAT); - if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| - PIPE_VBLANK_INTERRUPT_STATUS)) - { - vblank++; - drm_handle_vblank(dev, i915_get_plane(dev, 1)); - } I915_WRITE(PIPEBSTAT, pipeb_stats); } - /* Clear the generated interrupt */ - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { - I915_WRITE(IIR, iir); - (void) I915_READ(IIR); - } else { - I915_WRITE16(IIR, iir); - (void) I915_READ16(IIR); + I915_WRITE(IIR, iir); + if (dev->pdev->msi_enabled) + I915_WRITE(IMR, dev_priv->irq_mask_reg); + (void) I915_READ(IIR); /* Flush posted writes */ + + /* This is a global event, and not a pipe A event */ + if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS) + hotplug = 1; + + if (pipea_stats & PIPE_HOTPLUG_TV_INTERRUPT_STATUS) { + hotplug = 1; + /* Toggle hotplug detection to clear hotplug status */ + tvdac = I915_READ(TV_DAC); + I915_WRITE(TV_DAC, tvdac & ~TVDAC_STATE_CHG_EN); + I915_WRITE(TV_DAC, tvdac | TVDAC_STATE_CHG_EN); } if (dev->primary->master) { @@ -578,12 +589,25 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) } if (iir & I915_USER_INTERRUPT) { + dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); DRM_WAKEUP(&dev_priv->irq_queue); #ifdef I915_HAVE_FENCE i915_fence_handler(dev); #endif } + if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 0)); + } + + if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 1)); + } + if (vblank) { if (dev_priv->swaps_pending > 0) drm_locked_tasklet(dev, i915_vblank_tasklet); @@ -635,15 +659,9 @@ void i915_user_irq_on(struct drm_device *dev) struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; 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; - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - I915_WRITE(IER, dev_priv->irq_enable_reg); - else - I915_WRITE16(IER, dev_priv->irq_enable_reg); - } + if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)) + i915_enable_irq(dev_priv, I915_USER_INTERRUPT); DRM_SPINUNLOCK(&dev_priv->user_irq_lock); - } void i915_user_irq_off(struct drm_device *dev) @@ -651,28 +669,38 @@ void i915_user_irq_off(struct drm_device *dev) struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; DRM_SPINLOCK(&dev_priv->user_irq_lock); - if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { - // dev_priv->irq_enable_reg &= ~I915_USER_INTERRUPT; - // if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - // I915_WRITE(IER, dev_priv->irq_enable_reg); - // else - // I915_WRITE16(IER, dev_priv->irq_enable_reg); - } + BUG_ON(dev_priv->irq_enabled && dev_priv->user_irq_refcount <= 0); + if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) + i915_disable_irq(dev_priv, I915_USER_INTERRUPT); 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) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_i915_master_private *master_priv; int ret = 0; + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr, READ_BREADCRUMB(dev_priv)); - if (READ_BREADCRUMB(dev_priv) >= irq_nr) + master_priv = dev->primary->master->driver_priv; + + if (!master_priv) { + DRM_ERROR("no master priv?\n"); + return -EINVAL; + } + + if (READ_BREADCRUMB(dev_priv) >= irq_nr) { + master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); return 0; + } i915_user_irq_on(dev); DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, @@ -684,10 +712,8 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); } - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; + if (READ_BREADCRUMB(dev_priv) >= irq_nr) master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - } return ret; } @@ -739,16 +765,17 @@ int i915_enable_vblank(struct drm_device *dev, int plane) struct drm_i915_private *dev_priv = (struct drm_i915_private *) 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 = 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 = 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", @@ -775,11 +802,9 @@ int i915_enable_vblank(struct drm_device *dev, int plane) I915_WRITE(pipestat_reg, pipestat); } - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - I915_WRITE(IER, dev_priv->irq_enable_reg); - else - I915_WRITE16(IER, dev_priv->irq_enable_reg); - + DRM_SPINLOCK(&dev_priv->user_irq_lock); + i915_enable_irq(dev_priv, mask_reg); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); return 0; } @@ -789,16 +814,17 @@ void i915_disable_vblank(struct drm_device *dev, int plane) struct drm_i915_private *dev_priv = (struct drm_i915_private *) 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 = 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 = 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", @@ -806,13 +832,11 @@ void i915_disable_vblank(struct drm_device *dev, int plane) break; } - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - I915_WRITE(IER, dev_priv->irq_enable_reg); - else - I915_WRITE16(IER, dev_priv->irq_enable_reg); + DRM_SPINLOCK(&dev_priv->user_irq_lock); + i915_disable_irq(dev_priv, mask_reg); + DRM_SPINUNLOCK(&dev_priv->user_irq_lock); - if (pipestat_reg) - { + if (pipestat_reg) { pipestat = I915_READ (pipestat_reg); pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | PIPE_VBLANK_INTERRUPT_ENABLE); @@ -822,6 +846,7 @@ void i915_disable_vblank(struct drm_device *dev, int plane) pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | PIPE_VBLANK_INTERRUPT_STATUS); I915_WRITE(pipestat_reg, pipestat); + (void) I915_READ(pipestat_reg); } } @@ -829,15 +854,15 @@ void i915_enable_interrupt (struct drm_device *dev) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_connector *o; - - dev_priv->irq_enable_reg |= I915_USER_INTERRUPT; + + dev_priv->irq_mask_reg &= ~0; if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { if (dev->mode_config.num_connector) - dev_priv->irq_enable_reg |= I915_DISPLAY_PORT_INTERRUPT; + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PORT_INTERRUPT; } else { if (dev->mode_config.num_connector) - dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; /* Enable global interrupts for hotplug - not a pipeA event */ I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) | @@ -847,7 +872,8 @@ void i915_enable_interrupt (struct drm_device *dev) PIPE_HOTPLUG_INTERRUPT_STATUS); } - if (dev_priv->irq_enable_reg & (I915_DISPLAY_PORT_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)) { + if (!(dev_priv->irq_mask_reg & I915_DISPLAY_PORT_INTERRUPT) || + !(dev_priv->irq_mask_reg & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)) { u32 temp = 0; if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { @@ -891,11 +917,9 @@ void i915_enable_interrupt (struct drm_device *dev) } } - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - I915_WRITE(IER, dev_priv->irq_enable_reg); - else - I915_WRITE16(IER, dev_priv->irq_enable_reg); - + I915_WRITE(IMR, dev_priv->irq_mask_reg); + I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); + (void) I915_READ (IER); dev_priv->irq_enabled = 1; } @@ -927,17 +951,15 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_vblank_pipe *pipe = data; - u16 flag; + u32 flag = 0; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) - flag = I915_READ(IER); - else - flag = I915_READ16(IER); + if (dev_priv->irq_enabled) + flag = ~dev_priv->irq_mask_reg; pipe->pipe = 0; if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) @@ -1121,20 +1143,12 @@ void i915_driver_irq_preinstall(struct drm_device * dev) tmp = I915_READ(PIPEBSTAT); I915_WRITE(PIPEBSTAT, tmp); - - I915_WRITE16(HWSTAM, 0xeffe); - if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) { - I915_WRITE(IMR, 0x0); - I915_WRITE(IER, 0x0); - tmp = I915_READ(IIR); - I915_WRITE(IIR, tmp); - } else { - I915_WRITE16(IMR, 0x0); - I915_WRITE16(IER, 0x0); - tmp = I915_READ16(IIR); - I915_WRITE16(IIR, tmp); - } - + atomic_set(&dev_priv->irq_received, 0); + I915_WRITE(HWSTAM, 0xffff); + I915_WRITE(IER, 0x0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IIR, 0xffffffff); + (void) I915_READ(IIR); } int i915_driver_irq_postinstall(struct drm_device * dev) @@ -1148,7 +1162,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) @@ -1179,7 +1193,7 @@ void i915_driver_irq_uninstall(struct drm_device * dev) if (!dev_priv) return; - dev_priv->irq_enabled = 0; + dev_priv->irq_enabled = 1; temp = I915_READ(PIPEASTAT); I915_WRITE(PIPEASTAT, temp); |