Add HDMI FRL bits to DML 2.0
---
 .../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 16514f1e4ed9..1ef4ac8ccbb6 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 d89fd876975e..75dfeed1a066 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 1bc81e26a11f..cc8a603ce0f2 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

Reply via email to