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 03/13] drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0
Date: Tue, 12 May 2026 11:52:34 -0400	[thread overview]
Message-ID: <20260512155244.403854-4-harry.wentland@amd.com> (raw)
In-Reply-To: <20260512155244.403854-1-harry.wentland@amd.com>

Add HDMI FRL bits to DML 2.0

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
 .../gpu/drm/amd/display/dc/dml2_0/Makefile    |   2 +
 .../amd/display/dc/dml2_0/display_mode_core.c | 104 ++++-
 .../amd/display/dc/dml2_0/display_mode_util.c |   3 +
 .../dml2_0/dml21/dml21_translation_helper.c   |   4 +
 .../dml21/src/dml2_core/dml2_core_dcn4.c      |   1 +
 .../src/dml2_core/dml2_core_dcn4_calcs.c      |  29 +-
 .../src/dml2_core/dml2_core_shared_types.h    |   3 +
 .../lib_frl_cap_check.c                       | 396 +++++++++++++++++
 .../lib_frl_cap_check.h                       |  90 ++++
 .../dc/dml2_0/dml2_translation_helper.c       |   4 +
 .../drm/amd/display/dc/dml2_0/dml2_utils.c    |   2 +
 .../amd/display/dc/dml2_0/dml_frl_cap_chk.c   | 413 ++++++++++++++++++
 .../amd/display/dc/dml2_0/dml_frl_cap_chk.h   | 109 +++++
 13 files changed, 1153 insertions(+), 7 deletions(-)
 create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c
 create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h
 create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c
 create mode 100644 drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h

diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
index 8a451c36fdb3..44e00c2b7ac7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
@@ -80,6 +80,7 @@ CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_wrapper.o := $(dml2_ccflags)
 DML2 = display_mode_core.o display_mode_util.o dml2_wrapper_fpu.o dml2_wrapper.o \
 		dml2_utils.o dml2_policy.o dml2_translation_helper.o dml2_dc_resource_mgmt.o dml2_mall_phantom.o \
 		dml_display_rq_dlg_calc.o
+DML2 += dml_frl_cap_chk.o
 
 AMD_DAL_DML2 = $(addprefix $(AMDDALPATH)/dc/dml2_0/,$(DML2))
 
@@ -102,6 +103,7 @@ DML21 += src/dml2_pmo/dml2_pmo_factory.o
 DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o
 DML21 += src/dml2_pmo/dml2_pmo_dcn42.o
 DML21 += src/dml2_standalone_libraries/lib_float_math.o
+DML21 += src/dml2_standalone_libraries/lib_frl_cap_check.o
 DML21 += dml21_translation_helper.o
 DML21 += dml21_wrapper.o
 DML21 += dml21_wrapper_fpu.o
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c
index 241406e9e85a..fb4ea2ee28b7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c
@@ -27,6 +27,8 @@
 #include "display_mode_core.h"
 #include "display_mode_util.h"
 #include "display_mode_lib_defines.h"
+#include "dml_frl_cap_chk.h"
+#include "lib_frl_cap_check.h"
 
 #include "dml_assert.h"
 
@@ -864,7 +866,7 @@ static dml_uint_t dscceComputeDelay(
 	// #all other modes operate at 1 pixel per clock
 	else if (pixelFormat == dml_444)
 		pixelsPerClock = 1;
-	else if (pixelFormat == dml_n422)
+	else if (pixelFormat == dml_n422 || Output == dml_hdmifrl)
 		pixelsPerClock = 2;
 	else
 		pixelsPerClock = 1;
@@ -884,7 +886,7 @@ static dml_uint_t dscceComputeDelay(
 	w = sliceWidth / pixelsPerClock;
 
 	//422 mode has an additional cycle of delay
-	if (pixelFormat == dml_420 || pixelFormat == dml_444 || pixelFormat == dml_n422)
+	if (pixelFormat == dml_420 || pixelFormat == dml_444 || pixelFormat == dml_n422 || Output == dml_hdmifrl)
 		s = 0;
 	else
 		s = 1;
@@ -947,7 +949,7 @@ static dml_uint_t dscComputeDelay(enum dml_output_format_class pixelFormat, enum
 		Delay = Delay + 1;
 		// sft
 		Delay = Delay + 1;
-	} else if (pixelFormat == dml_n422) {
+	} else if (pixelFormat == dml_n422 || (Output == dml_hdmifrl && pixelFormat != dml_444)) {
 	// sfr
 	Delay = Delay + 2;
 	// dsccif
@@ -2741,20 +2743,44 @@ static dml_float_t TruncToValidBPP(
 	dml_uint_t NonDSCBPP1;
 	dml_uint_t NonDSCBPP2;
 
+	frl_cap_chk_result hdmifrlresult = FRL_CAP_CHK_OK;
+	frl_cap_chk_params hdmifrlparams = { 0 };
+	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 = (dml_uint_t)(DesiredBPP / 3);
+	hdmifrlparams.compressed = DSCEnable;
+	hdmifrlparams.slices = DSCSlices;
+	hdmifrlparams.slice_width = (dml_uint_t)(dml_ceil((dml_float_t) HActive / DSCSlices, 1.0));
+	hdmifrlparams.bpp_target = DesiredBPP;
+
 	if (Format == dml_420) {
 		NonDSCBPP0 = 12;
 		NonDSCBPP1 = 15;
 		NonDSCBPP2 = 18;
 		MinDSCBPP = 6;
 		MaxDSCBPP = 1.5 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = PIXEL_ENCODING_420;
+		hdmifrlparams.bpc = (dml_uint_t) (DesiredBPP / 1.5);
 	} else if (Format == dml_444) {
 		NonDSCBPP0 = 24;
 		NonDSCBPP1 = 30;
 		NonDSCBPP2 = 36;
 		MinDSCBPP = 8;
 		MaxDSCBPP = 3 * DSCInputBitPerComponent - 1.0 / 16;
+		hdmifrlparams.pixel_encoding = PIXEL_ENCODING_444;
+		hdmifrlparams.bpc = (dml_uint_t) (DesiredBPP / 3.0);
 	} else {
-		if (Output == dml_hdmi) {
+		hdmifrlparams.pixel_encoding = PIXEL_ENCODING_422;
+		hdmifrlparams.bpc = (dml_uint_t) (DesiredBPP / 2.0);
+
+		if (Output == dml_hdmi || Output == dml_hdmifrl) {
 			NonDSCBPP0 = 24;
 			NonDSCBPP1 = 24;
 			NonDSCBPP2 = 24;
@@ -2763,7 +2789,7 @@ static dml_float_t TruncToValidBPP(
 			NonDSCBPP1 = 20;
 			NonDSCBPP2 = 24;
 	}
-	if (Format == dml_n422) {
+	if (Format == dml_n422 || Output == dml_hdmifrl) {
 		MinDSCBPP = 7;
 			MaxDSCBPP = 2 * DSCInputBitPerComponent - 1.0 / 16.0;
 		} else {
@@ -2772,7 +2798,11 @@ static dml_float_t TruncToValidBPP(
 		}
 	}
 
-	if (Output == dml_dp2p0) {
+	if (Output == dml_hdmifrl) {
+		hdmifrlresult = frl_cap_chk_inter(&hdmifrlparams, &hdmifrlinter);
+		MaxLinkBPP = (1 - hdmifrlinter.overhead_max) * dml_min(hdmifrlinter.r_frl_char_min * 16.0 * (dml_float_t) Lanes / hdmifrlinter.f_pixel_clock_max + 24.0 * (dml_float_t) TB_BORROWED_MAX / (dml_float_t) HActive,
+														(hdmifrlinter.r_frl_char_min * 16.0 * (dml_float_t)Lanes / hdmifrlinter.f_pixel_clock_max * (dml_float_t) HTotal - 16.0 * (dml_float_t) hdmifrlinter.blank_audio_min) / (dml_float_t) HActive);
+	} else if (Output == dml_dp2p0) {
 		MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128.0 / 132.0 * 383.0 / 384.0 * 65536.0 / 65540.0;
 	} else if (DSCEnable && Output == dml_dp) {
 		MaxLinkBPP = LinkBitRate / 10.0 * 8.0 * Lanes / PixelClock * (1 - 2.4 / 100);
@@ -2824,6 +2854,8 @@ static dml_float_t TruncToValidBPP(
 		if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP == NonDSCBPP0)) ||
 				(DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) {
 			return __DML_DPP_INVALID__;
+		} else if ((Output == dml_hdmifrl && hdmifrlresult != FRL_CAP_CHK_OK) || (Output != dml_hdmifrl && MaxLinkBPP < DesiredBPP)) {
+			return __DML_DPP_INVALID__;
 		} else {
 			return DesiredBPP;
 		}
@@ -5516,6 +5548,66 @@ static void CalculateOutputLink(
 					*OutputRate = dml_output_rate_dp_rate_hbr3;
 				}
 			}
+		} else if (Output == dml_hdmifrl) {
+			if (DSCEnable == dml_dsc_enable) {
+				*RequiresDSC = true;
+				LinkDSCEnable = true;
+				*RequiresFEC = true;
+			} else {
+				*RequiresDSC = false;
+				LinkDSCEnable = false;
+				*RequiresFEC = false;
+			}
+			*OutBpp = 0;
+			if (PHYCLKD18PerState >= 3000 / 18) {
+				*OutBpp = TruncToValidBPP(3000, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "3x3";
+				*OutputType = dml_output_type_hdmifrl;
+				*OutputRate = dml_output_rate_hdmi_rate_3x3;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) {
+				*OutBpp = TruncToValidBPP(6000, 3, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "6x3";
+				*OutputType = dml_output_type_hdmifrl;
+				*OutputRate = dml_output_rate_hdmi_rate_6x3;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 6000 / 18) {
+				*OutBpp = TruncToValidBPP(6000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "6x4";
+				*OutputType = dml_output_type_hdmifrl;
+				*OutputRate = dml_output_rate_hdmi_rate_6x4;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 8000 / 18) {
+				*OutBpp = TruncToValidBPP(8000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				//OutputTypeAndRate = Output & "8x4";
+				*OutputType = dml_output_type_hdmifrl;
+				*OutputRate = dml_output_rate_hdmi_rate_8x4;
+			}
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 10000 / 18) {
+				*OutBpp = TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				if (*OutBpp == 0 && DSCEnable == dml_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0 && PHYCLKD18PerState < 12000 / 18) {
+					*RequiresDSC = true;
+					LinkDSCEnable = true;
+					*RequiresFEC = true;
+					*OutBpp = TruncToValidBPP(10000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				}
+				//OutputTypeAndRate = Output & "10x4";
+				*OutputType = dml_output_type_hdmifrl;
+				*OutputRate = dml_output_rate_hdmi_rate_10x4;
+			}
+
+			if (*OutBpp == 0 && PHYCLKD18PerState >= 12000 / 18) {
+				*OutBpp = TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				if (*OutBpp == 0 && DSCEnable == dml_dsc_enable_if_necessary && ForcedOutputLinkBPP == 0) {
+					*RequiresDSC = true;
+					LinkDSCEnable = true;
+					*RequiresFEC = true;
+					*OutBpp = TruncToValidBPP(12000, 4, HTotal, HActive, PixelClockBackEnd, ForcedOutputLinkBPP, LinkDSCEnable, Output, OutputFormat, DSCInputBitPerComponent, NumberOfDSCSlices, (dml_uint_t)AudioSampleRate, AudioSampleLayout, ODMModeNoDSC, ODMModeDSC, &dummy);
+				}
+				//OutputTypeAndRate = Output & "12x4";
+				*OutputType = dml_output_type_hdmifrl;
+				*OutputRate = dml_output_rate_hdmi_rate_12x4;
+			}
 		}
 	}
 }
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c
index 3939a0d8b835..a8519f547dce 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c
@@ -393,6 +393,7 @@ void dml_print_mode_support(struct display_mode_lib_st *mode_lib, dml_uint_t j)
 	dml_print("DML: MODE SUPPORT:     DISPCLK DPPCLK Support                     : %s\n", mode_lib->ms.support.DISPCLK_DPPCLK_Support[j] == true ? "Supported" : "NOT Supported");
 	dml_print("DML: MODE SUPPORT:     Total Available Pipes Support              : %s\n", mode_lib->ms.support.TotalAvailablePipesSupport[j] == true ? "Supported" : "NOT Supported");
 	dml_print("DML: MODE SUPPORT:     Number Of OTG Support                      : %s\n", mode_lib->ms.support.NumberOfOTGSupport == true ? "Supported" : "NOT Supported");
+	dml_print("DML: MODE SUPPORT:     Number Of HDMI FRL Support                 : %s\n", mode_lib->ms.support.NumberOfHDMIFRLSupport == true ? "Supported" : "NOT Supported");
 	dml_print("DML: MODE SUPPORT:     Number Of DP2p0 Support                    : %s\n", mode_lib->ms.support.NumberOfDP2p0Support == true ? "Supported" : "NOT Supported");
 	dml_print("DML: MODE SUPPORT:     Writeback Latency Support                  : %s\n", mode_lib->ms.support.WritebackLatencySupport == true ? "Supported" : "NOT Supported");
 	dml_print("DML: MODE SUPPORT:     Writeback Scale Ratio And Taps Support     : %s\n", mode_lib->ms.support.WritebackScaleRatioAndTapsSupport == true ? "Supported" : "NOT Supported");
@@ -451,6 +452,8 @@ void dml_print_dml_mode_support_info(const struct dml_mode_support_info_st *supp
 		dml_print("DML: support: NotEnoughLanesForMSO = 0x%x\n", support->NotEnoughLanesForMSO);
 	if (!fail_only || support->NumberOfOTGSupport == 0)
 		dml_print("DML: support: NumberOfOTGSupport = 0x%x\n", support->NumberOfOTGSupport);
+	if (!fail_only || support->NumberOfHDMIFRLSupport == 0)
+		dml_print("DML: support: NumberOfHDMIFRLSupport = 0x%x\n", support->NumberOfHDMIFRLSupport);
 	if (!fail_only || support->NumberOfDP2p0Support == 0)
 		dml_print("DML: support: NumberOfDP2p0Support = 0x%x\n", support->NumberOfDP2p0Support);
 	if (!fail_only || support->NonsupportedDSCInputBPC == 1)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c
index 25557c99a28e..c86b45bbeb2b 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c
@@ -213,6 +213,9 @@ static void populate_dml21_output_config_from_stream_state(struct dml2_link_outp
 	case SIGNAL_TYPE_DVI_DUAL_LINK:
 		output->output_encoder = dml2_hdmi;
 		break;
+	case SIGNAL_TYPE_HDMI_FRL:
+		output->output_encoder = dml2_hdmifrl;
+		break;
 	default:
 			output->output_encoder = dml2_dp;
 	}
@@ -247,6 +250,7 @@ static void populate_dml21_output_config_from_stream_state(struct dml2_link_outp
 	case SIGNAL_TYPE_DISPLAY_PORT_MST:
 	case SIGNAL_TYPE_EDP:
 	case SIGNAL_TYPE_VIRTUAL:
+	case SIGNAL_TYPE_HDMI_FRL:
 	default:
 		output->output_dp_link_rate = dml2_dp_rate_na;
 		break;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c
index 858e7bbc511f..c983869e0fa3 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c
@@ -66,6 +66,7 @@ struct dml2_core_ip_params core_dcn4_ip_caps_base = {
 	.cursor_64bpp_support = true,
 	.dynamic_metadata_vm_enabled = false,
 
+	.max_num_hdmi_frl_outputs = 1,
 	.max_num_dp2p0_outputs = 4,
 	.max_num_dp2p0_streams = 4,
 	.imall_supported = 1,
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index 827bd9143c87..f338e733318e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -7,6 +7,7 @@
 #include "dml2_core_dcn4_calcs.h"
 #include "dml2_debug.h"
 #include "lib_float_math.h"
+#include "lib_frl_cap_check.h"
 #include "dml_top_types.h"
 
 #define DML2_MAX_FMT_420_BUFFER_WIDTH 4096
@@ -1294,19 +1295,39 @@ static double TruncToValidBPP(
 	unsigned int NonDSCBPP2;
 	enum dml2_odm_mode ODMMode;
 
+	enum lib_frl_cap_check_status hdmifrlresult = LIB_FRL_CAP_CHECK_OK;
+
+	l->hdmifrlparams.lanes = (int)Lanes;
+	l->hdmifrlparams.f_pixel_clock_nominal = PixelClock * 1000000;
+	l->hdmifrlparams.r_bit_nominal = LinkBitRate * 1000000;
+	l->hdmifrlparams.layout = (int)AudioLayout;
+	l->hdmifrlparams.f_audio = AudioRate * 1000;
+	l->hdmifrlparams.h_active = (int)HActive;
+	l->hdmifrlparams.h_blank = (int)(HTotal - HActive);
+	l->hdmifrlparams.bpc = (int)(DesiredBPP / 3);
+	l->hdmifrlparams.compressed = DSCEnable;
+	l->hdmifrlparams.slices = (int)DSCSlices;
+	l->hdmifrlparams.slice_width = (int)(math_ceil2((double)HActive / DSCSlices, 1.0));
+	l->hdmifrlparams.bpp_target = DesiredBPP;
 	if (Format == dml2_420) {
 		NonDSCBPP0 = 12;
 		NonDSCBPP1 = 15;
 		NonDSCBPP2 = 18;
 		MinDSCBPP = 6;
 		MaxDSCBPP = 16;
+		l->hdmifrlparams.pixel_encoding = LIB_FRL_CAP_CHECK_PIXEL_ENCODING_420;
+		l->hdmifrlparams.bpc = (int)(DesiredBPP / 1.5);
 	} else if (Format == dml2_444) {
 		NonDSCBPP0 = 24;
 		NonDSCBPP1 = 30;
 		NonDSCBPP2 = 36;
 		MinDSCBPP = 8;
 		MaxDSCBPP = 16;
+		l->hdmifrlparams.pixel_encoding = LIB_FRL_CAP_CHECK_PIXEL_ENCODING_444;
+		l->hdmifrlparams.bpc = (int)(DesiredBPP / 3.0);
 	} else {
+		l->hdmifrlparams.pixel_encoding = LIB_FRL_CAP_CHECK_PIXEL_ENCODING_422;
+		l->hdmifrlparams.bpc = (int)(DesiredBPP / 2.0);
 
 		if (Output == dml2_hdmi || Output == dml2_hdmifrl) {
 			NonDSCBPP0 = 24;
@@ -1326,7 +1347,11 @@ static double TruncToValidBPP(
 		}
 	}
 
-	if (Output == dml2_dp2p0) {
+	if (Output == dml2_hdmifrl) {
+		hdmifrlresult = frl_cap_check_intermediates(&l->hdmifrlparams, &l->hdmifrlinter);
+		MaxLinkBPP = (1 - l->hdmifrlinter.overhead_max) * math_min2(l->hdmifrlinter.r_frl_char_min * 16.0 * (double)Lanes / l->hdmifrlinter.f_pixel_clock_max + 24.0 * (double)DML2_FRL_CHK_TB_BORROWED_MAX / (double)HActive,
+			(l->hdmifrlinter.r_frl_char_min * 16.0 * (double)Lanes / l->hdmifrlinter.f_pixel_clock_max * (double)HTotal - 16.0 * (double)l->hdmifrlinter.blank_audio_min) / (double)HActive);
+	} else if (Output == dml2_dp2p0) {
 		MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128.0 / 132.0 * 383.0 / 384.0 * 65536.0 / 65540.0;
 	} else if (DSCEnable && Output == dml2_dp) {
 		MaxLinkBPP = LinkBitRate / 10.0 * 8.0 * Lanes / PixelClock * (1 - 2.4 / 100);
@@ -1364,6 +1389,8 @@ static double TruncToValidBPP(
 		if (!((DSCEnable == false && (DesiredBPP == NonDSCBPP2 || DesiredBPP == NonDSCBPP1 || DesiredBPP == NonDSCBPP0)) ||
 			(DSCEnable && DesiredBPP >= MinDSCBPP && DesiredBPP <= MaxDSCBPP))) {
 			return __DML2_CALCS_DPP_INVALID__;
+		} else if ((Output == dml2_hdmifrl && hdmifrlresult != LIB_FRL_CAP_CHECK_OK) || (Output != dml2_hdmifrl && MaxLinkBPP < DesiredBPP)) {
+			return __DML2_CALCS_DPP_INVALID__;
 		} else {
 			return DesiredBPP;
 		}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
index 080bc3c3d244..11e295253f72 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
@@ -8,6 +8,7 @@
 #include "dml2_external_lib_deps.h"
 #include "dml_top_display_cfg_types.h"
 #include "dml_top_types.h"
+#include "lib_frl_cap_check.h"
 
 #define __DML_VBA_DEBUG__
 #define __DML2_CALCS_MAX_VRATIO_PRE_OTO__ 4.0 //<brief max vratio for one-to-one prefetch bw scheduling
@@ -1522,6 +1523,8 @@ struct dml2_core_shared_CalculateSwathAndDETConfiguration_locals {
 };
 
 struct dml2_core_shared_TruncToValidBPP_locals {
+	struct lib_frl_cap_check_params hdmifrlparams;
+	struct lib_frl_cap_check_intermediates hdmifrlinter;
 };
 
 struct dml2_core_shared_CalculateDETBufferSize_locals {
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c
new file mode 100644
index 000000000000..d62cdf8566cc
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: MIT
+//
+// Copyright 2024 Advanced Micro Devices, Inc.
+
+#include "lib_float_math.h"
+#include "lib_frl_cap_check.h"
+
+#define frl_dump_var(fmt, var) {}
+#define frl_print(fmt, ...) {}
+
+static const double   EPSILON = 0.01;
+static const double   DBL_EPSILON = 2.2204460492503131e-16;
+static const int      C_FRL_CB = 510;
+static const double   OVERHEAD_M = 0.003;  /* %   */
+static const double   TOLERANCE_PIXEL_CLOCK = 0.005;  /* %   */
+static const double   TOLERANCE_AUDIO_CLOCK = 1000;   /* ppm */
+static const int      TOLERANCE_FRL_BIT = 300;    /* ppm */
+static const int      ACR_RATE_MAX = 1500;
+const int             DML2_FRL_CHK_TB_BORROWED_MAX = 400;
+
+static enum lib_frl_cap_check_status frl_cap_check_common(struct lib_frl_cap_check_intermediates *inter, struct lib_frl_cap_check_params *params)
+{
+	double   audio_bw_reserve = (params->compressed ? 192000.0 : 0.0);
+	/*
+		if (getenv("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");
+		}
+	*/
+	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 = math_floor(inter->t_line * inter->r_frl_char_min * params->lanes);
+	/*
+		if (getenv("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);
+		}
+	*/
+	switch (params->audio_packet_type) {
+	case 0x02:
+		/* unsupported
+	case 0x07:
+		*/
+		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:
+		/* unsupported
+	case 0x0e:
+	case 0x0f:
+		*/
+		inter->ap = 1.0;
+		break;
+		/* unsupported
+	case 0x0b:
+	case 0x0c:
+		if (acat == 0x01)
+			ap = 2.0;
+		else if (acat == 0x02)
+			ap = 3.0;
+		else if (acat == 0x03)
+			ap = 4.0;
+		break;
+		*/
+	case 0x07:
+	case 0x0e:
+	case 0x0f:
+	case 0x0b:
+	case 0x0c:
+		// Unsupported audio format
+		return LIB_FRL_CAP_CHECK_ERROR_UNSUPPORTED_AUDIO;
+	default:
+		inter->ap = 0.0;
+	}
+
+	inter->r_ap = (math_max2(audio_bw_reserve, params->f_audio * inter->ap) + 2 * ACR_RATE_MAX) * (1 + TOLERANCE_AUDIO_CLOCK / 1000000.0);
+	inter->avg_audio_packets_line = inter->r_ap * inter->t_line;
+	inter->audio_packets_line = (int)math_ceil(inter->avg_audio_packets_line);
+	inter->blank_audio_min = 32 + 32 * inter->audio_packets_line; // h_blank_audio_min or hc_blank_audio_min
+
+	params->audio_packets_line = inter->audio_packets_line;
+	/*
+		if (getenv("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);
+		}
+	*/
+	return LIB_FRL_CAP_CHECK_OK;
+}
+
+
+static enum lib_frl_cap_check_status frl_cap_check_uncompressed(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter)
+{
+	enum lib_frl_cap_check_status 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;
+
+	res = frl_cap_check_common(inter, params);
+	if (res != LIB_FRL_CAP_CHECK_OK) {
+		return res;
+	}
+
+	k_420 = params->pixel_encoding == LIB_FRL_CAP_CHECK_PIXEL_ENCODING_420 ? 2 : 1;
+	k_cd = params->pixel_encoding == LIB_FRL_CAP_CHECK_PIXEL_ENCODING_422 ? 1.0 : params->bpc / 8.0;
+	c_frl_free = (int)math_max2(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)math_floor(math_max2(((7.0 / 8.0) * c_frl_free) - c_frl_rc_margin, 0.0));
+	bpp = (int)(24 * k_cd / k_420);
+	bytes_line = bpp * params->h_active / 8.0;
+	tb_active = (int)math_ceil(bytes_line / 3);
+	tb_blank = (int)math_ceil(params->h_blank * k_cd / k_420);
+	/*
+		if (getenv("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);
+		}
+	*/
+	if (!(inter->blank_audio_min <= tb_blank)) {
+		frl_dump_var("%i", inter->blank_audio_min);
+		frl_dump_var("%i", tb_blank);
+		return LIB_FRL_CAP_CHECK_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));
+	/*
+		if (getenv("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);
+		}
+	*/
+	if ((t_active_ref >= t_active_min) && (t_blank_ref >= t_blank_min)) {
+		t_borrowed = 0;
+		params->borrow_mode = LIB_FRL_CAP_CHECK_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_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_BLANK;
+	} else
+		return LIB_FRL_CAP_CHECK_ERROR_BORROW;
+
+	tb_borrowed = math_ceil(t_borrowed * f_tb_average);
+	/*
+		if (getenv("DEBUG_FRL_CAP_CHK"))
+		{
+			frl_dump_var("%le", tb_borrowed);
+			frl_dump_var("%i", params->borrow_mode);
+	}
+	*/
+	if (!(tb_borrowed <= DML2_FRL_CHK_TB_BORROWED_MAX))
+		return LIB_FRL_CAP_CHECK_ERROR_MAX_BORROW;
+
+	c_frl_actual_payload = (int)math_ceil((3.0 / 2.0) * tb_active) + tb_blank - c_frl_rc_savings;
+	utilization = c_frl_actual_payload / inter->c_frl_line;
+	margin = 1.0 - (utilization + inter->overhead_max);
+	/*
+		if (getenv("DEBUG_FRL_CAP_CHK"))
+		{
+			frl_dump_var("%i",  c_frl_actual_payload);
+			frl_dump_var("%le", utilization);
+			frl_dump_var("%le", margin);
+		}
+	*/
+	if (margin < 0 && math_fabs(margin) > EPSILON)
+		return LIB_FRL_CAP_CHECK_ERROR_MARGIN;
+
+	return LIB_FRL_CAP_CHECK_OK;
+}
+
+static enum lib_frl_cap_check_status frl_cap_check_compressed(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter)
+{
+	enum lib_frl_cap_check_status res;
+
+	int      c_frl_available;
+	int      c_frl_active_available;
+	int      c_frl_blank_available;
+	int      bytes_target;
+	int      hc_active_target;
+	int      hc_blank_target_est1;
+	int      hc_blank_target_est2;
+	int      hc_blank_target;
+	double   f_tb_average;
+	double   t_active_ref;
+	double   t_blank_ref;
+	double   t_active_target;
+	double   t_blank_target;
+	double   tb_borrowed;
+	int      c_frl_actual_target_payload;
+	double   utilization_targeted;
+	double   margin_target;
+#if defined(DEBUG_FRL_CAP_CHK)
+	double   tb_delta;
+	double   tb_delta_limit;
+	int      tb_worst;
+#endif
+
+	res = frl_cap_check_common(inter, params);
+	if (res != LIB_FRL_CAP_CHECK_OK)
+		return res;
+
+	c_frl_available = (int)math_floor((1 - inter->overhead_max) * inter->c_frl_line);
+	c_frl_active_available = (int)math_floor(c_frl_available * ((double)params->h_active / (params->h_active + params->h_blank)));
+	(void)c_frl_active_available;
+	c_frl_blank_available = (int)math_floor(c_frl_available * ((double)params->h_blank / (params->h_active + params->h_blank)));
+	(void)c_frl_blank_available;
+	bytes_target = params->slices * (int)math_ceil(params->bpp_target * params->slice_width / 8.0);
+
+	if (!params->bypass_hc_target_calc)
+		hc_active_target = (int)math_ceil(bytes_target / 3.0);
+	else
+		hc_active_target = params->hc_active_target;
+
+	hc_blank_target_est1 = (int)math_ceil(hc_active_target * ((double)params->h_blank / params->h_active));
+	hc_blank_target_est2 = (int)math_max2(hc_blank_target_est1, inter->blank_audio_min);
+
+	if (!params->bypass_hc_target_calc) {
+		hc_blank_target = 4 * (int)math_floor(math_min2(hc_blank_target_est2, c_frl_available - 3.0 / 2.0 * hc_active_target) / 4.0);
+
+		params->hc_active_target = hc_active_target;
+		params->hc_blank_target = hc_blank_target;
+	} else {
+		hc_blank_target = params->hc_blank_target;
+	}
+	/*
+		if (getenv("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);
+		}
+	*/
+	if (!(inter->blank_audio_min <= hc_blank_target)) {
+		frl_dump_var("%i", inter->blank_audio_min);
+		frl_dump_var("%i", hc_blank_target);
+		return LIB_FRL_CAP_CHECK_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 = math_max2((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;
+#if defined(DEBUG_FRL_CAP_CHK)
+	tb_delta = math_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) {
+#if defined(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_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_ACTIVE;
+	} else if (t_active_target - t_active_ref > DBL_EPSILON) {
+#if defined(DEBUG_FRL_CAP_CHK)
+		tb_delta_limit = tb_delta;
+#endif
+		params->borrow_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_BLANK;
+	} else {
+#if defined(DEBUG_FRL_CAP_CHK)
+		tb_delta_limit = 0;
+#endif
+		params->borrow_mode = LIB_FRL_CAP_CHECK_BORROW_MODE_NONE;
+	}
+
+#if defined(DEBUG_FRL_CAP_CHK)
+	tb_worst = (int)math_ceil(math_max2(tb_borrowed, tb_delta_limit));
+
+		{
+			frl_dump_var("%le", tb_delta_limit);
+			frl_dump_var("%le", tb_borrowed);
+			frl_dump_var("%i", params->borrow_mode);
+			frl_dump_var("%i", tb_worst);
+		}
+#endif
+	if (!(tb_borrowed <= DML2_FRL_CHK_TB_BORROWED_MAX))
+		return LIB_FRL_CAP_CHECK_ERROR_MAX_BORROW;
+
+	c_frl_actual_target_payload = (int)math_ceil(3.0 / 2.0 * hc_active_target) + hc_blank_target;
+	utilization_targeted = c_frl_actual_target_payload / inter->c_frl_line;
+	margin_target = 1.0 - (utilization_targeted + inter->overhead_max);
+#if defined(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 && math_fabs(margin_target) > EPSILON)
+		return LIB_FRL_CAP_CHECK_ERROR_MARGIN;
+
+	return LIB_FRL_CAP_CHECK_OK;
+}
+
+enum lib_frl_cap_check_status frl_cap_check(struct lib_frl_cap_check_params *params)
+{
+	struct lib_frl_cap_check_intermediates inter;
+	return frl_cap_check_intermediates(params, &inter);
+}
+
+enum lib_frl_cap_check_status frl_cap_check_intermediates(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter)
+{
+	if (params->compressed)
+		return frl_cap_check_compressed(params, inter);
+	return frl_cap_check_uncompressed(params, inter);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h
new file mode 100644
index 000000000000..aa2764856546
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_frl_cap_check.h
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: MIT
+//
+// Copyright 2024 Advanced Micro Devices, Inc.
+
+#ifndef __LIB_FRL_CAP_CHECK_H__
+#define __LIB_FRL_CAP_CHECK_H__
+
+#include "dml2_external_lib_deps.h"
+
+extern const int DML2_FRL_CHK_TB_BORROWED_MAX;
+
+enum lib_frl_cap_check_pixel_encoding {
+	LIB_FRL_CAP_CHECK_PIXEL_ENCODING_444,
+	LIB_FRL_CAP_CHECK_PIXEL_ENCODING_422,
+	LIB_FRL_CAP_CHECK_PIXEL_ENCODING_420
+};
+
+enum lib_frl_cap_check_borrow_mode {
+	LIB_FRL_CAP_CHECK_BORROW_MODE_NONE,
+	LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_ACTIVE,
+	LIB_FRL_CAP_CHECK_BORROW_MODE_FROM_BLANK
+};
+
+enum lib_frl_cap_check_status {
+	LIB_FRL_CAP_CHECK_OK = 0,
+
+	LIB_FRL_CAP_CHECK_ERROR_AUDIO_BW = -1,
+	LIB_FRL_CAP_CHECK_ERROR_BORROW = -2,
+	LIB_FRL_CAP_CHECK_ERROR_MAX_BORROW = -3,
+	LIB_FRL_CAP_CHECK_ERROR_MARGIN = -4,
+
+	LIB_FRL_CAP_CHECK_ERROR_UNSUPPORTED_AUDIO = -1000
+};
+
+struct lib_frl_cap_check_intermediates {
+	int c_frl_sb;
+	double overhead_sb;
+	double overhead_rs;
+	double overhead_map;
+	double overhead_min;
+	double overhead_max;
+	double f_pixel_clock_max;
+	double t_line;
+	double r_bit_min;
+	double r_frl_char_min;
+	double c_frl_line;
+	double ap;
+	double r_ap;
+	double avg_audio_packets_line;
+	int audio_packets_line;
+	int blank_audio_min;
+};
+
+struct lib_frl_cap_check_params {
+	int lanes;
+	double f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */
+	double r_bit_nominal; /* FRL bitrate (bps) */
+	int audio_packet_type;
+	double f_audio; /* Audio rate (Hz) */
+	int h_active; /* Active pixels per line */
+	int h_blank; /* Blanking pixels per line */
+	int bpc; /* Bits per component */
+
+	enum lib_frl_cap_check_pixel_encoding pixel_encoding;
+
+	bool compressed;
+	bool bypass_hc_target_calc;
+
+	/* DSC parameters */
+	int slices;
+	int slice_width;
+	double bpp_target;
+
+	int layout; /* not supported */
+	int acat; /* not supported */
+
+	/* outputs */
+	int audio_packets_line;
+
+	/* inputs or outputs */
+	int hc_active_target;
+	int hc_blank_target;
+
+	enum lib_frl_cap_check_borrow_mode borrow_mode;
+};
+
+enum lib_frl_cap_check_status frl_cap_check(struct lib_frl_cap_check_params *params);
+enum lib_frl_cap_check_status frl_cap_check_intermediates(struct lib_frl_cap_check_params *params, struct lib_frl_cap_check_intermediates *inter);
+
+#endif
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
index 0d8ff236c6d0..166f10b8862f 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
@@ -808,6 +808,9 @@ static void populate_dml_output_cfg_from_stream_state(struct dml_output_cfg_st *
 	case SIGNAL_TYPE_DVI_DUAL_LINK:
 		out->OutputEncoder[location] = dml_hdmi;
 		break;
+	case SIGNAL_TYPE_HDMI_FRL:
+		out->OutputEncoder[location] = dml_hdmifrl;
+		break;
 	default:
 		out->OutputEncoder[location] = dml_dp;
 	}
@@ -883,6 +886,7 @@ static void populate_dml_output_cfg_from_stream_state(struct dml_output_cfg_st *
 	case SIGNAL_TYPE_DISPLAY_PORT_MST:
 	case SIGNAL_TYPE_EDP:
 	case SIGNAL_TYPE_VIRTUAL:
+	case SIGNAL_TYPE_HDMI_FRL:
 	default:
 		out->OutputLinkDPRate[location] = dml_dp_rate_na;
 		break;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c
index 5ed14f694fb0..a3ce011612f7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c
@@ -173,6 +173,8 @@ bool is_dtbclk_required(const 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 (is_dp2p0_output_encoder(&context->res_ctx.pipe_ctx[i]))
 			return true;
 	}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c
new file mode 100644
index 000000000000..a638c0d6d765
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright 2022 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_frl_cap_chk.h"
+#include "display_mode_util.h"
+#include "lib_frl_cap_check.h"
+
+#define frl_dump_var(fmt, var) {}
+#define frl_print(fmt, ...) {}
+#include "dcn_calc_math.h"
+#define fabs(var) dcn_bw_fabs(var)
+#define floor(var) dcn_bw_floor(var)
+#define ceil(var) dcn_bw_ceil(var)
+
+#if !defined(TB_BORROWED_MAX)
+#define TB_BORROWED_MAX 400
+#endif
+
+static const double   EPSILON               = 0.01;
+static const double   DBL_EPSILON           = 2.2204460492503131e-16;
+static const int      C_FRL_CB              = 510;
+static const double   OVERHEAD_M            = 0.003;  /* %   */
+static const double   TOLERANCE_PIXEL_CLOCK = 0.005;  /* %   */
+static const double   TOLERANCE_AUDIO_CLOCK = 1000;   /* ppm */
+static const int      TOLERANCE_FRL_BIT     = 300;    /* ppm */
+static const int      ACR_RATE_MAX          = 1500;
+
+static frl_cap_chk_result frl_cap_chk_common(frl_cap_chk_intermediates *inter, frl_cap_chk_params *params)
+{
+	double   audio_bw_reserve = (params->compressed ? 192000.0 : 0.0);
+/*
+	if (getenv("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");
+	}
+*/
+	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 = floor(inter->t_line * inter->r_frl_char_min * params->lanes);
+/*
+	if (getenv("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);
+	}
+*/
+	switch (params->audio_packet_type) {
+	case 0x02:
+		/* unsupported
+	case 0x07:
+		*/
+		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:
+		/* unsupported
+	case 0x0e:
+	case 0x0f:
+		*/
+		inter->ap = 1.0;
+		break;
+		/* unsupported
+	case 0x0b:
+	case 0x0c:
+		if (acat == 0x01)
+			ap = 2.0;
+		else if (acat == 0x02)
+			ap = 3.0;
+		else if (acat == 0x03)
+			ap = 4.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 + TOLERANCE_AUDIO_CLOCK / 1000000.0);
+	inter->avg_audio_packets_line = inter->r_ap * inter->t_line;
+	inter->audio_packets_line     = (int)ceil(inter->avg_audio_packets_line);
+	inter->blank_audio_min        = 32 + 32 * inter->audio_packets_line; // h_blank_audio_min or hc_blank_audio_min
+
+	params->audio_packets_line = inter->audio_packets_line;
+/*
+	if (getenv("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);
+	}
+*/
+	return FRL_CAP_CHK_OK;
+}
+
+
+static frl_cap_chk_result frl_cap_chk_uncompressed(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter)
+{
+	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;
+
+	res = frl_cap_chk_common(inter, params);
+	if (res != FRL_CAP_CHK_OK) {
+		return res;
+	}
+
+	k_420 = params->pixel_encoding == PIXEL_ENCODING_420 ? 2 : 1;
+	k_cd = params->pixel_encoding == 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)floor(dml_max(((7.0/8.0) * c_frl_free) - c_frl_rc_margin, 0.0));
+	bpp = (int)(24 * k_cd / k_420);
+	bytes_line = bpp * params->h_active / 8.0;
+	tb_active = (int)ceil(bytes_line / 3);
+	tb_blank = (int)ceil(params->h_blank * k_cd / k_420);
+/*
+	if (getenv("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);
+	}
+*/
+	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));
+/*
+	if (getenv("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);
+	}
+*/
+	if ((t_active_ref >= t_active_min) && (t_blank_ref >= t_blank_min)) {
+		t_borrowed = 0;
+		params->borrow_mode = 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_mode = BORROW_MODE_FROM_BLANK;
+	} else
+		return FRL_CAP_CHK_ERROR_BORROW;
+
+	tb_borrowed = ceil(t_borrowed * f_tb_average);
+/*
+	if (getenv("DEBUG_FRL_CAP_CHK"))
+	{
+		frl_dump_var("%le", tb_borrowed);
+		frl_dump_var("%i", params->borrow_mode);
+}
+*/
+	if (!(tb_borrowed <= TB_BORROWED_MAX))
+		return FRL_CAP_CHK_ERROR_MAX_BORROW;
+
+	c_frl_actual_payload = (int)ceil((3.0/2.0) * tb_active) + tb_blank - c_frl_rc_savings;
+	utilization          = c_frl_actual_payload / inter->c_frl_line;
+	margin               = 1.0 - (utilization + inter->overhead_max);
+/*
+	if (getenv("DEBUG_FRL_CAP_CHK"))
+	{
+		frl_dump_var("%i",  c_frl_actual_payload);
+		frl_dump_var("%le", utilization);
+		frl_dump_var("%le", margin);
+	}
+*/
+	if (margin < 0 && fabs(margin) > EPSILON)
+		return FRL_CAP_CHK_ERROR_MARGIN;
+
+	return FRL_CAP_CHK_OK;
+}
+
+
+
+static frl_cap_chk_result frl_cap_chk_compressed(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter)
+{
+	frl_cap_chk_result          res;
+
+	int      c_frl_available;
+	int      c_frl_active_available;
+	int      c_frl_blank_available;
+	int      bytes_target;
+	int      hc_active_target;
+	int      hc_blank_target_est1;
+	int      hc_blank_target_est2;
+	int      hc_blank_target;
+	double   f_tb_average;
+	double   t_active_ref;
+	double   t_blank_ref;
+	double   t_active_target;
+	double   t_blank_target;
+	double   tb_borrowed;
+	int      c_frl_actual_target_payload;
+	double   utilization_targeted;
+	double   margin_target;
+
+	res = frl_cap_chk_common(inter, params);
+	if (res != FRL_CAP_CHK_OK)
+		return res;
+
+	c_frl_available        = (int)floor((1 - inter->overhead_max) * inter->c_frl_line);
+	c_frl_active_available = (int)floor(c_frl_available * ((double) params->h_active / (params->h_active + params->h_blank)));
+	(void) c_frl_active_available;
+	c_frl_blank_available  = (int)floor(c_frl_available * ((double) params->h_blank / (params->h_active + params->h_blank)));
+	(void) c_frl_blank_available;
+	bytes_target           = params->slices * (int)ceil(params->bpp_target * params->slice_width / 8.0);
+
+	if (!params->bypass_hc_target_calc)
+		hc_active_target = (int)ceil(bytes_target / 3.0);
+	else
+		hc_active_target = params->hc_active_target;
+
+	hc_blank_target_est1   = (int)ceil(hc_active_target * ((double) params->h_blank / params->h_active));
+	hc_blank_target_est2   = (int)dml_max(hc_blank_target_est1, inter->blank_audio_min);
+
+	if (!params->bypass_hc_target_calc) {
+		hc_blank_target = 4 * (int)floor(dml_min(hc_blank_target_est2, c_frl_available - 3.0/2.0 * hc_active_target) / 4.0);
+
+		params->hc_active_target = hc_active_target;
+		params->hc_blank_target = hc_blank_target;
+	} else {
+		hc_blank_target  = params->hc_blank_target;
+	}
+/*
+	if (getenv("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);
+	}
+*/
+	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;
+/*
+	if (getenv("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_target);
+		frl_dump_var("%le", t_blank_target);
+		frl_dump_var("%le", tb_delta);
+	}
+*/
+	if (t_blank_target - t_blank_ref > DBL_EPSILON) {
+		params->borrow_mode = BORROW_MODE_FROM_ACTIVE;
+	} else if (t_active_target - t_active_ref > DBL_EPSILON) {
+		params->borrow_mode = BORROW_MODE_FROM_BLANK;
+	} else {
+		params->borrow_mode = BORROW_MODE_NONE;
+	}
+
+/*
+	if (getenv("DEBUG_FRL_CAP_CHK"))
+	{
+		frl_dump_var("%le", tb_delta_limit);
+		frl_dump_var("%le", tb_borrowed);
+		frl_dump_var("%i", params->borrow_mode);
+		frl_dump_var("%i", tb_worst);
+	}
+*/
+	if (!(tb_borrowed <= TB_BORROWED_MAX))
+		return FRL_CAP_CHK_ERROR_MAX_BORROW;
+
+	c_frl_actual_target_payload = (int)ceil(3.0/2.0 * hc_active_target) + hc_blank_target;
+	utilization_targeted        = c_frl_actual_target_payload / inter->c_frl_line;
+	margin_target               = 1.0 - (utilization_targeted + inter->overhead_max);
+/*
+	if (getenv("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);
+	}
+*/
+	// oversubscribed bandwidth relative to margin
+	if (margin_target < 0 && fabs(margin_target) > EPSILON)
+		return FRL_CAP_CHK_ERROR_MARGIN;
+
+	return FRL_CAP_CHK_OK;
+}
+
+frl_cap_chk_result frl_cap_chk(frl_cap_chk_params *params)
+{
+	frl_cap_chk_intermediates   inter;
+	return frl_cap_chk_inter(params, &inter);
+}
+
+frl_cap_chk_result frl_cap_chk_inter(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter)
+{
+	if (params->compressed)
+		return frl_cap_chk_compressed(params, inter);
+	return frl_cap_chk_uncompressed(params, inter);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h
new file mode 100644
index 000000000000..87ac9b94e98b
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_frl_cap_chk.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 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
+ *
+ */
+
+#ifndef __DML_FRL_CAP_CHK_H__
+#define __DML_FRL_CAP_CHK_H__
+
+#include "os_types.h"
+
+typedef enum {
+	PIXEL_ENCODING_444,
+	PIXEL_ENCODING_422,
+	PIXEL_ENCODING_420
+} enum_pixel_encoding;
+
+typedef enum {
+	BORROW_MODE_NONE,
+	BORROW_MODE_FROM_ACTIVE,
+	BORROW_MODE_FROM_BLANK
+} enum_borrow_mode;
+
+typedef enum {
+	FRL_CAP_CHK_OK = 0,
+
+	FRL_CAP_CHK_ERROR_AUDIO_BW = -1,
+	FRL_CAP_CHK_ERROR_BORROW = -2,
+	FRL_CAP_CHK_ERROR_MAX_BORROW = -3,
+	FRL_CAP_CHK_ERROR_MARGIN = -4,
+
+	FRL_CAP_CHK_ERROR_UNSUPPORTED_AUDIO = -1000
+} frl_cap_chk_result;
+
+typedef struct {
+	int c_frl_sb;
+	double overhead_sb;
+	double overhead_rs;
+	double overhead_map;
+	double overhead_min;
+	double overhead_max;
+	double f_pixel_clock_max;
+	double t_line;
+	double r_bit_min;
+	double r_frl_char_min;
+	double c_frl_line;
+	double ap;
+	double r_ap;
+	double avg_audio_packets_line;
+	int audio_packets_line;
+	int blank_audio_min;
+} frl_cap_chk_intermediates;
+
+typedef struct {
+	int lanes;
+	double f_pixel_clock_nominal; /* Pixel Clock rate (Hz) */
+	double r_bit_nominal; /* FRL bitrate (bps) */
+	int audio_packet_type;
+	double f_audio; /* Audio rate (Hz) */
+	int h_active; /* Active pixels per line */
+	int h_blank; /* Blanking pixels per line */
+	int bpc; /* Bits per component */
+
+	enum_pixel_encoding pixel_encoding;
+
+	bool compressed;
+	bool bypass_hc_target_calc;
+
+	/* DSC parameters */
+	int slices;
+	int slice_width;
+	double bpp_target;
+
+	int layout; /* not supported */
+	int acat; /* not supported */
+
+	/* outputs */
+	int audio_packets_line;
+
+	/* inputs or outputs */
+	int hc_active_target;
+	int hc_blank_target;
+
+	enum_borrow_mode borrow_mode;
+} frl_cap_chk_params;
+
+frl_cap_chk_result frl_cap_chk(frl_cap_chk_params *params);
+frl_cap_chk_result frl_cap_chk_inter(frl_cap_chk_params *params, frl_cap_chk_intermediates *inter);
+
+#endif
-- 
2.54.0


  parent reply	other threads:[~2026-05-12 15:52 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 ` [PATCH v5 02/13] drm/amd/display: Add DML changes to support HDMI FRL Harry Wentland
2026-05-12 15:52 ` Harry Wentland [this message]
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-4-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.