diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 586 |
1 files changed, 503 insertions, 83 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a2274c71327..5d1dedc02f1 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -270,6 +270,21 @@ static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev, } } +static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + assert_spin_locked(&dev_priv->irq_lock); + + if (enable) + dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_FIFO_UNDERRUN; + else + dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_FIFO_UNDERRUN; + I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]); + POSTING_READ(GEN8_DE_PIPE_IMR(pipe)); +} + /** * ibx_display_interrupt_update - update SDEIMR * @dev_priv: driver private @@ -382,6 +397,8 @@ bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, ironlake_set_fifo_underrun_reporting(dev, pipe, enable); else if (IS_GEN7(dev)) ivybridge_set_fifo_underrun_reporting(dev, pipe, enable); + else if (IS_GEN8(dev)) + broadwell_set_fifo_underrun_reporting(dev, pipe, enable); done: spin_unlock_irqrestore(&dev_priv->irq_lock, flags); @@ -442,7 +459,7 @@ done: void -i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +i915_enable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask) { u32 reg = PIPESTAT(pipe); u32 pipestat = I915_READ(reg) & 0x7fff0000; @@ -459,7 +476,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) } void -i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +i915_disable_pipestat(drm_i915_private_t *dev_priv, enum pipe pipe, u32 mask) { u32 reg = PIPESTAT(pipe); u32 pipestat = I915_READ(reg) & 0x7fff0000; @@ -487,9 +504,10 @@ static void i915_enable_asle_pipestat(struct drm_device *dev) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_ENABLE); if (INTEL_INFO(dev)->gen >= 4) - i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_A, + PIPE_LEGACY_BLC_EVENT_ENABLE); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } @@ -1150,6 +1168,56 @@ static void snb_gt_irq_handler(struct drm_device *dev, ivybridge_parity_error_irq_handler(dev, gt_iir); } +static irqreturn_t gen8_gt_irq_handler(struct drm_device *dev, + struct drm_i915_private *dev_priv, + u32 master_ctl) +{ + u32 rcs, bcs, vcs; + uint32_t tmp = 0; + irqreturn_t ret = IRQ_NONE; + + if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) { + tmp = I915_READ(GEN8_GT_IIR(0)); + if (tmp) { + ret = IRQ_HANDLED; + rcs = tmp >> GEN8_RCS_IRQ_SHIFT; + bcs = tmp >> GEN8_BCS_IRQ_SHIFT; + if (rcs & GT_RENDER_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + if (bcs & GT_RENDER_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[BCS]); + I915_WRITE(GEN8_GT_IIR(0), tmp); + } else + DRM_ERROR("The master control interrupt lied (GT0)!\n"); + } + + if (master_ctl & GEN8_GT_VCS1_IRQ) { + tmp = I915_READ(GEN8_GT_IIR(1)); + if (tmp) { + ret = IRQ_HANDLED; + vcs = tmp >> GEN8_VCS1_IRQ_SHIFT; + if (vcs & GT_RENDER_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[VCS]); + I915_WRITE(GEN8_GT_IIR(1), tmp); + } else + DRM_ERROR("The master control interrupt lied (GT1)!\n"); + } + + if (master_ctl & GEN8_GT_VECS_IRQ) { + tmp = I915_READ(GEN8_GT_IIR(3)); + if (tmp) { + ret = IRQ_HANDLED; + vcs = tmp >> GEN8_VECS_IRQ_SHIFT; + if (vcs & GT_RENDER_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[VECS]); + I915_WRITE(GEN8_GT_IIR(3), tmp); + } else + DRM_ERROR("The master control interrupt lied (GT3)!\n"); + } + + return ret; +} + #define HPD_STORM_DETECT_PERIOD 1000 #define HPD_STORM_THRESHOLD 5 @@ -1222,25 +1290,29 @@ static void dp_aux_irq_handler(struct drm_device *dev) } #if defined(CONFIG_DEBUG_FS) -static void display_pipe_crc_update(struct drm_device *dev, enum pipe pipe, - uint32_t crc0, uint32_t crc1, - uint32_t crc2, uint32_t crc3, - uint32_t crc4) +static void display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe, + uint32_t crc0, uint32_t crc1, + uint32_t crc2, uint32_t crc3, + uint32_t crc4) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; struct intel_pipe_crc_entry *entry; int head, tail; + spin_lock(&pipe_crc->lock); + if (!pipe_crc->entries) { + spin_unlock(&pipe_crc->lock); DRM_ERROR("spurious interrupt\n"); return; } - head = atomic_read(&pipe_crc->head); - tail = atomic_read(&pipe_crc->tail); + head = pipe_crc->head; + tail = pipe_crc->tail; if (CIRC_SPACE(head, tail, INTEL_PIPE_CRC_ENTRIES_NR) < 1) { + spin_unlock(&pipe_crc->lock); DRM_ERROR("CRC buffer overflowing\n"); return; } @@ -1255,48 +1327,63 @@ static void display_pipe_crc_update(struct drm_device *dev, enum pipe pipe, entry->crc[4] = crc4; head = (head + 1) & (INTEL_PIPE_CRC_ENTRIES_NR - 1); - atomic_set(&pipe_crc->head, head); + pipe_crc->head = head; + + spin_unlock(&pipe_crc->lock); wake_up_interruptible(&pipe_crc->wq); } +#else +static inline void +display_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe, + uint32_t crc0, uint32_t crc1, + uint32_t crc2, uint32_t crc3, + uint32_t crc4) {} +#endif + -static void hsw_pipe_crc_update(struct drm_device *dev, enum pipe pipe) +static void hsw_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; - display_pipe_crc_update(dev, pipe, - I915_READ(PIPE_CRC_RES_1_IVB(pipe)), - 0, 0, 0, 0); + display_pipe_crc_irq_handler(dev, pipe, + I915_READ(PIPE_CRC_RES_1_IVB(pipe)), + 0, 0, 0, 0); } -static void ivb_pipe_crc_update(struct drm_device *dev, enum pipe pipe) +static void ivb_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; - display_pipe_crc_update(dev, pipe, - I915_READ(PIPE_CRC_RES_1_IVB(pipe)), - I915_READ(PIPE_CRC_RES_2_IVB(pipe)), - I915_READ(PIPE_CRC_RES_3_IVB(pipe)), - I915_READ(PIPE_CRC_RES_4_IVB(pipe)), - I915_READ(PIPE_CRC_RES_5_IVB(pipe))); + display_pipe_crc_irq_handler(dev, pipe, + I915_READ(PIPE_CRC_RES_1_IVB(pipe)), + I915_READ(PIPE_CRC_RES_2_IVB(pipe)), + I915_READ(PIPE_CRC_RES_3_IVB(pipe)), + I915_READ(PIPE_CRC_RES_4_IVB(pipe)), + I915_READ(PIPE_CRC_RES_5_IVB(pipe))); } -static void ilk_pipe_crc_update(struct drm_device *dev, enum pipe pipe) +static void i9xx_pipe_crc_irq_handler(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t res1, res2; + + if (INTEL_INFO(dev)->gen >= 3) + res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe)); + else + res1 = 0; - display_pipe_crc_update(dev, pipe, - I915_READ(PIPE_CRC_RES_RED_ILK(pipe)), - I915_READ(PIPE_CRC_RES_GREEN_ILK(pipe)), - I915_READ(PIPE_CRC_RES_BLUE_ILK(pipe)), - I915_READ(PIPE_CRC_RES_RES1_ILK(pipe)), - I915_READ(PIPE_CRC_RES_RES2_ILK(pipe))); + if (INTEL_INFO(dev)->gen >= 5 || IS_G4X(dev)) + res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe)); + else + res2 = 0; + + display_pipe_crc_irq_handler(dev, pipe, + I915_READ(PIPE_CRC_RES_RED(pipe)), + I915_READ(PIPE_CRC_RES_GREEN(pipe)), + I915_READ(PIPE_CRC_RES_BLUE(pipe)), + res1, res2); } -#else -static inline void hsw_pipe_crc_update(struct drm_device *dev, int pipe) {} -static inline void ivb_pipe_crc_update(struct drm_device *dev, int pipe) {} -static inline void ilk_pipe_crc_update(struct drm_device *dev, int pipe) {} -#endif /* The RPS events need forcewake, so we add them to a work queue and mask their * IMR bits until the work is done. Other interrupts can be processed without @@ -1365,13 +1452,16 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); for_each_pipe(pipe) { - if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) drm_handle_vblank(dev, pipe); if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) { intel_prepare_page_flip(dev, pipe); intel_finish_page_flip(dev, pipe); } + + if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) + i9xx_pipe_crc_irq_handler(dev, pipe); } /* Consume port. Then clear IIR or we'll miss events */ @@ -1475,9 +1565,9 @@ static void ivb_err_int_handler(struct drm_device *dev) if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) { if (IS_IVYBRIDGE(dev)) - ivb_pipe_crc_update(dev, pipe); + ivb_pipe_crc_irq_handler(dev, pipe); else - hsw_pipe_crc_update(dev, pipe); + hsw_pipe_crc_irq_handler(dev, pipe); } } @@ -1550,6 +1640,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) { struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe; if (de_iir & DE_AUX_CHANNEL_A) dp_aux_irq_handler(dev); @@ -1557,37 +1648,26 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) if (de_iir & DE_GSE) intel_opregion_asle_intr(dev); - if (de_iir & DE_PIPEA_VBLANK) - drm_handle_vblank(dev, 0); - - if (de_iir & DE_PIPEB_VBLANK) - drm_handle_vblank(dev, 1); - if (de_iir & DE_POISON) DRM_ERROR("Poison interrupt\n"); - if (de_iir & DE_PIPEA_FIFO_UNDERRUN) - if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) - DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); - - if (de_iir & DE_PIPEB_FIFO_UNDERRUN) - if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false)) - DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n"); - - if (de_iir & DE_PIPEA_CRC_DONE) - ilk_pipe_crc_update(dev, PIPE_A); + for_each_pipe(pipe) { + if (de_iir & DE_PIPE_VBLANK(pipe)) + drm_handle_vblank(dev, pipe); - if (de_iir & DE_PIPEB_CRC_DONE) - ilk_pipe_crc_update(dev, PIPE_B); + if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe)) + if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, false)) + DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n", + pipe_name(pipe)); - if (de_iir & DE_PLANEA_FLIP_DONE) { - intel_prepare_page_flip(dev, 0); - intel_finish_page_flip_plane(dev, 0); - } + if (de_iir & DE_PIPE_CRC_DONE(pipe)) + i9xx_pipe_crc_irq_handler(dev, pipe); - if (de_iir & DE_PLANEB_FLIP_DONE) { - intel_prepare_page_flip(dev, 1); - intel_finish_page_flip_plane(dev, 1); + /* plane/pipes map 1:1 on ilk+ */ + if (de_iir & DE_PLANE_FLIP_DONE(pipe)) { + intel_prepare_page_flip(dev, pipe); + intel_finish_page_flip_plane(dev, pipe); + } } /* check event from PCH */ @@ -1610,7 +1690,7 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir) static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) { struct drm_i915_private *dev_priv = dev->dev_private; - int i; + enum pipe i; if (de_iir & DE_ERR_INT_IVB) ivb_err_int_handler(dev); @@ -1621,10 +1701,12 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir) if (de_iir & DE_GSE_IVB) intel_opregion_asle_intr(dev); - for (i = 0; i < 3; i++) { - if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) + for_each_pipe(i) { + if (de_iir & (DE_PIPE_VBLANK_IVB(i))) drm_handle_vblank(dev, i); - if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) { + + /* plane/pipes map 1:1 on ilk+ */ + if (de_iir & DE_PLANE_FLIP_DONE_IVB(i)) { intel_prepare_page_flip(dev, i); intel_finish_page_flip_plane(dev, i); } @@ -1709,6 +1791,117 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) return ret; } +static irqreturn_t gen8_irq_handler(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 master_ctl; + irqreturn_t ret = IRQ_NONE; + uint32_t tmp = 0; + enum pipe pipe; + + atomic_inc(&dev_priv->irq_received); + + master_ctl = I915_READ(GEN8_MASTER_IRQ); + master_ctl &= ~GEN8_MASTER_IRQ_CONTROL; + if (!master_ctl) + return IRQ_NONE; + + I915_WRITE(GEN8_MASTER_IRQ, 0); + POSTING_READ(GEN8_MASTER_IRQ); + + ret = gen8_gt_irq_handler(dev, dev_priv, master_ctl); + + if (master_ctl & GEN8_DE_MISC_IRQ) { + tmp = I915_READ(GEN8_DE_MISC_IIR); + if (tmp & GEN8_DE_MISC_GSE) + intel_opregion_asle_intr(dev); + else if (tmp) + DRM_ERROR("Unexpected DE Misc interrupt\n"); + else + DRM_ERROR("The master control interrupt lied (DE MISC)!\n"); + + if (tmp) { + I915_WRITE(GEN8_DE_MISC_IIR, tmp); + ret = IRQ_HANDLED; + } + } + + if (master_ctl & GEN8_DE_PORT_IRQ) { + tmp = I915_READ(GEN8_DE_PORT_IIR); + if (tmp & GEN8_AUX_CHANNEL_A) + dp_aux_irq_handler(dev); + else if (tmp) + DRM_ERROR("Unexpected DE Port interrupt\n"); + else + DRM_ERROR("The master control interrupt lied (DE PORT)!\n"); + + if (tmp) { + I915_WRITE(GEN8_DE_PORT_IIR, tmp); + ret = IRQ_HANDLED; + } + } + + for_each_pipe(pipe) { + uint32_t pipe_iir; + + if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe))) + continue; + + pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe)); + if (pipe_iir & GEN8_PIPE_VBLANK) + drm_handle_vblank(dev, pipe); + + if (pipe_iir & GEN8_PIPE_FLIP_DONE) { + intel_prepare_page_flip(dev, pipe); + intel_finish_page_flip_plane(dev, pipe); + } + + if (pipe_iir & GEN8_PIPE_CDCLK_CRC_DONE) + hsw_pipe_crc_irq_handler(dev, pipe); + + if (pipe_iir & GEN8_PIPE_FIFO_UNDERRUN) { + if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, + false)) + DRM_DEBUG_DRIVER("Pipe %c FIFO underrun\n", + pipe_name(pipe)); + } + + if (pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS) { + DRM_ERROR("Fault errors on pipe %c\n: 0x%08x", + pipe_name(pipe), + pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS); + } + + if (pipe_iir) { + ret = IRQ_HANDLED; + I915_WRITE(GEN8_DE_PIPE_IIR(pipe), pipe_iir); + } else + DRM_ERROR("The master control interrupt lied (DE PIPE)!\n"); + } + + if (!HAS_PCH_NOP(dev) && master_ctl & GEN8_DE_PCH_IRQ) { + /* + * FIXME(BDW): Assume for now that the new interrupt handling + * scheme also closed the SDE interrupt handling race we've seen + * on older pch-split platforms. But this needs testing. + */ + u32 pch_iir = I915_READ(SDEIIR); + + cpt_irq_handler(dev, pch_iir); + + if (pch_iir) { + I915_WRITE(SDEIIR, pch_iir); + ret = IRQ_HANDLED; + } + } + + I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL); + POSTING_READ(GEN8_MASTER_IRQ); + + return ret; +} + static void i915_error_wake_up(struct drm_i915_private *dev_priv, bool reset_completed) { @@ -2027,7 +2220,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long irqflags; uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) : - DE_PIPE_VBLANK_ILK(pipe); + DE_PIPE_VBLANK(pipe); if (!i915_pipe_enabled(dev, pipe)) return -EINVAL; @@ -2050,7 +2243,7 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); imr = I915_READ(VLV_IMR); - if (pipe == 0) + if (pipe == PIPE_A) imr &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; else imr &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; @@ -2062,6 +2255,22 @@ static int valleyview_enable_vblank(struct drm_device *dev, int pipe) return 0; } +static int gen8_enable_vblank(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long irqflags; + + if (!i915_pipe_enabled(dev, pipe)) + return -EINVAL; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_VBLANK; + I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]); + POSTING_READ(GEN8_DE_PIPE_IMR(pipe)); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + return 0; +} + /* Called from drm generic code, passed 'crtc' which * we use as a pipe index */ @@ -2085,7 +2294,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long irqflags; uint32_t bit = (INTEL_INFO(dev)->gen >= 7) ? DE_PIPE_VBLANK_IVB(pipe) : - DE_PIPE_VBLANK_ILK(pipe); + DE_PIPE_VBLANK(pipe); spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ironlake_disable_display_irq(dev_priv, bit); @@ -2102,7 +2311,7 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe) i915_disable_pipestat(dev_priv, pipe, PIPE_START_VBLANK_INTERRUPT_ENABLE); imr = I915_READ(VLV_IMR); - if (pipe == 0) + if (pipe == PIPE_A) imr |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; else imr |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; @@ -2110,6 +2319,21 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } +static void gen8_disable_vblank(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long irqflags; + + if (!i915_pipe_enabled(dev, pipe)) + return; + + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_VBLANK; + I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]); + POSTING_READ(GEN8_DE_PIPE_IMR(pipe)); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); +} + static u32 ring_last_seqno(struct intel_ring_buffer *ring) { @@ -2268,8 +2492,12 @@ static void i915_hangcheck_elapsed(unsigned long data) if (waitqueue_active(&ring->irq_queue)) { /* Issue a wake-up to catch stuck h/w. */ if (!test_and_set_bit(ring->id, &dev_priv->gpu_error.missed_irq_rings)) { - DRM_ERROR("Hangcheck timer elapsed... %s idle\n", - ring->name); + if (!(dev_priv->gpu_error.test_irq_rings & intel_ring_flag(ring))) + DRM_ERROR("Hangcheck timer elapsed... %s idle\n", + ring->name); + else + DRM_INFO("Fake missed irq on %s\n", + ring->name); wake_up_all(&ring->irq_queue); } /* Safeguard against driver failure */ @@ -2440,6 +2668,53 @@ static void valleyview_irq_preinstall(struct drm_device *dev) POSTING_READ(VLV_IER); } +static void gen8_irq_preinstall(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe; + + atomic_set(&dev_priv->irq_received, 0); + + I915_WRITE(GEN8_MASTER_IRQ, 0); + POSTING_READ(GEN8_MASTER_IRQ); + + /* IIR can theoretically queue up two events. Be paranoid */ +#define GEN8_IRQ_INIT_NDX(type, which) do { \ + I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \ + POSTING_READ(GEN8_##type##_IMR(which)); \ + I915_WRITE(GEN8_##type##_IER(which), 0); \ + I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \ + POSTING_READ(GEN8_##type##_IIR(which)); \ + I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \ + } while (0) + +#define GEN8_IRQ_INIT(type) do { \ + I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \ + POSTING_READ(GEN8_##type##_IMR); \ + I915_WRITE(GEN8_##type##_IER, 0); \ + I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \ + POSTING_READ(GEN8_##type##_IIR); \ + I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \ + } while (0) + + GEN8_IRQ_INIT_NDX(GT, 0); + GEN8_IRQ_INIT_NDX(GT, 1); + GEN8_IRQ_INIT_NDX(GT, 2); + GEN8_IRQ_INIT_NDX(GT, 3); + + for_each_pipe(pipe) { + GEN8_IRQ_INIT_NDX(DE_PIPE, pipe); + } + + GEN8_IRQ_INIT(DE_PORT); + GEN8_IRQ_INIT(DE_MISC); + GEN8_IRQ_INIT(PCU); +#undef GEN8_IRQ_INIT +#undef GEN8_IRQ_INIT_NDX + + POSTING_READ(GEN8_PCU_IIR); +} + static void ibx_hpd_irq_setup(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -2593,7 +2868,8 @@ static int valleyview_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; + u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV | + PIPE_CRC_DONE_ENABLE; unsigned long irqflags; enable_mask = I915_DISPLAY_PORT_INTERRUPT; @@ -2623,9 +2899,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev) /* Interrupt setup is already guaranteed to be single-threaded, this is * just to make the assert_spin_locked check happy. */ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - i915_enable_pipestat(dev_priv, 0, pipestat_enable); - i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); - i915_enable_pipestat(dev_priv, 1, pipestat_enable); + i915_enable_pipestat(dev_priv, PIPE_A, pipestat_enable); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_B, pipestat_enable); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2644,6 +2920,117 @@ static int valleyview_irq_postinstall(struct drm_device *dev) return 0; } +static void gen8_gt_irq_postinstall(struct drm_i915_private *dev_priv) +{ + int i; + + /* These are interrupts we'll toggle with the ring mask register */ + uint32_t gt_interrupts[] = { + GT_RENDER_USER_INTERRUPT << GEN8_RCS_IRQ_SHIFT | + GT_RENDER_L3_PARITY_ERROR_INTERRUPT | + GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT, + GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT | + GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT, + 0, + GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT + }; + + for (i = 0; i < ARRAY_SIZE(gt_interrupts); i++) { + u32 tmp = I915_READ(GEN8_GT_IIR(i)); + if (tmp) + DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n", + i, tmp); + I915_WRITE(GEN8_GT_IMR(i), ~gt_interrupts[i]); + I915_WRITE(GEN8_GT_IER(i), gt_interrupts[i]); + } + POSTING_READ(GEN8_GT_IER(0)); +} + +static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + uint32_t de_pipe_masked = GEN8_PIPE_FLIP_DONE | + GEN8_PIPE_CDCLK_CRC_DONE | + GEN8_PIPE_FIFO_UNDERRUN | + GEN8_DE_PIPE_IRQ_FAULT_ERRORS; + uint32_t de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK; + int pipe; + dev_priv->de_irq_mask[PIPE_A] = ~de_pipe_masked; + dev_priv->de_irq_mask[PIPE_B] = ~de_pipe_masked; + dev_priv->de_irq_mask[PIPE_C] = ~de_pipe_masked; + + for_each_pipe(pipe) { + u32 tmp = I915_READ(GEN8_DE_PIPE_IIR(pipe)); + if (tmp) + DRM_ERROR("Interrupt (%d) should have been masked in pre-install 0x%08x\n", + pipe, tmp); + I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]); + I915_WRITE(GEN8_DE_PIPE_IER(pipe), de_pipe_enables); + } + POSTING_READ(GEN8_DE_PIPE_ISR(0)); + + I915_WRITE(GEN8_DE_PORT_IMR, ~GEN8_AUX_CHANNEL_A); + I915_WRITE(GEN8_DE_PORT_IER, GEN8_AUX_CHANNEL_A); + POSTING_READ(GEN8_DE_PORT_IER); +} + +static int gen8_irq_postinstall(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + gen8_gt_irq_postinstall(dev_priv); + gen8_de_irq_postinstall(dev_priv); + + ibx_irq_postinstall(dev); + + I915_WRITE(GEN8_MASTER_IRQ, DE_MASTER_IRQ_CONTROL); + POSTING_READ(GEN8_MASTER_IRQ); + + return 0; +} + +static void gen8_irq_uninstall(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe; + + if (!dev_priv) + return; + + atomic_set(&dev_priv->irq_received, 0); + + I915_WRITE(GEN8_MASTER_IRQ, 0); + +#define GEN8_IRQ_FINI_NDX(type, which) do { \ + I915_WRITE(GEN8_##type##_IMR(which), 0xffffffff); \ + I915_WRITE(GEN8_##type##_IER(which), 0); \ + I915_WRITE(GEN8_##type##_IIR(which), 0xffffffff); \ + } while (0) + +#define GEN8_IRQ_FINI(type) do { \ + I915_WRITE(GEN8_##type##_IMR, 0xffffffff); \ + I915_WRITE(GEN8_##type##_IER, 0); \ + I915_WRITE(GEN8_##type##_IIR, 0xffffffff); \ + } while (0) + + GEN8_IRQ_FINI_NDX(GT, 0); + GEN8_IRQ_FINI_NDX(GT, 1); + GEN8_IRQ_FINI_NDX(GT, 2); + GEN8_IRQ_FINI_NDX(GT, 3); + + for_each_pipe(pipe) { + GEN8_IRQ_FINI_NDX(DE_PIPE, pipe); + } + + GEN8_IRQ_FINI(DE_PORT); + GEN8_IRQ_FINI(DE_MISC); + GEN8_IRQ_FINI(PCU); +#undef GEN8_IRQ_FINI +#undef GEN8_IRQ_FINI_NDX + + POSTING_READ(GEN8_PCU_IIR); +} + static void valleyview_irq_uninstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -2716,6 +3103,7 @@ static void i8xx_irq_preinstall(struct drm_device * dev) static int i8xx_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned long irqflags; I915_WRITE16(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); @@ -2736,6 +3124,13 @@ static int i8xx_irq_postinstall(struct drm_device *dev) I915_USER_INTERRUPT); POSTING_READ16(IER); + /* Interrupt setup is already guaranteed to be single-threaded, this is + * just to make the assert_spin_locked check happy. */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + return 0; } @@ -2822,13 +3217,14 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg) if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS && - i8xx_handle_vblank(dev, 0, iir)) - flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(0); + for_each_pipe(pipe) { + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && + i8xx_handle_vblank(dev, pipe, iir)) + flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(pipe); - if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS && - i8xx_handle_vblank(dev, 1, iir)) - flip_mask &= ~DISPLAY_PLANE_FLIP_PENDING(1); + if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) + i9xx_pipe_crc_irq_handler(dev, pipe); + } iir = new_iir; } @@ -2875,6 +3271,7 @@ static int i915_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; + unsigned long irqflags; I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); @@ -2910,6 +3307,13 @@ static int i915_irq_postinstall(struct drm_device *dev) i915_enable_asle_pipestat(dev); + /* Interrupt setup is already guaranteed to be single-threaded, this is + * just to make the assert_spin_locked check happy. */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + return 0; } @@ -3021,6 +3425,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) 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, pipe); } if (blc_event || (iir & I915_ASLE_INTERRUPT)) @@ -3119,7 +3526,9 @@ static int i965_irq_postinstall(struct drm_device *dev) /* Interrupt setup is already guaranteed to be single-threaded, this is * just to make the assert_spin_locked check happy. */ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_EVENT_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_ENABLE); + i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_ENABLE); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); /* @@ -3265,6 +3674,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) 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, pipe); } @@ -3398,6 +3810,14 @@ void intel_irq_init(struct drm_device *dev) dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; + } else if (IS_GEN8(dev)) { + dev->driver->irq_handler = gen8_irq_handler; + dev->driver->irq_preinstall = gen8_irq_preinstall; + dev->driver->irq_postinstall = gen8_irq_postinstall; + dev->driver->irq_uninstall = gen8_irq_uninstall; + dev->driver->enable_vblank = gen8_enable_vblank; + dev->driver->disable_vblank = gen8_disable_vblank; + dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; |