drm/amd/display: add HDMI 2.1 FRL base support to DML 2.0

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>
Tested-by: Dan Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Harry Wentland 2026-05-08 10:43:43 -04:00 committed by Alex Deucher
parent a7c7b927c8
commit 8812fae129
13 changed files with 1153 additions and 7 deletions

View File

@ -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

View File

@ -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;
}
}
}
}

View File

@ -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)

View File

@ -215,6 +215,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;
}
@ -249,6 +252,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;

View File

@ -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,

View File

@ -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;
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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