diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 961 |
1 files changed, 417 insertions, 544 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2fe92d2e0f62..efd7827ff181 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -126,7 +126,7 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = { POSTING_READ(GEN8_##type##_IIR(which)); \ } while (0) -#define GEN5_IRQ_RESET(type) do { \ +#define GEN3_IRQ_RESET(type) do { \ I915_WRITE(type##IMR, 0xffffffff); \ POSTING_READ(type##IMR); \ I915_WRITE(type##IER, 0); \ @@ -136,10 +136,20 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = { POSTING_READ(type##IIR); \ } while (0) +#define GEN2_IRQ_RESET(type) do { \ + I915_WRITE16(type##IMR, 0xffff); \ + POSTING_READ16(type##IMR); \ + I915_WRITE16(type##IER, 0); \ + I915_WRITE16(type##IIR, 0xffff); \ + POSTING_READ16(type##IIR); \ + I915_WRITE16(type##IIR, 0xffff); \ + POSTING_READ16(type##IIR); \ +} while (0) + /* * We should clear IMR at preinstall/uninstall, and just check at postinstall. */ -static void gen5_assert_iir_is_zero(struct drm_i915_private *dev_priv, +static void gen3_assert_iir_is_zero(struct drm_i915_private *dev_priv, i915_reg_t reg) { u32 val = I915_READ(reg); @@ -155,20 +165,43 @@ static void gen5_assert_iir_is_zero(struct drm_i915_private *dev_priv, POSTING_READ(reg); } +static void gen2_assert_iir_is_zero(struct drm_i915_private *dev_priv, + i915_reg_t reg) +{ + u16 val = I915_READ16(reg); + + if (val == 0) + return; + + WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n", + i915_mmio_reg_offset(reg), val); + I915_WRITE16(reg, 0xffff); + POSTING_READ16(reg); + I915_WRITE16(reg, 0xffff); + POSTING_READ16(reg); +} + #define GEN8_IRQ_INIT_NDX(type, which, imr_val, ier_val) do { \ - gen5_assert_iir_is_zero(dev_priv, GEN8_##type##_IIR(which)); \ + gen3_assert_iir_is_zero(dev_priv, GEN8_##type##_IIR(which)); \ I915_WRITE(GEN8_##type##_IER(which), (ier_val)); \ I915_WRITE(GEN8_##type##_IMR(which), (imr_val)); \ POSTING_READ(GEN8_##type##_IMR(which)); \ } while (0) -#define GEN5_IRQ_INIT(type, imr_val, ier_val) do { \ - gen5_assert_iir_is_zero(dev_priv, type##IIR); \ +#define GEN3_IRQ_INIT(type, imr_val, ier_val) do { \ + gen3_assert_iir_is_zero(dev_priv, type##IIR); \ I915_WRITE(type##IER, (ier_val)); \ I915_WRITE(type##IMR, (imr_val)); \ POSTING_READ(type##IMR); \ } while (0) +#define GEN2_IRQ_INIT(type, imr_val, ier_val) do { \ + gen2_assert_iir_is_zero(dev_priv, type##IIR); \ + I915_WRITE16(type##IER, (ier_val)); \ + I915_WRITE16(type##IMR, (imr_val)); \ + POSTING_READ16(type##IMR); \ +} while (0) + static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir); @@ -534,62 +567,16 @@ void ibx_display_interrupt_update(struct drm_i915_private *dev_priv, POSTING_READ(SDEIMR); } -static void -__i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, - u32 enable_mask, u32 status_mask) +u32 i915_pipestat_enable_mask(struct drm_i915_private *dev_priv, + enum pipe pipe) { - i915_reg_t reg = PIPESTAT(pipe); - u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK; - - lockdep_assert_held(&dev_priv->irq_lock); - WARN_ON(!intel_irqs_enabled(dev_priv)); - - if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK || - status_mask & ~PIPESTAT_INT_STATUS_MASK, - "pipe %c: enable_mask=0x%x, status_mask=0x%x\n", - pipe_name(pipe), enable_mask, status_mask)) - return; - - if ((pipestat & enable_mask) == enable_mask) - return; - - dev_priv->pipestat_irq_mask[pipe] |= status_mask; - - /* Enable the interrupt, clear any pending status */ - pipestat |= enable_mask | status_mask; - I915_WRITE(reg, pipestat); - POSTING_READ(reg); -} - -static void -__i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, - u32 enable_mask, u32 status_mask) -{ - i915_reg_t reg = PIPESTAT(pipe); - u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK; + u32 status_mask = dev_priv->pipestat_irq_mask[pipe]; + u32 enable_mask = status_mask << 16; lockdep_assert_held(&dev_priv->irq_lock); - WARN_ON(!intel_irqs_enabled(dev_priv)); - - if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK || - status_mask & ~PIPESTAT_INT_STATUS_MASK, - "pipe %c: enable_mask=0x%x, status_mask=0x%x\n", - pipe_name(pipe), enable_mask, status_mask)) - return; - - if ((pipestat & enable_mask) == 0) - return; - - dev_priv->pipestat_irq_mask[pipe] &= ~status_mask; - pipestat &= ~enable_mask; - I915_WRITE(reg, pipestat); - POSTING_READ(reg); -} - -static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask) -{ - u32 enable_mask = status_mask << 16; + if (INTEL_GEN(dev_priv) < 5) + goto out; /* * On pipe A we don't support the PSR interrupt yet, @@ -612,35 +599,59 @@ static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask) if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV) enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV; +out: + WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK || + status_mask & ~PIPESTAT_INT_STATUS_MASK, + "pipe %c: enable_mask=0x%x, status_mask=0x%x\n", + pipe_name(pipe), enable_mask, status_mask); + return enable_mask; } -void -i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, - u32 status_mask) +void i915_enable_pipestat(struct drm_i915_private *dev_priv, + enum pipe pipe, u32 status_mask) { + i915_reg_t reg = PIPESTAT(pipe); u32 enable_mask; - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - enable_mask = vlv_get_pipestat_enable_mask(&dev_priv->drm, - status_mask); - else - enable_mask = status_mask << 16; - __i915_enable_pipestat(dev_priv, pipe, enable_mask, status_mask); + WARN_ONCE(status_mask & ~PIPESTAT_INT_STATUS_MASK, + "pipe %c: status_mask=0x%x\n", + pipe_name(pipe), status_mask); + + lockdep_assert_held(&dev_priv->irq_lock); + WARN_ON(!intel_irqs_enabled(dev_priv)); + + if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == status_mask) + return; + + dev_priv->pipestat_irq_mask[pipe] |= status_mask; + enable_mask = i915_pipestat_enable_mask(dev_priv, pipe); + + I915_WRITE(reg, enable_mask | status_mask); + POSTING_READ(reg); } -void -i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe, - u32 status_mask) +void i915_disable_pipestat(struct drm_i915_private *dev_priv, + enum pipe pipe, u32 status_mask) { + i915_reg_t reg = PIPESTAT(pipe); u32 enable_mask; - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - enable_mask = vlv_get_pipestat_enable_mask(&dev_priv->drm, - status_mask); - else - enable_mask = status_mask << 16; - __i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask); + WARN_ONCE(status_mask & ~PIPESTAT_INT_STATUS_MASK, + "pipe %c: status_mask=0x%x\n", + pipe_name(pipe), status_mask); + + lockdep_assert_held(&dev_priv->irq_lock); + WARN_ON(!intel_irqs_enabled(dev_priv)); + + if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == 0) + return; + + dev_priv->pipestat_irq_mask[pipe] &= ~status_mask; + enable_mask = i915_pipestat_enable_mask(dev_priv, pipe); + + I915_WRITE(reg, enable_mask | status_mask); + POSTING_READ(reg); } /** @@ -772,6 +783,57 @@ static u32 g4x_get_vblank_counter(struct drm_device *dev, unsigned int pipe) return I915_READ(PIPE_FRMCOUNT_G4X(pipe)); } +/* + * On certain encoders on certain platforms, pipe + * scanline register will not work to get the scanline, + * since the timings are driven from the PORT or issues + * with scanline register updates. + * This function will use Framestamp and current + * timestamp registers to calculate the scanline. + */ +static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_vblank_crtc *vblank = + &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; + const struct drm_display_mode *mode = &vblank->hwmode; + u32 vblank_start = mode->crtc_vblank_start; + u32 vtotal = mode->crtc_vtotal; + u32 htotal = mode->crtc_htotal; + u32 clock = mode->crtc_clock; + u32 scanline, scan_prev_time, scan_curr_time, scan_post_time; + + /* + * To avoid the race condition where we might cross into the + * next vblank just between the PIPE_FRMTMSTMP and TIMESTAMP_CTR + * reads. We make sure we read PIPE_FRMTMSTMP and TIMESTAMP_CTR + * during the same frame. + */ + do { + /* + * This field provides read back of the display + * pipe frame time stamp. The time stamp value + * is sampled at every start of vertical blank. + */ + scan_prev_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe)); + + /* + * The TIMESTAMP_CTR register has the current + * time stamp value. + */ + scan_curr_time = I915_READ_FW(IVB_TIMESTAMP_CTR); + + scan_post_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe)); + } while (scan_post_time != scan_prev_time); + + scanline = div_u64(mul_u32_u32(scan_curr_time - scan_prev_time, + clock), 1000 * htotal); + scanline = min(scanline, vtotal - 1); + scanline = (scanline + vblank_start) % vtotal; + + return scanline; +} + /* I915_READ_FW, only for fast reads of display block, no need for forcewake etc. */ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) { @@ -788,6 +850,9 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; mode = &vblank->hwmode; + if (mode->private_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP) + return __intel_get_crtc_scanline_from_timestamp(crtc); + vtotal = mode->crtc_vtotal; if (mode->flags & DRM_MODE_FLAG_INTERLACE) vtotal /= 2; @@ -1005,6 +1070,8 @@ static void notify_ring(struct intel_engine_cs *engine) spin_lock(&engine->breadcrumbs.irq_lock); wait = engine->breadcrumbs.irq_wait; if (wait) { + bool wakeup = engine->irq_seqno_barrier; + /* We use a callback from the dma-fence to submit * requests after waiting on our own requests. To * ensure minimum delay in queuing the next request to @@ -1017,12 +1084,18 @@ static void notify_ring(struct intel_engine_cs *engine) * and many waiters. */ if (i915_seqno_passed(intel_engine_get_seqno(engine), - wait->seqno) && - !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, - &wait->request->fence.flags)) - rq = i915_gem_request_get(wait->request); + wait->seqno)) { + struct drm_i915_gem_request *waiter = wait->request; + + wakeup = true; + if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, + &waiter->fence.flags) && + intel_wait_check_request(wait, waiter)) + rq = i915_gem_request_get(waiter); + } - wake_up_process(wait->tsk); + if (wakeup) + wake_up_process(wait->tsk); } else { __intel_engine_disarm_breadcrumbs(engine); } @@ -1305,10 +1378,11 @@ static void snb_gt_irq_handler(struct drm_i915_private *dev_priv, static void gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift) { + struct intel_engine_execlists * const execlists = &engine->execlists; bool tasklet = false; if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) { - if (port_count(&engine->execlist_port[0])) { + if (port_count(&execlists->port[0])) { __set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted); tasklet = true; } @@ -1316,11 +1390,11 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift) if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) { notify_ring(engine); - tasklet |= i915.enable_guc_submission; + tasklet |= i915_modparams.enable_guc_submission; } if (tasklet) - tasklet_hi_schedule(&engine->irq_tasklet); + tasklet_hi_schedule(&execlists->irq_tasklet); } static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv, @@ -1573,11 +1647,11 @@ static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, * bonkers. So let's just wait for the next vblank and read * out the buggy result. * - * On CHV sometimes the second CRC is bonkers as well, so + * On GEN8+ sometimes the second CRC is bonkers as well, so * don't trust that one either. */ if (pipe_crc->skipped == 0 || - (IS_CHERRYVIEW(dev_priv) && pipe_crc->skipped == 1)) { + (INTEL_GEN(dev_priv) >= 8 && pipe_crc->skipped == 1)) { pipe_crc->skipped++; spin_unlock(&pipe_crc->lock); return; @@ -1706,8 +1780,21 @@ static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 gt_iir) } } -static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv, - u32 iir, u32 pipe_stats[I915_MAX_PIPES]) +static void i9xx_pipestat_irq_reset(struct drm_i915_private *dev_priv) +{ + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + I915_WRITE(PIPESTAT(pipe), + PIPESTAT_INT_STATUS_MASK | + PIPE_FIFO_UNDERRUN_STATUS); + + dev_priv->pipestat_irq_mask[pipe] = 0; + } +} + +static void i9xx_pipestat_irq_ack(struct drm_i915_private *dev_priv, + u32 iir, u32 pipe_stats[I915_MAX_PIPES]) { int pipe; @@ -1720,7 +1807,7 @@ static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv, for_each_pipe(dev_priv, pipe) { i915_reg_t reg; - u32 mask, iir_bit = 0; + u32 status_mask, enable_mask, iir_bit = 0; /* * PIPESTAT bits get signalled even when the interrupt is @@ -1731,7 +1818,7 @@ static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv, */ /* fifo underruns are filterered in the underrun handler. */ - mask = PIPE_FIFO_UNDERRUN_STATUS; + status_mask = PIPE_FIFO_UNDERRUN_STATUS; switch (pipe) { case PIPE_A: @@ -1745,25 +1832,92 @@ static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv, break; } if (iir & iir_bit) - mask |= dev_priv->pipestat_irq_mask[pipe]; + status_mask |= dev_priv->pipestat_irq_mask[pipe]; - if (!mask) + if (!status_mask) continue; reg = PIPESTAT(pipe); - mask |= PIPESTAT_INT_ENABLE_MASK; - pipe_stats[pipe] = I915_READ(reg) & mask; + pipe_stats[pipe] = I915_READ(reg) & status_mask; + enable_mask = i915_pipestat_enable_mask(dev_priv, pipe); /* * Clear the PIPE*STAT regs before the IIR */ - if (pipe_stats[pipe] & (PIPE_FIFO_UNDERRUN_STATUS | - PIPESTAT_INT_STATUS_MASK)) - I915_WRITE(reg, pipe_stats[pipe]); + if (pipe_stats[pipe]) + I915_WRITE(reg, enable_mask | pipe_stats[pipe]); } spin_unlock(&dev_priv->irq_lock); } +static void i8xx_pipestat_irq_handler(struct drm_i915_private *dev_priv, + u16 iir, u32 pipe_stats[I915_MAX_PIPES]) +{ + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) + drm_handle_vblank(&dev_priv->drm, pipe); + + if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) + i9xx_pipe_crc_irq_handler(dev_priv, pipe); + + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); + } +} + +static void i915_pipestat_irq_handler(struct drm_i915_private *dev_priv, + u32 iir, u32 pipe_stats[I915_MAX_PIPES]) +{ + bool blc_event = false; + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) + drm_handle_vblank(&dev_priv->drm, pipe); + + if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) + blc_event = true; + + if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) + i9xx_pipe_crc_irq_handler(dev_priv, pipe); + + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); + } + + if (blc_event || (iir & I915_ASLE_INTERRUPT)) + intel_opregion_asle_intr(dev_priv); +} + +static void i965_pipestat_irq_handler(struct drm_i915_private *dev_priv, + u32 iir, u32 pipe_stats[I915_MAX_PIPES]) +{ + bool blc_event = false; + enum pipe pipe; + + for_each_pipe(dev_priv, pipe) { + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) + drm_handle_vblank(&dev_priv->drm, pipe); + + if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) + blc_event = true; + + if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) + i9xx_pipe_crc_irq_handler(dev_priv, pipe); + + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); + } + + if (blc_event || (iir & I915_ASLE_INTERRUPT)) + intel_opregion_asle_intr(dev_priv); + + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev_priv); +} + static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv, u32 pipe_stats[I915_MAX_PIPES]) { @@ -1879,7 +2033,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) /* Call regardless, as some status bits might not be * signalled in iir */ - valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); + i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); if (iir & (I915_LPE_PIPE_A_INTERRUPT | I915_LPE_PIPE_B_INTERRUPT)) @@ -1963,7 +2117,7 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg) /* Call regardless, as some status bits might not be * signalled in iir */ - valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats); + i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); if (iir & (I915_LPE_PIPE_A_INTERRUPT | I915_LPE_PIPE_B_INTERRUPT | @@ -2860,7 +3014,7 @@ static void ibx_irq_reset(struct drm_i915_private *dev_priv) if (HAS_PCH_NOP(dev_priv)) return; - GEN5_IRQ_RESET(SDE); + GEN3_IRQ_RESET(SDE); if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv)) I915_WRITE(SERR_INT, 0xffffffff); @@ -2888,15 +3042,13 @@ static void ibx_irq_pre_postinstall(struct drm_device *dev) static void gen5_gt_irq_reset(struct drm_i915_private *dev_priv) { - GEN5_IRQ_RESET(GT); + GEN3_IRQ_RESET(GT); if (INTEL_GEN(dev_priv) >= 6) - GEN5_IRQ_RESET(GEN6_PM); + GEN3_IRQ_RESET(GEN6_PM); } static void vlv_display_irq_reset(struct drm_i915_private *dev_priv) { - enum pipe pipe; - if (IS_CHERRYVIEW(dev_priv)) I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV); else @@ -2905,14 +3057,9 @@ static void vlv_display_irq_reset(struct drm_i915_private *dev_priv) i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - for_each_pipe(dev_priv, pipe) { - I915_WRITE(PIPESTAT(pipe), - PIPE_FIFO_UNDERRUN_STATUS | - PIPESTAT_INT_STATUS_MASK); - dev_priv->pipestat_irq_mask[pipe] = 0; - } + i9xx_pipestat_irq_reset(dev_priv); - GEN5_IRQ_RESET(VLV_); + GEN3_IRQ_RESET(VLV_); dev_priv->irq_mask = ~0; } @@ -2922,8 +3069,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) u32 enable_mask; enum pipe pipe; - pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV | - PIPE_CRC_DONE_INTERRUPT_STATUS; + pipestat_mask = PIPE_CRC_DONE_INTERRUPT_STATUS; i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); for_each_pipe(dev_priv, pipe) @@ -2943,7 +3089,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->irq_mask = ~enable_mask; - GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask); + GEN3_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask); } /* drm_dma.h hooks @@ -2952,9 +3098,10 @@ static void ironlake_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); - I915_WRITE(HWSTAM, 0xffffffff); + if (IS_GEN5(dev_priv)) + I915_WRITE(HWSTAM, 0xffffffff); - GEN5_IRQ_RESET(DE); + GEN3_IRQ_RESET(DE); if (IS_GEN7(dev_priv)) I915_WRITE(GEN7_ERR_INT, 0xffffffff); @@ -2963,7 +3110,7 @@ static void ironlake_irq_reset(struct drm_device *dev) ibx_irq_reset(dev_priv); } -static void valleyview_irq_preinstall(struct drm_device *dev) +static void valleyview_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -3001,9 +3148,9 @@ static void gen8_irq_reset(struct drm_device *dev) POWER_DOMAIN_PIPE(pipe))) GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); - GEN5_IRQ_RESET(GEN8_DE_PORT_); - GEN5_IRQ_RESET(GEN8_DE_MISC_); - GEN5_IRQ_RESET(GEN8_PCU_); + GEN3_IRQ_RESET(GEN8_DE_PORT_); + GEN3_IRQ_RESET(GEN8_DE_MISC_); + GEN3_IRQ_RESET(GEN8_PCU_); if (HAS_PCH_SPLIT(dev_priv)) ibx_irq_reset(dev_priv); @@ -3037,7 +3184,7 @@ void gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv, synchronize_irq(dev_priv->drm.irq); } -static void cherryview_irq_preinstall(struct drm_device *dev) +static void cherryview_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); @@ -3046,7 +3193,7 @@ static void cherryview_irq_preinstall(struct drm_device *dev) gen8_gt_irq_reset(dev_priv); - GEN5_IRQ_RESET(GEN8_PCU_); + GEN3_IRQ_RESET(GEN8_PCU_); spin_lock_irq(&dev_priv->irq_lock); if (dev_priv->display_irqs_enabled) @@ -3111,7 +3258,15 @@ static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv) static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv) { - u32 hotplug; + u32 val, hotplug; + + /* Display WA #1179 WaHardHangonHotPlug: cnp */ + if (HAS_PCH_CNP(dev_priv)) { + val = I915_READ(SOUTH_CHICKEN1); + val &= ~CHASSIS_CLK_REQ_DURATION_MASK; + val |= CHASSIS_CLK_REQ_DURATION(0xf); + I915_WRITE(SOUTH_CHICKEN1, val); + } /* Enable digital hotplug on the PCH */ hotplug = I915_READ(PCH_PORT_HOTPLUG); @@ -3238,10 +3393,12 @@ static void ibx_irq_postinstall(struct drm_device *dev) if (HAS_PCH_IBX(dev_priv)) mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON; - else + else if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv)) mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT; + else + mask = SDE_GMBUS_CPT; - gen5_assert_iir_is_zero(dev_priv, SDEIIR); + gen3_assert_iir_is_zero(dev_priv, SDEIIR); I915_WRITE(SDEIMR, ~mask); if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) || @@ -3272,7 +3429,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev) gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT; } - GEN5_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs); + GEN3_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs); if (INTEL_GEN(dev_priv) >= 6) { /* @@ -3285,7 +3442,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev) } dev_priv->pm_imr = 0xffffffff; - GEN5_IRQ_INIT(GEN6_PM, dev_priv->pm_imr, pm_irqs); + GEN3_IRQ_INIT(GEN6_PM, dev_priv->pm_imr, pm_irqs); } } @@ -3296,18 +3453,14 @@ static int ironlake_irq_postinstall(struct drm_device *dev) if (INTEL_GEN(dev_priv) >= 7) { display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | - DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB | - DE_PLANEB_FLIP_DONE_IVB | - DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB); + DE_PCH_EVENT_IVB | DE_AUX_CHANNEL_A_IVB); extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB | DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB | DE_DP_A_HOTPLUG_IVB); } else { display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | - DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | - DE_AUX_CHANNEL_A | - DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE | - DE_POISON); + DE_AUX_CHANNEL_A | DE_PIPEB_CRC_DONE | + DE_PIPEA_CRC_DONE | DE_POISON); extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT | DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN | DE_DP_A_HOTPLUG); @@ -3315,11 +3468,9 @@ static int ironlake_irq_postinstall(struct drm_device *dev) dev_priv->irq_mask = ~display_mask; - I915_WRITE(HWSTAM, 0xeffe); - ibx_irq_pre_postinstall(dev); - GEN5_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask); + GEN3_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask); gen5_gt_irq_postinstall(dev); @@ -3429,15 +3580,13 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) enum pipe pipe; if (INTEL_GEN(dev_priv) >= 9) { - de_pipe_masked |= GEN9_PIPE_PLANE1_FLIP_DONE | - GEN9_DE_PIPE_IRQ_FAULT_ERRORS; + de_pipe_masked |= GEN9_DE_PIPE_IRQ_FAULT_ERRORS; de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C | GEN9_AUX_CHANNEL_D; if (IS_GEN9_LP(dev_priv)) de_port_masked |= BXT_DE_PORT_GMBUS; } else { - de_pipe_masked |= GEN8_PIPE_PRIMARY_FLIP_DONE | - GEN8_DE_PIPE_IRQ_FAULT_ERRORS; + de_pipe_masked |= GEN8_DE_PIPE_IRQ_FAULT_ERRORS; } de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK | @@ -3460,8 +3609,8 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) dev_priv->de_irq_mask[pipe], de_pipe_enables); - GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables); - GEN5_IRQ_INIT(GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked); + GEN3_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables); + GEN3_IRQ_INIT(GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked); if (IS_GEN9_LP(dev_priv)) bxt_hpd_detection_setup(dev_priv); @@ -3505,98 +3654,36 @@ static int cherryview_irq_postinstall(struct drm_device *dev) return 0; } -static void gen8_irq_uninstall(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - - if (!dev_priv) - return; - - gen8_irq_reset(dev); -} - -static void valleyview_irq_uninstall(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - - if (!dev_priv) - return; - - I915_WRITE(VLV_MASTER_IER, 0); - POSTING_READ(VLV_MASTER_IER); - - gen5_gt_irq_reset(dev_priv); - - I915_WRITE(HWSTAM, 0xffffffff); - - spin_lock_irq(&dev_priv->irq_lock); - if (dev_priv->display_irqs_enabled) - vlv_display_irq_reset(dev_priv); - spin_unlock_irq(&dev_priv->irq_lock); -} - -static void cherryview_irq_uninstall(struct drm_device *dev) +static void i8xx_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); - if (!dev_priv) - return; - - I915_WRITE(GEN8_MASTER_IRQ, 0); - POSTING_READ(GEN8_MASTER_IRQ); - - gen8_gt_irq_reset(dev_priv); - - GEN5_IRQ_RESET(GEN8_PCU_); - - spin_lock_irq(&dev_priv->irq_lock); - if (dev_priv->display_irqs_enabled) - vlv_display_irq_reset(dev_priv); - spin_unlock_irq(&dev_priv->irq_lock); -} - -static void ironlake_irq_uninstall(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); + i9xx_pipestat_irq_reset(dev_priv); - if (!dev_priv) - return; - - ironlake_irq_reset(dev); -} - -static void i8xx_irq_preinstall(struct drm_device * dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int pipe; + I915_WRITE16(HWSTAM, 0xffff); - for_each_pipe(dev_priv, pipe) - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE16(IMR, 0xffff); - I915_WRITE16(IER, 0x0); - POSTING_READ16(IER); + GEN2_IRQ_RESET(); } static int i8xx_irq_postinstall(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); + u16 enable_mask; - I915_WRITE16(EMR, - ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + I915_WRITE16(EMR, ~(I915_ERROR_PAGE_TABLE | + I915_ERROR_MEMORY_REFRESH)); /* Unmask the interrupts that we always want on. */ dev_priv->irq_mask = ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | - I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT); - I915_WRITE16(IMR, dev_priv->irq_mask); + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); + + enable_mask = + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_USER_INTERRUPT; - I915_WRITE16(IER, - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | - I915_USER_INTERRUPT); - POSTING_READ16(IER); + GEN2_IRQ_INIT(, dev_priv->irq_mask, enable_mask); /* Interrupt setup is already guaranteed to be single-threaded, this is * just to make the assert_spin_locked check happy. */ @@ -3608,17 +3695,11 @@ static int i8xx_irq_postinstall(struct drm_device *dev) return 0; } -/* - * Returns true when a page flip has completed. - */ static irqreturn_t i8xx_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = to_i915(dev); - u16 iir, new_iir; - u32 pipe_stats[2]; - int pipe; - irqreturn_t ret; + irqreturn_t ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -3626,96 +3707,50 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) /* IRQs are synced during runtime_suspend, we don't require a wakeref */ disable_rpm_wakeref_asserts(dev_priv); - ret = IRQ_NONE; - iir = I915_READ16(IIR); - if (iir == 0) - goto out; + do { + u32 pipe_stats[I915_MAX_PIPES] = {}; + u16 iir; - while (iir) { - /* Can't rely on pipestat interrupt bit in iir as it might - * have been cleared after the pipestat interrupt was received. - * It doesn't set the bit in iir again, but it still produces - * interrupts (for non-MSI). - */ - spin_lock(&dev_priv->irq_lock); - if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - DRM_DEBUG("Command parser error, iir 0x%08x\n", iir); + iir = I915_READ16(IIR); + if (iir == 0) + break; - for_each_pipe(dev_priv, pipe) { - i915_reg_t reg = PIPESTAT(pipe); - pipe_stats[pipe] = I915_READ(reg); + ret = IRQ_HANDLED; - /* - * Clear the PIPE*STAT regs before the IIR - */ - if (pipe_stats[pipe] & 0x8000ffff) - I915_WRITE(reg, pipe_stats[pipe]); - } - spin_unlock(&dev_priv->irq_lock); + /* Call regardless, as some status bits might not be + * signalled in iir */ + i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); I915_WRITE16(IIR, iir); - new_iir = I915_READ16(IIR); /* Flush posted writes */ if (iir & I915_USER_INTERRUPT) notify_ring(dev_priv->engine[RCS]); - for_each_pipe(dev_priv, pipe) { - int plane = pipe; - if (HAS_FBC(dev_priv)) - plane = !plane; - - if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) - drm_handle_vblank(&dev_priv->drm, pipe); - - if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) - i9xx_pipe_crc_irq_handler(dev_priv, pipe); - - if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) - intel_cpu_fifo_underrun_irq_handler(dev_priv, - pipe); - } + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + DRM_DEBUG("Command parser error, iir 0x%08x\n", iir); - iir = new_iir; - } - ret = IRQ_HANDLED; + i8xx_pipestat_irq_handler(dev_priv, iir, pipe_stats); + } while (0); -out: enable_rpm_wakeref_asserts(dev_priv); return ret; } -static void i8xx_irq_uninstall(struct drm_device * dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int pipe; - - for_each_pipe(dev_priv, pipe) { - /* Clear enable bits; then clear status bits */ - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); - } - I915_WRITE16(IMR, 0xffff); - I915_WRITE16(IER, 0x0); - I915_WRITE16(IIR, I915_READ16(IIR)); -} - -static void i915_irq_preinstall(struct drm_device * dev) +static void i915_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); - int pipe; if (I915_HAS_HOTPLUG(dev_priv)) { i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); } - I915_WRITE16(HWSTAM, 0xeffe); - for_each_pipe(dev_priv, pipe) - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); - POSTING_READ(IER); + i9xx_pipestat_irq_reset(dev_priv); + + I915_WRITE(HWSTAM, 0xffffffff); + + GEN3_IRQ_RESET(); } static int i915_irq_postinstall(struct drm_device *dev) @@ -3723,15 +3758,14 @@ static int i915_irq_postinstall(struct drm_device *dev) struct drm_i915_private *dev_priv = to_i915(dev); u32 enable_mask; - I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | + I915_ERROR_MEMORY_REFRESH)); /* Unmask the interrupts that we always want on. */ dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | - I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT); + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); enable_mask = I915_ASLE_INTERRUPT | @@ -3740,20 +3774,13 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_USER_INTERRUPT; if (I915_HAS_HOTPLUG(dev_priv)) { - i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); - POSTING_READ(PORT_HOTPLUG_EN); - /* Enable in IER... */ enable_mask |= I915_DISPLAY_PORT_INTERRUPT; /* and unmask in IMR */ dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; } - I915_WRITE(IMR, dev_priv->irq_mask); - I915_WRITE(IER, enable_mask); - POSTING_READ(IER); - - i915_enable_asle_pipestat(dev_priv); + GEN3_IRQ_INIT(, dev_priv->irq_mask, enable_mask); /* Interrupt setup is already guaranteed to be single-threaded, this is * just to make the assert_spin_locked check happy. */ @@ -3762,6 +3789,8 @@ static int i915_irq_postinstall(struct drm_device *dev) i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS); spin_unlock_irq(&dev_priv->irq_lock); + i915_enable_asle_pipestat(dev_priv); + return 0; } @@ -3769,8 +3798,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = to_i915(dev); - u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; - int pipe, ret = IRQ_NONE; + irqreturn_t ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -3778,131 +3806,56 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) /* IRQs are synced during runtime_suspend, we don't require a wakeref */ disable_rpm_wakeref_asserts(dev_priv); - iir = I915_READ(IIR); do { - bool irq_received = (iir) != 0; - bool blc_event = false; - - /* Can't rely on pipestat interrupt bit in iir as it might - * have been cleared after the pipestat interrupt was received. - * It doesn't set the bit in iir again, but it still produces - * interrupts (for non-MSI). - */ - spin_lock(&dev_priv->irq_lock); - if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - DRM_DEBUG("Command parser error, iir 0x%08x\n", iir); - - for_each_pipe(dev_priv, pipe) { - i915_reg_t reg = PIPESTAT(pipe); - pipe_stats[pipe] = I915_READ(reg); - - /* Clear the PIPE*STAT regs before the IIR */ - if (pipe_stats[pipe] & 0x8000ffff) { - I915_WRITE(reg, pipe_stats[pipe]); - irq_received = true; - } - } - spin_unlock(&dev_priv->irq_lock); + u32 pipe_stats[I915_MAX_PIPES] = {}; + u32 hotplug_status = 0; + u32 iir; - if (!irq_received) + iir = I915_READ(IIR); + if (iir == 0) break; - /* Consume port. Then clear IIR or we'll miss events */ + ret = IRQ_HANDLED; + if (I915_HAS_HOTPLUG(dev_priv) && - iir & I915_DISPLAY_PORT_INTERRUPT) { - u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv); - if (hotplug_status) - i9xx_hpd_irq_handler(dev_priv, hotplug_status); - } + iir & I915_DISPLAY_PORT_INTERRUPT) + hotplug_status = i9xx_hpd_irq_ack(dev_priv); + + /* Call regardless, as some status bits might not be + * signalled in iir */ + i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); I915_WRITE(IIR, iir); - new_iir = I915_READ(IIR); /* Flush posted writes */ if (iir & I915_USER_INTERRUPT) notify_ring(dev_priv->engine[RCS]); - for_each_pipe(dev_priv, pipe) { - int plane = pipe; - if (HAS_FBC(dev_priv)) - plane = !plane; - - if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) - drm_handle_vblank(&dev_priv->drm, pipe); - - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - - if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) - i9xx_pipe_crc_irq_handler(dev_priv, pipe); - - if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) - intel_cpu_fifo_underrun_irq_handler(dev_priv, - pipe); - } + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + DRM_DEBUG("Command parser error, iir 0x%08x\n", iir); - if (blc_event || (iir & I915_ASLE_INTERRUPT)) - intel_opregion_asle_intr(dev_priv); + if (hotplug_status) + i9xx_hpd_irq_handler(dev_priv, hotplug_status); - /* With MSI, interrupts are only generated when iir - * transitions from zero to nonzero. If another bit got - * set while we were handling the existing iir bits, then - * we would never get another interrupt. - * - * This is fine on non-MSI as well, as if we hit this path - * we avoid exiting the interrupt handler only to generate - * another one. - * - * Note that for MSI this could cause a stray interrupt report - * if an interrupt landed in the time between writing IIR and - * the posting read. This should be rare enough to never - * trigger the 99% of 100,000 interrupts test for disabling - * stray interrupts. - */ - ret = IRQ_HANDLED; - iir = new_iir; - } while (iir); + i915_pipestat_irq_handler(dev_priv, iir, pipe_stats); + } while (0); enable_rpm_wakeref_asserts(dev_priv); return ret; } -static void i915_irq_uninstall(struct drm_device * dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int pipe; - - if (I915_HAS_HOTPLUG(dev_priv)) { - i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); - I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - } - - I915_WRITE16(HWSTAM, 0xffff); - for_each_pipe(dev_priv, pipe) { - /* Clear enable bits; then clear status bits */ - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); - } - I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); - - I915_WRITE(IIR, I915_READ(IIR)); -} - -static void i965_irq_preinstall(struct drm_device * dev) +static void i965_irq_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = to_i915(dev); - int pipe; i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - I915_WRITE(HWSTAM, 0xeffe); - for_each_pipe(dev_priv, pipe) - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); - POSTING_READ(IER); + i9xx_pipestat_irq_reset(dev_priv); + + I915_WRITE(HWSTAM, 0xffffffff); + + GEN3_IRQ_RESET(); } static int i965_irq_postinstall(struct drm_device *dev) @@ -3911,31 +3864,6 @@ static int i965_irq_postinstall(struct drm_device *dev) u32 enable_mask; u32 error_mask; - /* Unmask the interrupts that we always want on. */ - dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT | - I915_DISPLAY_PORT_INTERRUPT | - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | - I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | - I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); - - enable_mask = ~dev_priv->irq_mask; - enable_mask &= ~(I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT); - enable_mask |= I915_USER_INTERRUPT; - - if (IS_G4X(dev_priv)) - enable_mask |= I915_BSD_USER_INTERRUPT; - - /* Interrupt setup is already guaranteed to be single-threaded, this is - * just to make the assert_spin_locked check happy. */ - spin_lock_irq(&dev_priv->irq_lock); - i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); - i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS); - i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS); - spin_unlock_irq(&dev_priv->irq_lock); - /* * Enable some error detection, note the instruction error mask * bit is reserved, so we leave it masked. @@ -3951,12 +3879,34 @@ static int i965_irq_postinstall(struct drm_device *dev) } I915_WRITE(EMR, error_mask); - I915_WRITE(IMR, dev_priv->irq_mask); - I915_WRITE(IER, enable_mask); - POSTING_READ(IER); + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = + ~(I915_ASLE_INTERRUPT | + I915_DISPLAY_PORT_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); - i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); - POSTING_READ(PORT_HOTPLUG_EN); + enable_mask = + I915_ASLE_INTERRUPT | + I915_DISPLAY_PORT_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | + I915_USER_INTERRUPT; + + if (IS_G4X(dev_priv)) + enable_mask |= I915_BSD_USER_INTERRUPT; + + GEN3_IRQ_INIT(, dev_priv->irq_mask, enable_mask); + + /* Interrupt setup is already guaranteed to be single-threaded, this is + * just to make the assert_spin_locked check happy. */ + spin_lock_irq(&dev_priv->irq_lock); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS); + i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS); + spin_unlock_irq(&dev_priv->irq_lock); i915_enable_asle_pipestat(dev_priv); @@ -3992,9 +3942,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) { struct drm_device *dev = arg; struct drm_i915_private *dev_priv = to_i915(dev); - u32 iir, new_iir; - u32 pipe_stats[I915_MAX_PIPES]; - int ret = IRQ_NONE, pipe; + irqreturn_t ret = IRQ_NONE; if (!intel_irqs_enabled(dev_priv)) return IRQ_NONE; @@ -4002,121 +3950,46 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) /* IRQs are synced during runtime_suspend, we don't require a wakeref */ disable_rpm_wakeref_asserts(dev_priv); - iir = I915_READ(IIR); - - for (;;) { - bool irq_received = (iir) != 0; - bool blc_event = false; - - /* Can't rely on pipestat interrupt bit in iir as it might - * have been cleared after the pipestat interrupt was received. - * It doesn't set the bit in iir again, but it still produces - * interrupts (for non-MSI). - */ - spin_lock(&dev_priv->irq_lock); - if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - DRM_DEBUG("Command parser error, iir 0x%08x\n", iir); - - for_each_pipe(dev_priv, pipe) { - i915_reg_t reg = PIPESTAT(pipe); - pipe_stats[pipe] = I915_READ(reg); - - /* - * Clear the PIPE*STAT regs before the IIR - */ - if (pipe_stats[pipe] & 0x8000ffff) { - I915_WRITE(reg, pipe_stats[pipe]); - irq_received = true; - } - } - spin_unlock(&dev_priv->irq_lock); + do { + u32 pipe_stats[I915_MAX_PIPES] = {}; + u32 hotplug_status = 0; + u32 iir; - if (!irq_received) + iir = I915_READ(IIR); + if (iir == 0) break; ret = IRQ_HANDLED; - /* Consume port. Then clear IIR or we'll miss events */ - if (iir & I915_DISPLAY_PORT_INTERRUPT) { - u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv); - if (hotplug_status) - i9xx_hpd_irq_handler(dev_priv, hotplug_status); - } + if (iir & I915_DISPLAY_PORT_INTERRUPT) + hotplug_status = i9xx_hpd_irq_ack(dev_priv); + + /* Call regardless, as some status bits might not be + * signalled in iir */ + i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); I915_WRITE(IIR, iir); - new_iir = I915_READ(IIR); /* Flush posted writes */ if (iir & I915_USER_INTERRUPT) notify_ring(dev_priv->engine[RCS]); + if (iir & I915_BSD_USER_INTERRUPT) notify_ring(dev_priv->engine[VCS]); - for_each_pipe(dev_priv, pipe) { - if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) - drm_handle_vblank(&dev_priv->drm, pipe); - - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - - if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) - i9xx_pipe_crc_irq_handler(dev_priv, pipe); - - if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) - intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); - } - - if (blc_event || (iir & I915_ASLE_INTERRUPT)) - intel_opregion_asle_intr(dev_priv); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + DRM_DEBUG("Command parser error, iir 0x%08x\n", iir); - if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) - gmbus_irq_handler(dev_priv); + if (hotplug_status) + i9xx_hpd_irq_handler(dev_priv, hotplug_status); - /* With MSI, interrupts are only generated when iir - * transitions from zero to nonzero. If another bit got - * set while we were handling the existing iir bits, then - * we would never get another interrupt. - * - * This is fine on non-MSI as well, as if we hit this path - * we avoid exiting the interrupt handler only to generate - * another one. - * - * Note that for MSI this could cause a stray interrupt report - * if an interrupt landed in the time between writing IIR and - * the posting read. This should be rare enough to never - * trigger the 99% of 100,000 interrupts test for disabling - * stray interrupts. - */ - iir = new_iir; - } + i965_pipestat_irq_handler(dev_priv, iir, pipe_stats); + } while (0); enable_rpm_wakeref_asserts(dev_priv); return ret; } -static void i965_irq_uninstall(struct drm_device * dev) -{ - struct drm_i915_private *dev_priv = to_i915(dev); - int pipe; - - if (!dev_priv) - return; - - i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); - I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - - I915_WRITE(HWSTAM, 0xffffffff); - for_each_pipe(dev_priv, pipe) - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); - - for_each_pipe(dev_priv, pipe) - I915_WRITE(PIPESTAT(pipe), - I915_READ(PIPESTAT(pipe)) & 0x8000ffff); - I915_WRITE(IIR, I915_READ(IIR)); -} - /** * intel_irq_init - initializes irq support * @dev_priv: i915 device instance @@ -4197,17 +4070,17 @@ void intel_irq_init(struct drm_i915_private *dev_priv) if (IS_CHERRYVIEW(dev_priv)) { dev->driver->irq_handler = cherryview_irq_handler; - dev->driver->irq_preinstall = cherryview_irq_preinstall; + dev->driver->irq_preinstall = cherryview_irq_reset; dev->driver->irq_postinstall = cherryview_irq_postinstall; - dev->driver->irq_uninstall = cherryview_irq_uninstall; + dev->driver->irq_uninstall = cherryview_irq_reset; dev->driver->enable_vblank = i965_enable_vblank; dev->driver->disable_vblank = i965_disable_vblank; dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else if (IS_VALLEYVIEW(dev_priv)) { dev->driver->irq_handler = valleyview_irq_handler; - dev->driver->irq_preinstall = valleyview_irq_preinstall; + dev->driver->irq_preinstall = valleyview_irq_reset; dev->driver->irq_postinstall = valleyview_irq_postinstall; - dev->driver->irq_uninstall = valleyview_irq_uninstall; + dev->driver->irq_uninstall = valleyview_irq_reset; dev->driver->enable_vblank = i965_enable_vblank; dev->driver->disable_vblank = i965_disable_vblank; dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; @@ -4215,7 +4088,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->driver->irq_handler = gen8_irq_handler; dev->driver->irq_preinstall = gen8_irq_reset; dev->driver->irq_postinstall = gen8_irq_postinstall; - dev->driver->irq_uninstall = gen8_irq_uninstall; + dev->driver->irq_uninstall = gen8_irq_reset; dev->driver->enable_vblank = gen8_enable_vblank; dev->driver->disable_vblank = gen8_disable_vblank; if (IS_GEN9_LP(dev_priv)) @@ -4229,29 +4102,29 @@ void intel_irq_init(struct drm_i915_private *dev_priv) dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_reset; dev->driver->irq_postinstall = ironlake_irq_postinstall; - dev->driver->irq_uninstall = ironlake_irq_uninstall; + dev->driver->irq_uninstall = ironlake_irq_reset; dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup; } else { if (IS_GEN2(dev_priv)) { - dev->driver->irq_preinstall = i8xx_irq_preinstall; + dev->driver->irq_preinstall = i8xx_irq_reset; dev->driver->irq_postinstall = i8xx_irq_postinstall; dev->driver->irq_handler = i8xx_irq_handler; - dev->driver->irq_uninstall = i8xx_irq_uninstall; + dev->driver->irq_uninstall = i8xx_irq_reset; dev->driver->enable_vblank = i8xx_enable_vblank; dev->driver->disable_vblank = i8xx_disable_vblank; } else if (IS_GEN3(dev_priv)) { - dev->driver->irq_preinstall = i915_irq_preinstall; + dev->driver->irq_preinstall = i915_irq_reset; dev->driver->irq_postinstall = i915_irq_postinstall; - dev->driver->irq_uninstall = i915_irq_uninstall; + dev->driver->irq_uninstall = i915_irq_reset; dev->driver->irq_handler = i915_irq_handler; dev->driver->enable_vblank = i8xx_enable_vblank; dev->driver->disable_vblank = i8xx_disable_vblank; } else { - dev->driver->irq_preinstall = i965_irq_preinstall; + dev->driver->irq_preinstall = i965_irq_reset; dev->driver->irq_postinstall = i965_irq_postinstall; - dev->driver->irq_uninstall = i965_irq_uninstall; + dev->driver->irq_uninstall = i965_irq_reset; dev->driver->irq_handler = i965_irq_handler; dev->driver->enable_vblank = i965_enable_vblank; dev->driver->disable_vblank = i965_disable_vblank; |