Merge tag 'drm-intel-next-2015-09-11' of git://anongit.freedesktop.org/drm-intel...
[linux-drm-fsl-dcu.git] / drivers / gpu / drm / i915 / i915_irq.c
index 88d064e807830ab02b2367197d1fc6a674d8d262..77740cd0beab12e57f360da3b8e8d6360f3a55b6 100644 (file)
  * and related files, but that will be described in separate chapters.
  */
 
+static const u32 hpd_ilk[HPD_NUM_PINS] = {
+       [HPD_PORT_A] = DE_DP_A_HOTPLUG,
+};
+
+static const u32 hpd_ivb[HPD_NUM_PINS] = {
+       [HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB,
+};
+
+static const u32 hpd_bdw[HPD_NUM_PINS] = {
+       [HPD_PORT_A] = GEN8_PORT_DP_A_HOTPLUG,
+};
+
 static const u32 hpd_ibx[HPD_NUM_PINS] = {
        [HPD_CRT] = SDE_CRT_HOTPLUG,
        [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG,
@@ -62,6 +74,7 @@ static const u32 hpd_cpt[HPD_NUM_PINS] = {
 };
 
 static const u32 hpd_spt[HPD_NUM_PINS] = {
+       [HPD_PORT_A] = SDE_PORTA_HOTPLUG_SPT,
        [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT,
        [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT,
        [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT,
@@ -97,6 +110,7 @@ static const u32 hpd_status_i915[HPD_NUM_PINS] = {
 
 /* BXT hpd list */
 static const u32 hpd_bxt[HPD_NUM_PINS] = {
+       [HPD_PORT_A] = BXT_DE_PORT_HP_DDIA,
        [HPD_PORT_B] = BXT_DE_PORT_HP_DDIB,
        [HPD_PORT_C] = BXT_DE_PORT_HP_DDIC
 };
@@ -153,35 +167,46 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = {
 
 static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
 
-/* For display hotplug interrupt */
-void
-ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
+/**
+ * ilk_update_display_irq - update DEIMR
+ * @dev_priv: driver private
+ * @interrupt_mask: mask of interrupt bits to update
+ * @enabled_irq_mask: mask of interrupt bits to enable
+ */
+static void ilk_update_display_irq(struct drm_i915_private *dev_priv,
+                                  uint32_t interrupt_mask,
+                                  uint32_t enabled_irq_mask)
 {
+       uint32_t new_val;
+
        assert_spin_locked(&dev_priv->irq_lock);
 
+       WARN_ON(enabled_irq_mask & ~interrupt_mask);
+
        if (WARN_ON(!intel_irqs_enabled(dev_priv)))
                return;
 
-       if ((dev_priv->irq_mask & mask) != 0) {
-               dev_priv->irq_mask &= ~mask;
+       new_val = dev_priv->irq_mask;
+       new_val &= ~interrupt_mask;
+       new_val |= (~enabled_irq_mask & interrupt_mask);
+
+       if (new_val != dev_priv->irq_mask) {
+               dev_priv->irq_mask = new_val;
                I915_WRITE(DEIMR, dev_priv->irq_mask);
                POSTING_READ(DEIMR);
        }
 }
 
 void
-ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
+ironlake_enable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
 {
-       assert_spin_locked(&dev_priv->irq_lock);
-
-       if (WARN_ON(!intel_irqs_enabled(dev_priv)))
-               return;
+       ilk_update_display_irq(dev_priv, mask, mask);
+}
 
-       if ((dev_priv->irq_mask & mask) != mask) {
-               dev_priv->irq_mask |= mask;
-               I915_WRITE(DEIMR, dev_priv->irq_mask);
-               POSTING_READ(DEIMR);
-       }
+void
+ironlake_disable_display_irq(struct drm_i915_private *dev_priv, u32 mask)
+{
+       ilk_update_display_irq(dev_priv, mask, 0);
 }
 
 /**
@@ -350,6 +375,38 @@ void gen6_disable_rps_interrupts(struct drm_device *dev)
        synchronize_irq(dev->irq);
 }
 
+/**
+  * bdw_update_port_irq - update DE port interrupt
+  * @dev_priv: driver private
+  * @interrupt_mask: mask of interrupt bits to update
+  * @enabled_irq_mask: mask of interrupt bits to enable
+  */
+static void bdw_update_port_irq(struct drm_i915_private *dev_priv,
+                               uint32_t interrupt_mask,
+                               uint32_t enabled_irq_mask)
+{
+       uint32_t new_val;
+       uint32_t old_val;
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       WARN_ON(enabled_irq_mask & ~interrupt_mask);
+
+       if (WARN_ON(!intel_irqs_enabled(dev_priv)))
+               return;
+
+       old_val = I915_READ(GEN8_DE_PORT_IMR);
+
+       new_val = old_val;
+       new_val &= ~interrupt_mask;
+       new_val |= (~enabled_irq_mask & interrupt_mask);
+
+       if (new_val != old_val) {
+               I915_WRITE(GEN8_DE_PORT_IMR, new_val);
+               POSTING_READ(GEN8_DE_PORT_IMR);
+       }
+}
+
 /**
  * ibx_display_interrupt_update - update SDEIMR
  * @dev_priv: driver private
@@ -1263,7 +1320,31 @@ static bool bxt_port_hotplug_long_detect(enum port port, u32 val)
 {
        switch (port) {
        case PORT_A:
-               return val & BXT_PORTA_HOTPLUG_LONG_DETECT;
+               return val & PORTA_HOTPLUG_LONG_DETECT;
+       case PORT_B:
+               return val & PORTB_HOTPLUG_LONG_DETECT;
+       case PORT_C:
+               return val & PORTC_HOTPLUG_LONG_DETECT;
+       default:
+               return false;
+       }
+}
+
+static bool spt_port_hotplug2_long_detect(enum port port, u32 val)
+{
+       switch (port) {
+       case PORT_E:
+               return val & PORTE_HOTPLUG_LONG_DETECT;
+       default:
+               return false;
+       }
+}
+
+static bool spt_port_hotplug_long_detect(enum port port, u32 val)
+{
+       switch (port) {
+       case PORT_A:
+               return val & PORTA_HOTPLUG_LONG_DETECT;
        case PORT_B:
                return val & PORTB_HOTPLUG_LONG_DETECT;
        case PORT_C:
@@ -1275,6 +1356,16 @@ static bool bxt_port_hotplug_long_detect(enum port port, u32 val)
        }
 }
 
+static bool ilk_port_hotplug_long_detect(enum port port, u32 val)
+{
+       switch (port) {
+       case PORT_A:
+               return val & DIGITAL_PORTA_HOTPLUG_LONG_DETECT;
+       default:
+               return false;
+       }
+}
+
 static bool pch_port_hotplug_long_detect(enum port port, u32 val)
 {
        switch (port) {
@@ -1284,8 +1375,6 @@ static bool pch_port_hotplug_long_detect(enum port port, u32 val)
                return val & PORTC_HOTPLUG_LONG_DETECT;
        case PORT_D:
                return val & PORTD_HOTPLUG_LONG_DETECT;
-       case PORT_E:
-               return val & PORTE_HOTPLUG_LONG_DETECT;
        default:
                return false;
        }
@@ -1305,7 +1394,13 @@ static bool i9xx_port_hotplug_long_detect(enum port port, u32 val)
        }
 }
 
-/* Get a bit mask of pins that have triggered, and which ones may be long. */
+/*
+ * Get a bit mask of pins that have triggered, and which ones may be long.
+ * This can be called multiple times with the same masks to accumulate
+ * hotplug detection results from several registers.
+ *
+ * Note that the caller is expected to zero out the masks initially.
+ */
 static void intel_get_hpd_pins(u32 *pin_mask, u32 *long_mask,
                             u32 hotplug_trigger, u32 dig_hotplug_reg,
                             const u32 hpd[HPD_NUM_PINS],
@@ -1314,9 +1409,6 @@ static void intel_get_hpd_pins(u32 *pin_mask, u32 *long_mask,
        enum port port;
        int i;
 
-       *pin_mask = 0;
-       *long_mask = 0;
-
        for_each_hpd_pin(i) {
                if ((hpd[i] & hotplug_trigger) == 0)
                        continue;
@@ -1557,7 +1649,7 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
-       u32 pin_mask, long_mask;
+       u32 pin_mask = 0, long_mask = 0;
 
        if (!hotplug_status)
                return;
@@ -1572,20 +1664,26 @@ static void i9xx_hpd_irq_handler(struct drm_device *dev)
        if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) {
                u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
 
-               intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
-                                  hotplug_trigger, hpd_status_g4x,
-                                  i9xx_port_hotplug_long_detect);
-               intel_hpd_irq_handler(dev, pin_mask, long_mask);
+               if (hotplug_trigger) {
+                       intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
+                                          hotplug_trigger, hpd_status_g4x,
+                                          i9xx_port_hotplug_long_detect);
+
+                       intel_hpd_irq_handler(dev, pin_mask, long_mask);
+               }
 
                if (hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X)
                        dp_aux_irq_handler(dev);
        } else {
                u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915;
 
-               intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
-                                  hotplug_trigger, hpd_status_i915,
-                                  i9xx_port_hotplug_long_detect);
-               intel_hpd_irq_handler(dev, pin_mask, long_mask);
+               if (hotplug_trigger) {
+                       intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
+                                          hotplug_trigger, hpd_status_i915,
+                                          i9xx_port_hotplug_long_detect);
+
+                       intel_hpd_irq_handler(dev, pin_mask, long_mask);
+               }
        }
 }
 
@@ -1679,23 +1777,30 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
        return ret;
 }
 
+static void ibx_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger,
+                               const u32 hpd[HPD_NUM_PINS])
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
+
+       dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+       I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
+
+       intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
+                          dig_hotplug_reg, hpd,
+                          pch_port_hotplug_long_detect);
+
+       intel_hpd_irq_handler(dev, pin_mask, long_mask);
+}
+
 static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe;
        u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK;
 
-       if (hotplug_trigger) {
-               u32 dig_hotplug_reg, pin_mask, long_mask;
-
-               dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
-               I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
-
-               intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
-                                  dig_hotplug_reg, hpd_ibx,
-                                  pch_port_hotplug_long_detect);
-               intel_hpd_irq_handler(dev, pin_mask, long_mask);
-       }
+       if (hotplug_trigger)
+               ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx);
 
        if (pch_iir & SDE_AUDIO_POWER_MASK) {
                int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
@@ -1786,38 +1891,10 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        int pipe;
-       u32 hotplug_trigger;
+       u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
 
-       if (HAS_PCH_SPT(dev))
-               hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT;
-       else
-               hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT;
-
-       if (hotplug_trigger) {
-               u32 dig_hotplug_reg, pin_mask, long_mask;
-
-               dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
-               I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
-
-               if (HAS_PCH_SPT(dev)) {
-                       intel_get_hpd_pins(&pin_mask, &long_mask,
-                                          hotplug_trigger,
-                                          dig_hotplug_reg, hpd_spt,
-                                          pch_port_hotplug_long_detect);
-
-                       /* detect PORTE HP event */
-                       dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2);
-                       if (pch_port_hotplug_long_detect(PORT_E,
-                                                        dig_hotplug_reg))
-                               long_mask |= 1 << HPD_PORT_E;
-               } else
-                       intel_get_hpd_pins(&pin_mask, &long_mask,
-                                          hotplug_trigger,
-                                          dig_hotplug_reg, hpd_cpt,
-                                          pch_port_hotplug_long_detect);
-
-               intel_hpd_irq_handler(dev, pin_mask, long_mask);
-       }
+       if (hotplug_trigger)
+               ibx_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt);
 
        if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
                int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
@@ -1848,10 +1925,67 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
                cpt_serr_int_handler(dev);
 }
 
+static void spt_irq_handler(struct drm_device *dev, u32 pch_iir)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT &
+               ~SDE_PORTE_HOTPLUG_SPT;
+       u32 hotplug2_trigger = pch_iir & SDE_PORTE_HOTPLUG_SPT;
+       u32 pin_mask = 0, long_mask = 0;
+
+       if (hotplug_trigger) {
+               u32 dig_hotplug_reg;
+
+               dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+               I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
+
+               intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
+                                  dig_hotplug_reg, hpd_spt,
+                                  spt_port_hotplug_long_detect);
+       }
+
+       if (hotplug2_trigger) {
+               u32 dig_hotplug_reg;
+
+               dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2);
+               I915_WRITE(PCH_PORT_HOTPLUG2, dig_hotplug_reg);
+
+               intel_get_hpd_pins(&pin_mask, &long_mask, hotplug2_trigger,
+                                  dig_hotplug_reg, hpd_spt,
+                                  spt_port_hotplug2_long_detect);
+       }
+
+       if (pin_mask)
+               intel_hpd_irq_handler(dev, pin_mask, long_mask);
+
+       if (pch_iir & SDE_GMBUS_CPT)
+               gmbus_irq_handler(dev);
+}
+
+static void ilk_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger,
+                               const u32 hpd[HPD_NUM_PINS])
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
+
+       dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL);
+       I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg);
+
+       intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
+                          dig_hotplug_reg, hpd,
+                          ilk_port_hotplug_long_detect);
+
+       intel_hpd_irq_handler(dev, pin_mask, long_mask);
+}
+
 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;
+       u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG;
+
+       if (hotplug_trigger)
+               ilk_hpd_irq_handler(dev, hotplug_trigger, hpd_ilk);
 
        if (de_iir & DE_AUX_CHANNEL_A)
                dp_aux_irq_handler(dev);
@@ -1901,6 +2035,10 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        enum pipe pipe;
+       u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB;
+
+       if (hotplug_trigger)
+               ilk_hpd_irq_handler(dev, hotplug_trigger, hpd_ivb);
 
        if (de_iir & DE_ERR_INT_IVB)
                ivb_err_int_handler(dev);
@@ -2013,27 +2151,19 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        return ret;
 }
 
-static void bxt_hpd_handler(struct drm_device *dev, uint32_t iir_status)
+static void bxt_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger,
+                               const u32 hpd[HPD_NUM_PINS])
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 hp_control, hp_trigger;
-       u32 pin_mask, long_mask;
-
-       /* Get the status */
-       hp_trigger = iir_status & BXT_DE_PORT_HOTPLUG_MASK;
-       hp_control = I915_READ(BXT_HOTPLUG_CTL);
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0;
 
-       /* Hotplug not enabled ? */
-       if (!(hp_control & BXT_HOTPLUG_CTL_MASK)) {
-               DRM_ERROR("Interrupt when HPD disabled\n");
-               return;
-       }
+       dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG);
+       I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg);
 
-       /* Clear sticky bits in hpd status */
-       I915_WRITE(BXT_HOTPLUG_CTL, hp_control);
+       intel_get_hpd_pins(&pin_mask, &long_mask, hotplug_trigger,
+                          dig_hotplug_reg, hpd,
+                          bxt_port_hotplug_long_detect);
 
-       intel_get_hpd_pins(&pin_mask, &long_mask, hp_trigger, hp_control,
-                          hpd_bxt, bxt_port_hotplug_long_detect);
        intel_hpd_irq_handler(dev, pin_mask, long_mask);
 }
 
@@ -2050,7 +2180,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
        if (!intel_irqs_enabled(dev_priv))
                return IRQ_NONE;
 
-       if (IS_GEN9(dev))
+       if (INTEL_INFO(dev_priv)->gen >= 9)
                aux_mask |=  GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
                        GEN9_AUX_CHANNEL_D;
 
@@ -2083,6 +2213,12 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                tmp = I915_READ(GEN8_DE_PORT_IIR);
                if (tmp) {
                        bool found = false;
+                       u32 hotplug_trigger = 0;
+
+                       if (IS_BROXTON(dev_priv))
+                               hotplug_trigger = tmp & BXT_DE_PORT_HOTPLUG_MASK;
+                       else if (IS_BROADWELL(dev_priv))
+                               hotplug_trigger = tmp & GEN8_PORT_DP_A_HOTPLUG;
 
                        I915_WRITE(GEN8_DE_PORT_IIR, tmp);
                        ret = IRQ_HANDLED;
@@ -2092,8 +2228,11 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                                found = true;
                        }
 
-                       if (IS_BROXTON(dev) && tmp & BXT_DE_PORT_HOTPLUG_MASK) {
-                               bxt_hpd_handler(dev, tmp);
+                       if (hotplug_trigger) {
+                               if (IS_BROXTON(dev))
+                                       bxt_hpd_irq_handler(dev, hotplug_trigger, hpd_bxt);
+                               else
+                                       ilk_hpd_irq_handler(dev, hotplug_trigger, hpd_bdw);
                                found = true;
                        }
 
@@ -2124,7 +2263,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                            intel_pipe_handle_vblank(dev, pipe))
                                intel_check_page_flip(dev, pipe);
 
-                       if (IS_GEN9(dev))
+                       if (INTEL_INFO(dev_priv)->gen >= 9)
                                flip_done = pipe_iir & GEN9_PIPE_PLANE1_FLIP_DONE;
                        else
                                flip_done = pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE;
@@ -2142,7 +2281,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                                                                    pipe);
 
 
-                       if (IS_GEN9(dev))
+                       if (INTEL_INFO(dev_priv)->gen >= 9)
                                fault_errors = pipe_iir & GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
                        else
                                fault_errors = pipe_iir & GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
@@ -2166,7 +2305,11 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                if (pch_iir) {
                        I915_WRITE(SDEIIR, pch_iir);
                        ret = IRQ_HANDLED;
-                       cpt_irq_handler(dev, pch_iir);
+
+                       if (HAS_PCH_SPT(dev_priv))
+                               spt_irq_handler(dev, pch_iir);
+                       else
+                               cpt_irq_handler(dev, pch_iir);
                } else
                        DRM_ERROR("The master control interrupt lied (SDE)!\n");
 
@@ -3026,86 +3169,124 @@ static void cherryview_irq_preinstall(struct drm_device *dev)
        vlv_display_irq_reset(dev_priv);
 }
 
+static u32 intel_hpd_enabled_irqs(struct drm_device *dev,
+                                 const u32 hpd[HPD_NUM_PINS])
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_encoder *encoder;
+       u32 enabled_irqs = 0;
+
+       for_each_intel_encoder(dev, encoder)
+               if (dev_priv->hotplug.stats[encoder->hpd_pin].state == HPD_ENABLED)
+                       enabled_irqs |= hpd[encoder->hpd_pin];
+
+       return enabled_irqs;
+}
+
 static void ibx_hpd_irq_setup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_encoder *intel_encoder;
-       u32 hotplug_irqs, hotplug, enabled_irqs = 0;
+       u32 hotplug_irqs, hotplug, enabled_irqs;
 
        if (HAS_PCH_IBX(dev)) {
                hotplug_irqs = SDE_HOTPLUG_MASK;
-               for_each_intel_encoder(dev, intel_encoder)
-                       if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED)
-                               enabled_irqs |= hpd_ibx[intel_encoder->hpd_pin];
-       } else if (HAS_PCH_SPT(dev)) {
-               hotplug_irqs = SDE_HOTPLUG_MASK_SPT;
-               for_each_intel_encoder(dev, intel_encoder)
-                       if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED)
-                               enabled_irqs |= hpd_spt[intel_encoder->hpd_pin];
+               enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ibx);
        } else {
                hotplug_irqs = SDE_HOTPLUG_MASK_CPT;
-               for_each_intel_encoder(dev, intel_encoder)
-                       if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED)
-                               enabled_irqs |= hpd_cpt[intel_encoder->hpd_pin];
+               enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_cpt);
        }
 
        ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
 
        /*
         * Enable digital hotplug on the PCH, and configure the DP short pulse
-        * duration to 2ms (which is the minimum in the Display Port spec)
-        *
-        * This register is the same on all known PCH chips.
+        * duration to 2ms (which is the minimum in the Display Port spec).
+        * The pulse duration bits are reserved on LPT+.
         */
        hotplug = I915_READ(PCH_PORT_HOTPLUG);
        hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK);
        hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
        hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms;
        hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms;
+       /*
+        * When CPU and PCH are on the same package, port A
+        * HPD must be enabled in both north and south.
+        */
+       if (HAS_PCH_LPT_LP(dev))
+               hotplug |= PORTA_HOTPLUG_ENABLE;
        I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
+}
 
-       /* enable SPT PORTE hot plug */
-       if (HAS_PCH_SPT(dev)) {
-               hotplug = I915_READ(PCH_PORT_HOTPLUG2);
-               hotplug |= PORTE_HOTPLUG_ENABLE;
-               I915_WRITE(PCH_PORT_HOTPLUG2, hotplug);
-       }
+static void spt_hpd_irq_setup(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 hotplug_irqs, hotplug, enabled_irqs;
+
+       hotplug_irqs = SDE_HOTPLUG_MASK_SPT;
+       enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_spt);
+
+       ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs);
+
+       /* Enable digital hotplug on the PCH */
+       hotplug = I915_READ(PCH_PORT_HOTPLUG);
+       hotplug |= PORTD_HOTPLUG_ENABLE | PORTC_HOTPLUG_ENABLE |
+               PORTB_HOTPLUG_ENABLE | PORTA_HOTPLUG_ENABLE;
+       I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
+
+       hotplug = I915_READ(PCH_PORT_HOTPLUG2);
+       hotplug |= PORTE_HOTPLUG_ENABLE;
+       I915_WRITE(PCH_PORT_HOTPLUG2, hotplug);
 }
 
-static void bxt_hpd_irq_setup(struct drm_device *dev)
+static void ilk_hpd_irq_setup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_encoder *intel_encoder;
-       u32 hotplug_port = 0;
-       u32 hotplug_ctrl;
-
-       /* Now, enable HPD */
-       for_each_intel_encoder(dev, intel_encoder) {
-               if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state
-                               == HPD_ENABLED)
-                       hotplug_port |= hpd_bxt[intel_encoder->hpd_pin];
+       u32 hotplug_irqs, hotplug, enabled_irqs;
+
+       if (INTEL_INFO(dev)->gen >= 8) {
+               hotplug_irqs = GEN8_PORT_DP_A_HOTPLUG;
+               enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_bdw);
+
+               bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
+       } else if (INTEL_INFO(dev)->gen >= 7) {
+               hotplug_irqs = DE_DP_A_HOTPLUG_IVB;
+               enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ivb);
+
+               ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
+       } else {
+               hotplug_irqs = DE_DP_A_HOTPLUG;
+               enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_ilk);
+
+               ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs);
        }
 
-       /* Mask all HPD control bits */
-       hotplug_ctrl = I915_READ(BXT_HOTPLUG_CTL) & ~BXT_HOTPLUG_CTL_MASK;
+       /*
+        * Enable digital hotplug on the CPU, and configure the DP short pulse
+        * duration to 2ms (which is the minimum in the Display Port spec)
+        * The pulse duration bits are reserved on HSW+.
+        */
+       hotplug = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL);
+       hotplug &= ~DIGITAL_PORTA_PULSE_DURATION_MASK;
+       hotplug |= DIGITAL_PORTA_HOTPLUG_ENABLE | DIGITAL_PORTA_PULSE_DURATION_2ms;
+       I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, hotplug);
+
+       ibx_hpd_irq_setup(dev);
+}
+
+static void bxt_hpd_irq_setup(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 hotplug_irqs, hotplug, enabled_irqs;
 
-       /* Enable requested port in hotplug control */
-       /* TODO: implement (short) HPD support on port A */
-       WARN_ON_ONCE(hotplug_port & BXT_DE_PORT_HP_DDIA);
-       if (hotplug_port & BXT_DE_PORT_HP_DDIB)
-               hotplug_ctrl |= BXT_DDIB_HPD_ENABLE;
-       if (hotplug_port & BXT_DE_PORT_HP_DDIC)
-               hotplug_ctrl |= BXT_DDIC_HPD_ENABLE;
-       I915_WRITE(BXT_HOTPLUG_CTL, hotplug_ctrl);
+       enabled_irqs = intel_hpd_enabled_irqs(dev, hpd_bxt);
+       hotplug_irqs = BXT_DE_PORT_HOTPLUG_MASK;
 
-       /* Unmask DDI hotplug in IMR */
-       hotplug_ctrl = I915_READ(GEN8_DE_PORT_IMR) & ~hotplug_port;
-       I915_WRITE(GEN8_DE_PORT_IMR, hotplug_ctrl);
+       bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs);
 
-       /* Enable DDI hotplug in IER */
-       hotplug_ctrl = I915_READ(GEN8_DE_PORT_IER) | hotplug_port;
-       I915_WRITE(GEN8_DE_PORT_IER, hotplug_ctrl);
-       POSTING_READ(GEN8_DE_PORT_IER);
+       hotplug = I915_READ(PCH_PORT_HOTPLUG);
+       hotplug |= PORTC_HOTPLUG_ENABLE | PORTB_HOTPLUG_ENABLE |
+               PORTA_HOTPLUG_ENABLE;
+       I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
 }
 
 static void ibx_irq_postinstall(struct drm_device *dev)
@@ -3173,15 +3354,17 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
                                DE_PLANEB_FLIP_DONE_IVB |
                                DE_PLANEA_FLIP_DONE_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_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);
-               extra_mask = DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT |
-                               DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN;
+               extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT |
+                             DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
+                             DE_DP_A_HOTPLUG);
        }
 
        dev_priv->irq_mask = ~display_mask;
@@ -3377,24 +3560,31 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
 {
        uint32_t de_pipe_masked = GEN8_PIPE_CDCLK_CRC_DONE;
        uint32_t de_pipe_enables;
-       int pipe;
-       u32 de_port_en = GEN8_AUX_CHANNEL_A;
+       u32 de_port_masked = GEN8_AUX_CHANNEL_A;
+       u32 de_port_enables;
+       enum pipe pipe;
 
-       if (IS_GEN9(dev_priv)) {
+       if (INTEL_INFO(dev_priv)->gen >= 9) {
                de_pipe_masked |= GEN9_PIPE_PLANE1_FLIP_DONE |
                                  GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
-               de_port_en |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
-                       GEN9_AUX_CHANNEL_D;
-
+               de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
+                                 GEN9_AUX_CHANNEL_D;
                if (IS_BROXTON(dev_priv))
-                       de_port_en |= BXT_DE_PORT_GMBUS;
-       } else
+                       de_port_masked |= BXT_DE_PORT_GMBUS;
+       } else {
                de_pipe_masked |= GEN8_PIPE_PRIMARY_FLIP_DONE |
                                  GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
+       }
 
        de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
                                           GEN8_PIPE_FIFO_UNDERRUN;
 
+       de_port_enables = de_port_masked;
+       if (IS_BROXTON(dev_priv))
+               de_port_enables |= BXT_DE_PORT_HOTPLUG_MASK;
+       else if (IS_BROADWELL(dev_priv))
+               de_port_enables |= GEN8_PORT_DP_A_HOTPLUG;
+
        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;
@@ -3406,7 +3596,7 @@ 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_en, de_port_en);
+       GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables);
 }
 
 static int gen8_irq_postinstall(struct drm_device *dev)
@@ -3964,7 +4154,6 @@ static int i965_irq_postinstall(struct drm_device *dev)
 static void i915_hpd_irq_setup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_encoder *intel_encoder;
        u32 hotplug_en;
 
        assert_spin_locked(&dev_priv->irq_lock);
@@ -3973,9 +4162,7 @@ static void i915_hpd_irq_setup(struct drm_device *dev)
        hotplug_en &= ~HOTPLUG_INT_EN_MASK;
        /* Note HDMI and DP share hotplug bits */
        /* enable bits are the same for all generations */
-       for_each_intel_encoder(dev, intel_encoder)
-               if (dev_priv->hotplug.stats[intel_encoder->hpd_pin].state == HPD_ENABLED)
-                       hotplug_en |= hpd_mask_i915[intel_encoder->hpd_pin];
+       hotplug_en |= intel_hpd_enabled_irqs(dev, hpd_mask_i915);
        /* Programming the CRT detection parameters tends
           to generate a spurious hotplug event about three
           seconds later.  So just do it once.
@@ -4187,10 +4374,12 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                dev->driver->irq_uninstall = gen8_irq_uninstall;
                dev->driver->enable_vblank = gen8_enable_vblank;
                dev->driver->disable_vblank = gen8_disable_vblank;
-               if (HAS_PCH_SPLIT(dev))
-                       dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
-               else
+               if (IS_BROXTON(dev))
                        dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup;
+               else if (HAS_PCH_SPT(dev))
+                       dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup;
+               else
+                       dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
        } else if (HAS_PCH_SPLIT(dev)) {
                dev->driver->irq_handler = ironlake_irq_handler;
                dev->driver->irq_preinstall = ironlake_irq_reset;
@@ -4198,7 +4387,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                dev->driver->irq_uninstall = ironlake_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.hpd_irq_setup = ilk_hpd_irq_setup;
        } else {
                if (INTEL_INFO(dev_priv)->gen == 2) {
                        dev->driver->irq_preinstall = i8xx_irq_preinstall;