예제 #1
0
static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp)
{
	int rtp = 0;

	/* set training pattern on the source */
	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) {
		switch (tp) {
		case DP_TRAINING_PATTERN_1:
			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
			break;
		case DP_TRAINING_PATTERN_2:
			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2;
			break;
		case DP_TRAINING_PATTERN_3:
			rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3;
			break;
		}
		atombios_dig_encoder_setup(dp_info->encoder, rtp, 0);
	} else {
		switch (tp) {
		case DP_TRAINING_PATTERN_1:
			rtp = 0;
			break;
		case DP_TRAINING_PATTERN_2:
			rtp = 1;
			break;
		}
		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
					  dp_info->dp_clock, dp_info->enc_id, rtp);
	}

	/* enable training pattern on the sink */
	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_TRAINING_PATTERN_SET, tp);
}
예제 #2
0
static void radeon_dp_set_panel_mode(struct drm_encoder *encoder,
				     struct drm_connector *connector)
{
	struct drm_device *dev = encoder->dev;
	struct radeon_device *rdev = dev->dev_private;
	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
	int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;

	if (!ASIC_IS_DCE4(rdev))
		return;

	if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
	    ENCODER_OBJECT_ID_NUTMEG)
		panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
	else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
		 ENCODER_OBJECT_ID_TRAVIS)
		panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
	else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
		u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
		if (tmp & 1)
			panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
	}

	atombios_dig_encoder_setup(encoder,
				   ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
				   panel_mode);

	if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) &&
	    (panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) {
		radeon_write_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_SET, 1);
	}
}
예제 #3
0
static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
{
	struct radeon_encoder *radeon_encoder = to_radeon_encoder(dp_info->encoder);
	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
	u8 tmp;

	/* power up the sink */
	if (dp_info->dpcd[0] >= 0x11) {
		radeon_write_dpcd_reg(dp_info->radeon_connector,
				      DP_SET_POWER, DP_SET_POWER_D0);
		usleep_range(1000, 2000);
	}

	/* possibly enable downspread on the sink */
	if (dp_info->dpcd[3] & 0x1)
		radeon_write_dpcd_reg(dp_info->radeon_connector,
				      DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
	else
		radeon_write_dpcd_reg(dp_info->radeon_connector,
				      DP_DOWNSPREAD_CTRL, 0);

	if ((dp_info->connector->connector_type == DRM_MODE_CONNECTOR_eDP) &&
	    (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)) {
		radeon_write_dpcd_reg(dp_info->radeon_connector, DP_EDP_CONFIGURATION_SET, 1);
	}

	/* set the lane count on the sink */
	tmp = dp_info->dp_lane_count;
	if (drm_dp_enhanced_frame_cap(dp_info->dpcd))
		tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);

	/* set the link rate on the sink */
	tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock);
	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);

	/* start training on the source */
	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
		atombios_dig_encoder_setup(dp_info->encoder,
					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
	else
		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_START,
					  dp_info->dp_clock, dp_info->enc_id, 0);

	/* disable the training pattern on the sink */
	radeon_write_dpcd_reg(dp_info->radeon_connector,
			      DP_TRAINING_PATTERN_SET,
			      DP_TRAINING_PATTERN_DISABLE);

	return 0;
}
예제 #4
0
static void radeon_dp_set_panel_mode(struct drm_encoder *encoder,
				     struct drm_connector *connector)
{
	struct drm_device *dev = encoder->dev;
	struct radeon_device *rdev = dev->dev_private;
	int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;

	if (!ASIC_IS_DCE4(rdev))
		return;

	if (radeon_connector_encoder_is_dp_bridge(connector))
		panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;

	atombios_dig_encoder_setup(encoder,
				   ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
				   panel_mode);
}
예제 #5
0
static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
{
	u8 tmp;

	/* power up the sink */
	if (dp_info->dpcd[0] >= 0x11)
		radeon_write_dpcd_reg(dp_info->radeon_connector,
				      DP_SET_POWER, DP_SET_POWER_D0);

	/* possibly enable downspread on the sink */
	if (dp_info->dpcd[3] & 0x1)
		radeon_write_dpcd_reg(dp_info->radeon_connector,
				      DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
	else
		radeon_write_dpcd_reg(dp_info->radeon_connector,
				      DP_DOWNSPREAD_CTRL, 0);

	radeon_dp_set_panel_mode(dp_info->encoder, dp_info->connector);

	/* set the lane count on the sink */
	tmp = dp_info->dp_lane_count;
	if (dp_info->dpcd[0] >= 0x11)
		tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);

	/* set the link rate on the sink */
	tmp = dp_get_dp_link_rate_coded(dp_info->dp_clock);
	radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp);

	/* start training on the source */
	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
		atombios_dig_encoder_setup(dp_info->encoder,
					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
	else
		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_START,
					  dp_info->dp_clock, dp_info->enc_id, 0);

	/* disable the training pattern on the sink */
	radeon_write_dpcd_reg(dp_info->radeon_connector,
			      DP_TRAINING_PATTERN_SET,
			      DP_TRAINING_PATTERN_DISABLE);

	return 0;
}
예제 #6
0
static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info)
{
	DRM_UDELAY(400);

	/* disable the training pattern on the sink */
	radeon_write_dpcd_reg(dp_info->radeon_connector,
			      DP_TRAINING_PATTERN_SET,
			      DP_TRAINING_PATTERN_DISABLE);

	/* disable the training pattern on the source */
	if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder)
		atombios_dig_encoder_setup(dp_info->encoder,
					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
	else
		radeon_dp_encoder_service(dp_info->rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
					  dp_info->dp_clock, dp_info->enc_id, 0);

	return 0;
}
예제 #7
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);
	if (ASIC_IS_DCE4(rdev)) {
		/* start training on the source */
		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START);
		/* set training pattern 1 on the source */
		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1);
	} else {
		/* 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 */
	if (ASIC_IS_DCE4(rdev))
		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2);
	else
		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);

	/* disable the training pattern on the source */
	if (ASIC_IS_DCE4(rdev))
		atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE);
	else
		radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
					  dig_connector->dp_clock, enc_id, 0);
}