Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70aff66c authored by Jani Nikula's avatar Jani Nikula Committed by Daniel Vetter
Browse files

drm/i915/dp: do not write DP_TRAINING_PATTERN_SET all the time

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: default avatarJani Nikula <jani.nikula@intel.com>
Smoke-tested-by: default avatarPaulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 58c67ce9
Loading
Loading
Loading
Loading
+87 −42
Original line number Original line Diff line number Diff line
@@ -2318,7 +2318,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)


static bool
static bool
intel_dp_set_link_train(struct intel_dp *intel_dp,
intel_dp_set_link_train(struct intel_dp *intel_dp,
			uint32_t dp_reg_value,
			uint32_t *DP,
			uint8_t dp_train_pat)
			uint8_t dp_train_pat)
{
{
	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);
@@ -2354,50 +2354,51 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
		I915_WRITE(DP_TP_CTL(port), temp);
		I915_WRITE(DP_TP_CTL(port), temp);


	} else if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || port != PORT_A)) {
	} 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) {
		switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
		case DP_TRAINING_PATTERN_DISABLE:
		case DP_TRAINING_PATTERN_DISABLE:
			dp_reg_value |= DP_LINK_TRAIN_OFF_CPT;
			*DP |= DP_LINK_TRAIN_OFF_CPT;
			break;
			break;
		case DP_TRAINING_PATTERN_1:
		case DP_TRAINING_PATTERN_1:
			dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT;
			*DP |= DP_LINK_TRAIN_PAT_1_CPT;
			break;
			break;
		case DP_TRAINING_PATTERN_2:
		case DP_TRAINING_PATTERN_2:
			dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
			*DP |= DP_LINK_TRAIN_PAT_2_CPT;
			break;
			break;
		case DP_TRAINING_PATTERN_3:
		case DP_TRAINING_PATTERN_3:
			DRM_ERROR("DP training pattern 3 not supported\n");
			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;
			break;
		}
		}


	} else {
	} else {
		dp_reg_value &= ~DP_LINK_TRAIN_MASK;
		*DP &= ~DP_LINK_TRAIN_MASK;


		switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
		switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
		case DP_TRAINING_PATTERN_DISABLE:
		case DP_TRAINING_PATTERN_DISABLE:
			dp_reg_value |= DP_LINK_TRAIN_OFF;
			*DP |= DP_LINK_TRAIN_OFF;
			break;
			break;
		case DP_TRAINING_PATTERN_1:
		case DP_TRAINING_PATTERN_1:
			dp_reg_value |= DP_LINK_TRAIN_PAT_1;
			*DP |= DP_LINK_TRAIN_PAT_1;
			break;
			break;
		case DP_TRAINING_PATTERN_2:
		case DP_TRAINING_PATTERN_2:
			dp_reg_value |= DP_LINK_TRAIN_PAT_2;
			*DP |= DP_LINK_TRAIN_PAT_2;
			break;
			break;
		case DP_TRAINING_PATTERN_3:
		case DP_TRAINING_PATTERN_3:
			DRM_ERROR("DP training pattern 3 not supported\n");
			DRM_ERROR("DP training pattern 3 not supported\n");
			dp_reg_value |= DP_LINK_TRAIN_PAT_2;
			*DP |= DP_LINK_TRAIN_PAT_2;
			break;
			break;
		}
		}
	}
	}


	I915_WRITE(intel_dp->output_reg, dp_reg_value);
	I915_WRITE(intel_dp->output_reg, *DP);
	POSTING_READ(intel_dp->output_reg);
	POSTING_READ(intel_dp->output_reg);


	intel_dp_aux_native_write_1(intel_dp,
	ret = intel_dp_aux_native_write_1(intel_dp, DP_TRAINING_PATTERN_SET,
				    DP_TRAINING_PATTERN_SET,
					  dp_train_pat);
					  dp_train_pat);
	if (ret != 1)
		return false;


	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) !=
	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) !=
	    DP_TRAINING_PATTERN_DISABLE) {
	    DP_TRAINING_PATTERN_DISABLE) {
@@ -2412,6 +2413,37 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
	return true;
	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)
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);
	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -2464,22 +2496,20 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)


	DP |= DP_PORT_EN;
	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 = 0xff;
	voltage_tries = 0;
	voltage_tries = 0;
	loop_tries = 0;
	loop_tries = 0;
	for (;;) {
	for (;;) {
		/* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
		uint8_t link_status[DP_LINK_STATUS_SIZE];
		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;

		drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
		drm_dp_link_train_clock_recovery_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");
			DRM_ERROR("failed to get link status\n");
@@ -2501,7 +2531,9 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
				DRM_DEBUG_KMS("too many full retries, give up\n");
				DRM_DEBUG_KMS("too many full retries, give up\n");
				break;
				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;
			voltage_tries = 0;
			continue;
			continue;
		}
		}
@@ -2517,8 +2549,11 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
			voltage_tries = 0;
			voltage_tries = 0;
		voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
		voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;


		/* Compute new intel_dp->train_set as requested by target */
		/* Update training set as requested by target */
		intel_get_adjust_train(intel_dp, link_status);
		if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
			DRM_ERROR("failed to update link training\n");
			break;
		}
	}
	}


	intel_dp->DP = DP;
	intel_dp->DP = DP;
@@ -2532,6 +2567,13 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
	uint32_t DP = intel_dp->DP;
	uint32_t DP = intel_dp->DP;


	/* channel equalization */
	/* 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;
	tries = 0;
	cr_tries = 0;
	cr_tries = 0;
	channel_eq = false;
	channel_eq = false;
@@ -2544,21 +2586,18 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
			break;
			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);
		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;
			break;
		}


		/* Make sure clock is still ok */
		/* Make sure clock is still ok */
		if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
		if (!drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
			intel_dp_start_link_train(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);
			cr_tries++;
			cr_tries++;
			continue;
			continue;
		}
		}
@@ -2572,13 +2611,19 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
		if (tries > 5) {
		if (tries > 5) {
			intel_dp_link_down(intel_dp);
			intel_dp_link_down(intel_dp);
			intel_dp_start_link_train(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;
			tries = 0;
			cr_tries++;
			cr_tries++;
			continue;
			continue;
		}
		}


		/* Compute new intel_dp->train_set as requested by target */
		/* Update training set as requested by target */
		intel_get_adjust_train(intel_dp, link_status);
		if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
			DRM_ERROR("failed to update link training\n");
			break;
		}
		++tries;
		++tries;
	}
	}


@@ -2593,7 +2638,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)


void intel_dp_stop_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);
				DP_TRAINING_PATTERN_DISABLE);
}
}