From 49c00929ee7e92948e4aa5a0352073f3b46c7597 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 27 Sep 2013 14:04:18 +0300 Subject: [PATCH] drm/i915/dp: do not write DP_TRAINING_PATTERN_SET all the time Organization: Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo Cc: Jani Nikula Neither the DP spec nor the compliance test spec state or imply that we should write the DP_TRAINING_PATTERN_SET at every voltage swing and pre-emphasis change. Indeed we probably shouldn't. So don't. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=49402 Signed-off-by: Jani Nikula --- I haven't tested this much, but it fixes the Zotac DP -> dual-hdmi dongle for me. --- drivers/gpu/drm/i915/intel_dp.c | 129 ++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 95a3159..ab805d3 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2313,7 +2313,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) static bool intel_dp_set_link_train(struct intel_dp *intel_dp, - uint32_t dp_reg_value, + uint32_t *DP, uint8_t dp_train_pat) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -2349,50 +2349,51 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, I915_WRITE(DP_TP_CTL(port), temp); } else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) { - dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT; + *DP &= ~DP_LINK_TRAIN_MASK_CPT; switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { case DP_TRAINING_PATTERN_DISABLE: - dp_reg_value |= DP_LINK_TRAIN_OFF_CPT; + *DP |= DP_LINK_TRAIN_OFF_CPT; break; case DP_TRAINING_PATTERN_1: - dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT; + *DP |= DP_LINK_TRAIN_PAT_1_CPT; break; case DP_TRAINING_PATTERN_2: - dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT; + *DP |= DP_LINK_TRAIN_PAT_2_CPT; break; case DP_TRAINING_PATTERN_3: DRM_ERROR("DP training pattern 3 not supported\n"); - dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT; + *DP |= DP_LINK_TRAIN_PAT_2_CPT; break; } } else { - dp_reg_value &= ~DP_LINK_TRAIN_MASK; + *DP &= ~DP_LINK_TRAIN_MASK; switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { case DP_TRAINING_PATTERN_DISABLE: - dp_reg_value |= DP_LINK_TRAIN_OFF; + *DP |= DP_LINK_TRAIN_OFF; break; case DP_TRAINING_PATTERN_1: - dp_reg_value |= DP_LINK_TRAIN_PAT_1; + *DP |= DP_LINK_TRAIN_PAT_1; break; case DP_TRAINING_PATTERN_2: - dp_reg_value |= DP_LINK_TRAIN_PAT_2; + *DP |= DP_LINK_TRAIN_PAT_2; break; case DP_TRAINING_PATTERN_3: DRM_ERROR("DP training pattern 3 not supported\n"); - dp_reg_value |= DP_LINK_TRAIN_PAT_2; + *DP |= DP_LINK_TRAIN_PAT_2; break; } } - I915_WRITE(intel_dp->output_reg, dp_reg_value); + I915_WRITE(intel_dp->output_reg, *DP); POSTING_READ(intel_dp->output_reg); - intel_dp_aux_native_write_1(intel_dp, - DP_TRAINING_PATTERN_SET, - dp_train_pat); + ret = intel_dp_aux_native_write_1(intel_dp, DP_TRAINING_PATTERN_SET, + dp_train_pat); + if (ret != 1) + return false; if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) != DP_TRAINING_PATTERN_DISABLE) { @@ -2407,6 +2408,37 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, return true; } +static bool +intel_dp_reset_link_train(struct intel_dp *intel_dp, uint32_t *DP, + uint8_t dp_train_pat) +{ + memset(intel_dp->train_set, 0, 4); + intel_dp_set_signal_levels(intel_dp, DP); + return intel_dp_set_link_train(intel_dp, DP, dp_train_pat); +} + +static bool +intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP, + uint8_t link_status[DP_LINK_STATUS_SIZE]) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + intel_get_adjust_train(intel_dp, link_status); + intel_dp_set_signal_levels(intel_dp, DP); + + I915_WRITE(intel_dp->output_reg, *DP); + POSTING_READ(intel_dp->output_reg); + + ret = intel_dp_aux_native_write(intel_dp, DP_TRAINING_LANE0_SET, + intel_dp->train_set, + intel_dp->lane_count); + + return ret == intel_dp->lane_count; +} + static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -2459,21 +2491,19 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DP |= DP_PORT_EN; - memset(intel_dp->train_set, 0, 4); + /* clock recovery */ + if (!intel_dp_reset_link_train(intel_dp, &DP, + DP_TRAINING_PATTERN_1 | + DP_LINK_SCRAMBLING_DISABLE)) { + DRM_ERROR("failed to enable link training\n"); + return; + } + voltage = 0xff; voltage_tries = 0; loop_tries = 0; for (;;) { - /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ - uint8_t link_status[DP_LINK_STATUS_SIZE]; - - intel_dp_set_signal_levels(intel_dp, &DP); - - /* Set training pattern 1 */ - if (!intel_dp_set_link_train(intel_dp, DP, - DP_TRAINING_PATTERN_1 | - DP_LINK_SCRAMBLING_DISABLE)) - break; + uint8_t link_status[DP_LINK_STATUS_SIZE]; drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd); if (!intel_dp_get_link_status(intel_dp, link_status)) { @@ -2496,7 +2526,9 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DRM_DEBUG_KMS("too many full retries, give up\n"); break; } - memset(intel_dp->train_set, 0, 4); + intel_dp_reset_link_train(intel_dp, &DP, + DP_TRAINING_PATTERN_1 | + DP_LINK_SCRAMBLING_DISABLE); voltage_tries = 0; continue; } @@ -2512,8 +2544,11 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) voltage_tries = 0; voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; - /* Compute new intel_dp->train_set as requested by target */ - intel_get_adjust_train(intel_dp, link_status); + /* Update training set as requested by target */ + if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) { + DRM_ERROR("failed to update link training\n"); + break; + } } intel_dp->DP = DP; @@ -2527,11 +2562,18 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) uint32_t DP = intel_dp->DP; /* channel equalization */ + if (!intel_dp_set_link_train(intel_dp, &DP, + DP_TRAINING_PATTERN_2 | + DP_LINK_SCRAMBLING_DISABLE)) { + DRM_ERROR("failed to start channel equalization\n"); + return; + } + tries = 0; cr_tries = 0; channel_eq = false; for (;;) { - uint8_t link_status[DP_LINK_STATUS_SIZE]; + uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { DRM_ERROR("failed to train DP, aborting\n"); @@ -2539,21 +2581,18 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) break; } - intel_dp_set_signal_levels(intel_dp, &DP); - - /* channel eq pattern */ - if (!intel_dp_set_link_train(intel_dp, DP, - DP_TRAINING_PATTERN_2 | - DP_LINK_SCRAMBLING_DISABLE)) - break; - drm_dp_link_train_channel_eq_delay(intel_dp->dpcd); - if (!intel_dp_get_link_status(intel_dp, link_status)) + if (!intel_dp_get_link_status(intel_dp, link_status)) { + DRM_ERROR("failed to get link status\n"); break; + } /* Make sure clock is still ok */ if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) { intel_dp_start_link_train(intel_dp); + intel_dp_set_link_train(intel_dp, &DP, + DP_TRAINING_PATTERN_2 | + DP_LINK_SCRAMBLING_DISABLE); cr_tries++; continue; } @@ -2567,13 +2606,19 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) if (tries > 5) { intel_dp_link_down(intel_dp); intel_dp_start_link_train(intel_dp); + intel_dp_set_link_train(intel_dp, &DP, + DP_TRAINING_PATTERN_2 | + DP_LINK_SCRAMBLING_DISABLE); tries = 0; cr_tries++; continue; } - /* Compute new intel_dp->train_set as requested by target */ - intel_get_adjust_train(intel_dp, link_status); + /* Update training set as requested by target */ + if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) { + DRM_ERROR("failed to update link training\n"); + break; + } ++tries; } @@ -2588,7 +2633,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) void intel_dp_stop_link_train(struct intel_dp *intel_dp) { - intel_dp_set_link_train(intel_dp, intel_dp->DP, + intel_dp_set_link_train(intel_dp, &intel_dp->DP, DP_TRAINING_PATTERN_DISABLE); } -- 1.7.9.5