All of lore.kernel.org
 help / color / mirror / Atom feed
From: Harry Wentland <harry.wentland@amd.com>
To: <amd-gfx@lists.freedesktop.org>
Cc: <Jerry.Zuo@amd.com>, Harry Wentland <harry.wentland@amd.com>
Subject: [PATCH v5 02/13] drm/amd/display: Add DML changes to support HDMI FRL
Date: Tue, 12 May 2026 11:52:33 -0400	[thread overview]
Message-ID: <20260512155244.403854-3-harry.wentland@amd.com> (raw)
In-Reply-To: <20260512155244.403854-1-harry.wentland@amd.com>

This adds DML support for HDMI FRL.

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
 drivers/gpu/drm/amd/display/dc/dml/Makefile   |   3 +
 .../drm/amd/display/dc/dml/dcn20/dcn20_fpu.c  |   5 +
 .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.c  |  89 +++
 .../drm/amd/display/dc/dml/dcn30/dcn30_fpu.h  |  22 +
 .../dc/dml/dcn30/display_mode_vba_30.c        | 277 +++++++-
 .../amd/display/dc/dml/dcn302/dcn302_fpu.c    |   1 +
 .../amd/display/dc/dml/dcn303/dcn303_fpu.c    |   1 +
 .../dc/dml/dcn31/display_mode_vba_31.c        | 240 +++++++
 .../dc/dml/dcn314/display_mode_vba_314.c      | 238 +++++++
 .../drm/amd/display/dc/dml/dcn32/dcn32_fpu.c  |  10 +
 .../drm/amd/display/dc/dml/dcn32/dcn32_fpu.h  |   2 +
 .../dc/dml/dcn32/display_mode_vba_32.c        |  16 +-
 .../dc/dml/dcn32/display_mode_vba_util_32.c   | 124 +++-
 .../amd/display/dc/dml/display_mode_enums.h   |   1 +
 .../drm/amd/display/dc/dml/dml1_frl_cap_chk.c | 589 ++++++++++++++++++
 .../drm/amd/display/dc/dml/dml1_frl_cap_chk.h |   9 -
 16 files changed, 1612 insertions(+), 15 deletions(-)
 create mode 100644 drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c

diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile b/drivers/gpu/drm/amd/display/dc/dml/Makefile
index 268b5fbdb48b..10d4ace04d4f 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile
@@ -78,6 +78,7 @@ CFLAGS_$(AMDDALPATH)/dc/dml/calcs/dcn_calcs.o := $(dml_ccflags)
 CFLAGS_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_auto.o := $(dml_ccflags)
 CFLAGS_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_math.o := $(dml_ccflags) -Wno-tautological-compare
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/display_mode_vba.o := $(dml_rcflags)
+CFLAGS_$(AMDDALPATH)/dc/dml/dml1_frl_cap_chk.o := $(dml_ccflags)
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20.o := $(dml_rcflags)
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dcn20/display_rq_dlg_calc_20.o := $(dml_rcflags)
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dcn20/display_mode_vba_20v2.o := $(dml_rcflags)
@@ -117,9 +118,11 @@ CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/display_rq_dlg_helpers.o := $(dml_rcflags)
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/calcs/dcn_calcs.o := $(dml_rcflags)
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_auto.o := $(dml_rcflags)
 CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/calcs/dcn_calc_math.o := $(dml_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml/dml1_frl_cap_chk.o := $(dml_rcflags)
 
 ifdef CONFIG_DRM_AMD_DC_FP
 DML += display_mode_lib.o display_rq_dlg_helpers.o dml1_display_rq_dlg_calc.o
+DML += dml1_frl_cap_chk.o
 DML += dcn10/dcn10_fpu.o
 DML += dcn20/dcn20_fpu.o
 DML += display_mode_vba.o dcn20/display_rq_dlg_calc_20.o dcn20/display_mode_vba_20.o
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c
index ed7c989a5b13..5f088d113b9f 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c
@@ -1049,6 +1049,8 @@ static bool is_dtbclk_required(struct dc *dc, struct dc_state *context)
 	for (i = 0; i < dc->res_pool->pipe_count; i++) {
 		if (!context->res_ctx.pipe_ctx[i].stream)
 			continue;
+		if (dc_is_hdmi_frl_signal(context->res_ctx.pipe_ctx[i].stream->signal))
+			return true;
 		if (dc->link_srv->dp_is_128b_132b_signal(&context->res_ctx.pipe_ctx[i]))
 			return true;
 	}
@@ -1467,6 +1469,9 @@ int dcn20_populate_dml_pipes_from_context(struct dc *dc,
 		case SIGNAL_TYPE_DVI_DUAL_LINK:
 			pipes[pipe_cnt].dout.output_type = dm_hdmi;
 			break;
+		case SIGNAL_TYPE_HDMI_FRL:
+			pipes[pipe_cnt].dout.output_type = dm_hdmifrl;
+			break;
 		default:
 			/* In case there is no signal, set dp with 4 lanes to allow max config */
 			pipes[pipe_cnt].dout.is_virtual = 1;
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 0ba388c6aec1..a690324f78dc 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
@@ -33,6 +33,8 @@
 #include "display_mode_vba_30.h"
 #include "dcn30_fpu.h"
 
+#include "../dml1_frl_cap_chk.h"
+
 #define REG(reg)\
 	optc1->tg_regs->reg
 
@@ -131,6 +133,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_0_soc = {
 				.phyclk_mhz = 300.0,
 				.phyclk_d18_mhz = 667.0,
 				.dscclk_mhz = 405.6,
+				.dtbclk_mhz = 1217.0,
 			},
 		},
 
@@ -741,3 +744,89 @@ void patch_dcn30_soc_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_bo
 		}
 	}
 }
+
+#undef DC_LOGGER
+#define DC_LOGGER \
+		enc3->base.ctx->logger
+
+#define hdmi_frl_print(str, ...) {DC_LOG_HDMI_FRL(str, ##__VA_ARGS__); }
+
+#define DEBUG_FRL_CAP_CHK 1
+
+void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct hpo_frl_stream_encoder *enc,
+						struct dc_hdmi_frl_link_settings *frl_link_settings,
+						struct frl_cap_chk_params *frl_params,
+						const struct dc_crtc_timing *timing,
+						unsigned int dsc_max_rate)
+{
+	(void)enc;
+	dc_assert_fp_enabled();
+
+	switch (frl_link_settings->frl_link_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;
+	}
+
+	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;
+	frl_params->vic = timing->vic;
+}
+
+void hpo_fpu_enc3_validate_hdmi_frl_output_timing(
+		const struct dc_crtc_timing *timing,
+		const struct audio_check *audio,
+		struct frl_cap_chk_params *frl_params)
+{
+	dc_assert_fp_enabled();
+
+	if (timing->flags.DSC) {
+		frl_params->compressed = true;
+	} else {
+		frl_params->compressed = false;
+	}
+
+	frl_params->audio_packet_type = audio->audio_packet_type;
+	frl_params->f_audio = audio->max_audiosample_rate;
+	frl_params->acat = audio->acat;
+}
+
+enum frl_cap_chk_result frl_fpu_cap_chk_common(struct hpo_frl_stream_encoder *enc,
+					       struct frl_cap_chk_intermediates *inter,
+					       struct frl_cap_chk_params *params)
+{
+	(void)enc;
+	return dml1_frl_cap_chk_common(inter, params);
+}
+
+
+enum frl_cap_chk_result frl_fpu_cap_chk_uncompressed(struct hpo_frl_stream_encoder *enc,
+						     struct frl_cap_chk_params *params,
+						     struct frl_cap_chk_intermediates *inter)
+{
+	(void)enc;
+	return dml1_frl_cap_chk_uncompressed(params, inter);
+}
+
+enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct hpo_frl_stream_encoder *enc,
+						   struct frl_cap_chk_params *params,
+						   struct frl_cap_chk_intermediates *inter)
+{
+	(void)enc;
+	return -5;
+}
\ No newline at end of file
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h
index e3b6ad6a8784..e3a46915d168 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn30/dcn30_fpu.h
@@ -70,4 +70,26 @@ void dcn3_fpu_build_wm_range_table(struct clk_mgr *base);
 
 void patch_dcn30_soc_bounding_box(struct dc *dc, struct _vcs_dpi_soc_bounding_box_st *dcn3_0_ip);
 
+void hpo_fpu_enc3_validate_hdmi_frl_output_link(struct hpo_frl_stream_encoder *enc,
+						struct dc_hdmi_frl_link_settings *frl_link_settings,
+						struct frl_cap_chk_params *frl_params,
+						const struct dc_crtc_timing *timing,
+						unsigned int dsc_max_rate);
+
+void hpo_fpu_enc3_validate_hdmi_frl_output_timing(const struct dc_crtc_timing *timing,
+						  const struct audio_check *audio,
+						  struct frl_cap_chk_params *frl_params);
+
+enum frl_cap_chk_result frl_fpu_cap_chk_common(struct hpo_frl_stream_encoder *enc,
+					       struct frl_cap_chk_intermediates *inter,
+					       struct frl_cap_chk_params *params);
+
+enum frl_cap_chk_result frl_fpu_cap_chk_uncompressed(struct hpo_frl_stream_encoder *enc,
+						     struct frl_cap_chk_params *params,
+						     struct frl_cap_chk_intermediates *inter);
+
+enum frl_cap_chk_result frl_fpu_cap_chk_compressed(struct hpo_frl_stream_encoder *enc,
+						   struct frl_cap_chk_params *params,
+						   struct frl_cap_chk_intermediates *inter);
+
 #endif /* __DCN30_FPU_H__*/
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 f0b1bfb408f6..2a309e86f60f 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
@@ -28,6 +28,8 @@
 #include "display_mode_vba_30.h"
 #include "../dml_inline_defs.h"
 
+#include "../dml1_frl_cap_chk.h"
+
 
 /*
  * NOTE:
@@ -324,6 +326,17 @@ static void CalculateUrgentBurstFactor(
 		double *UrgentBurstFactorChroma,
 		bool *NotEnoughUrgentLatencyHiding);
 
+static double RequiredDTBCLK(
+		bool DSCEnable,
+		double PixelClock,
+		enum output_format_class OutputFormat,
+		double OutputBPP,
+		int DSCSlices,
+		long HTotal,
+		long HActive,
+		int AudioRate,
+		int AudioLayoutSingle);
+
 static void UseMinimumDCFCLK(
 		struct display_mode_lib *mode_lib,
 		struct vba_vars_st *v,
@@ -626,6 +639,8 @@ static unsigned int dscceComputeDelay(
 		pixelsPerClock = 1;
 	else if (pixelFormat == dm_n422)
 		pixelsPerClock = 2;
+	else if (Output == dm_hdmifrl)
+		pixelsPerClock = 2;
 	else
 		pixelsPerClock = 1;
 
@@ -646,6 +661,8 @@ static unsigned int dscceComputeDelay(
 	//422 mode has an additional cycle of delay
 	if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422)
 		s = 0;
+	else if (Output == dm_hdmifrl)
+		s = 0;
 	else
 		s = 1;
 
@@ -716,6 +733,25 @@ static unsigned int dscComputeDelay(enum output_format_class pixelFormat, enum o
 		Delay = Delay + 1;
 		//   sft
 		Delay = Delay + 1;
+	} else if (Output == dm_hdmifrl && pixelFormat != dm_444) {
+		//   sfr
+		Delay = Delay + 2;
+		//   dsccif
+		Delay = Delay + 1;
+		//   dscc - input deserializer
+		Delay = Delay + 5;
+		//  dscc - input cdc fifo
+		Delay = Delay + 25;
+		//   dscc - cdc uncertainty
+		Delay = Delay + 2;
+		//   dscc - output cdc fifo
+		Delay = Delay + 10;
+		//   dscc - cdc uncertainty
+		Delay = Delay + 2;
+		//   dscc - output serializer
+		Delay = Delay + 1;
+		//   sft
+		Delay = Delay + 1;
 	} else {
 		//   sfr
 		Delay = Delay + 2;
@@ -2017,6 +2053,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman
 				v->DSCFormatFactor = 1;
 			else if (v->OutputFormat[k] == dm_n422)
 				v->DSCFormatFactor = 2;
+			else if (v->Output[k] == dm_hdmifrl)
+				v->DSCFormatFactor = 2;
 			else
 				v->DSCFormatFactor = 1;
 			if (v->ODMCombineEnabled[k] == dm_odm_combine_mode_4to1)
@@ -3309,19 +3347,38 @@ static double TruncToValidBPP(
 	int NonDSCBPP1 = 0;
 	int NonDSCBPP2 = 0;
 
+	enum frl_cap_chk_result hdmifrlresult = { 0 };
+	struct frl_cap_chk_params hdmifrlparams = { 0 };
+	struct frl_cap_chk_intermediates hdmifrlinter = { 0 };
+
+	hdmifrlparams.lanes = Lanes;
+	hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000;
+	hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000;
+	hdmifrlparams.layout = AudioLayout;
+	hdmifrlparams.f_audio = AudioRate * 1000;
+	hdmifrlparams.h_active = HActive;
+	hdmifrlparams.h_blank = HTotal - HActive;
+	hdmifrlparams.compressed = DSCEnable;
+
 	if (Format == dm_420) {
 		NonDSCBPP0 = 12;
 		NonDSCBPP1 = 15;
 		NonDSCBPP2 = 18;
 		MinDSCBPP = 6;
 		MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
+		hdmifrlparams.bpc = (int)(DesiredBPP * 2 / 3);
 	} else if (Format == dm_444) {
 		NonDSCBPP0 = 24;
 		NonDSCBPP1 = 30;
 		NonDSCBPP2 = 36;
 		MinDSCBPP = 8;
 		MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
+	    hdmifrlparams.bpc = (int)(DesiredBPP / 3);
 	} else {
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
+	    hdmifrlparams.bpc = (int)(DesiredBPP / 2);
 		NonDSCBPP0 = 16;
 		NonDSCBPP1 = 20;
 		NonDSCBPP2 = 24;
@@ -3329,12 +3386,20 @@ static double TruncToValidBPP(
 		if (Format == dm_n422) {
 			MinDSCBPP = 7;
 			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
+		} else if (Output == dm_hdmifrl) {
+			MinDSCBPP = 7;
+			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
 		} else {
 			MinDSCBPP = 8;
 			MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0;
 		}
 	}
 
+	if (Output == dm_hdmifrl) {
+		hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter);
+		MaxLinkBPP = (1 - hdmifrlinter.overhead_max) * dml_min(hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive,
+				(hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive);
+	} else
 	if (DSCEnable && Output == dm_dp) {
 		MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100);
 	} else {
@@ -3372,6 +3437,8 @@ static double TruncToValidBPP(
 		if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP == NonDSCBPP0 || DesiredBPP == 18)) ||
 				(DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) {
 			return BPP_INVALID;
+		} else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) {
+			return BPP_INVALID;
 		} else {
 			return DesiredBPP;
 		}
@@ -4013,6 +4080,172 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 						// TODO: Need some other way to handle this nonsense
 						// v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3"
 					}
+				} else if (v->Output[k] == dm_hdmifrl) {
+					if (v->DSCEnable[k] == true || v->ODMCombineEnablePerState[i][k] == dm_odm_combine_mode_4to1) {
+						v->RequiresDSC[i][k] = true;
+						v->LinkDSCEnable = true;
+						v->RequiresFEC[i][k] = true;
+					} else {
+						v->RequiresDSC[i][k] = false;
+						v->LinkDSCEnable = false;
+						v->RequiresFEC[i][k] = false;
+					}
+					v->Outbpp = BPP_INVALID;
+					if (v->PHYCLKD18PerState[i] >= 3000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								3000,
+								3,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "3x3";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								6000,
+								3,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x3";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								6000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 8000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								8000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState(i, k) = v->Output[k] & "8x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								10000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						if (v->Outbpp == BPP_INVALID && v->ForcedOutputLinkBPP[k] == 0
+								&& v->PHYCLKD18PerState[i] < 12000.0 / 18) {
+							v->RequiresDSC[i][k] = true;
+							v->LinkDSCEnable = true;
+							v->RequiresFEC[i][k] = true;
+							v->Outbpp = TruncToValidBPP(
+									10000,
+									4,
+									v->HTotal[k],
+									v->HActive[k],
+									v->PixelClockBackEnd[k],
+									v->ForcedOutputLinkBPP[k],
+									v->LinkDSCEnable,
+									v->Output[k],
+									v->OutputFormat[k],
+									v->DSCInputBitPerComponent[k],
+									v->NumberOfDSCSlices[k],
+									v->AudioSampleRate[k],
+									v->AudioSampleLayout[k],
+									v->ODMCombineEnablePerState[i][k]);
+						}
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								12000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						if (v->Outbpp == BPP_INVALID && v->ForcedOutputLinkBPP[k] == 0) {
+							v->RequiresDSC[i][k] = true;
+							v->LinkDSCEnable = true;
+							v->RequiresFEC[i][k] = true;
+							v->Outbpp = TruncToValidBPP(
+									12000,
+									4,
+									v->HTotal[k],
+									v->HActive[k],
+									v->PixelClockBackEnd[k],
+									v->ForcedOutputLinkBPP[k],
+									v->LinkDSCEnable,
+									v->Output[k],
+									v->OutputFormat[k],
+									v->DSCInputBitPerComponent[k],
+									v->NumberOfDSCSlices[k],
+									v->AudioSampleRate[k],
+									v->AudioSampleLayout[k],
+									v->ODMCombineEnablePerState[i][k]);
+						}
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4";
+					}
 				}
 			} else {
 				v->OutputBppPerState[i][k] = 0;
@@ -4022,7 +4255,7 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 	for (i = start_state; i < v->soc.num_states; i++) {
 		v->DIOSupport[i] = true;
 		for (k = 0; k <= v->NumberOfActivePlanes - 1; k++) {
-			if (!v->skip_dio_check[k] && v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || v->Output[k] == dm_hdmi)
+			if (!v->skip_dio_check[k] && v->BlendingAndTiming[k] == k && (v->Output[k] == dm_dp || v->Output[k] == dm_edp || v->Output[k] == dm_hdmi || v->Output[k] == dm_hdmifrl)
 					&& (v->OutputBppPerState[i][k] == 0
 							|| (v->OutputFormat[k] == dm_420 && v->Interlace[k] == true && v->ProgressiveToInterlaceUnitInOPP == true))) {
 				v->DIOSupport[i] = false;
@@ -4030,6 +4263,25 @@ void dml30_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 		}
 	}
 
+	for (i = start_state; i < v->soc.num_states; ++i) {
+		v->DTBCLKRequiredMoreThanSupported[i] = false;
+		for (k = 0; k < v->NumberOfActivePlanes; ++k) {
+			if (v->BlendingAndTiming[k] == k && v->Output[k] == dm_hdmifrl
+					&& RequiredDTBCLK(
+							v->RequiresDSC[i][k],
+							v->PixelClockBackEnd[k],
+							v->OutputFormat[k],
+							v->OutputBppPerState[i][k],
+							v->NumberOfDSCSlices[k],
+							v->HTotal[k],
+							v->HActive[k],
+							v->AudioSampleRate[k],
+							v->AudioSampleLayout[k]) > v->DTBCLKPerState[i]) {
+				v->DTBCLKRequiredMoreThanSupported[i] = true;
+			}
+		}
+	}
+
 	for (i = start_state; i < v->soc.num_states; ++i) {
 		v->ODMCombine4To1SupportCheckOK[i] = true;
 		for (k = 0; k < v->NumberOfActivePlanes; ++k) {
@@ -6248,6 +6500,29 @@ static double CalculateUrgentLatency(
 	return ret;
 }
 
+static double RequiredDTBCLK(
+		bool DSCEnable,
+		double PixelClock,
+		enum output_format_class OutputFormat,
+		double OutputBPP,
+		int DSCSlices,
+		long HTotal,
+		long HActive,
+		int AudioRate,
+		int AudioLayout)
+{
+	if (DSCEnable != true) {
+		return dml_max(PixelClock / 4.0 * OutputBPP / 24.0, 25.0);
+	} else {
+		double PixelWordRate = PixelClock /  (OutputFormat == dm_444 ? 1 : 2);
+		double HCActive = dml_ceil(DSCSlices * dml_ceil(OutputBPP * dml_ceil(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1);
+		double HCBlank = 64 + 32 * dml_ceil((double)AudioRate * (AudioLayout == 1 ? 1.0 : 0.25) * HTotal / (PixelClock * 1000), 1);
+		double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal;
+		double HActiveTribyteRate = PixelWordRate * HCActive / HActive;
+		return dml_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002;
+	}
+}
+
 static noinline_for_stack void UseMinimumDCFCLK(
 		struct display_mode_lib *mode_lib,
 		struct vba_vars_st *v,
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c
index 90a7b29ebe74..781bc429807f 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn302/dcn302_fpu.c
@@ -119,6 +119,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_02_soc = {
 						.phyclk_mhz = 300.0,
 						.phyclk_d18_mhz = 667.0,
 						.dscclk_mhz = 405.6,
+						.dtbclk_mhz = 1217.0,
 				},
 		},
 
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c
index e42419c7868d..e6d7d67f06a4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn303/dcn303_fpu.c
@@ -118,6 +118,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_03_soc = {
 						.phyclk_mhz = 300.0,
 						.phyclk_d18_mhz = 667.0,
 						.dscclk_mhz = 405.6,
+						.dtbclk_mhz = 1217.0,
 				},
 		},
 
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 f9224a433220..261d90aaa3ad 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
@@ -29,6 +29,8 @@
 #include "display_mode_vba_31.h"
 #include "../dml_inline_defs.h"
 
+#include "../dml1_frl_cap_chk.h"
+
 /*
  * NOTE:
  *   This file is gcc-parsable HW gospel, coming straight from HW engineers.
@@ -357,6 +359,17 @@ static void CalculateUrgentBurstFactor(
 		double *UrgentBurstFactorChroma,
 		bool *NotEnoughUrgentLatencyHiding);
 
+static double RequiredDTBCLK(
+		bool DSCEnable,
+		double PixelClock,
+		enum output_format_class OutputFormat,
+		double OutputBPP,
+		int DSCSlices,
+		int HTotal,
+		int HActive,
+		int AudioRate,
+		int AudioLayoutSingle);
+
 static void UseMinimumDCFCLK(
 		struct display_mode_lib *mode_lib,
 		int MaxPrefetchMode,
@@ -695,6 +708,8 @@ static unsigned int dscceComputeDelay(
 		pixelsPerClock = 1;
 	else if (pixelFormat == dm_n422)
 		pixelsPerClock = 2;
+	else if (Output == dm_hdmifrl)
+		pixelsPerClock = 2;
 	// #all other modes operate at 1 pixel per clock
 	else
 		pixelsPerClock = 1;
@@ -716,6 +731,8 @@ static unsigned int dscceComputeDelay(
 	//422 mode has an additional cycle of delay
 	if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422)
 		s = 0;
+	else if (Output == dm_hdmifrl)
+		s = 0;
 	else
 		s = 1;
 
@@ -786,6 +803,25 @@ static unsigned int dscComputeDelay(enum output_format_class pixelFormat, enum o
 		Delay = Delay + 1;
 		//   sft
 		Delay = Delay + 1;
+	} else if (Output == dm_hdmifrl && pixelFormat != dm_444) {
+		//   sfr
+		Delay = Delay + 2;
+		//   dsccif
+		Delay = Delay + 1;
+		//   dscc - input deserializer
+		Delay = Delay + 5;
+		//  dscc - input cdc fifo
+		Delay = Delay + 25;
+		//   dscc - cdc uncertainty
+		Delay = Delay + 2;
+		//   dscc - output cdc fifo
+		Delay = Delay + 10;
+		//   dscc - cdc uncertainty
+		Delay = Delay + 2;
+		//   dscc - output serializer
+		Delay = Delay + 1;
+		//   sft
+		Delay = Delay + 1;
 	} else {
 		//   sfr
 		Delay = Delay + 2;
@@ -2243,6 +2279,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman
 				v->DSCFormatFactor = 1;
 			else if (v->OutputFormat[k] == dm_n422)
 				v->DSCFormatFactor = 2;
+			else if (v->Output[k] == dm_hdmifrl)
+				v->DSCFormatFactor = 2;
 			else
 				v->DSCFormatFactor = 1;
 			if (v->ODMCombineEnabled[k] == dm_odm_combine_mode_4to1)
@@ -3615,19 +3653,38 @@ static double TruncToValidBPP(
 	int NonDSCBPP1;
 	int NonDSCBPP2;
 
+	enum frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK;
+	struct frl_cap_chk_params hdmifrlparams = { 0 };
+	struct frl_cap_chk_intermediates hdmifrlinter = { 0 };
+
+	hdmifrlparams.lanes = Lanes;
+	hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000;
+	hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000;
+	hdmifrlparams.layout = AudioLayout;
+	hdmifrlparams.f_audio = AudioRate * 1000;
+	hdmifrlparams.h_active = HActive;
+	hdmifrlparams.h_blank = HTotal - HActive;
+	hdmifrlparams.compressed = DSCEnable;
+
 	if (Format == dm_420) {
 		NonDSCBPP0 = 12;
 		NonDSCBPP1 = 15;
 		NonDSCBPP2 = 18;
 		MinDSCBPP = 6;
 		MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
+		hdmifrlparams.bpc = (int)(DesiredBPP * 2 / 3);
 	} else if (Format == dm_444) {
 		NonDSCBPP0 = 24;
 		NonDSCBPP1 = 30;
 		NonDSCBPP2 = 36;
 		MinDSCBPP = 8;
 		MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
+	    hdmifrlparams.bpc = (int)(DesiredBPP / 3);
 	} else {
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
+	    hdmifrlparams.bpc = (int)(DesiredBPP / 2);
 
 		NonDSCBPP0 = 16;
 		NonDSCBPP1 = 20;
@@ -3636,12 +3693,22 @@ static double TruncToValidBPP(
 		if (Format == dm_n422) {
 			MinDSCBPP = 7;
 			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
+		} else if (Output == dm_hdmifrl) {
+			MinDSCBPP = 7;
+			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
 		} else {
 			MinDSCBPP = 8;
 			MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0;
 		}
 	}
 
+	if (Output == dm_hdmifrl) {
+		hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter);
+		MaxLinkBPP = (1 - hdmifrlinter.overhead_max)
+				* dml_min(
+						hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive,
+						(hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive);
+	} else
 	if (DSCEnable && Output == dm_dp) {
 		MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100);
 	} else {
@@ -3678,6 +3745,8 @@ static double TruncToValidBPP(
 		if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP <= NonDSCBPP0))
 				|| (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) {
 			return BPP_INVALID;
+		} else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) {
+			return BPP_INVALID;
 		} else {
 			return DesiredBPP;
 		}
@@ -4105,6 +4174,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 
 				if (v->ODMCombinePolicy == dm_odm_combine_policy_none
 						|| !(v->Output[k] == dm_dp ||
+						     v->Output[k] == dm_hdmifrl ||
 						     v->Output[k] == dm_dp2p0 ||
 						     v->Output[k] == dm_edp)) {
 					v->ODMCombineEnablePerState[i][k] = dm_odm_combine_mode_disabled;
@@ -4529,6 +4599,131 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 							// v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3"
 						}
 					}
+				} else if (v->Output[k] == dm_hdmifrl) {
+					if (v->DSCEnable[k] == true) {
+						v->RequiresDSC[i][k] = true;
+						v->LinkDSCEnable = true;
+						v->RequiresFEC[i][k] = true;
+					} else {
+						v->RequiresDSC[i][k] = false;
+						v->LinkDSCEnable = false;
+						v->RequiresFEC[i][k] = false;
+					}
+					v->Outbpp = BPP_INVALID;
+					if (v->PHYCLKD18PerState[i] >= 3000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								3000,
+								3,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "3x3";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								6000,
+								3,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x3";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								6000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 8000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								8000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState(i, k) = v->Output[k] & "8x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								10000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								12000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4";
+					}
 				}
 			} else {
 				v->OutputBppPerState[i][k] = 0;
@@ -4542,6 +4737,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 			if (v->BlendingAndTiming[k] == k
 					&& (v->Output[k] == dm_dp ||
 					    v->Output[k] == dm_edp ||
+					    v->Output[k] == dm_hdmifrl ||
 					    v->Output[k] == dm_hdmi) && v->OutputBppPerState[i][k] == 0) {
 				v->LinkCapacitySupport[i] = false;
 			}
@@ -4553,6 +4749,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 		if (v->BlendingAndTiming[k] == k
 				&& (v->Output[k] == dm_dp ||
 				    v->Output[k] == dm_edp ||
+				    v->Output[k] == dm_hdmifrl ||
 				    v->Output[k] == dm_hdmi)) {
 			if (v->OutputFormat[k] == dm_420 && v->Interlace[k] == 1 && v->ProgressiveToInterlaceUnitInOPP == true) {
 				P2IWith420 = true;
@@ -4564,11 +4761,31 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 		}
 	}
 
+	for (i = 0; i < v->soc.num_states; ++i) {
+		v->DTBCLKRequiredMoreThanSupported[i] = false;
+		for (k = 0; k < v->NumberOfActivePlanes; ++k) {
+			if (v->BlendingAndTiming[k] == k && v->Output[k] == dm_hdmifrl
+					&& RequiredDTBCLK(
+							v->RequiresDSC[i][k],
+							v->PixelClockBackEnd[k],
+							v->OutputFormat[k],
+							v->OutputBppPerState[i][k],
+							v->NumberOfDSCSlices[k],
+							v->HTotal[k],
+							v->HActive[k],
+							v->AudioSampleRate[k],
+							v->AudioSampleLayout[k]) > v->DTBCLKPerState[i]) {
+				v->DTBCLKRequiredMoreThanSupported[i] = true;
+			}
+		}
+	}
+
 	for (i = 0; i < v->soc.num_states; ++i) {
 		v->ODMCombine4To1SupportCheckOK[i] = true;
 		for (k = 0; k < v->NumberOfActivePlanes; ++k) {
 			if (v->BlendingAndTiming[k] == k && v->ODMCombineEnablePerState[i][k] == dm_odm_combine_mode_4to1
 					&& (v->ODMCombine4To1Supported == false || v->Output[k] == dm_dp || v->Output[k] == dm_edp
+							|| (v->Output[k] == dm_hdmifrl && v->DSCEnable[k] == false)
 							|| v->Output[k] == dm_hdmi)) {
 				v->ODMCombine4To1SupportCheckOK[i] = false;
 			}
@@ -7055,6 +7272,29 @@ static double CalculateUrgentLatency(
 	return ret;
 }
 
+static double RequiredDTBCLK(
+		bool DSCEnable,
+		double PixelClock,
+		enum output_format_class OutputFormat,
+		double OutputBPP,
+		int DSCSlices,
+		int HTotal,
+		int HActive,
+		int AudioRate,
+		int AudioLayout)
+{
+	if (DSCEnable != true) {
+		return dml_max(PixelClock / 4.0 * OutputBPP / 24.0, 25.0);
+	} else {
+		double PixelWordRate = PixelClock / (OutputFormat == dm_444 ? 1 : 2);
+		double HCActive = dml_ceil(DSCSlices * dml_ceil(OutputBPP * dml_ceil(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1);
+		double HCBlank = 64 + 32 * dml_ceil(AudioRate * (AudioLayout == 1 ? 1.0 : 0.25) * HTotal / (PixelClock * 1000), 1);
+		double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal;
+		double HActiveTribyteRate = PixelWordRate * HCActive / HActive;
+		return dml_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002;
+	}
+}
+
 static noinline_for_stack void UseMinimumDCFCLK(
 		struct display_mode_lib *mode_lib,
 		int MaxPrefetchMode,
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 dd9dc0c8cb43..674605d8d4fd 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
@@ -29,6 +29,8 @@
 #include "display_mode_vba_314.h"
 #include "../dml_inline_defs.h"
 
+#include "../dml1_frl_cap_chk.h"
+
 /*
  * NOTE:
  *   This file is gcc-parsable HW gospel, coming straight from HW engineers.
@@ -366,6 +368,17 @@ static void CalculateUrgentBurstFactor(
 		double *UrgentBurstFactorChroma,
 		bool *NotEnoughUrgentLatencyHiding);
 
+static double RequiredDTBCLK(
+		bool DSCEnable,
+		double PixelClock,
+		enum output_format_class OutputFormat,
+		double OutputBPP,
+		int DSCSlices,
+		int HTotal,
+		int HActive,
+		int AudioRate,
+		int AudioLayoutSingle);
+
 static void UseMinimumDCFCLK(
 		struct display_mode_lib *mode_lib,
 		int MaxPrefetchMode,
@@ -713,6 +726,8 @@ static unsigned int dscceComputeDelay(
 		pixelsPerClock = 1;
 	else if (pixelFormat == dm_n422)
 		pixelsPerClock = 2;
+	else if (Output == dm_hdmifrl)
+		pixelsPerClock = 2;
 	// #all other modes operate at 1 pixel per clock
 	else
 		pixelsPerClock = 1;
@@ -734,6 +749,8 @@ static unsigned int dscceComputeDelay(
 	//422 mode has an additional cycle of delay
 	if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422)
 		s = 0;
+	else if (Output == dm_hdmifrl)
+		s = 0;
 	else
 		s = 1;
 
@@ -804,6 +821,25 @@ static unsigned int dscComputeDelay(enum output_format_class pixelFormat, enum o
 		Delay = Delay + 1;
 		//   sft
 		Delay = Delay + 1;
+	} else if (Output == dm_hdmifrl && pixelFormat != dm_444) {
+		//   sfr
+		Delay = Delay + 2;
+		//   dsccif
+		Delay = Delay + 1;
+		//   dscc - input deserializer
+		Delay = Delay + 5;
+		//  dscc - input cdc fifo
+		Delay = Delay + 25;
+		//   dscc - cdc uncertainty
+		Delay = Delay + 2;
+		//   dscc - output cdc fifo
+		Delay = Delay + 10;
+		//   dscc - cdc uncertainty
+		Delay = Delay + 2;
+		//   dscc - output serializer
+		Delay = Delay + 1;
+		//   sft
+		Delay = Delay + 1;
 	} else {
 		//   sfr
 		Delay = Delay + 2;
@@ -2261,6 +2297,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman
 				v->DSCFormatFactor = 1;
 			else if (v->OutputFormat[k] == dm_n422)
 				v->DSCFormatFactor = 2;
+			else if (v->Output[k] == dm_hdmifrl)
+				v->DSCFormatFactor = 2;
 			else
 				v->DSCFormatFactor = 1;
 			if (v->ODMCombineEnabled[k] == dm_odm_combine_mode_4to1)
@@ -3721,19 +3759,38 @@ static double TruncToValidBPP(
 	int NonDSCBPP1;
 	int NonDSCBPP2;
 
+	enum frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK;
+	struct frl_cap_chk_params hdmifrlparams = { 0 };
+	struct frl_cap_chk_intermediates hdmifrlinter = { 0 };
+
+	hdmifrlparams.lanes = Lanes;
+	hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000;
+	hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000;
+	hdmifrlparams.layout = AudioLayout;
+	hdmifrlparams.f_audio = AudioRate * 1000;
+	hdmifrlparams.h_active = HActive;
+	hdmifrlparams.h_blank = HTotal - HActive;
+	hdmifrlparams.compressed = DSCEnable;
+
 	if (Format == dm_420) {
 		NonDSCBPP0 = 12;
 		NonDSCBPP1 = 15;
 		NonDSCBPP2 = 18;
 		MinDSCBPP = 6;
 		MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
+		hdmifrlparams.bpc = (int)(DesiredBPP * 2 / 3);
 	} else if (Format == dm_444) {
 		NonDSCBPP0 = 24;
 		NonDSCBPP1 = 30;
 		NonDSCBPP2 = 36;
 		MinDSCBPP = 8;
 		MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
+	    hdmifrlparams.bpc = (int)(DesiredBPP / 3);
 	} else {
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
+	    hdmifrlparams.bpc = (int)(DesiredBPP / 2);
 
 		NonDSCBPP0 = 16;
 		NonDSCBPP1 = 20;
@@ -3742,12 +3799,22 @@ static double TruncToValidBPP(
 		if (Format == dm_n422) {
 			MinDSCBPP = 7;
 			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
+		} else if (Output == dm_hdmifrl) {
+			MinDSCBPP = 7;
+			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
 		} else {
 			MinDSCBPP = 8;
 			MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0;
 		}
 	}
 
+	if (Output == dm_hdmifrl) {
+		hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter);
+		MaxLinkBPP = (1 - hdmifrlinter.overhead_max)
+				* dml_min(
+						hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive,
+						(hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max * HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive);
+	} else
 	if (DSCEnable && Output == dm_dp) {
 		MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100);
 	} else {
@@ -3784,6 +3851,8 @@ static double TruncToValidBPP(
 		if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP <= NonDSCBPP0))
 				|| (DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) {
 			return BPP_INVALID;
+		} else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP)) {
+			return BPP_INVALID;
 		} else {
 			return DesiredBPP;
 		}
@@ -4195,6 +4264,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_
 
 				if (v->ODMCombinePolicy == dm_odm_combine_policy_none
 						|| !(v->Output[k] == dm_dp ||
+						     v->Output[k] == dm_hdmifrl ||
 						     v->Output[k] == dm_dp2p0 ||
 						     v->Output[k] == dm_edp)) {
 					v->ODMCombineEnablePerState[i][k] = dm_odm_combine_mode_disabled;
@@ -4616,6 +4686,131 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_
 							// v->OutputTypeAndRatePerState[i][k] = v->Output[k] & " HBR3"
 						}
 					}
+				} else if (v->Output[k] == dm_hdmifrl) {
+					if (v->DSCEnable[k] == true) {
+						v->RequiresDSC[i][k] = true;
+						v->LinkDSCEnable = true;
+						v->RequiresFEC[i][k] = true;
+					} else {
+						v->RequiresDSC[i][k] = false;
+						v->LinkDSCEnable = false;
+						v->RequiresFEC[i][k] = false;
+					}
+					v->Outbpp = BPP_INVALID;
+					if (v->PHYCLKD18PerState[i] >= 3000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								3000,
+								3,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "3x3";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								6000,
+								3,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x3";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 6000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								6000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "6x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 8000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								8000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState(i, k) = v->Output[k] & "8x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 10000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								10000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "10x4";
+					}
+					if (v->Outbpp == BPP_INVALID && v->PHYCLKD18PerState[i] >= 12000.0 / 18) {
+						v->Outbpp = TruncToValidBPP(
+								12000,
+								4,
+								v->HTotal[k],
+								v->HActive[k],
+								v->PixelClockBackEnd[k],
+								v->ForcedOutputLinkBPP[k],
+								v->LinkDSCEnable,
+								v->Output[k],
+								v->OutputFormat[k],
+								v->DSCInputBitPerComponent[k],
+								v->NumberOfDSCSlices[k],
+								v->AudioSampleRate[k],
+								v->AudioSampleLayout[k],
+								v->ODMCombineEnablePerState[i][k]);
+						v->OutputBppPerState[i][k] = v->Outbpp;
+						//v->OutputTypeAndRatePerState[i][k] = v->Output[k] & "12x4";
+					}
 				}
 			} else {
 				v->OutputBppPerState[i][k] = 0;
@@ -4629,6 +4824,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_
 			if (v->BlendingAndTiming[k] == k
 					&& (v->Output[k] == dm_dp ||
 					    v->Output[k] == dm_edp ||
+					    v->Output[k] == dm_hdmifrl ||
 					    v->Output[k] == dm_hdmi) && v->OutputBppPerState[i][k] == 0) {
 				v->LinkCapacitySupport[i] = false;
 			}
@@ -4640,6 +4836,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_
 		if (v->BlendingAndTiming[k] == k
 				&& (v->Output[k] == dm_dp ||
 				    v->Output[k] == dm_edp ||
+				    v->Output[k] == dm_hdmifrl ||
 				    v->Output[k] == dm_hdmi)) {
 			if (v->OutputFormat[k] == dm_420 && v->Interlace[k] == 1 && v->ProgressiveToInterlaceUnitInOPP == true) {
 				P2IWith420 = true;
@@ -4651,6 +4848,24 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_
 		}
 	}
 
+	for (i = 0; i < v->soc.num_states; ++i) {
+		v->DTBCLKRequiredMoreThanSupported[i] = false;
+		for (k = 0; k < v->NumberOfActivePlanes; ++k) {
+			if (v->BlendingAndTiming[k] == k && v->Output[k] == dm_hdmifrl
+					&& RequiredDTBCLK(
+							v->RequiresDSC[i][k],
+							v->PixelClockBackEnd[k],
+							v->OutputFormat[k],
+							v->OutputBppPerState[i][k],
+							v->NumberOfDSCSlices[k],
+							v->HTotal[k],
+							v->HActive[k],
+							v->AudioSampleRate[k],
+							v->AudioSampleLayout[k]) > v->DTBCLKPerState[i]) {
+				v->DTBCLKRequiredMoreThanSupported[i] = true;
+			}
+		}
+	}
 
 	for (i = 0; i < v->soc.num_states; ++i) {
 		v->ODMCombine4To1SupportCheckOK[i] = true;
@@ -7141,6 +7356,29 @@ static double CalculateUrgentLatency(
 	return ret;
 }
 
+static double RequiredDTBCLK(
+		bool DSCEnable,
+		double PixelClock,
+		enum output_format_class OutputFormat,
+		double OutputBPP,
+		int DSCSlices,
+		int HTotal,
+		int HActive,
+		int AudioRate,
+		int AudioLayout)
+{
+	if (DSCEnable != true) {
+		return dml_max(PixelClock / 4.0 * OutputBPP / 24.0, 25.0);
+	} else {
+		double PixelWordRate = PixelClock / (OutputFormat == dm_444 ? 1 : 2);
+		double HCActive = dml_ceil(DSCSlices * dml_ceil(OutputBPP * dml_ceil(HActive / DSCSlices, 1) / 8.0, 1) / 3.0, 1);
+		double HCBlank = 64 + 32 * dml_ceil(AudioRate * (AudioLayout == 1 ? 1.0 : 0.25) * HTotal / (PixelClock * 1000), 1);
+		double AverageTribyteRate = PixelWordRate * (HCActive + HCBlank) / HTotal;
+		double HActiveTribyteRate = PixelWordRate * HCActive / HActive;
+		return dml_max4(PixelWordRate / 4.0, AverageTribyteRate / 4.0, HActiveTribyteRate / 4.0, 25.0) * 1.002;
+	}
+}
+
 static noinline_for_stack void UseMinimumDCFCLK(
 		struct display_mode_lib *mode_lib,
 		int MaxPrefetchMode,
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c
index 1b1ab6a6d53a..b5bd1358fdfd 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c
@@ -1608,6 +1608,8 @@ static bool is_dtbclk_required(struct dc *dc, struct dc_state *context)
 	for (i = 0; i < dc->res_pool->pipe_count; i++) {
 		if (!context->res_ctx.pipe_ctx[i].stream)
 			continue;
+		if (dc_is_hdmi_frl_signal(context->res_ctx.pipe_ctx[i].stream->signal))
+			return true;
 		if (dc->link_srv->dp_is_128b_132b_signal(&context->res_ctx.pipe_ctx[i]))
 			return true;
 	}
@@ -3588,3 +3590,11 @@ void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context)
 		}
 	}
 }
+
+unsigned int dcn32_get_max_dispclk_mhz(struct dc *dc, struct dc_state *context)
+{
+	(void)dc;
+	int max_level = context->bw_ctx.dml.soc.num_states;
+
+	return (unsigned int) context->bw_ctx.dml.soc.clock_limits[max_level - 1].dispclk_mhz;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h
index 273d2bd79d85..ff83fbc811d3 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h
@@ -77,4 +77,6 @@ void dcn32_override_min_req_memclk(struct dc *dc, struct dc_state *context);
 
 void dcn32_set_clock_limits(const struct _vcs_dpi_soc_bounding_box_st *soc_bb);
 
+unsigned int dcn32_get_max_dispclk_mhz(struct dc *dc, struct dc_state *context);
+
 #endif
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c
index 505b5a5f212e..81e608b3cc74 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_32.c
@@ -29,6 +29,8 @@
 #include "../dml_inline_defs.h"
 #include "display_mode_vba_util_32.h"
 
+#include "../dml1_frl_cap_chk.h"
+
 void dml32_recalculate(struct display_mode_lib *mode_lib);
 static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerformanceCalculation(
 		struct display_mode_lib *mode_lib);
@@ -342,6 +344,8 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman
 				mode_lib->vba.DSCFormatFactor = 1;
 			else if (mode_lib->vba.OutputFormat[k] == dm_n422)
 				mode_lib->vba.DSCFormatFactor = 2;
+			else if (mode_lib->vba.Output[k] == dm_hdmifrl)
+				mode_lib->vba.DSCFormatFactor = 2;
 			else
 				mode_lib->vba.DSCFormatFactor = 1;
 			if (mode_lib->vba.ODMCombineEnabled[k] == dm_odm_combine_mode_4to1)
@@ -2282,6 +2286,8 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 	for (k = 0; k < mode_lib->vba.NumberOfActiveSurfaces; ++k) {
 		if (mode_lib->vba.BlendingAndTiming[k] == k) {
 			v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveOTG = v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveOTG + 1;
+			if (mode_lib->vba.Output[k] == dm_hdmifrl)
+				v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 = v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 + 1;
 			if (mode_lib->vba.Output[k] == dm_dp2p0) {
 				v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 = v->dummy_vars.dml32_ModeSupportAndSystemConfigurationFull.TotalNumberOfActiveDP2p0 + 1;
 				if (mode_lib->vba.OutputMultistreamId[k]
@@ -2328,6 +2334,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 			if (mode_lib->vba.BlendingAndTiming[k] == k
 				&& (mode_lib->vba.Output[k] == dm_dp || mode_lib->vba.Output[k] == dm_dp2p0
 					|| mode_lib->vba.Output[k] == dm_edp
+					|| mode_lib->vba.Output[k] == dm_hdmifrl
 					|| mode_lib->vba.Output[k] == dm_hdmi)
 				&& mode_lib->vba.OutputBppPerState[i][k] == 0 &&
 				(mode_lib->vba.UsesMALLForPStateChange[k] != dm_use_mall_pstate_change_phantom_pipe)) {
@@ -2353,6 +2360,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 		if (mode_lib->vba.BlendingAndTiming[k] == k
 				&& (mode_lib->vba.Output[k] == dm_dp || mode_lib->vba.Output[k] == dm_dp2p0
 						|| mode_lib->vba.Output[k] == dm_edp
+						|| mode_lib->vba.Output[k] == dm_hdmifrl
 						|| mode_lib->vba.Output[k] == dm_hdmi)) {
 			if (mode_lib->vba.OutputFormat[k]
 					== dm_420 && mode_lib->vba.Interlace[k] == 1 &&
@@ -2388,7 +2396,9 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 				}
 			}
 
-			if ((mode_lib->vba.Output[k] == dm_edp || mode_lib->vba.Output[k] == dm_hdmi)) {
+			if ((mode_lib->vba.Output[k] == dm_edp
+					|| mode_lib->vba.Output[k] == dm_hdmifrl
+					|| mode_lib->vba.Output[k] == dm_hdmi)) {
 				if (mode_lib->vba.OutputMultistreamEn[k] == true && mode_lib->vba.OutputMultistreamId[k] == k)
 					mode_lib->vba.MultistreamWithHDMIOreDP = true;
 				for (j = 0; j < mode_lib->vba.NumberOfActiveSurfaces; ++j) {
@@ -2415,6 +2425,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 		mode_lib->vba.DTBCLKRequiredMoreThanSupported[i] = false;
 		for (k = 0; k < mode_lib->vba.NumberOfActiveSurfaces; ++k) {
 			if (mode_lib->vba.BlendingAndTiming[k] == k
+					&& mode_lib->vba.Output[k] == dm_hdmifrl
 					&& dml32_RequiredDTBCLK(mode_lib->vba.RequiresDSC[i][k],
 							mode_lib->vba.PixelClockBackEnd[k],
 							mode_lib->vba.OutputFormat[k],
@@ -2451,6 +2462,7 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 		for (k = 0; k <= mode_lib->vba.NumberOfActiveSurfaces - 1; k++) {
 			if (mode_lib->vba.BlendingAndTiming[k] == k) {
 				if (mode_lib->vba.Output[k] == dm_dp || mode_lib->vba.Output[k] == dm_dp2p0
+						|| mode_lib->vba.Output[k] == dm_hdmifrl
 						|| mode_lib->vba.Output[k] == dm_edp) {
 					if (mode_lib->vba.OutputFormat[k] == dm_420) {
 						mode_lib->vba.DSCFormatFactor = 2;
@@ -2458,6 +2470,8 @@ void dml32_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
 						mode_lib->vba.DSCFormatFactor = 1;
 					} else if (mode_lib->vba.OutputFormat[k] == dm_n422) {
 						mode_lib->vba.DSCFormatFactor = 2;
+					} else if (mode_lib->vba.Output[k] == dm_hdmifrl) {
+						mode_lib->vba.DSCFormatFactor = 2;
 					} else {
 						mode_lib->vba.DSCFormatFactor = 1;
 					}
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 15f5248340a7..40a0a5815ca5 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
@@ -24,6 +24,7 @@
  */
 #include "display_mode_vba_util_32.h"
 #include "../dml_inline_defs.h"
+#include "../dml1_frl_cap_chk.h"
 #include "display_mode_vba_32.h"
 #include "../display_mode_lib.h"
 
@@ -55,6 +56,8 @@ unsigned int dml32_dscceComputeDelay(
 
 	if (pixelFormat == dm_420)
 		pixelsPerClock = 2;
+	else if (Output == dm_hdmifrl)
+		pixelsPerClock = 2;
 	else if (pixelFormat == dm_n422)
 		pixelsPerClock = 2;
 	// #all other modes operate at 1 pixel per clock
@@ -78,6 +81,8 @@ unsigned int dml32_dscceComputeDelay(
 	//422 mode has an additional cycle of delay
 	if (pixelFormat == dm_420 || pixelFormat == dm_444 || pixelFormat == dm_n422)
 		s = 0;
+	else if (Output == dm_hdmifrl)
+		s = 0;
 	else
 		s = 1;
 
@@ -140,7 +145,7 @@ unsigned int dml32_dscComputeDelay(enum output_format_class pixelFormat, enum ou
 		Delay = Delay + 1;
 		//   sft
 		Delay = Delay + 1;
-	} else if (pixelFormat == dm_n422 || (pixelFormat != dm_444)) {
+	} else if (pixelFormat == dm_n422 || (Output == dm_hdmifrl && pixelFormat != dm_444)) {
 		//   sfr
 		Delay = Delay + 2;
 		//   dsccif
@@ -1546,6 +1551,91 @@ void dml32_CalculateOutputLink(
 					*OutputRate = dm_output_rate_dp_rate_hbr3;
 				}
 			}
+		} else if (Output == dm_hdmifrl) {
+			if (DSCEnable == true) {
+				*RequiresDSC = true;
+				LinkDSCEnable = true;
+				*RequiresFEC = true;
+			} else {
+				*RequiresDSC = false;
+				LinkDSCEnable = false;
+				*RequiresFEC = false;
+			}
+			*OutBpp = 0;
+			if (PHYCLKD18PerState >= 3000 / 18) {
+				*OutBpp = dml32_TruncToValidBPP(3000, 3, HTotal, HActive, PixelClockBackEnd,
+						ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+						DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+						AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "3x3";
+				*OutputType = dm_output_type_hdmifrl;
+				*OutputRate = dm_output_rate_hdmi_rate_3x3;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) {
+				*OutBpp = dml32_TruncToValidBPP(6000, 3, HTotal, HActive, PixelClockBackEnd,
+						ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+						DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+						AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "6x3";
+				*OutputType = dm_output_type_hdmifrl;
+				*OutputRate = dm_output_rate_hdmi_rate_6x3;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) {
+				*OutBpp = dml32_TruncToValidBPP(6000, 4, HTotal, HActive, PixelClockBackEnd,
+						ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+						DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+						AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "6x4";
+				*OutputType = dm_output_type_hdmifrl;
+				*OutputRate = dm_output_rate_hdmi_rate_6x4;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 8000 / 18) {
+				*OutBpp = dml32_TruncToValidBPP(8000, 4, HTotal, HActive, PixelClockBackEnd,
+						ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+						DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+						AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "8x4";
+				*OutputType = dm_output_type_hdmifrl;
+				*OutputRate = dm_output_rate_hdmi_rate_8x4;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 10000 / 18) {
+				*OutBpp = dml32_TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd,
+						ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+						DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+						AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				if (*OutBpp == 0 && DSCEnable == true && ForcedOutputLinkBPP == 0 &&
+						PHYCLKD18PerState < 12000 / 18) {
+					*RequiresDSC = true;
+					LinkDSCEnable = true;
+					*RequiresFEC = true;
+					*OutBpp = dml32_TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd,
+							ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+							DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+							AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				}
+				//OutputTypeAndRate = Output & "10x4";
+				*OutputType = dm_output_type_hdmifrl;
+				*OutputRate = dm_output_rate_hdmi_rate_10x4;
+			}
+
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 12000 / 18) {
+				*OutBpp = dml32_TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd,
+						ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+						DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+						AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				if (*OutBpp == 0 && DSCEnable == true && ForcedOutputLinkBPP == 0) {
+					*RequiresDSC = true;
+					LinkDSCEnable = true;
+					*RequiresFEC = true;
+					*OutBpp = dml32_TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd,
+							ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat,
+							DSCInputBitPerComponent, NumberOfDSCSlices, (unsigned int)AudioSampleRate,
+							AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				}
+				//OutputTypeAndRate = Output & "12x4";
+				*OutputType = dm_output_type_hdmifrl;
+				*OutputRate = dm_output_rate_hdmi_rate_12x4;
+			}
 		}
 	}
 }
@@ -1599,12 +1689,27 @@ double dml32_TruncToValidBPP(
 	unsigned int   NonDSCBPP2;
 	unsigned int   NonDSCBPP3 = BPP_INVALID;
 
+	enum frl_cap_chk_result          hdmifrlresult = FRL_CAP_CHK_OK;
+	struct frl_cap_chk_params          hdmifrlparams = { 0 };
+	struct frl_cap_chk_intermediates   hdmifrlinter = { 0 };
+
+	hdmifrlparams.lanes = Lanes;
+	hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000;
+	hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000;
+	hdmifrlparams.layout = AudioLayout;
+	hdmifrlparams.f_audio = AudioRate * 1000;
+	hdmifrlparams.h_active = HActive;
+	hdmifrlparams.h_blank = HTotal - HActive;
+	hdmifrlparams.bpc = (int)(DesiredBPP / 3);
+	hdmifrlparams.compressed = DSCEnable;
+
 	if (Format == dm_420) {
 		NonDSCBPP0 = 12;
 		NonDSCBPP1 = 15;
 		NonDSCBPP2 = 18;
 		MinDSCBPP = 6;
 		MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_420;
 	} else if (Format == dm_444) {
 		NonDSCBPP3 = 18;
 		NonDSCBPP0 = 24;
@@ -1612,8 +1717,10 @@ double dml32_TruncToValidBPP(
 		NonDSCBPP2 = 36;
 		MinDSCBPP = 8;
 		MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_444;
 	} else {
-		if (Output == dm_hdmi) {
+		hdmifrlparams.pixel_encoding = HDMI_FRL_PIXEL_ENCODING_422;
+		if (Output == dm_hdmi || Output == dm_hdmifrl) {
 			NonDSCBPP0 = 24;
 			NonDSCBPP1 = 24;
 			NonDSCBPP2 = 24;
@@ -1622,7 +1729,7 @@ double dml32_TruncToValidBPP(
 			NonDSCBPP1 = 20;
 			NonDSCBPP2 = 24;
 		}
-		if (Format == dm_n422) {
+		if (Format == dm_n422 || Output == dm_hdmifrl) {
 			MinDSCBPP = 7;
 			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
 		} else {
@@ -1630,7 +1737,13 @@ double dml32_TruncToValidBPP(
 			MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16.0;
 		}
 	}
-	if (Output == dm_dp2p0) {
+	if (Output == dm_hdmifrl) {
+		hdmifrlresult = dml1_frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter);
+		MaxLinkBPP = (1 - hdmifrlinter.overhead_max) * dml_min(hdmifrlinter.r_frl_char_min * 16 *
+				Lanes / hdmifrlinter.f_pixel_clock_max + 24 * TB_BORROWED_MAX / HActive,
+				(hdmifrlinter.r_frl_char_min * 16 * Lanes / hdmifrlinter.f_pixel_clock_max *
+						HTotal - 16 * hdmifrlinter.blank_audio_min) / HActive);
+	} else if (Output == dm_dp2p0) {
 		MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128 / 132 * 383 / 384 * 65536 / 65540;
 	} else if (DSCEnable && Output == dm_dp) {
 		MaxLinkBPP = LinkBitRate / 10 * 8 * Lanes / PixelClock * (1 - 2.4 / 100);
@@ -1681,6 +1794,9 @@ double dml32_TruncToValidBPP(
 				DesiredBPP <= NonDSCBPP0)) ||
 				(DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP)))
 			return BPP_INVALID;
+		else if ((Output == dm_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) ||
+				(Output != dm_hdmifrl && MaxLinkBPP < DesiredBPP))
+			return BPP_INVALID;
 		else
 			return DesiredBPP;
 	}
diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h
index d5831a34f5a1..42013fa5ad01 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h
+++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_enums.h
@@ -30,6 +30,7 @@ enum output_encoder_class {
 	dm_hdmi = 1,
 	dm_wb = 2,
 	dm_edp = 3,
+	dm_hdmifrl = 4,
 	dm_dp2p0 = 5,
 };
 enum output_format_class {
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
new file mode 100644
index 000000000000..9dde4e56f237
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2023 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dml_logger.h"
+#include "dml1_frl_cap_chk.h"
+#include "dml_inline_defs.h"
+
+static const double __maybe_unused EPSILON			= 0.01;
+static const double __maybe_unused DBL_EPSILON			= 2.2204460492503131e-16;
+static const double __maybe_unused OVERHEAD_M			= 0.003;  /* %   */
+static const double __maybe_unused TOLERANCE_PIXEL_CLOCK	= 0.005;  /* %   */
+static const double __maybe_unused DML_TOLERANCE_AUDIO_CLOCK	= 1000;   /* ppm */
+
+#define frl_dump_var(fmt, var) {}
+#define frl_print(fmt, ...) {}
+
+const struct frl_primary_format prim_format_444[] = {
+/* VIC/Rate/Lanes/HCactive/HCBlank */
+	{64,  3, 3, 960,  360}, /* 1920x1080 @ 100 */
+	{77,  3, 3, 960,  360}, /* 1920x1080 @ 100 */
+	{63,  3, 3, 960,  140}, /* 1920x1080 @ 120 */
+	{78,  3, 3, 960,  140}, /* 1920x1080 @ 120 */
+	{93,  3, 3, 1920, 828}, /* 3840x2160 @ 24 */
+	{103, 3, 3, 1920, 828}, /* 3840x2160 @ 24 */
+	{94,  3, 3, 1920, 720}, /* 3840x2160 @ 25 */
+	{104, 3, 3, 1920, 720}, /* 3840x2160 @ 25 */
+	{95,  3, 3, 1920, 280}, /* 3840x2160 @ 30 */
+	{105, 3, 3, 1920, 280}, /* 3840x2160 @ 30 */
+	{114, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */
+	{116, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */
+	{96,  3, 3, 1920, 720}, /* 3840x2160 @ 50 */
+	{106, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */
+	{97,  3, 3, 1920, 280}, /* 3840x2160 @ 60 */
+	{107, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */
+	{117, 6, 3, 1920, 720}, /* 3840x2160 @ 100 */
+	{119, 6, 3, 1920, 720}, /* 3840x2160 @ 100 */
+	{118, 6, 3, 1920, 280}, /* 3840x2160 @ 120 */
+	{120, 6, 3, 1920, 280}, /* 3840x2160 @ 120 */
+	{98,  3, 3, 2048, 700}, /* 4096x2160 @ 24 */
+	{99,  3, 3, 2048, 592}, /* 4096x2160 @ 25 */
+	{100, 3, 3, 2048, 152}, /* 4096x2160 @ 30 */
+	{115, 3, 3, 2048, 700}, /* 4096x2160 @ 48 */
+	{101, 3, 3, 2048, 592}, /* 4096x2160 @ 50 */
+	{102, 3, 3, 2048, 152}, /* 4096x2160 @ 60 */
+	{218, 6, 3, 2048, 592}, /* 4096x2160 @ 100 */
+	{219, 6, 3, 2048, 152}, /* 4096x2160 @ 120 */
+	{121, 3, 3, 2560, 1188}, /* 5120x2160 @ 24 */
+	{122, 3, 3, 2560, 1040}, /* 5120x2160 @ 25 */
+	{123, 3, 3, 2560, 440}, /* 5120x2160 @ 30 */
+	{124, 3, 3, 2560, 256}, /* 5120x2160 @ 48 */
+	{125, 3, 3, 2560, 484}, /* 5120x2160 @ 50 */
+	{126, 3, 3, 2307, 144}, /* 5120x2160 @ 60 */
+	{127, 6, 3, 2560, 484}, /* 5120x2160 @ 100 */
+	{193, 6, 3, 2334, 104}, /* 5120x2160 @ 120 */
+	{194, 6, 3, 3840, 1660}, /* 7680x2160 @ 24 */
+	{202, 6, 3, 3840, 1660}, /* 7680x2160 @ 24 */
+	{195, 6, 3, 3840, 1560}, /* 7680x2160 @ 25 */
+	{203, 6, 3, 3840, 1560}, /* 7680x2160 @ 25 */
+	{196, 6, 3, 3840, 660}, /* 7680x2160 @ 30 */
+	{204, 6, 3, 3840, 660}, /* 7680x2160 @ 30 */
+	{197, 6, 4, 3142, 1292}, /* 7680x2160 @ 48 */
+	{205, 6, 4, 3142, 1292}, /* 7680x2160 @ 48 */
+	{198, 6, 4, 3142, 1180}, /* 7680x2160 @ 50 */
+	{206, 6, 4, 3142, 1180}, /* 7680x2160 @ 50 */
+	{199, 6, 4, 3182, 140}, /* 7680x2160 @ 60 */
+	{207, 6, 4, 3182, 140}, /* 7680x2160 @ 60 */
+	{200, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */
+	{208, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */
+	{201, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */
+	{209, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */
+	{210, 6, 3, 4854, 912}, /* 10240x4320 @ 24 */
+	{211, 6, 3, 4827, 1536}, /* 10240x4320 @ 25 */
+	{212, 6, 3, 4720, 128}, /* 10240x4320 @ 30 */
+	{213, 8, 4, 4347, 756}, /* 10240x4320 @ 48 */
+	{214, 8, 4, 4320, 1376}, /* 10240x4320 @ 50 */
+	{215, 8, 4, 4187, 124}, /* 10240x4320 @ 60 */
+};
+
+const struct frl_primary_format prim_format_422[] = {
+/* VIC/Rate/Lanes/HCactive/HCBlank */
+	{64,  3, 3, 960,  360}, /* 1920x1080 @ 100 */
+	{77,  3, 3, 960,  360}, /* 1920x1080 @ 100 */
+	{63,  3, 3, 960,  140}, /* 1920x1080 @ 120 */
+	{78,  3, 3, 960,  140}, /* 1920x1080 @ 120 */
+	{93,  3, 3, 1920, 828}, /* 3840x2160 @ 24 */
+	{103, 3, 3, 1920, 828}, /* 3840x2160 @ 24 */
+	{94,  3, 3, 1920, 720}, /* 3840x2160 @ 25 */
+	{104, 3, 3, 1920, 720}, /* 3840x2160 @ 25 */
+	{95,  3, 3, 1920, 280}, /* 3840x2160 @ 30 */
+	{105, 3, 3, 1920, 280}, /* 3840x2160 @ 30 */
+	{114, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */
+	{116, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */
+	{96,  3, 3, 1920, 720}, /* 3840x2160 @ 50 */
+	{106, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */
+	{97,  3, 3, 1920, 280}, /* 3840x2160 @ 60 */
+	{107, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */
+	{117, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */
+	{119, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */
+	{118, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */
+	{120, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */
+	{98,  3, 3, 2048, 700}, /* 4096x2160 @ 24 */
+	{99,  3, 3, 2048, 592}, /* 4096x2160 @ 25 */
+	{100, 3, 3, 2048, 152}, /* 4096x2160 @ 30 */
+	{115, 3, 3, 2048, 700}, /* 4096x2160 @ 48 */
+	{101, 3, 3, 2048, 592}, /* 4096x2160 @ 50 */
+	{102, 3, 3, 2048, 152}, /* 4096x2160 @ 60 */
+	{218, 6, 3, 2048, 592}, /* 4096x2160 @ 100 */
+	{219, 6, 3, 2048, 152}, /* 4096x2160 @ 120 */
+	{121, 3, 3, 2560, 1188}, /* 5120x2160 @ 24 */
+	{122, 3, 3, 2560, 1040}, /* 5120x2160 @ 25 */
+	{123, 3, 3, 2560, 440}, /* 5120x2160 @ 30 */
+	{124, 3, 3, 2560, 256}, /* 5120x2160 @ 48 */
+	{125, 3, 3, 2560, 484}, /* 5120x2160 @ 50 */
+	{126, 3, 3, 2307, 144}, /* 5120x2160 @ 60 */
+	{127, 6, 3, 2560, 484}, /* 5120x2160 @ 100 */
+	{193, 6, 3, 2334, 104}, /* 5120x2160 @ 120 */
+	{194, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */
+	{202, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */
+	{195, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */
+	{203, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */
+	{196, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */
+	{204, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */
+	{197, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */
+	{205, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */
+	{198, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */
+	{206, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */
+	{199, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */
+	{207, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */
+	{200, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */
+	{208, 10, 4, 2680, 784}, /* 7680x2160 @ 100 */
+	{201, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */
+	{209, 10, 4, 2600, 100}, /* 7680x2160 @ 120 */
+	{210, 6, 3, 4854, 912}, /* 10240x4320 @ 24 */
+	{211, 6, 3, 4827, 1536}, /* 10240x4320 @ 25 */
+	{212, 6, 3, 4720, 128}, /* 10240x4320 @ 30 */
+	{213, 6, 4, 3360, 420}, /* 10240x4320 @ 48 */
+	{214, 6, 4, 3334, 892}, /* 10240x4320 @ 50 */
+	{215, 6, 4, 3120, 124}, /* 10240x4320 @ 60 */
+	{216, 12, 4, 3334, 764}, /* 10240x4320 @ 100 */
+	{217, 12, 4, 3120, 124}, /* 10240x4320 @ 120 */
+};
+
+const struct frl_primary_format prim_format_420[] = {
+/* VIC/Rate/Lanes/HCactive/HCBlank */
+	{114, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */
+	{116, 3, 3, 1920, 828}, /* 3840x2160 @ 48 */
+	{96,  3, 3, 1920, 720}, /* 3840x2160 @ 50 */
+	{106, 3, 3, 1920, 720}, /* 3840x2160 @ 50 */
+	{97,  3, 3, 1920, 280}, /* 3840x2160 @ 60 */
+	{107, 3, 3, 1920, 280}, /* 3840x2160 @ 60 */
+	{117, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */
+	{119, 3, 3, 1370, 104}, /* 3840x2160 @ 100 */
+	{118, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */
+	{120, 3, 3, 1130, 104}, /* 3840x2160 @ 120 */
+	{115, 3, 3, 2048, 700}, /* 4096x2160 @ 48 */
+	{101, 3, 3, 2048, 592}, /* 4096x2160 @ 50 */
+	{102, 3, 3, 2048, 152}, /* 4096x2160 @ 60 */
+	{218, 3, 3, 1376, 96}, /* 4096x2160 @ 100 */
+	{219, 3, 3, 1131, 84}, /* 4096x2160 @ 120 */
+	{124, 3, 3, 2560, 256}, /* 5120x2160 @ 48 */
+	{125, 3, 3, 2560, 484}, /* 5120x2160 @ 50 */
+	{126, 3, 3, 2307, 144}, /* 5120x2160 @ 60 */
+	{127, 6, 3, 2560, 484}, /* 5120x2160 @ 100 */
+	{193, 6, 3, 2334, 104}, /* 5120x2160 @ 120 */
+	{194, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */
+	{202, 3, 3, 2460, 816}, /* 7680x2160 @ 24 */
+	{195, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */
+	{203, 3, 3, 2460, 732}, /* 7680x2160 @ 25 */
+	{196, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */
+	{204, 3, 3, 2360, 144}, /* 7680x2160 @ 30 */
+	{197, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */
+	{205, 6, 3, 2460, 816}, /* 7680x2160 @ 48 */
+	{198, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */
+	{206, 6, 3, 2460, 732}, /* 7680x2160 @ 50 */
+	{199, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */
+	{207, 6, 3, 2380, 116}, /* 7680x2160 @ 60 */
+	{200, 8, 4, 2240, 480}, /* 7680x2160 @ 100 */
+	{208, 8, 4, 2240, 480}, /* 7680x2160 @ 100 */
+	{201, 8, 4, 2062, 108}, /* 7680x2160 @ 120 */
+	{209, 8, 4, 2062, 108}, /* 7680x2160 @ 120 */
+	{210, 3, 3, 2614, 172}, /* 10240x4320 @ 24 */
+	{211, 3, 3, 2614, 500}, /* 10240x4320 @ 25 */
+	{212, 6, 3, 4720, 128}, /* 10240x4320 @ 30 */
+	{213, 6, 3, 2614, 172}, /* 10240x4320 @ 48 */
+	{214, 6, 4, 3334, 892}, /* 10240x4320 @ 50 */
+	{215, 6, 4, 3120, 124}, /* 10240x4320 @ 60 */
+	{216, 10, 4, 2854, 520}, /* 10240x4320 @ 100 */
+	{217, 10, 4, 2587, 120}, /* 10240x4320 @ 120 */
+};
+
+enum frl_cap_chk_result dml1_frl_cap_chk_common(struct frl_cap_chk_intermediates *inter,
+						struct frl_cap_chk_params *params)
+{
+	double audio_bw_reserve = (params->compressed ? 192000.0 : 0.0);
+
+	dc_assert_fp_enabled();
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		printf("frl_cap_chk inputs:\n");
+		printf("-------------------\n");
+		frl_dump_var("%i",  params->lanes);
+		frl_dump_var("%le", params->f_pixel_clock_nominal);
+		frl_dump_var("%le", params->r_bit_nominal);
+		frl_dump_var("%i",  params->audio_packet_type);
+		frl_dump_var("%le", params->f_audio);
+		frl_dump_var("%i",  params->h_active);
+		frl_dump_var("%i",  params->h_blank);
+		frl_dump_var("%i",  params->bpc);
+		frl_dump_var("%i",  params->pixel_encoding);
+		frl_dump_var("%i",  params->compressed);
+		frl_dump_var("%i",  params->slices);
+		frl_dump_var("%i",  params->slice_width);
+		frl_dump_var("%le", params->bpp_target);
+		frl_dump_var("%i",  params->layout);
+		frl_dump_var("%i",  params->acat);
+		printf("frl_cap_chk outputs:\n");
+		printf("---------------------\n");
+	}
+#endif
+
+	inter->c_frl_sb          = 4 * C_FRL_CB + params->lanes;
+	inter->overhead_sb       = (double)params->lanes / inter->c_frl_sb;
+	inter->overhead_rs       = 8.0 * 4.0 / inter->c_frl_sb;
+	inter->overhead_map      = 2.5 / inter->c_frl_sb;
+	inter->overhead_min      = inter->overhead_sb + inter->overhead_rs + inter->overhead_map;
+	inter->overhead_max      = inter->overhead_min + OVERHEAD_M;
+	inter->f_pixel_clock_max = params->f_pixel_clock_nominal * (1.0 + TOLERANCE_PIXEL_CLOCK);
+	inter->t_line            = (params->h_active + params->h_blank) / inter->f_pixel_clock_max;
+	inter->r_bit_min         = params->r_bit_nominal * (1.0 - TOLERANCE_FRL_BIT / 1000000.0);
+	inter->r_frl_char_min    = inter->r_bit_min / 18.0;
+	inter->c_frl_line        = dml_floor(inter->t_line * inter->r_frl_char_min * params->lanes, 1);
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		frl_dump_var("%i",  inter->c_frl_sb);
+		frl_dump_var("%le", inter->overhead_sb);
+		frl_dump_var("%le", inter->overhead_rs);
+		frl_dump_var("%le", inter->overhead_map);
+		frl_dump_var("%le", inter->overhead_min);
+		frl_dump_var("%le", inter->overhead_max);
+		frl_dump_var("%le", inter->f_pixel_clock_max);
+		frl_dump_var("%le", inter->t_line);
+		frl_dump_var("%le", inter->r_bit_min);
+		frl_dump_var("%le", inter->r_frl_char_min);
+		frl_dump_var("%le", inter->c_frl_line);
+	}
+#endif
+
+	switch (params->audio_packet_type) {
+	case 0x02:
+		if (params->layout == 0)
+			inter->ap = 0.25;
+		else if (params->layout == 1)
+			inter->ap = 1.0;
+		break;
+	case 0x08:
+		inter->ap = 0.25;
+		break;
+	case 0x09:
+		inter->ap = 1.0;
+		break;
+	case 0x07:
+	case 0x0e:
+	case 0x0f:
+	case 0x0b:
+	case 0x0c:
+		/* Unsupported audio format */
+		return FRL_CAP_CHK_ERROR_UNSUPPORTED_AUDIO;
+	default:
+		inter->ap = 0.0;
+	}
+
+	inter->r_ap                   = (dml_max(audio_bw_reserve, params->f_audio * inter->ap) + 2 * ACR_RATE_MAX) * (1 + DML_TOLERANCE_AUDIO_CLOCK / 1000000.0);
+	inter->avg_audio_packets_line = inter->r_ap * inter->t_line;
+	inter->audio_packets_line     = (int)dml_ceil(inter->avg_audio_packets_line, 1);
+	inter->blank_audio_min        = 32 + 32 * inter->audio_packets_line; // h_blank_audio_min or hc_blank_audio_min
+
+	params->borrow_params.audio_packets_line = inter->audio_packets_line;
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		frl_dump_var("%le", inter->ap);
+		frl_dump_var("%le", inter->r_ap);
+		frl_dump_var("%le", inter->avg_audio_packets_line);
+		frl_dump_var("%i",  inter->audio_packets_line);
+		frl_dump_var("%i",  inter->blank_audio_min);
+	}
+#endif
+
+	return FRL_CAP_CHK_OK;
+}
+
+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 res;
+	int      k_420;
+	double   k_cd;
+	int      c_frl_free;
+	int      c_frl_rc_margin;
+	int      c_frl_rc_savings;
+	int      bpp;
+	double   bytes_line;
+	int      tb_active;
+	int      tb_blank;
+	double   f_tb_average;
+	double   t_active_ref;
+	double   t_blank_ref;
+	double   t_active_min;
+	double   t_blank_min;
+	double   t_borrowed;
+	double   tb_borrowed;
+	int      c_frl_actual_payload;
+	double   utilization;
+	double   margin;
+
+	dc_assert_fp_enabled();
+
+	res = dml1_frl_cap_chk_common(inter, params);
+	if (res != FRL_CAP_CHK_OK)
+		return res;
+
+	k_420            = params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_420 ? 2 : 1;
+	k_cd             = params->pixel_encoding == HDMI_FRL_PIXEL_ENCODING_422 ? 1.0 : params->bpc / 8.0;
+	c_frl_free       = (int)dml_max(params->h_blank * k_cd / k_420 - 32 * (1 + inter->audio_packets_line) - 7, 0);
+	c_frl_rc_margin  = 4;
+	c_frl_rc_savings = (int)dml_floor(dml_max(((7.0 / 8.0) * c_frl_free) - c_frl_rc_margin, 0.0), 1);
+	bpp              = (int)(24 * k_cd / k_420);
+	bytes_line       = bpp * params->h_active / 8.0;
+	tb_active        = (int)dml_ceil(bytes_line / 3, 1);
+	tb_blank         = (int)dml_ceil(params->h_blank * k_cd / k_420, 1);
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		frl_dump_var("%i", k_420);
+		frl_dump_var("%le", k_cd);
+		frl_dump_var("%i", c_frl_free);
+		frl_dump_var("%i", c_frl_rc_margin);
+		frl_dump_var("%i", c_frl_rc_savings);
+		frl_dump_var("%i", bpp);
+		frl_dump_var("%le", bytes_line);
+		frl_dump_var("%i", tb_active);
+		frl_dump_var("%i", tb_blank);
+	}
+#endif
+
+	if (!(inter->blank_audio_min <= tb_blank)) {
+		frl_dump_var("%i", inter->blank_audio_min);
+		frl_dump_var("%i", tb_blank);
+		return FRL_CAP_CHK_ERROR_AUDIO_BW;
+	}
+
+	f_tb_average = (inter->f_pixel_clock_max / (params->h_active + params->h_blank)) * (tb_active + tb_blank);
+	t_active_ref = inter->t_line * ((double)params->h_active / (params->h_active + params->h_blank));
+	t_blank_ref  = inter->t_line * ((double)params->h_blank / (params->h_active + params->h_blank));
+	t_active_min = (3.0 / 2.0) * tb_active / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max));
+	t_blank_min  = tb_blank / (params->lanes * inter->r_frl_char_min * (1.0 - inter->overhead_max));
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		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_min);
+		frl_dump_var("%le", t_blank_min);
+	}
+#endif
+
+	if (t_active_ref >= t_active_min && t_blank_ref >= t_blank_min) {
+		t_borrowed = 0;
+		params->borrow_params.borrow_mode = FRL_BORROW_MODE_NONE;
+	} else if ((t_active_ref < t_active_min) && (t_blank_ref >= t_blank_min)) {
+		t_borrowed = t_active_min - t_active_ref;
+		params->borrow_params.borrow_mode = FRL_BORROW_MODE_FROM_BLANK;
+	} else {
+		return FRL_CAP_CHK_ERROR_BORROW;
+	}
+
+	tb_borrowed = dml_ceil(t_borrowed * f_tb_average, 1);
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		frl_dump_var("%le", tb_borrowed);
+		frl_dump_var("%i", params->borrow_params.borrow_mode);
+	}
+#endif
+
+	if (!(tb_borrowed <= TB_BORROWED_MAX))
+		return FRL_CAP_CHK_ERROR_MAX_BORROW;
+
+	c_frl_actual_payload = (int)(dml_ceil((3.0 / 2.0) * tb_active, 1) + tb_blank - c_frl_rc_savings);
+	utilization          = c_frl_actual_payload / inter->c_frl_line;
+	margin               = 1.0 - (utilization + inter->overhead_max);
+
+#ifdef DEBUG_FRL_CAP_CHK
+	{
+		frl_dump_var("%i",  c_frl_actual_payload);
+		frl_dump_var("%le", utilization);
+		frl_dump_var("%le", margin);
+	}
+#endif
+
+	if (margin < 0 && dcn_bw_fabs(margin) > 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;
+
+	return dml1_frl_cap_chk_inter(params, &inter);
+}
+
+enum frl_cap_chk_result dml1_frl_cap_chk_inter(struct frl_cap_chk_params *params,
+					       struct frl_cap_chk_intermediates *inter)
+{
+	return dml1_frl_cap_chk_uncompressed(params, inter);
+}
+
+static double calculate_compressed_active_time(uint32_t h_active,
+	const uint32_t h_blank,
+	const int hc_active,
+	const int hc_blank,
+	const uint32_t frl_num_lanes,
+	const double pix_clk,
+	const int frl_link_rate)
+{
+	double f_tb_average;
+	double r_bit_nominal;
+	double r_bit_min;
+	double r_frl_char_min;
+	double t_active_est_1;
+	double t_active_est_2;
+	double t_active_target;
+	int c_frl_sb = 510;
+	int frl_bit_tolerance = 300;
+	double overhead_m = 0.003;
+	double overhead_sb;
+	double overhead_rs;
+	double overhead_map;
+	double overhead_min;
+	double overhead_max;
+
+	switch (frl_link_rate) {
+	case FRL_LINK_RATE_3GBPS:
+		r_bit_nominal = 3.0e9;
+		break;
+	case FRL_LINK_RATE_6GBPS:
+	case FRL_LINK_RATE_6GBPS_4LANE:
+		r_bit_nominal = 6.0e9;
+		break;
+	case FRL_LINK_RATE_8GBPS:
+		r_bit_nominal = 8.0e9;
+		break;
+	case FRL_LINK_RATE_10GBPS:
+	default:
+		r_bit_nominal = 10.0e9;
+		break;
+	case FRL_LINK_RATE_12GBPS:
+		r_bit_nominal = 12.0e9;
+		break;
+	}
+
+	f_tb_average = pix_clk / (h_active + h_blank)
+					* (hc_active + hc_blank);
+
+	c_frl_sb = 4 * c_frl_sb + frl_num_lanes;
+	overhead_sb = (double)frl_num_lanes / c_frl_sb;
+	overhead_rs = 8.0 * 4.0 / c_frl_sb;
+	overhead_map = 2.5 / c_frl_sb;
+	overhead_min = overhead_sb + overhead_rs + overhead_map;
+	overhead_max = overhead_min + overhead_m;
+
+	r_bit_min = r_bit_nominal * (1.0 - frl_bit_tolerance / 1000000.0);
+	r_frl_char_min = r_bit_min / 18.0;
+	t_active_est_1 = hc_active / f_tb_average;
+	t_active_est_2 = (3.0 / 2.0 * hc_active) /
+				  (frl_num_lanes * r_frl_char_min * (1.0 - overhead_max));
+
+	if (t_active_est_1 > t_active_est_2) {
+		t_active_target = t_active_est_1;
+	} else {
+		t_active_target = t_active_est_2;
+	}
+
+	return t_active_target;
+}
+
+void frl_modified_pix_clock_for_dsc_padding(const int hc_active_target,
+	const int hc_blank_target,
+	const uint8_t frl_num_lanes,
+	const uint32_t pix_clk_100hz,
+	const int frl_link_rate,
+	const uint32_t h_addressable,
+	const uint32_t h_border_left,
+	const uint32_t h_border_right,
+	const uint32_t h_total,
+	const uint32_t h_addressable_otg,
+	uint32_t *pix_clk_100hz_otg,
+	uint32_t *h_total_otg)
+{
+	double pix_clk;
+	int h_active;
+	int h_blank;
+	double t_active_target;
+	double hw_pix_clk;
+	double h_total_otg_temp;
+
+	pix_clk = (double)pix_clk_100hz * 100;
+
+	h_active = h_addressable + h_border_left + h_border_right;
+	h_blank = h_total - h_active;
+
+	t_active_target = calculate_compressed_active_time(h_active, h_blank, hc_active_target, hc_blank_target, frl_num_lanes, pix_clk, frl_link_rate);
+
+	h_total_otg_temp = ((double)h_addressable_otg * (double)h_total) / ((double)pix_clk_100hz * 100.0 * t_active_target);
+	/* Htotal must be a multiple of 4, also take the ceiling */
+	*h_total_otg = (uint32_t)dml_ceil(h_total_otg_temp, 4.0);
+
+	hw_pix_clk = (double)(pix_clk_100hz * 100.0 * (double)*h_total_otg) / (double)h_total;
+	*pix_clk_100hz_otg = (uint32_t)(hw_pix_clk / 100.0);
+}
+
+int frl_modify_borrow_mode_for_dsc_padding(const uint32_t pix_clk_100hz,
+	const uint32_t h_active,
+	const uint32_t h_active_padded,
+	const uint32_t h_blank,
+	const uint32_t h_blank_padded,
+	const int hc_active,
+	const int hc_blank,
+	const uint8_t frl_num_lanes,
+	const int frl_link_rate)
+{
+	double f_pixel_clock_max;
+	double t_line;
+	double t_active;
+	double t_blank;
+	double t_active_target;
+	double t_blank_target;
+	double pix_clk_tolerance = 0.005;
+
+	enum frl_borrow_mode borrow_mode;
+
+	f_pixel_clock_max = (double)pix_clk_100hz * (1.0 + pix_clk_tolerance);
+	t_line = (double)(h_active + h_blank) / f_pixel_clock_max;
+
+	t_active_target = calculate_compressed_active_time(h_active, h_blank, hc_active, hc_blank, frl_num_lanes, f_pixel_clock_max, frl_link_rate);
+
+	t_active = t_line * ((double)h_active_padded / (h_active_padded + h_blank_padded));
+	t_blank = t_line - t_active;
+
+	t_blank_target = t_line - t_active_target;
+
+	if (t_blank_target - t_blank > DBL_EPSILON) {
+		borrow_mode = FRL_BORROW_MODE_FROM_ACTIVE;
+	} else if (t_active_target - t_active > DBL_EPSILON) {
+		borrow_mode = FRL_BORROW_MODE_FROM_BLANK;
+	} else {
+		borrow_mode = FRL_BORROW_MODE_NONE;
+	}
+
+	return borrow_mode;
+}
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 debe4c1dc0f7..545f498ea396 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,13 +119,6 @@ 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 */
 
@@ -145,8 +138,6 @@ 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,
-- 
2.54.0


  parent reply	other threads:[~2026-05-12 15:53 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-12 15:52 [PATCH v5 00/13] HDMI FRL and DSC Support for amdgpu Harry Wentland
2026-05-12 15:52 ` [PATCH v5 01/13] drm/amd/display: Add HDMI FRL definitions to includes Harry Wentland
2026-05-12 15:52 ` Harry Wentland [this message]
2026-05-12 15:52 ` [PATCH v5 03/13] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0 Harry Wentland
2026-05-12 15:52 ` [PATCH v5 04/13] drm/amd/display: Add DCCG DIO, HPO, OPP, and OPTC support for FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 05/13] drm/amd/display: Add FRL support to clk_mgr, dsc, hdcp Harry Wentland
2026-05-12 15:52 ` [PATCH v5 06/13] drm/amd/display: Tie FRL programming together in HWSS Harry Wentland
2026-05-12 15:52 ` [PATCH v5 07/13] drm/amd/display: Add DC resource support for FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 08/13] drm/amd/display Add DC link " Harry Wentland
2026-05-12 15:52 ` [PATCH v5 09/13] drm/amd/display: Add support for FRL to DC core Harry Wentland
2026-05-12 15:52 ` [PATCH v5 10/13] drm/amd/display: Update HDCP and info_packet modules for FRL Harry Wentland
2026-05-12 15:52 ` [PATCH v5 11/13] drm/amd/display: Tie FRL support into amdgpu_dm Harry Wentland
2026-05-13  0:11   ` [PATCH] drm/amd/display: fix FRL link-status polling never running dyllan
2026-05-12 15:52 ` [PATCH v5 12/13] drm/amd/display: add HDMI 2.1 Compliance Support Harry Wentland
2026-05-12 15:52 ` [PATCH v5 13/13] drm/amd/display: add HDMI 2.1 DSC over FRL support Harry Wentland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260512155244.403854-3-harry.wentland@amd.com \
    --to=harry.wentland@amd.com \
    --cc=Jerry.Zuo@amd.com \
    --cc=amd-gfx@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.