amd-gfx.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
From: Harry Wentland <harry.wentland@amd.com>
To: <amd-gfx@lists.freedesktop.org>
Cc: <Jerry.Zuo@amd.com>, Harry Wentland <harry.wentland@amd.com>
Subject: [PATCH v5 07/13] drm/amd/display: Add DC resource support for FRL
Date: Tue, 12 May 2026 11:52:38 -0400	[thread overview]
Message-ID: <20260512155244.403854-8-harry.wentland@amd.com> (raw)
In-Reply-To: <20260512155244.403854-1-harry.wentland@amd.com>

Add support for FRL in DC resources. This is mostly the register
macros, encoder creation, and HW capabilities.

Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Fangzhi Zuo <Jerry.Zuo@amd.com>
---
 .../dc/resource/dce112/dce112_resource.c      |   3 +
 .../dc/resource/dcn30/dcn30_resource.c        | 126 +++++++++++++++++
 .../dc/resource/dcn301/dcn301_resource.c      |   1 +
 .../dc/resource/dcn302/dcn302_resource.c      | 109 +++++++++++++++
 .../dc/resource/dcn303/dcn303_resource.c      | 109 +++++++++++++++
 .../dc/resource/dcn31/dcn31_resource.c        | 127 +++++++++++++++++
 .../dc/resource/dcn314/dcn314_resource.c      | 127 +++++++++++++++++
 .../dc/resource/dcn315/dcn315_resource.c      | 128 +++++++++++++++++
 .../dc/resource/dcn316/dcn316_resource.c      | 126 +++++++++++++++++
 .../dc/resource/dcn32/dcn32_resource.c        | 131 +++++++++++++++++
 .../dc/resource/dcn32/dcn32_resource.h        |  79 +++++++++--
 .../dc/resource/dcn321/dcn321_resource.c      | 132 ++++++++++++++++++
 .../dc/resource/dcn35/dcn35_resource.c        | 121 ++++++++++++++++
 .../dc/resource/dcn351/dcn351_resource.c      | 121 ++++++++++++++++
 .../dc/resource/dcn36/dcn36_resource.c        | 121 ++++++++++++++++
 .../dc/resource/dcn401/dcn401_resource.c      | 121 ++++++++++++++++
 .../dc/resource/dcn42/dcn42_resource.c        | 121 ++++++++++++++++
 17 files changed, 1789 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c
index ad0214d99a45..6d49d6b70b21 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c
@@ -981,6 +981,9 @@ enum dc_status resource_map_phy_clock_resources(
 		|| dc_is_virtual_signal(pipe_ctx->stream->signal))
 		pipe_ctx->clock_source =
 				dc->res_pool->dp_clock_source;
+	else if (pipe_ctx->stream->signal == SIGNAL_TYPE_HDMI_FRL)
+			pipe_ctx->clock_source =
+				dc->res_pool->dp_clock_source;
 	else {
 		if (stream && stream->link && stream->link->link_enc)
 			pipe_ctx->clock_source = find_matching_pll(
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
index e49728cb41d9..70eacb0edfd9 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
@@ -50,6 +50,8 @@
 #include "dcn30/dcn30_vpg.h"
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn30/dcn30_dio_link_encoder.h"
 #include "dce/dce_clock_source.h"
 #include "dce/dce_audio.h"
@@ -400,6 +402,45 @@ static const struct dcn10_link_enc_mask le_mask = {
 };
 
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\
+}
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6)
+
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\
+}
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+	hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 static const struct dce_panel_cntl_registers panel_cntl_regs[] = {
 	{ DCN_PANEL_CNTL_REG_LIST() }
 };
@@ -673,6 +714,7 @@ static const struct resource_caps res_cap_dcn3 = {
 	.num_video_plane = 6,
 	.num_audio = 6,
 	.num_stream_encoder = 6,
+	.num_hpo_frl = 1,
 	.num_pll = 6,
 	.num_dwb = 1,
 	.num_ddc = 6,
@@ -1075,6 +1117,69 @@ static struct stream_encoder *dcn30_stream_encoder_create(enum engine_id eng_id,
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn30_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									  struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 6;
+		afmt_inst = 6;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG sub-block */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn30_vpg_create(ctx, vpg_inst);
+	afmt = dcn30_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn30_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								      struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id-ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct dce_hwseq *dcn30_hwseq_create(struct dc_context *ctx)
 {
 	struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL);
@@ -1091,6 +1196,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn30_create_audio,
 	.create_stream_encoder = dcn30_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn30_hpo_frl_stream_encoder_create,
 	.create_hwseq = dcn30_hwseq_create,
 };
 
@@ -1113,6 +1219,23 @@ static void dcn30_resource_destruct(struct dcn30_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < (unsigned int)pool->base.res_cap->num_dsc; i++) {
 		if (pool->base.dscs[i] != NULL)
 			dcn20_dsc_destroy(&pool->base.dscs[i]);
@@ -2275,6 +2398,7 @@ static const struct resource_funcs dcn30_res_pool_funcs = {
 	.destroy = dcn30_destroy_resource_pool,
 	.link_enc_create = dcn30_link_encoder_create,
 	.panel_cntl_create = dcn30_panel_cntl_create,
+	.hpo_frl_link_enc_create = dcn30_hpo_frl_link_encoder_create,
 	.validate_bandwidth = dcn30_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn30_calculate_wm_and_dlg,
 	.update_soc_for_wm_a = dcn30_update_soc_for_wm_a,
@@ -2358,6 +2482,8 @@ static bool dcn30_resource_construct(
 	dc->caps.max_slave_rgb_planes = 2;
 	dc->caps.post_blend_color_processing = true;
 	dc->caps.force_dp_tps4_for_cp2520 = true;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.extended_aux_timeout_support = true;
 	dc->caps.dmcub_support = true;
 
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c
index 69890c26a8b1..77f9e371f48e 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c
@@ -644,6 +644,7 @@ static struct resource_caps res_cap_dcn301 = {
 	.num_video_plane = 4,
 	.num_audio = 4,
 	.num_stream_encoder = 4,
+	.num_hpo_frl = 0,
 	.num_pll = 4,
 	.num_dwb = 1,
 	.num_ddc = 4,
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
index db7de6036408..caeb001dd7de 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
@@ -32,6 +32,8 @@
 #include "dcn30/dcn30_dio_stream_encoder.h"
 #include "dcn30/dcn30_dwb.h"
 #include "dcn30/dcn30_dpp.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
 #include "dcn30/dcn30_hubbub.h"
 #include "dcn30/dcn30_hubp.h"
 #include "dcn30/dcn30_mmhubbub.h"
@@ -129,6 +131,7 @@ static const struct resource_caps res_cap_dcn302 = {
 		.num_video_plane = 5,
 		.num_audio = 5,
 		.num_stream_encoder = 5,
+		.num_hpo_frl = 1,
 		.num_dwb = 1,
 		.num_ddc = 5,
 		.num_vmid = 16,
@@ -451,6 +454,91 @@ static struct stream_encoder *dcn302_stream_encoder_create(enum engine_id eng_id
 	return &enc1->base;
 }
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+		[id] = { DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id) }
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+		DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 5)
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+		hpo_frl_stream_encoder_reg_list(0),
+		hpo_frl_stream_encoder_dme_reg_list(5),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+		DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+		DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+static struct hpo_frl_stream_encoder *dcn302_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+		struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct vpg *vpg;
+	struct afmt *afmt;
+	int vpg_inst;
+	int afmt_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG sub-block */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn302_vpg_create(ctx, vpg_inst);
+	afmt = dcn302_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, eng_id, vpg, afmt,
+			&hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+#define hpo_frl_link_encoder_reg_list(id)\
+		[id] = { DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id) }
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+		hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+		DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+		DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
+static struct hpo_frl_link_encoder *dcn302_hpo_frl_link_encoder_create(enum engine_id eng_id, struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0,
+			&hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 #define clk_src_regs(index, pllid)\
 		[index] = { CS_COMMON_REG_LIST_DCN3_02(index, pllid) }
 
@@ -970,6 +1058,7 @@ static const struct resource_create_funcs res_create_funcs = {
 		.read_dce_straps = read_dce_straps,
 		.create_audio = dcn302_create_audio,
 		.create_stream_encoder = dcn302_stream_encoder_create,
+		.create_hpo_frl_stream_encoder = dcn302_hpo_frl_stream_encoder_create,
 		.create_hwseq = dcn302_hwseq_create,
 };
 
@@ -1036,6 +1125,23 @@ static void dcn302_resource_destruct(struct resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) {
+		if (pool->hpo_frl_stream_enc[i] != NULL) {
+			if (pool->hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->hpo_frl_stream_enc[i]->vpg));
+				pool->hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->hpo_frl_stream_enc[i]->afmt));
+				pool->hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->hpo_frl_stream_enc[i]));
+			pool->hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < (unsigned int)pool->res_cap->num_dsc; i++) {
 		if (pool->dscs[i] != NULL)
 			dcn20_dsc_destroy(&pool->dscs[i]);
@@ -1172,6 +1278,7 @@ static struct resource_funcs dcn302_res_pool_funcs = {
 		.destroy = dcn302_destroy_resource_pool,
 		.link_enc_create = dcn302_link_encoder_create,
 		.panel_cntl_create = dcn302_panel_cntl_create,
+		.hpo_frl_link_enc_create = dcn302_hpo_frl_link_encoder_create,
 		.validate_bandwidth = dcn30_validate_bandwidth,
 		.calculate_wm_and_dlg = dcn30_calculate_wm_and_dlg,
 		.update_soc_for_wm_a = dcn30_update_soc_for_wm_a,
@@ -1270,6 +1377,8 @@ static bool dcn302_resource_construct(
 	dc->caps.max_slave_rgb_planes = 2;
 	dc->caps.post_blend_color_processing = true;
 	dc->caps.force_dp_tps4_for_cp2520 = true;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.extended_aux_timeout_support = true;
 	dc->caps.dmcub_support = true;
 	dc->caps.max_v_total = (1 << 15) - 1;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
index fc7353451e8f..58c314237ce6 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
@@ -32,6 +32,8 @@
 #include "dcn30/dcn30_dio_stream_encoder.h"
 #include "dcn30/dcn30_dpp.h"
 #include "dcn30/dcn30_dwb.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
 #include "dcn30/dcn30_hubbub.h"
 #include "dcn30/dcn30_hubp.h"
 #include "dcn30/dcn30_mmhubbub.h"
@@ -126,6 +128,7 @@ static const struct resource_caps res_cap_dcn303 = {
 		.num_video_plane = 2,
 		.num_audio = 2,
 		.num_stream_encoder = 2,
+		.num_hpo_frl = 1,
 		.num_dwb = 1,
 		.num_ddc = 2,
 		.num_vmid = 16,
@@ -438,6 +441,91 @@ static struct stream_encoder *dcn303_stream_encoder_create(enum engine_id eng_id
 	return &enc1->base;
 }
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+		[id] = { DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id) }
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+		DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 2)
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+		hpo_frl_stream_encoder_reg_list(0),
+		hpo_frl_stream_encoder_dme_reg_list(2),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+		DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+		DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+static struct hpo_frl_stream_encoder *dcn303_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+		struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct vpg *vpg;
+	struct afmt *afmt;
+	int vpg_inst;
+	int afmt_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 2;
+		afmt_inst = 2;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG sub-block */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn303_vpg_create(ctx, vpg_inst);
+	afmt = dcn303_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios, eng_id, vpg, afmt,
+			&hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+#define hpo_frl_link_encoder_reg_list(id)\
+		[id] = { DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id) }
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+		hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+		DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+		DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
+static struct hpo_frl_link_encoder *dcn303_hpo_frl_link_encoder_create(enum engine_id eng_id, struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0,
+			&hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0], &hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 #define clk_src_regs(index, pllid)\
 		[index] = { CS_COMMON_REG_LIST_DCN3_03(index, pllid) }
 
@@ -915,6 +1003,7 @@ static const struct resource_create_funcs res_create_funcs = {
 		.read_dce_straps = read_dce_straps,
 		.create_audio = dcn303_create_audio,
 		.create_stream_encoder = dcn303_stream_encoder_create,
+		.create_hpo_frl_stream_encoder = dcn303_hpo_frl_stream_encoder_create,
 		.create_hwseq = dcn303_hwseq_create,
 };
 
@@ -980,6 +1069,23 @@ static void dcn303_resource_destruct(struct resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->hpo_frl_stream_enc_count; i++) {
+		if (pool->hpo_frl_stream_enc[i] != NULL) {
+			if (pool->hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->hpo_frl_stream_enc[i]->vpg));
+				pool->hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->hpo_frl_stream_enc[i]->afmt));
+				pool->hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->hpo_frl_stream_enc[i]));
+			pool->hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < (unsigned int)pool->res_cap->num_dsc; i++) {
 		if (pool->dscs[i] != NULL)
 			dcn20_dsc_destroy(&pool->dscs[i]);
@@ -1116,6 +1222,7 @@ static struct resource_funcs dcn303_res_pool_funcs = {
 		.destroy = dcn303_destroy_resource_pool,
 		.link_enc_create = dcn303_link_encoder_create,
 		.panel_cntl_create = dcn303_panel_cntl_create,
+		.hpo_frl_link_enc_create = dcn303_hpo_frl_link_encoder_create,
 		.validate_bandwidth = dcn30_validate_bandwidth,
 		.calculate_wm_and_dlg = dcn30_calculate_wm_and_dlg,
 		.update_soc_for_wm_a = dcn30_update_soc_for_wm_a,
@@ -1214,6 +1321,8 @@ static bool dcn303_resource_construct(
 	dc->caps.max_slave_rgb_planes = 1;
 	dc->caps.post_blend_color_processing = true;
 	dc->caps.force_dp_tps4_for_cp2520 = true;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.extended_aux_timeout_support = true;
 	dc->caps.dmcub_support = true;
 	dc->caps.max_v_total = (1 << 15) - 1;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
index 5925c40da6ee..15730fe1b9db 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
@@ -54,6 +54,8 @@
 #include "dcn30/dcn30_vpg.h"
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn31/dcn31_apg.h"
@@ -431,6 +433,45 @@ static const struct dcn10_link_enc_mask le_mask = {
 	DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\
+}
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6)
+
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\
+}
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+	hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_list(id)\
 [id] = {\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST(id)\
@@ -833,6 +874,7 @@ static const struct resource_caps res_cap_dcn31 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 5,
@@ -904,6 +946,7 @@ static const struct dc_debug_options debug_defaults_drv = {
 		}
 	},
 	.disable_z10 = true,
+	.max_frl_rate = HDMI_FRL_LINK_RATE_10GBPS,
 	.enable_z9_disable_interface = true, /* Allow support for the PMFW interface for disable Z9*/
 	.dml_hostvm_override = DML_HOSTVM_OVERRIDE_FALSE,
 	.using_dml2 = false,
@@ -1283,6 +1326,69 @@ static struct stream_encoder *dcn31_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									  struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								      struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id - ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1368,6 +1474,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn31_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn31_hwseq_create,
@@ -1392,6 +1499,23 @@ static void dcn31_resource_destruct(struct dcn31_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1871,6 +1995,7 @@ static struct resource_funcs dcn31_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn31_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg,
@@ -1955,6 +2080,8 @@ static bool dcn31_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
index 16eee0c6f4bc..fbfedfca211f 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
@@ -57,6 +57,8 @@
 #include "dcn30/dcn30_afmt.h"
 #include "dcn31/dcn31_dio_link_encoder.h"
 #include "dcn314/dcn314_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn31/dcn31_apg.h"
@@ -438,6 +440,45 @@ static const struct dcn10_link_enc_mask le_mask = {
 	DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\
+}
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6)
+
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\
+}
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+	hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_list(id)\
 [id] = {\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST(id)\
@@ -845,6 +886,7 @@ static const struct resource_caps res_cap_dcn314 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 5,
@@ -1341,6 +1383,71 @@ static struct stream_encoder *dcn314_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									  struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		//Maps to VPG INST 5, vpg_inst 5 reg offset padded to inst 9
+		vpg_inst = 9;
+		afmt_inst = 5;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift,
+					       &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								      struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id - ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1427,6 +1534,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn314_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn314_hwseq_create,
@@ -1451,6 +1559,23 @@ static void dcn314_resource_destruct(struct dcn314_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1797,6 +1922,7 @@ static struct resource_funcs dcn314_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn314_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg,
@@ -1883,6 +2009,7 @@ static bool dcn314_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
index 37404aa0edd9..0f800f586e08 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
@@ -53,6 +53,8 @@
 #include "dcn30/dcn30_vpg.h"
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn31/dcn31_apg.h"
@@ -433,6 +435,45 @@ static const struct dcn10_link_enc_mask le_mask = {
 	DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\
+}
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6)
+
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\
+}
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+	hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_list(id)\
 [id] = {\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST(id)\
@@ -832,6 +873,7 @@ static const struct resource_caps res_cap_dcn31 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 5,
@@ -903,6 +945,7 @@ static const struct dc_debug_options debug_defaults_drv = {
 			.afmt = true,
 		}
 	},
+	.max_frl_rate = HDMI_FRL_LINK_RATE_12GBPS,
 	.psr_power_use_phy_fsm = 0,
 	.using_dml2 = false,
 	.min_disp_clk_khz = 100000,
@@ -1284,6 +1327,70 @@ static struct stream_encoder *dcn315_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									  struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift,
+					       &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								      struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id - ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1369,6 +1476,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn315_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn31_hwseq_create,
@@ -1393,6 +1501,23 @@ static void dcn315_resource_destruct(struct dcn315_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1867,6 +1992,7 @@ static struct resource_funcs dcn315_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn31_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg,
@@ -1930,6 +2056,8 @@ static bool dcn315_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
index d946663f416a..efbc2a506046 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
@@ -53,6 +53,8 @@
 #include "dcn30/dcn30_vpg.h"
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn31/dcn31_apg.h"
@@ -421,6 +423,45 @@ static const struct dcn10_link_enc_mask le_mask = {
 	DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST(id)\
+}
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST(id, 6)
+
+
+static const struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[] = {
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6),
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+[id] = {\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST(id)\
+}
+
+static const struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[] = {
+	hpo_frl_link_encoder_reg_list(0),
+};
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 
 
 #define hpo_dp_stream_encoder_reg_list(id)\
@@ -827,6 +868,7 @@ static const struct resource_caps res_cap_dcn31 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 5,
@@ -898,6 +940,7 @@ static const struct dc_debug_options debug_defaults_drv = {
 			.afmt = true,
 		}
 	},
+	.max_frl_rate = HDMI_FRL_LINK_RATE_10GBPS, /*same as dcn3.1 for now*/
 	.using_dml2 = false,
 };
 
@@ -1277,6 +1320,70 @@ static struct stream_encoder *dcn316_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									  struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift,
+					       &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								      struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id - ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
@@ -1364,6 +1471,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn316_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn31_hwseq_create,
@@ -1388,6 +1496,21 @@ static void dcn316_resource_destruct(struct dcn316_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1743,6 +1866,7 @@ static struct resource_funcs dcn316_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn31_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn31_calculate_wm_and_dlg,
@@ -1806,6 +1930,8 @@ static bool dcn316_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
index 1bdb4ffd2921..e03dc966e121 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
@@ -55,6 +55,8 @@
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
 #include "dcn32/dcn32_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -144,6 +146,10 @@ enum dcn32_clk_src_array_id {
 	REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)\
+	REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
+		reg ## block ## id ## _ ## reg_name
+
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)\
 	REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
@@ -338,6 +344,36 @@ static const struct dcn10_link_enc_mask le_mask = {
 	//DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6)
+
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_init(id)\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -668,6 +704,7 @@ static const struct resource_caps res_cap_dcn32 = {
 	.num_video_plane = 4,
 	.num_audio = 5,
 	.num_stream_encoder = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 5,
@@ -1279,6 +1316,79 @@ static struct stream_encoder *dcn32_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn32_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									  struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6);
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn32_vpg_create(ctx, vpg_inst);
+	afmt = dcn32_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift,
+					       &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn32_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								      struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id - ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn32_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1380,6 +1490,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn32_create_audio,
 	.create_stream_encoder = dcn32_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn32_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn32_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn32_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn32_hwseq_create,
@@ -1404,6 +1515,23 @@ static void dcn32_resource_destruct(struct dcn32_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -2139,6 +2267,7 @@ static struct resource_funcs dcn32_res_pool_funcs = {
 	.destroy = dcn32_destroy_resource_pool,
 	.link_enc_create = dcn32_link_encoder_create,
 	.link_enc_create_minimal = NULL,
+	.hpo_frl_link_enc_create = dcn32_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn32_panel_cntl_create,
 	.validate_bandwidth = dcn32_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn32_calculate_wm_and_dlg,
@@ -2279,6 +2408,8 @@ static bool dcn32_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h
index 91be493e0bb6..68e7140f1505 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.h
@@ -323,20 +323,27 @@ unsigned int dcn32_get_max_hw_cursor_size(const struct dc *dc,
 		SRI_ARR(DC_HPD_TOGGLE_FILT_CNTL, HPD, id))
 
 /* Link encoder */
-#define LE_DCN3_REG_LIST_RI(id)                                                \
-  SRI_ARR(DIG_BE_CNTL, DIG, id), SRI_ARR(DIG_BE_EN_CNTL, DIG, id),             \
-      SRI_ARR(TMDS_CTL_BITS, DIG, id),                                         \
-      SRI_ARR(TMDS_DCBALANCER_CONTROL, DIG, id), SRI_ARR(DP_CONFIG, DP, id),   \
-      SRI_ARR(DP_DPHY_CNTL, DP, id), SRI_ARR(DP_DPHY_PRBS_CNTL, DP, id),       \
-      SRI_ARR(DP_DPHY_SCRAM_CNTL, DP, id), SRI_ARR(DP_DPHY_SYM0, DP, id),      \
-      SRI_ARR(DP_DPHY_SYM1, DP, id), SRI_ARR(DP_DPHY_SYM2, DP, id),            \
-      SRI_ARR(DP_DPHY_TRAINING_PATTERN_SEL, DP, id),                           \
-      SRI_ARR(DP_LINK_CNTL, DP, id), SRI_ARR(DP_LINK_FRAMING_CNTL, DP, id),    \
-      SRI_ARR(DP_MSE_SAT0, DP, id), SRI_ARR(DP_MSE_SAT1, DP, id),              \
-      SRI_ARR(DP_MSE_SAT2, DP, id), SRI_ARR(DP_MSE_SAT_UPDATE, DP, id),        \
-      SRI_ARR(DP_SEC_CNTL, DP, id), SRI_ARR(DP_VID_STREAM_CNTL, DP, id),       \
-      SRI_ARR(DP_DPHY_FAST_TRAINING, DP, id), SRI_ARR(DP_SEC_CNTL1, DP, id),   \
-      SRI_ARR(DP_DPHY_BS_SR_SWAP_CNTL, DP, id),                                \
+#define LE_DCN3_REG_LIST_RI(id)                                      \
+      SRI_ARR(DIG_BE_CNTL, DIG, id),                                 \
+      SRI_ARR(DIG_BE_EN_CNTL, DIG, id),                              \
+      SRI_ARR(TMDS_CTL_BITS, DIG, id),                               \
+      SRI_ARR(TMDS_DCBALANCER_CONTROL, DIG, id),                     \
+      SRI_ARR(DP_CONFIG, DP, id), SRI_ARR(DP_DPHY_CNTL, DP, id),     \
+      SRI_ARR(DP_DPHY_PRBS_CNTL, DP, id),                            \
+      SRI_ARR(DP_DPHY_SCRAM_CNTL, DP, id),                           \
+      SRI_ARR(DP_DPHY_SYM0, DP, id), SRI_ARR(DP_DPHY_SYM1, DP, id),  \
+      SRI_ARR(DP_DPHY_SYM2, DP, id),                                 \
+      SRI_ARR(DP_DPHY_TRAINING_PATTERN_SEL, DP, id),                 \
+      SRI_ARR(DP_LINK_CNTL, DP, id),                                 \
+      SRI_ARR(DP_LINK_FRAMING_CNTL, DP, id),                         \
+      SRI_ARR(DP_MSE_SAT0, DP, id), SRI_ARR(DP_MSE_SAT1, DP, id),    \
+      SRI_ARR(DP_MSE_SAT2, DP, id),                                  \
+      SRI_ARR(DP_MSE_SAT_UPDATE, DP, id),                            \
+      SRI_ARR(DP_SEC_CNTL, DP, id),                                  \
+      SRI_ARR(DP_VID_STREAM_CNTL, DP, id),                           \
+      SRI_ARR(DP_DPHY_FAST_TRAINING, DP, id),                        \
+      SRI_ARR(DP_SEC_CNTL1, DP, id),                                 \
+      SRI_ARR(DP_DPHY_BS_SR_SWAP_CNTL, DP, id),                      \
       SRI_ARR(DP_DPHY_HBR2_PATTERN_CONTROL, DP, id)
 
 #define LE_DCN31_REG_LIST_RI(id)                                               \
@@ -1281,4 +1288,48 @@ unsigned int dcn32_get_max_hw_cursor_size(const struct dc *dc,
       I2C_HW_ENGINE_COMMON_REG_LIST_RI(id), SR_ARR_I2C(DIO_MEM_PWR_CTRL, id),  \
       SR_ARR_I2C(DIO_MEM_PWR_STATUS, id)
 
+#define DCN3_0_HDMI_STREAM_ENC_REG_LIST_RI(id)                                 \
+  SR_ARR(HDMI_STREAM_ENC_CLOCK_CONTROL, id),                                   \
+      SR_ARR(HDMI_STREAM_ENC_INPUT_MUX_CONTROL, id),                          \
+      SR_ARR(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL0, id),    \
+      SR_ARR(HDMI_STREAM_ENC_CLOCK_RAMP_ADJUSTER_FIFO_STATUS_CONTROL2, id)
+
+#define DCN3_0_HDMI_TB_ENC_REG_LIST_RI(id)                                     \
+  SR_ARR(HDMI_TB_ENC_CONTROL, id), SR_ARR(HDMI_TB_ENC_H_ACTIVE_BLANK, id),     \
+      SR_ARR(HDMI_TB_ENC_HC_ACTIVE_BLANK, id), SR_ARR(HDMI_TB_ENC_MODE, id),   \
+      SR_ARR(HDMI_TB_ENC_PACKET_CONTROL, id),                                  \
+      SR_ARR(HDMI_TB_ENC_DB_CONTROL, id),                                      \
+      SR_ARR(HDMI_TB_ENC_PIXEL_FORMAT, id),                                    \
+      SR_ARR(HDMI_TB_ENC_VBI_PACKET_CONTROL1, id),                             \
+      SR_ARR(HDMI_TB_ENC_GC_CONTROL, id),                                      \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET_CONTROL0, id),                         \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET_CONTROL1, id),                         \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET0_1_LINE, id),                          \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET2_3_LINE, id),                          \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET4_5_LINE, id),                          \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET6_7_LINE, id),                          \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET8_9_LINE, id),                          \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET10_11_LINE, id),                        \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET12_13_LINE, id),                        \
+      SR_ARR(HDMI_TB_ENC_GENERIC_PACKET14_LINE, id),                           \
+      SR_ARR(HDMI_TB_ENC_ACR_PACKET_CONTROL, id),                              \
+      SR_ARR(HDMI_TB_ENC_ACR_32_0, id), SR_ARR(HDMI_TB_ENC_ACR_32_1, id),      \
+      SR_ARR(HDMI_TB_ENC_ACR_44_0, id), SR_ARR(HDMI_TB_ENC_ACR_44_1, id),      \
+      SR_ARR(HDMI_TB_ENC_ACR_48_0, id), SR_ARR(HDMI_TB_ENC_ACR_48_1, id),      \
+      SR_ARR(HDMI_TB_ENC_CRC_CNTL, id),                                        \
+      SR_ARR(HDMI_TB_ENC_METADATA_PACKET_CONTROL, id)
+
+#define DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, offset) \
+	SRI_ARR_DME(DME_CONTROL, DME, id, offset)
+
+#define DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)                              \
+  DCN3_0_HDMI_STREAM_ENC_REG_LIST_RI(id), DCN3_0_HDMI_TB_ENC_REG_LIST_RI(id)
+
+#define DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id) \
+	SR_ARR(HDMI_LINK_ENC_CLK_CTRL, id), \
+	SR_ARR(HDMI_LINK_ENC_CONTROL, id), \
+	SR_ARR(HDMI_FRL_ENC_CONFIG, id), \
+	SR_ARR(HDMI_FRL_ENC_CONFIG2, id),\
+	SR_ARR(HDMI_FRL_ENC_MEM_CTRL, id)
+
 #endif /* _DCN32_RESOURCE_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
index 296558258672..65f445f38cfb 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
@@ -57,6 +57,8 @@
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
 #include "dcn32/dcn32_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -139,6 +141,10 @@ enum dcn321_clk_src_array_id {
 	REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)\
+	REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
+		reg ## block ## id ## _ ## reg_name
+
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)\
 	REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
@@ -332,6 +338,35 @@ static const struct dcn10_link_enc_mask le_mask = {
 //	DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6)
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_init(id)\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -659,6 +694,7 @@ static const struct resource_caps res_cap_dcn321 = {
 	.num_video_plane = 4,
 	.num_audio = 5,
 	.num_stream_encoder = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 5,
@@ -1255,6 +1291,79 @@ static struct stream_encoder *dcn321_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn321_hpo_frl_stream_encoder_create(enum engine_id eng_id,
+									   struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct afmt *afmt;
+	struct vpg *vpg;
+	int afmt_inst;
+	int vpg_inst;
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6);
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else {
+		return NULL;
+	}
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn321_vpg_create(ctx, vpg_inst);
+	afmt = dcn321_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3,
+					       ctx,
+					       ctx->dc_bios,
+					       eng_id,
+					       vpg,
+					       afmt,
+					       &hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					       &hpo_se_shift,
+					       &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn321_hpo_frl_link_encoder_create(enum engine_id eng_id,
+								       struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3,
+					ctx,
+					eng_id - ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+					&hpo_le_shift,
+					&hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn321_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1356,6 +1465,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn321_create_audio,
 	.create_stream_encoder = dcn321_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn321_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn321_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn321_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn321_hwseq_create,
@@ -1380,6 +1490,23 @@ static void dcn321_resource_destruct(struct dcn321_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1635,6 +1762,7 @@ static struct resource_funcs dcn321_res_pool_funcs = {
 	.destroy = dcn321_destroy_resource_pool,
 	.link_enc_create = dcn321_link_encoder_create,
 	.link_enc_create_minimal = NULL,
+	.hpo_frl_link_enc_create = dcn321_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn32_panel_cntl_create,
 	.validate_bandwidth = dcn32_validate_bandwidth,
 	.calculate_wm_and_dlg = dcn32_calculate_wm_and_dlg,
@@ -1772,6 +1900,8 @@ static bool dcn321_resource_construct(
 	dc->caps.max_slave_rgb_planes = 2;
 	dc->caps.post_blend_color_processing = true;
 	dc->caps.force_dp_tps4_for_cp2520 = true;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
@@ -1814,6 +1944,8 @@ static bool dcn321_resource_construct(
 	dc->caps.color.mpc.ogam_rom_caps.hlg = 0;
 	dc->caps.color.mpc.ocsc = 1;
 	dc->caps.color.mpc.preblend = true;
+	/* HACK: Force FRL support until BIOS is ready. */
+	dc->config.force_hdmi21_frl_enc_enable = true;
 
 	/* Use pipe context based otg sync logic */
 	dc->config.use_pipe_ctx_sync_logic = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
index c7fba9f39e19..ec92e8f7d173 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
@@ -58,6 +58,8 @@
 #include "dcn30/dcn30_afmt.h"
 #include "dcn31/dcn31_dio_link_encoder.h"
 #include "dcn35/dcn35_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -154,6 +156,10 @@ enum dcn35_clk_src_array_id {
 	REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)\
+	REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
+		reg ## block ## id ## _ ## reg_name
+
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)\
 	REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
@@ -353,6 +359,35 @@ static const struct dcn10_link_enc_mask le_mask = {
 	//DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6)
+
+#define hpo_frl_stream_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_init(id)\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -685,6 +720,7 @@ static const struct resource_caps res_cap_dcn35 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 4,/*1 c10 edp, 3xc20 combo PHY*/
@@ -1337,6 +1373,72 @@ static struct stream_encoder *dcn35_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct vpg *vpg;
+	struct afmt *afmt;
+	int vpg_inst;
+	int afmt_inst;
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6);
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios,
+					eng_id, vpg, afmt,
+					&hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1438,6 +1540,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn35_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn35_hwseq_create,
@@ -1462,6 +1565,21 @@ static void dcn35_resource_destruct(struct dcn35_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1826,6 +1944,7 @@ static struct resource_funcs dcn35_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn35_validate_bandwidth,
 	.calculate_wm_and_dlg = NULL,
@@ -1912,6 +2031,8 @@ static bool dcn35_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
index e9fc43abf342..4d2d26d64a56 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
@@ -37,6 +37,8 @@
 
 #include "dcn31/dcn31_dio_link_encoder.h"
 #include "dcn35/dcn35_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -134,6 +136,10 @@ enum dcn351_clk_src_array_id {
 	REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)\
+	REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
+		reg ## block ## id ## _ ## reg_name
+
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)\
 	REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
@@ -333,6 +339,35 @@ static const struct dcn10_link_enc_mask le_mask = {
 	//DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6)
+
+#define hpo_frl_stream_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_init(id)\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -665,6 +700,7 @@ static const struct resource_caps res_cap_dcn351 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 4,/*1 c10 edp, 3xc20 combo PHY*/
@@ -1317,6 +1353,72 @@ static struct stream_encoder *dcn35_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct vpg *vpg;
+	struct afmt *afmt;
+	int vpg_inst;
+	int afmt_inst;
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6);
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios,
+					eng_id, vpg, afmt,
+					&hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1418,6 +1520,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn35_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn351_hwseq_create,
@@ -1442,6 +1545,21 @@ static void dcn351_resource_destruct(struct dcn351_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1799,6 +1917,7 @@ static struct resource_funcs dcn351_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn351_validate_bandwidth,
 	.calculate_wm_and_dlg = NULL,
@@ -1885,6 +2004,8 @@ static bool dcn351_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
index eb597be989a6..4bab31fa2b96 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
@@ -37,6 +37,8 @@
 #include "dcn30/dcn30_afmt.h"
 #include "dcn31/dcn31_dio_link_encoder.h"
 #include "dcn35/dcn35_dio_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -139,6 +141,10 @@ enum dcn36_clk_src_array_id {
 	REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)\
+	REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
+		reg ## block ## id ## _ ## reg_name
+
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)\
 	REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
@@ -338,6 +344,35 @@ static const struct dcn10_link_enc_mask le_mask = {
 	//DPCS_DCN31_MASK_SH_LIST(_MASK)
 };
 
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6)
+
+#define hpo_frl_stream_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn30_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN3_0_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_init(id)\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -672,6 +707,7 @@ static const struct resource_caps res_cap_dcn36 = {
 	.num_audio = 5,
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 2,
 	.num_pll = 4,/*1 c10 edp, 3xc20 combo PHY*/
@@ -1324,6 +1360,72 @@ static struct stream_encoder *dcn35_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn31_hpo_frl_stream_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_stream_encoder *hpo_enc3;
+	struct vpg *vpg;
+	struct afmt *afmt;
+	int vpg_inst;
+	int afmt_inst;
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(6);
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 5;
+		afmt_inst = 5;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn31_vpg_create(ctx, vpg_inst);
+	afmt = dcn31_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc3 || !vpg || !afmt) {
+		kfree(hpo_enc3);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn30_hpo_frl_stream_encoder_construct(hpo_enc3, ctx, ctx->dc_bios,
+					eng_id, vpg, afmt,
+					&hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc3->base;
+}
+
+static struct hpo_frl_link_encoder *dcn31_hpo_frl_link_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_enc3;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_enc3 = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_enc3)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_enc3, ctx, eng_id-ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_enc3->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn31_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1425,6 +1527,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn31_create_audio,
 	.create_stream_encoder = dcn35_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn31_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn31_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn31_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn36_hwseq_create,
@@ -1449,6 +1552,21 @@ static void dcn36_resource_destruct(struct dcn36_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN30_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+			kfree(DCN30_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1800,6 +1918,7 @@ static struct resource_funcs dcn36_res_pool_funcs = {
 	.link_enc_create_minimal = dcn31_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn31_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn31_panel_cntl_create,
 	.validate_bandwidth = dcn35_validate_bandwidth,
 	.calculate_wm_and_dlg = NULL,
@@ -1882,6 +2001,8 @@ static bool dcn36_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
+	dc->config.skip_frl_pretraining = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c
index 9498cbff2449..3fa82c4b7f92 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c
@@ -35,6 +35,8 @@
 #include "dcn30/dcn30_afmt.h"
 #include "dcn30/dcn30_dio_stream_encoder.h"
 #include "dcn401/dcn401_dio_stream_encoder.h"
+#include "dcn401/dcn401_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -126,6 +128,10 @@ enum dcn401_clk_src_array_id {
 	REG_STRUCT[id-1].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)\
+	REG_STRUCT[id - offset].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
+		reg ## block ## id ## _ ## reg_name
+
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)\
 	REG_STRUCT[index].reg_name = BASE(reg ## block ## id ## _ ## reg_name ## _BASE_IDX) + \
 		reg ## block ## id ## _ ## reg_name
@@ -313,6 +319,35 @@ static const struct dcn10_link_enc_mask le_mask = {
 };
 
 
+#define hpo_frl_stream_encoder_reg_list(id)\
+	DCN3_0_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+#define hpo_frl_stream_encoder_dme_reg_list(id)\
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 4)
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn401_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN401_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn401_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN401_HPO_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id)\
+		DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)
+};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)
+};
+
 #define hpo_dp_stream_encoder_reg_init(id)\
 	DCN3_1_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -657,6 +692,7 @@ static const struct resource_caps res_cap_dcn4_01 = {
 	.num_video_plane = 4,
 	.num_audio = 4,
 	.num_stream_encoder = 4,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 4,
 	.num_pll = 4,
@@ -1248,6 +1284,71 @@ static struct stream_encoder *dcn401_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn401_hpo_frl_stream_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn401_hpo_frl_stream_encoder *hpo_enc401;
+	struct vpg *vpg;
+	struct afmt *afmt;
+	int vpg_inst;
+	int afmt_inst;
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+	hpo_frl_stream_encoder_dme_reg_list(4);
+
+	/* Mapping of VPG, AFMT, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 4;
+		afmt_inst = 4;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG, AFMT sub-blocks */
+	hpo_enc401 = kzalloc(sizeof(struct dcn401_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn401_vpg_create(ctx, vpg_inst);
+	afmt = dcn401_afmt_create(ctx, afmt_inst);
+
+	if (!hpo_enc401 || !vpg || !afmt) {
+		kfree(hpo_enc401);
+		kfree(vpg);
+		kfree(afmt);
+		return NULL;
+	}
+
+	dcn401_hpo_frl_stream_encoder_construct(hpo_enc401, ctx, ctx->dc_bios,
+					eng_id, vpg, afmt,
+					&hpo_frl_stream_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc401->base;
+}
+
+static struct hpo_frl_link_encoder *dcn401_hpo_frl_link_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_link_enc;
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_link_enc)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_link_enc, ctx, eng_id-ENGINE_ID_HPO_0,
+					&hpo_frl_link_enc_regs[eng_id-ENGINE_ID_HPO_0],
+					&hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_link_enc->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn401_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1375,6 +1476,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn401_create_audio,
 	.create_stream_encoder = dcn401_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn401_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn401_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn401_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn401_hwseq_create,
@@ -1405,6 +1507,21 @@ static void dcn401_resource_destruct(struct dcn401_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN31_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+			if (pool->base.hpo_frl_stream_enc[i]->afmt != NULL) {
+				kfree(DCN30_AFMT_FROM_AFMT(pool->base.hpo_frl_stream_enc[i]->afmt));
+				pool->base.hpo_frl_stream_enc[i]->afmt = NULL;
+			}
+			kfree(DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1833,6 +1950,7 @@ static struct resource_funcs dcn401_res_pool_funcs = {
 	.destroy = dcn401_destroy_resource_pool,
 	.link_enc_create = dcn401_link_encoder_create,
 	.link_enc_create_minimal = NULL,
+	.hpo_frl_link_enc_create = dcn401_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn32_panel_cntl_create,
 	.validate_bandwidth = dcn401_validate_bandwidth,
 	.calculate_wm_and_dlg = NULL,
@@ -1970,6 +2088,7 @@ static bool dcn401_resource_construct(
 	dc->caps.max_slave_rgb_planes = 3;
 	dc->caps.post_blend_color_processing = true;
 	dc->caps.force_dp_tps4_for_cp2520 = true;
+	dc->caps.hdmi_hpo = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.edp_dsc_support = true;
@@ -2015,6 +2134,8 @@ static bool dcn401_resource_construct(
 	dc->caps.color.mpc.ogam_rom_caps.hlg = 0;
 	dc->caps.color.mpc.ocsc = 1;
 	dc->caps.color.mpc.preblend = true;
+	/* HACK: Force FRL support until BIOS is ready. */
+	dc->config.force_hdmi21_frl_enc_enable = true;
 	dc->config.use_spl = true;
 	dc->config.prefer_easf = true;
 
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
index 52a1996a654f..39a9724b9478 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
@@ -40,6 +40,9 @@
 #include "dcn31/dcn31_vpg.h"
 #include "dcn42/dcn42_dio_stream_encoder.h"
 #include "dcn42/dcn42_pg_cntl.h"
+#include "dcn401/dcn401_hpo_frl_stream_encoder.h"
+#include "dcn42/dcn42_hpo_frl_stream_encoder.h"
+#include "dcn30/dcn30_hpo_frl_link_encoder.h"
 #include "dcn31/dcn31_hpo_dp_stream_encoder.h"
 #include "dcn31/dcn31_hpo_dp_link_encoder.h"
 #include "dcn32/dcn32_hpo_dp_link_encoder.h"
@@ -140,6 +143,9 @@ enum dcn401_clk_src_array_id {
 	REG_STRUCT[id - 1].reg_name = BASE(reg##block##id##_##reg_name##_BASE_IDX) + \
 								  reg##block##id##_##reg_name
 
+#define SRI_ARR_DME(reg_name, block, id, offset)                                      \
+	REG_STRUCT[id - offset].reg_name = BASE(reg##block##id##_##reg_name##_BASE_IDX) + \
+									   reg##block##id##_##reg_name
 
 #define SRI_ARR_ALPHABET(reg_name, block, index, id)                            \
 	REG_STRUCT[index].reg_name = BASE(reg##block##id##_##reg_name##_BASE_IDX) + \
@@ -297,6 +303,34 @@ static const struct dcn10_link_enc_shift le_shift = {
 static const struct dcn10_link_enc_mask le_mask = {
 	LINK_ENCODER_MASK_SH_LIST_DCN42(_MASK)};
 
+#define hpo_frl_stream_encoder_reg_list(id) \
+	DCN42_HPO_FRL_STREAM_ENC_REG_LIST_RI(id)
+
+#define hpo_frl_stream_encoder_dme_reg_list(id) \
+	DCN3_0_HPO_STREAM_ENC_DME_REG_LIST_RI(id, 6)
+
+static struct dcn30_hpo_frl_stream_enc_registers hpo_frl_stream_enc_regs[2];
+
+static const struct dcn401_hpo_frl_stream_encoder_shift hpo_se_shift = {
+	DCN401_HPO_STREAM_ENC_MASK_SH_LIST(__SHIFT),
+	DCN42_HDMI_STREAM_ENC_MASK_SH_LIST(__SHIFT)
+};
+static const struct dcn401_hpo_frl_stream_encoder_mask hpo_se_mask = {
+	DCN401_HPO_STREAM_ENC_MASK_SH_LIST(_MASK),
+	DCN42_HDMI_STREAM_ENC_MASK_SH_LIST(_MASK)
+};
+
+#define hpo_frl_link_encoder_reg_list(id) \
+	DCN3_0_HPO_FRL_LINK_ENC_REG_LIST_RI(id)
+
+static struct dcn30_hpo_frl_link_encoder_registers hpo_frl_link_enc_regs[1];
+
+static const struct dcn30_hpo_frl_link_encoder_shift hpo_le_shift = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(__SHIFT)};
+
+static const struct dcn30_hpo_frl_link_encoder_mask hpo_le_mask = {
+	DCN3_0_HPO_FRL_LINK_ENC_MASK_SH_LIST(_MASK)};
+
 #define hpo_dp_stream_encoder_reg_init(id) \
 	DCN42_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
 
@@ -658,6 +692,7 @@ static const struct resource_caps res_cap_dcn42 = {
 	.num_stream_encoder = 5,
 	.num_dig_link_enc = 5,
 	.num_usb4_dpia = 6,
+	.num_hpo_frl = 1,
 	.num_hpo_dp_stream_encoder = 4,
 	.num_hpo_dp_link_encoder = 4,
 	.num_pll = 5,
@@ -1260,6 +1295,74 @@ static struct stream_encoder *dcn42_stream_encoder_create(
 	return &enc1->base;
 }
 
+static struct hpo_frl_stream_encoder *dcn42_hpo_frl_stream_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn42_hpo_frl_stream_encoder *hpo_enc42;
+	struct vpg *vpg;
+	struct apg *apg;
+
+	uint32_t vpg_inst;
+	uint32_t apg_inst;
+
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_stream_enc_regs
+	hpo_frl_stream_encoder_reg_list(0),
+		hpo_frl_stream_encoder_dme_reg_list(6);
+
+	/* Mapping of VPG, DME register blocks to HPO block instance */
+	if (eng_id == ENGINE_ID_HPO_0) {
+		vpg_inst = 9; /*hw hard wired to inst 9, ref to dcn header file*/
+		apg_inst = 9;
+	} else
+		return NULL;
+
+	/* allocate HPO stream encoder and create VPG sub-block */
+	hpo_enc42 = kzalloc(sizeof(struct dcn42_hpo_frl_stream_encoder), GFP_KERNEL);
+	vpg = dcn42_vpg_create(ctx, vpg_inst);
+	apg = dcn42_apg_create(ctx, apg_inst);
+
+	if (!hpo_enc42 || !vpg || !apg) {
+		kfree(hpo_enc42);
+		kfree(vpg);
+		kfree(apg);
+		return NULL;
+	}
+
+	dcn42_hpo_frl_stream_encoder_construct(hpo_enc42, ctx, ctx->dc_bios,
+			eng_id, vpg, apg,
+			&hpo_frl_stream_enc_regs[eng_id - ENGINE_ID_HPO_0],
+			&hpo_se_shift, &hpo_se_mask);
+
+	return &hpo_enc42->base;
+}
+
+static struct hpo_frl_link_encoder *dcn42_hpo_frl_link_encoder_create(
+	enum engine_id eng_id,
+	struct dc_context *ctx)
+{
+	struct dcn30_hpo_frl_link_encoder *hpo_link_enc;
+
+	ASSERT((eng_id == ENGINE_ID_HPO_0) || (eng_id == ENGINE_ID_HPO_1));
+
+#undef REG_STRUCT
+#define REG_STRUCT hpo_frl_link_enc_regs
+	hpo_frl_link_encoder_reg_list(0);
+
+	/* allocate HPO link encoder */
+	hpo_link_enc = kzalloc(sizeof(struct dcn30_hpo_frl_link_encoder), GFP_KERNEL);
+	if (!hpo_link_enc)
+		return NULL; /* out of memory */
+
+	hpo_frl_link_encoder3_construct(hpo_link_enc, ctx, eng_id - ENGINE_ID_HPO_0,
+			&hpo_frl_link_enc_regs[eng_id - ENGINE_ID_HPO_0],
+			&hpo_le_shift, &hpo_le_mask);
+
+	return &hpo_link_enc->base;
+}
+
 static struct hpo_dp_stream_encoder *dcn42_hpo_dp_stream_encoder_create(
 	enum engine_id eng_id,
 	struct dc_context *ctx)
@@ -1365,6 +1468,7 @@ static const struct resource_create_funcs res_create_funcs = {
 	.read_dce_straps = read_dce_straps,
 	.create_audio = dcn42_create_audio,
 	.create_stream_encoder = dcn42_stream_encoder_create,
+	.create_hpo_frl_stream_encoder = dcn42_hpo_frl_stream_encoder_create,
 	.create_hpo_dp_stream_encoder = dcn42_hpo_dp_stream_encoder_create,
 	.create_hpo_dp_link_encoder = dcn42_hpo_dp_link_encoder_create,
 	.create_hwseq = dcn42_hwseq_create,
@@ -1395,6 +1499,21 @@ static void dcn42_resource_destruct(struct dcn42_resource_pool *pool)
 		}
 	}
 
+	for (i = 0; i < pool->base.hpo_frl_stream_enc_count; i++) {
+		if (pool->base.hpo_frl_stream_enc[i] != NULL) {
+			if (pool->base.hpo_frl_stream_enc[i]->vpg != NULL) {
+				kfree(DCN31_VPG_FROM_VPG(pool->base.hpo_frl_stream_enc[i]->vpg));
+				pool->base.hpo_frl_stream_enc[i]->vpg = NULL;
+			}
+			if (pool->base.hpo_frl_stream_enc[i]->apg != NULL) {
+				kfree(DCN31_APG_FROM_APG(pool->base.hpo_frl_stream_enc[i]->apg));
+				pool->base.hpo_frl_stream_enc[i]->apg = NULL;
+			}
+			kfree(DCN401_HPO_FRL_STRENC_FROM_HPO_FRL_STRENC(pool->base.hpo_frl_stream_enc[i]));
+			pool->base.hpo_frl_stream_enc[i] = NULL;
+		}
+	}
+
 	for (i = 0; i < pool->base.hpo_dp_stream_enc_count; i++) {
 		if (pool->base.hpo_dp_stream_enc[i] != NULL) {
 			if (pool->base.hpo_dp_stream_enc[i]->vpg != NULL) {
@@ -1791,6 +1910,7 @@ static struct resource_funcs dcn42_res_pool_funcs = {
 	.link_enc_create_minimal = dcn42_link_enc_create_minimal,
 	.link_encs_assign = link_enc_cfg_link_encs_assign,
 	.link_enc_unassign = link_enc_cfg_link_enc_unassign,
+	.hpo_frl_link_enc_create = dcn42_hpo_frl_link_encoder_create,
 	.panel_cntl_create = dcn32_panel_cntl_create,
 	.validate_bandwidth = dcn42_validate_bandwidth,
 	.calculate_wm_and_dlg = NULL,
@@ -1909,6 +2029,7 @@ static bool dcn42_resource_construct(
 	dc->caps.force_dp_tps4_for_cp2520 = true;
 	if (dc->config.forceHBR2CP2520)
 		dc->caps.force_dp_tps4_for_cp2520 = false;
+	dc->caps.hdmi_hpo = true;
 	dc->caps.dp_hdmi21_pcon_support = true;
 	dc->caps.dp_hpo = true;
 	dc->caps.edp_dsc_support = true;
-- 
2.54.0


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

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

Reply instructions:

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

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

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

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

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

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

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).