Merge tag 'master-2013-09-09' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil...
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index efe3fc671e1e56e24f127be58c24461a447a3714..83cce0cdb7691a9aa6024defc01c869a57b4a90a 100644 (file)
@@ -85,6 +85,12 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 {
        assert_spin_locked(&dev_priv->irq_lock);
 
+       if (dev_priv->pc8.irqs_disabled) {
+               WARN(1, "IRQs disabled\n");
+               dev_priv->pc8.regsave.deimr &= ~mask;
+               return;
+       }
+
        if ((dev_priv->irq_mask & mask) != 0) {
                dev_priv->irq_mask &= ~mask;
                I915_WRITE(DEIMR, dev_priv->irq_mask);
@@ -97,6 +103,12 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 {
        assert_spin_locked(&dev_priv->irq_lock);
 
+       if (dev_priv->pc8.irqs_disabled) {
+               WARN(1, "IRQs disabled\n");
+               dev_priv->pc8.regsave.deimr |= mask;
+               return;
+       }
+
        if ((dev_priv->irq_mask & mask) != mask) {
                dev_priv->irq_mask |= mask;
                I915_WRITE(DEIMR, dev_priv->irq_mask);
@@ -116,6 +128,14 @@ static void ilk_update_gt_irq(struct drm_i915_private *dev_priv,
 {
        assert_spin_locked(&dev_priv->irq_lock);
 
+       if (dev_priv->pc8.irqs_disabled) {
+               WARN(1, "IRQs disabled\n");
+               dev_priv->pc8.regsave.gtimr &= ~interrupt_mask;
+               dev_priv->pc8.regsave.gtimr |= (~enabled_irq_mask &
+                                               interrupt_mask);
+               return;
+       }
+
        dev_priv->gt_irq_mask &= ~interrupt_mask;
        dev_priv->gt_irq_mask |= (~enabled_irq_mask & interrupt_mask);
        I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
@@ -142,16 +162,25 @@ static void snb_update_pm_irq(struct drm_i915_private *dev_priv,
                              uint32_t interrupt_mask,
                              uint32_t enabled_irq_mask)
 {
-       uint32_t pmimr, new_val;
+       uint32_t new_val;
 
        assert_spin_locked(&dev_priv->irq_lock);
 
-       pmimr = new_val = I915_READ(GEN6_PMIMR);
+       if (dev_priv->pc8.irqs_disabled) {
+               WARN(1, "IRQs disabled\n");
+               dev_priv->pc8.regsave.gen6_pmimr &= ~interrupt_mask;
+               dev_priv->pc8.regsave.gen6_pmimr |= (~enabled_irq_mask &
+                                                    interrupt_mask);
+               return;
+       }
+
+       new_val = dev_priv->pm_irq_mask;
        new_val &= ~interrupt_mask;
        new_val |= (~enabled_irq_mask & interrupt_mask);
 
-       if (new_val != pmimr) {
-               I915_WRITE(GEN6_PMIMR, new_val);
+       if (new_val != dev_priv->pm_irq_mask) {
+               dev_priv->pm_irq_mask = new_val;
+               I915_WRITE(GEN6_PMIMR, dev_priv->pm_irq_mask);
                POSTING_READ(GEN6_PMIMR);
        }
 }
@@ -166,11 +195,6 @@ void snb_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask)
        snb_update_pm_irq(dev_priv, mask, 0);
 }
 
-static void snb_set_pm_irq(struct drm_i915_private *dev_priv, uint32_t val)
-{
-       snb_update_pm_irq(dev_priv, 0xffffffff, ~val);
-}
-
 static bool ivb_can_enable_err_int(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -261,6 +285,15 @@ static void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
 
        assert_spin_locked(&dev_priv->irq_lock);
 
+       if (dev_priv->pc8.irqs_disabled &&
+           (interrupt_mask & SDE_HOTPLUG_MASK_CPT)) {
+               WARN(1, "IRQs disabled\n");
+               dev_priv->pc8.regsave.sdeimr &= ~interrupt_mask;
+               dev_priv->pc8.regsave.sdeimr |= (~enabled_irq_mask &
+                                                interrupt_mask);
+               return;
+       }
+
        I915_WRITE(SDEIMR, sdeimr);
        POSTING_READ(SDEIMR);
 }
@@ -788,6 +821,9 @@ static void gen6_pm_rps_work(struct work_struct *work)
        snb_enable_pm_irq(dev_priv, GEN6_PM_RPS_EVENTS);
        spin_unlock_irq(&dev_priv->irq_lock);
 
+       /* Make sure we didn't queue anything we're not going to process. */
+       WARN_ON(pm_iir & ~GEN6_PM_RPS_EVENTS);
+
        if ((pm_iir & GEN6_PM_RPS_EVENTS) == 0)
                return;
 
@@ -943,28 +979,6 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                ivybridge_parity_error_irq_handler(dev);
 }
 
-/* Legacy way of handling PM interrupts */
-static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv,
-                                u32 pm_iir)
-{
-       /*
-        * IIR bits should never already be set because IMR should
-        * prevent an interrupt from being shown in IIR. The warning
-        * displays a case where we've unsafely cleared
-        * dev_priv->rps.pm_iir. Although missing an interrupt of the same
-        * type is not a problem, it displays a problem in the logic.
-        *
-        * The mask bit in IMR is cleared by dev_priv->rps.work.
-        */
-
-       spin_lock(&dev_priv->irq_lock);
-       dev_priv->rps.pm_iir |= pm_iir;
-       snb_set_pm_irq(dev_priv, dev_priv->rps.pm_iir);
-       spin_unlock(&dev_priv->irq_lock);
-
-       queue_work(dev_priv->wq, &dev_priv->rps.work);
-}
-
 #define HPD_STORM_DETECT_PERIOD 1000
 #define HPD_STORM_THRESHOLD 5
 
@@ -1013,8 +1027,13 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
                dev_priv->display.hpd_irq_setup(dev);
        spin_unlock(&dev_priv->irq_lock);
 
-       queue_work(dev_priv->wq,
-                  &dev_priv->hotplug_work);
+       /*
+        * Our hotplug handler can grab modeset locks (by calling down into the
+        * fb helpers). Hence it must not be run on our own dev-priv->wq work
+        * queue for otherwise the flush_work in the pageflip code will
+        * deadlock.
+        */
+       schedule_work(&dev_priv->hotplug_work);
 }
 
 static void gmbus_irq_handler(struct drm_device *dev)
@@ -1031,31 +1050,28 @@ static void dp_aux_irq_handler(struct drm_device *dev)
        wake_up_all(&dev_priv->gmbus_wait_queue);
 }
 
-/* Unlike gen6_rps_irq_handler() from which this function is originally derived,
- * we must be able to deal with other PM interrupts. This is complicated because
- * of the way in which we use the masks to defer the RPS work (which for
- * posterity is necessary because of forcewake).
- */
-static void hsw_pm_irq_handler(struct drm_i915_private *dev_priv,
-                              u32 pm_iir)
+/* The RPS events need forcewake, so we add them to a work queue and mask their
+ * IMR bits until the work is done. Other interrupts can be processed without
+ * the work queue. */
+static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
 {
        if (pm_iir & GEN6_PM_RPS_EVENTS) {
                spin_lock(&dev_priv->irq_lock);
                dev_priv->rps.pm_iir |= pm_iir & GEN6_PM_RPS_EVENTS;
-               snb_set_pm_irq(dev_priv, dev_priv->rps.pm_iir);
-               /* never want to mask useful interrupts. */
-               WARN_ON(I915_READ_NOTRACE(GEN6_PMIMR) & ~GEN6_PM_RPS_EVENTS);
+               snb_disable_pm_irq(dev_priv, pm_iir & GEN6_PM_RPS_EVENTS);
                spin_unlock(&dev_priv->irq_lock);
 
                queue_work(dev_priv->wq, &dev_priv->rps.work);
        }
 
-       if (pm_iir & PM_VEBOX_USER_INTERRUPT)
-               notify_ring(dev_priv->dev, &dev_priv->ring[VECS]);
+       if (HAS_VEBOX(dev_priv->dev)) {
+               if (pm_iir & PM_VEBOX_USER_INTERRUPT)
+                       notify_ring(dev_priv->dev, &dev_priv->ring[VECS]);
 
-       if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT) {
-               DRM_ERROR("VEBOX CS error interrupt 0x%08x\n", pm_iir);
-               i915_handle_error(dev_priv->dev, false);
+               if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT) {
+                       DRM_ERROR("VEBOX CS error interrupt 0x%08x\n", pm_iir);
+                       i915_handle_error(dev_priv->dev, false);
+               }
        }
 }
 
@@ -1127,7 +1143,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
                if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
                        gmbus_irq_handler(dev);
 
-               if (pm_iir & GEN6_PM_RPS_EVENTS)
+               if (pm_iir)
                        gen6_rps_irq_handler(dev_priv, pm_iir);
 
                I915_WRITE(GTIIR, gt_iir);
@@ -1372,6 +1388,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 de_iir, gt_iir, de_ier, sde_ier = 0;
        irqreturn_t ret = IRQ_NONE;
+       bool err_int_reenable = false;
 
        atomic_inc(&dev_priv->irq_received);
 
@@ -1400,7 +1417,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
         * handler. */
        if (IS_HASWELL(dev)) {
                spin_lock(&dev_priv->irq_lock);
-               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+               err_int_reenable = ~dev_priv->irq_mask & DE_ERR_INT_IVB;
+               if (err_int_reenable)
+                       ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
                spin_unlock(&dev_priv->irq_lock);
        }
 
@@ -1427,16 +1446,13 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        if (INTEL_INFO(dev)->gen >= 6) {
                u32 pm_iir = I915_READ(GEN6_PMIIR);
                if (pm_iir) {
-                       if (IS_HASWELL(dev))
-                               hsw_pm_irq_handler(dev_priv, pm_iir);
-                       else if (pm_iir & GEN6_PM_RPS_EVENTS)
-                               gen6_rps_irq_handler(dev_priv, pm_iir);
+                       gen6_rps_irq_handler(dev_priv, pm_iir);
                        I915_WRITE(GEN6_PMIIR, pm_iir);
                        ret = IRQ_HANDLED;
                }
        }
 
-       if (IS_HASWELL(dev)) {
+       if (err_int_reenable) {
                spin_lock(&dev_priv->irq_lock);
                if (ivb_can_enable_err_int(dev))
                        ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
@@ -1644,7 +1660,13 @@ void i915_handle_error(struct drm_device *dev, bool wedged)
                        wake_up_all(&ring->irq_queue);
        }
 
-       queue_work(dev_priv->wq, &dev_priv->gpu_error.work);
+       /*
+        * Our reset work can grab modeset locks (since it needs to reset the
+        * state of outstanding pagelips). Hence it must not be run on our own
+        * dev-priv->wq work queue for otherwise the flush_work in the pageflip
+        * code will deadlock.
+        */
+       schedule_work(&dev_priv->gpu_error.work);
 }
 
 static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe)
@@ -2016,9 +2038,9 @@ static void i915_hangcheck_elapsed(unsigned long data)
 
        for_each_ring(ring, dev_priv, i) {
                if (ring->hangcheck.score > FIRE) {
-                       DRM_ERROR("%s on %s\n",
-                                 stuck[i] ? "stuck" : "no progress",
-                                 ring->name);
+                       DRM_INFO("%s on %s\n",
+                                stuck[i] ? "stuck" : "no progress",
+                                ring->name);
                        rings_hung++;
                }
        }
@@ -2217,8 +2239,9 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
                if (HAS_VEBOX(dev))
                        pm_irqs |= PM_VEBOX_USER_INTERRUPT;
 
+               dev_priv->pm_irq_mask = 0xffffffff;
                I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
-               I915_WRITE(GEN6_PMIMR, 0xffffffff);
+               I915_WRITE(GEN6_PMIMR, dev_priv->pm_irq_mask);
                I915_WRITE(GEN6_PMIER, pm_irqs);
                POSTING_READ(GEN6_PMIER);
        }
@@ -3138,3 +3161,67 @@ void intel_hpd_init(struct drm_device *dev)
                dev_priv->display.hpd_irq_setup(dev);
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
+
+/* Disable interrupts so we can allow Package C8+. */
+void hsw_pc8_disable_interrupts(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+
+       dev_priv->pc8.regsave.deimr = I915_READ(DEIMR);
+       dev_priv->pc8.regsave.sdeimr = I915_READ(SDEIMR);
+       dev_priv->pc8.regsave.gtimr = I915_READ(GTIMR);
+       dev_priv->pc8.regsave.gtier = I915_READ(GTIER);
+       dev_priv->pc8.regsave.gen6_pmimr = I915_READ(GEN6_PMIMR);
+
+       ironlake_disable_display_irq(dev_priv, ~DE_PCH_EVENT_IVB);
+       ibx_disable_display_interrupt(dev_priv, ~SDE_HOTPLUG_MASK_CPT);
+       ilk_disable_gt_irq(dev_priv, 0xffffffff);
+       snb_disable_pm_irq(dev_priv, 0xffffffff);
+
+       dev_priv->pc8.irqs_disabled = true;
+
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
+
+/* Restore interrupts so we can recover from Package C8+. */
+void hsw_pc8_restore_interrupts(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long irqflags;
+       uint32_t val, expected;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+
+       val = I915_READ(DEIMR);
+       expected = ~DE_PCH_EVENT_IVB;
+       WARN(val != expected, "DEIMR is 0x%08x, not 0x%08x\n", val, expected);
+
+       val = I915_READ(SDEIMR) & ~SDE_HOTPLUG_MASK_CPT;
+       expected = ~SDE_HOTPLUG_MASK_CPT;
+       WARN(val != expected, "SDEIMR non-HPD bits are 0x%08x, not 0x%08x\n",
+            val, expected);
+
+       val = I915_READ(GTIMR);
+       expected = 0xffffffff;
+       WARN(val != expected, "GTIMR is 0x%08x, not 0x%08x\n", val, expected);
+
+       val = I915_READ(GEN6_PMIMR);
+       expected = 0xffffffff;
+       WARN(val != expected, "GEN6_PMIMR is 0x%08x, not 0x%08x\n", val,
+            expected);
+
+       dev_priv->pc8.irqs_disabled = false;
+
+       ironlake_enable_display_irq(dev_priv, ~dev_priv->pc8.regsave.deimr);
+       ibx_enable_display_interrupt(dev_priv,
+                                    ~dev_priv->pc8.regsave.sdeimr &
+                                    ~SDE_HOTPLUG_MASK_CPT);
+       ilk_enable_gt_irq(dev_priv, ~dev_priv->pc8.regsave.gtimr);
+       snb_enable_pm_irq(dev_priv, ~dev_priv->pc8.regsave.gen6_pmimr);
+       I915_WRITE(GTIER, dev_priv->pc8.regsave.gtier);
+
+       spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+}
This page took 0.043223 seconds and 5 git commands to generate.