drm/amd/display: Enable DSC over eDP
authorMikita Lipski <mikita.lipski@amd.com>
Wed, 20 Oct 2021 12:51:04 +0000 (08:51 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 22 Nov 2021 19:45:01 +0000 (14:45 -0500)
[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>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
drivers/gpu/drm/amd/display/dc/core/dc_link.c
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/dc.h
drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c
drivers/gpu/drm/amd/display/dc/dcn31/dcn31_resource.c
drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
drivers/gpu/drm/amd/display/dc/inc/hw/dsc.h
drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
drivers/gpu/drm/amd/display/include/ddc_service_types.h

index 3716593673050990e4b4a7e9521d1794cd8fc015..a82f9e0854812bd0fd0465a4b76247b888b8bebd 100644 (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,
index 8cbeeb7c986d03d3f1144bda5ef40c3b18a531bb..9a8fa2d44264ca513b1c27605aa41fcfffd5d559 100644 (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");
        }
index 8a8a5aead34d7782b93e1580a6693024885e48ed..8f23c160f291809f4b69cc6d413b16d20283097b 100644 (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;
 
index 6583ec8548fa52279742c1a7b52378af1f7bc7fc..2bd27c58ef5d32fd890bf044886fe9f0e15c3f7a 100644 (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;
index 2bebc52c8ed9b74b1afe3ceec0bd5b1cf4ce32cc..764663df78874aca694c037b21e475041c9a3448 100644 (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 */
index 60b2ccffaf903256ccccef7b105029d0c8ba066d..87ed48d5530dc7b51507350d26d0f8f2bc4efa40 100644 (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,
index 18896294ae12e7e2562ca89de9edc7edd151f63e..88e04068794005132026c673dc1f3564762b2b67 100644 (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;
index 2d0f1f4a8fffa85f06f40bf2028919d4058cd2b0..9c74564cbd8dee637bebb02aadc3a7c30062c0a7 100644 (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;
index f94135c6e3c222b1ccb54f58825949581e548ce7..0c83e79d3ce371df84d33e2448a51d4002e0f02a 100644 (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;
 };
 
index de624fd7a2e3605ecb598ce721be0b1b0323abad..9946c13621edbac8d429f4afd7581433006713b9 100644 (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;
+       /*
+        * DSC enable status in driver
+        */
+       uint8_t dsc_enable_status;
        /**
-        * Explicit padding to 4 byte boundary.
+        * Explicit padding to 3 byte boundary.
         */
-       uint8_t pad3[4];
+       uint8_t pad3[3];
 };
 
 /**
index 85b25e68446409845824cd5a605b9c8392b08370..a2b80514d83ebb239e06225825955d105120984f 100644 (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,