コード例 #1
0
status_t
dp_link_train_cr(uint32 connectorIndex)
{
	TRACE("%s\n", __func__);

	dp_info* dp = &gConnector[connectorIndex]->dpInfo;

	// Display Port Clock Recovery Training

	bool clockRecovery = false;
	uint8 voltage = 0xff;
	int lane;

	dp_set_tp(connectorIndex, DP_TRAIN_PATTERN_1);
	memset(dp->trainingSet, 0, 4);
	dp_update_vs_emph(connectorIndex);
	snooze(400);

	while (1) {
		if (dp->trainingReadInterval == 0)
			snooze(100);
		else
			snooze(1000 * 4 * dp->trainingReadInterval);

		if (!dp_get_link_status(dp))
			break;

		if (dp_clock_recovery_ok(dp)) {
			clockRecovery = true;
			break;
		}

		for (lane = 0; lane < dp->laneCount; lane++) {
			if ((dp->trainingSet[lane] & DP_TRAIN_MAX_SWING_EN) == 0)
				break;
		}

		if (lane == dp->laneCount) {
			ERROR("%s: clock recovery reached max voltage\n", __func__);
			break;
		}

		if ((dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK) == voltage) {
			dp->trainingAttempts++;
			if (dp->trainingAttempts >= 5) {
				ERROR("%s: clock recovery tried 5 times\n", __func__);
				break;
			}
		} else
			dp->trainingAttempts = 0;

		voltage = dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK;

		// Compute new trainingSet as requested by sink
		dp_get_adjust_train(dp);

		dp_update_vs_emph(connectorIndex);
	}

	if (!clockRecovery) {
		ERROR("%s: clock recovery failed\n", __func__);
		return B_ERROR;
	}

	TRACE("%s: clock recovery at voltage %d pre-emphasis %d\n",
		__func__, dp->trainingSet[0] & DP_TRAIN_VCC_SWING_MASK,
		(dp->trainingSet[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
		>> DP_TRAIN_PRE_EMPHASIS_SHIFT);
	return B_OK;
}
コード例 #2
0
static int radeon_dp_link_train_cr(struct radeon_dp_link_train_info *dp_info)
{
	bool clock_recovery;
 	u8 voltage;
	int i;

	radeon_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1);
	memset(dp_info->train_set, 0, 4);
	radeon_dp_update_vs_emph(dp_info);

	udelay(400);

	/* clock recovery loop */
	clock_recovery = false;
	dp_info->tries = 0;
	voltage = 0xff;
	while (1) {
		if (dp_info->rd_interval == 0)
			udelay(100);
		else
			mdelay(dp_info->rd_interval * 4);

		if (!radeon_dp_get_link_status(dp_info->radeon_connector, dp_info->link_status)) {
			DRM_ERROR("displayport link status failed\n");
			break;
		}

		if (dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) {
			clock_recovery = true;
			break;
		}

		for (i = 0; i < dp_info->dp_lane_count; i++) {
			if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
				break;
		}
		if (i == dp_info->dp_lane_count) {
			DRM_ERROR("clock recovery reached max voltage\n");
			break;
		}

		if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
			++dp_info->tries;
			if (dp_info->tries == 5) {
				DRM_ERROR("clock recovery tried 5 times\n");
				break;
			}
		} else
			dp_info->tries = 0;

		voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;

		/* Compute new train_set as requested by sink */
		dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count, dp_info->train_set);

		radeon_dp_update_vs_emph(dp_info);
	}
	if (!clock_recovery) {
		DRM_ERROR("clock recovery failed\n");
		return -1;
	} else {
		DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n",
			  dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
			  (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
			  DP_TRAIN_PRE_EMPHASIS_SHIFT);
		return 0;
	}
}
コード例 #3
0
void dp_link_train(struct drm_encoder *encoder,
		   struct drm_connector *connector)
{
	struct drm_device *dev = encoder->dev;
	struct radeon_device *rdev = dev->dev_private;
	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
	struct radeon_encoder_atom_dig *dig;
	struct radeon_connector *radeon_connector;
	struct radeon_connector_atom_dig *dig_connector;
	int enc_id = 0;
	bool clock_recovery, channel_eq;
	u8 link_status[DP_LINK_STATUS_SIZE];
	u8 link_configuration[DP_LINK_CONFIGURATION_SIZE];
	u8 tries, voltage;
	u8 train_set[4];
	int i;

	if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
	    (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
		return;

	if (!radeon_encoder->enc_priv)
		return;
	dig = radeon_encoder->enc_priv;

	radeon_connector = to_radeon_connector(connector);
	if (!radeon_connector->con_priv)
		return;
	dig_connector = radeon_connector->con_priv;

	if (dig->dig_encoder)
		enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
	else
		enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
	if (dig_connector->linkb)
		enc_id |= ATOM_DP_CONFIG_LINK_B;
	else
		enc_id |= ATOM_DP_CONFIG_LINK_A;

	memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
	if (dig_connector->dp_clock == 270000)
		link_configuration[0] = DP_LINK_BW_2_7;
	else
		link_configuration[0] = DP_LINK_BW_1_62;
	link_configuration[1] = dig_connector->dp_lane_count;
	if (dig_connector->dpcd[0] >= 0x11)
		link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;

	/* power up the sink */
	dp_set_power(radeon_connector, DP_SET_POWER_D0);
	/* disable the training pattern on the sink */
	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
	/* set link bw and lanes on the sink */
	dp_set_link_bw_lanes(radeon_connector, link_configuration);
	/* disable downspread on the sink */
	dp_set_downspread(radeon_connector, 0);
	/* start training on the source */
	radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
				  dig_connector->dp_clock, enc_id, 0);
	/* set training pattern 1 on the source */
	radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
				  dig_connector->dp_clock, enc_id, 0);

	/* set initial vs/emph */
	memset(train_set, 0, 4);
	udelay(400);
	/* set training pattern 1 on the sink */
	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1);

	dp_update_dpvs_emph(radeon_connector, encoder, train_set);

	/* clock recovery loop */
	clock_recovery = false;
	tries = 0;
	voltage = 0xff;
	for (;;) {
		udelay(100);
		if (!atom_dp_get_link_status(radeon_connector, link_status))
			break;

		if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) {
			clock_recovery = true;
			break;
		}

		for (i = 0; i < dig_connector->dp_lane_count; i++) {
			if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
				break;
		}
		if (i == dig_connector->dp_lane_count) {
			DRM_ERROR("clock recovery reached max voltage\n");
			break;
		}

		if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
			++tries;
			if (tries == 5) {
				DRM_ERROR("clock recovery tried 5 times\n");
				break;
			}
		} else
			tries = 0;

		voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;

		/* Compute new train_set as requested by sink */
		dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
		dp_update_dpvs_emph(radeon_connector, encoder, train_set);
	}
	if (!clock_recovery)
		DRM_ERROR("clock recovery failed\n");
	else
		DRM_DEBUG("clock recovery at voltage %d pre-emphasis %d\n",
			  train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
			  (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
			  DP_TRAIN_PRE_EMPHASIS_SHIFT);


	/* set training pattern 2 on the sink */
	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
	/* set training pattern 2 on the source */
	radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
				  dig_connector->dp_clock, enc_id, 1);

	/* channel equalization loop */
	tries = 0;
	channel_eq = false;
	for (;;) {
		udelay(400);
		if (!atom_dp_get_link_status(radeon_connector, link_status))
			break;

		if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) {
			channel_eq = true;
			break;
		}

		/* Try 5 times */
		if (tries > 5) {
			DRM_ERROR("channel eq failed: 5 tries\n");
			break;
		}

		/* Compute new train_set as requested by sink */
		dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
		dp_update_dpvs_emph(radeon_connector, encoder, train_set);

		tries++;
	}

	if (!channel_eq)
		DRM_ERROR("channel eq failed\n");
	else
		DRM_DEBUG("channel eq at voltage %d pre-emphasis %d\n",
			  train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
			  (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
			  >> DP_TRAIN_PRE_EMPHASIS_SHIFT);

	/* disable the training pattern on the sink */
	dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);

	radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
				  dig_connector->dp_clock, enc_id, 0);
}