mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-11-27 01:11:31 +00:00
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: Josip Pavic <Josip.Pavic@amd.com> Reviewed-by: Harry Wentland <Harry.Wentland@amd.com> Acked-by: Anthony Koo <Anthony.Koo@amd.com> Acked-by: Leo Li <sunpeng.li@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
33e0a44583
commit
8628d02f60
3 changed files with 142 additions and 65 deletions
|
|
@ -93,12 +93,10 @@ static void dpcd_set_link_settings(
|
||||||
struct dc_link *link,
|
struct dc_link *link,
|
||||||
const struct link_training_settings *lt_settings)
|
const struct link_training_settings *lt_settings)
|
||||||
{
|
{
|
||||||
uint8_t rate = (uint8_t)
|
uint8_t rate;
|
||||||
(lt_settings->link_settings.link_rate);
|
|
||||||
|
|
||||||
union down_spread_ctrl downspread = { {0} };
|
union down_spread_ctrl downspread = { {0} };
|
||||||
union lane_count_set lane_count_set = { {0} };
|
union lane_count_set lane_count_set = { {0} };
|
||||||
uint8_t link_set_buffer[2];
|
|
||||||
|
|
||||||
downspread.raw = (uint8_t)
|
downspread.raw = (uint8_t)
|
||||||
(lt_settings->link_settings.link_spread);
|
(lt_settings->link_settings.link_spread);
|
||||||
|
|
@ -111,29 +109,42 @@ static void dpcd_set_link_settings(
|
||||||
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
|
lane_count_set.bits.POST_LT_ADJ_REQ_GRANTED =
|
||||||
link->dpcd_caps.max_ln_count.bits.POST_LT_ADJ_REQ_SUPPORTED;
|
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,
|
core_link_write_dpcd(link, DP_DOWNSPREAD_CTRL,
|
||||||
&downspread.raw, sizeof(downspread));
|
&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 &&
|
if (link->dpcd_caps.dpcd_rev.raw >= DPCD_REV_14 &&
|
||||||
(link->dpcd_caps.link_rate_set >= 1 &&
|
lt_settings->link_settings.use_link_rate_set == true) {
|
||||||
link->dpcd_caps.link_rate_set <= 8)) {
|
rate = 0;
|
||||||
|
core_link_write_dpcd(link, DP_LINK_BW_SET, &rate, 1);
|
||||||
core_link_write_dpcd(link, DP_LINK_RATE_SET,
|
core_link_write_dpcd(link, DP_LINK_RATE_SET,
|
||||||
&link->dpcd_caps.link_rate_set, 1);
|
<_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n",
|
if (rate) {
|
||||||
__func__,
|
DC_LOG_HW_LINK_TRAINING("%s\n %x rate = %x\n %x lane = %x\n %x spread = %x\n",
|
||||||
DP_LINK_BW_SET,
|
__func__,
|
||||||
lt_settings->link_settings.link_rate,
|
DP_LINK_BW_SET,
|
||||||
DP_LANE_COUNT_SET,
|
lt_settings->link_settings.link_rate,
|
||||||
lt_settings->link_settings.lane_count,
|
DP_LANE_COUNT_SET,
|
||||||
DP_DOWNSPREAD_CTRL,
|
lt_settings->link_settings.lane_count,
|
||||||
lt_settings->link_settings.link_spread);
|
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.link_rate = link_setting->link_rate;
|
||||||
lt_settings.link_settings.lane_count = link_setting->lane_count;
|
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*/
|
/*@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 */
|
/* Set Default link settings */
|
||||||
struct dc_link_settings max_link_cap = {LANE_COUNT_FOUR, LINK_RATE_HIGH,
|
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 */
|
/* Higher link settings based on feature supported */
|
||||||
if (link->link_enc->features.flags.bits.IS_HBR2_CAPABLE)
|
if (link->link_enc->features.flags.bits.IS_HBR2_CAPABLE)
|
||||||
|
|
@ -1629,17 +1642,102 @@ bool dp_validate_mode_timing(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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, false, 0};
|
||||||
|
struct dc_link_settings current_link_setting =
|
||||||
|
initial_link_setting;
|
||||||
|
uint32_t link_bw;
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
*/
|
||||||
|
while (current_link_setting.link_rate <=
|
||||||
|
link->verified_link_cap.link_rate) {
|
||||||
|
link_bw = bandwidth_in_kbps_from_link_settings(
|
||||||
|
¤t_link_setting);
|
||||||
|
if (req_bw <= link_bw) {
|
||||||
|
*link_setting = current_link_setting;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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
|
||||||
|
*/
|
||||||
|
while (current_link_setting.link_rate <=
|
||||||
|
link->verified_link_cap.link_rate) {
|
||||||
|
link_bw = bandwidth_in_kbps_from_link_settings(
|
||||||
|
¤t_link_setting);
|
||||||
|
if (req_bw <= link_bw) {
|
||||||
|
*link_setting = current_link_setting;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 =
|
||||||
|
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,
|
void decide_link_settings(struct dc_stream_state *stream,
|
||||||
struct dc_link_settings *link_setting)
|
struct dc_link_settings *link_setting)
|
||||||
{
|
{
|
||||||
|
|
||||||
struct dc_link_settings initial_link_setting = {
|
|
||||||
LANE_COUNT_ONE, LINK_RATE_LOW, LINK_SPREAD_DISABLED};
|
|
||||||
struct dc_link_settings current_link_setting =
|
|
||||||
initial_link_setting;
|
|
||||||
struct dc_link *link;
|
struct dc_link *link;
|
||||||
uint32_t req_bw;
|
uint32_t req_bw;
|
||||||
uint32_t link_bw;
|
|
||||||
|
|
||||||
req_bw = bandwidth_in_kbps_from_timing(&stream->timing);
|
req_bw = bandwidth_in_kbps_from_timing(&stream->timing);
|
||||||
|
|
||||||
|
|
@ -1664,38 +1762,11 @@ void decide_link_settings(struct dc_stream_state *stream,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EDP use the link cap setting */
|
|
||||||
if (link->connector_signal == SIGNAL_TYPE_EDP) {
|
if (link->connector_signal == SIGNAL_TYPE_EDP) {
|
||||||
*link_setting = link->verified_link_cap;
|
if (decide_edp_link_settings(link, link_setting, req_bw))
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
*/
|
|
||||||
while (current_link_setting.link_rate <=
|
|
||||||
link->verified_link_cap.link_rate) {
|
|
||||||
link_bw = bandwidth_in_kbps_from_link_settings(
|
|
||||||
¤t_link_setting);
|
|
||||||
if (req_bw <= link_bw) {
|
|
||||||
*link_setting = current_link_setting;
|
|
||||||
return;
|
return;
|
||||||
}
|
} else if (decide_dp_link_settings(link, link_setting, req_bw))
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BREAK_TO_DEBUGGER();
|
BREAK_TO_DEBUGGER();
|
||||||
ASSERT(link->verified_link_cap.lane_count != LANE_COUNT_UNKNOWN);
|
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)
|
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 entry;
|
||||||
uint32_t link_rate_in_khz;
|
uint32_t link_rate_in_khz;
|
||||||
enum dc_link_rate link_rate = LINK_RATE_UNKNOWN;
|
enum dc_link_rate link_rate = LINK_RATE_UNKNOWN;
|
||||||
|
|
||||||
retrieve_link_cap(link);
|
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
|
// Read DPCD 00010h - 0001Fh 16 bytes at one shot
|
||||||
core_link_read_dpcd(link, DP_SUPPORTED_LINK_RATES,
|
core_link_read_dpcd(link, DP_SUPPORTED_LINK_RATES,
|
||||||
supported_link_rates, sizeof(supported_link_rates));
|
supported_link_rates, sizeof(supported_link_rates));
|
||||||
|
|
||||||
link->dpcd_caps.link_rate_set = 0;
|
|
||||||
for (entry = 0; entry < 16; entry += 2) {
|
for (entry = 0; entry < 16; entry += 2) {
|
||||||
// DPCD register reports per-lane link rate = 16-bit link rate capability
|
// 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 +
|
link_rate_in_khz = (supported_link_rates[entry+1] * 0x100 +
|
||||||
supported_link_rates[entry]) * 200;
|
supported_link_rates[entry]) * 200;
|
||||||
|
|
||||||
if (link_rate_in_khz != 0) {
|
if (link_rate_in_khz != 0) {
|
||||||
link_rate = linkRateInKHzToLinkRateMultiplier(link_rate_in_khz);
|
link_rate = linkRateInKHzToLinkRateMultiplier(link_rate_in_khz);
|
||||||
if (link->reported_link_cap.link_rate < link_rate) {
|
link->dpcd_caps.edp_supported_link_rates[link->dpcd_caps.edp_supported_link_rates_count] = link_rate;
|
||||||
link->reported_link_cap.link_rate = link_rate;
|
link->dpcd_caps.edp_supported_link_rates_count++;
|
||||||
link->dpcd_caps.link_rate_set = entry;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ struct dc_config {
|
||||||
bool gpu_vm_support;
|
bool gpu_vm_support;
|
||||||
bool disable_disp_pll_sharing;
|
bool disable_disp_pll_sharing;
|
||||||
bool fbc_support;
|
bool fbc_support;
|
||||||
|
bool optimize_edp_link_rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum visual_confirm {
|
enum visual_confirm {
|
||||||
|
|
@ -648,6 +649,10 @@ struct dpcd_caps {
|
||||||
union max_lane_count max_ln_count;
|
union max_lane_count max_ln_count;
|
||||||
union max_down_spread max_down_spread;
|
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) */
|
/* dongle type (DP converter, CV smart dongle) */
|
||||||
enum display_dongle_type dongle_type;
|
enum display_dongle_type dongle_type;
|
||||||
/* Dongle's downstream count. */
|
/* Dongle's downstream count. */
|
||||||
|
|
@ -665,7 +670,6 @@ struct dpcd_caps {
|
||||||
int8_t branch_dev_name[6];
|
int8_t branch_dev_name[6];
|
||||||
int8_t branch_hw_revision;
|
int8_t branch_hw_revision;
|
||||||
int8_t branch_fw_revision[2];
|
int8_t branch_fw_revision[2];
|
||||||
uint8_t link_rate_set;
|
|
||||||
|
|
||||||
bool allow_invalid_MSA_timing_param;
|
bool allow_invalid_MSA_timing_param;
|
||||||
bool panel_mode_edp;
|
bool panel_mode_edp;
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,8 @@ struct dc_link_settings {
|
||||||
enum dc_lane_count lane_count;
|
enum dc_lane_count lane_count;
|
||||||
enum dc_link_rate link_rate;
|
enum dc_link_rate link_rate;
|
||||||
enum dc_link_spread link_spread;
|
enum dc_link_spread link_spread;
|
||||||
|
bool use_link_rate_set;
|
||||||
|
uint8_t link_rate_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dc_lane_settings {
|
struct dc_lane_settings {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue