drm/i915: Create an ivybridge_irq_preinstall
[deliverable/linux.git] / drivers / gpu / drm / i915 / i915_irq.c
index 0aa2ef0d2ae01b3d8e796a23f63ee119b657fbdc..5d5bb8ce0fb40e10c2ae315270acf56d43e69d6c 100644 (file)
@@ -112,6 +112,213 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
        }
 }
 
+static bool ivb_can_enable_err_int(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *crtc;
+       enum pipe pipe;
+
+       for_each_pipe(pipe) {
+               crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+               if (crtc->cpu_fifo_underrun_disabled)
+                       return false;
+       }
+
+       return true;
+}
+
+static bool cpt_can_enable_serr_int(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe;
+       struct intel_crtc *crtc;
+
+       for_each_pipe(pipe) {
+               crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+               if (crtc->pch_fifo_underrun_disabled)
+                       return false;
+       }
+
+       return true;
+}
+
+static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
+                                                enum pipe pipe, bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN :
+                                         DE_PIPEB_FIFO_UNDERRUN;
+
+       if (enable)
+               ironlake_enable_display_irq(dev_priv, bit);
+       else
+               ironlake_disable_display_irq(dev_priv, bit);
+}
+
+static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
+                                                 bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (enable) {
+               if (!ivb_can_enable_err_int(dev))
+                       return;
+
+               I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN_A |
+                                        ERR_INT_FIFO_UNDERRUN_B |
+                                        ERR_INT_FIFO_UNDERRUN_C);
+
+               ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
+       } else {
+               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+       }
+}
+
+static void ibx_set_fifo_underrun_reporting(struct intel_crtc *crtc,
+                                           bool enable)
+{
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t bit = (crtc->pipe == PIPE_A) ? SDE_TRANSA_FIFO_UNDER :
+                                               SDE_TRANSB_FIFO_UNDER;
+
+       if (enable)
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~bit);
+       else
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) | bit);
+
+       POSTING_READ(SDEIMR);
+}
+
+static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
+                                           enum transcoder pch_transcoder,
+                                           bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (enable) {
+               if (!cpt_can_enable_serr_int(dev))
+                       return;
+
+               I915_WRITE(SERR_INT, SERR_INT_TRANS_A_FIFO_UNDERRUN |
+                                    SERR_INT_TRANS_B_FIFO_UNDERRUN |
+                                    SERR_INT_TRANS_C_FIFO_UNDERRUN);
+
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~SDE_ERROR_CPT);
+       } else {
+               I915_WRITE(SDEIMR, I915_READ(SDEIMR) | SDE_ERROR_CPT);
+       }
+
+       POSTING_READ(SDEIMR);
+}
+
+/**
+ * intel_set_cpu_fifo_underrun_reporting - enable/disable FIFO underrun messages
+ * @dev: drm device
+ * @pipe: pipe
+ * @enable: true if we want to report FIFO underrun errors, false otherwise
+ *
+ * This function makes us disable or enable CPU fifo underruns for a specific
+ * pipe. Notice that on some Gens (e.g. IVB, HSW), disabling FIFO underrun
+ * reporting for one pipe may also disable all the other CPU error interruts for
+ * the other pipes, due to the fact that there's just one interrupt mask/enable
+ * bit for all the pipes.
+ *
+ * Returns the previous state of underrun reporting.
+ */
+bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
+                                          enum pipe pipe, bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       unsigned long flags;
+       bool ret;
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+       ret = !intel_crtc->cpu_fifo_underrun_disabled;
+
+       if (enable == ret)
+               goto done;
+
+       intel_crtc->cpu_fifo_underrun_disabled = !enable;
+
+       if (IS_GEN5(dev) || IS_GEN6(dev))
+               ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
+       else if (IS_GEN7(dev))
+               ivybridge_set_fifo_underrun_reporting(dev, enable);
+
+done:
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+       return ret;
+}
+
+/**
+ * intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages
+ * @dev: drm device
+ * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older)
+ * @enable: true if we want to report FIFO underrun errors, false otherwise
+ *
+ * This function makes us disable or enable PCH fifo underruns for a specific
+ * PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO
+ * underrun reporting for one transcoder may also disable all the other PCH
+ * error interruts for the other transcoders, due to the fact that there's just
+ * one interrupt mask/enable bit for all the transcoders.
+ *
+ * Returns the previous state of underrun reporting.
+ */
+bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
+                                          enum transcoder pch_transcoder,
+                                          bool enable)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe p;
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+       unsigned long flags;
+       bool ret;
+
+       if (HAS_PCH_LPT(dev)) {
+               crtc = NULL;
+               for_each_pipe(p) {
+                       struct drm_crtc *c = dev_priv->pipe_to_crtc_mapping[p];
+                       if (intel_pipe_has_type(c, INTEL_OUTPUT_ANALOG)) {
+                               crtc = c;
+                               break;
+                       }
+               }
+               if (!crtc) {
+                       DRM_ERROR("PCH FIFO underrun, but no CRTC using the PCH found\n");
+                       return false;
+               }
+       } else {
+               crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder];
+       }
+       intel_crtc = to_intel_crtc(crtc);
+
+       spin_lock_irqsave(&dev_priv->irq_lock, flags);
+
+       ret = !intel_crtc->pch_fifo_underrun_disabled;
+
+       if (enable == ret)
+               goto done;
+
+       intel_crtc->pch_fifo_underrun_disabled = !enable;
+
+       if (HAS_PCH_IBX(dev))
+               ibx_set_fifo_underrun_reporting(intel_crtc, enable);
+       else
+               cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
+
+done:
+       spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
+       return ret;
+}
+
+
 void
 i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 {
@@ -142,28 +349,21 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
 }
 
 /**
- * intel_enable_asle - enable ASLE interrupt for OpRegion
+ * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion
  */
-void intel_enable_asle(struct drm_device *dev)
+static void i915_enable_asle_pipestat(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        unsigned long irqflags;
 
-       /* FIXME: opregion/asle for VLV */
-       if (IS_VALLEYVIEW(dev))
+       if (!dev_priv->opregion.asle || !IS_MOBILE(dev))
                return;
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
 
-       if (HAS_PCH_SPLIT(dev))
-               ironlake_enable_display_irq(dev_priv, DE_GSE);
-       else {
-               i915_enable_pipestat(dev_priv, 1,
-                                    PIPE_LEGACY_BLC_EVENT_ENABLE);
-               if (INTEL_INFO(dev)->gen >= 4)
-                       i915_enable_pipestat(dev_priv, 0,
-                                            PIPE_LEGACY_BLC_EVENT_ENABLE);
-       }
+       i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE);
+       if (INTEL_INFO(dev)->gen >= 4)
+               i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE);
 
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
@@ -181,10 +381,16 @@ static int
 i915_pipe_enabled(struct drm_device *dev, int pipe)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
-       enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
-                                                                     pipe);
 
-       return I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE;
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               /* Locking is horribly broken here, but whatever. */
+               struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+               return intel_crtc->active;
+       } else {
+               return I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE;
+       }
 }
 
 /* Called from drm generic code, passed a 'crtc', which
@@ -334,6 +540,21 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
                                                     crtc);
 }
 
+static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *connector)
+{
+       enum drm_connector_status old_status;
+
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       old_status = connector->status;
+
+       connector->status = connector->funcs->detect(connector, false);
+       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n",
+                     connector->base.id,
+                     drm_get_connector_name(connector),
+                     old_status, connector->status);
+       return (old_status != connector->status);
+}
+
 /*
  * Handle hotplug events outside the interrupt handler proper.
  */
@@ -350,6 +571,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
        struct drm_connector *connector;
        unsigned long irqflags;
        bool hpd_disabled = false;
+       bool changed = false;
+       u32 hpd_event_bits;
 
        /* HPD irq before everything is fully set up. */
        if (!dev_priv->enable_hotplug_processing)
@@ -359,6 +582,9 @@ static void i915_hotplug_work_func(struct work_struct *work)
        DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
        spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+
+       hpd_event_bits = dev_priv->hpd_event_bits;
+       dev_priv->hpd_event_bits = 0;
        list_for_each_entry(connector, &mode_config->connector_list, head) {
                intel_connector = to_intel_connector(connector);
                intel_encoder = intel_connector->encoder;
@@ -373,6 +599,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
                                | DRM_CONNECTOR_POLL_DISCONNECT;
                        hpd_disabled = true;
                }
+               if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+                       DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n",
+                                     drm_get_connector_name(connector), intel_encoder->hpd_pin);
+               }
        }
         /* if there were no outputs to poll, poll was disabled,
          * therefore make sure it's enabled when disabling HPD on
@@ -385,14 +615,20 @@ static void i915_hotplug_work_func(struct work_struct *work)
 
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
-       list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
-               if (intel_encoder->hot_plug)
-                       intel_encoder->hot_plug(intel_encoder);
-
+       list_for_each_entry(connector, &mode_config->connector_list, head) {
+               intel_connector = to_intel_connector(connector);
+               intel_encoder = intel_connector->encoder;
+               if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
+                       if (intel_encoder->hot_plug)
+                               intel_encoder->hot_plug(intel_encoder);
+                       if (intel_hpd_irq_event(dev, connector))
+                               changed = true;
+               }
+       }
        mutex_unlock(&mode_config->mutex);
 
-       /* Just fire off a uevent and let userspace tell us what to do */
-       drm_helper_hpd_irq_event(dev);
+       if (changed)
+               drm_kms_helper_hotplug_event(dev);
 }
 
 static void ironlake_handle_rps_change(struct drm_device *dev)
@@ -482,7 +718,21 @@ static void gen6_pm_rps_work(struct work_struct *work)
         */
        if (!(new_delay > dev_priv->rps.max_delay ||
              new_delay < dev_priv->rps.min_delay)) {
-               gen6_set_rps(dev_priv->dev, new_delay);
+               if (IS_VALLEYVIEW(dev_priv->dev))
+                       valleyview_set_rps(dev_priv->dev, new_delay);
+               else
+                       gen6_set_rps(dev_priv->dev, new_delay);
+       }
+
+       if (IS_VALLEYVIEW(dev_priv->dev)) {
+               /*
+                * On VLV, when we enter RC6 we may not be at the minimum
+                * voltage level, so arm a timer to check.  It should only
+                * fire when there's activity or once after we've entered
+                * RC6, and then won't be re-armed until the next RPS interrupt.
+                */
+               mod_delayed_work(dev_priv->wq, &dev_priv->rps.vlv_work,
+                                msecs_to_jiffies(100));
        }
 
        mutex_unlock(&dev_priv->rps.hw_lock);
@@ -592,6 +842,7 @@ static void snb_gt_irq_handler(struct drm_device *dev,
                ivybridge_handle_parity_error(dev);
 }
 
+/* Legacy way of handling PM interrupts */
 static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
                                u32 pm_iir)
 {
@@ -636,6 +887,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
                    dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED)
                        continue;
 
+               dev_priv->hpd_event_bits |= (1 << i);
                if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies,
                                   dev_priv->hpd_stats[i].hpd_last_jiffies
                                   + msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) {
@@ -643,6 +895,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
                        dev_priv->hpd_stats[i].hpd_cnt = 0;
                } else if (dev_priv->hpd_stats[i].hpd_cnt > HPD_STORM_THRESHOLD) {
                        dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED;
+                       dev_priv->hpd_event_bits &= ~(1 << i);
                        DRM_DEBUG_KMS("HPD interrupt storm detected on PIN %d\n", i);
                        ret = true;
                } else {
@@ -669,6 +922,31 @@ static void dp_aux_irq_handler(struct drm_device *dev)
        wake_up_all(&dev_priv->gmbus_wait_queue);
 }
 
+/* Unlike gen6_queue_rps_work() 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)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev_priv->rps.lock, flags);
+       dev_priv->rps.pm_iir |= pm_iir & GEN6_PM_DEFERRED_EVENTS;
+       if (dev_priv->rps.pm_iir) {
+               I915_WRITE(GEN6_PMIMR, dev_priv->rps.pm_iir);
+               /* never want to mask useful interrupts. (also posting read) */
+               WARN_ON(I915_READ_NOTRACE(GEN6_PMIMR) & ~GEN6_PM_DEFERRED_EVENTS);
+               /* TODO: if queue_work is slow, move it out of the spinlock */
+               queue_work(dev_priv->wq, &dev_priv->rps.work);
+       }
+       spin_unlock_irqrestore(&dev_priv->rps.lock, flags);
+
+       if (pm_iir & ~GEN6_PM_DEFERRED_EVENTS)
+               DRM_ERROR("Unexpected PM interrupted\n");
+}
+
 static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -763,10 +1041,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
                        ibx_hpd_irq_setup(dev);
                queue_work(dev_priv->wq, &dev_priv->hotplug_work);
        }
-       if (pch_iir & SDE_AUDIO_POWER_MASK)
+       if (pch_iir & SDE_AUDIO_POWER_MASK) {
+               int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >>
+                              SDE_AUDIO_POWER_SHIFT);
                DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
-                                (pch_iir & SDE_AUDIO_POWER_MASK) >>
-                                SDE_AUDIO_POWER_SHIFT);
+                                port_name(port));
+       }
 
        if (pch_iir & SDE_AUX_MASK)
                dp_aux_irq_handler(dev);
@@ -795,10 +1075,64 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
        if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
                DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
 
-       if (pch_iir & SDE_TRANSB_FIFO_UNDER)
-               DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n");
        if (pch_iir & SDE_TRANSA_FIFO_UNDER)
-               DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+
+       if (pch_iir & SDE_TRANSB_FIFO_UNDER)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+}
+
+static void ivb_err_int_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 err_int = I915_READ(GEN7_ERR_INT);
+
+       if (err_int & ERR_INT_POISON)
+               DRM_ERROR("Poison interrupt\n");
+
+       if (err_int & ERR_INT_FIFO_UNDERRUN_A)
+               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
+                       DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
+
+       if (err_int & ERR_INT_FIFO_UNDERRUN_B)
+               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
+                       DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+
+       if (err_int & ERR_INT_FIFO_UNDERRUN_C)
+               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false))
+                       DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n");
+
+       I915_WRITE(GEN7_ERR_INT, err_int);
+}
+
+static void cpt_serr_int_handler(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 serr_int = I915_READ(SERR_INT);
+
+       if (serr_int & SERR_INT_POISON)
+               DRM_ERROR("PCH poison interrupt\n");
+
+       if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
+
+       if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
+
+       if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN)
+               if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C,
+                                                         false))
+                       DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n");
+
+       I915_WRITE(SERR_INT, serr_int);
 }
 
 static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
@@ -812,10 +1146,12 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
                        ibx_hpd_irq_setup(dev);
                queue_work(dev_priv->wq, &dev_priv->hotplug_work);
        }
-       if (pch_iir & SDE_AUDIO_POWER_MASK_CPT)
-               DRM_DEBUG_DRIVER("PCH audio power change on port %d\n",
-                                (pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
-                                SDE_AUDIO_POWER_SHIFT_CPT);
+       if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) {
+               int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >>
+                              SDE_AUDIO_POWER_SHIFT_CPT);
+               DRM_DEBUG_DRIVER("PCH audio power change on port %c\n",
+                                port_name(port));
+       }
 
        if (pch_iir & SDE_AUX_MASK_CPT)
                dp_aux_irq_handler(dev);
@@ -834,6 +1170,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
                        DRM_DEBUG_DRIVER("  pipe %c FDI IIR: 0x%08x\n",
                                         pipe_name(pipe),
                                         I915_READ(FDI_RX_IIR(pipe)));
+
+       if (pch_iir & SDE_ERROR_CPT)
+               cpt_serr_int_handler(dev);
 }
 
 static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
@@ -846,6 +1185,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        atomic_inc(&dev_priv->irq_received);
 
+       /* We get interrupts on unclaimed registers, so check for this before we
+        * do any I915_{READ,WRITE}. */
+       if (IS_HASWELL(dev) &&
+           (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
+               DRM_ERROR("Unclaimed register before interrupt\n");
+               I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
+       }
+
        /* disable master interrupt before clearing iir  */
        de_ier = I915_READ(DEIER);
        I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
@@ -861,6 +1208,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
                POSTING_READ(SDEIER);
        }
 
+       /* On Haswell, also mask ERR_INT because we don't want to risk
+        * generating "unclaimed register" interrupts from inside the interrupt
+        * handler. */
+       if (IS_HASWELL(dev))
+               ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
+
        gt_iir = I915_READ(GTIIR);
        if (gt_iir) {
                snb_gt_irq_handler(dev, dev_priv, gt_iir);
@@ -870,11 +1223,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        de_iir = I915_READ(DEIIR);
        if (de_iir) {
+               if (de_iir & DE_ERR_INT_IVB)
+                       ivb_err_int_handler(dev);
+
                if (de_iir & DE_AUX_CHANNEL_A_IVB)
                        dp_aux_irq_handler(dev);
 
                if (de_iir & DE_GSE_IVB)
-                       intel_opregion_gse_intr(dev);
+                       intel_opregion_asle_intr(dev);
 
                for (i = 0; i < 3; i++) {
                        if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
@@ -901,12 +1257,17 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
 
        pm_iir = I915_READ(GEN6_PMIIR);
        if (pm_iir) {
-               if (pm_iir & GEN6_PM_DEFERRED_EVENTS)
+               if (IS_HASWELL(dev))
+                       hsw_pm_irq_handler(dev_priv, pm_iir);
+               else if (pm_iir & GEN6_PM_DEFERRED_EVENTS)
                        gen6_queue_rps_work(dev_priv, pm_iir);
                I915_WRITE(GEN6_PMIIR, pm_iir);
                ret = IRQ_HANDLED;
        }
 
+       if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev))
+               ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
+
        I915_WRITE(DEIER, de_ier);
        POSTING_READ(DEIER);
        if (!HAS_PCH_NOP(dev)) {
@@ -968,7 +1329,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
                dp_aux_irq_handler(dev);
 
        if (de_iir & DE_GSE)
-               intel_opregion_gse_intr(dev);
+               intel_opregion_asle_intr(dev);
 
        if (de_iir & DE_PIPEA_VBLANK)
                drm_handle_vblank(dev, 0);
@@ -976,6 +1337,17 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
        if (de_iir & DE_PIPEB_VBLANK)
                drm_handle_vblank(dev, 1);
 
+       if (de_iir & DE_POISON)
+               DRM_ERROR("Poison interrupt\n");
+
+       if (de_iir & DE_PIPEA_FIFO_UNDERRUN)
+               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
+                       DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
+
+       if (de_iir & DE_PIPEB_FIFO_UNDERRUN)
+               if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
+                       DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
+
        if (de_iir & DE_PLANEA_FLIP_DONE) {
                intel_prepare_page_flip(dev, 0);
                intel_finish_page_flip_plane(dev, 0);
@@ -1222,11 +1594,13 @@ i915_error_state_free(struct kref *error_ref)
        for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
                i915_error_object_free(error->ring[i].batchbuffer);
                i915_error_object_free(error->ring[i].ringbuffer);
+               i915_error_object_free(error->ring[i].ctx);
                kfree(error->ring[i].requests);
        }
 
        kfree(error->active_bo);
        kfree(error->overlay);
+       kfree(error->display);
        kfree(error);
 }
 static void capture_bo(struct drm_i915_error_buffer *err,
@@ -1932,11 +2306,11 @@ ring_last_seqno(struct intel_ring_buffer *ring)
                          struct drm_i915_gem_request, list)->seqno;
 }
 
-static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err)
+static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring,
+                                    u32 ring_seqno, bool *err)
 {
        if (list_empty(&ring->request_list) ||
-           i915_seqno_passed(ring->get_seqno(ring, false),
-                             ring_last_seqno(ring))) {
+           i915_seqno_passed(ring_seqno, ring_last_seqno(ring))) {
                /* Issue a wake-up to catch stuck h/w. */
                if (waitqueue_active(&ring->irq_queue)) {
                        DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
@@ -2003,28 +2377,33 @@ static bool kick_ring(struct intel_ring_buffer *ring)
        return false;
 }
 
+static bool i915_hangcheck_ring_hung(struct intel_ring_buffer *ring)
+{
+       if (IS_GEN2(ring->dev))
+               return false;
+
+       /* Is the chip hanging on a WAIT_FOR_EVENT?
+        * If so we can simply poke the RB_WAIT bit
+        * and break the hang. This should work on
+        * all but the second generation chipsets.
+        */
+       return !kick_ring(ring);
+}
+
 static bool i915_hangcheck_hung(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
 
        if (dev_priv->gpu_error.hangcheck_count++ > 1) {
                bool hung = true;
+               struct intel_ring_buffer *ring;
+               int i;
 
                DRM_ERROR("Hangcheck timer elapsed... GPU hung\n");
                i915_handle_error(dev, true);
 
-               if (!IS_GEN2(dev)) {
-                       struct intel_ring_buffer *ring;
-                       int i;
-
-                       /* Is the chip hanging on a WAIT_FOR_EVENT?
-                        * If so we can simply poke the RB_WAIT bit
-                        * and break the hang. This should work on
-                        * all but the second generation chipsets.
-                        */
-                       for_each_ring(ring, dev_priv, i)
-                               hung &= !kick_ring(ring);
-               }
+               for_each_ring(ring, dev_priv, i)
+                       hung &= i915_hangcheck_ring_hung(ring);
 
                return hung;
        }
@@ -2042,19 +2421,19 @@ void i915_hangcheck_elapsed(unsigned long data)
 {
        struct drm_device *dev = (struct drm_device *)data;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       uint32_t acthd[I915_NUM_RINGS], instdone[I915_NUM_INSTDONE_REG];
        struct intel_ring_buffer *ring;
        bool err = false, idle;
        int i;
+       u32 seqno[I915_NUM_RINGS];
+       bool work_done;
 
        if (!i915_enable_hangcheck)
                return;
 
-       memset(acthd, 0, sizeof(acthd));
        idle = true;
        for_each_ring(ring, dev_priv, i) {
-           idle &= i915_hangcheck_ring_idle(ring, &err);
-           acthd[i] = intel_ring_get_active_head(ring);
+               seqno[i] = ring->get_seqno(ring, false);
+               idle &= i915_hangcheck_ring_idle(ring, seqno[i], &err);
        }
 
        /* If all work is done then ACTHD clearly hasn't advanced. */
@@ -2070,20 +2449,19 @@ void i915_hangcheck_elapsed(unsigned long data)
                return;
        }
 
-       i915_get_extra_instdone(dev, instdone);
-       if (memcmp(dev_priv->gpu_error.last_acthd, acthd,
-                  sizeof(acthd)) == 0 &&
-           memcmp(dev_priv->gpu_error.prev_instdone, instdone,
-                  sizeof(instdone)) == 0) {
+       work_done = false;
+       for_each_ring(ring, dev_priv, i) {
+               if (ring->hangcheck.seqno != seqno[i]) {
+                       work_done = true;
+                       ring->hangcheck.seqno = seqno[i];
+               }
+       }
+
+       if (!work_done) {
                if (i915_hangcheck_hung(dev))
                        return;
        } else {
                dev_priv->gpu_error.hangcheck_count = 0;
-
-               memcpy(dev_priv->gpu_error.last_acthd, acthd,
-                      sizeof(acthd));
-               memcpy(dev_priv->gpu_error.prev_instdone, instdone,
-                      sizeof(instdone));
        }
 
 repeat:
@@ -2113,6 +2491,37 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
        I915_WRITE(GTIER, 0x0);
        POSTING_READ(GTIER);
 
+       /* south display irq */
+       I915_WRITE(SDEIMR, 0xffffffff);
+       /*
+        * SDEIER is also touched by the interrupt handler to work around missed
+        * PCH interrupts. Hence we can't update it after the interrupt handler
+        * is enabled - instead we unconditionally enable all PCH interrupt
+        * sources here, but then only unmask them as needed with SDEIMR.
+        */
+       I915_WRITE(SDEIER, 0xffffffff);
+       POSTING_READ(SDEIER);
+}
+
+static void ivybridge_irq_preinstall(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       atomic_set(&dev_priv->irq_received, 0);
+
+       I915_WRITE(HWSTAM, 0xeffe);
+
+       /* XXX hotplug from PCH */
+
+       I915_WRITE(DEIMR, 0xffffffff);
+       I915_WRITE(DEIER, 0x0);
+       POSTING_READ(DEIER);
+
+       /* and GT */
+       I915_WRITE(GTIMR, 0xffffffff);
+       I915_WRITE(GTIER, 0x0);
+       POSTING_READ(GTIER);
+
        if (HAS_PCH_NOP(dev))
                return;
 
@@ -2201,14 +2610,18 @@ static void ibx_irq_postinstall(struct drm_device *dev)
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        u32 mask;
 
-       if (HAS_PCH_IBX(dev))
-               mask = SDE_GMBUS | SDE_AUX_MASK;
-       else
-               mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
-
        if (HAS_PCH_NOP(dev))
                return;
 
+       if (HAS_PCH_IBX(dev)) {
+               mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER |
+                      SDE_TRANSA_FIFO_UNDER | SDE_POISON;
+       } else {
+               mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT;
+
+               I915_WRITE(SERR_INT, I915_READ(SERR_INT));
+       }
+
        I915_WRITE(SDEIIR, I915_READ(SDEIIR));
        I915_WRITE(SDEIMR, ~mask);
 }
@@ -2219,7 +2632,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        /* enable kind of interrupts always enabled */
        u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
                           DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
-                          DE_AUX_CHANNEL_A;
+                          DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
+                          DE_PIPEA_FIFO_UNDERRUN | DE_POISON;
        u32 render_irqs;
 
        dev_priv->irq_mask = ~display_mask;
@@ -2269,12 +2683,14 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
                DE_PLANEC_FLIP_DONE_IVB |
                DE_PLANEB_FLIP_DONE_IVB |
                DE_PLANEA_FLIP_DONE_IVB |
-               DE_AUX_CHANNEL_A_IVB;
+               DE_AUX_CHANNEL_A_IVB |
+               DE_ERR_INT_IVB;
        u32 render_irqs;
 
        dev_priv->irq_mask = ~display_mask;
 
        /* should always can generate irq */
+       I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
        I915_WRITE(DEIIR, I915_READ(DEIIR));
        I915_WRITE(DEIMR, dev_priv->irq_mask);
        I915_WRITE(DEIER,
@@ -2305,7 +2721,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
        u32 enable_mask;
        u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV;
        u32 render_irqs;
-       u16 msid;
 
        enable_mask = I915_DISPLAY_PORT_INTERRUPT;
        enable_mask |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
@@ -2321,13 +2736,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev)
                I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT |
                I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
 
-       /* Hack for broken MSIs on VLV */
-       pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000);
-       pci_read_config_word(dev->pdev, 0x98, &msid);
-       msid &= 0xff; /* mask out delivery bits */
-       msid |= (1<<14);
-       pci_write_config_word(dev_priv->dev->pdev, 0x98, msid);
-
        I915_WRITE(PORT_HOTPLUG_EN, 0);
        POSTING_READ(PORT_HOTPLUG_EN);
 
@@ -2402,6 +2810,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
        I915_WRITE(DEIMR, 0xffffffff);
        I915_WRITE(DEIER, 0x0);
        I915_WRITE(DEIIR, I915_READ(DEIIR));
+       if (IS_GEN7(dev))
+               I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
 
        I915_WRITE(GTIMR, 0xffffffff);
        I915_WRITE(GTIER, 0x0);
@@ -2413,6 +2823,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
        I915_WRITE(SDEIMR, 0xffffffff);
        I915_WRITE(SDEIER, 0x0);
        I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
+               I915_WRITE(SERR_INT, I915_READ(SERR_INT));
 }
 
 static void i8xx_irq_preinstall(struct drm_device * dev)
@@ -2626,7 +3038,7 @@ static int i915_irq_postinstall(struct drm_device *dev)
        I915_WRITE(IER, enable_mask);
        POSTING_READ(IER);
 
-       intel_opregion_enable_asle(dev);
+       i915_enable_asle_pipestat(dev);
 
        return 0;
 }
@@ -2860,7 +3272,7 @@ static int i965_irq_postinstall(struct drm_device *dev)
        I915_WRITE(PORT_HOTPLUG_EN, 0);
        POSTING_READ(PORT_HOTPLUG_EN);
 
-       intel_opregion_enable_asle(dev);
+       i915_enable_asle_pipestat(dev);
 
        return 0;
 }
@@ -3113,9 +3525,9 @@ void intel_irq_init(struct drm_device *dev)
                dev->driver->disable_vblank = valleyview_disable_vblank;
                dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
        } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
-               /* Share pre & uninstall handlers with ILK/SNB */
+               /* Share uninstall handlers with ILK/SNB */
                dev->driver->irq_handler = ivybridge_irq_handler;
-               dev->driver->irq_preinstall = ironlake_irq_preinstall;
+               dev->driver->irq_preinstall = ivybridge_irq_preinstall;
                dev->driver->irq_postinstall = ivybridge_irq_postinstall;
                dev->driver->irq_uninstall = ironlake_irq_uninstall;
                dev->driver->enable_vblank = ivybridge_enable_vblank;
This page took 0.075294 seconds and 5 git commands to generate.