summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/i915_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c211
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;