mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 14:44:12 +00:00
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:
parent
2430be71c0
commit
2665f63a73
11 changed files with 256 additions and 8 deletions
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
¤t_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,
|
||||
¤t_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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue