Thread (18 messages) 18 messages, 1 author, 2d ago

[PATCH v6 14/15] drm/amd/display: add HDMI 2.1 DSC over FRL support

From: Harry Wentland <harry.wentland@amd.com>
Date: 2026-05-20 20:29:59
Subsystem: amd display core, amd display core - dml, drm drivers, radeon and amdgpu drm drivers, the rest · Maintainers: Harry Wentland, Leo Li, Austin Zheng, Jun Lei, David Airlie, Simona Vetter, Alex Deucher, Christian König, Linus Torvalds

Add all the bits to enable DSC over FRL.

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <redacted>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  74 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c |  45 +-
 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 |  56 ++
 .../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 | 201 +++++
 .../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.h   |   3 -
 .../drm/amd/display/dc/hwss/hw_sequencer.h    |   2 +
 .../amd/display/dc/inc/hw/stream_encoder.h    |   4 +
 .../amd/display/dc/inc/hw/timing_generator.h  |   1 -
 .../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    |   8 +-
 27 files changed, 1366 insertions(+), 14 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 10bf4451d4de..63fd63ea1310 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7346,12 +7346,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);
 	}
 }
 
@@ -7425,6 +7443,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 =
@@ -7463,8 +7485,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 &&
@@ -7477,11 +7509,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 d2598e391eb8..f9ec006e08c4 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);
 
@@ -1093,10 +1100,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/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 668d280f0fdb..0ef2e0322e1f 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -4059,6 +4059,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 97380cda9eb4..88446817a71f 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
@@ -1641,6 +1641,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;
@@ -3733,6 +3736,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 09235ae5055e..d4c61c9be922 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4594,6 +4594,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 */
 
 /**
@@ -4620,6 +4652,30 @@ 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;
 
+#if defined(CONFIG_DRM_AMD_DC_FP)
+	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);
+		}
+	}
+#endif
 }
 
 /**
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 42b2a88d2d52..1b2c22545be3 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 3ec3c165f8cd..d3273709bb8d 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 */
 
@@ -1152,6 +1158,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 7cb34e2b44a1..de169ed3b3b7 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 a690324f78dc..6f5a11d37899 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
@@ -782,6 +782,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;
@@ -797,6 +829,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;
 	}
@@ -828,5 +866,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 2a309e86f60f..33b5136f7ce3 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
@@ -3359,6 +3359,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 261d90aaa3ad..bd14ebea1111 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 674605d8d4fd..2ea5cf37f273 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 40a0a5815ca5..ebf792fb22e2 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..260f58a97fc7 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,211 @@ enum frl_cap_chk_result dml1_frl_cap_chk_uncompressed(struct frl_cap_chk_params
 	return FRL_CAP_CHK_OK;
 }
 
+#if defined (CONFIG_DRM_AMD_DC_FP)
+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;
+}
+#endif
+
 enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params)
 {
 	struct frl_cap_chk_intermediates inter;
 
+#if defined (CONFIG_DRM_AMD_DC_FP)
+	if (params->compressed)
+		return dml1_frl_cap_chk_compressed(params, &inter);
+#endif
+
 	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 9aa5adb15103..ba4988118c68 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 */
@@ -878,6 +1558,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;
@@ -1063,6 +1747,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));
 
@@ -1081,6 +1767,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;
 
@@ -1204,6 +1895,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:
@@ -1282,6 +1975,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:
@@ -1408,6 +2106,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.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h
index 2306354e90af..a963d360a368 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h
@@ -85,9 +85,6 @@ int dcn30_hw_get_max_fva_factor(struct dc *dc,
 		struct dc_crtc_timing *timing,
 		unsigned int max_pixel_clock);
 
-void dcn30_hw_set_vstartup_dsc_frl(struct dc *dc,
-		struct pipe_ctx *pipe_ctx);
-
 bool dcn30_does_plane_fit_in_mall(struct dc *dc,
 		unsigned int pitch,
 		unsigned int height,
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 a9569078622f..dfb278a9fc3e 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -1710,6 +1710,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 22d5f291c7db..2b95cb11eede 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
@@ -369,6 +369,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/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
index e4e17f630e97..2eaba65894ab 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
@@ -498,7 +498,6 @@ struct timing_generator_funcs {
 	int (*get_max_hw_supported_fva_factor)(struct timing_generator *optc,
 		struct dc_crtc_timing *timing,
 		unsigned int max_pixclk_100hz);
-	void (*set_vstartup_dsc_frl)(struct timing_generator *optc);
 	void (*set_vtotal_change_limit)(struct timing_generator *optc,
 			uint32_t limit);
 	void (*align_vblanks)(struct timing_generator *master_optc,
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 408fa6d11102..42e7f0e9b3ce 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;
@@ -2423,6 +2454,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)
@@ -2603,6 +2636,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 9087f786d6c9..c35074177a36 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,
@@ -1106,6 +1139,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) {
@@ -1128,6 +1165,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..90ec4cb8a9dd 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,
-- 
2.54.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help