drm/i915: Check for FIFO underruns after modeset on IVB/HSW and CPT/PPT
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Fri, 30 Oct 2015 17:22:21 +0000 (19:22 +0200)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Tue, 10 Nov 2015 14:23:17 +0000 (16:23 +0200)
Due to the shared error interrupt on IVB/HSW and CPT/PPT we may not
always get an interrupt on a FIFO underrun. But we can always do an
explicit check (like we do on GMCH platforms that have no underrun
interrupt).

v2: Drop stale kerneldoc for i9xx_check_fifo_underruns() (Daniel)

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/1446225741-11070-1-git-send-email-ville.syrjala@linux.intel.com
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fifo_underrun.c

index f859f86c18b02ab8e7645a0032b2967846287e96..5046b99da459982f8630454f1752bb5803cd252d 100644 (file)
@@ -4688,9 +4688,9 @@ intel_post_enable_primary(struct drm_crtc *crtc)
        if (IS_GEN2(dev))
                intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
 
-       /* Underruns don't raise interrupts, so check manually. */
-       if (HAS_GMCH_DISPLAY(dev))
-               i9xx_check_fifo_underruns(dev_priv);
+       /* Underruns don't always raise interrupts, so check manually. */
+       intel_check_cpu_fifo_underruns(dev_priv);
+       intel_check_pch_fifo_underruns(dev_priv);
 }
 
 /**
index 0f8fd140b6327b8c9f8c16e9701a4ceba65ca182..d45677436170357e098929d75f91c2ac281e0f71 100644 (file)
@@ -965,7 +965,8 @@ void intel_cpu_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv,
                                         enum pipe pipe);
 void intel_pch_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv,
                                         enum transcoder pch_transcoder);
-void i9xx_check_fifo_underruns(struct drm_i915_private *dev_priv);
+void intel_check_cpu_fifo_underruns(struct drm_i915_private *dev_priv);
+void intel_check_pch_fifo_underruns(struct drm_i915_private *dev_priv);
 
 /* i915_irq.c */
 void gen5_enable_gt_irq(struct drm_i915_private *dev_priv, uint32_t mask);
index 54daa66c697077f5b0609ddb5eec0d7989db780a..af906c7db5c337164773197ad93d86429b12442c 100644 (file)
@@ -84,38 +84,21 @@ static bool cpt_can_enable_serr_int(struct drm_device *dev)
        return true;
 }
 
-/**
- * i9xx_check_fifo_underruns - check for fifo underruns
- * @dev_priv: i915 device instance
- *
- * This function checks for fifo underruns on GMCH platforms. This needs to be
- * done manually on modeset to make sure that we catch all underruns since they
- * do not generate an interrupt by themselves on these platforms.
- */
-void i9xx_check_fifo_underruns(struct drm_i915_private *dev_priv)
+static void i9xx_check_fifo_underruns(struct intel_crtc *crtc)
 {
-       struct intel_crtc *crtc;
-
-       spin_lock_irq(&dev_priv->irq_lock);
-
-       for_each_intel_crtc(dev_priv->dev, crtc) {
-               u32 reg = PIPESTAT(crtc->pipe);
-               u32 pipestat;
-
-               if (crtc->cpu_fifo_underrun_disabled)
-                       continue;
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       u32 reg = PIPESTAT(crtc->pipe);
+       u32 pipestat = I915_READ(reg) & 0xffff0000;
 
-               pipestat = I915_READ(reg) & 0xffff0000;
-               if ((pipestat & PIPE_FIFO_UNDERRUN_STATUS) == 0)
-                       continue;
+       assert_spin_locked(&dev_priv->irq_lock);
 
-               I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
-               POSTING_READ(reg);
+       if ((pipestat & PIPE_FIFO_UNDERRUN_STATUS) == 0)
+               return;
 
-               DRM_ERROR("pipe %c underrun\n", pipe_name(crtc->pipe));
-       }
+       I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS);
+       POSTING_READ(reg);
 
-       spin_unlock_irq(&dev_priv->irq_lock);
+       DRM_ERROR("pipe %c underrun\n", pipe_name(crtc->pipe));
 }
 
 static void i9xx_set_fifo_underrun_reporting(struct drm_device *dev,
@@ -150,6 +133,23 @@ static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
                ironlake_disable_display_irq(dev_priv, bit);
 }
 
+static void ivybridge_check_fifo_underruns(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum pipe pipe = crtc->pipe;
+       uint32_t err_int = I915_READ(GEN7_ERR_INT);
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       if ((err_int & ERR_INT_FIFO_UNDERRUN(pipe)) == 0)
+               return;
+
+       I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN(pipe));
+       POSTING_READ(GEN7_ERR_INT);
+
+       DRM_ERROR("fifo underrun on pipe %c\n", pipe_name(pipe));
+}
+
 static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
                                                  enum pipe pipe,
                                                  bool enable, bool old)
@@ -202,6 +202,24 @@ static void ibx_set_fifo_underrun_reporting(struct drm_device *dev,
                ibx_disable_display_interrupt(dev_priv, bit);
 }
 
+static void cpt_check_pch_fifo_underruns(struct intel_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       enum transcoder pch_transcoder = (enum transcoder) crtc->pipe;
+       uint32_t serr_int = I915_READ(SERR_INT);
+
+       assert_spin_locked(&dev_priv->irq_lock);
+
+       if ((serr_int & SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)) == 0)
+               return;
+
+       I915_WRITE(SERR_INT, SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder));
+       POSTING_READ(SERR_INT);
+
+       DRM_ERROR("pch fifo underrun on pch transcoder %c\n",
+                 transcoder_name(pch_transcoder));
+}
+
 static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
                                            enum transcoder pch_transcoder,
                                            bool enable, bool old)
@@ -375,3 +393,56 @@ void intel_pch_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv,
                DRM_ERROR("PCH transcoder %c FIFO underrun\n",
                          transcoder_name(pch_transcoder));
 }
+
+/**
+ * intel_check_cpu_fifo_underruns - check for CPU fifo underruns immediately
+ * @dev_priv: i915 device instance
+ *
+ * Check for CPU fifo underruns immediately. Useful on IVB/HSW where the shared
+ * error interrupt may have been disabled, and so CPU fifo underruns won't
+ * necessarily raise an interrupt, and on GMCH platforms where underruns never
+ * raise an interrupt.
+ */
+void intel_check_cpu_fifo_underruns(struct drm_i915_private *dev_priv)
+{
+       struct intel_crtc *crtc;
+
+       spin_lock_irq(&dev_priv->irq_lock);
+
+       for_each_intel_crtc(dev_priv->dev, crtc) {
+               if (crtc->cpu_fifo_underrun_disabled)
+                       continue;
+
+               if (HAS_GMCH_DISPLAY(dev_priv))
+                       i9xx_check_fifo_underruns(crtc);
+               else if (IS_GEN7(dev_priv))
+                       ivybridge_check_fifo_underruns(crtc);
+       }
+
+       spin_unlock_irq(&dev_priv->irq_lock);
+}
+
+/**
+ * intel_check_pch_fifo_underruns - check for PCH fifo underruns immediately
+ * @dev_priv: i915 device instance
+ *
+ * Check for PCH fifo underruns immediately. Useful on CPT/PPT where the shared
+ * error interrupt may have been disabled, and so PCH fifo underruns won't
+ * necessarily raise an interrupt.
+ */
+void intel_check_pch_fifo_underruns(struct drm_i915_private *dev_priv)
+{
+       struct intel_crtc *crtc;
+
+       spin_lock_irq(&dev_priv->irq_lock);
+
+       for_each_intel_crtc(dev_priv->dev, crtc) {
+               if (crtc->pch_fifo_underrun_disabled)
+                       continue;
+
+               if (HAS_PCH_CPT(dev_priv))
+                       cpt_check_pch_fifo_underruns(crtc);
+       }
+
+       spin_unlock_irq(&dev_priv->irq_lock);
+}