drm/i915: disable PCH ports if needed when disabling a CRTC
authorJesse Barnes <jbarnes@virtuousgeek.org>
Mon, 7 Feb 2011 21:46:40 +0000 (13:46 -0800)
committerChris Wilson <chris@chris-wilson.co.uk>
Fri, 11 Feb 2011 20:32:44 +0000 (20:32 +0000)
Disable any PCH ports associated with a pipe when disabling it.  This
should prevent transcoder disable failures due to ports still being on.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
[ickle: introduce *_PIPE_ENABLED() macro]
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_display.c

index 6bd9659..8ecf5db 100644 (file)
 #define   LVDS_PORT_EN                 (1 << 31)
 /* Selects pipe B for LVDS data.  Must be set on pre-965. */
 #define   LVDS_PIPEB_SELECT            (1 << 30)
+#define   LVDS_PIPE_MASK               (1 << 30)
 /* LVDS dithering flag on 965/g4x platform */
 #define   LVDS_ENABLE_DITHER           (1 << 25)
 /* LVDS sync polarity flags. Set to invert (i.e. negative) */
 #define   LVDS_B0B3_POWER_DOWN         (0 << 2)
 #define   LVDS_B0B3_POWER_UP           (3 << 2)
 
+#define LVDS_PIPE_ENABLED(V, P) \
+       (((V) & (LVDS_PIPE_MASK | LVDS_PORT_EN)) == ((P) << 30 | LVDS_PORT_EN))
+
 /* Video Data Island Packet control */
 #define VIDEO_DIP_DATA         0x61178
 #define VIDEO_DIP_CTL          0x61170
 
 #define   DP_PORT_EN                   (1 << 31)
 #define   DP_PIPEB_SELECT              (1 << 30)
+#define   DP_PIPE_MASK                 (1 << 30)
+
+#define DP_PIPE_ENABLED(V, P) \
+       (((V) & (DP_PIPE_MASK | DP_PORT_EN)) == ((P) << 30 | DP_PORT_EN))
 
 /* Link training mode - select a suitable mode for each stage */
 #define   DP_LINK_TRAIN_PAT_1          (0 << 28)
 #define  ADPA_CRT_HOTPLUG_VOLREF_475MV  (1<<17)
 #define  ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
 
+#define ADPA_PIPE_ENABLED(V, P) \
+       (((V) & (ADPA_TRANS_SELECT_MASK | ADPA_DAC_ENABLE)) == ((P) << 30 | ADPA_DAC_ENABLE))
+
 /* or SDVOB */
 #define HDMIB   0xe1140
 #define  PORT_ENABLE    (1 << 31)
 #define  TRANSCODER_A   (0)
 #define  TRANSCODER_B   (1 << 30)
+#define  TRANSCODER_MASK   (1 << 30)
 #define  COLOR_FORMAT_8bpc      (0)
 #define  COLOR_FORMAT_12bpc     (3 << 26)
 #define  SDVOB_HOTPLUG_ENABLE   (1 << 23)
 #define  HSYNC_ACTIVE_HIGH      (1 << 3)
 #define  PORT_DETECTED          (1 << 2)
 
+#define HDMI_PIPE_ENABLED(V, P) \
+       (((V) & (TRANSCODER_MASK | PORT_ENABLE)) == ((P) << 30 | PORT_ENABLE))
+
 /* PCH SDVOB multiplex with HDMIB */
 #define PCH_SDVOB      HDMIB
 
index 37765e0..42c64cb 100644 (file)
@@ -1270,12 +1270,8 @@ static void assert_transcoder_disabled(struct drm_i915_private *dev_priv,
 static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv,
                                   enum pipe pipe, int reg)
 {
-       u32 val;
-       u32 sel_pipe;
-
-       val = I915_READ(reg);
-       sel_pipe = (val & DP_PIPEB_SELECT) >> 30;
-       WARN((val & DP_PORT_EN) && sel_pipe == pipe,
+       u32 val = I915_READ(reg);
+       WARN(DP_PIPE_ENABLED(val, pipe),
             "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
             reg, pipe_name(pipe));
 }
@@ -1283,12 +1279,8 @@ static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv,
 static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv,
                                     enum pipe pipe, int reg)
 {
-       u32 val;
-       u32 sel_pipe;
-
-       val = I915_READ(reg);
-       sel_pipe = (val & TRANSCODER_B) >> 30;
-       WARN((val & PORT_ENABLE) && sel_pipe == pipe,
+       u32 val = I915_READ(reg);
+       WARN(HDMI_PIPE_ENABLED(val, pipe),
             "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
             reg, pipe_name(pipe));
 }
@@ -1298,7 +1290,6 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
 {
        int reg;
        u32 val;
-       u32 sel_pipe;
 
        assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_B);
        assert_pch_dp_disabled(dev_priv, pipe, PCH_DP_C);
@@ -1306,15 +1297,13 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
 
        reg = PCH_ADPA;
        val = I915_READ(reg);
-       sel_pipe = (val & ADPA_TRANS_B_SELECT) >> 30;
-       WARN(sel_pipe == pipe && (val & ADPA_DAC_ENABLE),
+       WARN(ADPA_PIPE_ENABLED(val, pipe),
             "PCH VGA enabled on transcoder %c, should be disabled\n",
             pipe_name(pipe));
 
        reg = PCH_LVDS;
        val = I915_READ(reg);
-       sel_pipe = (val & LVDS_PIPEB_SELECT) >> 30;
-       WARN(sel_pipe == pipe && (val & LVDS_PORT_EN),
+       WARN(LVDS_PIPE_ENABLED(val, pipe),
             "PCH LVDS enabled on transcoder %c, should be disabled\n",
             pipe_name(pipe));
 
@@ -1628,6 +1617,53 @@ static void intel_disable_plane(struct drm_i915_private *dev_priv,
        intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
+static void disable_pch_dp(struct drm_i915_private *dev_priv,
+                          enum pipe pipe, int reg)
+{
+       u32 val = I915_READ(reg);
+       if (DP_PIPE_ENABLED(val, pipe))
+               I915_WRITE(reg, val & ~DP_PORT_EN);
+}
+
+static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
+                            enum pipe pipe, int reg)
+{
+       u32 val = I915_READ(reg);
+       if (HDMI_PIPE_ENABLED(val, pipe))
+               I915_WRITE(reg, val & ~PORT_ENABLE);
+}
+
+/* Disable any ports connected to this transcoder */
+static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
+                                   enum pipe pipe)
+{
+       u32 reg, val;
+
+       val = I915_READ(PCH_PP_CONTROL);
+       I915_WRITE(PCH_PP_CONTROL, val | PANEL_UNLOCK_REGS);
+
+       disable_pch_dp(dev_priv, pipe, PCH_DP_B);
+       disable_pch_dp(dev_priv, pipe, PCH_DP_C);
+       disable_pch_dp(dev_priv, pipe, PCH_DP_D);
+
+       reg = PCH_ADPA;
+       val = I915_READ(reg);
+       if (ADPA_PIPE_ENABLED(val, pipe))
+               I915_WRITE(reg, val & ~ADPA_DAC_ENABLE);
+
+       reg = PCH_LVDS;
+       val = I915_READ(reg);
+       if (LVDS_PIPE_ENABLED(val, pipe)) {
+               I915_WRITE(reg, val & ~LVDS_PORT_EN);
+               POSTING_READ(reg);
+               udelay(100);
+       }
+
+       disable_pch_hdmi(dev_priv, pipe, HDMIB);
+       disable_pch_hdmi(dev_priv, pipe, HDMIC);
+       disable_pch_hdmi(dev_priv, pipe, HDMID);
+}
+
 static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 {
        struct drm_device *dev = crtc->dev;
@@ -2864,14 +2900,12 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
        ironlake_fdi_disable(crtc);
 
-       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               temp = I915_READ(PCH_LVDS);
-               if (temp & LVDS_PORT_EN) {
-                       I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
-                       POSTING_READ(PCH_LVDS);
-                       udelay(100);
-               }
-       }
+       /* This is a horrible layering violation; we should be doing this in
+        * the connector/encoder ->prepare instead, but we don't always have
+        * enough information there about the config to know whether it will
+        * actually be necessary or just cause undesired flicker.
+        */
+       intel_disable_pch_ports(dev_priv, pipe);
 
        intel_disable_transcoder(dev_priv, pipe);