Commit 8628d02f authored by Josip Pavic's avatar Josip Pavic Committed by Alex Deucher
Browse files

drm/amd/display: optionally optimize edp link rate based on timing



[Why]
eDP v1.4 allows panels to report link rates other than RBR/HBR/HBR2, that
may be more optimal for the panel's timing. Power can be saved by using
a link rate closer to the required bandwidth of the panel's timing.

[How]
Scan the table of reported link rates from the panel, and select the
minimum link rate that satisfies the bandwidth requirements of the panel's
timing. Include a flag to make the feature optional.

Signed-off-by: default avatarJosip Pavic <Josip.Pavic@amd.com>
Reviewed-by: default avatarHarry Wentland <Harry.Wentland@amd.com>
Acked-by: default avatarAnthony Koo <Anthony.Koo@amd.com>
Acked-by: default avatarLeo Li <sunpeng.li@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 33e0a445
Loading
Loading
Loading
Loading
+133 −62
Original line number Diff line number Diff line
@@ -93,12 +93,10 @@ static void dpcd_set_link_settings(
	struct dc_link *link,
	const struct link_training_settings *lt_settings)
{
	uint8_t rate = (uint8_t)
	(lt_settings->link_settings.link_rate);
	uint8_t rate;

	union down_spread_ctrl downspread = { {0} };
	union lane_count_set lane_count_set = { {0} };
	uint8_t link_set_buffer[2];

	downspread.raw = (uint8_t)
	(lt_settings->link_settings.link_spread);
@@ -111,21 +109,24 @@ static void dpcd_set_link_settings(
	lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
		link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;

	link_set_buffer[0] = rate;
	link_set_buffer[1] = lane_count_set.raw;

	core_link_write_dpcd(link, DP_LINK_BW_SET,
	link_set_buffer, 2);
	core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
	&downspread.raw, sizeof(downspread));

	core_link_write_dpcd(link, DP_LANE_COUNT_SET,
	&lane_count_set.raw, 1);

	if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
		(link->dpcd_caps.link_rate_set >= 1 &&
		link->dpcd_caps.link_rate_set <= 8)) {
			lt_settings->link_settings.use_link_rate_set == true) {
		rate = 0;
		core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
		core_link_write_dpcd(link, DP_LINK_RATE_SET,
		&link->dpcd_caps.link_rate_set, 1);
				&lt_settings->link_settings.link_rate_set, 1);
	} else {
		rate = (uint8_t) (lt_settings->link_settings.link_rate);
		core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
	}

	if (rate) {
		DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n",
			__func__,
			DP_LINK_BW_SET,
@@ -134,6 +135,16 @@ static void dpcd_set_link_settings(
			lt_settings->link_settings.lane_count,
			DP_DOWNSPREAD_CTRL,
			lt_settings->link_settings.link_spread);
	} else {
		DC_LOG_HW_LINK_TRAINING("%s\n %x rate set = %x\n %x lane = %x\n %x spread = %x\n",
			__func__,
			DP_LINK_RATE_SET,
			lt_settings->link_settings.link_rate_set,
			DP_LANE_COUNT_SET,
			lt_settings->link_settings.lane_count,
			DP_DOWNSPREAD_CTRL,
			lt_settings->link_settings.link_spread);
	}

}

@@ -952,6 +963,8 @@ enum link_training_result dc_link_dp_perform_link_training(

	lt_settings.link_settings.link_rate = link_setting->link_rate;
	lt_settings.link_settings.lane_count = link_setting->lane_count;
	lt_settings.link_settings.use_link_rate_set = link_setting->use_link_rate_set;
	lt_settings.link_settings.link_rate_set = link_setting->link_rate_set;

	/*@todo[vdevulap] move SS to LS, should not be handled by displaypath*/

@@ -1075,7 +1088,7 @@ static struct dc_link_settings get_max_link_cap(struct dc_link *link)
{
	/* Set Default link settings */
	struct dc_link_settings max_link_cap = {LANE_COUNT_FOUR, LINK_RATE_HIGH,
			LINK_SPREAD_05_DOWNSPREAD_30KHZ};
			LINK_SPREAD_05_DOWNSPREAD_30KHZ, false, 0};

	/* Higher link settings based on feature supported */
	if (link->link_enc->features.flags.bits.IS_HBR2_CAPABLE)
@@ -1629,47 +1642,65 @@ bool dp_validate_mode_timing(
		return false;
}

void decide_link_settings(struct dc_stream_state *stream,
	struct dc_link_settings *link_setting)
static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_settings *link_setting, uint32_t req_bw)
{

	struct dc_link_settings initial_link_setting = {
		LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED};
		LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED, false, 0};
	struct dc_link_settings current_link_setting =
			initial_link_setting;
	struct dc_link *link;
	uint32_t req_bw;
	uint32_t link_bw;

	req_bw = bandwidth_in_kbps_from_timing(&stream->timing);

	link = stream->link;

	/* if preferred is specified through AMDDP, use it, if it's enough
	 * to drive the mode
	/* search for the minimum link setting that:
	 * 1. is supported according to the link training result
	 * 2. could support the b/w requested by the timing
	 */
	if (link->preferred_link_setting.lane_count !=
			LANE_COUNT_UNKNOWN &&
			link->preferred_link_setting.link_rate !=
					LINK_RATE_UNKNOWN) {
		*link_setting =  link->preferred_link_setting;
		return;
	while (current_link_setting.link_rate <=
			link->verified_link_cap.link_rate) {
		link_bw = bandwidth_in_kbps_from_link_settings(
				&current_link_setting);
		if (req_bw <= link_bw) {
			*link_setting = current_link_setting;
			return true;
		}

	/* MST doesn't perform link training for now
	 * TODO: add MST specific link training routine
	 */
	if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
		*link_setting = link->verified_link_cap;
		return;
		if (current_link_setting.lane_count <
				link->verified_link_cap.lane_count) {
			current_link_setting.lane_count =
					increase_lane_count(
							current_link_setting.lane_count);
		} else {
			current_link_setting.link_rate =
					increase_link_rate(
							current_link_setting.link_rate);
			current_link_setting.lane_count =
					initial_link_setting.lane_count;
		}
	}

	/* EDP use the link cap setting */
	if (link->connector_signal == SIGNAL_TYPE_EDP) {
	return false;
}

static bool decide_edp_link_settings(struct dc_link *link, struct dc_link_settings *link_setting, uint32_t req_bw)
{
	struct dc_link_settings initial_link_setting;
	struct dc_link_settings current_link_setting;
	uint32_t link_bw;

	if (link->dpcd_caps.dpcd_rev.raw < DPCD_REV_14 ||
			link->dpcd_caps.edp_supported_link_rates_count == 0 ||
			link->dc->config.optimize_edp_link_rate == false) {
		*link_setting = link->verified_link_cap;
		return;
		return true;
	}

	memset(&initial_link_setting, 0, sizeof(initial_link_setting));
	initial_link_setting.lane_count = LANE_COUNT_ONE;
	initial_link_setting.link_rate = link->dpcd_caps.edp_supported_link_rates[0];
	initial_link_setting.link_spread = LINK_SPREAD_DISABLED;
	initial_link_setting.use_link_rate_set = true;
	initial_link_setting.link_rate_set = 0;
	current_link_setting = initial_link_setting;

	/* search for the minimum link setting that:
	 * 1. is supported according to the link training result
	 * 2. could support the b/w requested by the timing
@@ -1680,7 +1711,7 @@ void decide_link_settings(struct dc_stream_state *stream,
				&current_link_setting);
		if (req_bw <= link_bw) {
			*link_setting = current_link_setting;
			return;
			return true;
		}

		if (current_link_setting.lane_count <
@@ -1689,14 +1720,54 @@ void decide_link_settings(struct dc_stream_state *stream,
					increase_lane_count(
							current_link_setting.lane_count);
		} else {
			if (current_link_setting.link_rate_set < link->dpcd_caps.edp_supported_link_rates_count) {
				current_link_setting.link_rate_set++;
				current_link_setting.link_rate =
					increase_link_rate(
							current_link_setting.link_rate);
					link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
				current_link_setting.lane_count =
									initial_link_setting.lane_count;
			} else
				break;
		}
	}
	return false;
}

void decide_link_settings(struct dc_stream_state *stream,
	struct dc_link_settings *link_setting)
{
	struct dc_link *link;
	uint32_t req_bw;

	req_bw = bandwidth_in_kbps_from_timing(&stream->timing);

	link = stream->link;

	/* if preferred is specified through AMDDP, use it, if it's enough
	 * to drive the mode
	 */
	if (link->preferred_link_setting.lane_count !=
			LANE_COUNT_UNKNOWN &&
			link->preferred_link_setting.link_rate !=
					LINK_RATE_UNKNOWN) {
		*link_setting =  link->preferred_link_setting;
		return;
	}

	/* MST doesn't perform link training for now
	 * TODO: add MST specific link training routine
	 */
	if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
		*link_setting = link->verified_link_cap;
		return;
	}

	if (link->connector_signal == SIGNAL_TYPE_EDP) {
		if (decide_edp_link_settings(link, link_setting, req_bw))
			return;
	} else if (decide_dp_link_settings(link, link_setting, req_bw))
		return;

	BREAK_TO_DEBUGGER();
	ASSERT(link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN);

@@ -2536,31 +2607,31 @@ enum dc_link_rate linkRateInKHzToLinkRateMultiplier(uint32_t link_rate_in_khz)

void detect_edp_sink_caps(struct dc_link *link)
{
	uint8_t supported_link_rates[16] = {0};
	uint8_t supported_link_rates[16];
	uint32_t entry;
	uint32_t link_rate_in_khz;
	enum dc_link_rate link_rate = LINK_RATE_UNKNOWN;

	retrieve_link_cap(link);
	link->dpcd_caps.edp_supported_link_rates_count = 0;
	memset(supported_link_rates, 0, sizeof(supported_link_rates));

	if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14) {
	if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
			link->dc->config.optimize_edp_link_rate) {
		// Read DPCD 00010h - 0001Fh 16 bytes at one shot
		core_link_read_dpcd(link, DP_SUPPORTED_LINK_RATES,
							supported_link_rates, sizeof(supported_link_rates));

		link->dpcd_caps.link_rate_set = 0;
		for (entry = 0; entry < 16; entry += 2) {
			// DPCD register reports per-lane link rate = 16-bit link rate capability
			// value X 200 kHz. Need multipler to find link rate in kHz.
			// value X 200 kHz. Need multiplier to find link rate in kHz.
			link_rate_in_khz = (supported_link_rates[entry+1] * 0x100 +
										supported_link_rates[entry]) * 200;

			if (link_rate_in_khz != 0) {
				link_rate = linkRateInKHzToLinkRateMultiplier(link_rate_in_khz);
				if (link->reported_link_cap.link_rate < link_rate) {
					link->reported_link_cap.link_rate = link_rate;
					link->dpcd_caps.link_rate_set = entry;
				}
				link->dpcd_caps.edp_supported_link_rates[link->dpcd_caps.edp_supported_link_rates_count] = link_rate;
				link->dpcd_caps.edp_supported_link_rates_count++;
			}
		}
	}
+5 −1
Original line number Diff line number Diff line
@@ -164,6 +164,7 @@ struct dc_config {
	bool gpu_vm_support;
	bool disable_disp_pll_sharing;
	bool fbc_support;
	bool optimize_edp_link_rate;
};

enum visual_confirm {
@@ -648,6 +649,10 @@ struct dpcd_caps {
	union max_lane_count max_ln_count;
	union max_down_spread max_down_spread;

	/* valid only for eDP v1.4 or higher*/
	uint8_t edp_supported_link_rates_count;
	enum dc_link_rate edp_supported_link_rates[8];

	/* dongle type (DP converter, CV smart dongle) */
	enum display_dongle_type dongle_type;
	/* Dongle's downstream count. */
@@ -665,7 +670,6 @@ struct dpcd_caps {
	int8_t branch_dev_name[6];
	int8_t branch_hw_revision;
	int8_t branch_fw_revision[2];
	uint8_t link_rate_set;

	bool allow_invalid_MSA_timing_param;
	bool panel_mode_edp;
+2 −0
Original line number Diff line number Diff line
@@ -94,6 +94,8 @@ struct dc_link_settings {
	enum dc_lane_count lane_count;
	enum dc_link_rate link_rate;
	enum dc_link_spread link_spread;
	bool use_link_rate_set;
	uint8_t link_rate_set;
};

struct dc_lane_settings {