Commit 1612c8bd authored by Ville Syrjälä's avatar Ville Syrjälä Committed by Daniel Vetter

drm/i915: Fix the IBX transcoder B workarounds

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: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Reviewed-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent e8504ee2
...@@ -3843,6 +3843,7 @@ static void ...@@ -3843,6 +3843,7 @@ static void
intel_dp_link_down(struct intel_dp *intel_dp) intel_dp_link_down(struct intel_dp *intel_dp)
{ {
struct intel_digital_port *intel_dig_port = dp_to_dig_port(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; enum port port = intel_dig_port->port;
struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_device *dev = intel_dig_port->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
...@@ -3859,34 +3860,38 @@ intel_dp_link_down(struct intel_dp *intel_dp) ...@@ -3859,34 +3860,38 @@ intel_dp_link_down(struct intel_dp *intel_dp)
if ((IS_GEN7(dev) && port == PORT_A) || if ((IS_GEN7(dev) && port == PORT_A) ||
(HAS_PCH_CPT(dev) && port != PORT_A)) { (HAS_PCH_CPT(dev) && port != PORT_A)) {
DP &= ~DP_LINK_TRAIN_MASK_CPT; 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 { } else {
if (IS_CHERRYVIEW(dev)) if (IS_CHERRYVIEW(dev))
DP &= ~DP_LINK_TRAIN_MASK_CHV; DP &= ~DP_LINK_TRAIN_MASK_CHV;
else else
DP &= ~DP_LINK_TRAIN_MASK; 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); POSTING_READ(intel_dp->output_reg);
if (HAS_PCH_IBX(dev) && DP &= ~(DP_PORT_EN | DP_AUDIO_OUTPUT_ENABLE);
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { I915_WRITE(intel_dp->output_reg, DP);
/* Hardware workaround: leaving our transcoder select POSTING_READ(intel_dp->output_reg);
* set to transcoder B while it's off will prevent the
* corresponding HDMI output on transcoder A. /*
* * HW workaround for IBX, we need to move the port
* Combine this with another hardware workaround: * to transcoder A after disabling it to allow the
* transcoder select bit can only be cleared while the * matching HDMI port to be enabled on transcoder A.
* port is enabled.
*/ */
DP &= ~DP_PIPEB_SELECT; 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); I915_WRITE(intel_dp->output_reg, DP);
POSTING_READ(intel_dp->output_reg); POSTING_READ(intel_dp->output_reg);
}
DP &= ~DP_AUDIO_OUTPUT_ENABLE; DP &= ~DP_PORT_EN;
I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); I915_WRITE(intel_dp->output_reg, DP);
POSTING_READ(intel_dp->output_reg); POSTING_READ(intel_dp->output_reg);
}
msleep(intel_dp->panel_power_down_delay); msleep(intel_dp->panel_power_down_delay);
} }
......
...@@ -873,41 +873,37 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) ...@@ -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_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
u32 temp; u32 temp;
u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
if (crtc->config->has_audio) if (crtc->config->has_audio)
intel_audio_codec_disable(encoder); intel_audio_codec_disable(encoder);
temp = I915_READ(intel_hdmi->hdmi_reg); temp = I915_READ(intel_hdmi->hdmi_reg);
/* HW workaround for IBX, we need to move the port to transcoder A temp &= ~(SDVO_ENABLE | SDVO_AUDIO_ENABLE);
* before disabling it. */ I915_WRITE(intel_hdmi->hdmi_reg, temp);
if (HAS_PCH_IBX(dev)) { POSTING_READ(intel_hdmi->hdmi_reg);
struct drm_crtc *crtc = encoder->base.crtc;
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
if (temp & SDVO_PIPE_B_SELECT) { /*
* 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_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); I915_WRITE(intel_hdmi->hdmi_reg, temp);
POSTING_READ(intel_hdmi->hdmi_reg); POSTING_READ(intel_hdmi->hdmi_reg);
/* Again we need to write this twice. */
I915_WRITE(intel_hdmi->hdmi_reg, temp); I915_WRITE(intel_hdmi->hdmi_reg, temp);
POSTING_READ(intel_hdmi->hdmi_reg); POSTING_READ(intel_hdmi->hdmi_reg);
/* Transcoder selection bits only update temp &= ~SDVO_ENABLE;
* effectively on vblank. */
if (crtc)
intel_wait_for_vblank(dev, pipe);
else
msleep(50);
}
}
temp &= ~enable_bits;
I915_WRITE(intel_hdmi->hdmi_reg, temp); I915_WRITE(intel_hdmi->hdmi_reg, temp);
POSTING_READ(intel_hdmi->hdmi_reg); POSTING_READ(intel_hdmi->hdmi_reg);
}
} }
static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit) static int hdmi_portclock_limit(struct intel_hdmi *hdmi, bool respect_dvi_limit)
......
...@@ -1437,6 +1437,7 @@ static void intel_disable_sdvo(struct intel_encoder *encoder) ...@@ -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 drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_sdvo *intel_sdvo = to_sdvo(encoder); struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
u32 temp; u32 temp;
intel_sdvo_set_active_outputs(intel_sdvo, 0); intel_sdvo_set_active_outputs(intel_sdvo, 0);
...@@ -1445,32 +1446,22 @@ static void intel_disable_sdvo(struct intel_encoder *encoder) ...@@ -1445,32 +1446,22 @@ static void intel_disable_sdvo(struct intel_encoder *encoder)
DRM_MODE_DPMS_OFF); DRM_MODE_DPMS_OFF);
temp = I915_READ(intel_sdvo->sdvo_reg); 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. */ temp &= ~SDVO_ENABLE;
I915_WRITE(intel_sdvo->sdvo_reg, temp); intel_sdvo_write_sdvox(intel_sdvo, temp);
POSTING_READ(intel_sdvo->sdvo_reg);
/* Transcoder selection bits only update /*
* effectively on vblank. */ * HW workaround for IBX, we need to move the port
if (crtc) * to transcoder A after disabling it to allow the
intel_wait_for_vblank(encoder->base.dev, pipe); * matching DP port to be enabled on transcoder A.
else */
msleep(50); 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);
intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE); temp &= ~SDVO_ENABLE;
intel_sdvo_write_sdvox(intel_sdvo, temp);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment