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

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
......@@ -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++;
}
}
}
......
......@@ -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;
......
......@@ -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 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment