diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 211 |
1 files changed, 148 insertions, 63 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b02c97c62ec1..7193cf8d8f74 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -79,6 +79,26 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */ [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS }; +static void intel_irq_iir_reset16(struct drm_i915_private *dev_priv, + uint32_t iir) +{ + I915_WRITE16(iir, 0xffff); + if (I915_READ16(iir)) { + I915_WRITE16(iir, 0xffff); + POSTING_READ16(iir); + } +} + +static void intel_irq_iir_reset(struct drm_i915_private *dev_priv, + uint32_t iir) +{ + I915_WRITE(iir, 0xffffffff); + if (I915_READ(iir)) { + I915_WRITE(iir, 0xffffffff); + POSTING_READ(iir); + } +} + static void intel_irq_reg_reset16(struct drm_i915_private *dev_priv, uint32_t ier, uint32_t imr, uint32_t iir) { @@ -89,12 +109,7 @@ static void intel_irq_reg_reset16(struct drm_i915_private *dev_priv, } else { POSTING_READ16(imr); } - - I915_WRITE16(iir, 0xffff); - if (I915_READ16(iir)) { - I915_WRITE16(iir, 0xffff); - POSTING_READ16(iir); - } + intel_irq_iir_reset16(dev_priv, iir); } static void intel_irq_reg_reset(struct drm_i915_private *dev_priv, uint32_t ier, @@ -107,21 +122,20 @@ static void intel_irq_reg_reset(struct drm_i915_private *dev_priv, uint32_t ier, } else { POSTING_READ(imr); } - - I915_WRITE(iir, 0xffffffff); - if (I915_READ(iir)) { - I915_WRITE(iir, 0xffffffff); - POSTING_READ(iir); - } + intel_irq_iir_reset(dev_priv, iir); } static void intel_irq_reg_init16(struct drm_i915_private *dev_priv, uint32_t ier, uint16_t ier_val, uint32_t imr, - uint16_t imr_val, uint32_t iir) + uint16_t imr_val, uint32_t iir, + bool check_state) { - WARN(I915_READ16(iir) != 0, "Register 0x%x is not zero\n", iir); - WARN(I915_READ16(imr) != 0xffff, "Register 0x%x is not 0xffff\n", imr); - WARN(I915_READ16(ier) != 0, "Register 0x%x is not zero\n", ier); + if (check_state) { + WARN(I915_READ16(iir) != 0, "Register 0x%x is not zero\n", iir); + WARN(I915_READ16(imr) != 0xffff, + "Register 0x%x is not 0xffff\n", imr); + WARN(I915_READ16(ier) != 0, "Register 0x%x is not zero\n", ier); + } I915_WRITE16(imr, imr_val); I915_WRITE16(ier, ier_val); POSTING_READ16(ier); @@ -129,12 +143,14 @@ static void intel_irq_reg_init16(struct drm_i915_private *dev_priv, static void intel_irq_reg_init(struct drm_i915_private *dev_priv, uint32_t ier, uint32_t ier_val, uint32_t imr, uint32_t imr_val, - uint32_t iir) + uint32_t iir, bool check_state) { - WARN(I915_READ(iir) != 0, "Register 0x%x is not zero\n", iir); - WARN(I915_READ(imr) != 0xffffffff, "Register 0x%x is not 0xffffffff\n", - imr); - WARN(I915_READ(ier) != 0, "Register 0x%x is not zero\n", ier); + if (check_state) { + WARN(I915_READ(iir) != 0, "Register 0x%x is not zero\n", iir); + WARN(I915_READ(imr) != 0xffffffff, + "Register 0x%x is not 0xffffffff\n", imr); + WARN(I915_READ(ier) != 0, "Register 0x%x is not zero\n", ier); + } I915_WRITE(imr, imr_val); I915_WRITE(ier, ier_val); POSTING_READ(ier); @@ -2041,32 +2057,59 @@ void i915_hangcheck_elapsed(unsigned long data) DRM_I915_HANGCHECK_JIFFIES)); } -static void ibx_irq_reset(struct drm_device *dev) +static void ibx_irq_reset(struct drm_device *dev, bool disable_pch_hpd, + bool disable_others) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; + uint32_t hotplug_mask, others_mask, sdeier_val, sdeimr_val; if (HAS_PCH_NOP(dev)) return; - intel_irq_reg_reset(dev_priv, SDEIER, SDEIMR, SDEIIR); - if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev)) - I915_WRITE(SERR_INT, 0xffffffff); + hotplug_mask = HAS_PCH_IBX(dev) ? + SDE_HOTPLUG_MASK : SDE_HOTPLUG_MASK_CPT; + others_mask = ~hotplug_mask; + sdeier_val = I915_READ(SDEIER); + sdeimr_val = I915_READ(SDEIMR); - for_each_pipe(pipe) { - /* No pixel path on LPT_LP. */ - if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) - break; + if (disable_pch_hpd) { + sdeier_val &= ~hotplug_mask; + sdeimr_val |= hotplug_mask; + } - intel_irq_reg_reset(dev_priv, 0, FDI_RX_IMR(pipe), - FDI_RX_IIR(pipe)); - /* Only 1 PCH transcoder. */ - if (HAS_PCH_LPT(dev)) - break; + if (disable_others) { + sdeier_val &= ~others_mask; + sdeimr_val |= others_mask; } - if (HAS_PCH_LPT(dev)) - intel_irq_reg_reset(dev_priv, 0, PCH_GTCIMR, PCH_GTCIIR); + DRM_DEBUG_KMS("=== sdeier_val:0x%x sdeimr_val:0x%x, pch:%d others:%d\n", + sdeier_val, sdeimr_val, disable_pch_hpd, disable_others); + //intel_irq_reg_reset(dev_priv, SDEIER, SDEIMR, SDEIIR); + I915_WRITE(SDEIMR, sdeimr_val); + I915_WRITE(SDEIER, sdeier_val); + POSTING_READ(SDEIER); + intel_irq_iir_reset(dev_priv, SDEIIR); + + if (disable_others) { + if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev)) + I915_WRITE(SERR_INT, 0xffffffff); + + for_each_pipe(pipe) { + /* No pixel path on LPT_LP. */ + if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) + break; + + intel_irq_reg_reset(dev_priv, 0, FDI_RX_IMR(pipe), + FDI_RX_IIR(pipe)); + /* Only 1 PCH transcoder. */ + if (HAS_PCH_LPT(dev)) + break; + } + + if (HAS_PCH_LPT(dev)) + intel_irq_reg_reset(dev_priv, 0, PCH_GTCIMR, PCH_GTCIIR); + } } static void gen5_gt_irq_reset(struct drm_device *dev) @@ -2082,24 +2125,54 @@ static void gen5_gt_irq_reset(struct drm_device *dev) /* drm_dma.h hooks */ -static void ironlake_irq_reset(struct drm_device *dev) +static void ironlake_irq_reset(struct drm_device *dev, bool disable_pch_hpd, + bool disable_others, bool disable_master) { struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t deier_val, deimr_val, pch_hpd_bit, others_bits; + + pch_hpd_bit = (INTEL_INFO(dev)->gen >= 7) ? + DE_PCH_EVENT_IVB : DE_PCH_EVENT; + others_bits = ~(pch_hpd_bit | DE_MASTER_IRQ_CONTROL); + + deier_val = I915_READ(DEIER); + deimr_val = I915_READ(DEIMR); + + if (disable_pch_hpd) { + deier_val &= ~pch_hpd_bit; + deimr_val |= pch_hpd_bit; + } + if (disable_others) + deier_val &= ~others_bits; + deimr_val |= others_bits; + if (disable_master) + deier_val &= ~DE_MASTER_IRQ_CONTROL; + + if (disable_others) + I915_WRITE(HWSTAM, 0xffffffff); + + //intel_irq_reg_reset(dev_priv, DEIER, DEIMR, DEIIR); + DRM_DEBUG_KMS("=== deier:0x%x deimr:0x%x hpd:%d others:%d master:%d\n", + deier_val, deimr_val, disable_pch_hpd, disable_others, + disable_master); + I915_WRITE(DEIMR, deimr_val); + I915_WRITE(DEIER, deier_val); + POSTING_READ(DEIER); + intel_irq_iir_reset(dev_priv, DEIIR); - I915_WRITE(HWSTAM, 0xffffffff); + if (disable_others) { + if (IS_GEN7(dev)) + I915_WRITE(GEN7_ERR_INT, 0xffffffff); - intel_irq_reg_reset(dev_priv, DEIER, DEIMR, DEIIR); - if (IS_GEN7(dev)) - I915_WRITE(GEN7_ERR_INT, 0xffffffff); + gen5_gt_irq_reset(dev); - gen5_gt_irq_reset(dev); - - if (IS_HASWELL(dev)) { - intel_irq_reg_reset(dev_priv, AUDIER, AUDIMR, AUDIIR); - intel_irq_reg_reset(dev_priv, 0, SRDIMR, SRDIIR); + if (IS_HASWELL(dev)) { + intel_irq_reg_reset(dev_priv, AUDIER, AUDIMR, AUDIIR); + intel_irq_reg_reset(dev_priv, 0, SRDIMR, SRDIIR); + } } - ibx_irq_reset(dev); + ibx_irq_reset(dev, disable_pch_hpd, disable_others); } static void ironlake_irq_preinstall(struct drm_device *dev) @@ -2108,7 +2181,7 @@ static void ironlake_irq_preinstall(struct drm_device *dev) atomic_set(&dev_priv->irq_received, 0); - ironlake_irq_reset(dev); + ironlake_irq_reset(dev, true, true, true); } static void valleyview_irq_preinstall(struct drm_device *dev) @@ -2194,7 +2267,8 @@ static void ibx_irq_postinstall(struct drm_device *dev) * is enabled - instead we unconditionally enable all PCH interrupt * sources here, but then only unmask them as needed with SDEIMR. */ - intel_irq_reg_init(dev_priv, SDEIER, 0xffffffff, SDEIMR, ~mask, SDEIIR); + intel_irq_reg_init(dev_priv, SDEIER, 0xffffffff, SDEIMR, ~mask, SDEIIR, + false); for_each_pipe(pipe) { /* No pixel path on LPT_LP. */ @@ -2240,7 +2314,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev) } intel_irq_reg_init(dev_priv, GTIER, gt_irqs, GTIMR, - dev_priv->gt_irq_mask, GTIIR); + dev_priv->gt_irq_mask, GTIIR, true); if (INTEL_INFO(dev)->gen >= 6) { pm_irqs |= GEN6_PM_RPS_EVENTS; @@ -2249,7 +2323,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev) pm_irqs |= PM_VEBOX_USER_INTERRUPT; intel_irq_reg_init(dev_priv, GEN6_PMIER, pm_irqs, GEN6_PMIMR, - 0xffffffff, GEN6_PMIIR); + 0xffffffff, GEN6_PMIIR, true); } } @@ -2282,7 +2356,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) dev_priv->irq_mask = ~display_mask; intel_irq_reg_init(dev_priv, DEIER, display_mask | extra_mask, DEIMR, - dev_priv->irq_mask, DEIIR); + dev_priv->irq_mask, DEIIR, false); gen5_gt_irq_postinstall(dev); @@ -2340,7 +2414,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev) I915_WRITE(PIPESTAT(0), 0xffff); I915_WRITE(PIPESTAT(1), 0xffff); intel_irq_reg_init(dev_priv, VLV_IER, enable_mask, VLV_IMR, - dev_priv->irq_mask, VLV_IIR); + dev_priv->irq_mask, VLV_IIR, true); /* Interrupt setup is already guaranteed to be single-threaded, this is * just to make the assert_spin_locked check happy. */ @@ -2381,16 +2455,16 @@ static void valleyview_irq_uninstall(struct drm_device *dev) intel_irq_reg_reset(dev_priv, VLV_IER, VLV_IMR, VLV_IIR); } -static void ironlake_irq_uninstall(struct drm_device *dev) +static void ironlake_irq_uninstall(struct drm_device *dev, bool disable_pch_hpd, + bool disable_others, bool disable_master) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - if (!dev_priv) - return; - - del_timer_sync(&dev_priv->hotplug_reenable_timer); + if (disable_pch_hpd) + del_timer_sync(&dev_priv->hotplug_reenable_timer); - ironlake_irq_reset(dev); + ironlake_irq_reset(dev, disable_pch_hpd, disable_others, + disable_master); } static void i8xx_irq_preinstall(struct drm_device * dev) @@ -2425,7 +2499,7 @@ static int i8xx_irq_postinstall(struct drm_device *dev) I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); intel_irq_reg_init16(dev_priv, IER, enable_mask, IMR, - dev_priv->irq_mask, IIR); + dev_priv->irq_mask, IIR, true); return 0; } @@ -2594,7 +2668,7 @@ static int i915_irq_postinstall(struct drm_device *dev) } intel_irq_reg_init(dev_priv, IER, enable_mask, IMR, dev_priv->irq_mask, - IIR); + IIR, true); i915_enable_asle_pipestat(dev); @@ -2821,7 +2895,7 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(EMR, error_mask); intel_irq_reg_init(dev_priv, IER, enable_mask, IMR, dev_priv->irq_mask, - IIR); + IIR, true); I915_WRITE(PORT_HOTPLUG_EN, 0); POSTING_READ(PORT_HOTPLUG_EN); @@ -3037,6 +3111,16 @@ static void i915_reenable_hotplug_timer_func(unsigned long data) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } +static void intel_irq_uninstall(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (!dev_priv) + return; + + dev_priv->display.irq_uninstall(dev, true, true, true); +} + void intel_irq_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3079,10 +3163,11 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; dev->driver->irq_postinstall = ironlake_irq_postinstall; - dev->driver->irq_uninstall = ironlake_irq_uninstall; + dev->driver->irq_uninstall = intel_irq_uninstall; dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup; + dev_priv->display.irq_uninstall = ironlake_irq_uninstall; } else { if (INTEL_INFO(dev)->gen == 2) { dev->driver->irq_preinstall = i8xx_irq_preinstall; |