Add all the bits to enable DSC over FRL.

Signed-off-by: Harry Wentland <[email protected]>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  74 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c |  91 ++-
 .../display/amdgpu_dm/amdgpu_dm_mst_types.c   |  12 +
 drivers/gpu/drm/amd/display/dc/core/dc.c      |   6 +
 .../drm/amd/display/dc/core/dc_hw_sequencer.c |  13 +
 .../gpu/drm/amd/display/dc/core/dc_resource.c |  54 ++
 .../gpu/drm/amd/display/dc/core/dc_stream.c   |   2 +-
 drivers/gpu/drm/amd/display/dc/dc.h           |   7 +
 drivers/gpu/drm/amd/display/dc/dc_dsc.h       |   8 +
 .../gpu/drm/amd/display/dc/dc_hdmi_types.h    |   2 +
 .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c  |  40 +-
 .../dc/dml/dcn30/display_mode_vba_30.c        |   3 +
 .../dc/dml/dcn31/display_mode_vba_31.c        |   3 +
 .../dc/dml/dcn314/display_mode_vba_314.c      |   3 +
 .../dc/dml/dcn32/display_mode_vba_util_32.c   |   3 +
 .../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 197 +++++
 .../drm/amd/display/dc/dml/dml1_frl_cap_chk.h |   9 +
 drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c   | 704 ++++++++++++++++++
 .../hpo/dcn30/dcn30_hpo_frl_stream_encoder.c  | 105 +++
 .../dcn401/dcn401_hpo_frl_stream_encoder.c    |   1 +
 .../hpo/dcn42/dcn42_hpo_frl_stream_encoder.c  |   1 +
 .../amd/display/dc/hwss/dcn30/dcn30_hwseq.c   |   9 +
 .../amd/display/dc/hwss/dcn30/dcn30_init.c    |   1 +
 .../amd/display/dc/hwss/dcn31/dcn31_init.c    |   1 +
 .../amd/display/dc/hwss/dcn314/dcn314_init.c  |   1 +
 .../amd/display/dc/hwss/dcn32/dcn32_init.c    |   1 +
 .../amd/display/dc/hwss/dcn35/dcn35_init.c    |   1 +
 .../amd/display/dc/hwss/dcn351/dcn351_init.c  |   1 +
 .../amd/display/dc/hwss/dcn401/dcn401_init.c  |   1 +
 .../amd/display/dc/hwss/dcn42/dcn42_init.c    |   1 +
 .../drm/amd/display/dc/hwss/hw_sequencer.h    |   5 +
 .../amd/display/dc/inc/hw/stream_encoder.h    |   4 +
 .../gpu/drm/amd/display/dc/link/link_dpms.c   |  38 +
 .../display/dc/link/protocols/link_hdmi_frl.c |  38 +
 .../amd/display/dc/optc/dcn30/dcn30_optc.c    |  30 +-
 .../amd/display/dc/optc/dcn30/dcn30_optc.h    |   1 +
 .../amd/display/dc/optc/dcn31/dcn31_optc.c    |   1 +
 .../amd/display/dc/optc/dcn314/dcn314_optc.c  |   1 +
 .../amd/display/dc/optc/dcn32/dcn32_optc.c    |   1 +
 .../amd/display/dc/optc/dcn35/dcn35_optc.c    |   1 +
 .../amd/display/dc/optc/dcn401/dcn401_optc.c  |   1 +
 .../amd/display/dc/optc/dcn42/dcn42_optc.c    |   1 +
 42 files changed, 1460 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index be4b66b00be9..007f099ffbb5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7323,12 +7323,30 @@ static void update_dsc_caps(struct amdgpu_dm_connector 
*aconnector,
 
        if (aconnector->dc_link && (sink->sink_signal == 
SIGNAL_TYPE_DISPLAY_PORT ||
            sink->sink_signal == SIGNAL_TYPE_EDP)) {
-               if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE ||
-                       sink->link->dpcd_caps.dongle_type == 
DISPLAY_DONGLE_DP_HDMI_CONVERTER)
+               if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE)
                        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,
                                dsc_caps);
+               else if (sink->link->dpcd_caps.dongle_type == 
DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
+                       if 
(aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT
 &&
+                                       
!aconnector->dsc_settings.dsc_force_disable_passthrough &&
+                                       
aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 
&&
+                                       sink->edid_caps.frl_dsc_support &&
+                                       sink->edid_caps.max_frl_rate > 0 &&
+                                       sink->edid_caps.frl_dsc_max_frl_rate > 
0)
+                               
dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
+                       else
+                               
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,
+                                     dsc_caps);
+               }
+       } else if (aconnector->dc_link && sink->sink_signal == 
SIGNAL_TYPE_HDMI_FRL) {
+               if (sink->edid_caps.frl_dsc_support &&
+                               sink->edid_caps.max_frl_rate > 0 &&
+                               sink->edid_caps.frl_dsc_max_frl_rate > 0)
+                       dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, 
&sink->edid_caps, dsc_caps);
        }
 }
 
@@ -7402,6 +7420,10 @@ static void apply_dsc_policy_for_stream(struct 
amdgpu_dm_connector *aconnector,
        struct drm_connector *drm_connector = &aconnector->base;
        u32 link_bandwidth_kbps;
        struct dc *dc = sink->ctx->dc;
+       const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL;
+       u32 converter_bw_in_kbps;
+       u32 sink_bw_in_kbps;
+       u32 dsc_sink_bw_in_kbps;
        u32 max_supported_bw_in_kbps, timing_bw_in_kbps;
        u32 dsc_max_supported_bw_in_kbps;
        u32 max_dsc_target_bpp_limit_override =
@@ -7440,8 +7462,18 @@ static void apply_dsc_policy_for_stream(struct 
amdgpu_dm_connector *aconnector,
                } else if (sink->link->dpcd_caps.dongle_type == 
DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
                        timing_bw_in_kbps = 
dc_bandwidth_in_kbps_from_timing(&stream->timing,
                                        
dc_link_get_highest_encoding_format(aconnector->dc_link));
-                       max_supported_bw_in_kbps = link_bandwidth_kbps;
-                       dsc_max_supported_bw_in_kbps = link_bandwidth_kbps;
+                       converter_bw_in_kbps = 
aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps;
+                       sink_bw_in_kbps = 
dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate);
+                       dsc_sink_bw_in_kbps = 
dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, 
sink->edid_caps.frl_dsc_max_frl_rate);
+
+                       if (dsc_caps->is_frl) {
+                               max_supported_bw_in_kbps = 
min(link_bandwidth_kbps, converter_bw_in_kbps);
+                               max_supported_bw_in_kbps = 
min(max_supported_bw_in_kbps, sink_bw_in_kbps);
+                               dsc_max_supported_bw_in_kbps = 
min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps);
+                       } else {
+                               max_supported_bw_in_kbps = link_bandwidth_kbps;
+                               dsc_max_supported_bw_in_kbps = 
link_bandwidth_kbps;
+                       }
 
                        if (timing_bw_in_kbps > max_supported_bw_in_kbps &&
                                        max_supported_bw_in_kbps > 0 &&
@@ -7454,11 +7486,41 @@ static void apply_dsc_policy_for_stream(struct 
amdgpu_dm_connector *aconnector,
                                                
dc_link_get_highest_encoding_format(aconnector->dc_link),
                                                &stream->timing.dsc_cfg)) {
                                        stream->timing.flags.DSC = 1;
-                                       drm_dbg_driver(drm_connector->dev, "%s: 
SST_DSC [%s] DSC is selected from DP-HDMI PCON\n",
-                                                                        
__func__, drm_connector->name);
+                                       drm_dbg_driver(drm_connector->dev, "%s: 
SST_DSC [%s] DSC is selected from %s\n",
+                                                       __func__, 
drm_connector->name,
+                                                       (dsc_caps->is_frl == 1) 
? "HDMI FRL RX" : "DP-HDMI PCON");
                                }
                }
        }
+       else if (aconnector->dc_link && sink->sink_signal == 
SIGNAL_TYPE_HDMI_FRL) {
+               struct dc_dsc_policy dsc_policy = {0};
+
+               frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
+               if (frl_verified_link_cap->frl_link_rate != 
HDMI_FRL_LINK_RATE_DISABLE &&
+                       aconnector->dc_link->frl_flags.force_frl_dsc) {
+                       dc_dsc_policy_set_enable_dsc_when_not_needed(true);
+                       dc_dsc_get_policy_for_timing(&stream->timing, 0, 
&dsc_policy, dc_link_get_highest_encoding_format(stream->link));
+               }
+
+               timing_bw_in_kbps = 
dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL);
+               link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, 
frl_verified_link_cap->frl_link_rate);
+               dsc_sink_bw_in_kbps = 
dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, 
sink->edid_caps.frl_dsc_max_frl_rate);
+
+               if ((timing_bw_in_kbps > link_bandwidth_kbps && 
dsc_sink_bw_in_kbps > 0) ||
+                   (dsc_policy.enable_dsc_when_not_needed || 
dsc_options.force_dsc_when_not_needed)) {
+                       if 
(dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
+                                       dsc_caps,
+                                       &dsc_options,
+                                       dsc_sink_bw_in_kbps,
+                                       &stream->timing,
+                                       
dc_link_get_highest_encoding_format(aconnector->dc_link),
+                                       &stream->timing.dsc_cfg)) {
+                               stream->timing.flags.DSC = 1;
+                               drm_dbg_driver(drm_connector->dev, "%s: 
HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n",
+                                               __func__, drm_connector->name);
+                       }
+               }
+       }
 
        /* Overwrite the stream flag if DSC is enabled through debugfs */
        if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index 05f9a01f223c..e14980271c96 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -178,8 +178,15 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
 
        edid_caps->edid_hdmi = connector->display_info.is_hdmi;
 
-       if (edid_caps->edid_hdmi)
+       if (edid_caps->edid_hdmi) {
                
populate_hdmi_info_from_connector(&connector->display_info.hdmi, edid_caps);
+               drm_dbg_driver(connector->dev, "%s: HDMI_FRL [%s] max_frl_rate 
%d\n", __func__, connector->name, edid_caps->max_frl_rate);
+               if (edid_caps->frl_dsc_support)
+                       drm_dbg_driver(connector->dev, "%s: HDMI_FRL_DSC [%s] 
frl_dsc_10bpc %d, frl_dsc_12bpc %d, frl_dsc_all_bpp %d, frl_dsc_native_420 %d, 
frl_dsc_max_slices %d, frl_dsc_max_frl_rate %d, frl_dsc_total_chunk_kbytes 
%d\n",
+                                       __func__, connector->name, 
edid_caps->frl_dsc_10bpc, edid_caps->frl_dsc_12bpc, \
+                                       edid_caps->frl_dsc_all_bpp, 
edid_caps->frl_dsc_native_420, edid_caps->frl_dsc_max_slices, \
+                                       edid_caps->frl_dsc_max_frl_rate, 
edid_caps->frl_dsc_total_chunk_kbytes);
+       }
 
        apply_edid_quirks(dev, edid_buf, edid_caps);
 
@@ -896,8 +903,23 @@ bool dm_helpers_dp_write_dsc_enable(
                port = aconnector->mst_output_port;
 
                if (enable) {
-                       if (port->passthrough_aux) {
-                               ret = drm_dp_dpcd_write(port->passthrough_aux,
+                       if (port->passthrough_aux
+                           || (stream->timing.dsc_cfg.is_frl &&
+                               (!stream->sink->dsc_caps.dsc_dec_caps.is_frl ||
+                               (stream->sink->dsc_caps.dsc_dec_caps.is_frl &&
+                               
aconnector->mst_downstream_port_present.fields.PORT_PRESENT &&
+                               
aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==
+                               DOWN_STREAM_DETAILED_HDMI)))
+                           ) {
+                               if (!stream->sink->dsc_caps.dsc_dec_caps.is_frl)
+                                       /* DSC passthrough to DP receiver */
+                                       enable_passthrough = DSC_DECODING;
+                               else if 
(aconnector->mst_downstream_port_present.fields.PORT_PRESENT &&
+                                        
aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==
+                                        DOWN_STREAM_DETAILED_HDMI)
+                                       /* DSC passthrough to FRL sink */
+                                       enable_passthrough = DSC_PASSTHROUGH;
+                               ret = 
drm_dp_dpcd_write(&aconnector->mst_output_port->aux,
                                                        DP_DSC_ENABLE,
                                                        &enable_passthrough, 1);
                                drm_dbg_dp(dev,
@@ -921,8 +943,13 @@ bool dm_helpers_dp_write_dsc_enable(
                                   "virtual dpcd",
                                   ret);
 
-                       if (port->passthrough_aux) {
-                               ret = drm_dp_dpcd_write(port->passthrough_aux,
+                       if (port->passthrough_aux
+                           || (stream->timing.dsc_cfg.is_frl &&
+                               (!stream->sink->dsc_caps.dsc_dec_caps.is_frl ||
+                                
(aconnector->mst_downstream_port_present.fields.PORT_PRESENT &&
+                                 
aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE ==
+                                 DOWN_STREAM_DETAILED_HDMI)))) {
+                               ret = 
drm_dp_dpcd_write(&aconnector->mst_output_port->aux,
                                                        DP_DSC_ENABLE,
                                                        &enable_passthrough, 1);
                                drm_dbg_dp(dev,
@@ -939,10 +966,22 @@ bool dm_helpers_dp_write_dsc_enable(
                                   "SST_DSC Send DSC %s to SST RX\n",
                                   enable_dsc ? "enable" : "disable");
                } else if (stream->sink->link->dpcd_caps.dongle_type == 
DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
+                       if (enable) {
+                               if (stream->timing.dsc_cfg.is_frl) {
+                                       enable_dsc = DSC_PASSTHROUGH;
+                               } else {
+                                       enable_dsc = DSC_DECODING;
+                               }
+                               drm_dbg_dp(dev,
+                                         "SST_DSC Sent DSC decoding enable to 
%s port, ret = %u\n",
+                                         (port->passthrough_aux) ?
+                                         "remote HDMI FRL RX" :
+                                         "DP-HDMI PCON", ret);
+                       } else {
+                               enable_dsc = DSC_DISABLE;
+                               drm_dbg_dp(dev, "SST_DSC Send DSC disable to 
DP-HDMI PCON\n");
+                       }
                        ret = dm_helpers_dp_write_dpcd(ctx, stream->link, 
DP_DSC_ENABLE, &enable_dsc, 1);
-                       drm_dbg_dp(dev,
-                                  "SST_DSC Send DSC %s to DP-HDMI PCON\n",
-                                  enable_dsc ? "enable" : "disable");
                }
        }
 
@@ -1054,10 +1093,46 @@ static uint8_t get_max_frl_rate(uint8_t max_lanes, 
uint8_t max_rate_per_lane)
        return max_frl_rate;
 }
 
+static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
+{
+       uint8_t dsc_max_slices;
+
+       if ((max_slices == 1) && (clk_per_slice == 340))
+               dsc_max_slices = 1;
+       else if ((max_slices == 2) && (clk_per_slice == 340))
+               dsc_max_slices = 2;
+       else if ((max_slices == 4) && (clk_per_slice == 340))
+               dsc_max_slices = 3;
+       else if ((max_slices == 8) && (clk_per_slice == 340))
+               dsc_max_slices = 4;
+       else if ((max_slices == 8) && (clk_per_slice == 400))
+               dsc_max_slices = 5;
+       else if ((max_slices == 12) && (clk_per_slice == 400))
+               dsc_max_slices = 6;
+       else if ((max_slices == 16) && (clk_per_slice == 400))
+               dsc_max_slices = 7;
+       else
+               dsc_max_slices = 0;
+
+       return dsc_max_slices;
+}
+
 void populate_hdmi_info_from_connector(struct drm_hdmi_info *hdmi, struct 
dc_edid_caps *edid_caps)
 {
        edid_caps->scdc_present = hdmi->scdc.supported;
        edid_caps->max_frl_rate = get_max_frl_rate(hdmi->max_lanes, 
hdmi->max_frl_rate_per_lane);
+       edid_caps->frl_dsc_support = hdmi->dsc_cap.v_1p2;
+       if (edid_caps->frl_dsc_support) {
+               if (hdmi->dsc_cap.bpc_supported == 10)
+                       edid_caps->frl_dsc_10bpc = true;
+               else if (hdmi->dsc_cap.bpc_supported == 12)
+                       edid_caps->frl_dsc_12bpc = true;
+               edid_caps->frl_dsc_all_bpp = hdmi->dsc_cap.all_bpp;
+               edid_caps->frl_dsc_native_420 = hdmi->dsc_cap.native_420;
+               edid_caps->frl_dsc_max_slices = 
get_dsc_max_slices(hdmi->dsc_cap.max_slices, hdmi->dsc_cap.clk_per_slice);
+               edid_caps->frl_dsc_max_frl_rate = 
get_max_frl_rate(hdmi->dsc_cap.max_lanes, hdmi->dsc_cap.max_frl_rate_per_lane);
+               edid_caps->frl_dsc_total_chunk_kbytes = 
hdmi->dsc_cap.total_chunk_kbytes;
+       }
 }
 
 enum dc_edid_status dm_helpers_read_local_edid(
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 8e2a8c2c1d84..bc5a9e1663ba 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -295,6 +295,17 @@ static bool validate_dsc_caps_on_connector(struct 
amdgpu_dm_connector *aconnecto
        if (!aconnector->dsc_aux)
                return false;
 
+       if (port->passthrough_aux &&
+           aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support &&
+           dc_sink->edid_caps.frl_dsc_support &&
+           dc_sink->edid_caps.max_frl_rate > 0 &&
+           dc_sink->edid_caps.frl_dsc_max_frl_rate > 0) {
+               dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc,
+                                     &dc_sink->edid_caps, 
&dc_sink->dsc_caps.dsc_dec_caps);
+               DRM_DEBUG_DRIVER("%s: [%s] DSC is selected from FRL RX\n", 
__func__, aconnector->base.name);
+               return true;
+       } else {
+               DRM_DEBUG_DRIVER("%s: [%s] DSC is selected from DP-HDMI 
PCON\n", __func__, aconnector->base.name);
        if (drm_dp_dpcd_read(aconnector->dsc_aux, DP_DSC_SUPPORT, dsc_caps, 16) 
< 0)
                return false;
 
@@ -307,6 +318,7 @@ static bool validate_dsc_caps_on_connector(struct 
amdgpu_dm_connector *aconnecto
                                  &dc_sink->dsc_caps.dsc_dec_caps))
                return false;
 
+       }
        return true;
 }
 #endif
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c 
b/drivers/gpu/drm/amd/display/dc/core/dc.c
index b4ad3ec0f029..517844a99451 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -4050,6 +4050,12 @@ static void add_link_update_dsc_config_sequence(
                                        pipe_ctx->stream_res.stream_enc,
                                        true, dsc_packed_pps, false);
                }
+               else if (dc_is_hdmi_frl_signal(stream->signal)) {
+                       hwss_add_hpo_frl_stream_enc_set_dsc_config(seq_state,
+                               pipe_ctx->stream_res.hpo_frl_stream_enc,
+                               &stream->timing,
+                               dsc_packed_pps);
+               }
        }
 }
 
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c 
b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
index c0c35d6653a5..ad01ffdc73c3 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
@@ -1642,6 +1642,9 @@ void hwss_execute_sequence(struct dc *dc,
                case STREAM_ENC_DP_SET_DSC_PPS_INFO_PACKET:
                        hwss_stream_enc_dp_set_dsc_pps_info_packet(params);
                        break;
+               case HPO_FRL_STREAM_ENC_SET_DSC_CONFIG:
+                       hwss_hpo_frl_stream_enc_set_dsc_config(params);
+                       break;
                case DP_TRACE_SOURCE_SEQUENCE:
                        hwss_dp_trace_source_sequence(params);
                        break;
@@ -3734,6 +3737,16 @@ void hwss_stream_enc_dp_set_dsc_pps_info_packet(union 
block_sequence_params *par
                        
params->stream_enc_dp_set_dsc_pps_info_packet_params.pps_sdp_stream);
 }
 
+void hwss_hpo_frl_stream_enc_set_dsc_config(union block_sequence_params 
*params)
+{
+       if (params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc 
&&
+           
params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config)
+               
params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+                       
params->hpo_frl_stream_enc_set_dsc_config_params.hpo_frl_stream_enc,
+                       (struct dc_crtc_timing 
*)params->hpo_frl_stream_enc_set_dsc_config_params.timing,
+                       
params->hpo_frl_stream_enc_set_dsc_config_params.dsc_packed_pps);
+}
+
 void hwss_set_dmdata_attributes(union block_sequence_params *params)
 {
        struct hubp *hubp = params->set_dmdata_attributes_params.hubp;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c 
b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index 2457b563e6c8..0815400468ed 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4592,6 +4592,38 @@ enum dc_status dc_validate_with_context(struct dc *dc,
 }
 
 #if defined(CONFIG_DRM_AMD_DC_FP)
+/**
+ * dc_update_modified_pix_clock_for_dsc_with_padding() - update pix_clk for 
dsc with padding
+ *
+ * @dc_stream_state: Pointer to the stream structure.
+ * @dc_crtc_timing: Pointer to the stream dc_crtc_timing structure.
+ * @dsc_padding_params: Pointer to the DSC padding parameters structure.
+ *
+ * This function updated the pix_clk for dsc with padding stored in pipe_ctx
+ * such that the OTG h_active time fits withing the expected compressed active
+ * time calculated according to HDMI spec. H_total is then increased to
+ * maintain the same OTG line time as before the increased pix_clk.
+ */
+static void dc_update_modified_pix_clock_for_dsc_with_padding(const struct 
dc_stream_state *stream,
+               const struct dc_crtc_timing *timing, struct dsc_padding_params 
*dsc_padding_params)
+{
+       DC_FP_START();
+       
frl_modified_pix_clock_for_dsc_padding(stream->link->frl_verified_link_cap.borrow_params.hc_active_target,
+       stream->link->frl_verified_link_cap.borrow_params.hc_blank_target,
+       stream->link->frl_verified_link_cap.frl_num_lanes,
+       timing->pix_clk_100hz,
+       stream->link->frl_verified_link_cap.frl_link_rate,
+       timing->h_addressable,
+       timing->h_border_left,
+       timing->h_border_right,
+       timing->h_total,
+       (timing->h_addressable + dsc_padding_params->dsc_hactive_padding),
+       &dsc_padding_params->dsc_pix_clk_100hz,
+       &dsc_padding_params->dsc_htotal_padding);
+       DC_FP_END();
+
+       dsc_padding_params->dsc_htotal_padding = 
dsc_padding_params->dsc_htotal_padding - timing->h_total;
+}
 #endif /* CONFIG_DRM_AMD_DC_FP */
 
 /**
@@ -4618,6 +4650,28 @@ static void 
calculate_timing_params_for_dsc_with_padding(struct pipe_ctx *pipe_c
        if (stream)
                pipe_ctx->dsc_padding_params.dsc_pix_clk_100hz = 
stream->timing.pix_clk_100hz;
 
+       uint32_t hactive;
+       uint32_t ceil_slice_width;
+       if (stream && stream->timing.flags.DSC) {
+               hactive = stream->timing.h_addressable + 
stream->timing.h_border_left + stream->timing.h_border_right;
+
+               /* Assume if determined slices does not divide Hactive evenly, 
Hborrow is needed for padding*/
+               if (hactive % stream->timing.dsc_cfg.num_slices_h != 0) {
+                       ceil_slice_width = (hactive / 
stream->timing.dsc_cfg.num_slices_h) + 1;
+
+                       /* If YCBCR420 slice width must be even */
+                       if (stream->timing.pixel_encoding == 
PIXEL_ENCODING_YCBCR420 && ceil_slice_width % 2 != 0)
+                               ceil_slice_width++;
+
+                       pipe_ctx->dsc_padding_params.dsc_hactive_padding =
+                               (uint8_t)(ceil_slice_width * 
stream->timing.dsc_cfg.num_slices_h - hactive);
+
+                       if (stream->timing.h_total - hactive - 
pipe_ctx->dsc_padding_params.dsc_hactive_padding < 32)
+                               
pipe_ctx->dsc_padding_params.dsc_hactive_padding = 0;
+
+                       
dc_update_modified_pix_clock_for_dsc_with_padding(stream, &stream->timing, 
&pipe_ctx->dsc_padding_params);
+               }
+       }
 }
 
 /**
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c 
b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
index f694c6ad6e98..c1984ebe0ef1 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
@@ -99,7 +99,7 @@ void update_stream_signal(struct dc_stream_state *stream, 
struct dc_sink *sink)
 
                if (stream->link->frl_flags.force_frl_always ||
                                stream->link->frl_flags.force_frl_max
-                               )
+                               || stream->link->frl_flags.force_frl_dsc)
                        stream->signal = SIGNAL_TYPE_HDMI_FRL;
        }
 }
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h 
b/drivers/gpu/drm/amd/display/dc/dc.h
index 965fa9830355..4c75595a5742 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -121,6 +121,12 @@ struct frl_cap_chk_params_fixed31_32 {
 
        bool     compressed;              /* set to true if DSC is enabled */
        bool     bypass_hc_target_calc;   /* debug only */
+       bool     allow_all_bpp;           /* dsc_all_bpp */
+
+       /* DSC parameters */
+       int      slices;
+       int      slice_width;
+       struct fixed31_32   bpp_target;
        int      layout;
        int      acat;    /* not supported */
 
@@ -1151,6 +1157,7 @@ struct dc_debug_options {
        int  select_ffe;
        int  limit_ffe;
        bool force_frl_always;
+       bool force_frl_dsc;
        bool force_frl_max;
        bool apply_vsdb_rcc_wa;
        bool enable_hdmi_idcc;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h 
b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
index 101bce6b8de6..c0564167c587 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
@@ -63,11 +63,19 @@ struct dc_dsc_config_options {
        bool force_dsc_when_not_needed;
 };
 
+struct dc_dsc_primary_bpp {
+       uint32_t vic;
+       uint32_t target_bpp;
+};
 bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
                const uint8_t *dpcd_dsc_basic_data,
                const uint8_t *dpcd_dsc_ext_data,
                struct dsc_dec_dpcd_caps *dsc_sink_caps);
 
+bool dc_dsc_parse_dsc_edid(const struct dc *dc,
+               const struct dc_edid_caps *edid_caps,
+               struct dsc_dec_dpcd_caps *dsc_sink_caps);
+
 bool dc_dsc_compute_bandwidth_range(
                const struct display_stream_compressor *dsc,
                uint32_t dsc_min_slice_height_override,
diff --git a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h 
b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
index afd21eb6bbcd..5923a5f112a9 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
@@ -138,6 +138,7 @@ union hdmi_scdc_status_flags_data {
                uint8_t LANE3_LOCKED:1;
                uint8_t RESERVED:1;
                uint8_t FLT_READY:1;
+               uint8_t DSC_DECODEFAIL:1;
        } fields;
 };
 
@@ -270,6 +271,7 @@ struct dc_hdmi_frl_flags {
        int  select_ffe;
        int  limit_ffe;
        bool force_frl_always;
+       bool force_frl_dsc;
        bool force_frl_max;
        bool apply_vsdb_rcc_wa;
 };
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c 
b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
index 58479bea9976..523d8261be94 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.c
@@ -780,6 +780,38 @@ void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct 
hpo_frl_stream_encoder *e
                break;
        }
 
+       if (timing->flags.DSC &&
+                       (unsigned int)frl_link_settings->frl_link_rate > 
dsc_max_rate) {
+               if (dsc_max_rate < HDMI_FRL_LINK_RATE_6GBPS_4LANE) {
+                       frl_params->lanes = 3;
+               } else {
+                       frl_params->lanes = 4;
+               }
+
+               switch (dsc_max_rate) {
+               case HDMI_FRL_LINK_RATE_3GBPS:
+                       frl_params->r_bit_nominal = 3.0e9;
+                       break;
+               case HDMI_FRL_LINK_RATE_6GBPS:
+               case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
+                       frl_params->r_bit_nominal = 6.0e9;
+                       break;
+               case HDMI_FRL_LINK_RATE_8GBPS:
+                       frl_params->r_bit_nominal = 8.0e9;
+                       break;
+               case HDMI_FRL_LINK_RATE_10GBPS:
+               default:
+                       frl_params->r_bit_nominal = 10.0e9;
+                       break;
+               case HDMI_FRL_LINK_RATE_12GBPS:
+                       frl_params->r_bit_nominal = 12.0e9;
+                       break;
+               }
+       }
+
+       if (timing->flags.DSC && timing->rid > 0)
+               frl_params->is_ovt = true;
+
        frl_params->f_pixel_clock_nominal = (double)timing->pix_clk_100hz * 100;
        frl_params->h_active = timing->h_addressable + timing->h_border_left + 
timing->h_border_right;
        frl_params->h_blank = timing->h_total - frl_params->h_active;
@@ -795,6 +827,12 @@ void hpo_fpu_enc3_validate_hdmi_frl_output_timing(
 
        if (timing->flags.DSC) {
                frl_params->compressed = true;
+               frl_params->slices = timing->dsc_cfg.num_slices_h;
+               frl_params->slice_width = (frl_params->h_active + 
frl_params->slices - 1) / frl_params->slices;
+               // If YCBCR420 slice width must be even
+               if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 && 
frl_params->slice_width % 2 != 0)
+                       frl_params->slice_width++;
+               frl_params->bpp_target = timing->dsc_cfg.bits_per_pixel / 16.0;
        } else {
                frl_params->compressed = false;
        }
@@ -826,5 +864,5 @@ enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct 
hpo_frl_stream_encoder
                                                   struct 
frl_cap_chk_intermediates *inter)
 {
        (void)enc;
-       return -5;
+       return dml1_frl_cap_chk_compressed(params, inter);
 }
\ No newline at end of file
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c 
b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
index b54d24cdfbe2..55db25437952 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/display_mode_vba_30.c
@@ -3358,6 +3358,9 @@ static double TruncToValidBPP(
        hdmifrlparams.h_active = HActive;
        hdmifrlparams.h_blank = HTotal - HActive;
        hdmifrlparams.compressed = DSCEnable;
+       hdmifrlparams.slices = DSCSlices;
+       hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 
1.0);
+       hdmifrlparams.bpp_target = DesiredBPP;
 
        if (Format == dm_420) {
                NonDSCBPP0 = 12;
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c 
b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
index 96fdb3f6fa37..3216fd10ad04 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
@@ -3665,6 +3665,9 @@ static double TruncToValidBPP(
        hdmifrlparams.h_active = HActive;
        hdmifrlparams.h_blank = HTotal - HActive;
        hdmifrlparams.compressed = DSCEnable;
+       hdmifrlparams.slices = DSCSlices;
+       hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 
1.0);
+       hdmifrlparams.bpp_target = DesiredBPP;
 
        if (Format == dm_420) {
                NonDSCBPP0 = 12;
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c 
b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
index 50c217413abf..2f412cee57c7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
@@ -3771,6 +3771,9 @@ static double TruncToValidBPP(
        hdmifrlparams.h_active = HActive;
        hdmifrlparams.h_blank = HTotal - HActive;
        hdmifrlparams.compressed = DSCEnable;
+       hdmifrlparams.slices = DSCSlices;
+       hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 
1.0);
+       hdmifrlparams.bpp_target = DesiredBPP;
 
        if (Format == dm_420) {
                NonDSCBPP0 = 12;
diff --git 
a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c 
b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
index 530f6fdc8df3..2ddc30d08b73 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
@@ -1702,6 +1702,9 @@ double dml32_TruncToValidBPP(
        hdmifrlparams.h_blank = HTotal - HActive;
        hdmifrlparams.bpc = (int)(DesiredBPP / 3);
        hdmifrlparams.compressed = DSCEnable;
+       hdmifrlparams.slices = DSCSlices;
+       hdmifrlparams.slice_width = (int)dml_ceil((double) HActive / DSCSlices, 
1.0);
+       hdmifrlparams.bpp_target = DesiredBPP;
 
        if (Format == dm_420) {
                NonDSCBPP0 = 12;
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c 
b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
index 9dde4e56f237..327d89b66613 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
@@ -430,10 +430,207 @@ enum frl_cap_chk_result 
dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params
        return FRL_CAP_CHK_OK;
 }
 
+enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct frl_cap_chk_params 
*params,
+                                                   struct 
frl_cap_chk_intermediates *inter)
+{
+       enum frl_cap_chk_result res;
+       int      c_frl_available;
+#if defined(DEBUG_FRL_CAP_CHK)
+       int      c_frl_active_available;
+       int      c_frl_blank_available;
+#endif
+       int      bytes_target = 0;
+       int      hc_active_target;
+       int      hc_blank_target_est1;
+       int      hc_blank_target_est2;
+       int      hc_blank_target = 0;
+       int      c_frl_actual_target_payload;
+       double   utilization_targeted;
+       double   margin_target;
+       double   f_tb_average;
+       double   t_active_ref;
+       double   t_blank_ref;
+       double   t_active_target;
+       double   t_blank_target;
+       double   tb_borrowed;
+#ifdef DEBUG_FRL_CAP_CHK
+       double   tb_delta;
+       double   tb_delta_limit;
+       int      tb_worst;
+#endif
+       int table_size_444 = ARRAY_SIZE(prim_format_444);
+       int table_size_422 = ARRAY_SIZE(prim_format_422);
+       int table_size_420 = ARRAY_SIZE(prim_format_420);
+       int i;
+       bool hc_active_blank_predefined = false;
+
+       dc_assert_fp_enabled();
+
+       res = dml1_frl_cap_chk_common(inter, params);
+
+       if (res != FRL_CAP_CHK_OK)
+               return res;
+
+       c_frl_available        = (int)dml_floor((1 - inter->overhead_max) * 
inter->c_frl_line, 1);
+#if defined(DEBUG_FRL_CAP_CHK)
+       c_frl_active_available = dml_floor(c_frl_available * 
((double)params->h_active / (params->h_active + params->h_blank)), 1);
+       c_frl_blank_available  = dml_floor(c_frl_available * 
((double)params->h_blank / (params->h_active + params->h_blank)), 1);
+#endif
+       bytes_target           = (int)(params->slices * 
dml_ceil(params->bpp_target * params->slice_width / 8.0, 1));
+
+       if (!params->bypass_hc_target_calc)
+                       hc_active_target = (int)dml_ceil(bytes_target / 3.0, 1);
+       else
+               hc_active_target = params->borrow_params.hc_active_target;
+
+       if (!params->allow_all_bpp && params->vic != 0) {
+               if (params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_444) {
+                       for (i = 0; i < table_size_444 ; i++) {
+                               if (prim_format_444[i].vic == params->vic) {
+                                       params->borrow_params.hc_active_target 
= prim_format_444[i].hc_active;
+                                       params->borrow_params.hc_blank_target  
= prim_format_444[i].hc_blank;
+                                       hc_active_blank_predefined = true;
+                                       break;
+                               }
+                       }
+               } else if (params->pixel_encoding == 
HDMI_FRL_PIXEL_ENCODING_422) {
+                       for (i = 0; i < table_size_422 ; i++) {
+                               if (prim_format_422[i].vic == params->vic) {
+                                       params->borrow_params.hc_active_target 
= prim_format_422[i].hc_active;
+                                       params->borrow_params.hc_blank_target  
= prim_format_422[i].hc_blank;
+                                       hc_active_blank_predefined = true;
+                                       break;
+                               }
+                       }
+               } else if (params->pixel_encoding == 
HDMI_FRL_PIXEL_ENCODING_420) {
+                       for (i = 0; i < table_size_420 ; i++) {
+                               if (prim_format_420[i].vic == params->vic) {
+                                       params->borrow_params.hc_active_target 
= prim_format_420[i].hc_active;
+                                       params->borrow_params.hc_blank_target  
= prim_format_420[i].hc_blank;
+                                       hc_active_blank_predefined = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if (hc_active_blank_predefined) {
+                       hc_active_target = 
params->borrow_params.hc_active_target;
+                       hc_blank_target = params->borrow_params.hc_blank_target;
+               }
+       }
+
+       hc_blank_target_est1 = (int)dml_ceil(hc_active_target * 
((double)params->h_blank / params->h_active), 1);
+       hc_blank_target_est2 = (int)dml_max(hc_blank_target_est1, 
inter->blank_audio_min);
+
+       if (!hc_active_blank_predefined) {
+               if (!params->bypass_hc_target_calc) {
+                       hc_blank_target = (int)(4 * 
dml_floor(dml_min(hc_blank_target_est2, c_frl_available - 3.0 / 2.0 * 
hc_active_target) / 4.0, 1));
+
+                       params->borrow_params.hc_active_target = 
hc_active_target;
+                       params->borrow_params.hc_blank_target  = 
hc_blank_target;
+               } else {
+                       hc_blank_target  = 
params->borrow_params.hc_blank_target;
+               }
+       }
+
+#ifdef DEBUG_FRL_CAP_CHK
+       {
+               frl_dump_var("%i", c_frl_available);
+               frl_dump_var("%i", c_frl_active_available);
+               frl_dump_var("%i", c_frl_blank_available);
+               frl_dump_var("%i", bytes_target);
+               frl_dump_var("%i", hc_active_target);
+               frl_dump_var("%i", hc_blank_target_est1);
+               frl_dump_var("%i", hc_blank_target_est2);
+               frl_dump_var("%i", hc_blank_target);
+       }
+#endif
+
+       if (!(inter->blank_audio_min <= hc_blank_target)) {
+               frl_dump_var("%i", inter->blank_audio_min);
+               frl_dump_var("%i", hc_blank_target);
+               return FRL_CAP_CHK_ERROR_AUDIO_BW;
+       }
+
+       f_tb_average    = inter->f_pixel_clock_max / (params->h_active + 
params->h_blank) * (hc_active_target + hc_blank_target);
+       t_active_ref    = inter->t_line * ((double)params->h_active / 
(params->h_active + params->h_blank));
+       t_blank_ref     = inter->t_line - t_active_ref; // * ((double) 
params->h_blank / (params->h_active + params->h_blank));
+       t_active_target = dml_max((hc_active_target / f_tb_average),
+                                 (3.0 / 2.0 * hc_active_target) /
+                                 (params->lanes * inter->r_frl_char_min * (1.0 
- inter->overhead_max)));
+       t_blank_target  = inter->t_line - t_active_target;
+
+       tb_borrowed     = t_active_target * f_tb_average - hc_active_target;
+#ifdef DEBUG_FRL_CAP_CHK
+       tb_delta        = dcn_bw_fabs(t_active_target - t_active_ref) * 
(hc_active_target + hc_blank_target_est1) / inter->t_line;
+
+       {
+               frl_dump_var("%le", f_tb_average);
+               frl_dump_var("%le", t_active_ref);
+               frl_dump_var("%le", t_blank_ref);
+               frl_dump_var("%le", t_active_target);
+               frl_dump_var("%le", t_blank_target);
+               frl_dump_var("%le", tb_delta);
+       }
+#endif
+
+       if (t_blank_target - t_blank_ref > DBL_EPSILON) {
+#ifdef DEBUG_FRL_CAP_CHK
+               tb_delta_limit = (t_active_ref - hc_active_target / 
f_tb_average) * (hc_active_target + hc_blank_target_est1) / inter->t_line;
+#endif
+               params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_ACTIVE;
+       } else if (t_active_target - t_active_ref > DBL_EPSILON) {
+#ifdef DEBUG_FRL_CAP_CHK
+               tb_delta_limit = tb_delta;
+#endif
+               params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK;
+       } else {
+#ifdef DEBUG_FRL_CAP_CHK
+               tb_delta_limit = 0;
+#endif
+               params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE;
+       }
+
+#ifdef DEBUG_FRL_CAP_CHK
+       tb_worst = dml_ceil(dml_max(tb_borrowed, tb_delta_limit), 1);
+
+       {
+               frl_dump_var("%le", tb_delta_limit);
+               frl_dump_var("%le", tb_borrowed);
+               frl_dump_var("%i", params->borrow_params.borrow_mode);
+               frl_dump_var("%i", tb_worst);
+       }
+#endif
+
+       if (!(tb_borrowed <= TB_BORROWED_MAX))
+               return FRL_CAP_CHK_ERROR_MAX_BORROW;
+
+       c_frl_actual_target_payload = (int)(dml_ceil(3.0 / 2.0 * 
hc_active_target, 1) + hc_blank_target);
+       utilization_targeted        = c_frl_actual_target_payload / 
inter->c_frl_line;
+       margin_target               = 1.0 - (utilization_targeted + 
inter->overhead_max);
+
+#ifdef DEBUG_FRL_CAP_CHK
+       {
+               frl_dump_var("%i",  c_frl_actual_target_payload);
+               frl_dump_var("%le", utilization_targeted);
+               frl_dump_var("%le", margin_target);
+       }
+#endif
+
+       // oversubscribed bandwidth relative to margin
+       if (margin_target < 0 && dcn_bw_fabs(margin_target) > EPSILON)
+               return FRL_CAP_CHK_ERROR_MARGIN;
+
+       return FRL_CAP_CHK_OK;
+}
+
 enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params)
 {
        struct frl_cap_chk_intermediates inter;
 
+       if (params->compressed)
+               return dml1_frl_cap_chk_compressed(params, &inter);
+
        return dml1_frl_cap_chk_inter(params, &inter);
 }
 
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h 
b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
index 545f498ea396..debe4c1dc0f7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
+++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.h
@@ -119,6 +119,13 @@ struct frl_cap_chk_params {
 
        bool     compressed;              /* set to true if DSC is enabled */
        bool     bypass_hc_target_calc;   /* debug only */
+       bool     allow_all_bpp;           /* dsc_all_bpp */
+
+       /* DSC parameters */
+       int      slices;
+       int      slice_width;
+       double   bpp_target;
+       bool     is_ovt;
        int      layout;
        int      acat;    /* not supported */
 
@@ -138,6 +145,8 @@ enum frl_cap_chk_result dml1_frl_cap_chk_common(struct 
frl_cap_chk_intermediates
 enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct 
frl_cap_chk_params *params,
                                                      struct 
frl_cap_chk_intermediates *inter);
 
+enum frl_cap_chk_result dml1_frl_cap_chk_compressed(struct frl_cap_chk_params 
*params,
+                                                   struct 
frl_cap_chk_intermediates *inter);
 #endif
 
 void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target,
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c 
b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
index 8dfb6dd14eb2..c0f489ccf5e1 100644
--- a/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dc_dsc.c
@@ -151,6 +151,166 @@ uint32_t dc_bandwidth_in_kbps_from_timing(
        return kbps;
 }
 
+const struct dc_dsc_primary_bpp prim_bpp_444[] = {
+       /* VIC/BPP */
+       {64,  192}, /* 1920x1080 @ 100 */
+       {77,  192}, /* 1920x1080 @ 100 */
+       {63,  192}, /* 1920x1080 @ 120 */
+       {78,  192}, /* 1920x1080 @ 120 */
+       {93,  192}, /* 3840x2160 @ 24 */
+       {103, 192}, /* 3840x2160 @ 24 */
+       {94,  192}, /* 3840x2160 @ 25 */
+       {104, 192}, /* 3840x2160 @ 25 */
+       {95,  192}, /* 3840x2160 @ 30 */
+       {105, 192}, /* 3840x2160 @ 30 */
+       {114, 192}, /* 3840x2160 @ 48 */
+       {116, 192}, /* 3840x2160 @ 48 */
+       {96,  192}, /* 3840x2160 @ 50 */
+       {106, 192}, /* 3840x2160 @ 50 */
+       {97,  192}, /* 3840x2160 @ 60 */
+       {107, 192}, /* 3840x2160 @ 60 */
+       {117, 192}, /* 3840x2160 @ 100 */
+       {119, 192}, /* 3840x2160 @ 100 */
+       {118, 192}, /* 3840x2160 @ 120 */
+       {120, 192}, /* 3840x2160 @ 120 */
+       {98,  192}, /* 4096x2160 @ 24 */
+       {99,  192}, /* 4096x2160 @ 25 */
+       {100, 192}, /* 4096x2160 @ 30 */
+       {115, 192}, /* 4096x2160 @ 48 */
+       {101, 192}, /* 4096x2160 @ 50 */
+       {102, 192}, /* 4096x2160 @ 60 */
+       {218, 192}, /* 4096x2160 @ 100 */
+       {219, 192}, /* 4096x2160 @ 120 */
+       {121, 192}, /* 5120x2160 @ 24 */
+       {122, 192}, /* 5120x2160 @ 25 */
+       {123, 192}, /* 5120x2160 @ 30 */
+       {124, 192}, /* 5120x2160 @ 48 */
+       {125, 192}, /* 5120x2160 @ 50 */
+       {126, 173}, /* 5120x2160 @ 60 */
+       {127, 192}, /* 5120x2160 @ 100 */
+       {193, 175}, /* 5120x2160 @ 120 */
+       {194, 192}, /* 7680x2160 @ 24 */
+       {202, 192}, /* 7680x2160 @ 24 */
+       {195, 192}, /* 7680x2160 @ 25 */
+       {203, 192}, /* 7680x2160 @ 25 */
+       {196, 192}, /* 7680x2160 @ 30 */
+       {204, 192}, /* 7680x2160 @ 30 */
+       {197, 157}, /* 7680x2160 @ 48 */
+       {205, 157}, /* 7680x2160 @ 48 */
+       {198, 157}, /* 7680x2160 @ 50 */
+       {206, 157}, /* 7680x2160 @ 50 */
+       {199, 159}, /* 7680x2160 @ 60 */
+       {207, 159}, /* 7680x2160 @ 60 */
+       {200, 134}, /* 7680x2160 @ 100 */
+       {208, 134}, /* 7680x2160 @ 100 */
+       {201, 130}, /* 7680x2160 @ 120 */
+       {209, 130}, /* 7680x2160 @ 120 */
+       {210, 182}, /* 10240x4320 @ 24 */
+       {211, 181}, /* 10240x4320 @ 25 */
+       {212, 177}, /* 10240x4320 @ 30 */
+       {213, 163}, /* 10240x4320 @ 48 */
+       {214, 162}, /* 10240x4320 @ 50 */
+       {215, 157}, /* 10240x4320 @ 60 */
+};
+const struct dc_dsc_primary_bpp prim_bpp_422[] = {
+       /* VIC/BPP */
+       {114, 192}, /* 3840x2160 @ 48 */
+       {116, 192}, /* 3840x2160 @ 48 */
+       {96,  192}, /* 3840x2160 @ 50 */
+       {106, 192}, /* 3840x2160 @ 50 */
+       {97,  192}, /* 3840x2160 @ 60 */
+       {107, 192}, /* 3840x2160 @ 60 */
+       {117, 137}, /* 3840x2160 @ 100 */
+       {119, 137}, /* 3840x2160 @ 100 */
+       {118, 113}, /* 3840x2160 @ 120 */
+       {120, 113}, /* 3840x2160 @ 120 */
+       {115, 192}, /* 4096x2160 @ 48 */
+       {101, 192}, /* 4096x2160 @ 50 */
+       {102, 192}, /* 4096x2160 @ 60 */
+       {218, 192}, /* 4096x2160 @ 100 */
+       {219, 192}, /* 4096x2160 @ 120 */
+       {121, 192}, /* 5120x2160 @ 24 */
+       {122, 192}, /* 5120x2160 @ 25 */
+       {123, 192}, /* 5120x2160 @ 30 */
+       {124, 192}, /* 5120x2160 @ 48 */
+       {125, 192}, /* 5120x2160 @ 50 */
+       {126, 173}, /* 5120x2160 @ 60 */
+       {127, 192}, /* 5120x2160 @ 100 */
+       {193, 175}, /* 5120x2160 @ 120 */
+       {194, 123}, /* 7680x2160 @ 24 */
+       {202, 123}, /* 7680x2160 @ 24 */
+       {195, 123}, /* 7680x2160 @ 25 */
+       {203, 123}, /* 7680x2160 @ 25 */
+       {196, 118}, /* 7680x2160 @ 30 */
+       {204, 118}, /* 7680x2160 @ 30 */
+       {197, 123}, /* 7680x2160 @ 48 */
+       {205, 123}, /* 7680x2160 @ 48 */
+       {198, 123}, /* 7680x2160 @ 50 */
+       {206, 123}, /* 7680x2160 @ 50 */
+       {199, 119}, /* 7680x2160 @ 60 */
+       {207, 119}, /* 7680x2160 @ 60 */
+       {200, 134}, /* 7680x2160 @ 100 */
+       {208, 134}, /* 7680x2160 @ 100 */
+       {201, 130}, /* 7680x2160 @ 120 */
+       {209, 130}, /* 7680x2160 @ 120 */
+       {210, 182}, /* 10240x4320 @ 24 */
+       {211, 181}, /* 10240x4320 @ 25 */
+       {212, 177}, /* 10240x4320 @ 30 */
+       {213, 126}, /* 10240x4320 @ 48 */
+       {214, 125}, /* 10240x4320 @ 50 */
+       {215, 117}, /* 10240x4320 @ 60 */
+       {216, 125}, /* 10240x4320 @ 100 */
+       {217, 117}, /* 10240x4320 @ 120 */
+};
+
+const struct dc_dsc_primary_bpp prim_bpp_420[] = {
+       /* VIC/BPP */
+       {114, 192}, /* 3840x2160 @ 48 */
+       {116, 192}, /* 3840x2160 @ 48 */
+       {96,  192}, /* 3840x2160 @ 50 */
+       {106, 192}, /* 3840x2160 @ 50 */
+       {97,  192}, /* 3840x2160 @ 60 */
+       {107, 192}, /* 3840x2160 @ 60 */
+       {117, 137}, /* 3840x2160 @ 100 */
+       {119, 137}, /* 3840x2160 @ 100 */
+       {118, 113}, /* 3840x2160 @ 120 */
+       {120, 113}, /* 3840x2160 @ 120 */
+       {115, 192}, /* 4096x2160 @ 48 */
+       {101, 192}, /* 4096x2160 @ 50 */
+       {102, 192}, /* 4096x2160 @ 60 */
+       {218, 129}, /* 4096x2160 @ 100 */
+       {219, 106}, /* 4096x2160 @ 120 */
+       {124, 192}, /* 5120x2160 @ 48 */
+       {125, 192}, /* 5120x2160 @ 50 */
+       {126, 173}, /* 5120x2160 @ 60 */
+       {127, 192}, /* 5120x2160 @ 100 */
+       {193, 175}, /* 5120x2160 @ 120 */
+       {194, 123}, /* 7680x4320 @ 24 */
+       {202, 123}, /* 7680x4320 @ 24 */
+       {195, 123}, /* 7680x4320 @ 25 */
+       {203, 123}, /* 7680x4320 @ 25 */
+       {196, 118}, /* 7680x4320 @ 30 */
+       {204, 118}, /* 7680x4320 @ 30 */
+       {197, 123}, /* 7680x4320 @ 48 */
+       {205, 123}, /* 7680x4320 @ 48 */
+       {198, 123}, /* 7680x4320 @ 50 */
+       {206, 123}, /* 7680x4320 @ 50 */
+       {199, 119}, /* 7680x4320 @ 60 */
+       {207, 119}, /* 7680x4320 @ 60 */
+       {200, 112}, /* 7680x4320 @ 100 */
+       {208, 112}, /* 7680x4320 @ 100 */
+       {201, 103}, /* 7680x4320 @ 120 */
+       {209, 103}, /* 7680x4320 @ 120 */
+       {210,  98}, /* 10240x4320 @ 24 */
+       {211,  98}, /* 10240x4320 @ 25 */
+       {212, 177}, /* 10240x4320 @ 30 */
+       {213,  98}, /* 10240x4320 @ 48 */
+       {214, 125}, /* 10240x4320 @ 50 */
+       {215, 117}, /* 10240x4320 @ 60 */
+       {216, 107}, /* 10240x4320 @ 100 */
+       {217,  97}, /* 10240x4320 @ 120 */
+};
+
 /* Forward Declerations */
 static unsigned int get_min_dsc_slice_count_for_odm(
                const struct display_stream_compressor *dsc,
@@ -194,6 +354,15 @@ static bool setup_dsc_config(
                int min_slice_count,
                struct dc_dsc_config *dsc_cfg);
 
+static bool convert_bandwidth_to_frl_params(
+       int bandwidth_kbps,
+       int *num_lanes,
+       int *frl_rate);
+
+static uint32_t compute_bpp_x16_from_frl_params(
+               const struct dc_crtc_timing *timing,
+               const uint32_t num_slices_h,
+               const struct dsc_enc_caps *dsc_caps);
 static bool dsc_buff_block_size_from_dpcd(int dpcd_buff_block_size, int 
*buff_block_size)
 {
 
@@ -328,6 +497,207 @@ static bool dsc_bpp_increment_div_from_dpcd(uint8_t 
bpp_increment_dpcd, uint32_t
 
 
 
+static bool get_vic_preset_bpp(
+               const struct dc_crtc_timing *timing,
+               int *preset_bpp)
+{
+       bool preset_found = false;
+       uint32_t table_size_444 = ARRAY_SIZE(prim_bpp_444);
+       uint32_t table_size_422 = ARRAY_SIZE(prim_bpp_422);
+       uint32_t table_size_420 = ARRAY_SIZE(prim_bpp_420);
+       uint32_t i;
+       uint32_t vid_id;
+
+       if (timing->vic == 0 && timing->hdmi_vic == 0)
+               return false;
+
+       vid_id = timing->vic;
+       switch (timing->hdmi_vic) {
+       case 1:
+               vid_id = 95;
+               break;
+       case 2:
+               vid_id = 94;
+               break;
+       case 3:
+               vid_id = 93;
+               break;
+       case 4:
+               vid_id = 98;
+               break;
+       default:
+               break;
+       }
+
+       if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
+                       timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
+               for (i = 0; i < table_size_444 ; i++) {
+                       if (prim_bpp_444[i].vic == vid_id) {
+                               preset_found = true;
+                               *preset_bpp = prim_bpp_444[i].target_bpp;
+                               break;
+                       }
+               }
+       } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
+               for (i = 0; i < table_size_422 ; i++) {
+                       if (prim_bpp_422[i].vic == vid_id) {
+                               preset_found = true;
+                               *preset_bpp = prim_bpp_422[i].target_bpp;
+                               break;
+                       }
+               }
+       } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) {
+               for (i = 0; i < table_size_420 ; i++) {
+                       if (prim_bpp_420[i].vic == vid_id) {
+                               preset_found = true;
+                               *preset_bpp = prim_bpp_420[i].target_bpp;
+                               break;
+                       }
+               }
+       } else {
+               return false;
+       }
+
+       return preset_found;
+}
+
+static int hdmi_dsc_get_num_slices(const struct dc_crtc_timing *timing)
+{
+       int k_slice_adjust = 1;
+       int adj_pix_clk_mhz;
+       int min_slices;
+       int slice_target;
+       int slice_width = timing->h_addressable;
+       int h_ratio_adj_pix_clk_mhz;
+
+       if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
+                       timing->pixel_encoding == PIXEL_ENCODING_YCBCR444)
+               k_slice_adjust = 2;
+
+       adj_pix_clk_mhz = k_slice_adjust * timing->pix_clk_100hz / 10000 / 2;
+       h_ratio_adj_pix_clk_mhz = adj_pix_clk_mhz * timing->h_addressable / 
timing->h_total;
+       if (adj_pix_clk_mhz <= 2720) {
+               min_slices = adj_pix_clk_mhz / 340;
+               if (adj_pix_clk_mhz % 340 != 0)
+                       min_slices++;
+       } else if (adj_pix_clk_mhz <= 4800) {
+               min_slices = adj_pix_clk_mhz / 400;
+               if (adj_pix_clk_mhz % 400 != 0)
+                       min_slices++;
+       } else if (h_ratio_adj_pix_clk_mhz <= 4800) {
+               min_slices = h_ratio_adj_pix_clk_mhz / 600;
+               if (h_ratio_adj_pix_clk_mhz % 600 != 0)
+                       min_slices++;
+       } else {
+               min_slices = h_ratio_adj_pix_clk_mhz / 900;
+               if (h_ratio_adj_pix_clk_mhz % 900 != 0)
+                       min_slices++;
+       }
+
+       do {
+               if (min_slices <= 1)
+                       slice_target = 1;
+               else if (min_slices <= 2)
+                       slice_target = 2;
+               else if (min_slices <= 4)
+                       slice_target = 4;
+               else if (min_slices <= 8)
+                       slice_target = 8;
+               else if (min_slices <= 12)
+                       slice_target = 12;
+               else if (min_slices <= 16)
+                       slice_target = 16;
+               else
+                       return 0;
+
+               slice_width = timing->h_addressable / slice_target;
+               min_slices++;
+       } while (slice_width > 2720);
+
+       return slice_target;
+}
+
+static int hdmi_dsc_get_bpp(const struct dc_crtc_timing *timing,
+               const struct dsc_enc_caps *dsc_common_caps)
+{
+       int max_dsc_bpp, min_dsc_bpp;
+       int target_bytes;
+       bool bpp_found = false;
+       int bpp_decrement_x16;
+       int src_fractional_bpp = dsc_common_caps->bpp_increment_div;
+       int bpp_target;
+       int bpp_target_x16;
+       int bpc_factor = 8;
+       int slice_width;
+       int num_slices;
+       bool hdmi_all_bpp = dsc_common_caps->is_vic_all_bpp;
+       int hdmi_max_chunk_bytes = dsc_common_caps->total_chunk_kbytes;
+
+       int preset_bpp;
+       bool preset_found = false;
+
+       if (timing->display_color_depth == COLOR_DEPTH_101010)
+               bpc_factor = 10;
+       if (timing->display_color_depth == COLOR_DEPTH_121212)
+               bpc_factor = 12;
+
+       /* Assuming: bpc as 8*/
+       if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
+                       timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
+               min_dsc_bpp = 8;
+               max_dsc_bpp = 3 * bpc_factor;
+       } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
+               min_dsc_bpp = 7;
+               max_dsc_bpp = 2 * bpc_factor;
+       } else {
+               min_dsc_bpp = 6;
+               max_dsc_bpp = 3 * bpc_factor / 2;
+       }
+
+       if (!hdmi_all_bpp)
+               max_dsc_bpp = min(max_dsc_bpp, 12);
+
+
+       num_slices = hdmi_dsc_get_num_slices(timing);
+       if (num_slices == 0)
+               return 0;
+
+       slice_width = timing->h_addressable / num_slices;
+
+       bpp_target = max_dsc_bpp;
+       bpp_decrement_x16 = 16 / src_fractional_bpp;
+       bpp_target_x16 = (bpp_target * 16) - bpp_decrement_x16;
+       if (!hdmi_all_bpp)
+                       bpp_target_x16 = (bpp_target * 16);
+
+       /* check if byte target is below allowed Kbytes */
+       while (bpp_target_x16 > (min_dsc_bpp * 16)) {
+               target_bytes = num_slices * slice_width * bpp_target_x16 / 16 / 
8;
+               if (target_bytes <= hdmi_max_chunk_bytes) {
+                       bpp_found = true;
+                       break;
+               }
+               bpp_target_x16 -= bpp_decrement_x16;
+       }
+
+       if (bpp_found) {
+               if (!hdmi_all_bpp) {
+                       /* Get preset bpp for CTA modes */
+                       preset_found = get_vic_preset_bpp(timing, &preset_bpp);
+                       if (preset_found) {
+                               bpp_target_x16 = preset_bpp;
+                               target_bytes =
+                                               num_slices * slice_width * 
bpp_target_x16 / 16 / 8;
+                               if (target_bytes > hdmi_max_chunk_bytes)
+                                       return 0;
+                       }
+               }
+               return bpp_target_x16;
+       }
+
+       return 0;
+}
+
 bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
                const uint8_t *dpcd_dsc_basic_data,
                const uint8_t *dpcd_dsc_branch_decoder_caps,
@@ -450,6 +820,104 @@ bool dc_dsc_parse_dsc_dpcd(const struct dc *dc,
        dsc_sink_caps->is_dp = true;
        return true;
 }
+bool dc_dsc_parse_dsc_edid(const struct dc *dc, const struct dc_edid_caps 
*edid_caps,
+               struct dsc_dec_dpcd_caps *dsc_sink_caps)
+{
+       (void)dc;
+       dsc_sink_caps->is_dsc_supported = edid_caps->frl_dsc_support;
+       if (!edid_caps->frl_dsc_support)
+               return false;
+
+       dsc_sink_caps->dsc_version = 0x21;
+       dsc_sink_caps->is_frl = true;
+       dsc_sink_caps->is_dp = false;
+
+       switch (edid_caps->frl_dsc_max_slices) {
+       case 0:
+               break;
+       case 1:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 340;
+               dsc_sink_caps->throughput_mode_1_mps = 680;
+               break;
+       case 2:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 340;
+               dsc_sink_caps->throughput_mode_1_mps = 680;
+               break;
+       case 3:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 340;
+               dsc_sink_caps->throughput_mode_1_mps = 680;
+               break;
+       case 4:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 340;
+               dsc_sink_caps->throughput_mode_1_mps = 680;
+               break;
+       case 5:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 400;
+               dsc_sink_caps->throughput_mode_1_mps = 800;
+               break;
+       case 6:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_12 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 400;
+               dsc_sink_caps->throughput_mode_1_mps = 800;
+               break;
+       case 7:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_12 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 600;
+               dsc_sink_caps->throughput_mode_1_mps = 1200;
+               break;
+       case 8:
+       default:
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_1 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_2 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_4 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_8 = 1;
+               dsc_sink_caps->slice_caps1.bits.NUM_SLICES_12 = 1;
+               dsc_sink_caps->throughput_mode_0_mps = 900;
+               dsc_sink_caps->throughput_mode_1_mps = 1800;
+               break;
+       }
+       dsc_sink_caps->lb_bit_depth = 13; //Table 7-25
+       dsc_sink_caps->is_block_pred_supported = true; //Table 7-25
+       dsc_sink_caps->color_formats.bits.RGB = 1;
+       dsc_sink_caps->color_formats.bits.YCBCR_444 = 1;
+       dsc_sink_caps->color_formats.bits.YCBCR_NATIVE_422 = 1;
+       dsc_sink_caps->color_formats.bits.YCBCR_NATIVE_420 =
+                       (edid_caps->frl_dsc_native_420 == true) ? 1 : 0;
+       dsc_sink_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1;
+       dsc_sink_caps->color_depth.bits.COLOR_DEPTH_10_BPC =
+                       (edid_caps->frl_dsc_10bpc == true) ? 1 : 0;
+       dsc_sink_caps->color_depth.bits.COLOR_DEPTH_12_BPC =
+                       (edid_caps->frl_dsc_12bpc == true) ? 1 : 0;
+       dsc_sink_caps->max_slice_width = 2560;
+       dsc_sink_caps->bpp_increment_div = 16; /* bpp increment divisor, e.g. 
if 16, it's 1/16th of a bit */
+       dsc_sink_caps->is_vic_all_bpp = edid_caps->frl_dsc_all_bpp;
+       dsc_sink_caps->total_chunk_kbytes =
+                       1024 * (1 + edid_caps->frl_dsc_total_chunk_kbytes);
+
+       return true;
+}
 
 /* If DSC is possbile, get DSC bandwidth range based on [min_bpp, max_bpp] 
target bitrate range and
  * timing's pixel clock and uncompressed bandwidth.
@@ -757,6 +1225,9 @@ 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->is_frl = dsc_sink_caps->is_frl;
+       dsc_common_caps->is_vic_all_bpp = dsc_sink_caps->is_vic_all_bpp;
+       dsc_common_caps->total_chunk_kbytes = dsc_sink_caps->total_chunk_kbytes;
        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;
@@ -786,6 +1257,191 @@ static uint32_t compute_bpp_x16_from_target_bandwidth(
        return dc_fixpt_floor(bpp_x16);
 }
 
+static bool convert_bandwidth_to_frl_params(
+       int bandwidth_kbps,
+       int *num_lanes,
+       int *frl_rate)
+{
+       if (bandwidth_kbps == 0)
+               return false;
+
+       switch (bandwidth_kbps) {
+       case 9000000:
+               *num_lanes = 3;
+               *frl_rate = 3000;
+               break;
+       case 18000000:
+               *num_lanes = 3;
+               *frl_rate = 6000;
+               break;
+       case 24000000:
+               *num_lanes = 4;
+               *frl_rate = 6000;
+               break;
+       case 32000000:
+               *num_lanes = 4;
+               *frl_rate = 8000;
+               break;
+       case 40000000:
+               *num_lanes = 4;
+               *frl_rate = 10000;
+               break;
+       case 48000000:
+               *num_lanes = 4;
+               *frl_rate = 12000;
+               break;
+       default:
+               return false;
+       }
+       return true;
+}
+
+static uint32_t compute_bpp_x16_from_frl_params(
+               const struct dc_crtc_timing *timing,
+               const uint32_t num_slices_h,
+               const struct dsc_enc_caps *dsc_caps)
+{
+       struct fixed31_32 pixel_clock;
+       uint32_t num_lanes = dsc_caps->num_lanes;
+       uint32_t frl_rate = dsc_caps->frl_rate;
+       uint32_t h_active = timing->h_addressable;
+       uint32_t h_blank = timing->h_total - timing->h_addressable;
+       uint32_t bpp_target_x16;
+       struct fixed31_32 r_bit;
+       uint32_t f_audio = 48000;
+       struct fixed31_32 pixel_rate_tolerance;
+       uint32_t audio_tolerance = 1000;
+       uint32_t frl_bit_tolerance = 300;
+       uint32_t acr_rate_max = 1500;
+       uint32_t c_frl_cb = 510;
+       uint32_t c_frl_sb;
+       struct fixed31_32 overhead_sb;
+       struct fixed31_32 overhead_rs;
+       struct fixed31_32 overhead_map;
+       struct fixed31_32 overhead_min;
+       struct fixed31_32 overhead_m;
+       struct fixed31_32 overhead_max;
+       struct fixed31_32 pixel_clock_max;
+       struct fixed31_32 t_line;
+       struct fixed31_32 r_bit_min;
+       struct fixed31_32 r_frl_char_min;
+       struct fixed31_32 c_frl_line;
+       uint32_t c_frl_line_int;
+       struct fixed31_32 c_frl_available;
+       uint32_t c_frl_av_int;
+       struct fixed31_32 c_frl_active_av;
+       struct fixed31_32 c_frl_blank_av;
+       uint32_t acat_ap = 4;
+       struct fixed31_32 r_ap;
+       struct fixed31_32 max_audio_tol_rate;
+       struct fixed31_32 avg_audio_packets_line;
+       uint32_t avg_audio_packets_line_int;
+       int hc_blank_audio_min;
+       uint32_t bytes_target;
+       uint32_t hc_active_target;
+       uint32_t hc_blank_target_est1;
+       uint32_t hc_blank_target_est2;
+       struct fixed31_32 hc_blank_target_bandwidth;
+       int hc_blank_target;
+       uint32_t bpc_factor = 8;
+       uint32_t min_dsc_bpp_x16;
+       uint32_t max_dsc_bpp_x16;
+       bool hdmi_all_bpp = dsc_caps->is_vic_all_bpp;
+       uint32_t slice_width;
+
+       if (timing->display_color_depth == COLOR_DEPTH_101010)
+               bpc_factor = 10;
+       if (timing->display_color_depth == COLOR_DEPTH_121212)
+               bpc_factor = 12;
+
+       /* Assuming: bpc as 8*/
+       if (timing->pixel_encoding == PIXEL_ENCODING_RGB ||
+                       timing->pixel_encoding == PIXEL_ENCODING_YCBCR444) {
+               min_dsc_bpp_x16 = 8 * 16;
+               max_dsc_bpp_x16 = 3 * 16 * bpc_factor;
+       } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
+               min_dsc_bpp_x16 = 7 * 16;
+               max_dsc_bpp_x16 = 2 * 16 * bpc_factor;
+       } else {
+               min_dsc_bpp_x16 = 6 * 16;
+               max_dsc_bpp_x16 = 3 * 16 * bpc_factor / 2;
+       }
+
+       max_dsc_bpp_x16 = MIN(max_dsc_bpp_x16, 256);
+       if (!hdmi_all_bpp)
+               max_dsc_bpp_x16 = MIN(max_dsc_bpp_x16, 192);
+
+       c_frl_sb = 4 * c_frl_cb + num_lanes;
+       pixel_clock = 
dc_fixpt_div_int(dc_fixpt_from_int(timing->pix_clk_100hz), 10000);
+       r_bit = dc_fixpt_from_int(frl_rate);
+       pixel_rate_tolerance = dc_fixpt_div_int(dc_fixpt_from_int(5), 1000);
+       overhead_sb = dc_fixpt_div_int(dc_fixpt_from_int(num_lanes), c_frl_sb);
+       overhead_rs = dc_fixpt_div_int(dc_fixpt_from_int(32), c_frl_sb);
+       overhead_map = dc_fixpt_div_int(dc_fixpt_from_int(25), (c_frl_sb * 10));
+       overhead_min = dc_fixpt_add(overhead_sb, overhead_rs);
+       overhead_min = dc_fixpt_add(overhead_min, overhead_map);
+       overhead_m = dc_fixpt_div_int(dc_fixpt_from_int(3), 1000);
+       overhead_max = dc_fixpt_add(overhead_min, overhead_m);
+       pixel_rate_tolerance = dc_fixpt_add_int(pixel_rate_tolerance, 1);
+       pixel_clock_max = dc_fixpt_mul(pixel_clock, pixel_rate_tolerance);
+       t_line = dc_fixpt_div(dc_fixpt_from_int(h_active + h_blank), 
pixel_clock_max);
+       r_bit_min = dc_fixpt_div_int(dc_fixpt_from_int(frl_bit_tolerance), 
1000000);
+       r_bit_min = dc_fixpt_sub(dc_fixpt_from_int(1), r_bit_min);
+       r_bit_min = dc_fixpt_mul(r_bit, r_bit_min);
+       r_frl_char_min = dc_fixpt_div_int(r_bit_min, 18);
+       c_frl_line = dc_fixpt_mul(t_line, r_frl_char_min);
+       c_frl_line = dc_fixpt_mul_int(c_frl_line, num_lanes);
+       c_frl_line_int = dc_fixpt_floor(c_frl_line);
+       c_frl_available = dc_fixpt_sub(dc_fixpt_from_int(1), overhead_max);
+       c_frl_available = dc_fixpt_mul_int(c_frl_available, c_frl_line_int);
+       c_frl_av_int = dc_fixpt_floor(c_frl_available);
+       c_frl_active_av = dc_fixpt_mul_int(dc_fixpt_from_int(c_frl_av_int), 
h_active);
+       c_frl_active_av = dc_fixpt_div_int(c_frl_active_av, (h_active + 
h_blank));
+       c_frl_blank_av = dc_fixpt_mul_int(dc_fixpt_from_int(c_frl_av_int), 
h_blank);
+       c_frl_blank_av =  dc_fixpt_div_int(c_frl_blank_av, (h_active + 
h_blank));
+       r_ap = dc_fixpt_max(dc_fixpt_from_int(192000),
+                       dc_fixpt_from_int(f_audio * acat_ap));
+       r_ap = dc_fixpt_add(r_ap, dc_fixpt_from_int(2 * acr_rate_max));
+       max_audio_tol_rate =  
dc_fixpt_div_int(dc_fixpt_from_int(audio_tolerance), 1000000);
+       max_audio_tol_rate =  dc_fixpt_add(dc_fixpt_from_int(1), 
max_audio_tol_rate);
+       r_ap = dc_fixpt_mul(r_ap, max_audio_tol_rate);
+       avg_audio_packets_line = dc_fixpt_mul(r_ap, t_line);
+       avg_audio_packets_line = dc_fixpt_div_int(avg_audio_packets_line, 
1000000);
+       avg_audio_packets_line_int = dc_fixpt_ceil(avg_audio_packets_line);
+       hc_blank_audio_min = 32 + 32 * avg_audio_packets_line_int;
+       slice_width = dc_fixpt_ceil(dc_fixpt_div_int(
+                       dc_fixpt_from_int(h_active), num_slices_h));
+
+       /* Slice width for 420 must be even */
+       if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 && slice_width % 
2 != 0) {
+               slice_width++;
+       }
+
+       for (uint32_t i = max_dsc_bpp_x16; i >= min_dsc_bpp_x16; i--) {
+               bpp_target_x16 = i;
+               bytes_target = num_slices_h * dc_fixpt_ceil(dc_fixpt_div_int(
+                               dc_fixpt_from_int(bpp_target_x16 * 
slice_width), 8 * 16));
+               hc_active_target = dc_fixpt_ceil(dc_fixpt_div_int(
+                               dc_fixpt_from_int(bytes_target), 3));
+               hc_blank_target_est1 = dc_fixpt_ceil(dc_fixpt_div_int(
+                               dc_fixpt_from_int(hc_active_target * h_blank), 
h_active));
+               hc_blank_target_est2 = dc_fixpt_floor(dc_fixpt_max(
+                               dc_fixpt_from_int(hc_blank_target_est1),
+                               dc_fixpt_from_int(hc_blank_audio_min)));
+               hc_blank_target_bandwidth = 
dc_fixpt_div_int(dc_fixpt_from_int(3), 2);
+               hc_blank_target_bandwidth = 
dc_fixpt_mul(hc_blank_target_bandwidth,
+                               dc_fixpt_from_int(hc_active_target));
+               hc_blank_target_bandwidth = 
dc_fixpt_sub(dc_fixpt_from_int(c_frl_av_int),
+                               hc_blank_target_bandwidth);
+               hc_blank_target_bandwidth = 
dc_fixpt_min(hc_blank_target_bandwidth,
+                               dc_fixpt_from_int(hc_blank_target_est2));
+               hc_blank_target_bandwidth = 
dc_fixpt_div_int(hc_blank_target_bandwidth, 4);
+               hc_blank_target = dc_fixpt_floor(hc_blank_target_bandwidth) * 4;
+               if (hc_blank_target >= hc_blank_audio_min)
+                       return bpp_target_x16;
+       }
+       return 0;
+}
 /* Decide DSC bandwidth range based on signal, timing, specs specific and 
input min and max
  * requirements.
  * The range output includes decided min/max target bpp, the respective 
bandwidth requirements
@@ -812,6 +1468,30 @@ static bool decide_dsc_bandwidth_range(
                        range->min_target_bpp_x16 = preferred_bpp_x16;
                }
        }
+       else if (dsc_caps->is_frl) {
+               uint32_t specs_preferred_bpp_x16 = hdmi_dsc_get_bpp(timing, 
dsc_caps);
+               uint32_t specs_calculated_bpp_x16 = 0;
+
+               if (timing->vic) {
+                       /* For CTA timing, we should strictly follow HDMI spec. 
*/
+                       range->max_target_bpp_x16 = specs_preferred_bpp_x16;
+                       if (dsc_caps->is_vic_all_bpp || dsc_caps->is_dp)
+                               range->min_target_bpp_x16 = min_bpp_x16;
+                       else
+                               range->min_target_bpp_x16 = 
specs_preferred_bpp_x16;
+               } else {
+                       if (timing->vic == 0 && timing->hdmi_vic == 0)
+                               specs_calculated_bpp_x16 = 
compute_bpp_x16_from_frl_params(
+                                               timing, num_slices_h, dsc_caps);
+
+                       if (specs_calculated_bpp_x16 != 0)
+                               specs_preferred_bpp_x16 = 
MIN(specs_calculated_bpp_x16,
+                                               specs_preferred_bpp_x16);
+
+                       range->max_target_bpp_x16 = MIN(max_bpp_x16, 
specs_preferred_bpp_x16);
+                       range->min_target_bpp_x16 = min_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 */
@@ -877,6 +1557,10 @@ static bool decide_dsc_target_bpp_x16(
                                        dsc_common_caps->bpp_increment_div,
                                        dsc_common_caps->is_dp);
                }
+               /* Assign minimum bpp and validate TB borrow scenario later */
+               if (target_bandwidth_kbps < range.min_kbps)
+                       if (dsc_common_caps->is_frl)
+                               *target_bpp_x16 = range.min_target_bpp_x16;
        }
 
        return *target_bpp_x16 != 0;
@@ -1061,6 +1745,8 @@ static bool setup_dsc_config(
        int pic_height;
        int slice_height;
        struct dc_dsc_policy policy;
+       int num_lanes;
+       int frl_rate;
 
        memset(dsc_cfg, 0, sizeof(struct dc_dsc_config));
 
@@ -1078,6 +1764,11 @@ static bool setup_dsc_config(
        is_dsc_possible = intersect_dsc_caps(dsc_sink_caps, dsc_enc_caps, 
timing->pixel_encoding, &dsc_common_caps);
        if (!is_dsc_possible)
                goto done;
+       if (convert_bandwidth_to_frl_params(
+                                       target_bandwidth_kbps, &num_lanes, 
&frl_rate)) {
+               dsc_common_caps.num_lanes = num_lanes;
+               dsc_common_caps.frl_rate = frl_rate;
+       }
 
        sink_per_slice_throughput_mps = 0;
 
@@ -1201,6 +1892,8 @@ static bool setup_dsc_config(
                else
                        is_dsc_possible = false;
        }
+       if (dsc_sink_caps->is_frl)
+               num_slices_h = hdmi_dsc_get_num_slices(timing);
        // When we force ODM, num dsc h slices must be divisible by num odm h 
slices
        switch (options->dsc_force_odm_hslice_override) {
        case 0:
@@ -1279,6 +1972,11 @@ static bool setup_dsc_config(
        dsc_cfg->block_pred_enable = dsc_common_caps.is_block_pred_supported;
        dsc_cfg->linebuf_depth = dsc_common_caps.lb_bit_depth;
        dsc_cfg->version_minor = (dsc_common_caps.dsc_version & 0xf0) >> 4;
+       dsc_cfg->is_frl = dsc_sink_caps->is_frl;
+       if (dsc_cfg->is_frl)
+               dsc_cfg->num_slices_h = num_slices_h;
+       dsc_cfg->is_vic_all_bpp = dsc_sink_caps->is_vic_all_bpp;
+       dsc_cfg->total_chunk_kbytes = dsc_sink_caps->total_chunk_kbytes;
        dsc_cfg->is_dp = dsc_sink_caps->is_dp;
 
 done:
@@ -1405,6 +2103,12 @@ void dc_dsc_get_policy_for_timing(const struct 
dc_crtc_timing *timing,
                /* DP specs limits to 3 x bpc */
                policy->max_target_bpp = 3 * bpc;
                policy->ycbcr422_simple = true;
+               if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422 && 
link_encoding == DC_LINK_ENCODING_HDMI_FRL) {
+                       /* HDMI FRL YCbCr422 native support */
+                       policy->min_target_bpp = 7;
+                       policy->max_target_bpp = 2 * bpc;
+                       policy->ycbcr422_simple = false;
+               }
                break;
        case PIXEL_ENCODING_YCBCR420:
                /* DP specs limits to 6 */
diff --git 
a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c 
b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
index cd7d2bb661e5..cff5c95a771c 100644
--- a/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn30/dcn30_hpo_frl_stream_encoder.c
@@ -216,6 +216,16 @@ void hpo_enc3_set_hdmi_stream_attribute(struct 
hpo_frl_stream_encoder *enc,
                break;
        }
 
+       /* When compression active, CD/PP/Phase field shall be zero in GCP */
+       if (crtc_timing->flags.DSC) {
+               REG_UPDATE_2(HDMI_TB_ENC_PIXEL_FORMAT,
+                               HDMI_DEEP_COLOR_DEPTH, 0,
+                               HDMI_DEEP_COLOR_ENABLE, 0);
+       } else {
+               REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT,
+                       HDMI_DSC_MODE, 0);
+       }
+
        /* Configure ODM combine mode */
        switch (odm_combine_num_segments) {
        case 1:
@@ -457,6 +467,94 @@ void hpo_enc3_update_hdmi_info_packet(struct 
dcn30_hpo_frl_stream_encoder *enc3,
        }
 }
 
+void hpo_enc3_hdmi_set_dsc_config(
+       struct hpo_frl_stream_encoder *enc,
+       struct dc_crtc_timing *timing,
+       uint8_t *dsc_packed_pps)
+{
+       struct dcn30_hpo_frl_stream_encoder *enc3 = 
DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(enc);
+       enum optc_dsc_mode dsc_mode = OPTC_DSC_DISABLED;
+       uint8_t i;
+
+       if (dsc_packed_pps) {
+               if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420 ||
+                               (timing->pixel_encoding == 
PIXEL_ENCODING_YCBCR422
+                                       && !timing->dsc_cfg.ycbcr422_simple))
+                       dsc_mode = OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;
+               else
+                       dsc_mode = OPTC_DSC_ENABLED_444;
+       }
+
+       REG_UPDATE(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0,
+                       FIFO_DSC_MODE, dsc_mode);
+
+       REG_UPDATE(HDMI_TB_ENC_PIXEL_FORMAT,
+                       HDMI_DSC_MODE, dsc_mode);
+
+       /* 5 packets for hdmi 2.1, use generic packets 5-10 to transmit*/
+       /* TODO: do we change new bit to 0 after first transmission? Do we set 
End bit when exiting dsc? */
+       if (dsc_mode != OPTC_DSC_DISABLED) {
+               struct dc_info_packet emp_packet = {0};
+               uint32_t dsc_pic_width = timing->h_addressable + 
timing->h_border_left + timing->h_border_right;
+               uint32_t h_back = timing->h_total - dsc_pic_width - 
timing->h_sync_width - timing->h_front_porch;
+               /* HCactivebytes = Slices * ceil(SliceWidth * bpp/8)
+                * use ... + 15) / 16 to achieve ceil since bpp is stored as 
16x actual value
+                */
+               uint32_t h_cactive_bytes = timing->dsc_cfg.num_slices_h * (
+                               (dsc_pic_width / timing->dsc_cfg.num_slices_h * 
timing->dsc_cfg.bits_per_pixel / 8 + 15) / 16);
+
+               /* Packet 0 */
+               emp_packet.valid = true;
+               emp_packet.hb0 = 0x7F; /* Default */
+               emp_packet.hb1 = (1 << 7); /* First */
+               emp_packet.hb2 = 0; /* Sequence index */
+               emp_packet.sb[0] = (1 << 1) | (1 << 2) | (1 << 7); /* Sync[1] = 
1, VFR[2] = 1, New[7] = 1*/
+               emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/
+               emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/
+               emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/
+               memcpy(&emp_packet.sb[7], dsc_packed_pps, 21);
+               hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet);
+
+               /* Packets 1-3 */
+               emp_packet.hb1 = 0; /* Not first or last*/
+               for (i = 1; i < 4; i++) {
+                       emp_packet.hb2 = i; /* Sequence index */
+                       memcpy(&emp_packet.sb[0], &dsc_packed_pps[21 + 28 * (i 
- 1)], 28);
+                       hpo_enc3_update_hdmi_info_packet(enc3, 5 + i, 
&emp_packet);
+               }
+
+               /* Packet 4 */
+               emp_packet.hb2 = 4; /* Sequence index */
+               memcpy(&emp_packet.sb[0], &dsc_packed_pps[105], 23);
+               emp_packet.sb[23] = (uint8_t)timing->h_front_porch; /* 
Hfront[7:0] */
+               emp_packet.sb[24] = (uint8_t)(timing->h_front_porch >> 8); /* 
Hfront[15:8] */
+               emp_packet.sb[25] = (uint8_t)timing->h_sync_width; /* 
Hsync[7:0] */
+               emp_packet.sb[26] = (uint8_t)(timing->h_sync_width >> 8); /* 
Hsync[15:8] */
+               emp_packet.sb[27] = (uint8_t)h_back; /* Hback[7:0] */
+               hpo_enc3_update_hdmi_info_packet(enc3, 9, &emp_packet);
+
+               /* Packet 5 */
+               emp_packet.hb1 = (1 << 6); /* Last */
+               emp_packet.hb2 = 5;
+               emp_packet.sb[0] = (uint8_t)(h_back >> 8); /* Hback[15:8] */
+               emp_packet.sb[1] = (uint8_t)h_cactive_bytes; /* 
HCactive_bytes[7:0] */
+               emp_packet.sb[2] = (uint8_t)(h_cactive_bytes >> 8); /* 
HCactive_bytes[15:8] */
+               hpo_enc3_update_hdmi_info_packet(enc3, 10, &emp_packet);
+
+               /* Packet 0 - Clear New[7] */
+               emp_packet.valid = true;
+               emp_packet.hb0 = 0x7F; /* Default */
+               emp_packet.hb1 = (1 << 7); /* First */
+               emp_packet.hb2 = 0; /* Sequence index */
+               emp_packet.sb[0] = (1 << 1) | (1 << 2); /* Sync[1] = 1, VFR[2] 
= 1*/
+               emp_packet.sb[2] = 1; /* Organization_ID = 1 (Vesa spec)*/
+               emp_packet.sb[4] = 2; /* Data_Set_Tag(LSB) = 2*/
+               emp_packet.sb[6] = 136; /* Data_Set_Length(LSB) = 136*/
+               memcpy(&emp_packet.sb[7], dsc_packed_pps, 21);
+               hpo_enc3_update_hdmi_info_packet(enc3, 5, &emp_packet);
+       }
+}
+
 void hpo_enc3_stop_hdmi_info_packets(
        struct hpo_frl_stream_encoder *enc)
 {
@@ -777,12 +875,18 @@ bool hpo_enc3_validate_hdmi_frl_output(
        default:
                break;
        }
+       frl_params.allow_all_bpp = timing->dsc_cfg.is_vic_all_bpp;
        if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420)
                frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
        else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422)
                frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
        else
                frl_params.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
+       /* DSC parameters */
+       frl_params.bypass_hc_target_calc = false;
+       DC_FP_START();
+       hpo_fpu_enc3_validate_hdmi_frl_output_timing(timing, audio, 
&frl_params);
+       DC_FP_END();
        /* Audio parameters */
        /* TODO: set Audio parameters */
 
@@ -910,6 +1014,7 @@ static const struct hpo_frl_stream_encoder_funcs 
dcn30_str_enc_funcs = {
        .set_avmute                     = enc3_stream_encoder_set_avmute,
        .validate_hdmi_frl_output       = hpo_enc3_validate_hdmi_frl_output,
        .read_state                     = hpo_enc3_read_state,
+       .hdmi_frl_set_dsc_config        = hpo_enc3_hdmi_set_dsc_config,
        .set_dynamic_metadata           = hpo_enc3_set_dynamic_metadata,
        .hdmi_frl_fifo_odm_enabled = hpo_enc3_fifo_odm_enabled,
 };
diff --git 
a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c 
b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
index 27f7ffd89629..28cb14dc87b0 100644
--- a/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn401/dcn401_hpo_frl_stream_encoder.c
@@ -879,6 +879,7 @@ static const struct hpo_frl_stream_encoder_funcs 
dcn401_str_enc_funcs = {
        .hdmi_audio_disable             = hpo_enc401_hdmi_audio_disable,
        .set_avmute                     = enc401_stream_encoder_set_avmute,
        .read_state                     = hpo_enc401_read_state,
+       .hdmi_frl_set_dsc_config        = hpo_enc401_hdmi_set_dsc_config,
        .set_dynamic_metadata           = hpo_enc401_set_dynamic_metadata,
 };
 
diff --git 
a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c 
b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
index 0ec386347f80..d4f66e62c729 100644
--- a/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/hpo/dcn42/dcn42_hpo_frl_stream_encoder.c
@@ -179,6 +179,7 @@ static const struct hpo_frl_stream_encoder_funcs 
dcn42_str_enc_funcs = {
        .hdmi_audio_disable             = hpo_enc42_hdmi_audio_disable,
        .set_avmute                     = enc401_stream_encoder_set_avmute,
        .read_state                     = hpo_enc401_read_state,
+       .hdmi_frl_set_dsc_config        = hpo_enc401_hdmi_set_dsc_config,
        .set_dynamic_metadata           = hpo_enc401_set_dynamic_metadata,
 };
 
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
index cfca7af6cd1f..a416efcc9325 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
@@ -955,6 +955,15 @@ enum dc_status dcn30_setup_hdmi_frl_link(
        return status;
 }
 
+void dcn30_hw_set_vstartup_dsc_frl(struct dc *dc,
+               struct pipe_ctx *pipe_ctx)
+{
+       (void)dc;
+       if (pipe_ctx->stream_res.tg->funcs->set_vstartup_dsc_frl)
+               pipe_ctx->stream_res.tg->funcs->set_vstartup_dsc_frl(
+                       pipe_ctx->stream_res.tg);
+}
+
 bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
 {
        union dmub_rb_cmd cmd;
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
index 26c7386a8a36..cd151c75f59e 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_init.c
@@ -106,6 +106,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = {
        .enable_dp_link_output = dce110_enable_dp_link_output,
        .disable_link_output = dce110_disable_link_output,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .set_disp_pattern_generator = dcn30_set_disp_pattern_generator,
        .get_dcc_en_bits = dcn10_get_dcc_en_bits,
        .update_visual_confirm_color = dcn10_update_visual_confirm_color,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
index 23b30d6f3956..69b8e7030fa4 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_init.c
@@ -99,6 +99,7 @@ static const struct hw_sequencer_funcs dcn31_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .set_backlight_level = dcn21_set_backlight_level,
        .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
        .set_pipe = dcn21_set_pipe,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
index 98771fc443c7..8c50604a4895 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_init.c
@@ -101,6 +101,7 @@ static const struct hw_sequencer_funcs dcn314_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .set_backlight_level = dcn21_set_backlight_level,
        .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
        .set_pipe = dcn21_set_pipe,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
index 0b3e8512ebf1..203c4f7ecd2b 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_init.c
@@ -98,6 +98,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .get_max_dispclk_mhz = dcn32_get_max_dispclk_mhz,
        .apply_idle_power_optimizations = dcn32_apply_idle_power_optimizations,
        .does_plane_fit_in_mall = NULL,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
index fc18d2207711..c0cefeff6e85 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
@@ -108,6 +108,7 @@ static const struct hw_sequencer_funcs dcn35_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .set_backlight_level = dcn31_set_backlight_level,
        .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
        .set_pipe = dcn21_set_pipe,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
index 19ec5b4edfdc..a852a2345296 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn351/dcn351_init.c
@@ -101,6 +101,7 @@ static const struct hw_sequencer_funcs dcn351_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .set_backlight_level = dcn31_set_backlight_level,
        .set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
        .set_pipe = dcn21_set_pipe,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
index d24a352937b4..e1cc16aa207c 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
@@ -85,6 +85,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .apply_idle_power_optimizations = dcn401_apply_idle_power_optimizations,
        .does_plane_fit_in_mall = NULL,
        .set_backlight_level = dcn31_set_backlight_level,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c 
b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
index 49c13611a518..e54d814b3ea9 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_init.c
@@ -86,6 +86,7 @@ static const struct hw_sequencer_funcs dcn42_funcs = {
        .get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
        .calc_vupdate_position = dcn10_calc_vupdate_position,
        .setup_hdmi_frl_link = dcn30_setup_hdmi_frl_link,
+       .set_vstartup_dsc_frl = dcn30_hw_set_vstartup_dsc_frl,
        .apply_idle_power_optimizations = dcn35_apply_idle_power_optimizations,
        .does_plane_fit_in_mall = NULL,
        .set_backlight_level = dcn31_set_backlight_level,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h 
b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
index 7e65ccd11386..34e386e16d0c 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -1437,6 +1437,9 @@ struct hw_sequencer_funcs {
        unsigned int (*get_max_dispclk_mhz)(struct dc *dc,
                        struct dc_state *context);
 
+       void (*set_vstartup_dsc_frl)(struct dc *dc,
+                       struct pipe_ctx *pipe_ctx);
+
        /* Idle Optimization Related */
        bool (*apply_idle_power_optimizations)(struct dc *dc, bool enable);
 
@@ -1710,6 +1713,8 @@ void 
hwss_hpo_dp_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_para
 
 void hwss_stream_enc_dp_set_dsc_pps_info_packet(union block_sequence_params 
*params);
 
+void hwss_hpo_frl_stream_enc_set_dsc_config(union block_sequence_params 
*params);
+
 void hwss_set_dmdata_attributes(union block_sequence_params *params);
 
 void hwss_dp_trace_source_sequence(union block_sequence_params *params);
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h 
b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
index 1800b1929b16..570c45c7830f 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/stream_encoder.h
@@ -368,6 +368,10 @@ struct hpo_frl_stream_encoder {
  * stream interfaces for setup the FRL stream encoder.
  */
 struct hpo_frl_stream_encoder_funcs {
+       void (*hdmi_frl_set_dsc_config)(
+               struct hpo_frl_stream_encoder *enc,
+               struct dc_crtc_timing *timing,
+               uint8_t *dsc_packed_pps);
        /**
         * @hdmi_frl_enable:
         *
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c 
b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
index ca56854dc9f1..1cd4a882f66d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -549,6 +549,8 @@ static void update_psp_stream_config(struct pipe_ctx 
*pipe_ctx, bool dpms_off)
 
        /* dig front end */
        config.dig_fe = (uint8_t) 
pipe_ctx->stream_res.stream_enc->stream_enc_inst;
+       if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+               config.dig_fe = 
(uint8_t)pipe_ctx->stream_res.hpo_frl_stream_enc->stream_enc_inst;
 
        /* stream encoder index */
        config.stream_enc_idx = (uint8_t)(pipe_ctx->stream_res.stream_enc->id - 
ENGINE_ID_DIGA);
@@ -785,6 +787,22 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, 
bool enable)
 
                        /* PPS SDP is set elsewhere because it has to be done 
after DIG FE is connected to DIG BE */
                }
+               else if (dc_is_hdmi_frl_signal(stream->signal)) {
+                       uint8_t dsc_packed_pps[128];
+                       struct dc_crtc_timing patched_crtc_timing = 
stream->timing;
+
+                       DC_LOG_DSC("Setting stream encoder DSC config for 
engine %d:", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
+                       dsc_optc_config_log(dsc, &dsc_optc_cfg);
+
+                       /* if we are borrowing from hblank, h_addressable and  
pic_width need to be adjusted */
+                       if (dc->debug.enable_hblank_borrow) {
+                               dsc_cfg.pic_width = 
stream->timing.h_addressable;
+                       }
+
+                       dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, 
&dsc_packed_pps[0]);
+                       
pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+                                       
pipe_ctx->stream_res.hpo_frl_stream_enc, &patched_crtc_timing, 
&dsc_packed_pps[0]);
+               }
 
                /* Enable DSC in OPTC */
                DC_LOG_DSC("Setting optc DSC config for tg instance %d:", 
pipe_ctx->stream_res.tg->inst);
@@ -816,6 +834,9 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool 
enable)
                                                        
pipe_ctx->stream_res.stream_enc, false, NULL, true);
                        }
                }
+               else if (dc_is_hdmi_frl_signal(stream->signal))
+                       
pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+                                       
pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
 
                /* disable DSC block */
                for (odm_pipe = pipe_ctx; odm_pipe; odm_pipe = 
odm_pipe->next_odm_pipe) {
@@ -903,6 +924,12 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, 
bool enable, bool immedi
                                                &dsc_packed_pps[0],
                                                immediate_update);
                }
+               else if (dc_is_hdmi_frl_signal(stream->signal)) {
+                       //TODO: bring HDMI FRL in line with DP
+                       DC_LOG_DSC("Setting stream encoder DSC PPS SDP for 
engine %d\n", (int)pipe_ctx->stream_res.hpo_frl_stream_enc->id);
+                       
pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+                                       
pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, &dsc_packed_pps[0]);
+               }
        } else {
                /* disable DSC PPS in stream encoder */
                memset(&stream->dsc_packed_pps[0], 0, 
sizeof(stream->dsc_packed_pps));
@@ -917,6 +944,10 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, 
bool enable, bool immedi
                                
pipe_ctx->stream_res.stream_enc->funcs->dp_set_dsc_pps_info_packet(
                                                
pipe_ctx->stream_res.stream_enc, false, NULL, true);
                }
+               else if (dc_is_hdmi_frl_signal(stream->signal))
+                       //TODO: bring HDMI FRL in line with DP
+                       
pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_set_dsc_config(
+                                       
pipe_ctx->stream_res.hpo_frl_stream_enc, &stream->timing, NULL);
        }
 
        return true;
@@ -2426,6 +2457,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx)
        if (pipe_ctx->stream->timing.flags.DSC) {
                if (dc_is_dp_signal(pipe_ctx->stream->signal))
                        link_set_dsc_enable(pipe_ctx, false);
+               else if (dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+                       link_set_dsc_on_stream(pipe_ctx, false);
        }
        if (dp_is_128b_132b_signal(pipe_ctx)) {
                if (pipe_ctx->stream_res.tg->funcs->set_out_mux)
@@ -2606,6 +2639,11 @@ void link_set_dpms_on(
                }
        }
 
+       if (pipe_ctx->stream->timing.flags.DSC &&
+                       dc_is_hdmi_frl_signal(pipe_ctx->stream->signal))
+                       //TODO: bring HDMI FRL in line with DP
+                       link_set_dsc_on_stream(pipe_ctx, true);
+
        /* turn off otg test pattern if enable */
        if (pipe_ctx->stream_res.tg->funcs->set_test_pattern)
                
pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c 
b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
index 6db1f07fdb79..decc3061c58c 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -92,6 +92,25 @@ static void hdmi_return_preeshoot_and_deemphasis(struct 
dc_link *link,
        }
 }
 
+static bool hdmi_frl_test_dsc_max_rate(struct ddc_service *ddc_service)
+{
+       uint8_t slave_address = HDMI_SCDC_ADDRESS;
+       uint8_t offset = HDMI_SCDC_SOURCE_TEST_REQ;
+       union hdmi_scdc_source_test_req test_req = {0};
+
+       DC_LOGGER_INIT(ddc_service->link->ctx->logger);
+
+       link_query_ddc_data(ddc_service, slave_address,
+                                       &offset, sizeof(offset), &test_req.byte,
+                                       sizeof(test_req.byte));
+       if (test_req.fields.DSC_FRL_MAX) {
+               FRL_INFO("FRL TEST REQ:  DSC_FRL_MAX = 1");
+               return true;
+       }
+
+       return false;
+}
+
 enum clock_source_id hdmi_frl_find_matching_phypll(
                struct dc_link *link)
 {
@@ -767,6 +786,9 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
                        link->preferred_hdmi_frl_settings.force_frl_max ||
                        link->ctx->dc->debug.force_frl_max ? true :
                        hdmi_frl_test_max_rate(link->ddc);
+       link->frl_flags.force_frl_dsc =
+                       link->ctx->dc->debug.force_frl_dsc ? true :
+                       hdmi_frl_test_dsc_max_rate(link->ddc);
        link->frl_flags.apply_vsdb_rcc_wa =
                        link->ctx->dc->debug.apply_vsdb_rcc_wa;
 
@@ -778,8 +800,16 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
                        link->frl_flags.force_frl_always = true;
 
        if (!link->frl_flags.force_frl_max &&
+                       !link->frl_flags.force_frl_dsc &&
                        link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) 
{
                link->frl_flags.force_frl_max = true;
+               link->frl_flags.force_frl_dsc = true;
+       }
+
+       if (link->frl_flags.force_frl_max &&
+                       !link->frl_flags.force_frl_dsc &&
+                       link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) 
{
+               link->frl_flags.force_frl_dsc = true;
        }
 
        if (link->local_sink &&
@@ -996,6 +1026,9 @@ void hdmi_frl_set_preferred_link_settings(struct dc *dc,
                        resource_build_info_frame(pipe);
                        link_stream->ctx->dc->hwss.update_info_frame(pipe);
 
+                       if (link_stream->timing.flags.DSC)
+                               link_set_dsc_on_stream(pipe, true);
+
                        link_stream->ctx->dc->hwss.enable_audio_stream(pipe);
                        link_stream->ctx->dc->hwss.enable_stream(pipe);
                        link_stream->ctx->dc->hwss.unblank_stream(pipe,
@@ -1104,6 +1137,10 @@ void hdmi_frl_decide_link_settings(struct 
dc_stream_state *stream,
                *frl_link_settings = stream->link->frl_verified_link_cap;
                return;
        }
+       if (stream->link->frl_flags.force_frl_dsc) {
+               *frl_link_settings = stream->link->frl_verified_link_cap;
+               return;
+       }
 
        if (stream->link->local_sink)
                if 
(stream->link->local_sink->edid_caps.panel_patch.hdmi_spe_handling) {
@@ -1126,6 +1163,7 @@ void hdmi_frl_decide_link_settings(struct dc_stream_state 
*stream,
        } while (!success);
 
        *frl_link_settings = temp_settings;
+       update_borrow_mode_from_dsc_padding(dsc_padding_params, 
&stream->timing, frl_link_settings);
 }
 
 void hdmi_frl_write_read_request_enable(struct ddc_service *ddc_service)
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
index d72574db1f07..78fd8d262de3 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.c
@@ -189,7 +189,13 @@ void optc3_set_dsc_config(struct timing_generator *optc,
        struct optc *optc1 = DCN10TG_FROM_TG(optc);
 
        optc2_set_dsc_config(optc, dsc_mode, dsc_bytes_per_pixel, 
dsc_slice_width);
-       REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 0);
+
+       if (dsc_mode != OPTC_DSC_DISABLED
+                       && optc1->signal == SIGNAL_TYPE_HDMI_FRL) {
+               REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 1);
+       } else {
+               REG_UPDATE(OTG_V_SYNC_A_CNTL, OTG_V_SYNC_MODE, 0);
+       }
 }
 
 void optc3_set_odm_bypass(struct timing_generator *optc,
@@ -314,6 +320,27 @@ bool optc3_get_pipe_update_pending(struct timing_generator 
*optc)
        return (flip_pending == 1 || dc_update_pending == 1);
 }
 
+void optc3_set_vstartup_dsc_frl(struct timing_generator *optc)
+{
+       struct optc *optc1 = DCN10TG_FROM_TG(optc);
+       unsigned int vblank_end = 0;
+       unsigned int vstartup_start = 0;
+
+       REG_GET(OTG_V_BLANK_START_END, OTG_V_BLANK_END, &vblank_end);
+       REG_GET(OTG_VSTARTUP_PARAM, VSTARTUP_START, &vstartup_start);
+
+       /* In FRL+DSC mode the VSYNC is generated in OTG at start of HBALNK
+        * before frame start (VCOUNT=0 and HCOUNT=0). We need to program
+        *  VSTARTUP at least one line before frame start to ensure the VSYNC
+        *  will be generated in VRR mode. We need to program
+        *  VSTARTUP_START >= V_BLANK_END + 1.
+        *  When fullscreen = false,
+        *  global_sync will restore VSTARTUP_START to normal value
+        */
+       if (vblank_end >= vstartup_start)
+               REG_SET(OTG_VSTARTUP_PARAM, 0, VSTARTUP_START,
+                               vblank_end+1);
+}
 /**
  * optc3_set_timing_double_buffer() - DRR double buffering control
  *
@@ -409,6 +436,7 @@ static const struct timing_generator_funcs dcn30_tg_funcs = 
{
                .get_optc_source = optc2_get_optc_source,
                .set_out_mux = optc3_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h 
b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
index 16c5610b49ac..8a88867f4361 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn30/dcn30_optc.h
@@ -349,6 +349,7 @@ void optc3_triplebuffer_lock(struct timing_generator *optc);
 void optc3_program_blank_color(struct timing_generator *optc,
                const struct tg_color *blank_color);
 
+void optc3_set_vstartup_dsc_frl(struct timing_generator *optc);
 void optc3_set_vtotal_change_limit(struct timing_generator *optc,
                uint32_t limit);
 
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
index 98aaa22ce81c..bbf703777f72 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
@@ -500,6 +500,7 @@ static const struct timing_generator_funcs dcn31_tg_funcs = 
{
                .get_optc_source = optc2_get_optc_source,
                .set_out_mux = optc3_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
index a7cf34937b2f..4d4b517575e2 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
@@ -244,6 +244,7 @@ static const struct timing_generator_funcs dcn314_tg_funcs 
= {
                .get_optc_source = optc2_get_optc_source,
                .set_out_mux = optc3_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
index 60e546b69a05..99258b2fb14e 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
@@ -354,6 +354,7 @@ static const struct timing_generator_funcs dcn32_tg_funcs = 
{
                .get_optc_source = optc2_get_optc_source,
                .set_out_mux = optc3_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
index a880e4a6d165..f6183c2e78e4 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
@@ -595,6 +595,7 @@ static const struct timing_generator_funcs dcn35_tg_funcs = 
{
                .set_h_timing_div_manual_mode = 
optc32_set_h_timing_div_manual_mode,
                .set_out_mux = optc3_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
index a6d76f451cf8..581517c9cec4 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
@@ -521,6 +521,7 @@ static const struct timing_generator_funcs dcn401_tg_funcs 
= {
                .get_optc_source = optc2_get_optc_source,
                .set_out_mux = optc401_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c 
b/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
index ed66a2bbb8ae..f5280bc3c7ec 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn42/dcn42_optc.c
@@ -250,6 +250,7 @@ static struct timing_generator_funcs dcn42_tg_funcs = {
                .wait_otg_disable = optc35_wait_otg_disable,
                .set_out_mux = optc401_set_out_mux,
                .set_drr_trigger_window = optc3_set_drr_trigger_window,
+               .set_vstartup_dsc_frl = optc3_set_vstartup_dsc_frl,
                .set_vtotal_change_limit = optc3_set_vtotal_change_limit,
                .set_gsl = optc2_set_gsl,
                .set_gsl_source_select = optc2_set_gsl_source_select,
-- 
2.54.0

Reply via email to