drm/i915: add transcoder enable/disable functions
authorJesse Barnes <jbarnes@virtuousgeek.org>
Mon, 3 Jan 2011 20:14:26 +0000 (12:14 -0800)
committerChris Wilson <chris@chris-wilson.co.uk>
Wed, 19 Jan 2011 12:37:18 +0000 (12:37 +0000)
Along with assertion checks for the FDI transmitters and receivers
(including PLLs).  Modify the pipe enable function to check for FDI PLL
status as well, when driving PCH ports.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/intel_display.c

index f274d5b..dc5168e 100644 (file)
@@ -1081,6 +1081,84 @@ static void assert_pll(struct drm_i915_private *dev_priv,
 #define assert_pll_enabled(d, p) assert_pll(d, p, true)
 #define assert_pll_disabled(d, p) assert_pll(d, p, false)
 
+/* For ILK+ */
+static void assert_pch_pll(struct drm_i915_private *dev_priv,
+                          enum pipe pipe, bool state)
+{
+       int reg;
+       u32 val;
+       bool cur_state;
+
+       reg = PCH_DPLL(pipe);
+       val = I915_READ(reg);
+       cur_state = !!(val & DPLL_VCO_ENABLE);
+       WARN(cur_state != state,
+            "PCH PLL state assertion failure (expected %s, current %s)\n",
+            state_string(state), state_string(cur_state));
+}
+#define assert_pch_pll_enabled(d, p) assert_pch_pll(d, p, true)
+#define assert_pch_pll_disabled(d, p) assert_pch_pll(d, p, false)
+
+static void assert_fdi_tx(struct drm_i915_private *dev_priv,
+                         enum pipe pipe, bool state)
+{
+       int reg;
+       u32 val;
+       bool cur_state;
+
+       reg = FDI_TX_CTL(pipe);
+       val = I915_READ(reg);
+       cur_state = !!(val & FDI_TX_ENABLE);
+       WARN(cur_state != state,
+            "FDI TX state assertion failure (expected %s, current %s)\n",
+            state_string(state), state_string(cur_state));
+}
+#define assert_fdi_tx_enabled(d, p) assert_fdi_tx(d, p, true)
+#define assert_fdi_tx_disabled(d, p) assert_fdi_tx(d, p, false)
+
+static void assert_fdi_rx(struct drm_i915_private *dev_priv,
+                         enum pipe pipe, bool state)
+{
+       int reg;
+       u32 val;
+       bool cur_state;
+
+       reg = FDI_RX_CTL(pipe);
+       val = I915_READ(reg);
+       cur_state = !!(val & FDI_RX_ENABLE);
+       WARN(cur_state != state,
+            "FDI RX state assertion failure (expected %s, current %s)\n",
+            state_string(state), state_string(cur_state));
+}
+#define assert_fdi_rx_enabled(d, p) assert_fdi_rx(d, p, true)
+#define assert_fdi_rx_disabled(d, p) assert_fdi_rx(d, p, false)
+
+static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv,
+                                     enum pipe pipe)
+{
+       int reg;
+       u32 val;
+
+       /* ILK FDI PLL is always enabled */
+       if (dev_priv->info->gen == 5)
+               return;
+
+       reg = FDI_TX_CTL(pipe);
+       val = I915_READ(reg);
+       WARN(!(val & FDI_TX_PLL_ENABLE), "FDI TX PLL assertion failure, should be active but is disabled\n");
+}
+
+static void assert_fdi_rx_pll_enabled(struct drm_i915_private *dev_priv,
+                                     enum pipe pipe)
+{
+       int reg;
+       u32 val;
+
+       reg = FDI_RX_CTL(pipe);
+       val = I915_READ(reg);
+       WARN(!(val & FDI_RX_PLL_ENABLE), "FDI RX PLL assertion failure, should be active but is disabled\n");
+}
+
 static void assert_panel_unlocked(struct drm_i915_private *dev_priv,
                                  enum pipe pipe)
 {
@@ -1298,10 +1376,59 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv,
        udelay(200);
 }
 
+static void intel_enable_transcoder(struct drm_i915_private *dev_priv,
+                                   enum pipe pipe)
+{
+       int reg;
+       u32 val;
+
+       /* PCH only available on ILK+ */
+       BUG_ON(dev_priv->info->gen < 5);
+
+       /* Make sure PCH DPLL is enabled */
+       assert_pch_pll_enabled(dev_priv, pipe);
+
+       /* FDI must be feeding us bits for PCH ports */
+       assert_fdi_tx_enabled(dev_priv, pipe);
+       assert_fdi_rx_enabled(dev_priv, pipe);
+
+       reg = TRANSCONF(pipe);
+       val = I915_READ(reg);
+       /*
+        * make the BPC in transcoder be consistent with
+        * that in pipeconf reg.
+        */
+       val &= ~PIPE_BPC_MASK;
+       val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+       I915_WRITE(reg, val | TRANS_ENABLE);
+       if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
+               DRM_ERROR("failed to enable transcoder %d\n", pipe);
+}
+
+static void intel_disable_transcoder(struct drm_i915_private *dev_priv,
+                                    enum pipe pipe)
+{
+       int reg;
+       u32 val;
+
+       /* FDI relies on the transcoder */
+       assert_fdi_tx_disabled(dev_priv, pipe);
+       assert_fdi_rx_disabled(dev_priv, pipe);
+
+       reg = TRANSCONF(pipe);
+       val = I915_READ(reg);
+       val &= ~TRANS_ENABLE;
+       I915_WRITE(reg, val);
+       /* wait for PCH transcoder off, transcoder state */
+       if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
+               DRM_ERROR("failed to disable transcoder\n");
+}
+
 /**
  * intel_enable_pipe - enable a pipe, assertiing requirements
  * @dev_priv: i915 private structure
  * @pipe: pipe to enable
+ * @pch_port: on ILK+, is this pipe driving a PCH port or not
  *
  * Enable @pipe, making sure that various hardware specific requirements
  * are met, if applicable, e.g. PLL enabled, LVDS pairs enabled, etc.
@@ -1311,7 +1438,8 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv,
  * Will wait until the pipe is actually running (i.e. first vblank) before
  * returning.
  */
-static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
+static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
+                             bool pch_port)
 {
        int reg;
        u32 val;
@@ -1323,6 +1451,14 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
         */
        if (!HAS_PCH_SPLIT(dev_priv->dev))
                assert_pll_enabled(dev_priv, pipe);
+       else {
+               if (pch_port) {
+                       /* if driving the PCH, we need FDI enabled */
+                       assert_fdi_rx_pll_enabled(dev_priv, pipe);
+                       assert_fdi_tx_pll_enabled(dev_priv, pipe);
+               }
+               /* FIXME: assert CPU port conditions for SNB+ */
+       }
 
        reg = PIPECONF(pipe);
        val = I915_READ(reg);
@@ -2385,6 +2521,31 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
                   atomic_read(&obj->pending_flip) == 0);
 }
 
+static bool intel_crtc_driving_pch(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct intel_encoder *encoder;
+
+       /*
+        * If there's a non-PCH eDP on this crtc, it must be DP_A, and that
+        * must be driven by its own crtc; no sharing is possible.
+        */
+       list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+               if (encoder->base.crtc != crtc)
+                       continue;
+
+               switch (encoder->type) {
+               case INTEL_OUTPUT_EDP:
+                       if (!intel_encoder_is_pch_edp(&encoder->base))
+                               return false;
+                       continue;
+               }
+       }
+
+       return true;
+}
+
 static void ironlake_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -2393,6 +2554,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        u32 reg, temp;
+       bool is_pch_port;
 
        if (intel_crtc->active)
                return;
@@ -2423,7 +2585,9 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                           dev_priv->pch_pf_size);
        }
 
-       intel_enable_pipe(dev_priv, pipe);
+       is_pch_port = intel_crtc_driving_pch(crtc);
+
+       intel_enable_pipe(dev_priv, pipe, is_pch_port);
        intel_enable_plane(dev_priv, plane, pipe);
 
        /* For PCH output, training FDI link */
@@ -2492,18 +2656,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                I915_WRITE(reg, temp);
        }
 
-       /* enable PCH transcoder */
-       reg = TRANSCONF(pipe);
-       temp = I915_READ(reg);
-       /*
-        * make the BPC in transcoder be consistent with
-        * that in pipeconf reg.
-        */
-       temp &= ~PIPE_BPC_MASK;
-       temp |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
-       I915_WRITE(reg, temp | TRANS_ENABLE);
-       if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
-               DRM_ERROR("failed to enable transcoder %d\n", pipe);
+       intel_enable_transcoder(dev_priv, pipe);
 
        intel_crtc_load_lut(crtc);
        intel_update_fbc(dev);
@@ -2592,15 +2745,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
                }
        }
 
-       /* disable PCH transcoder */
-       reg = TRANSCONF(plane);
-       temp = I915_READ(reg);
-       if (temp & TRANS_ENABLE) {
-               I915_WRITE(reg, temp & ~TRANS_ENABLE);
-               /* wait for PCH transcoder off, transcoder state */
-               if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
-                       DRM_ERROR("failed to disable transcoder\n");
-       }
+       intel_disable_transcoder(dev_priv, pipe);
 
        if (HAS_PCH_CPT(dev)) {
                /* disable TRANS_DP_CTL */
@@ -2702,7 +2847,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        intel_update_watermarks(dev);
 
        intel_enable_pll(dev_priv, pipe);
-       intel_enable_pipe(dev_priv, pipe);
+       intel_enable_pipe(dev_priv, pipe, false);
        intel_enable_plane(dev_priv, plane, pipe);
 
        intel_crtc_load_lut(crtc);
@@ -4688,7 +4833,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        I915_WRITE(PIPECONF(pipe), pipeconf);
        POSTING_READ(PIPECONF(pipe));
        if (!HAS_PCH_SPLIT(dev))
-               intel_enable_pipe(dev_priv, pipe);
+               intel_enable_pipe(dev_priv, pipe, false);
 
        intel_wait_for_vblank(dev, pipe);