diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0887581..5689d09 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -256,10 +256,15 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir; + u32 de_iir, gt_iir, de_ier; u32 new_de_iir, new_gt_iir; struct drm_i915_master_private *master_priv; + /* disable master interrupt before clearing iir */ + de_ier = I915_READ(DEIER); + I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); + (void)I915_READ(DEIER); + de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); @@ -292,6 +297,9 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) gt_iir = new_gt_iir; } + I915_WRITE(DEIER, de_ier); + (void)I915_READ(DEIER); + return ret; } @@ -513,6 +521,63 @@ static void i915_handle_error(struct drm_device *dev, bool wedged) queue_work(dev_priv->wq, &dev_priv->error_work); } +static void i915_handle_hotplug_interrupt(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); + + DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", + hotplug_status); + + if (!(hotplug_status & dev_priv->hotplug_supported_mask)) + goto out_ack; + + /* See if this was a spurious interrupt, disable ports if necessary */ + if (hotplug_status & HDMIB_HOTPLUG_INT_STATUS && + !(hotplug_status & HDMIB_HOTPLUG_INT_LIVE_STATUS)) { + /* spurious event, disable port B */ + I915_WRITE(HDMIB, I915_READ(HDMIB) & ~PORT_ENABLE); + } + + if (hotplug_status & HDMIC_HOTPLUG_INT_STATUS && + !(hotplug_status & HDMIC_HOTPLUG_INT_LIVE_STATUS)) { + /* spurious event, disable port C */ + I915_WRITE(HDMIC, I915_READ(HDMIC) & ~PORT_ENABLE); + } + + if (hotplug_status & HDMID_HOTPLUG_INT_STATUS && + !(hotplug_status & HDMID_HOTPLUG_INT_LIVE_STATUS)) { + /* spurious event, disable port D */ + I915_WRITE(HDMID, I915_READ(HDMID) & ~PORT_ENABLE); + } + + queue_work(dev_priv->wq, &dev_priv->hotplug_work); + + /* EOS interrupts occurs */ + if (IS_IGD(dev) && (hotplug_status & CRT_EOS_INT_STATUS)) { + u32 temp; + + DRM_DEBUG_DRIVER("EOS interrupt occurs\n"); + /* status is already cleared */ + temp = I915_READ(ADPA); + temp &= ~ADPA_DAC_ENABLE; + I915_WRITE(ADPA, temp); + + temp = I915_READ(PORT_HOTPLUG_EN); + temp &= ~CRT_EOS_INT_EN; + I915_WRITE(PORT_HOTPLUG_EN, temp); + + temp = I915_READ(PORT_HOTPLUG_STAT); + if (temp & CRT_EOS_INT_STATUS) + I915_WRITE(PORT_HOTPLUG_STAT, + CRT_EOS_INT_STATUS); + } + +out_ack: + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); + I915_READ(PORT_HOTPLUG_STAT); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -582,39 +647,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) /* Consume port. Then clear IIR or we'll miss events */ if ((I915_HAS_HOTPLUG(dev)) && - (iir & I915_DISPLAY_PORT_INTERRUPT)) { - u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); - - DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", - hotplug_status); - if (hotplug_status & dev_priv->hotplug_supported_mask) - queue_work(dev_priv->wq, - &dev_priv->hotplug_work); - - I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); - I915_READ(PORT_HOTPLUG_STAT); - - /* EOS interrupts occurs */ - if (IS_IGD(dev) && - (hotplug_status & CRT_EOS_INT_STATUS)) { - u32 temp; - - DRM_DEBUG_DRIVER("EOS interrupt occurs\n"); - /* status is already cleared */ - temp = I915_READ(ADPA); - temp &= ~ADPA_DAC_ENABLE; - I915_WRITE(ADPA, temp); - - temp = I915_READ(PORT_HOTPLUG_EN); - temp &= ~CRT_EOS_INT_EN; - I915_WRITE(PORT_HOTPLUG_EN, temp); - - temp = I915_READ(PORT_HOTPLUG_STAT); - if (temp & CRT_EOS_INT_STATUS) - I915_WRITE(PORT_HOTPLUG_STAT, - CRT_EOS_INT_STATUS); - } - } + (iir & I915_DISPLAY_PORT_INTERRUPT)) + i915_handle_hotplug_interrupt(dev); I915_WRITE(IIR, iir); new_iir = I915_READ(IIR); /* Flush posted writes */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 381cd88..10541c9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -881,12 +881,18 @@ #define PORT_HOTPLUG_STAT 0x61114 -#define HDMIB_HOTPLUG_INT_STATUS (1 << 29) -#define DPB_HOTPLUG_INT_STATUS (1 << 29) -#define HDMIC_HOTPLUG_INT_STATUS (1 << 28) -#define DPC_HOTPLUG_INT_STATUS (1 << 28) -#define HDMID_HOTPLUG_INT_STATUS (1 << 27) -#define DPD_HOTPLUG_INT_STATUS (1 << 27) +#define HDMIB_HOTPLUG_INT_LIVE_STATUS (1 << 29) +#define DPB_HOTPLUG_INT_LIVE_STATUS (1 << 29) +#define HDMIC_HOTPLUG_INT_LIVE_STATUS (1 << 28) +#define DPC_HOTPLUG_INT_LIVE_STATUS (1 << 28) +#define HDMID_HOTPLUG_INT_LIVE_STATUS (1 << 27) +#define DPD_HOTPLUG_INT_LIVE_STATUS (1 << 27) +#define HDMIB_HOTPLUG_INT_STATUS (3 << 21) +#define DPB_HOTPLUG_INT_STATUS (3 << 21) +#define HDMIC_HOTPLUG_INT_STATUS (3 << 19) +#define DPC_HOTPLUG_INT_STATUS (3 << 19) +#define HDMID_HOTPLUG_INT_STATUS (3 << 17) +#define DPD_HOTPLUG_INT_STATUS (3 << 17) #define CRT_EOS_INT_STATUS (1 << 12) #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10)