drm/amd/display: Enable DSC over eDP

[why]
- Adding a DM interface to enable DSC over eDP on Linux
- DSC over eDP will allow to power savings by reducing
the bandwidth required to support panel's modes
- Apply link optimization algorithm to reduce link bandwidth
when DSC is enabled

[how]
- Read eDP panel's DSC capabilities
- Apply DSC policy on eDP panel based on its DSC capabilities
- Enable DSC encoder's on the pipe
- Enable DSC on panel's side by setting DSC_ENABLE DPCD register
- Adding link optimization algorithm to reduce link rate or lane
count based

Reviewed-by: Nicholas Kazlauskas <Nicholas.Kazlauskas@amd.com>
Acked-by: Wayne Lin <wayne.lin@amd.com>
Signed-off-by: Mikita Lipski <mikita.lipski@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Mikita Lipski 2021-10-20 08:51:04 -04:00 committed by Alex Deucher
parent 2430be71c0
commit 2665f63a73
11 changed files with 256 additions and 8 deletions

View file

@ -1478,8 +1478,10 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
if (amdgpu_dc_debug_mask & DC_DISABLE_STUTTER)
adev->dm.dc->debug.disable_stutter = true;
if (amdgpu_dc_debug_mask & DC_DISABLE_DSC)
if (amdgpu_dc_debug_mask & DC_DISABLE_DSC) {
adev->dm.dc->debug.disable_dsc = true;
adev->dm.dc->debug.disable_dsc_edp = true;
}
if (amdgpu_dc_debug_mask & DC_DISABLE_CLOCK_GATING)
adev->dm.dc->debug.disable_clock_gate = true;
@ -6034,7 +6036,8 @@ static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
{
stream->timing.flags.DSC = 0;
if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
sink->sink_signal == SIGNAL_TYPE_EDP)) {
dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
@ -6042,6 +6045,64 @@ static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
}
}
static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector,
struct dc_sink *sink, struct dc_stream_state *stream,
struct dsc_dec_dpcd_caps *dsc_caps,
uint32_t max_dsc_target_bpp_limit_override)
{
const struct dc_link_settings *verified_link_cap = NULL;
uint32_t link_bw_in_kbps;
uint32_t edp_min_bpp_x16, edp_max_bpp_x16;
struct dc *dc = sink->ctx->dc;
struct dc_dsc_bw_range bw_range = {0};
struct dc_dsc_config dsc_cfg = {0};
verified_link_cap = dc_link_get_link_cap(stream->link);
link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap);
edp_min_bpp_x16 = 8 * 16;
edp_max_bpp_x16 = 8 * 16;
if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel)
edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel;
if (edp_max_bpp_x16 < edp_min_bpp_x16)
edp_min_bpp_x16 = edp_max_bpp_x16;
if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0],
dc->debug.dsc_min_slice_height_override,
edp_min_bpp_x16, edp_max_bpp_x16,
dsc_caps,
&stream->timing,
&bw_range)) {
if (bw_range.max_kbps < link_bw_in_kbps) {
if (dc_dsc_compute_config(dc->res_pool->dscs[0],
dsc_caps,
dc->debug.dsc_min_slice_height_override,
max_dsc_target_bpp_limit_override,
0,
&stream->timing,
&dsc_cfg)) {
stream->timing.dsc_cfg = dsc_cfg;
stream->timing.flags.DSC = 1;
stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16;
}
return;
}
}
if (dc_dsc_compute_config(dc->res_pool->dscs[0],
dsc_caps,
dc->debug.dsc_min_slice_height_override,
max_dsc_target_bpp_limit_override,
link_bw_in_kbps,
&stream->timing,
&dsc_cfg)) {
stream->timing.dsc_cfg = dsc_cfg;
stream->timing.flags.DSC = 1;
}
}
static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
struct dc_sink *sink, struct dc_stream_state *stream,
struct dsc_dec_dpcd_caps *dsc_caps)
@ -6049,6 +6110,7 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
struct drm_connector *drm_connector = &aconnector->base;
uint32_t link_bandwidth_kbps;
uint32_t max_dsc_target_bpp_limit_override = 0;
struct dc *dc = sink->ctx->dc;
link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
dc_link_get_link_cap(aconnector->dc_link));
@ -6061,7 +6123,12 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
dc_dsc_policy_set_enable_dsc_when_not_needed(
aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE);
if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_EDP && !dc->debug.disable_dsc_edp &&
dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) {
apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override);
} else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
dsc_caps,

View file

@ -584,7 +584,7 @@ bool dm_helpers_dp_write_dsc_enable(
ret = drm_dp_dpcd_write(aconnector->dsc_aux, DP_DSC_ENABLE, &enable_dsc, 1);
}
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT) {
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT || SIGNAL_TYPE_EDP) {
ret = dm_helpers_dp_write_dpcd(ctx, stream->link, DP_DSC_ENABLE, &enable_dsc, 1);
DC_LOG_DC("Send DSC %s to sst display\n", enable_dsc ? "enable" : "disable");
}

View file

@ -4792,6 +4792,8 @@ bool dc_link_should_enable_fec(const struct dc_link *link)
link->local_sink &&
link->local_sink->edid_caps.panel_patch.disable_fec) ||
(link->connector_signal == SIGNAL_TYPE_EDP
// enable FEC for EDP if DSC is supported
&& link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT == false
))
is_fec_disable = true;

View file

@ -3346,6 +3346,148 @@ bool decide_edp_link_settings(struct dc_link *link, struct dc_link_settings *lin
return false;
}
static bool decide_edp_link_settings_with_dsc(struct dc_link *link,
struct dc_link_settings *link_setting,
uint32_t req_bw,
enum dc_link_rate max_link_rate)
{
struct dc_link_settings initial_link_setting;
struct dc_link_settings current_link_setting;
uint32_t link_bw;
unsigned int policy = 0;
policy = link->ctx->dc->debug.force_dsc_edp_policy;
if (max_link_rate == LINK_RATE_UNKNOWN)
max_link_rate = link->verified_link_cap.link_rate;
/*
* edp_supported_link_rates_count is only valid for eDP v1.4 or higher.
* Per VESA eDP spec, "The DPCD revision for eDP v1.4 is 13h"
*/
if ((link->dpcd_caps.dpcd_rev.raw < DPCD_REV_13 ||
link->dpcd_caps.edp_supported_link_rates_count == 0)) {
/* for DSC enabled case, we search for minimum lane count */
memset(&initial_link_setting, 0, sizeof(initial_link_setting));
initial_link_setting.lane_count = LANE_COUNT_ONE;
initial_link_setting.link_rate = LINK_RATE_LOW;
initial_link_setting.link_spread = LINK_SPREAD_DISABLED;
initial_link_setting.use_link_rate_set = false;
initial_link_setting.link_rate_set = 0;
current_link_setting = initial_link_setting;
if (req_bw > dc_link_bandwidth_kbps(link, &link->verified_link_cap))
return false;
/* 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 <=
max_link_rate) {
link_bw = dc_link_bandwidth_kbps(
link,
&current_link_setting);
if (req_bw <= link_bw) {
*link_setting = current_link_setting;
return true;
}
if (policy) {
/* minimize lane */
if (current_link_setting.link_rate < max_link_rate) {
current_link_setting.link_rate =
increase_link_rate(
current_link_setting.link_rate);
} else {
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);
current_link_setting.link_rate = initial_link_setting.link_rate;
} else
break;
}
} else {
/* minimize link rate */
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;
}
/* if optimize edp link is supported */
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 <=
max_link_rate) {
link_bw = dc_link_bandwidth_kbps(
link,
&current_link_setting);
if (req_bw <= link_bw) {
*link_setting = current_link_setting;
return true;
}
if (policy) {
/* minimize lane */
if (current_link_setting.link_rate_set <
link->dpcd_caps.edp_supported_link_rates_count
&& current_link_setting.link_rate < max_link_rate) {
current_link_setting.link_rate_set++;
current_link_setting.link_rate =
link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
} else {
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);
current_link_setting.link_rate_set = initial_link_setting.link_rate_set;
current_link_setting.link_rate =
link->dpcd_caps.edp_supported_link_rates[current_link_setting.link_rate_set];
} else
break;
}
} else {
/* minimize link rate */
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;
}
static bool decide_mst_link_settings(const struct dc_link *link, struct dc_link_settings *link_setting)
{
*link_setting = link->verified_link_cap;
@ -3380,7 +3522,25 @@ void decide_link_settings(struct dc_stream_state *stream,
if (decide_mst_link_settings(link, link_setting))
return;
} else if (link->connector_signal == SIGNAL_TYPE_EDP) {
if (decide_edp_link_settings(link, link_setting, req_bw))
/* enable edp link optimization for DSC eDP case */
if (stream->timing.flags.DSC) {
enum dc_link_rate max_link_rate = LINK_RATE_UNKNOWN;
if (link->ctx->dc->debug.force_dsc_edp_policy) {
/* calculate link max link rate cap*/
struct dc_link_settings tmp_link_setting;
struct dc_crtc_timing tmp_timing = stream->timing;
uint32_t orig_req_bw;
tmp_link_setting.link_rate = LINK_RATE_UNKNOWN;
tmp_timing.flags.DSC = 0;
orig_req_bw = dc_bandwidth_in_kbps_from_timing(&tmp_timing);
decide_edp_link_settings(link, &tmp_link_setting, orig_req_bw);
max_link_rate = tmp_link_setting.link_rate;
}
if (decide_edp_link_settings_with_dsc(link, link_setting, req_bw, max_link_rate))
return;
} else if (decide_edp_link_settings(link, link_setting, req_bw))
return;
} else if (decide_dp_link_settings(link, link_setting, req_bw))
return;

View file

@ -188,6 +188,7 @@ struct dc_caps {
#if defined(CONFIG_DRM_AMD_DC_DCN)
bool dp_hpo;
#endif
bool edp_dsc_support;
bool vbios_lttpr_aware;
bool vbios_lttpr_enable;
};
@ -667,6 +668,8 @@ struct dc_debug_options {
bool validate_dml_output;
bool enable_dmcub_surface_flip;
bool usbc_combo_phy_reset_wa;
bool disable_dsc_edp;
unsigned int force_dsc_edp_policy;
bool enable_dram_clock_change_one_display_vactive;
#if defined(CONFIG_DRM_AMD_DC_DCN)
/* TODO - remove once tested */

View file

@ -329,6 +329,7 @@ static bool dmub_psr_copy_settings(struct dmub_psr *dmub,
copy_settings_data->fec_enable_delay_in100us = link->dc->debug.fec_enable_delay_in100us;
copy_settings_data->cmd_version = DMUB_CMD_PSR_CONTROL_VERSION_1;
copy_settings_data->panel_inst = panel_inst;
copy_settings_data->dsc_enable_status = (pipe_ctx->stream->timing.flags.DSC == 1);
if (link->fec_state == dc_link_fec_enabled &&
(!memcmp(link->dpcd_caps.sink_dev_id_str, DP_SINK_DEVICE_STR_ID_1,

View file

@ -2199,6 +2199,7 @@ static bool dcn31_resource_construct(
dc->caps.post_blend_color_processing = true;
dc->caps.force_dp_tps4_for_cp2520 = true;
dc->caps.dp_hpo = true;
dc->caps.edp_dsc_support = true;
dc->caps.extended_aux_timeout_support = true;
dc->caps.dmcub_support = true;
dc->caps.is_apu = true;

View file

@ -455,6 +455,7 @@ static bool intersect_dsc_caps(
if (pixel_encoding == PIXEL_ENCODING_YCBCR422 || pixel_encoding == PIXEL_ENCODING_YCBCR420)
dsc_common_caps->bpp_increment_div = min(dsc_common_caps->bpp_increment_div, (uint32_t)8);
dsc_common_caps->edp_sink_max_bits_per_pixel = dsc_sink_caps->edp_max_bits_per_pixel;
dsc_common_caps->is_dp = dsc_sink_caps->is_dp;
return true;
}
@ -513,6 +514,13 @@ static bool decide_dsc_bandwidth_range(
range->min_target_bpp_x16 = preferred_bpp_x16;
}
}
/* TODO - make this value generic to all signal types */
else if (dsc_caps->edp_sink_max_bits_per_pixel) {
/* apply max bpp limitation from edp sink */
range->max_target_bpp_x16 = MIN(dsc_caps->edp_sink_max_bits_per_pixel,
max_bpp_x16);
range->min_target_bpp_x16 = min_bpp_x16;
}
else {
range->max_target_bpp_x16 = max_bpp_x16;
range->min_target_bpp_x16 = min_bpp_x16;

View file

@ -88,6 +88,7 @@ struct dsc_enc_caps {
int32_t max_total_throughput_mps; /* Maximum total throughput with all the slices combined */
int32_t max_slice_width;
uint32_t bpp_increment_div; /* bpp increment divisor, e.g. if 16, it's 1/16th of a bit */
uint32_t edp_sink_max_bits_per_pixel;
bool is_dp;
};

View file

@ -1549,10 +1549,14 @@ struct dmub_cmd_psr_copy_settings_data {
* Currently the support is only for 0 or 1
*/
uint8_t panel_inst;
/**
* Explicit padding to 4 byte boundary.
/*
* DSC enable status in driver
*/
uint8_t pad3[4];
uint8_t dsc_enable_status;
/**
* Explicit padding to 3 byte boundary.
*/
uint8_t pad3[3];
};
/**

View file

@ -35,6 +35,7 @@
#define DP_BRANCH_DEVICE_ID_00E04C 0x00E04C
#define DP_BRANCH_DEVICE_ID_006037 0x006037
#define DP_DEVICE_ID_38EC11 0x38EC11
enum ddc_result {
DDC_RESULT_UNKNOWN = 0,
DDC_RESULT_SUCESSFULL,