drm/i915: Hook up pfit for DSI
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index d0c64ed657b9a7c560919e5e2939e9eea772e358..93da4feb304829c2006621db164ae11b8a353679 100644 (file)
@@ -1264,18 +1264,17 @@ out:
        mutex_unlock(&dev_priv->dev->struct_mutex);
 }
 
-static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
+static void ivybridge_parity_error_irq_handler(struct drm_i915_private *dev_priv,
+                                              u32 iir)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
-
-       if (!HAS_L3_DPF(dev))
+       if (!HAS_L3_DPF(dev_priv))
                return;
 
        spin_lock(&dev_priv->irq_lock);
-       gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev));
+       gen5_disable_gt_irq(dev_priv, GT_PARITY_ERROR(dev_priv));
        spin_unlock(&dev_priv->irq_lock);
 
-       iir &= GT_PARITY_ERROR(dev);
+       iir &= GT_PARITY_ERROR(dev_priv);
        if (iir & GT_RENDER_L3_PARITY_ERROR_INTERRUPT_S1)
                dev_priv->l3_parity.which_slice |= 1 << 1;
 
@@ -1285,8 +1284,7 @@ static void ivybridge_parity_error_irq_handler(struct drm_device *dev, u32 iir)
        queue_work(dev_priv->wq, &dev_priv->l3_parity.error_work);
 }
 
-static void ilk_gt_irq_handler(struct drm_device *dev,
-                              struct drm_i915_private *dev_priv,
+static void ilk_gt_irq_handler(struct drm_i915_private *dev_priv,
                               u32 gt_iir)
 {
        if (gt_iir &
@@ -1296,8 +1294,7 @@ static void ilk_gt_irq_handler(struct drm_device *dev,
                notify_ring(&dev_priv->engine[VCS]);
 }
 
-static void snb_gt_irq_handler(struct drm_device *dev,
-                              struct drm_i915_private *dev_priv,
+static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
                               u32 gt_iir)
 {
 
@@ -1314,8 +1311,8 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                      GT_RENDER_CS_MASTER_ERROR_INTERRUPT))
                DRM_DEBUG("Command parser error, gt_iir 0x%08x\n", gt_iir);
 
-       if (gt_iir & GT_PARITY_ERROR(dev))
-               ivybridge_parity_error_irq_handler(dev, gt_iir);
+       if (gt_iir & GT_PARITY_ERROR(dev_priv))
+               ivybridge_parity_error_irq_handler(dev_priv, gt_iir);
 }
 
 static __always_inline void
@@ -1327,60 +1324,45 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
                tasklet_schedule(&engine->irq_tasklet);
 }
 
-static irqreturn_t gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
-                                      u32 master_ctl)
+static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv,
+                                  u32 master_ctl,
+                                  u32 gt_iir[4])
 {
        irqreturn_t ret = IRQ_NONE;
 
        if (master_ctl & (GEN8_GT_RCS_IRQ | GEN8_GT_BCS_IRQ)) {
-               u32 iir = I915_READ_FW(GEN8_GT_IIR(0));
-               if (iir) {
-                       I915_WRITE_FW(GEN8_GT_IIR(0), iir);
+               gt_iir[0] = I915_READ_FW(GEN8_GT_IIR(0));
+               if (gt_iir[0]) {
+                       I915_WRITE_FW(GEN8_GT_IIR(0), gt_iir[0]);
                        ret = IRQ_HANDLED;
-
-                       gen8_cs_irq_handler(&dev_priv->engine[RCS],
-                                           iir, GEN8_RCS_IRQ_SHIFT);
-
-                       gen8_cs_irq_handler(&dev_priv->engine[BCS],
-                                           iir, GEN8_BCS_IRQ_SHIFT);
                } else
                        DRM_ERROR("The master control interrupt lied (GT0)!\n");
        }
 
        if (master_ctl & (GEN8_GT_VCS1_IRQ | GEN8_GT_VCS2_IRQ)) {
-               u32 iir = I915_READ_FW(GEN8_GT_IIR(1));
-               if (iir) {
-                       I915_WRITE_FW(GEN8_GT_IIR(1), iir);
+               gt_iir[1] = I915_READ_FW(GEN8_GT_IIR(1));
+               if (gt_iir[1]) {
+                       I915_WRITE_FW(GEN8_GT_IIR(1), gt_iir[1]);
                        ret = IRQ_HANDLED;
-
-                       gen8_cs_irq_handler(&dev_priv->engine[VCS],
-                                           iir, GEN8_VCS1_IRQ_SHIFT);
-
-                       gen8_cs_irq_handler(&dev_priv->engine[VCS2],
-                                           iir, GEN8_VCS2_IRQ_SHIFT);
                } else
                        DRM_ERROR("The master control interrupt lied (GT1)!\n");
        }
 
        if (master_ctl & GEN8_GT_VECS_IRQ) {
-               u32 iir = I915_READ_FW(GEN8_GT_IIR(3));
-               if (iir) {
-                       I915_WRITE_FW(GEN8_GT_IIR(3), iir);
+               gt_iir[3] = I915_READ_FW(GEN8_GT_IIR(3));
+               if (gt_iir[3]) {
+                       I915_WRITE_FW(GEN8_GT_IIR(3), gt_iir[3]);
                        ret = IRQ_HANDLED;
-
-                       gen8_cs_irq_handler(&dev_priv->engine[VECS],
-                                           iir, GEN8_VECS_IRQ_SHIFT);
                } else
                        DRM_ERROR("The master control interrupt lied (GT3)!\n");
        }
 
        if (master_ctl & GEN8_GT_PM_IRQ) {
-               u32 iir = I915_READ_FW(GEN8_GT_IIR(2));
-               if (iir & dev_priv->pm_rps_events) {
+               gt_iir[2] = I915_READ_FW(GEN8_GT_IIR(2));
+               if (gt_iir[2] & dev_priv->pm_rps_events) {
                        I915_WRITE_FW(GEN8_GT_IIR(2),
-                                     iir & dev_priv->pm_rps_events);
+                                     gt_iir[2] & dev_priv->pm_rps_events);
                        ret = IRQ_HANDLED;
-                       gen6_rps_irq_handler(dev_priv, iir);
                } else
                        DRM_ERROR("The master control interrupt lied (PM)!\n");
        }
@@ -1388,6 +1370,31 @@ static irqreturn_t gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
        return ret;
 }
 
+static void gen8_gt_irq_handler(struct drm_i915_private *dev_priv,
+                               u32 gt_iir[4])
+{
+       if (gt_iir[0]) {
+               gen8_cs_irq_handler(&dev_priv->engine[RCS],
+                                   gt_iir[0], GEN8_RCS_IRQ_SHIFT);
+               gen8_cs_irq_handler(&dev_priv->engine[BCS],
+                                   gt_iir[0], GEN8_BCS_IRQ_SHIFT);
+       }
+
+       if (gt_iir[1]) {
+               gen8_cs_irq_handler(&dev_priv->engine[VCS],
+                                   gt_iir[1], GEN8_VCS1_IRQ_SHIFT);
+               gen8_cs_irq_handler(&dev_priv->engine[VCS2],
+                                   gt_iir[1], GEN8_VCS2_IRQ_SHIFT);
+       }
+
+       if (gt_iir[3])
+               gen8_cs_irq_handler(&dev_priv->engine[VECS],
+                                   gt_iir[3], GEN8_VECS_IRQ_SHIFT);
+
+       if (gt_iir[2] & dev_priv->pm_rps_events)
+               gen6_rps_irq_handler(dev_priv, gt_iir[2]);
+}
+
 static bool bxt_port_hotplug_long_detect(enum port port, u32 val)
 {
        switch (port) {
@@ -1644,10 +1651,10 @@ static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
        return true;
 }
 
-static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
+static void valleyview_pipestat_irq_ack(struct drm_device *dev, u32 iir,
+                                       u32 pipe_stats[I915_MAX_PIPES])
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 pipe_stats[I915_MAX_PIPES] = { };
        int pipe;
 
        spin_lock(&dev_priv->irq_lock);
@@ -1701,6 +1708,13 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
                        I915_WRITE(reg, pipe_stats[pipe]);
        }
        spin_unlock(&dev_priv->irq_lock);
+}
+
+static void valleyview_pipestat_irq_handler(struct drm_device *dev,
+                                           u32 pipe_stats[I915_MAX_PIPES])
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       enum pipe pipe;
 
        for_each_pipe(dev_priv, pipe) {
                if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS &&
@@ -1723,21 +1737,20 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
                gmbus_irq_handler(dev);
 }
 
-static void i9xx_hpd_irq_handler(struct drm_device *dev)
+static u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = dev->dev_private;
        u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
-       u32 pin_mask = 0, long_mask = 0;
 
-       if (!hotplug_status)
-               return;
+       if (hotplug_status)
+               I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
 
-       I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
-       /*
-        * Make sure hotplug status is cleared before we clear IIR, or else we
-        * may miss hotplug events.
-        */
-       POSTING_READ(PORT_HOTPLUG_STAT);
+       return hotplug_status;
+}
+
+static void i9xx_hpd_irq_handler(struct drm_device *dev,
+                                u32 hotplug_status)
+{
+       u32 pin_mask = 0, long_mask = 0;
 
        if (IS_G4X(dev) || IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
                u32 hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X;
@@ -1768,7 +1781,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 iir, gt_iir, pm_iir;
        irqreturn_t ret = IRQ_NONE;
 
        if (!intel_irqs_enabled(dev_priv))
@@ -1777,35 +1789,49 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
        /* IRQs are synced during runtime_suspend, we don't require a wakeref */
        disable_rpm_wakeref_asserts(dev_priv);
 
-       while (true) {
-               /* Find, clear, then process each source of interrupt */
+       do {
+               u32 iir, gt_iir, pm_iir;
+               u32 pipe_stats[I915_MAX_PIPES] = {};
+               u32 hotplug_status = 0;
+               u32 ier = 0;
 
                gt_iir = I915_READ(GTIIR);
-               if (gt_iir)
-                       I915_WRITE(GTIIR, gt_iir);
-
                pm_iir = I915_READ(GEN6_PMIIR);
-               if (pm_iir)
-                       I915_WRITE(GEN6_PMIIR, pm_iir);
-
                iir = I915_READ(VLV_IIR);
 
                if (gt_iir == 0 && pm_iir == 0 && iir == 0)
-                       goto out;
+                       break;
 
                ret = IRQ_HANDLED;
 
+               /*
+                * Theory on interrupt generation, based on empirical evidence:
+                *
+                * x = ((VLV_IIR & VLV_IER) ||
+                *      (((GT_IIR & GT_IER) || (GEN6_PMIIR & GEN6_PMIER)) &&
+                *       (VLV_MASTER_IER & MASTER_INTERRUPT_ENABLE)));
+                *
+                * A CPU interrupt will only be raised when 'x' has a 0->1 edge.
+                * Hence we clear MASTER_INTERRUPT_ENABLE and VLV_IER to
+                * guarantee the CPU interrupt will be raised again even if we
+                * don't end up clearing all the VLV_IIR, GT_IIR, GEN6_PMIIR
+                * bits this time around.
+                */
+               I915_WRITE(VLV_MASTER_IER, 0);
+               ier = I915_READ(VLV_IER);
+               I915_WRITE(VLV_IER, 0);
+
                if (gt_iir)
-                       snb_gt_irq_handler(dev, dev_priv, gt_iir);
+                       I915_WRITE(GTIIR, gt_iir);
                if (pm_iir)
-                       gen6_rps_irq_handler(dev_priv, pm_iir);
+                       I915_WRITE(GEN6_PMIIR, pm_iir);
 
                if (iir & I915_DISPLAY_PORT_INTERRUPT)
-                       i9xx_hpd_irq_handler(dev);
+                       hotplug_status = i9xx_hpd_irq_ack(dev_priv);
 
                /* Call regardless, as some status bits might not be
                 * signalled in iir */
-               valleyview_pipestat_irq_handler(dev, iir);
+               valleyview_pipestat_irq_ack(dev, iir, pipe_stats);
 
                /*
                 * VLV_IIR is single buffered, and reflects the level
@@ -1813,9 +1839,22 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                 */
                if (iir)
                        I915_WRITE(VLV_IIR, iir);
-       }
 
-out:
+               I915_WRITE(VLV_IER, ier);
+               I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE);
+               POSTING_READ(VLV_MASTER_IER);
+
+               if (gt_iir)
+                       snb_gt_irq_handler(dev_priv, gt_iir);
+               if (pm_iir)
+                       gen6_rps_irq_handler(dev_priv, pm_iir);
+
+               if (hotplug_status)
+                       i9xx_hpd_irq_handler(dev, hotplug_status);
+
+               valleyview_pipestat_irq_handler(dev, pipe_stats);
+       } while (0);
+
        enable_rpm_wakeref_asserts(dev_priv);
 
        return ret;
@@ -1825,7 +1864,6 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = arg;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 master_ctl, iir;
        irqreturn_t ret = IRQ_NONE;
 
        if (!intel_irqs_enabled(dev_priv))
@@ -1835,6 +1873,12 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
        disable_rpm_wakeref_asserts(dev_priv);
 
        do {
+               u32 master_ctl, iir;
+               u32 gt_iir[4] = {};
+               u32 pipe_stats[I915_MAX_PIPES] = {};
+               u32 hotplug_status = 0;
+               u32 ier = 0;
+
                master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
                iir = I915_READ(VLV_IIR);
 
@@ -1843,16 +1887,31 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
 
                ret = IRQ_HANDLED;
 
+               /*
+                * Theory on interrupt generation, based on empirical evidence:
+                *
+                * x = ((VLV_IIR & VLV_IER) ||
+                *      ((GEN8_MASTER_IRQ & ~GEN8_MASTER_IRQ_CONTROL) &&
+                *       (GEN8_MASTER_IRQ & GEN8_MASTER_IRQ_CONTROL)));
+                *
+                * A CPU interrupt will only be raised when 'x' has a 0->1 edge.
+                * Hence we clear GEN8_MASTER_IRQ_CONTROL and VLV_IER to
+                * guarantee the CPU interrupt will be raised again even if we
+                * don't end up clearing all the VLV_IIR and GEN8_MASTER_IRQ_CONTROL
+                * bits this time around.
+                */
                I915_WRITE(GEN8_MASTER_IRQ, 0);
+               ier = I915_READ(VLV_IER);
+               I915_WRITE(VLV_IER, 0);
 
-               gen8_gt_irq_handler(dev_priv, master_ctl);
+               gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
 
                if (iir & I915_DISPLAY_PORT_INTERRUPT)
-                       i9xx_hpd_irq_handler(dev);
+                       hotplug_status = i9xx_hpd_irq_ack(dev_priv);
 
                /* Call regardless, as some status bits might not be
                 * signalled in iir */
-               valleyview_pipestat_irq_handler(dev, iir);
+               valleyview_pipestat_irq_ack(dev, iir, pipe_stats);
 
                /*
                 * VLV_IIR is single buffered, and reflects the level
@@ -1861,8 +1920,16 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
                if (iir)
                        I915_WRITE(VLV_IIR, iir);
 
+               I915_WRITE(VLV_IER, ier);
                I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
                POSTING_READ(GEN8_MASTER_IRQ);
+
+               gen8_gt_irq_handler(dev_priv, gt_iir);
+
+               if (hotplug_status)
+                       i9xx_hpd_irq_handler(dev, hotplug_status);
+
+               valleyview_pipestat_irq_handler(dev, pipe_stats);
        } while (0);
 
        enable_rpm_wakeref_asserts(dev_priv);
@@ -2223,9 +2290,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
                I915_WRITE(GTIIR, gt_iir);
                ret = IRQ_HANDLED;
                if (INTEL_INFO(dev)->gen >= 6)
-                       snb_gt_irq_handler(dev, dev_priv, gt_iir);
+                       snb_gt_irq_handler(dev_priv, gt_iir);
                else
-                       ilk_gt_irq_handler(dev, dev_priv, gt_iir);
+                       ilk_gt_irq_handler(dev_priv, gt_iir);
        }
 
        de_iir = I915_READ(DEIIR);
@@ -2425,6 +2492,7 @@ 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;
+       u32 gt_iir[4] = {};
        irqreturn_t ret;
 
        if (!intel_irqs_enabled(dev_priv))
@@ -2441,7 +2509,8 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
        disable_rpm_wakeref_asserts(dev_priv);
 
        /* Find, clear, then process each source of interrupt */
-       ret = gen8_gt_irq_handler(dev_priv, master_ctl);
+       ret = gen8_gt_irq_ack(dev_priv, master_ctl, gt_iir);
+       gen8_gt_irq_handler(dev_priv, gt_iir);
        ret |= gen8_de_irq_handler(dev_priv, master_ctl);
 
        I915_WRITE_FW(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL);
@@ -4203,8 +4272,11 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 
                /* Consume port.  Then clear IIR or we'll miss events */
                if (I915_HAS_HOTPLUG(dev) &&
-                   iir & I915_DISPLAY_PORT_INTERRUPT)
-                       i9xx_hpd_irq_handler(dev);
+                   iir & I915_DISPLAY_PORT_INTERRUPT) {
+                       u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+                       if (hotplug_status)
+                               i9xx_hpd_irq_handler(dev, hotplug_status);
+               }
 
                I915_WRITE(IIR, iir & ~flip_mask);
                new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -4433,8 +4505,11 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
                ret = IRQ_HANDLED;
 
                /* Consume port.  Then clear IIR or we'll miss events */
-               if (iir & I915_DISPLAY_PORT_INTERRUPT)
-                       i9xx_hpd_irq_handler(dev);
+               if (iir & I915_DISPLAY_PORT_INTERRUPT) {
+                       u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+                       if (hotplug_status)
+                               i9xx_hpd_irq_handler(dev, hotplug_status);
+               }
 
                I915_WRITE(IIR, iir & ~flip_mask);
                new_iir = I915_READ(IIR); /* Flush posted writes */
This page took 0.040846 seconds and 5 git commands to generate.