drm/i915: Fix the IBX transcoder B workarounds
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Tue, 5 May 2015 14:17:34 +0000 (17:17 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 21 May 2015 21:07:45 +0000 (23:07 +0200)
Currently the IBX transcoder B workarounds are not working correctly.
Well, the HDMI one seems to be working somewhat, but the DP one is
definitely busted.

After a bit of experimentation it looks like the best way to make this
work is first disable the port on transcoder B, and then re-enable it
transcoder A, and immediately disable it again.

We can also clean up the code by noting that we can't be called without
a valid crtc. And also note that port A on ILK does not need the
workaround, so let's check for that one too.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_sdvo.c

index d86e1f95166609d331724544523c4be01c780565..abd442af41272c30b1e7719a32ca53c49a50760b 100644 (file)
@@ -3843,6 +3843,7 @@ static void
 intel_dp_link_down(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
        enum port port = intel_dig_port->port;
        struct drm_device *dev = intel_dig_port->base.base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3859,34 +3860,38 @@ intel_dp_link_down(struct intel_dp *intel_dp)
        if ((IS_GEN7(dev) && port == PORT_A) ||
            (HAS_PCH_CPT(dev) && port != PORT_A)) {
                DP &= ~DP_LINK_TRAIN_MASK_CPT;
-               I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
+               DP |= DP_LINK_TRAIN_PAT_IDLE_CPT;
        } else {
                if (IS_CHERRYVIEW(dev))
                        DP &= ~DP_LINK_TRAIN_MASK_CHV;
                else
                        DP &= ~DP_LINK_TRAIN_MASK;
-               I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
+               DP |= DP_LINK_TRAIN_PAT_IDLE;
        }
+       I915_WRITE(intel_dp->output_reg, DP);
        POSTING_READ(intel_dp->output_reg);
 
-       if (HAS_PCH_IBX(dev) &&
-           I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
-               /* Hardware workaround: leaving our transcoder select
-                * set to transcoder B while it's off will prevent the
-                * corresponding HDMI output on transcoder A.
-                *
-                * Combine this with another hardware workaround:
-                * transcoder select bit can only be cleared while the
-                * port is enabled.
-                */
-               DP &= ~DP_PIPEB_SELECT;
+       DP &= ~(DP_PORT_EN | DP_AUDIO_OUTPUT_ENABLE);
+       I915_WRITE(intel_dp->output_reg, DP);
+       POSTING_READ(intel_dp->output_reg);
+
+       /*
+        * HW workaround for IBX, we need to move the port
+        * to transcoder A after disabling it to allow the
+        * matching HDMI port to be enabled on transcoder A.
+        */
+       if (HAS_PCH_IBX(dev) && crtc->pipe == PIPE_B && port != PORT_A) {
+               /* always enable with pattern 1 (as per spec) */
+               DP &= ~(DP_PIPEB_SELECT | DP_LINK_TRAIN_MASK);
+               DP |= DP_PORT_EN | DP_LINK_TRAIN_PAT_1;
+               I915_WRITE(intel_dp->output_reg, DP);
+               POSTING_READ(intel_dp->output_reg);
+
+               DP &= ~DP_PORT_EN;
                I915_WRITE(intel_dp->output_reg, DP);
                POSTING_READ(intel_dp->output_reg);
        }
 
-       DP &= ~DP_AUDIO_OUTPUT_ENABLE;
-       I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
-       POSTING_READ(intel_dp->output_reg);
        msleep(intel_dp->panel_power_down_delay);
 }
 
index d148e09db901333a8b3472d0f6610ee647889b77..fb2dc84e1061e7aaf3dad9bdbe3b62929fdfd809 100644 (file)
@@ -873,41 +873,37 @@ static void intel_disable_hdmi(struct intel_encoder *encoder)
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        u32 temp;
-       u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
 
        if (crtc->config->has_audio)
                intel_audio_codec_disable(encoder);
 
        temp = I915_READ(intel_hdmi->hdmi_reg);
 
-       /* HW workaround for IBX, we need to move the port to transcoder A
-        * before disabling it. */
-       if (HAS_PCH_IBX(dev)) {
-               struct drm_crtc *crtc = encoder->base.crtc;
-               int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
-
-               if (temp & SDVO_PIPE_B_SELECT) {
-                       temp &= ~SDVO_PIPE_B_SELECT;
-                       I915_WRITE(intel_hdmi->hdmi_reg, temp);
-                       POSTING_READ(intel_hdmi->hdmi_reg);
-
-                       /* Again we need to write this twice. */
-                       I915_WRITE(intel_hdmi->hdmi_reg, temp);
-                       POSTING_READ(intel_hdmi->hdmi_reg);
-
-                       /* Transcoder selection bits only update
-                        * effectively on vblank. */
-                       if (crtc)
-                               intel_wait_for_vblank(dev, pipe);
-                       else
-                               msleep(50);
-               }
-       }
-
-       temp &= ~enable_bits;
-
+       temp &= ~(SDVO_ENABLE | SDVO_AUDIO_ENABLE);
        I915_WRITE(intel_hdmi->hdmi_reg, temp);
        POSTING_READ(intel_hdmi->hdmi_reg);
+
+       /*
+        * HW workaround for IBX, we need to move the port
+        * to transcoder A after disabling it to allow the
+        * matching DP port to be enabled on transcoder A.
+        */
+       if (HAS_PCH_IBX(dev) && crtc->pipe == PIPE_B) {
+               temp &= ~SDVO_PIPE_B_SELECT;
+               temp |= SDVO_ENABLE;
+               /*
+                * HW workaround, need to write this twice for issue
+                * that may result in first write getting masked.
+                */
+               I915_WRITE(intel_hdmi->hdmi_reg, temp);
+               POSTING_READ(intel_hdmi->hdmi_reg);
+               I915_WRITE(intel_hdmi->hdmi_reg, temp);
+               POSTING_READ(intel_hdmi->hdmi_reg);
+
+               temp &= ~SDVO_ENABLE;
+               I915_WRITE(intel_hdmi->hdmi_reg, temp);
+               POSTING_READ(intel_hdmi->hdmi_reg);
+       }
 }
 
 static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit)
index e3e9c98eaf526c4e5ae5ea058e9401b2403c2344..4a876914f2120b75ef85dc7711fb6413d280ef89 100644 (file)
@@ -1437,6 +1437,7 @@ static void intel_disable_sdvo(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
        struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
+       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        u32 temp;
 
        intel_sdvo_set_active_outputs(intel_sdvo, 0);
@@ -1445,32 +1446,22 @@ static void intel_disable_sdvo(struct intel_encoder *encoder)
                                                   DRM_MODE_DPMS_OFF);
 
        temp = I915_READ(intel_sdvo->sdvo_reg);
-       if ((temp & SDVO_ENABLE) != 0) {
-               /* HW workaround for IBX, we need to move the port to
-                * transcoder A before disabling it. */
-               if (HAS_PCH_IBX(encoder->base.dev)) {
-                       struct drm_crtc *crtc = encoder->base.crtc;
-                       int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
-
-                       if (temp & SDVO_PIPE_B_SELECT) {
-                               temp &= ~SDVO_PIPE_B_SELECT;
-                               I915_WRITE(intel_sdvo->sdvo_reg, temp);
-                               POSTING_READ(intel_sdvo->sdvo_reg);
-
-                               /* Again we need to write this twice. */
-                               I915_WRITE(intel_sdvo->sdvo_reg, temp);
-                               POSTING_READ(intel_sdvo->sdvo_reg);
-
-                               /* Transcoder selection bits only update
-                                * effectively on vblank. */
-                               if (crtc)
-                                       intel_wait_for_vblank(encoder->base.dev, pipe);
-                               else
-                                       msleep(50);
-                       }
-               }
 
-               intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
+       temp &= ~SDVO_ENABLE;
+       intel_sdvo_write_sdvox(intel_sdvo, temp);
+
+       /*
+        * HW workaround for IBX, we need to move the port
+        * to transcoder A after disabling it to allow the
+        * matching DP port to be enabled on transcoder A.
+        */
+       if (HAS_PCH_IBX(dev_priv) && crtc->pipe == PIPE_B) {
+               temp &= ~SDVO_PIPE_B_SELECT;
+               temp |= SDVO_ENABLE;
+               intel_sdvo_write_sdvox(intel_sdvo, temp);
+
+               temp &= ~SDVO_ENABLE;
+               intel_sdvo_write_sdvox(intel_sdvo, temp);
        }
 }