Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 08/11] drm/mediatek: mtk_dp: Move max link rate to SoC specific data
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	jitao.shi, granquet, rex-bc.chen, dmitry.osipenko, ck.hu,
	amergnat, justin.yeh, jason-jh.lin, dri-devel, linux-mediatek,
	devicetree, linux-kernel, linux-arm-kernel, kernel
In-Reply-To: <20260701122024.19557-1-angelogioacchino.delregno@collabora.com>

In preparation for adding support for the eDP IP found in the
MT8196 SoC, having a higher supported maximum link rate, move
this parameter to SoC data instead of statically assigning it
to the training info during initialization.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index bf3a4b15f3ec..5272e717bfda 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -167,6 +167,7 @@ struct mtk_dp_data {
 	bool audio_supported;
 	bool audio_pkt_in_hblank_area;
 	u16 audio_m_div2_bit;
+	u8 hw_max_link_rate;
 };
 
 static const struct mtk_dp_efuse_fmt mt8188_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
@@ -1481,7 +1482,7 @@ static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
 {
 	bool plugged_in = (mtk_dp->bridge.type == DRM_MODE_CONNECTOR_eDP);
 
-	mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
+	mtk_dp->train_info.link_rate = mtk_dp->data->hw_max_link_rate;
 	mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
 	mtk_dp->train_info.cable_plugged_in = plugged_in;
 
@@ -3013,6 +3014,7 @@ static const struct mtk_dp_data mt8188_dp_data = {
 	.audio_supported = true,
 	.audio_pkt_in_hblank_area = true,
 	.audio_m_div2_bit = MT8188_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+	.hw_max_link_rate = DP_LINK_BW_5_4,
 };
 
 static const struct mtk_dp_data mt8195_edp_data = {
@@ -3021,6 +3023,7 @@ static const struct mtk_dp_data mt8195_edp_data = {
 	.efuse_fmt = mt8195_edp_efuse_fmt,
 	.audio_supported = false,
 	.audio_m_div2_bit = MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+	.hw_max_link_rate = DP_LINK_BW_5_4,
 };
 
 static const struct mtk_dp_data mt8195_dp_data = {
@@ -3029,6 +3032,7 @@ static const struct mtk_dp_data mt8195_dp_data = {
 	.efuse_fmt = mt8195_dp_efuse_fmt,
 	.audio_supported = true,
 	.audio_m_div2_bit = MT8195_AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+	.hw_max_link_rate = DP_LINK_BW_5_4,
 };
 
 static const struct of_device_id mtk_dp_of_match[] = {
-- 
2.54.0



^ permalink raw reply related

* [PATCH 11/11] drm/mediatek: mtk_dp: Clarify XTAL freq and Debounce registers
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	jitao.shi, granquet, rex-bc.chen, dmitry.osipenko, ck.hu,
	amergnat, justin.yeh, jason-jh.lin, dri-devel, linux-mediatek,
	devicetree, linux-kernel, linux-arm-kernel, kernel
In-Reply-To: <20260701122024.19557-1-angelogioacchino.delregno@collabora.com>

Clarify the meaning of the sub-fields in the crystal frequency and
transmitter interrupt debounce registers, with the only purpose of
increasing human readability.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dp.c     | 35 ++++++++++++++++-----------
 drivers/gpu/drm/mediatek/mtk_dp_reg.h |  6 +++--
 2 files changed, 25 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index 605ead7fa9bf..b861a5df9316 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -1204,9 +1204,15 @@ static void mtk_dp_initialize_settings_v2p1(struct mtk_dp *mtk_dp)
 
 static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
 {
+	/*
+	 * Set DP XTAL freq to 26MHz and accumulator to 1.
+	 * Valid values for XTAL freq are 12 and 26.
+	 */
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
-			   XTAL_FREQ_DP_TRANS_P0_DEFAULT,
-			   XTAL_FREQ_DP_TRANS_P0_MASK);
+			   FIELD_PREP_CONST(XTAL_FREQ_DP_ACCUM_NUM_MASK, 1) |
+			   FIELD_PREP_CONST(XTAL_FREQ_DP_CLOCK_MHZ_MASK, 26),
+			   XTAL_FREQ_DP_ACCUM_NUM_MASK | XTAL_FREQ_DP_CLOCK_MHZ_MASK);
+
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
 			   FEC_CLOCK_EN_MODE_DP_TRANS_P0,
 			   FEC_CLOCK_EN_MODE_DP_TRANS_P0);
@@ -1248,28 +1254,29 @@ static void mtk_dp_initialize_aux_hpd_detect_settings(struct mtk_dp *mtk_dp)
 
 static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
 {
-	u32 val;
-
 	/* Mask AUX TOP interrupt, as this uses transmitter for HPD */
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
 			   AUX_TOP_IRQ_MSK, AUX_TOP_IRQ_MSK);
 
-	/* Debounce threshold */
+	/* Set Hotplug debounce threshold time: xtal_period * deb_thd */
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
 			   8, HPD_DEB_THD_DP_TRANS_P0_MASK);
 
-	val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
-	       HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
-	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
-			   val, HPD_INT_THD_DP_TRANS_P0_MASK);
-
 	/*
-	 * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
-	 * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+	 * Set interrupt debounce threshold time
+	 * VAL  |  0     1     2     3  INT_THD
+	 * Low  | 100   300   500   700 uS
+	 * High | 700   900  1100  1300 uS
 	 */
-	val = (5 << 8) | (5 << 12);
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
-			   val,
+			   FIELD_PREP_CONST(HPD_INT_LOW_THD_DP_TRANS_P0_MASK, 2) |
+			   FIELD_PREP_CONST(HPD_INT_HIGH_THD_DP_TRANS_P0_MASK, 2),
+			   HPD_INT_THD_DP_TRANS_P0_MASK);
+
+	/* Connection and Disconnection thresholds: 1.5ms + (5 x 0.1) ms = 2ms */
+	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+			   FIELD_PREP_CONST(HPD_DISC_THD_DP_TRANS_P0_MASK, 5) |
+			   FIELD_PREP_CONST(HPD_CONN_THD_DP_TRANS_P0_MASK, 5),
 			   HPD_DISC_THD_DP_TRANS_P0_MASK |
 			   HPD_CONN_THD_DP_TRANS_P0_MASK);
 	mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
index 9caf743f4fe6..418a2318c656 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -253,6 +253,8 @@
 #define MTK_DP_TRANS_P0_3410				0x1410
 #define HPD_DEB_THD_DP_TRANS_P0_MASK				GENMASK(3, 0)
 #define HPD_INT_THD_DP_TRANS_P0_MASK				GENMASK(7, 4)
+#define HPD_INT_HIGH_THD_DP_TRANS_P0_MASK			GENMASK(3, 2)
+#define HPD_INT_LOW_THD_DP_TRANS_P0_MASK			GENMASK(1, 0)
 #define HPD_INT_THD_DP_TRANS_P0_LOWER_500US			(2 << 4)
 #define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US			(2 << 6)
 #define HPD_DISC_THD_DP_TRANS_P0_MASK				GENMASK(11, 8)
@@ -267,8 +269,8 @@
 #define IRQ_MASK_DP_TRANS_P0_INT_IRQ				(BIT(3) << 4)
 #define IRQ_STATUS_DP_TRANS_P0_MASK				GENMASK(15, 12)
 #define MTK_DP_TRANS_P0_342C				0x142c
-#define XTAL_FREQ_DP_TRANS_P0_DEFAULT				(BIT(0) | BIT(3) | BIT(5) | BIT(6))
-#define XTAL_FREQ_DP_TRANS_P0_MASK				GENMASK(7, 0)
+#define XTAL_FREQ_DP_ACCUM_NUM_MASK				GENMASK(1, 0)
+#define XTAL_FREQ_DP_CLOCK_MHZ_MASK				GENMASK(7, 2)
 #define MTK_DP_TRANS_P0_3430				0x1430
 #define HPD_INT_THD_ECO_DP_TRANS_P0_MASK			GENMASK(1, 0)
 #define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT		BIT(1)
-- 
2.54.0



^ permalink raw reply related

* [PATCH 03/42] drm/mediatek: Export OVL Blend function
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel, Paul-pl Chen,
	Nancy Lin
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

From: Paul-pl Chen <paul-pl.chen@mediatek.com>

For the new BLENDER component, the OVL ignore pixel alpha logic
should be exported as a function and reused it.

Signed-off-by: Nancy Lin <nancy.lin@mediatek.com>
Signed-off-by: Paul-pl Chen <paul-pl.chen@mediatek.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 68 +++++++++++++++++--------
 drivers/gpu/drm/mediatek/mtk_disp_ovl.h |  8 +++
 2 files changed, 56 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 9ded20202191..fa4607304acb 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -215,6 +215,23 @@ void mtk_ovl_disable_vblank(struct device *dev)
 	writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_INTEN);
 }
 
+bool mtk_ovl_is_ignore_pixel_alpha(struct mtk_plane_state *state, unsigned int blend_mode)
+{
+	if (!state->base.fb)
+		return false;
+
+	/*
+	 * Although the alpha channel can be ignored, CONST_BLD must be enabled
+	 * for XRGB format, otherwise OVL will still read the value from memory.
+	 * For RGB888 related formats, whether CONST_BLD is enabled or not won't
+	 * affect the result. Therefore we use !has_alpha as the condition.
+	 */
+	if (blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !state->base.fb->format->has_alpha)
+		return true;
+
+	return false;
+}
+
 u32 mtk_ovl_get_blend_modes(struct device *dev)
 {
 	struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
@@ -401,6 +418,29 @@ void mtk_ovl_layer_off(struct device *dev, unsigned int idx,
 		      DISP_REG_OVL_RDMA_CTRL(idx));
 }
 
+unsigned int mtk_ovl_get_blend_mode(struct mtk_plane_state *state, unsigned int blend_modes)
+{
+	unsigned int blend_mode = DRM_MODE_BLEND_COVERAGE;
+
+	/*
+	 * For the platforms where OVL_CON_CLRFMT_MAN is defined in the hardware data sheet
+	 * and supports premultiplied color formats, such as OVL_CON_CLRFMT_PARGB888
+	 * and supports premultiplied color formats, such as OVL_CON_CLRFMT_PARGB8888.
+	 *
+	 * Check blend_modes in the driver data to see if premultiplied mode is supported.
+	 * If not, use coverage mode instead to set it to the supported color formats.
+	 *
+	 * Current DRM assumption is that alpha is default premultiplied, so the bitmask of
+	 * blend_modes must include BIT(DRM_MODE_BLEND_PREMULTI). Otherwise, mtk_plane_init()
+	 * will get an error return from drm_plane_create_blend_mode_property() and
+	 * state->base.pixel_blend_mode should not be used.
+	 */
+	if (blend_modes & BIT(DRM_MODE_BLEND_PREMULTI))
+		blend_mode = state->base.pixel_blend_mode;
+
+	return blend_mode;
+}
+
 unsigned int mtk_ovl_fmt_convert(unsigned int fmt, unsigned int blend_mode,
 				 bool fmt_rgb565_is_0, bool color_convert,
 				 u8 clrfmt_shift, u32 clrfmt_man, u32 byte_swap, u32 rgb_swap)
@@ -529,7 +569,7 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 	unsigned int rotation = pending->rotation;
 	unsigned int offset = (pending->y << 16) | pending->x;
 	unsigned int src_size = (pending->height << 16) | pending->width;
-	unsigned int blend_mode = state->base.pixel_blend_mode;
+	unsigned int blend_mode = mtk_ovl_get_blend_mode(state, ovl->data->blend_modes);
 	unsigned int ignore_pixel_alpha = 0;
 	unsigned int con;
 
@@ -554,17 +594,8 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 		 * For blend_modes supported SoCs, always enable alpha blending.
 		 * For blend_modes unsupported SoCs, enable alpha blending when has_alpha is set.
 		 */
-		if (blend_mode || state->base.fb->format->has_alpha)
+		if (state->base.pixel_blend_mode || state->base.fb->format->has_alpha)
 			con |= OVL_CON_AEN;
-
-		/*
-		 * Although the alpha channel can be ignored, CONST_BLD must be enabled
-		 * for XRGB format, otherwise OVL will still read the value from memory.
-		 * For RGB888 related formats, whether CONST_BLD is enabled or not won't
-		 * affect the result. Therefore we use !has_alpha as the condition.
-		 */
-		if (blend_mode == DRM_MODE_BLEND_PIXEL_NONE || !state->base.fb->format->has_alpha)
-			ignore_pixel_alpha = OVL_CONST_BLEND;
 	}
 
 	/*
@@ -590,6 +621,9 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 
 	mtk_ddp_write_relaxed(cmdq_pkt, con, &ovl->cmdq_reg, ovl->regs,
 			      DISP_REG_OVL_CON(idx));
+
+	if (mtk_ovl_is_ignore_pixel_alpha(state, blend_mode))
+		ignore_pixel_alpha = OVL_CONST_BLEND;
 	mtk_ddp_write_relaxed(cmdq_pkt, pitch_lsb | ignore_pixel_alpha,
 			      &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH(idx));
 	mtk_ddp_write_relaxed(cmdq_pkt, src_size, &ovl->cmdq_reg, ovl->regs,
@@ -754,9 +788,7 @@ static const struct mtk_disp_ovl_data mt8192_ovl_driver_data = {
 	.layer_nr = 4,
 	.fmt_rgb565_is_0 = true,
 	.smi_id_en = true,
-	.blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) |
-		       BIT(DRM_MODE_BLEND_COVERAGE) |
-		       BIT(DRM_MODE_BLEND_PIXEL_NONE),
+	.blend_modes = MTK_OVL_SUPPORT_BLEND_MODES,
 	.formats = mt8173_ovl_formats,
 	.num_formats = mt8173_ovl_formats_len,
 };
@@ -767,9 +799,7 @@ static const struct mtk_disp_ovl_data mt8192_ovl_2l_driver_data = {
 	.layer_nr = 2,
 	.fmt_rgb565_is_0 = true,
 	.smi_id_en = true,
-	.blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) |
-		       BIT(DRM_MODE_BLEND_COVERAGE) |
-		       BIT(DRM_MODE_BLEND_PIXEL_NONE),
+	.blend_modes = MTK_OVL_SUPPORT_BLEND_MODES,
 	.formats = mt8173_ovl_formats,
 	.num_formats = mt8173_ovl_formats_len,
 };
@@ -781,9 +811,7 @@ static const struct mtk_disp_ovl_data mt8195_ovl_driver_data = {
 	.fmt_rgb565_is_0 = true,
 	.smi_id_en = true,
 	.supports_afbc = true,
-	.blend_modes = BIT(DRM_MODE_BLEND_PREMULTI) |
-		       BIT(DRM_MODE_BLEND_COVERAGE) |
-		       BIT(DRM_MODE_BLEND_PIXEL_NONE),
+	.blend_modes = MTK_OVL_SUPPORT_BLEND_MODES,
 	.formats = mt8195_ovl_formats,
 	.num_formats = mt8195_ovl_formats_len,
 	.supports_clrfmt_ext = true,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.h b/drivers/gpu/drm/mediatek/mtk_disp_ovl.h
index 919498496bb6..419c2136006c 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.h
@@ -6,12 +6,20 @@
 #ifndef __MTK_DISP_OVL_H__
 #define __MTK_DISP_OVL_H__
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 
+#define MTK_OVL_SUPPORT_BLEND_MODES \
+	 (BIT(DRM_MODE_BLEND_PREMULTI) | \
+	  BIT(DRM_MODE_BLEND_COVERAGE) | \
+	  BIT(DRM_MODE_BLEND_PIXEL_NONE))
+
 extern const u32 mt8195_ovl_formats[];
 extern const size_t mt8195_ovl_formats_len;
 
 bool mtk_ovl_is_10bit_rgb(unsigned int fmt);
+bool mtk_ovl_is_ignore_pixel_alpha(struct mtk_plane_state *state, unsigned int blend_mode);
+unsigned int mtk_ovl_get_blend_mode(struct mtk_plane_state *state, unsigned int blend_modes);
 unsigned int mtk_ovl_fmt_convert(unsigned int fmt, unsigned int blend_mode,
 				 bool fmt_rgb565_is_0, bool color_convert,
 				 u8 clrfmt_shift, u32 clrfmt_man, u32 byte_swap, u32 rgb_swap);
-- 
2.54.0



^ permalink raw reply related

* [PATCH 08/42] drm/mediatek: De-duplicate internal component checks
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Both mtk_ddp_comp and mtk_drm_drv are performing similar checks
to distinguish components that have an external driver from ones
that don't - with the exception of mtk_ddp_comp also checking if
the component is related to a backlight driver.

Create helper functions in the main component management driver
mtk_ddp_comp, one to check if the currently checked component is
internal/simple, and one (static, internal to mtk_ddp_comp only)
to check if it is a backlight related one.

Since the amount of internal/simple components is lower than the
amount of ones having a specific driver, and since it is expected
that most of (if not all) of any new hardware support will need a
specific driver as well, the helpers are doing an inverted check
compared to what the code was doing before, as both mtk_ddp_comp
and mtk_drm_drv were checking for external component explicitly,
while now they're doing the same, but checking if NOT internal.

Also, while at it, for the OVL_ADAPTOR components case, instead
of checking for MTK_DISP_OVL_ADAPTOR, call the already provided
mtk_ovl_adaptor_is_comp_present() function to check for this type
of component.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c | 47 ++++++++++++++-----------
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h |  1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  | 16 ++-------
 3 files changed, 30 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 2ba2123238b3..f7103e20abe5 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -629,6 +629,27 @@ static void mtk_ddp_comp_clk_put(void *_clk)
 	clk_put(clk);
 }
 
+static bool mtk_ddp_comp_is_backlight_comp(enum mtk_ddp_comp_type type)
+{
+	return type == MTK_DISP_BLS || type == MTK_DISP_PWM;
+}
+
+bool mtk_ddp_comp_is_internal_comp(enum mtk_ddp_comp_type type)
+{
+	switch (type) {
+	case MTK_DISP_DITHER:
+	case MTK_DISP_OD:
+	case MTK_DISP_POSTMASK:
+	case MTK_DISP_RSZ:
+	case MTK_DISP_UFOE:
+		return true;
+	default:
+		break;
+	};
+
+	return false;
+}
+
 static int mtk_ddp_comp_init_internal_comp(struct device *dev, struct device *comp_dev)
 {
 	struct device_node *comp_node = comp_dev->of_node;
@@ -700,26 +721,12 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 	if (ret)
 		return ret;
 
-	if (type == MTK_DISP_AAL ||
-	    type == MTK_DISP_BLS ||
-	    type == MTK_DISP_CCORR ||
-	    type == MTK_DISP_COLOR ||
-	    type == MTK_DISP_DSC ||
-	    type == MTK_DISP_GAMMA ||
-	    type == MTK_DISP_MERGE ||
-	    type == MTK_DISP_OVL ||
-	    type == MTK_DISP_OVL_2L ||
-	    type == MTK_DISP_PWM ||
-	    type == MTK_DISP_RDMA ||
-	    type == MTK_DISP_WDMA ||
-	    type == MTK_DISP_DPI ||
-	    type == MTK_DISP_DP_INTF ||
-	    type == MTK_DISP_DSI)
-		goto end;
-
-	ret = mtk_ddp_comp_init_internal_comp(dev, comp->dev);
-	if (ret)
-		return ret;
+	/* If there's no external driver for this component, allocate and init now */
+	if (mtk_ddp_comp_is_internal_comp(type) || mtk_ddp_comp_is_backlight_comp(type)) {
+		ret = mtk_ddp_comp_init_internal_comp(dev, comp->dev);
+		if (ret)
+			return ret;
+	}
 end:
 	hash_add(hlist->ddp_list, &comp->lnode, comp->id);
 
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index 4203eecb2a7b..58a06add1368 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -351,6 +351,7 @@ static inline struct mtk_ddp_comp
 	return NULL;
 }
 
+bool mtk_ddp_comp_is_internal_comp(enum mtk_ddp_comp_type type);
 int mtk_ddp_comp_get_id(struct device_node *node,
 			enum mtk_ddp_comp_type comp_type);
 int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index e956e1966b86..18683aee61ff 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -1166,20 +1166,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		 * blocks have separate component platform drivers and initialize their own
 		 * DDP component structure. The others are initialized here.
 		 */
-		if (comp_type == MTK_DISP_AAL ||
-		    comp_type == MTK_DISP_CCORR ||
-		    comp_type == MTK_DISP_COLOR ||
-		    comp_type == MTK_DISP_DSC ||
-		    comp_type == MTK_DISP_GAMMA ||
-		    comp_type == MTK_DISP_MERGE ||
-		    comp_type == MTK_DISP_OVL ||
-		    comp_type == MTK_DISP_OVL_2L ||
-		    comp_type == MTK_DISP_OVL_ADAPTOR ||
-		    comp_type == MTK_DISP_RDMA ||
-		    comp_type == MTK_DISP_WDMA ||
-		    comp_type == MTK_DISP_DP_INTF ||
-		    comp_type == MTK_DISP_DPI ||
-		    comp_type == MTK_DISP_DSI) {
+		if (!mtk_ddp_comp_is_internal_comp(comp_type) &&
+		    !mtk_ovl_adaptor_is_comp_present(node)) {
 			dev_info(dev, "Adding component match for %pOF\n",
 				 node);
 			drm_of_component_match_add(dev, &match, component_compare_of,
-- 
2.54.0



^ permalink raw reply related

* [PATCH 12/42] drm/mediatek: ovl_adaptor: Add special MERGE component check
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

On the legacy ovl_adaptor for MT8195 and MT8188, four MERGE IPs
from VPPSYS are used, and those shall be exclusive to the OVL
Adaptor driver.

For this reason, add a branch in mtk_ovl_adaptor_is_comp_present()
to check if the component that is being checked is MERGE and, if
so, iterate through the list of component matches to understand if
it is one of those that must be exclusive to OVL Adaptor.

This resolves a corner case happening when mediatek-drm retries to
probe after a probe deferral, where all MERGE components would get
added to both "normal" and OVL Adaptor pipelines.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index ed9a2e35ba8a..bbf1d8ccda05 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -519,6 +519,17 @@ bool mtk_ovl_adaptor_is_comp_present(struct device_node *node)
 	if (type >= OVL_ADAPTOR_TYPE_NUM)
 		return false;
 
+	/* Check if this is one of the MERGE components used in OVL Adaptor */
+	if (type == OVL_ADAPTOR_TYPE_MERGE) {
+		int id = of_alias_get_id(node, private_comp_stem[type]);
+
+		for (int i = OVL_ADAPTOR_MERGE0; i <= OVL_ADAPTOR_MERGE3; i++)
+			if (comp_matches[i].alias_id == id)
+				return true;
+
+		return false;
+	}
+
 	/*
 	 * In the context of mediatek-drm, ETHDR, MDP_RDMA and Padding are
 	 * used exclusively by OVL Adaptor: if this component is not one of
-- 
2.54.0



^ permalink raw reply related

* [PATCH 20/42] drm/mediatek: mtk_dpi: Pass parameters with new mtk_dpi_sync structure
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

In preparation for moving common code between DPI and DVO into a
new mtk_dpi_common library, add a new mtk_dpi_sync structure which
contains all of the sync parameters, such as HSYNC and Left/Right
Odd and Even VSYNC.

This is now passed as parameter of mtk_dpi_set_display_mode() and
stack initialized in mtk_dpi_bridge_enable(): future preparation
changes will add abstraction for hardware specific configuration
which purpose will be to interpret the generic parameters to write
those to the hardware specific registers.

This brings no functional change.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c | 76 +++++++++++++++---------------
 1 file changed, 39 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index cc6d74ce8b5e..ecd13ea782c2 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -112,6 +112,14 @@ struct mtk_dpi_sync_param {
 	bool shift_half_line;
 };
 
+struct mtk_dpi_sync {
+	struct mtk_dpi_sync_param hsync;
+	struct mtk_dpi_sync_param vsync_l_odd;
+	struct mtk_dpi_sync_param vsync_l_even;
+	struct mtk_dpi_sync_param vsync_r_odd;
+	struct mtk_dpi_sync_param vsync_r_even;
+};
+
 struct mtk_dpi_yc_limit {
 	u16 y_top;
 	u16 y_bottom;
@@ -600,14 +608,10 @@ static void mtk_dpi_set_pixel_clk(struct mtk_dpi *dpi, struct videomode *vm, int
 }
 
 static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
+				    struct mtk_dpi_sync *sync,
 				    struct drm_display_mode *mode)
 {
 	struct mtk_dpi_polarities dpi_pol;
-	struct mtk_dpi_sync_param hsync;
-	struct mtk_dpi_sync_param vsync_lodd = { 0 };
-	struct mtk_dpi_sync_param vsync_leven = { 0 };
-	struct mtk_dpi_sync_param vsync_rodd = { 0 };
-	struct mtk_dpi_sync_param vsync_reven = { 0 };
 	struct videomode vm = { 0 };
 
 	drm_display_mode_to_videomode(mode, &vm);
@@ -624,42 +628,39 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 
 	/*
 	 * Depending on the IP version, we may output a different amount of
-	 * pixels for each iteration: divide the clock by this number and
-	 * adjust the display porches accordingly.
+	 * pixels for each iteration: adjust the display porches accordingly.
 	 */
-	hsync.sync_width = vm.hsync_len / dpi->conf->pixels_per_iter;
-	hsync.back_porch = vm.hback_porch / dpi->conf->pixels_per_iter;
-	hsync.front_porch = vm.hfront_porch / dpi->conf->pixels_per_iter;
-
-	hsync.shift_half_line = false;
-	vsync_lodd.sync_width = vm.vsync_len;
-	vsync_lodd.back_porch = vm.vback_porch;
-	vsync_lodd.front_porch = vm.vfront_porch;
-	vsync_lodd.shift_half_line = false;
-
-	if (vm.flags & DISPLAY_FLAGS_INTERLACED &&
-	    mode->flags & DRM_MODE_FLAG_3D_MASK) {
-		vsync_leven = vsync_lodd;
-		vsync_rodd = vsync_lodd;
-		vsync_reven = vsync_lodd;
-		vsync_leven.shift_half_line = true;
-		vsync_reven.shift_half_line = true;
-	} else if (vm.flags & DISPLAY_FLAGS_INTERLACED &&
-		   !(mode->flags & DRM_MODE_FLAG_3D_MASK)) {
-		vsync_leven = vsync_lodd;
-		vsync_leven.shift_half_line = true;
-	} else if (!(vm.flags & DISPLAY_FLAGS_INTERLACED) &&
-		   mode->flags & DRM_MODE_FLAG_3D_MASK) {
-		vsync_rodd = vsync_lodd;
+	sync->hsync.sync_width = vm.hsync_len / dpi->conf->pixels_per_iter;
+	sync->hsync.back_porch = vm.hback_porch / dpi->conf->pixels_per_iter;
+	sync->hsync.front_porch = vm.hfront_porch / dpi->conf->pixels_per_iter;
+	sync->hsync.shift_half_line = false;
+
+	sync->vsync_l_odd.sync_width = vm.vsync_len;
+	sync->vsync_l_odd.back_porch = vm.vback_porch;
+	sync->vsync_l_odd.front_porch = vm.vfront_porch;
+	sync->vsync_l_odd.shift_half_line = false;
+
+	if (vm.flags & DISPLAY_FLAGS_INTERLACED) {
+		sync->vsync_l_even = sync->vsync_l_odd;
+		sync->vsync_l_even.shift_half_line = true;
+
+		if (mode->flags & DRM_MODE_FLAG_3D_MASK) {
+			sync->vsync_r_odd = sync->vsync_l_odd;
+			sync->vsync_r_even = sync->vsync_l_odd;
+			sync->vsync_r_even.shift_half_line = true;
+		}
+	} else if (mode->flags & DRM_MODE_FLAG_3D_MASK) {
+		sync->vsync_r_odd = sync->vsync_l_odd;
 	}
+
 	mtk_dpi_sw_reset(dpi, true);
 	mtk_dpi_config_pol(dpi, &dpi_pol);
 
-	mtk_dpi_config_hsync(dpi, &hsync);
-	mtk_dpi_config_vsync_lodd(dpi, &vsync_lodd);
-	mtk_dpi_config_vsync_rodd(dpi, &vsync_rodd);
-	mtk_dpi_config_vsync_leven(dpi, &vsync_leven);
-	mtk_dpi_config_vsync_reven(dpi, &vsync_reven);
+	mtk_dpi_config_hsync(dpi, &sync->hsync);
+	mtk_dpi_config_vsync_lodd(dpi, &sync->vsync_l_odd);
+	mtk_dpi_config_vsync_rodd(dpi, &sync->vsync_r_odd);
+	mtk_dpi_config_vsync_leven(dpi, &sync->vsync_l_even);
+	mtk_dpi_config_vsync_reven(dpi, &sync->vsync_r_even);
 
 	mtk_dpi_config_3d(dpi, !!(mode->flags & DRM_MODE_FLAG_3D_MASK));
 	mtk_dpi_config_interface(dpi, !!(vm.flags &
@@ -863,12 +864,13 @@ static void mtk_dpi_bridge_disable(struct drm_bridge *bridge)
 static void mtk_dpi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+	struct mtk_dpi_sync sync = { 0 };
 
 	if (dpi->pinctrl && dpi->pins_dpi)
 		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
 
 	mtk_dpi_power_on(dpi);
-	mtk_dpi_set_display_mode(dpi, &dpi->mode);
+	mtk_dpi_set_display_mode(dpi, &sync, &dpi->mode);
 	mtk_dpi_enable(dpi);
 }
 
-- 
2.54.0



^ permalink raw reply related

* [PATCH 15/42] drm/mediatek: mtk_crtc: Complete documentation for struct mtk_crtc
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

The mtk_crtc structure was missing many member descriptions, for
which reason, a while ago it was demoted from kerneldoc to code
comment.

Add documentation for all of the missing structure members and
promote it back to kerneldoc.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_crtc.c | 33 +++++++++++++++++++----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 8656b56618a2..e543fae9c580 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -41,18 +41,31 @@ struct mtk_crtc_hw_layer {
 	u8 layer_stages_nr;
 };
 
-/*
+/**
  * struct mtk_crtc - MediaTek specific crtc structure.
- * @base: crtc object.
- * @enabled: records whether crtc_enable succeeded
+ * @base:            CRTC object
+ * @enabled:         Records whether crtc_enable succeeded
+ * @pending_needs_vblank: Records whether pending config operation needs a VBlank to finish
+ * @event:           VBlank event to signal upon completion of state update
  * @hwlayers:        Array of mtk_crtc_hw_layer structures, one for each overlay plane
  * @hwlayer_nr:      Number of hwlayers
- * @mmsys_dev: pointer to the mmsys device for configuration registers
- * @mutex: handle to one of the ten disp_mutex streams
- * @ddp_comp_nr: number of components in ddp_comp
- * @ddp_comp: array of pointers the mtk_ddp_comp structures used by this crtc
- *
- * TODO: Needs update: this header is missing a bunch of member descriptions.
+ * @pending_planes:  Planes pending atomic configuration operation
+ * @pending_async_planes: Planes pending asynchronous configuration operation
+ * @cmdq_client:     CMDQ Mailbox Client structure
+ * @cmdq_handle:     Handle to CMDQ Packet structure, used to send a packet
+ * @cmdq_event:      Bitmask of GCE Events that CMDQ listens to (to send packets on event)
+ * @cmdq_vblank_cnt: Number of VBlanks after which CMDQ packet sending operation times out
+ * @cb_blocking_queue: Wait queue for sending blocking command packet through CMDQ Mailbox
+ * @mmsys_dev:       Pointer to the MMSYS device for configuration registers
+ * @dma_dev:         Pointer to the DMA device (usually linked to an IOMMU)
+ * @mutex:           Pointer to the MediaTek MuteX device for HW triggers mute/unmuting
+ * @ddp_comp_nr:     Number of HW components in ddp_comp structure
+ * @ddp_comp:        Array of HW components used in one Display Controller pipeline
+ * @num_conn_routes: Number of alternative connection routes for a pipeline
+ * @conn_routes:     Array of HW components usable as alternative connection route
+ * @hw_lock:         Display HW access mutex lock
+ * @config_updating: Tracks whether an asynchronous config update operation is in progress
+ * @config_lock:     Spinlock to protect config_updating variable
  */
 struct mtk_crtc {
 	struct drm_crtc			base;
@@ -82,10 +95,8 @@ struct mtk_crtc {
 	unsigned int			num_conn_routes;
 	const struct mtk_drm_route	*conn_routes;
 
-	/* lock for display hardware access */
 	struct mutex			hw_lock;
 	bool				config_updating;
-	/* lock for config_updating to cmd buffer */
 	spinlock_t			config_lock;
 };
 
-- 
2.54.0



^ permalink raw reply related

* [PATCH 25/42] drm/mediatek: Pass mtk_ddp_comp in clk and config callbacks
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

In preparation for adding support for new Display Controller HW
components found in newer-gen SoCs like MT8196/MT8894, change the
.clk_enable(), .clk_disable(), and .config() callbacks to pass a
struct mtk_ddp_comp pointer instead of a struct device, and update
all of the currently supported components to reflect this change.

This is done because some of the new drivers need to track more in
the status of the actual components, such as indexing for multiple
stages layer strategy and others.

Despite being a rather big change in terms of lines, this brings
no functional differences.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c       | 22 +++---
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h       | 20 +++---
 drivers/gpu/drm/mediatek/mtk_disp_aal.c       | 12 ++--
 drivers/gpu/drm/mediatek/mtk_disp_ccorr.c     | 12 ++--
 drivers/gpu/drm/mediatek/mtk_disp_color.c     | 12 ++--
 drivers/gpu/drm/mediatek/mtk_disp_drv.h       | 70 +++++++++----------
 drivers/gpu/drm/mediatek/mtk_disp_dsc.c       |  8 +--
 drivers/gpu/drm/mediatek/mtk_disp_gamma.c     | 12 ++--
 drivers/gpu/drm/mediatek/mtk_disp_merge.c     | 18 ++---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c       | 12 ++--
 .../gpu/drm/mediatek/mtk_disp_ovl_adaptor.c   | 50 +++++++------
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c      | 12 ++--
 drivers/gpu/drm/mediatek/mtk_disp_wdma.c      | 12 ++--
 drivers/gpu/drm/mediatek/mtk_ethdr.c          | 10 +--
 drivers/gpu/drm/mediatek/mtk_ethdr.h          |  4 +-
 drivers/gpu/drm/mediatek/mtk_mdp_rdma.c       |  8 +--
 drivers/gpu/drm/mediatek/mtk_padding.c        |  8 +--
 17 files changed, 155 insertions(+), 147 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index f8687f161953..8b9228084828 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -109,16 +109,16 @@ void mtk_ddp_write_mask(struct cmdq_pkt *cmdq_pkt, unsigned int value,
 #endif
 }
 
-static int mtk_ddp_clk_enable(struct device *dev)
+static int mtk_ddp_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
+	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(priv->clk);
 }
 
-static void mtk_ddp_clk_disable(struct device *dev)
+static void mtk_ddp_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
+	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(priv->clk);
 }
@@ -149,11 +149,11 @@ void mtk_dither_set_common(void __iomem *regs, struct cmdq_client_reg *cmdq_reg,
 	}
 }
 
-static void mtk_dither_config(struct device *dev, unsigned int w,
+static void mtk_dither_config(struct mtk_ddp_comp *comp, unsigned int w,
 			      unsigned int h, unsigned int vrefresh,
 			      unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
+	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(comp->dev);
 
 	mtk_ddp_write(cmdq_pkt, w << 16 | h, &priv->cmdq_reg, priv->regs, DISP_REG_DITHER_SIZE);
 	mtk_ddp_write(cmdq_pkt, DITHER_RELAY_MODE, &priv->cmdq_reg, priv->regs,
@@ -185,15 +185,15 @@ static void mtk_dither_set(struct device *dev, unsigned int bpc,
 			      DISP_DITHERING, cmdq_pkt);
 }
 
-static void mtk_od_config(struct device *dev, unsigned int w,
+static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
 			  unsigned int h, unsigned int vrefresh,
 			  unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
+	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(comp->dev);
 
 	mtk_ddp_write(cmdq_pkt, w << 16 | h, &priv->cmdq_reg, priv->regs, DISP_REG_OD_SIZE);
 	mtk_ddp_write(cmdq_pkt, OD_RELAYMODE, &priv->cmdq_reg, priv->regs, DISP_REG_OD_CFG);
-	mtk_dither_set(dev, bpc, DISP_REG_OD_CFG, cmdq_pkt);
+	mtk_dither_set(comp->dev, bpc, DISP_REG_OD_CFG, cmdq_pkt);
 }
 
 static void mtk_od_start(struct device *dev)
@@ -203,11 +203,11 @@ static void mtk_od_start(struct device *dev)
 	writel(1, priv->regs + DISP_REG_OD_EN);
 }
 
-static void mtk_postmask_config(struct device *dev, unsigned int w,
+static void mtk_postmask_config(struct mtk_ddp_comp *comp, unsigned int w,
 				unsigned int h, unsigned int vrefresh,
 				unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(dev);
+	struct mtk_ddp_comp_dev *priv = dev_get_drvdata(comp->dev);
 
 	mtk_ddp_write(cmdq_pkt, w << 16 | h, &priv->cmdq_reg, priv->regs,
 		      DISP_REG_POSTMASK_SIZE);
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index ab9d5e4dfb98..1297db252821 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -36,9 +36,9 @@ struct mtk_drm_comp_list {
 struct mtk_ddp_comp_funcs {
 	int (*power_on)(struct device *dev);
 	void (*power_off)(struct device *dev);
-	int (*clk_enable)(struct device *dev);
-	void (*clk_disable)(struct device *dev);
-	void (*config)(struct device *dev, unsigned int w,
+	int (*clk_enable)(struct mtk_ddp_comp *comp);
+	void (*clk_disable)(struct mtk_ddp_comp *comp);
+	void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
 		       unsigned int h, unsigned int vrefresh,
 		       unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 	void (*dsc_setup)(struct device *dev, struct drm_dsc_config *dsc);
@@ -75,8 +75,8 @@ struct mtk_ddp_comp_funcs {
 			struct mtk_ddp_comp *next);
 	void (*disconnect)(struct mtk_ddp_comp *comp, struct device *mmsys_dev,
 			   struct mtk_ddp_comp *next);
-	void (*add)(struct device *dev, struct mtk_mutex *mutex);
-	void (*remove)(struct device *dev, struct mtk_mutex *mutex);
+	void (*add)(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex);
+	void (*remove)(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex);
 	unsigned int (*encoder_index)(struct device *dev);
 	enum drm_mode_status (*mode_valid)(struct device *dev, const struct drm_display_mode *mode);
 };
@@ -113,7 +113,7 @@ static inline void mtk_ddp_comp_power_off(struct mtk_ddp_comp *comp)
 static inline int mtk_ddp_comp_clk_enable(struct mtk_ddp_comp *comp)
 {
 	if (comp->funcs && comp->funcs->clk_enable)
-		return comp->funcs->clk_enable(comp->dev);
+		return comp->funcs->clk_enable(comp);
 
 	return 0;
 }
@@ -121,7 +121,7 @@ static inline int mtk_ddp_comp_clk_enable(struct mtk_ddp_comp *comp)
 static inline void mtk_ddp_comp_clk_disable(struct mtk_ddp_comp *comp)
 {
 	if (comp->funcs && comp->funcs->clk_disable)
-		comp->funcs->clk_disable(comp->dev);
+		comp->funcs->clk_disable(comp);
 }
 
 static inline
@@ -139,7 +139,7 @@ static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
 				       struct cmdq_pkt *cmdq_pkt)
 {
 	if (comp->funcs && comp->funcs->config)
-		comp->funcs->config(comp->dev, w, h, vrefresh, bpc, cmdq_pkt);
+		comp->funcs->config(comp, w, h, vrefresh, bpc, cmdq_pkt);
 }
 
 static inline void mtk_ddp_comp_dsc_setup(struct mtk_ddp_comp *comp,
@@ -314,7 +314,7 @@ static inline bool mtk_ddp_comp_is_afbc_supported(struct mtk_ddp_comp *comp)
 static inline bool mtk_ddp_comp_add(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex)
 {
 	if (comp->funcs && comp->funcs->add) {
-		comp->funcs->add(comp->dev, mutex);
+		comp->funcs->add(comp, mutex);
 		return true;
 	}
 	return false;
@@ -323,7 +323,7 @@ static inline bool mtk_ddp_comp_add(struct mtk_ddp_comp *comp, struct mtk_mutex
 static inline bool mtk_ddp_comp_remove(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex)
 {
 	if (comp->funcs && comp->funcs->remove) {
-		comp->funcs->remove(comp->dev, mutex);
+		comp->funcs->remove(comp, mutex);
 		return true;
 	}
 	return false;
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_aal.c b/drivers/gpu/drm/mediatek/mtk_disp_aal.c
index abc9e5525d03..5199354e7b51 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_aal.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_aal.c
@@ -50,25 +50,25 @@ struct mtk_disp_aal {
 	const struct mtk_disp_aal_data *data;
 };
 
-int mtk_aal_clk_enable(struct device *dev)
+int mtk_aal_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_aal *aal = dev_get_drvdata(dev);
+	struct mtk_disp_aal *aal = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(aal->clk);
 }
 
-void mtk_aal_clk_disable(struct device *dev)
+void mtk_aal_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_aal *aal = dev_get_drvdata(dev);
+	struct mtk_disp_aal *aal = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(aal->clk);
 }
 
-void mtk_aal_config(struct device *dev, unsigned int w,
+void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
 			   unsigned int h, unsigned int vrefresh,
 			   unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_aal *aal = dev_get_drvdata(dev);
+	struct mtk_disp_aal *aal = dev_get_drvdata(comp->dev);
 	u32 sz;
 
 	sz = FIELD_PREP(DISP_AAL_SIZE_HSIZE, w);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c b/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c
index 6d7bf4afa78d..81ce82ba6a9f 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c
@@ -40,25 +40,25 @@ struct mtk_disp_ccorr {
 	const struct mtk_disp_ccorr_data	*data;
 };
 
-int mtk_ccorr_clk_enable(struct device *dev)
+int mtk_ccorr_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
+	struct mtk_disp_ccorr *ccorr = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(ccorr->clk);
 }
 
-void mtk_ccorr_clk_disable(struct device *dev)
+void mtk_ccorr_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
+	struct mtk_disp_ccorr *ccorr = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(ccorr->clk);
 }
 
-void mtk_ccorr_config(struct device *dev, unsigned int w,
+void mtk_ccorr_config(struct mtk_ddp_comp *comp, unsigned int w,
 			     unsigned int h, unsigned int vrefresh,
 			     unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
+	struct mtk_disp_ccorr *ccorr = dev_get_drvdata(comp->dev);
 
 	mtk_ddp_write(cmdq_pkt, w << 16 | h, &ccorr->cmdq_reg, ccorr->regs,
 		      DISP_CCORR_SIZE);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_color.c b/drivers/gpu/drm/mediatek/mtk_disp_color.c
index 39c7de4cdcc1..04ae5e528c34 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_color.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_color.c
@@ -43,25 +43,25 @@ struct mtk_disp_color {
 	const struct mtk_disp_color_data	*data;
 };
 
-int mtk_color_clk_enable(struct device *dev)
+int mtk_color_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_color *color = dev_get_drvdata(dev);
+	struct mtk_disp_color *color = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(color->clk);
 }
 
-void mtk_color_clk_disable(struct device *dev)
+void mtk_color_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_color *color = dev_get_drvdata(dev);
+	struct mtk_disp_color *color = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(color->clk);
 }
 
-void mtk_color_config(struct device *dev, unsigned int w,
+void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_color *color = dev_get_drvdata(dev);
+	struct mtk_disp_color *color = dev_get_drvdata(comp->dev);
 
 	mtk_ddp_write(cmdq_pkt, w, &color->cmdq_reg, color->regs, DISP_COLOR_WIDTH(color));
 	mtk_ddp_write(cmdq_pkt, h, &color->cmdq_reg, color->regs, DISP_COLOR_HEIGHT(color));
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index abdce23fab17..62daf5ade9d1 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -17,9 +17,9 @@
 
 struct drm_dsc_config;
 
-int mtk_aal_clk_enable(struct device *dev);
-void mtk_aal_clk_disable(struct device *dev);
-void mtk_aal_config(struct device *dev, unsigned int w,
+int mtk_aal_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_aal_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
 		    unsigned int h, unsigned int vrefresh,
 		    unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 unsigned int mtk_aal_gamma_get_lut_size(struct device *dev);
@@ -28,18 +28,18 @@ void mtk_aal_start(struct device *dev);
 void mtk_aal_stop(struct device *dev);
 
 void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state);
-int mtk_ccorr_clk_enable(struct device *dev);
-void mtk_ccorr_clk_disable(struct device *dev);
-void mtk_ccorr_config(struct device *dev, unsigned int w,
+int mtk_ccorr_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_ccorr_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_ccorr_config(struct mtk_ddp_comp *comp, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 void mtk_ccorr_start(struct device *dev);
 void mtk_ccorr_stop(struct device *dev);
 
 void mtk_color_bypass_shadow(struct device *dev);
-int mtk_color_clk_enable(struct device *dev);
-void mtk_color_clk_disable(struct device *dev);
-void mtk_color_config(struct device *dev, unsigned int w,
+int mtk_color_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_color_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 void mtk_color_start(struct device *dev);
@@ -52,8 +52,8 @@ void mtk_dpi_start(struct device *dev);
 void mtk_dpi_stop(struct device *dev);
 unsigned int mtk_dpi_encoder_index(struct device *dev);
 
-int mtk_dsc_clk_enable(struct device *dev);
-void mtk_dsc_clk_disable(struct device *dev);
+int mtk_dsc_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_dsc_clk_disable(struct mtk_ddp_comp *comp);
 void mtk_dsc_setup(struct device *dev, struct drm_dsc_config *dsc_cfg);
 void mtk_dsc_start(struct device *dev);
 void mtk_dsc_stop(struct device *dev);
@@ -67,9 +67,9 @@ void mtk_dvo_start(struct device *dev);
 void mtk_dvo_stop(struct device *dev);
 unsigned int mtk_dvo_encoder_index(struct device *dev);
 
-int mtk_gamma_clk_enable(struct device *dev);
-void mtk_gamma_clk_disable(struct device *dev);
-void mtk_gamma_config(struct device *dev, unsigned int w,
+int mtk_gamma_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_gamma_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 unsigned int mtk_gamma_get_lut_size(struct device *dev);
@@ -77,9 +77,9 @@ void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state);
 void mtk_gamma_start(struct device *dev);
 void mtk_gamma_stop(struct device *dev);
 
-int mtk_merge_clk_enable(struct device *dev);
-void mtk_merge_clk_disable(struct device *dev);
-void mtk_merge_config(struct device *dev, unsigned int width,
+int mtk_merge_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_merge_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_merge_config(struct mtk_ddp_comp *comp, unsigned int width,
 		      unsigned int height, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 void mtk_merge_start(struct device *dev);
@@ -95,9 +95,9 @@ enum drm_mode_status mtk_merge_mode_valid(struct device *dev,
 void mtk_ovl_bgclr_in_on(struct device *dev);
 void mtk_ovl_bgclr_in_off(struct device *dev);
 void mtk_ovl_bypass_shadow(struct device *dev);
-int mtk_ovl_clk_enable(struct device *dev);
-void mtk_ovl_clk_disable(struct device *dev);
-void mtk_ovl_config(struct device *dev, unsigned int w,
+int mtk_ovl_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_ovl_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
 		    unsigned int h, unsigned int vrefresh,
 		    unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 int mtk_ovl_layer_check(struct device *dev, unsigned int idx,
@@ -124,8 +124,8 @@ const u32 *mtk_ovl_get_formats(struct device *dev);
 size_t mtk_ovl_get_num_formats(struct device *dev);
 bool mtk_ovl_is_afbc_supported(struct device *dev);
 
-void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex);
-void mtk_ovl_adaptor_remove_comp(struct device *dev, struct mtk_mutex *mutex);
+void mtk_ovl_adaptor_add_comp(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex);
+void mtk_ovl_adaptor_remove_comp(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex);
 bool mtk_ovl_adaptor_is_comp_present(struct device_node *node);
 void mtk_ovl_adaptor_connect(struct mtk_ddp_comp *comp, struct device *mmsys_dev,
 			     struct mtk_ddp_comp *next);
@@ -133,9 +133,9 @@ void mtk_ovl_adaptor_disconnect(struct mtk_ddp_comp *comp, struct device *mmsys_
 				struct mtk_ddp_comp *next);
 int mtk_ovl_adaptor_power_on(struct device *dev);
 void mtk_ovl_adaptor_power_off(struct device *dev);
-int mtk_ovl_adaptor_clk_enable(struct device *dev);
-void mtk_ovl_adaptor_clk_disable(struct device *dev);
-void mtk_ovl_adaptor_config(struct device *dev, unsigned int w,
+int mtk_ovl_adaptor_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_ovl_adaptor_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_ovl_adaptor_config(struct mtk_ddp_comp *comp, unsigned int w,
 			    unsigned int h, unsigned int vrefresh,
 			    unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 void mtk_ovl_adaptor_layer_config(struct device *dev, unsigned int idx,
@@ -157,9 +157,9 @@ enum drm_mode_status mtk_ovl_adaptor_mode_valid(struct device *dev,
 						const struct drm_display_mode *mode);
 
 void mtk_rdma_bypass_shadow(struct device *dev);
-int mtk_rdma_clk_enable(struct device *dev);
-void mtk_rdma_clk_disable(struct device *dev);
-void mtk_rdma_config(struct device *dev, unsigned int width,
+int mtk_rdma_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_rdma_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
 		     unsigned int height, unsigned int vrefresh,
 		     unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 unsigned int mtk_rdma_layer_nr(struct device *dev, int pipeline_index);
@@ -179,8 +179,8 @@ size_t mtk_rdma_get_num_formats(struct device *dev);
 
 int mtk_mdp_rdma_power_on(struct device *dev);
 void mtk_mdp_rdma_power_off(struct device *dev);
-int mtk_mdp_rdma_clk_enable(struct device *dev);
-void mtk_mdp_rdma_clk_disable(struct device *dev);
+int mtk_mdp_rdma_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_mdp_rdma_clk_disable(struct mtk_ddp_comp *comp);
 void mtk_mdp_rdma_start(struct device *dev, struct cmdq_pkt *cmdq_pkt);
 void mtk_mdp_rdma_stop(struct device *dev, struct cmdq_pkt *cmdq_pkt);
 void mtk_mdp_rdma_config(struct device *dev, struct mtk_mdp_rdma_cfg *cfg,
@@ -188,9 +188,9 @@ void mtk_mdp_rdma_config(struct device *dev, struct mtk_mdp_rdma_cfg *cfg,
 const u32 *mtk_mdp_rdma_get_formats(struct device *dev);
 size_t mtk_mdp_rdma_get_num_formats(struct device *dev);
 
-int mtk_wdma_clk_enable(struct device *dev);
-void mtk_wdma_clk_disable(struct device *dev);
-void mtk_wdma_config(struct device *dev, unsigned int width,
+int mtk_wdma_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_wdma_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_wdma_config(struct mtk_ddp_comp *comp, unsigned int width,
 		     unsigned int height, unsigned int vrefresh,
 		     unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
 unsigned int mtk_wdma_layer_nr(struct device *dev, int pipeline_index);
@@ -208,8 +208,8 @@ void mtk_wdma_disable_vblank(struct device *dev);
 const u32 *mtk_wdma_get_formats(struct device *dev);
 size_t mtk_wdma_get_num_formats(struct device *dev);
 
-int mtk_padding_clk_enable(struct device *dev);
-void mtk_padding_clk_disable(struct device *dev);
+int mtk_padding_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_padding_clk_disable(struct mtk_ddp_comp *comp);
 void mtk_padding_start(struct device *dev);
 void mtk_padding_stop(struct device *dev);
 #endif
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_dsc.c b/drivers/gpu/drm/mediatek/mtk_disp_dsc.c
index 79b66bd7e5f3..19d2e42861f0 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_dsc.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_dsc.c
@@ -135,16 +135,16 @@ struct mtk_dsc {
 	bool dsc_config_done;
 };
 
-int mtk_dsc_clk_enable(struct device *dev)
+int mtk_dsc_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_dsc *disp_dsc = dev_get_drvdata(dev);
+	struct mtk_dsc *disp_dsc = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(disp_dsc->clk);
 }
 
-void mtk_dsc_clk_disable(struct device *dev)
+void mtk_dsc_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_dsc *disp_dsc = dev_get_drvdata(dev);
+	struct mtk_dsc *disp_dsc = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(disp_dsc->clk);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_gamma.c b/drivers/gpu/drm/mediatek/mtk_disp_gamma.c
index 8afd15006df2..a30a032e6c35 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_gamma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_gamma.c
@@ -64,16 +64,16 @@ struct mtk_disp_gamma {
 	const struct mtk_disp_gamma_data *data;
 };
 
-int mtk_gamma_clk_enable(struct device *dev)
+int mtk_gamma_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
+	struct mtk_disp_gamma *gamma = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(gamma->clk);
 }
 
-void mtk_gamma_clk_disable(struct device *dev)
+void mtk_gamma_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
+	struct mtk_disp_gamma *gamma = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(gamma->clk);
 }
@@ -206,11 +206,11 @@ void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state)
 	writel(cfg_val, gamma->regs + DISP_GAMMA_CFG);
 }
 
-void mtk_gamma_config(struct device *dev, unsigned int w,
+void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_gamma *gamma = dev_get_drvdata(dev);
+	struct mtk_disp_gamma *gamma = dev_get_drvdata(comp->dev);
 	u32 sz;
 
 	sz = FIELD_PREP(DISP_GAMMA_SIZE_HSIZE, w);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_merge.c b/drivers/gpu/drm/mediatek/mtk_disp_merge.c
index b174dda091d3..947e5a95947b 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_merge.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_merge.c
@@ -126,11 +126,11 @@ static void mtk_merge_fifo_setting(struct mtk_disp_merge *priv,
 			   FLD_PREULTRA_TH_LOW | FLD_PREULTRA_TH_HIGH);
 }
 
-void mtk_merge_config(struct device *dev, unsigned int w,
+void mtk_merge_config(struct mtk_ddp_comp *comp, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	mtk_merge_advance_config(dev, w, 0, h, vrefresh, bpc, cmdq_pkt);
+	mtk_merge_advance_config(comp->dev, w, 0, h, vrefresh, bpc, cmdq_pkt);
 }
 
 void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int r_w,
@@ -191,14 +191,14 @@ void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int
 			   DISP_REG_MERGE_CFG_12, FLD_CFG_MERGE_MODE);
 }
 
-int mtk_merge_clk_enable(struct device *dev)
+int mtk_merge_clk_enable(struct mtk_ddp_comp *comp)
 {
-	int ret = 0;
-	struct mtk_disp_merge *priv = dev_get_drvdata(dev);
+	struct mtk_disp_merge *priv = dev_get_drvdata(comp->dev);
+	int ret;
 
 	ret = clk_prepare_enable(priv->clk);
 	if (ret) {
-		dev_err(dev, "merge clk prepare enable failed\n");
+		dev_err(comp->dev, "merge clk prepare enable failed\n");
 		return ret;
 	}
 
@@ -207,16 +207,16 @@ int mtk_merge_clk_enable(struct device *dev)
 		/* should clean up the state of priv->clk */
 		clk_disable_unprepare(priv->clk);
 
-		dev_err(dev, "async clk prepare enable failed\n");
+		dev_err(comp->dev, "async clk prepare enable failed\n");
 		return ret;
 	}
 
 	return ret;
 }
 
-void mtk_merge_clk_disable(struct device *dev)
+void mtk_merge_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_merge *priv = dev_get_drvdata(dev);
+	struct mtk_disp_merge *priv = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(priv->async_clk);
 	clk_disable_unprepare(priv->clk);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 489a209c4e55..5ceba04a30b1 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -260,16 +260,16 @@ bool mtk_ovl_is_afbc_supported(struct device *dev)
 	return ovl->data->supports_afbc;
 }
 
-int mtk_ovl_clk_enable(struct device *dev)
+int mtk_ovl_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
+	struct mtk_disp_ovl *ovl = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(ovl->clk);
 }
 
-void mtk_ovl_clk_disable(struct device *dev)
+void mtk_ovl_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
+	struct mtk_disp_ovl *ovl = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(ovl->clk);
 }
@@ -327,11 +327,11 @@ static void mtk_ovl_set_bit_depth(struct device *dev, int idx, u32 format,
 			   OVL_CON_CLRFMT_BIT_DEPTH_MASK(idx));
 }
 
-void mtk_ovl_config(struct device *dev, unsigned int w,
+void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
 		    unsigned int h, unsigned int vrefresh,
 		    unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
+	struct mtk_disp_ovl *ovl = dev_get_drvdata(comp->dev);
 
 	if (w != 0 && h != 0)
 		mtk_ddp_write_relaxed(cmdq_pkt, h << 16 | w, &ovl->cmdq_reg, ovl->regs,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index 225ab87bca71..e0e956ec56a7 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -208,11 +208,11 @@ void mtk_ovl_adaptor_layer_config(struct device *dev, unsigned int idx,
 	mtk_ethdr_layer_config(ethdr, idx, state, cmdq_pkt);
 }
 
-void mtk_ovl_adaptor_config(struct device *dev, unsigned int w,
+void mtk_ovl_adaptor_config(struct mtk_ddp_comp *comp, unsigned int w,
 			    unsigned int h, unsigned int vrefresh,
 			    unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
+	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(comp->dev);
 
 	mtk_ethdr_config(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_ETHDR0], w, h,
 			 vrefresh, bpc, cmdq_pkt);
@@ -306,41 +306,49 @@ void mtk_ovl_adaptor_power_off(struct device *dev)
 	power_off(dev, OVL_ADAPTOR_ID_MAX);
 }
 
-int mtk_ovl_adaptor_clk_enable(struct device *dev)
+int mtk_ovl_adaptor_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
-	struct device *comp;
+	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(comp->dev);
 	int ret;
 	int i;
 
 	for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
-		comp = ovl_adaptor->ovl_adaptor_comp[i];
-		if (!comp || !comp_matches[i].funcs->clk_enable)
+		struct mtk_ddp_comp adaptor_comp;
+
+		if (!ovl_adaptor->ovl_adaptor_comp[i] ||
+		    !comp_matches[i].funcs->clk_enable)
 			continue;
-		ret = comp_matches[i].funcs->clk_enable(comp);
+
+		adaptor_comp.dev = ovl_adaptor->ovl_adaptor_comp[i];
+
+		ret = comp_matches[i].funcs->clk_enable(&adaptor_comp);
 		if (ret) {
-			dev_err(dev, "Failed to enable clock %d, err %d\n", i, ret);
+			dev_err(comp->dev, "Failed to enable clock %d, err %d\n", i, ret);
 			while (--i >= 0)
-				comp_matches[i].funcs->clk_disable(comp);
+				comp_matches[i].funcs->clk_disable(&adaptor_comp);
 			return ret;
 		}
 	}
 	return 0;
 }
 
-void mtk_ovl_adaptor_clk_disable(struct device *dev)
+void mtk_ovl_adaptor_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
-	struct device *comp;
+	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(comp->dev);
 	int i;
 
 	for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
-		comp = ovl_adaptor->ovl_adaptor_comp[i];
-		if (!comp || !comp_matches[i].funcs->clk_disable)
+		struct mtk_ddp_comp adaptor_comp;
+
+		if (!ovl_adaptor->ovl_adaptor_comp[i] ||
+		    !comp_matches[i].funcs->clk_disable)
 			continue;
-		comp_matches[i].funcs->clk_disable(comp);
+
+		adaptor_comp.dev = ovl_adaptor->ovl_adaptor_comp[i];
+
+		comp_matches[i].funcs->clk_disable(&adaptor_comp);
 		if (i < OVL_ADAPTOR_MERGE0)
-			pm_runtime_put(comp);
+			pm_runtime_put(adaptor_comp.dev);
 	}
 }
 
@@ -423,10 +431,10 @@ size_t mtk_ovl_adaptor_get_num_formats(struct device *dev)
 	return mtk_mdp_rdma_get_num_formats(ovl_adaptor->ovl_adaptor_comp[OVL_ADAPTOR_MDP_RDMA0]);
 }
 
-void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex)
+void mtk_ovl_adaptor_add_comp(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex)
 {
 	int i;
-	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
+	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(comp->dev);
 
 	for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
 		if (!ovl_adaptor->ovl_adaptor_comp[i])
@@ -439,10 +447,10 @@ void mtk_ovl_adaptor_add_comp(struct device *dev, struct mtk_mutex *mutex)
 	}
 }
 
-void mtk_ovl_adaptor_remove_comp(struct device *dev, struct mtk_mutex *mutex)
+void mtk_ovl_adaptor_remove_comp(struct mtk_ddp_comp *comp, struct mtk_mutex *mutex)
 {
 	int i;
-	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(dev);
+	struct mtk_disp_ovl_adaptor *ovl_adaptor = dev_get_drvdata(comp->dev);
 
 	for (i = 0; i < OVL_ADAPTOR_ID_MAX; i++) {
 		if (!ovl_adaptor->ovl_adaptor_comp[i])
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index dce7a9bc3f11..4b6ecd9bdd5f 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -157,16 +157,16 @@ size_t mtk_rdma_get_num_formats(struct device *dev)
 	return rdma->data->num_formats;
 }
 
-int mtk_rdma_clk_enable(struct device *dev)
+int mtk_rdma_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
+	struct mtk_disp_rdma *rdma = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(rdma->clk);
 }
 
-void mtk_rdma_clk_disable(struct device *dev)
+void mtk_rdma_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
+	struct mtk_disp_rdma *rdma = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(rdma->clk);
 }
@@ -182,13 +182,13 @@ void mtk_rdma_stop(struct device *dev)
 	rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0);
 }
 
-void mtk_rdma_config(struct device *dev, unsigned int width,
+void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
 		     unsigned int height, unsigned int vrefresh,
 		     unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
 	unsigned int threshold;
 	unsigned int reg;
-	struct mtk_disp_rdma *rdma = dev_get_drvdata(dev);
+	struct mtk_disp_rdma *rdma = dev_get_drvdata(comp->dev);
 	u32 rdma_fifo_size;
 
 	mtk_ddp_write_mask(cmdq_pkt, width, &rdma->cmdq_reg, rdma->regs,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_wdma.c b/drivers/gpu/drm/mediatek/mtk_disp_wdma.c
index 26c80e7e152b..d5ebd3e3d80f 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_wdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_wdma.c
@@ -189,16 +189,16 @@ size_t mtk_wdma_get_num_formats(struct device *dev)
 	return wdma->data->num_formats;
 }
 
-int mtk_wdma_clk_enable(struct device *dev)
+int mtk_wdma_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_wdma *wdma = dev_get_drvdata(dev);
+	struct mtk_disp_wdma *wdma = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(wdma->clk);
 }
 
-void mtk_wdma_clk_disable(struct device *dev)
+void mtk_wdma_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_disp_wdma *wdma = dev_get_drvdata(dev);
+	struct mtk_disp_wdma *wdma = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(wdma->clk);
 }
@@ -214,11 +214,11 @@ void mtk_wdma_stop(struct device *dev)
 	wdma_update_bits(dev, DISP_REG_WDMA_EN, WDMA_ENGINE_EN, 0);
 }
 
-void mtk_wdma_config(struct device *dev, unsigned int width,
+void mtk_wdma_config(struct mtk_ddp_comp *comp, unsigned int width,
 		     unsigned int height, unsigned int vrefresh,
 		     unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
 {
-	struct mtk_disp_wdma *wdma = dev_get_drvdata(dev);
+	struct mtk_disp_wdma *wdma = dev_get_drvdata(comp->dev);
 
 	writel(WDMA_FORCE_COMMIT | WDMA_BYPASS_SHADOW,
 	       wdma->regs + DISP_REG_WDMA_SHADOW_CTRL);
diff --git a/drivers/gpu/drm/mediatek/mtk_ethdr.c b/drivers/gpu/drm/mediatek/mtk_ethdr.c
index 96832d0cca37..aa257c3780b6 100644
--- a/drivers/gpu/drm/mediatek/mtk_ethdr.c
+++ b/drivers/gpu/drm/mediatek/mtk_ethdr.c
@@ -281,21 +281,21 @@ void mtk_ethdr_stop(struct device *dev)
 	writel(0, mixer->regs + MIX_RST);
 }
 
-int mtk_ethdr_clk_enable(struct device *dev)
+int mtk_ethdr_clk_enable(struct mtk_ddp_comp *comp)
 {
 	int ret;
-	struct mtk_ethdr *priv = dev_get_drvdata(dev);
+	struct mtk_ethdr *priv = dev_get_drvdata(comp->dev);
 
 	ret = clk_bulk_prepare_enable(ETHDR_CLK_NUM, priv->ethdr_clk);
 	if (ret)
-		dev_err(dev,
+		dev_err(comp->dev,
 			"ethdr_clk prepare enable failed\n");
 	return ret;
 }
 
-void mtk_ethdr_clk_disable(struct device *dev)
+void mtk_ethdr_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_ethdr *priv = dev_get_drvdata(dev);
+	struct mtk_ethdr *priv = dev_get_drvdata(comp->dev);
 
 	clk_bulk_disable_unprepare(ETHDR_CLK_NUM, priv->ethdr_clk);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_ethdr.h b/drivers/gpu/drm/mediatek/mtk_ethdr.h
index a72aeee46829..b81b75a092c1 100644
--- a/drivers/gpu/drm/mediatek/mtk_ethdr.h
+++ b/drivers/gpu/drm/mediatek/mtk_ethdr.h
@@ -8,8 +8,8 @@
 
 void mtk_ethdr_start(struct device *dev);
 void mtk_ethdr_stop(struct device *dev);
-int mtk_ethdr_clk_enable(struct device *dev);
-void mtk_ethdr_clk_disable(struct device *dev);
+int mtk_ethdr_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_ethdr_clk_disable(struct mtk_ddp_comp *comp);
 void mtk_ethdr_config(struct device *dev, unsigned int w,
 		      unsigned int h, unsigned int vrefresh,
 		      unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
diff --git a/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c b/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c
index 7982788ae9df..62cc0abca5f3 100644
--- a/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c
@@ -258,16 +258,16 @@ void mtk_mdp_rdma_power_off(struct device *dev)
 	pm_runtime_put(dev);
 }
 
-int mtk_mdp_rdma_clk_enable(struct device *dev)
+int mtk_mdp_rdma_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_mdp_rdma *rdma = dev_get_drvdata(dev);
+	struct mtk_mdp_rdma *rdma = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(rdma->clk);
 }
 
-void mtk_mdp_rdma_clk_disable(struct device *dev)
+void mtk_mdp_rdma_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_mdp_rdma *rdma = dev_get_drvdata(dev);
+	struct mtk_mdp_rdma *rdma = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(rdma->clk);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_padding.c b/drivers/gpu/drm/mediatek/mtk_padding.c
index b4e3e5a3428b..95183600d150 100644
--- a/drivers/gpu/drm/mediatek/mtk_padding.c
+++ b/drivers/gpu/drm/mediatek/mtk_padding.c
@@ -38,16 +38,16 @@ struct mtk_padding {
 	struct cmdq_client_reg	cmdq_reg;
 };
 
-int mtk_padding_clk_enable(struct device *dev)
+int mtk_padding_clk_enable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_padding *padding = dev_get_drvdata(dev);
+	struct mtk_padding *padding = dev_get_drvdata(comp->dev);
 
 	return clk_prepare_enable(padding->clk);
 }
 
-void mtk_padding_clk_disable(struct device *dev)
+void mtk_padding_clk_disable(struct mtk_ddp_comp *comp)
 {
-	struct mtk_padding *padding = dev_get_drvdata(dev);
+	struct mtk_padding *padding = dev_get_drvdata(comp->dev);
 
 	clk_disable_unprepare(padding->clk);
 }
-- 
2.54.0



^ permalink raw reply related

* [PATCH 14/42] drm/mediatek: Add support for hardware multi-stage layers
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

In preparation for adding support for newer, more complex, display
controllers like the ones found in the MediaTek Kompanio Ultra and
Genio Pro 5100 (MT8196/MT8894), add support for multi-stage layers
as in newer hardware, there is a special strategy using multiple
hardware components of one (or multiple) display controller(s) to
form one single layer.

Multi-stage layers are one more step towards extra flexibility of
the Display Subsystem, making it possible to change capabilities
of Display Controller Pipelines depending on the final application
of a board: for example, one board may choose to use fewer layers
per pipeline to achieve more pipelines (more concurrent outputs),
or more layers to achieve less, but more capable (as in offloading
and in achievable bandwidth) concurrent outputs.

This currently adds boilerplate to support this hardware, but does
not yet implement the IP-specific support for MT8196/8894, which
will be done after all preparation is done.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_crtc.c           | 304 ++++++++++++------
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h       |  15 +-
 drivers/gpu/drm/mediatek/mtk_disp_drv.h       |  12 +-
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c       |  12 +-
 .../gpu/drm/mediatek/mtk_disp_ovl_adaptor.c   |   2 +-
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c      |   9 +-
 drivers/gpu/drm/mediatek/mtk_disp_wdma.c      |   2 +-
 7 files changed, 246 insertions(+), 110 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 5e243550d35e..8656b56618a2 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -8,6 +8,7 @@
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/mailbox_controller.h>
+#include <linux/minmax.h>
 #include <linux/of.h>
 #include <linux/pm_runtime.h>
 #include <linux/soc/mediatek/mtk-cmdq.h>
@@ -28,12 +29,24 @@
 #include "mtk_drm_drv.h"
 #include "mtk_plane.h"
 
+/*
+ * struct mtk_crtc_hw_layer - MediaTek specific layer structure
+ * @plane:           DRM Plane
+ * @layer_stages:    HW Components layer stage indices to form one full layer/plane
+ * @layer_stages_nr: Number of layer stages in array
+ */
+struct mtk_crtc_hw_layer {
+	struct drm_plane plane;
+	u8 *layer_stages;
+	u8 layer_stages_nr;
+};
+
 /*
  * struct mtk_crtc - MediaTek specific crtc structure.
  * @base: crtc object.
  * @enabled: records whether crtc_enable succeeded
- * @planes: array of 4 drm_plane structures, one for each overlay plane
- * @pending_planes: whether any plane has pending changes to be applied
+ * @hwlayers:        Array of mtk_crtc_hw_layer structures, one for each overlay plane
+ * @hwlayer_nr:      Number of hwlayers
  * @mmsys_dev: pointer to the mmsys device for configuration registers
  * @mutex: handle to one of the ten disp_mutex streams
  * @ddp_comp_nr: number of components in ddp_comp
@@ -48,8 +61,8 @@ struct mtk_crtc {
 	bool				pending_needs_vblank;
 	struct drm_pending_vblank_event	*event;
 
-	struct drm_plane		*planes;
-	unsigned int			layer_nr;
+	struct mtk_crtc_hw_layer	*hwlayers;
+	unsigned int			hwlayer_nr;
 	bool				pending_planes;
 	bool				pending_async_planes;
 
@@ -254,29 +267,6 @@ static void mtk_crtc_ddp_clk_disable(struct mtk_crtc *mtk_crtc)
 		mtk_ddp_comp_clk_disable(mtk_crtc->ddp_comp[i]);
 }
 
-static
-struct mtk_ddp_comp *mtk_ddp_comp_for_plane(struct drm_crtc *crtc,
-					    struct drm_plane *plane,
-					    unsigned int *local_layer)
-{
-	struct mtk_crtc *mtk_crtc = to_mtk_crtc(crtc);
-	struct mtk_ddp_comp *comp;
-	int i, count = 0;
-	unsigned int local_index = plane - mtk_crtc->planes;
-
-	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
-		comp = mtk_crtc->ddp_comp[i];
-		if (local_index < (count + mtk_ddp_comp_layer_nr(comp))) {
-			*local_layer = local_index - count;
-			return comp;
-		}
-		count += mtk_ddp_comp_layer_nr(comp);
-	}
-
-	WARN(1, "Failed to find component for plane %d\n", plane->index);
-	return NULL;
-}
-
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
 static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
 {
@@ -303,8 +293,8 @@ static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
 	state->pending_config = false;
 
 	if (mtk_crtc->pending_planes) {
-		for (i = 0; i < mtk_crtc->layer_nr; i++) {
-			struct drm_plane *plane = &mtk_crtc->planes[i];
+		for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+			struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 			struct mtk_plane_state *plane_state;
 
 			plane_state = to_mtk_plane_state(plane->state);
@@ -315,8 +305,8 @@ static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
 	}
 
 	if (mtk_crtc->pending_async_planes) {
-		for (i = 0; i < mtk_crtc->layer_nr; i++) {
-			struct drm_plane *plane = &mtk_crtc->planes[i];
+		for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+			struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 			struct mtk_plane_state *plane_state;
 
 			plane_state = to_mtk_plane_state(plane->state);
@@ -340,6 +330,21 @@ static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
 }
 #endif
 
+static void mtk_crtc_config_layer(struct mtk_crtc *mtk_crtc,
+				  struct mtk_plane_state *plane_state,
+				  int layer_num, struct cmdq_pkt *cmdq_pkt)
+{
+	u8 *layer_stages = mtk_crtc->hwlayers[layer_num].layer_stages;
+	u8 i = 0;
+
+	do {
+		mtk_ddp_comp_layer_config(mtk_crtc->ddp_comp[layer_stages[i]],
+					  layer_num, plane_state, cmdq_pkt);
+	} while (++i < mtk_crtc->hwlayers[layer_num].layer_stages_nr);
+
+	return;
+}
+
 static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 {
 	struct drm_crtc *crtc = &mtk_crtc->base;
@@ -436,20 +441,16 @@ static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 	}
 
 	/* Initially configure all planes */
-	for (i = 0; i < mtk_crtc->layer_nr; i++) {
-		struct drm_plane *plane = &mtk_crtc->planes[i];
+	for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+		struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 		struct mtk_plane_state *plane_state;
-		struct mtk_ddp_comp *comp;
-		unsigned int local_layer;
 
 		plane_state = to_mtk_plane_state(plane->state);
 
 		/* should not enable layer before crtc enabled */
 		plane_state->pending.enable = false;
-		comp = mtk_ddp_comp_for_plane(crtc, plane, &local_layer);
-		if (comp)
-			mtk_ddp_comp_layer_config(comp, local_layer,
-						  plane_state, NULL);
+
+		mtk_crtc_config_layer(mtk_crtc, plane_state, i, NULL);
 	}
 
 	return 0;
@@ -511,7 +512,6 @@ static void mtk_crtc_ddp_config(struct drm_crtc *crtc,
 	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
 	unsigned int i;
-	unsigned int local_layer;
 
 	/*
 	 * TODO: instead of updating the registers here, we should prepare
@@ -529,8 +529,8 @@ static void mtk_crtc_ddp_config(struct drm_crtc *crtc,
 	}
 
 	if (mtk_crtc->pending_planes) {
-		for (i = 0; i < mtk_crtc->layer_nr; i++) {
-			struct drm_plane *plane = &mtk_crtc->planes[i];
+		for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+			struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 			struct mtk_plane_state *plane_state;
 
 			plane_state = to_mtk_plane_state(plane->state);
@@ -538,12 +538,8 @@ static void mtk_crtc_ddp_config(struct drm_crtc *crtc,
 			if (!plane_state->pending.config)
 				continue;
 
-			comp = mtk_ddp_comp_for_plane(crtc, plane, &local_layer);
+			mtk_crtc_config_layer(mtk_crtc, plane_state, i, cmdq_handle);
 
-			if (comp)
-				mtk_ddp_comp_layer_config(comp, local_layer,
-							  plane_state,
-							  cmdq_handle);
 			if (!cmdq_handle)
 				plane_state->pending.config = false;
 		}
@@ -553,8 +549,8 @@ static void mtk_crtc_ddp_config(struct drm_crtc *crtc,
 	}
 
 	if (mtk_crtc->pending_async_planes) {
-		for (i = 0; i < mtk_crtc->layer_nr; i++) {
-			struct drm_plane *plane = &mtk_crtc->planes[i];
+		for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+			struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 			struct mtk_plane_state *plane_state;
 
 			plane_state = to_mtk_plane_state(plane->state);
@@ -562,12 +558,8 @@ static void mtk_crtc_ddp_config(struct drm_crtc *crtc,
 			if (!plane_state->pending.async_config)
 				continue;
 
-			comp = mtk_ddp_comp_for_plane(crtc, plane, &local_layer);
+			mtk_crtc_config_layer(mtk_crtc, plane_state, i, cmdq_handle);
 
-			if (comp)
-				mtk_ddp_comp_layer_config(comp, local_layer,
-							  plane_state,
-							  cmdq_handle);
 			if (!cmdq_handle)
 				plane_state->pending.async_config = false;
 		}
@@ -597,8 +589,8 @@ static void mtk_crtc_update_config(struct mtk_crtc *mtk_crtc, bool needs_vblank)
 	if (needs_vblank)
 		mtk_crtc->pending_needs_vblank = true;
 
-	for (i = 0; i < mtk_crtc->layer_nr; i++) {
-		struct drm_plane *plane = &mtk_crtc->planes[i];
+	for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+		struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 		struct mtk_plane_state *plane_state;
 
 		plane_state = to_mtk_plane_state(plane->state);
@@ -747,12 +739,19 @@ static void mtk_crtc_update_output(struct drm_crtc *crtc,
 int mtk_crtc_plane_check(struct drm_crtc *crtc, struct drm_plane *plane,
 			 struct mtk_plane_state *state)
 {
-	unsigned int local_layer;
-	struct mtk_ddp_comp *comp;
+	struct mtk_crtc_hw_layer *hwlayer = container_of(plane, struct mtk_crtc_hw_layer, plane);
+	struct mtk_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	u8 *layer_stages = hwlayer->layer_stages;
+	int i, ret;
+
+	for (i = 0; i < hwlayer->layer_stages_nr; i++) {
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[layer_stages[i]];
+
+		ret = mtk_ddp_comp_layer_check(comp, 0, state);
+		if (ret)
+			return ret;
+	}
 
-	comp = mtk_ddp_comp_for_plane(crtc, plane, &local_layer);
-	if (comp)
-		return mtk_ddp_comp_layer_check(comp, local_layer, state);
 	return 0;
 }
 
@@ -771,8 +770,8 @@ void mtk_crtc_plane_disable(struct drm_crtc *crtc, struct drm_plane *plane)
 		return;
 
 	/* set pending plane state to disabled */
-	for (i = 0; i < mtk_crtc->layer_nr; i++) {
-		struct drm_plane *mtk_plane = &mtk_crtc->planes[i];
+	for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+		struct drm_plane *mtk_plane = &mtk_crtc->hwlayers[i].plane;
 		struct mtk_plane_state *mtk_plane_state = to_mtk_plane_state(mtk_plane->state);
 
 		if (mtk_plane->index == plane->index) {
@@ -841,8 +840,8 @@ static void mtk_crtc_atomic_disable(struct drm_crtc *crtc,
 		return;
 
 	/* Set all pending plane state to disabled */
-	for (i = 0; i < mtk_crtc->layer_nr; i++) {
-		struct drm_plane *plane = &mtk_crtc->planes[i];
+	for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+		struct drm_plane *plane = &mtk_crtc->hwlayers[i].plane;
 		struct mtk_plane_state *plane_state;
 
 		plane_state = to_mtk_plane_state(plane->state);
@@ -936,11 +935,11 @@ static int mtk_crtc_init(struct drm_device *drm, struct mtk_crtc *mtk_crtc,
 	struct drm_plane *cursor = NULL;
 	int i, ret;
 
-	for (i = 0; i < mtk_crtc->layer_nr; i++) {
-		if (mtk_crtc->planes[i].type == DRM_PLANE_TYPE_PRIMARY)
-			primary = &mtk_crtc->planes[i];
-		else if (mtk_crtc->planes[i].type == DRM_PLANE_TYPE_CURSOR)
-			cursor = &mtk_crtc->planes[i];
+	for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+		if (mtk_crtc->hwlayers[i].plane.type == DRM_PLANE_TYPE_PRIMARY)
+			primary = &mtk_crtc->hwlayers[i].plane;
+		else if (mtk_crtc->hwlayers[i].plane.type == DRM_PLANE_TYPE_CURSOR)
+			cursor = &mtk_crtc->hwlayers[i].plane;
 	}
 
 	ret = drm_crtc_init_with_planes(drm, &mtk_crtc->base, primary, cursor,
@@ -957,23 +956,6 @@ static int mtk_crtc_init(struct drm_device *drm, struct mtk_crtc *mtk_crtc,
 	return ret;
 }
 
-static int mtk_crtc_num_comp_planes(struct mtk_crtc *mtk_crtc, int comp_idx)
-{
-	struct mtk_ddp_comp *comp;
-
-	if (comp_idx > 1)
-		return 0;
-
-	comp = mtk_crtc->ddp_comp[comp_idx];
-	if (!comp->funcs)
-		return 0;
-
-	if (comp_idx == 1 && !comp->funcs->bgclr_in_on)
-		return 0;
-
-	return mtk_ddp_comp_layer_nr(comp);
-}
-
 static inline
 enum drm_plane_type mtk_crtc_plane_type(unsigned int plane_idx,
 					unsigned int num_planes)
@@ -991,15 +973,15 @@ static int mtk_crtc_init_comp_planes(struct drm_device *drm_dev,
 				     struct mtk_crtc *mtk_crtc,
 				     int comp_idx, int pipe)
 {
-	int num_planes = mtk_crtc_num_comp_planes(mtk_crtc, comp_idx);
 	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[comp_idx];
+	unsigned int num_planes = mtk_ddp_comp_layer_nr(comp, comp_idx);
 	int i, ret;
 
 	for (i = 0; i < num_planes; i++) {
-		ret = mtk_plane_init(drm_dev,
-				&mtk_crtc->planes[mtk_crtc->layer_nr],
-				BIT(pipe),
-				mtk_crtc_plane_type(mtk_crtc->layer_nr, num_planes),
+		struct mtk_crtc_hw_layer *hwlayer = &mtk_crtc->hwlayers[mtk_crtc->hwlayer_nr];
+
+		ret = mtk_plane_init(drm_dev, &hwlayer->plane, BIT(pipe),
+				mtk_crtc_plane_type(mtk_crtc->hwlayer_nr, num_planes),
 				mtk_ddp_comp_supported_rotations(comp),
 				mtk_ddp_comp_get_blend_modes(comp),
 				mtk_ddp_comp_get_formats(comp),
@@ -1008,7 +990,98 @@ static int mtk_crtc_init_comp_planes(struct drm_device *drm_dev,
 		if (ret)
 			return ret;
 
-		mtk_crtc->layer_nr++;
+		hwlayer->layer_stages[hwlayer->layer_stages_nr] = comp_idx;
+		hwlayer->layer_stages_nr++;
+
+		mtk_crtc->hwlayer_nr++;
+	}
+	return 0;
+}
+
+static struct mtk_crtc_hw_layer
+*mtk_crtc_find_hwlayer_with_comp(struct mtk_crtc *mtk_crtc, int comp)
+{
+	struct mtk_crtc_hw_layer *hwlayer;
+	int i, j;
+
+	for (i = 0; i < mtk_crtc->hwlayer_nr; i++) {
+		hwlayer = &mtk_crtc->hwlayers[i];
+
+		for (j = 0; j < hwlayer->layer_stages_nr; j++) {
+			if (hwlayer->layer_stages[j] == comp)
+				return hwlayer;
+		}
+	}
+
+	return NULL;
+}
+
+static int mtk_crtc_init_layer_stages(struct mtk_crtc *mtk_crtc)
+{
+	unsigned int prev_master_layer_component = UINT_MAX;
+	unsigned int master_layer_component = 0;
+	unsigned int comp_idx;
+
+	for (comp_idx = 0; comp_idx < mtk_crtc->ddp_comp_nr; comp_idx++) {
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[comp_idx];
+		struct mtk_crtc_hw_layer *hwlayer = NULL;
+
+		/*
+		 * A hardware IP represented as a component with layer_nr > 0
+		 * is the master (and also the first layer stage), which may
+		 * then be composed of other multiple hardware-configurable
+		 * layer stages that are directly connected to it.
+		 *
+		 * Only quirk here is that, in some cases, some layer stages
+		 * may be connected together and hence not directly connected
+		 * to the master: this corner case is not getting a perfectly
+		 * accurate representation in the following code, as it treats
+		 * all of the additional layer stages as if they are directly
+		 * connected to the master.
+		 * That doesn't matter, because any special configuration for
+		 * this corner case is handled in the .connect handler of each
+		 * specific component driver.
+		 *
+		 * For example, this is a multi-component layer stage layout:
+		 *    FRAME_IN ->   _____ LAYER_MASTER ______ 
+		 *                 /       |        |        \  -> BLEND -> NEXT
+		 *              HW_STG1  HW_STG2  HW_STG3  HW_STGx    HW    HWIP
+		 *
+		 * And following, another one that gets handled (to simplify the
+		 * code) anyway like the first:
+		 * FRAME_IN -> LAYER_MASTER -> HW_STG1 -> HW_STG(x) -> NEXT HWIP
+		 *
+		 * For the moment, this also supposes that a component cannot be
+		 * both primary and secondary (so if it has layer_nr, stage_nr
+		 * will not be evaluated for such component).
+		 */
+		if (mtk_ddp_comp_layer_nr(comp, comp_idx)) {
+			master_layer_component = comp_idx;
+			continue;
+		}
+
+		/* If this component doesn't provide any stages, keep searching */
+		if (!mtk_ddp_comp_stage_nr(comp))
+			continue;
+
+		/*
+		 * If the index of the master layer component changed, find it
+		 * in the stack of registered layer components
+		 */ 
+		if (master_layer_component != prev_master_layer_component) {
+			hwlayer = mtk_crtc_find_hwlayer_with_comp(mtk_crtc, master_layer_component);
+			if (!hwlayer) {
+				drm_err(mtk_crtc->base.dev,
+					"Could not find layer master for %d!\n",
+					master_layer_component);
+				continue;
+			}
+			prev_master_layer_component = master_layer_component;
+		}
+
+		/* Add the newly found layer stage to the correct layer master */
+		hwlayer->layer_stages[hwlayer->layer_stages_nr] = comp_idx;
+		hwlayer->layer_stages_nr++;
 	}
 	return 0;
 }
@@ -1038,6 +1111,7 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 	struct mtk_ddp_comp *dma_comp;
 	struct mtk_crtc *mtk_crtc;
 	unsigned int num_comp_planes = 0;
+	unsigned int max_comp_stages = 0;
 	int ret;
 	int i, j;
 	bool has_ctm = false;
@@ -1123,20 +1197,54 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 						&mtk_crtc->base);
 	}
 
-	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
-		num_comp_planes += mtk_crtc_num_comp_planes(mtk_crtc, i);
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
+
+		num_comp_planes += mtk_ddp_comp_layer_nr(comp, 0);
+		max_comp_stages = MAX(mtk_ddp_comp_stage_nr(comp), max_comp_stages);
+	}
 
-	mtk_crtc->planes = devm_kcalloc(dev, num_comp_planes,
-					sizeof(struct drm_plane), GFP_KERNEL);
-	if (!mtk_crtc->planes)
+	/*
+	 * On the older and/or simpler display controllers, each layer is single
+	 * stage, while newer ones are more complex and may have multiple stages
+	 * to form one full layer.
+	 *
+	 * In both cases, anyway, in order to form a complete layer for plane,
+	 * there will always be at least one layer stage, so the maximum number
+	 * of stages is always (1 + max_comp_stages): for this reason, just add 1.
+	 */
+	max_comp_stages++;
+
+	mtk_crtc->hwlayers = devm_kcalloc(dev, num_comp_planes,
+					  sizeof(*mtk_crtc->hwlayers),
+					  GFP_KERNEL);
+	if (!mtk_crtc->hwlayers)
 		return -ENOMEM;
 
+	for (i = 0; i < num_comp_planes; i++) {
+		struct mtk_crtc_hw_layer *comp_plane = &mtk_crtc->hwlayers[i];
+		comp_plane->layer_stages = devm_kcalloc(dev, max_comp_stages,
+							sizeof(*comp_plane->layer_stages),
+							GFP_KERNEL);
+		if (!comp_plane->layer_stages)
+			return -ENOMEM;
+	}
+
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
 		ret = mtk_crtc_init_comp_planes(drm_dev, mtk_crtc, i, crtc_i);
 		if (ret)
 			return ret;
 	}
 
+	/* Initialize multi-stage only if present */
+	if (max_comp_stages > 1) {
+		ret = mtk_crtc_init_layer_stages(mtk_crtc);
+		if (ret)
+			return ret;
+	}
+	dev_dbg(dev, "Found %u layers composed by maximum of %u stage(s) each.\n",
+		mtk_crtc->hwlayer_nr, max_comp_stages);
+
 	/*
 	 * Default to use the first component as the dma dev.
 	 * In the case of ovl_adaptor sub driver, it needs to use the
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index 5206445b7157..a7ed46a95037 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -50,7 +50,8 @@ struct mtk_ddp_comp_funcs {
 	void (*enable_vblank)(struct device *dev);
 	void (*disable_vblank)(struct device *dev);
 	unsigned int (*supported_rotations)(struct device *dev);
-	unsigned int (*layer_nr)(struct device *dev);
+	unsigned int (*layer_nr)(struct device *dev, int pipeline_index);
+	unsigned int (*layerstage_nr)(struct device *dev);
 	int (*layer_check)(struct device *dev,
 			   unsigned int idx,
 			   struct mtk_plane_state *state);
@@ -196,10 +197,18 @@ unsigned int mtk_ddp_comp_supported_rotations(struct mtk_ddp_comp *comp)
 	return DRM_MODE_ROTATE_0;
 }
 
-static inline unsigned int mtk_ddp_comp_layer_nr(struct mtk_ddp_comp *comp)
+static inline unsigned int mtk_ddp_comp_layer_nr(struct mtk_ddp_comp *comp, int pipeline_idx)
 {
 	if (comp->funcs && comp->funcs->layer_nr)
-		return comp->funcs->layer_nr(comp->dev);
+		return comp->funcs->layer_nr(comp->dev, pipeline_idx);
+
+	return 0;
+}
+
+static inline unsigned int mtk_ddp_comp_stage_nr(struct mtk_ddp_comp *comp)
+{
+	if (comp->funcs && comp->funcs->layerstage_nr)
+		return comp->funcs->layerstage_nr(comp->dev);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index e0c30c6c7cc8..7706d95b3be8 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2020 MediaTek Inc.
+ * Copyright (c) 2020-2025 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
  */
 
 #ifndef _MTK_DISP_DRV_H_
@@ -98,7 +100,7 @@ int mtk_ovl_layer_check(struct device *dev, unsigned int idx,
 void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
 			  struct mtk_plane_state *state,
 			  struct cmdq_pkt *cmdq_pkt);
-unsigned int mtk_ovl_layer_nr(struct device *dev);
+unsigned int mtk_ovl_layer_nr(struct device *dev, int pipeline_index);
 void mtk_ovl_layer_on(struct device *dev, unsigned int idx,
 		      struct cmdq_pkt *cmdq_pkt);
 void mtk_ovl_layer_off(struct device *dev, unsigned int idx,
@@ -141,7 +143,7 @@ void mtk_ovl_adaptor_enable_vblank(struct device *dev);
 void mtk_ovl_adaptor_disable_vblank(struct device *dev);
 void mtk_ovl_adaptor_start(struct device *dev);
 void mtk_ovl_adaptor_stop(struct device *dev);
-unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev);
+unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev, int pipeline_index);
 struct device *mtk_ovl_adaptor_dma_dev_get(struct device *dev);
 u32 mtk_ovl_adaptor_get_blend_modes(struct device *dev);
 const u32 *mtk_ovl_adaptor_get_formats(struct device *dev);
@@ -155,7 +157,7 @@ void mtk_rdma_clk_disable(struct device *dev);
 void mtk_rdma_config(struct device *dev, unsigned int width,
 		     unsigned int height, unsigned int vrefresh,
 		     unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
-unsigned int mtk_rdma_layer_nr(struct device *dev);
+unsigned int mtk_rdma_layer_nr(struct device *dev, int pipeline_index);
 void mtk_rdma_layer_config(struct device *dev, unsigned int idx,
 			   struct mtk_plane_state *state,
 			   struct cmdq_pkt *cmdq_pkt);
@@ -186,7 +188,7 @@ void mtk_wdma_clk_disable(struct device *dev);
 void mtk_wdma_config(struct device *dev, unsigned int width,
 		     unsigned int height, unsigned int vrefresh,
 		     unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
-unsigned int mtk_wdma_layer_nr(struct device *dev);
+unsigned int mtk_wdma_layer_nr(struct device *dev, int pipeline_index);
 void mtk_wdma_layer_config(struct device *dev, unsigned int idx,
 			   struct mtk_plane_state *state,
 			   struct cmdq_pkt *cmdq_pkt);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index fa4607304acb..489a209c4e55 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -348,10 +348,20 @@ void mtk_ovl_config(struct device *dev, unsigned int w,
 	mtk_ddp_write(cmdq_pkt, 0x0, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RST);
 }
 
-unsigned int mtk_ovl_layer_nr(struct device *dev)
+unsigned int mtk_ovl_layer_nr(struct device *dev, int pipeline_index)
 {
 	struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
 
+	/*
+	 * Only the first OVL in a display pipeline can form layers, and it
+	 * must be either:
+	 *  - The first HW component in the pipeline; or
+	 *  - The second HW component in the pipeline, taking its input from
+	 *    a ReadDMA (RDMA) output.
+	 */
+	if (pipeline_index > 1)
+		return 0;
+
 	return ovl->data->layer_nr;
 }
 
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index bbf1d8ccda05..78bc3bee0794 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -360,7 +360,7 @@ enum drm_mode_status mtk_ovl_adaptor_mode_valid(struct device *dev,
 	return MODE_OK;
 }
 
-unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev)
+unsigned int mtk_ovl_adaptor_layer_nr(struct device *dev, int pipeline_index)
 {
 	return MTK_OVL_ADAPTOR_LAYER_NUM;
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index 9fd9bb1ee544..dce7a9bc3f11 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -251,8 +251,15 @@ static unsigned int rdma_fmt_convert(struct mtk_disp_rdma *rdma,
 	}
 }
 
-unsigned int mtk_rdma_layer_nr(struct device *dev)
+unsigned int mtk_rdma_layer_nr(struct device *dev, int pipeline_index)
 {
+	/*
+	 * ReadDMA may participate in forming a layer only if it is the first
+	 * component in a pipeline, usually passing data to a DispHW Overlay (OVL)
+	 */
+	if (pipeline_index > 0)
+		return 0;
+
 	return 1;
 }
 
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_wdma.c b/drivers/gpu/drm/mediatek/mtk_disp_wdma.c
index 761dbe0148d8..26c80e7e152b 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_wdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_wdma.c
@@ -259,7 +259,7 @@ static u32 wdma_fmt_convert(unsigned int fmt)
 	}
 }
 
-unsigned int mtk_wdma_layer_nr(struct device *dev)
+unsigned int mtk_wdma_layer_nr(struct device *dev, int pipeline_index)
 {
 	return 1;
 }
-- 
2.54.0



^ permalink raw reply related

* [PATCH 26/42] dt-bindings: display: mediatek: Introduce MT8196 Layer Blender
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add documentation for the Overlay Layer Blender IP found in the
newer generation SoCs like MT8196, MT8894, MT6991, and their
variants.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 .../mediatek/mediatek,mt8196-blender.yaml     | 97 +++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-blender.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-blender.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-blender.yaml
new file mode 100644
index 000000000000..d907a51c495e
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-blender.yaml
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,mt8196-blender.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Display Overlay Layer Blender
+
+maintainers:
+  - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+  The MediaTek Display Overlay Layer Blender hardware is responsible
+  for performing the alpha blending operation for overlapping layers
+  from various sources.
+
+properties:
+  compatible:
+    - const: mediatek,mt8196-disp-blender
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+
+    required:
+      - port@0
+      - port@1
+
+  trigger-sources:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/mediatek,mt8196-clock.h>
+    #include <dt-bindings/power/mediatek,mt8196-power.h>
+
+    blender@328e0000 {
+        compatible = "mediatek,mt8196-disp-blender";
+        reg = <0x328e0000 0x1000>;
+        clocks = <&ovlsys0 CLK_OVL_BLENDER1>;
+        power-domains = <&hpm_hwv MT8196_POWER_DOMAIN_OVL0_DORMANT>;
+        trigger-sources = <&ovl0_mutex 11>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <0>;
+
+                endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&exdma0_2_out>;
+                };
+            };
+
+            port@1 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <1>;
+
+                endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&exdma0_3_in>;
+                };
+            };
+        };
+    };
+
+...
-- 
2.54.0



^ permalink raw reply related

* [PATCH 28/42] dt-bindings: display: mediatek: Introduce MT8196 extended DMA Engine
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add documentation for the extended DMA Engine (exDMA) IP found in
the newer generation SoCs like MT8196, MT8894, MT6991, and their
variants.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 .../mediatek/mediatek,mt8196-exdma.yaml       | 104 ++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-exdma.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-exdma.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-exdma.yaml
new file mode 100644
index 000000000000..ca08a74ef7ad
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-exdma.yaml
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,mt8196-exdma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Display Overlay extended DMA (exDMA) Engine
+
+maintainers:
+  - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+  The MediaTek Display Overlay extended DMA (exDMA) Engine hardware is used
+  to configure the Display Controller to read the data from DRAM to perform
+  layer overlaying.
+  This hardware also includes color conversion functions to output pixels
+  processed into a consistent color domain.
+
+properties:
+  compatible:
+    - const: mediatek,mt8196-disp-exdma
+
+  reg:
+    maxItems: 1
+
+  "#dma-cells":
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+
+    required:
+      - port@0
+      - port@1
+
+  trigger-sources:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - "#dma-cells"
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/mediatek,mt8196-clock.h>
+    #include <dt-bindings/power/mediatek,mt8196-power.h>
+
+    dma-controller@32850000 {
+        compatible = "mediatek,mt8196-disp-exdma";
+        reg = <0x32850000 0x1000>;
+        #dma-cells = <1>;
+        clocks = <&ovlsys0 CLK_OVL_EXDMA2>;
+        power-domains = <&hpm_hwv MT8196_POWER_DOMAIN_OVL0_DORMANT>;
+        trigger-sources = <&ovl0_mutex 2>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <0>;
+
+                endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&ovl0_resizer0_out>;
+                };
+            };
+
+            port@1 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <1>;
+
+                endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&blender0_1_in>;
+                };
+            };
+        };
+    };
+
+...
-- 
2.54.0



^ permalink raw reply related

* [PATCH 30/42] dt-bindings: display: mediatek: Introduce MT8196 Output Processor
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add documentation for the Overlay Output Processor IP found in
the newer Generation SoCs like MT8196, MT8894, MT6991, and their
variants.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 .../mediatek/mediatek,mt8196-outproc.yaml     | 107 ++++++++++++++++++
 1 file changed, 107 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-outproc.yaml

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-outproc.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-outproc.yaml
new file mode 100644
index 000000000000..634107db269c
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,mt8196-outproc.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/mediatek/mediatek,mt8196-outproc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Display Overlay Output Processor
+
+maintainers:
+  - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+  The MediaTek Display Overlay Output Processor hardware handles the
+  post-stage of pixel processing in the layer overlaying process and
+  is also capable of performing gamma correction, ensuring that the
+  per-pixel values are within the expected luma range.
+  If an instance of this hardware is configured as last stage, it is
+  also capable of generating VBlank (Frame Done) events which can be
+  then dispatched either via an interrupt (to the CPU) or through a
+  Global Command Engine (GCE) Event, internally sent to programmable
+  Command Queue (CMDQ) Engine(s) for offloading purposes.
+
+properties:
+  compatible:
+    - const: mediatek,mt8196-disp-outproc
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    description: Optional VBlank event interrupt
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+
+    required:
+      - port@0
+      - port@1
+
+  trigger-sources:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/mediatek,mt8196-clock.h>
+    #include <dt-bindings/power/mediatek,mt8196-power.h>
+
+    outproc@32970000 {
+        compatible = "mediatek,mt8196-disp-outproc";
+        reg = <0x32970000 0x1000>;
+        clocks = <&ovlsys0 CLK_OVL_OUTPROC0>;
+        power-domains = <&hpm_hwv MT8196_POWER_DOMAIN_OVL0_DORMANT>;
+        trigger-sources = <&ovl0_mutex 20>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <0>;
+
+                endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&blender0_8_out>;
+                };
+            };
+
+            port@1 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <1>;
+
+                endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&ovl0_directlink_out_relay6>;
+                };
+            };
+        };
+    };
+
+...
-- 
2.54.0



^ permalink raw reply related

* [PATCH 31/42] drm/mediatek: Add support for Display Output Processor component
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add support for the Display Output Processor hardware component,
found in the MediaTek Kompanio Ultra MT8196, Genio Pro 5100 MT8894,
Dimensity 9400 MT6991 and others.

This hardware handles the post-stage of pixel processing in the
layer overlaying process and is also capable of performing gamma
correction, ensuring that the per-pixel values are within the
expected gamma range.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/Makefile           |   1 +
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c     |  14 ++
 drivers/gpu/drm/mediatek/mtk_disp_drv.h     |  14 ++
 drivers/gpu/drm/mediatek/mtk_disp_outproc.c | 247 ++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   3 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h      |   1 +
 include/linux/soc/mediatek/mtk-mmsys.h      |   1 +
 7 files changed, 281 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_outproc.c

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 5506f306a7b6..bec46e981ae1 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -10,6 +10,7 @@ mediatek-drm-y := mtk_crtc.o \
 		  mtk_disp_exdma.o \
 		  mtk_disp_gamma.o \
 		  mtk_disp_merge.o \
+		  mtk_disp_outproc.o \
 		  mtk_disp_ovl.o \
 		  mtk_disp_ovl_adaptor.o \
 		  mtk_disp_rdma.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 9896420f902d..0ecb942d6129 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -345,6 +345,18 @@ static const struct mtk_ddp_comp_funcs ddp_od = {
 	.start = mtk_od_start,
 };
 
+static const struct mtk_ddp_comp_funcs ddp_outproc = {
+	.clk_enable = mtk_outproc_clk_enable,
+	.clk_disable = mtk_outproc_clk_disable,
+	.start = mtk_outproc_start,
+	.stop = mtk_outproc_stop,
+	.config = mtk_outproc_config,
+	.register_vblank_cb = mtk_outproc_register_vblank_cb,
+	.unregister_vblank_cb = mtk_outproc_unregister_vblank_cb,
+	.enable_vblank = mtk_outproc_enable_vblank,
+	.disable_vblank = mtk_outproc_disable_vblank,
+};
+
 static const struct mtk_ddp_comp_funcs ddp_ovl = {
 	.clk_enable = mtk_ovl_clk_enable,
 	.clk_disable = mtk_ovl_clk_disable,
@@ -451,6 +463,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_MERGE] = "merge",
 	[MTK_DISP_MUTEX] = "mutex",
 	[MTK_DISP_OD] = "od",
+	[MTK_DISP_OUTPROC] = "outproc",
 	[MTK_DISP_OVL] = "ovl",
 	[MTK_DISP_OVL_2L] = "ovl-2l",
 	[MTK_DISP_OVL_ADAPTOR] = "ovl_adaptor",
@@ -477,6 +490,7 @@ static const struct mtk_ddp_comp_funcs *mtk_ddp_funcs[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_GAMMA]		= &ddp_gamma,
 	[MTK_DISP_MERGE]		= &ddp_merge,
 	[MTK_DISP_OD]			= &ddp_od,
+	[MTK_DISP_OUTPROC]		= &ddp_outproc,
 	[MTK_DISP_OVL]			= &ddp_ovl,
 	[MTK_DISP_OVL_2L]		= &ddp_ovl,
 	[MTK_DISP_OVL_ADAPTOR]		= &ddp_ovl_adaptor,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index df4357b0954b..7b16f46ce9e2 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -121,6 +121,20 @@ void mtk_merge_stop_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt);
 enum drm_mode_status mtk_merge_mode_valid(struct device *dev,
 					  const struct drm_display_mode *mode);
 
+void mtk_outproc_start(struct device *dev);
+void mtk_outproc_stop(struct device *dev);
+int mtk_outproc_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_outproc_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_outproc_config(struct mtk_ddp_comp *comp, unsigned int w,
+			     unsigned int h, unsigned int vrefresh,
+			     unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
+void mtk_outproc_register_vblank_cb(struct device *dev,
+					 void (*vblank_cb)(void *),
+					 void *vblank_cb_data);
+void mtk_outproc_unregister_vblank_cb(struct device *dev);
+void mtk_outproc_enable_vblank(struct device *dev);
+void mtk_outproc_disable_vblank(struct device *dev);
+
 void mtk_ovl_bgclr_in_on(struct device *dev);
 void mtk_ovl_bgclr_in_off(struct device *dev);
 void mtk_ovl_bypass_shadow(struct device *dev);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_outproc.c b/drivers/gpu/drm/mediatek/mtk_disp_outproc.c
new file mode 100644
index 000000000000..b8eeefd69964
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_outproc.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ *                    Nancy Lin <nancy.lin@mediatek.com>
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/soc/mediatek/mtk-cmdq.h>
+#include <linux/soc/mediatek/mtk-mmsys.h>
+
+#include "mtk_crtc.h"
+#include "mtk_ddp_comp.h"
+#include "mtk_disp_drv.h"
+#include "mtk_drm_drv.h"
+
+#define DISP_REG_OVL_OUTPROC_INTEN			0x004
+#define DISP_REG_OVL_OUTPROC_INTSTA			0x008
+#  define OVL_OUTPROC_FME_CPL_INTEN			BIT(1)
+
+#define DISP_REG_OVL_OUTPROC_DATAPATH_CON		0x010
+#  define OVL_OUTPROC_DATAPATH_CON_OUTPUT_CLAMP		BIT(26)
+
+#define DISP_REG_OVL_OUTPROC_EN				0x020
+#  define OVL_OUTPROC_OVL_EN				BIT(0)
+
+#define DISP_REG_OVL_OUTPROC_RST			0x024
+#  define OVL_OUTPROC_RST				BIT(0)
+
+#define DISP_REG_OVL_OUTPROC_SHADOW_CTRL		0x028
+#  define OVL_OUTPROC_BYPASS_SHADOW			BIT(2)
+
+#define DISP_REG_OVL_OUTPROC_ROI_SIZE			0x030
+
+struct mtk_disp_outproc {
+	void __iomem		*regs;
+	struct clk		*clk;
+	void			(*vblank_cb)(void *data);
+	void			*vblank_cb_data;
+	int			irq;
+	struct cmdq_client_reg	cmdq_reg;
+};
+
+void mtk_outproc_register_vblank_cb(struct device *dev, void (*vblank_cb)(void *),
+				    void *vblank_cb_data)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	priv->vblank_cb = vblank_cb;
+	priv->vblank_cb_data = vblank_cb_data;
+}
+
+void mtk_outproc_unregister_vblank_cb(struct device *dev)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	priv->vblank_cb = NULL;
+	priv->vblank_cb_data = NULL;
+}
+
+void mtk_outproc_enable_vblank(struct device *dev)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	writel(0x0, priv->regs + DISP_REG_OVL_OUTPROC_INTSTA);
+	writel(OVL_OUTPROC_FME_CPL_INTEN, priv->regs + DISP_REG_OVL_OUTPROC_INTEN);
+}
+
+void mtk_outproc_disable_vblank(struct device *dev)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	writel(0x0, priv->regs + DISP_REG_OVL_OUTPROC_INTEN);
+}
+
+static irqreturn_t mtk_outproc_irq_handler(int irq, void *dev_id)
+{
+	struct mtk_disp_outproc *priv = dev_id;
+	u32 val;
+
+	val = readl(priv->regs + DISP_REG_OVL_OUTPROC_INTSTA);
+	if (val == 0)
+		return IRQ_NONE;
+
+	writel(0, priv->regs + DISP_REG_OVL_OUTPROC_INTSTA);
+
+	if (priv->vblank_cb)
+		priv->vblank_cb(priv->vblank_cb_data);
+
+	return IRQ_HANDLED;
+}
+
+void mtk_outproc_config(struct mtk_ddp_comp *comp, unsigned int w,
+			unsigned int h, unsigned int vrefresh,
+			unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(comp->dev);
+
+	writel(((h & GENMASK(15, 0)) << 16) | (w & GENMASK(15, 0)),
+	       priv->regs + DISP_REG_OVL_OUTPROC_ROI_SIZE);
+
+	writel(OVL_OUTPROC_DATAPATH_CON_OUTPUT_CLAMP,
+	       priv->regs + DISP_REG_OVL_OUTPROC_DATAPATH_CON);
+}
+
+void mtk_outproc_start(struct device *dev)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+	u32 val;
+
+	writel(OVL_OUTPROC_RST, priv->regs + DISP_REG_OVL_OUTPROC_RST);
+	writel(0, priv->regs + DISP_REG_OVL_OUTPROC_RST);
+
+	val = readl(priv->regs + DISP_REG_OVL_OUTPROC_SHADOW_CTRL);
+	val |= OVL_OUTPROC_BYPASS_SHADOW;
+	writel(val, priv->regs + DISP_REG_OVL_OUTPROC_SHADOW_CTRL);
+
+	writel(OVL_OUTPROC_OVL_EN, priv->regs + DISP_REG_OVL_OUTPROC_EN);
+}
+
+void mtk_outproc_stop(struct device *dev)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	/* Don't reset at stop time to avoid vblank timeouts */
+	writel(0, priv->regs + DISP_REG_OVL_OUTPROC_EN);
+}
+
+int mtk_outproc_clk_enable(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(comp->dev);
+
+	return clk_prepare_enable(priv->clk);
+}
+
+void mtk_outproc_clk_disable(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(comp->dev);
+
+	clk_disable_unprepare(priv->clk);
+}
+
+static int mtk_outproc_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	if (priv->irq)
+		enable_irq(priv->irq);
+
+	return 0;
+}
+
+static void mtk_outproc_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct mtk_disp_outproc *priv = dev_get_drvdata(dev);
+
+	if (priv->irq)
+		disable_irq(priv->irq);
+}
+
+static const struct component_ops mtk_disp_outproc_component_ops = {
+	.bind	= mtk_outproc_bind,
+	.unbind = mtk_outproc_unbind,
+};
+
+static int mtk_disp_outproc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_disp_outproc *priv;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->regs))
+		return dev_err_probe(dev, PTR_ERR(priv->regs), "Cannot get reg resource\n");
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clk))
+		return dev_err_probe(dev, PTR_ERR(priv->clk), "Cannot get clocks\n");
+
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+	ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
+	if (ret)
+		dev_dbg(dev, "No mediatek,gce-client-reg\n");
+#endif
+
+	priv->irq = platform_get_irq_optional(pdev, 0);
+	if (priv->irq > 0) {
+		ret = devm_request_irq(dev, priv->irq, mtk_outproc_irq_handler,
+				       IRQF_NO_AUTOEN, dev_name(dev), priv);
+		if (ret)
+			return dev_err_probe(dev, ret, "Failed to request irq\n");
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = component_add(dev, &mtk_disp_outproc_component_ops);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add component\n");
+
+	return ret;
+}
+
+static void mtk_disp_outproc_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_disp_outproc_component_ops);
+}
+
+static const struct of_device_id mtk_disp_outproc_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8196-disp-outproc" },
+	{ /* sentinel */},
+};
+
+MODULE_DEVICE_TABLE(of, mtk_disp_outproc_driver_dt_match);
+
+struct platform_driver mtk_disp_outproc_driver = {
+	.probe		= mtk_disp_outproc_probe,
+	.remove		= mtk_disp_outproc_remove,
+	.driver		= {
+		.name	= "mediatek-disp-outproc",
+		.owner	= THIS_MODULE,
+		.of_match_table = mtk_disp_outproc_driver_dt_match,
+	},
+};
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_AUTHOR("Nancy Lin <nancy.lin@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek Display Controller Output Processing Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 9f1264bb1d5e..3ed0e3f45fe2 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -557,6 +557,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt8173-disp-od",
 	  .data = (void *)MTK_DISP_OD },
+	{ .compatible = "mediatek,mt8196-disp-outproc",
+	  .data = (void *)MTK_DISP_OUTPROC },
 	{ .compatible = "mediatek,mt2701-disp-ovl",
 	  .data = (void *)MTK_DISP_OVL },
 	{ .compatible = "mediatek,mt8167-disp-ovl",
@@ -1070,6 +1072,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_exdma_driver,
 	&mtk_disp_gamma_driver,
 	&mtk_disp_merge_driver,
+	&mtk_disp_outproc_driver,
 	&mtk_disp_ovl_adaptor_driver,
 	&mtk_disp_ovl_driver,
 	&mtk_disp_rdma_driver,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 4ef61c1e33e3..3f84384f8569 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -80,6 +80,7 @@ extern struct platform_driver mtk_disp_dsc_driver;
 extern struct platform_driver mtk_disp_exdma_driver;
 extern struct platform_driver mtk_disp_gamma_driver;
 extern struct platform_driver mtk_disp_merge_driver;
+extern struct platform_driver mtk_disp_outproc_driver;
 extern struct platform_driver mtk_disp_ovl_adaptor_driver;
 extern struct platform_driver mtk_disp_ovl_driver;
 extern struct platform_driver mtk_disp_rdma_driver;
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index d5c082d920c4..a1c8a436b3dc 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -99,6 +99,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_MERGE,
 	MTK_DISP_MUTEX,
 	MTK_DISP_OD,
+	MTK_DISP_OUTPROC,
 	MTK_DISP_OVL,
 	MTK_DISP_OVL_2L,
 	MTK_DISP_OVL_ADAPTOR,
-- 
2.54.0



^ permalink raw reply related

* [PATCH 11/42] drm/mediatek: Add support for MuteX trigger-sources parsing
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add support for getting the Mute-X trigger source ID from the
devicetree of each component requiring one.

In order to retain compatibility with older devicetrees that are
not specifying one, add a hardcoded table of Mute-X IDs in the
mtk_drm_legacy code, and inject all of the hardcoded identifiers
during initialization in mtk_drm_drv if the MediaTek Mute-X node
does not have #trigger-source-cells as this implies that the
driver is surely dealing with an old devicetree.

While at it, also migrate the mtk_disp_ovl_adaptor registration
mechanism to mtk_drm_legacy and change its platform data to get
a struct mtk_drm_private instead of mmsys_dev: this is done in
order to get a handle to the MuteX node in OVL_ADAPTOR, which
purpose is to get the legacy trigger IDs through, again, tables
in mtk_drm_legacy.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c       |  19 +
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h       |   2 +
 .../gpu/drm/mediatek/mtk_disp_ovl_adaptor.c   |  17 +-
 drivers/gpu/drm/mediatek/mtk_drm_drv.c        |  28 +-
 drivers/gpu/drm/mediatek/mtk_drm_legacy.c     | 567 ++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_legacy.h     |   8 +
 6 files changed, 621 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index b0d38646ca03..f45588ae7342 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -604,6 +604,21 @@ static void mtk_ddp_comp_clk_put(void *_clk)
 	clk_put(clk);
 }
 
+int mtk_ddp_comp_get_mutex_trigger(struct device_node *node, unsigned int index)
+{
+	struct fwnode_reference_args mutex_trigger;
+	int ret;
+
+	ret = fwnode_property_get_reference_args(of_fwnode_handle(node),
+						 "trigger-sources",
+						 "#trigger-source-cells",
+						 0, index, &mutex_trigger);
+	if (ret < 0)
+		return ret;
+
+	return mutex_trigger.args[0];
+}
+
 static bool mtk_ddp_comp_is_backlight_comp(enum mtk_ddp_comp_type type)
 {
 	return type == MTK_DISP_BLS || type == MTK_DISP_PWM;
@@ -696,6 +711,10 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 	if (ret)
 		return ret;
 
+	ret = mtk_ddp_comp_get_mutex_trigger(node, 0);
+	if (ret >= 0)
+		comp->mtx_trig_id = ret;
+
 	/* If there's no external driver for this component, allocate and init now */
 	if (mtk_ddp_comp_is_internal_comp(type) || mtk_ddp_comp_is_backlight_comp(type)) {
 		ret = mtk_ddp_comp_init_internal_comp(dev, comp->dev);
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index 76a2b649516d..5206445b7157 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -81,6 +81,7 @@ struct mtk_ddp_comp {
 	struct device *dev;
 	int irq;
 	unsigned int id;
+	u8 mtx_trig_id;
 	int encoder_index;
 	const struct mtk_ddp_comp_funcs *funcs;
 
@@ -360,6 +361,7 @@ int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev);
 int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 		      struct mtk_drm_comp_list *hlist,
 		      unsigned int comp_id);
+int mtk_ddp_comp_get_mutex_trigger(struct device_node *node, unsigned int index);
 enum mtk_ddp_comp_type mtk_ddp_comp_get_type(unsigned int comp_id);
 void mtk_ddp_write(struct cmdq_pkt *cmdq_pkt, unsigned int value,
 		   struct cmdq_client_reg *cmdq_reg, void __iomem *regs,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index c0af3e3b51d5..ed9a2e35ba8a 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -20,6 +20,7 @@
 #include "mtk_ddp_comp.h"
 #include "mtk_disp_drv.h"
 #include "mtk_drm_drv.h"
+#include "mtk_drm_legacy.h"
 #include "mtk_ethdr.h"
 
 #define MTK_OVL_ADAPTOR_RDMA_MAX_WIDTH 1920
@@ -68,6 +69,7 @@ struct ovl_adaptor_comp_match {
 struct mtk_disp_ovl_adaptor {
 	struct device *ovl_adaptor_comp[OVL_ADAPTOR_ID_MAX];
 	struct device *mmsys_dev;
+	u8 mtx_trig_ids[OVL_ADAPTOR_ID_MAX];
 	bool children_bound;
 };
 
@@ -534,7 +536,8 @@ static void ovl_adaptor_put_device(void *_dev)
 	put_device(dev);
 }
 
-static int ovl_adaptor_comp_init(struct device *dev, struct component_match **match)
+static int ovl_adaptor_comp_init(struct device *dev, struct device_node *mutex_node,
+				 struct component_match **match)
 {
 	struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
 	struct device_node *parent;
@@ -544,7 +547,9 @@ static int ovl_adaptor_comp_init(struct device *dev, struct component_match **ma
 
 	for_each_child_of_node_scoped(parent, node) {
 		enum mtk_ovl_adaptor_comp_type type;
+		enum mtk_ddp_comp_id ddp_type;
 		int id, ret;
+		u8 mtx_id;
 
 		ret = ovl_adaptor_of_get_ddp_comp_type(node, &type);
 		if (ret)
@@ -563,6 +568,10 @@ static int ovl_adaptor_comp_init(struct device *dev, struct component_match **ma
 			continue;
 		}
 
+		ddp_type = comp_matches[id].comp_id;
+		mtx_id = mtk_drm_legacy_get_ovl_adaptor_mutex_trig_id(ddp_type,
+								      mutex_node);
+
 		comp_pdev = of_find_device_by_node(node);
 		if (!comp_pdev)
 			return -EPROBE_DEFER;
@@ -573,6 +582,7 @@ static int ovl_adaptor_comp_init(struct device *dev, struct component_match **ma
 			return ret;
 
 		priv->ovl_adaptor_comp[id] = &comp_pdev->dev;
+		priv->mtx_trig_ids[id] = mtx_id;
 
 		drm_of_component_match_add(dev, match, component_compare_of, node);
 		dev_dbg(dev, "Adding component match for %pOF\n", node);
@@ -634,6 +644,7 @@ static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
 
 static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
 {
+	struct mtk_drm_private *drm_private = pdev->dev.platform_data;
 	struct mtk_disp_ovl_adaptor *priv;
 	struct device *dev = &pdev->dev;
 	struct component_match *match = NULL;
@@ -645,11 +656,11 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, priv);
 
-	ret = ovl_adaptor_comp_init(dev, &match);
+	ret = ovl_adaptor_comp_init(dev, drm_private->mutex_node, &match);
 	if (ret < 0)
 		return ret;
 
-	priv->mmsys_dev = pdev->dev.platform_data;
+	priv->mmsys_dev = drm_private->mmsys_dev;
 
 	component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 1396cbc65627..cdff5edd09da 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -409,11 +409,6 @@ static const struct drm_driver mtk_drm_driver = {
 	.minor = DRIVER_MINOR,
 };
 
-static int compare_dev(struct device *dev, void *data)
-{
-	return dev == (struct device *)data;
-}
-
 static int mtk_drm_bind(struct device *dev)
 {
 	struct mtk_drm_private *private = dev_get_drvdata(dev);
@@ -856,7 +851,6 @@ static int mtk_drm_probe(struct platform_device *pdev)
 	struct mtk_mmsys_driver_data *mtk_drm_data;
 	struct device_node *node;
 	struct component_match *match = NULL;
-	struct platform_device *ovl_adaptor;
 	int ret;
 	int i;
 
@@ -904,17 +898,6 @@ static int mtk_drm_probe(struct platform_device *pdev)
 	if (!private->all_drm_private)
 		return -ENOMEM;
 
-	/* Bringup ovl_adaptor */
-	if (mtk_drm_find_mmsys_comp(private, DDP_COMPONENT_DRM_OVL_ADAPTOR)) {
-		ovl_adaptor = platform_device_register_data(dev, "mediatek-disp-ovl-adaptor",
-							    PLATFORM_DEVID_AUTO,
-							    (void *)private->mmsys_dev,
-							    sizeof(*private->mmsys_dev));
-		mtk_ddp_comp_init(&ovl_adaptor->dev, NULL, &private->hlist,
-				  DDP_COMPONENT_DRM_OVL_ADAPTOR);
-		component_match_add(dev, &match, compare_dev, &ovl_adaptor->dev);
-	}
-
 	/* Iterate over sibling DISP function blocks */
 	for_each_child_of_node(phandle->parent, node) {
 		enum mtk_ddp_comp_type comp_type;
@@ -979,6 +962,17 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		goto err_node;
 	}
 
+	/* If mtk-mutex is not a trigger source, this is an old devicetree */
+	if (!of_property_present(private->mutex_node, "#trigger-source-cells")) {
+		ret = mtk_drm_legacy_inject_mutex_trig_ids(&private->hlist, private->mutex_node);
+		if (ret)
+			return ret;
+	}
+
+	/* Bringup ovl_adaptor */
+	if (mtk_drm_find_mmsys_comp(private, DDP_COMPONENT_DRM_OVL_ADAPTOR))
+		mtk_drm_legacy_ovl_adaptor_probe(dev, private, &match);
+
 	pm_runtime_enable(dev);
 
 	platform_set_drvdata(pdev, private);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_legacy.c b/drivers/gpu/drm/mediatek/mtk_drm_legacy.c
index 623e510de9ff..d6e3ab7e08ba 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_legacy.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_legacy.c
@@ -29,9 +29,224 @@
  * added to this file.
  */
 
+#include <linux/component.h>
+#include <linux/of.h>
+
 #include "mtk_drm_drv.h"
 #include "mtk_drm_legacy.h"
 
+#define MT2701_MUTEX_MOD_DISP_OVL		3
+#define MT2701_MUTEX_MOD_DISP_WDMA		6
+#define MT2701_MUTEX_MOD_DISP_COLOR		7
+#define MT2701_MUTEX_MOD_DISP_BLS		9
+#define MT2701_MUTEX_MOD_DISP_RDMA0		10
+#define MT2701_MUTEX_MOD_DISP_RDMA1		12
+
+#define MT2712_MUTEX_MOD_DISP_PWM2		10
+#define MT2712_MUTEX_MOD_DISP_OVL0		11
+#define MT2712_MUTEX_MOD_DISP_OVL1		12
+#define MT2712_MUTEX_MOD_DISP_RDMA0		13
+#define MT2712_MUTEX_MOD_DISP_RDMA1		14
+#define MT2712_MUTEX_MOD_DISP_RDMA2		15
+#define MT2712_MUTEX_MOD_DISP_WDMA0		16
+#define MT2712_MUTEX_MOD_DISP_WDMA1		17
+#define MT2712_MUTEX_MOD_DISP_COLOR0		18
+#define MT2712_MUTEX_MOD_DISP_COLOR1		19
+#define MT2712_MUTEX_MOD_DISP_AAL0		20
+#define MT2712_MUTEX_MOD_DISP_UFOE		22
+#define MT2712_MUTEX_MOD_DISP_PWM0		23
+#define MT2712_MUTEX_MOD_DISP_PWM1		24
+#define MT2712_MUTEX_MOD_DISP_OD0		25
+#define MT2712_MUTEX_MOD2_DISP_AAL1		33
+#define MT2712_MUTEX_MOD2_DISP_OD1		34
+
+#define MT6893_MUTEX_MOD_DISP_OVL0		0
+#define MT6893_MUTEX_MOD_DISP_OVL0_2L		1
+#define MT6893_MUTEX_MOD_DISP_RDMA0		2
+#define MT6893_MUTEX_MOD_DISP_WDMA0		3
+#define MT6893_MUTEX_MOD_DISP_COLOR0		4
+#define MT6893_MUTEX_MOD_DISP_CCORR0		5
+#define MT6893_MUTEX_MOD_DISP_AAL0		6
+#define MT6893_MUTEX_MOD_DISP_GAMMA0		7
+#define MT6893_MUTEX_MOD_DISP_DITHER0		8
+#define MT6893_MUTEX_MOD_DISP_DSI0		9
+#define MT6893_MUTEX_MOD_DISP_PWM0		11
+#define MT6893_MUTEX_MOD_DISP_OVL1		12
+#define MT6893_MUTEX_MOD_DISP_OVL1_2L		13
+#define MT6893_MUTEX_MOD_DISP_RDMA1		14
+#define MT6893_MUTEX_MOD_DISP_WDMA1		15
+#define MT6893_MUTEX_MOD_DISP_COLOR1		16
+#define MT6893_MUTEX_MOD_DISP_AAL1		18
+#define MT6893_MUTEX_MOD_DISP_DITHER1		20
+#define MT6893_MUTEX_MOD_DISP_DSI1		21
+#define MT6893_MUTEX_MOD_DISP_OVL2		23
+#define MT6893_MUTEX_MOD_DISP_POSTMASK0		25
+#define MT6893_MUTEX_MOD_DISP_MERGE0		27
+#define MT6893_MUTEX_MOD_DISP_MERGE1		28
+#define MT6893_MUTEX_MOD_DISP_DSC0		29
+#define MT6893_MUTEX_MOD_DISP_DP		31
+#define MT6893_MUTEX_MOD_DISP_RDMA4		34
+
+#define MT8167_MUTEX_MOD_DISP_PWM		1
+#define MT8167_MUTEX_MOD_DISP_OVL0		6
+#define MT8167_MUTEX_MOD_DISP_OVL1		7
+#define MT8167_MUTEX_MOD_DISP_RDMA0		8
+#define MT8167_MUTEX_MOD_DISP_RDMA1		9
+#define MT8167_MUTEX_MOD_DISP_WDMA0		10
+#define MT8167_MUTEX_MOD_DISP_CCORR		11
+#define MT8167_MUTEX_MOD_DISP_COLOR		12
+#define MT8167_MUTEX_MOD_DISP_AAL		13
+#define MT8167_MUTEX_MOD_DISP_GAMMA		14
+#define MT8167_MUTEX_MOD_DISP_DITHER		15
+#define MT8167_MUTEX_MOD_DISP_UFOE		16
+
+#define MT8173_MUTEX_MOD_DISP_OVL0		11
+#define MT8173_MUTEX_MOD_DISP_OVL1		12
+#define MT8173_MUTEX_MOD_DISP_RDMA0		13
+#define MT8173_MUTEX_MOD_DISP_RDMA1		14
+#define MT8173_MUTEX_MOD_DISP_RDMA2		15
+#define MT8173_MUTEX_MOD_DISP_WDMA0		16
+#define MT8173_MUTEX_MOD_DISP_WDMA1		17
+#define MT8173_MUTEX_MOD_DISP_COLOR0		18
+#define MT8173_MUTEX_MOD_DISP_COLOR1		19
+#define MT8173_MUTEX_MOD_DISP_AAL		20
+#define MT8173_MUTEX_MOD_DISP_GAMMA		21
+#define MT8173_MUTEX_MOD_DISP_UFOE		22
+#define MT8173_MUTEX_MOD_DISP_PWM0		23
+#define MT8173_MUTEX_MOD_DISP_PWM1		24
+#define MT8173_MUTEX_MOD_DISP_OD		25
+
+#define MT8183_MUTEX_MOD_DISP_RDMA0		0
+#define MT8183_MUTEX_MOD_DISP_RDMA1		1
+#define MT8183_MUTEX_MOD_DISP_OVL0		9
+#define MT8183_MUTEX_MOD_DISP_OVL0_2L		10
+#define MT8183_MUTEX_MOD_DISP_OVL1_2L		11
+#define MT8183_MUTEX_MOD_DISP_WDMA0		12
+#define MT8183_MUTEX_MOD_DISP_COLOR0		13
+#define MT8183_MUTEX_MOD_DISP_CCORR0		14
+#define MT8183_MUTEX_MOD_DISP_AAL0		15
+#define MT8183_MUTEX_MOD_DISP_GAMMA0		16
+#define MT8183_MUTEX_MOD_DISP_DITHER0		17
+
+#define MT8186_MUTEX_MOD_DISP_OVL0		0
+#define MT8186_MUTEX_MOD_DISP_OVL0_2L		1
+#define MT8186_MUTEX_MOD_DISP_RDMA0		2
+#define MT8186_MUTEX_MOD_DISP_COLOR0		4
+#define MT8186_MUTEX_MOD_DISP_CCORR0		5
+#define MT8186_MUTEX_MOD_DISP_AAL0		7
+#define MT8186_MUTEX_MOD_DISP_GAMMA0		8
+#define MT8186_MUTEX_MOD_DISP_POSTMASK0		9
+#define MT8186_MUTEX_MOD_DISP_DITHER0		10
+#define MT8186_MUTEX_MOD_DISP_RDMA1		17
+
+#define MT8188_MUTEX_MOD_DISP_OVL0		0
+#define MT8188_MUTEX_MOD_DISP_WDMA0		1
+#define MT8188_MUTEX_MOD_DISP_RDMA0		2
+#define MT8188_MUTEX_MOD_DISP_COLOR0		3
+#define MT8188_MUTEX_MOD_DISP_CCORR0		4
+#define MT8188_MUTEX_MOD_DISP_AAL0		5
+#define MT8188_MUTEX_MOD_DISP_GAMMA0		6
+#define MT8188_MUTEX_MOD_DISP_DITHER0		7
+#define MT8188_MUTEX_MOD_DISP_DSI0		8
+#define MT8188_MUTEX_MOD_DISP_DSC_WRAP0_CORE0	9
+#define MT8188_MUTEX_MOD_DISP_VPP_MERGE		20
+#define MT8188_MUTEX_MOD_DISP_DP_INTF0		21
+#define MT8188_MUTEX_MOD_DISP_POSTMASK0		24
+#define MT8188_MUTEX_MOD2_DISP_PWM0		33
+
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA0	0
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA1	1
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA2	2
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA3	3
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA4	4
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA5	5
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA6	6
+#define MT8188_MUTEX_MOD_DISP1_MDP_RDMA7	7
+#define MT8188_MUTEX_MOD_DISP1_PADDING0		8
+#define MT8188_MUTEX_MOD_DISP1_PADDING1		9
+#define MT8188_MUTEX_MOD_DISP1_PADDING2		10
+#define MT8188_MUTEX_MOD_DISP1_PADDING3		11
+#define MT8188_MUTEX_MOD_DISP1_PADDING4		12
+#define MT8188_MUTEX_MOD_DISP1_PADDING5		13
+#define MT8188_MUTEX_MOD_DISP1_PADDING6		14
+#define MT8188_MUTEX_MOD_DISP1_PADDING7		15
+#define MT8188_MUTEX_MOD_DISP1_VPP_MERGE0	20
+#define MT8188_MUTEX_MOD_DISP1_VPP_MERGE1	21
+#define MT8188_MUTEX_MOD_DISP1_VPP_MERGE2	22
+#define MT8188_MUTEX_MOD_DISP1_VPP_MERGE3	23
+#define MT8188_MUTEX_MOD_DISP1_VPP_MERGE4	24
+#define MT8188_MUTEX_MOD_DISP1_DISP_MIXER	30
+#define MT8188_MUTEX_MOD_DISP1_DPI1		38
+#define MT8188_MUTEX_MOD_DISP1_DP_INTF1		39
+
+#define MT8192_MUTEX_MOD_DISP_OVL0		0
+#define MT8192_MUTEX_MOD_DISP_OVL0_2L		1
+#define MT8192_MUTEX_MOD_DISP_RDMA0		2
+#define MT8192_MUTEX_MOD_DISP_COLOR0		4
+#define MT8192_MUTEX_MOD_DISP_CCORR0		5
+#define MT8192_MUTEX_MOD_DISP_AAL0		6
+#define MT8192_MUTEX_MOD_DISP_GAMMA0		7
+#define MT8192_MUTEX_MOD_DISP_POSTMASK0		8
+#define MT8192_MUTEX_MOD_DISP_DITHER0		9
+#define MT8192_MUTEX_MOD_DISP_OVL2_2L		16
+#define MT8192_MUTEX_MOD_DISP_RDMA4		17
+
+#define MT8195_MUTEX_MOD_DISP_OVL0		0
+#define MT8195_MUTEX_MOD_DISP_WDMA0		1
+#define MT8195_MUTEX_MOD_DISP_RDMA0		2
+#define MT8195_MUTEX_MOD_DISP_COLOR0		3
+#define MT8195_MUTEX_MOD_DISP_CCORR0		4
+#define MT8195_MUTEX_MOD_DISP_AAL0		5
+#define MT8195_MUTEX_MOD_DISP_GAMMA0		6
+#define MT8195_MUTEX_MOD_DISP_DITHER0		7
+#define MT8195_MUTEX_MOD_DISP_DSI0		8
+#define MT8195_MUTEX_MOD_DISP_DSC_WRAP0_CORE0	9
+#define MT8195_MUTEX_MOD_DISP_VPP_MERGE		20
+#define MT8195_MUTEX_MOD_DISP_DP_INTF0		21
+#define MT8195_MUTEX_MOD_DISP_PWM0		27
+
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA0	0
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA1	1
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA2	2
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA3	3
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA4	4
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA5	5
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA6	6
+#define MT8195_MUTEX_MOD_DISP1_MDP_RDMA7	7
+#define MT8195_MUTEX_MOD_DISP1_VPP_MERGE0	8
+#define MT8195_MUTEX_MOD_DISP1_VPP_MERGE1	9
+#define MT8195_MUTEX_MOD_DISP1_VPP_MERGE2	10
+#define MT8195_MUTEX_MOD_DISP1_VPP_MERGE3	11
+#define MT8195_MUTEX_MOD_DISP1_VPP_MERGE4	12
+#define MT8195_MUTEX_MOD_DISP1_DISP_MIXER	18
+#define MT8195_MUTEX_MOD_DISP1_DPI0		25
+#define MT8195_MUTEX_MOD_DISP1_DPI1		26
+#define MT8195_MUTEX_MOD_DISP1_DP_INTF0		27
+
+#define MT8365_MUTEX_MOD_DISP_OVL0		7
+#define MT8365_MUTEX_MOD_DISP_OVL0_2L		8
+#define MT8365_MUTEX_MOD_DISP_RDMA0		9
+#define MT8365_MUTEX_MOD_DISP_RDMA1		10
+#define MT8365_MUTEX_MOD_DISP_WDMA0		11
+#define MT8365_MUTEX_MOD_DISP_COLOR0		12
+#define MT8365_MUTEX_MOD_DISP_CCORR		13
+#define MT8365_MUTEX_MOD_DISP_AAL		14
+#define MT8365_MUTEX_MOD_DISP_GAMMA		15
+#define MT8365_MUTEX_MOD_DISP_DITHER		16
+#define MT8365_MUTEX_MOD_DISP_DSI0		17
+#define MT8365_MUTEX_MOD_DISP_PWM0		20
+#define MT8365_MUTEX_MOD_DISP_DPI0		22
+
+struct mtk_drm_legacy_mtx_pairs {
+	struct mtk_drm_comp_definition comp;
+	u8 mtx_trig_id;
+};
+
+struct mtk_drm_legacy_mtx_data {
+	const struct mtk_drm_legacy_mtx_pairs *pairs;
+	u8 num_pairs;
+};
+
 static const struct mtk_drm_comp_definition mt2701_mtk_ddp_main[] = {
 	{ DDP_COMPONENT_OVL0 },
 	{ DDP_COMPONENT_RDMA0 },
@@ -307,3 +522,355 @@ struct mtk_drm_path_definition mt8195_vdo1_legacy_paths[MAX_CRTC] = {
 		.len = ARRAY_SIZE(mt8195_mtk_ddp_ext),
 	},
 };
+
+static const struct mtk_drm_legacy_mtx_pairs mt2701_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_BLS }, MT2701_MUTEX_MOD_DISP_BLS },
+	{ { DDP_COMPONENT_COLOR0 }, MT2701_MUTEX_MOD_DISP_COLOR },
+	{ { DDP_COMPONENT_OVL0 }, MT2701_MUTEX_MOD_DISP_OVL },
+	{ { DDP_COMPONENT_RDMA0 }, MT2701_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT2701_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_WDMA0 }, MT2701_MUTEX_MOD_DISP_WDMA },
+};
+
+const struct mtk_drm_legacy_mtx_data mt2701_legacy_mtx_data = {
+	.pairs = mt2701_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt2701_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt2712_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT2712_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_AAL1 }, MT2712_MUTEX_MOD2_DISP_AAL1 },
+	{ { DDP_COMPONENT_COLOR0 }, MT2712_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_COLOR1 }, MT2712_MUTEX_MOD_DISP_COLOR1 },
+	{ { DDP_COMPONENT_OD0 }, MT2712_MUTEX_MOD_DISP_OD0 },
+	{ { DDP_COMPONENT_OD1 }, MT2712_MUTEX_MOD2_DISP_OD1 },
+	{ { DDP_COMPONENT_OVL0 }, MT2712_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL1 }, MT2712_MUTEX_MOD_DISP_OVL1 },
+	{ { DDP_COMPONENT_PWM0 }, MT2712_MUTEX_MOD_DISP_PWM0 },
+	{ { DDP_COMPONENT_PWM1 }, MT2712_MUTEX_MOD_DISP_PWM1 },
+	{ { DDP_COMPONENT_PWM2 }, MT2712_MUTEX_MOD_DISP_PWM2 },
+	{ { DDP_COMPONENT_RDMA0 }, MT2712_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT2712_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_RDMA2 }, MT2712_MUTEX_MOD_DISP_RDMA2 },
+	{ { DDP_COMPONENT_UFOE }, MT2712_MUTEX_MOD_DISP_UFOE },
+	{ { DDP_COMPONENT_WDMA0 }, MT2712_MUTEX_MOD_DISP_WDMA0 },
+	{ { DDP_COMPONENT_WDMA1 }, MT2712_MUTEX_MOD_DISP_WDMA1 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt2712_legacy_mtx_data = {
+	.pairs = mt2712_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt2712_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt6893_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT6893_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_AAL1 }, MT6893_MUTEX_MOD_DISP_AAL1 },
+	{ { DDP_COMPONENT_CCORR }, MT6893_MUTEX_MOD_DISP_CCORR0 },
+	{ { DDP_COMPONENT_COLOR0 }, MT6893_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_COLOR1 }, MT6893_MUTEX_MOD_DISP_COLOR1 },
+	{ { DDP_COMPONENT_DITHER0 }, MT6893_MUTEX_MOD_DISP_DITHER0 },
+	{ { DDP_COMPONENT_DITHER1 }, MT6893_MUTEX_MOD_DISP_DITHER1 },
+	{ { DDP_COMPONENT_DP_INTF0 }, MT6893_MUTEX_MOD_DISP_DP },
+	{ { DDP_COMPONENT_DSC0 }, MT6893_MUTEX_MOD_DISP_DSC0 },
+	{ { DDP_COMPONENT_DSI0 }, MT6893_MUTEX_MOD_DISP_DSI0 },
+	{ { DDP_COMPONENT_DSI1 }, MT6893_MUTEX_MOD_DISP_DSI1 },
+	{ { DDP_COMPONENT_GAMMA }, MT6893_MUTEX_MOD_DISP_GAMMA0 },
+	{ { DDP_COMPONENT_MERGE1 }, MT6893_MUTEX_MOD_DISP_MERGE1 },
+	{ { DDP_COMPONENT_OVL0 }, MT6893_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL1 }, MT6893_MUTEX_MOD_DISP_OVL1 },
+	{ { DDP_COMPONENT_OVL_2L0 }, MT6893_MUTEX_MOD_DISP_OVL0_2L },
+	{ { DDP_COMPONENT_OVL_2L1 }, MT6893_MUTEX_MOD_DISP_OVL1_2L },
+	{ { DDP_COMPONENT_OVL_2L2 }, MT6893_MUTEX_MOD_DISP_OVL2 },
+	{ { DDP_COMPONENT_POSTMASK0 }, MT6893_MUTEX_MOD_DISP_POSTMASK0 },
+	{ { DDP_COMPONENT_PWM0 }, MT6893_MUTEX_MOD_DISP_PWM0 },
+	{ { DDP_COMPONENT_RDMA0 }, MT6893_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT6893_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_RDMA4 }, MT6893_MUTEX_MOD_DISP_RDMA4 },
+	{ { DDP_COMPONENT_WDMA0 }, MT6893_MUTEX_MOD_DISP_WDMA0 },
+	{ { DDP_COMPONENT_WDMA1 }, MT6893_MUTEX_MOD_DISP_WDMA1 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt6893_legacy_mtx_data = {
+	.pairs = mt6893_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt6893_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8167_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT8167_MUTEX_MOD_DISP_AAL },
+	{ { DDP_COMPONENT_CCORR }, MT8167_MUTEX_MOD_DISP_CCORR },
+	{ { DDP_COMPONENT_COLOR0 }, MT8167_MUTEX_MOD_DISP_COLOR },
+	{ { DDP_COMPONENT_DITHER0 }, MT8167_MUTEX_MOD_DISP_DITHER },
+	{ { DDP_COMPONENT_GAMMA }, MT8167_MUTEX_MOD_DISP_GAMMA },
+	{ { DDP_COMPONENT_OVL0 }, MT8167_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL1 }, MT8167_MUTEX_MOD_DISP_OVL1 },
+	{ { DDP_COMPONENT_PWM0 }, MT8167_MUTEX_MOD_DISP_PWM },
+	{ { DDP_COMPONENT_RDMA0 }, MT8167_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT8167_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_UFOE }, MT8167_MUTEX_MOD_DISP_UFOE },
+	{ { DDP_COMPONENT_WDMA0 }, MT8167_MUTEX_MOD_DISP_WDMA0 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8167_legacy_mtx_data = {
+	.pairs = mt8167_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8167_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8173_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT8173_MUTEX_MOD_DISP_AAL },
+	{ { DDP_COMPONENT_COLOR0 }, MT8173_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_COLOR1 }, MT8173_MUTEX_MOD_DISP_COLOR1 },
+	{ { DDP_COMPONENT_GAMMA }, MT8173_MUTEX_MOD_DISP_GAMMA },
+	{ { DDP_COMPONENT_OD0 }, MT8173_MUTEX_MOD_DISP_OD },
+	{ { DDP_COMPONENT_OVL0 }, MT8173_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL1 }, MT8173_MUTEX_MOD_DISP_OVL1 },
+	{ { DDP_COMPONENT_PWM0 }, MT8173_MUTEX_MOD_DISP_PWM0 },
+	{ { DDP_COMPONENT_PWM1 }, MT8173_MUTEX_MOD_DISP_PWM1 },
+	{ { DDP_COMPONENT_RDMA0 }, MT8173_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT8173_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_RDMA2 }, MT8173_MUTEX_MOD_DISP_RDMA2 },
+	{ { DDP_COMPONENT_UFOE }, MT8173_MUTEX_MOD_DISP_UFOE },
+	{ { DDP_COMPONENT_WDMA0 }, MT8173_MUTEX_MOD_DISP_WDMA0 },
+	{ { DDP_COMPONENT_WDMA1 }, MT8173_MUTEX_MOD_DISP_WDMA1 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8173_legacy_mtx_data = {
+	.pairs = mt8173_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8173_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8183_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT8183_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_CCORR }, MT8183_MUTEX_MOD_DISP_CCORR0 },
+	{ { DDP_COMPONENT_COLOR0 }, MT8183_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_DITHER0 }, MT8183_MUTEX_MOD_DISP_DITHER0 },
+	{ { DDP_COMPONENT_GAMMA }, MT8183_MUTEX_MOD_DISP_GAMMA0 },
+	{ { DDP_COMPONENT_OVL0 }, MT8183_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL_2L0 }, MT8183_MUTEX_MOD_DISP_OVL0_2L },
+	{ { DDP_COMPONENT_OVL_2L1 }, MT8183_MUTEX_MOD_DISP_OVL1_2L },
+	{ { DDP_COMPONENT_RDMA0 }, MT8183_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT8183_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_WDMA0 }, MT8183_MUTEX_MOD_DISP_WDMA0 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8183_legacy_mtx_data = {
+	.pairs = mt8183_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8183_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8186_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT8186_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_CCORR }, MT8186_MUTEX_MOD_DISP_CCORR0 },
+	{ { DDP_COMPONENT_COLOR0 }, MT8186_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_DITHER0 }, MT8186_MUTEX_MOD_DISP_DITHER0 },
+	{ { DDP_COMPONENT_GAMMA }, MT8186_MUTEX_MOD_DISP_GAMMA0 },
+	{ { DDP_COMPONENT_OVL0 }, MT8186_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL_2L0 }, MT8186_MUTEX_MOD_DISP_OVL0_2L },
+	{ { DDP_COMPONENT_POSTMASK0 }, MT8186_MUTEX_MOD_DISP_POSTMASK0 },
+	{ { DDP_COMPONENT_RDMA0 }, MT8186_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT8186_MUTEX_MOD_DISP_RDMA1 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8186_legacy_mtx_data = {
+	.pairs = mt8186_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8186_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8188_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_OVL0 }, MT8188_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_WDMA0 }, MT8188_MUTEX_MOD_DISP_WDMA0 },
+	{ { DDP_COMPONENT_RDMA0 }, MT8188_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_COLOR0 }, MT8188_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_CCORR }, MT8188_MUTEX_MOD_DISP_CCORR0 },
+	{ { DDP_COMPONENT_AAL0 }, MT8188_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_GAMMA }, MT8188_MUTEX_MOD_DISP_GAMMA0 },
+	{ { DDP_COMPONENT_POSTMASK0 }, MT8188_MUTEX_MOD_DISP_POSTMASK0 },
+	{ { DDP_COMPONENT_DITHER0 }, MT8188_MUTEX_MOD_DISP_DITHER0 },
+	{ { DDP_COMPONENT_MERGE0 }, MT8188_MUTEX_MOD_DISP_VPP_MERGE },
+	{ { DDP_COMPONENT_DSC0 }, MT8188_MUTEX_MOD_DISP_DSC_WRAP0_CORE0 },
+	{ { DDP_COMPONENT_DSI0 }, MT8188_MUTEX_MOD_DISP_DSI0 },
+	{ { DDP_COMPONENT_PWM0 }, MT8188_MUTEX_MOD2_DISP_PWM0 },
+	{ { DDP_COMPONENT_DP_INTF0 }, MT8188_MUTEX_MOD_DISP_DP_INTF0 },
+	{ { DDP_COMPONENT_DP_INTF1 }, MT8188_MUTEX_MOD_DISP1_DP_INTF1 },
+	{ { DDP_COMPONENT_DPI1 }, MT8188_MUTEX_MOD_DISP1_DPI1 },
+	{ { DDP_COMPONENT_ETHDR_MIXER }, MT8188_MUTEX_MOD_DISP1_DISP_MIXER },
+	{ { DDP_COMPONENT_MDP_RDMA0 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA0 },
+	{ { DDP_COMPONENT_MDP_RDMA1 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA1 },
+	{ { DDP_COMPONENT_MDP_RDMA2 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA2 },
+	{ { DDP_COMPONENT_MDP_RDMA3 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA3 },
+	{ { DDP_COMPONENT_MDP_RDMA4 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA4 },
+	{ { DDP_COMPONENT_MDP_RDMA5 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA5 },
+	{ { DDP_COMPONENT_MDP_RDMA6 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA6 },
+	{ { DDP_COMPONENT_MDP_RDMA7 }, MT8188_MUTEX_MOD_DISP1_MDP_RDMA7 },
+	{ { DDP_COMPONENT_PADDING0 }, MT8188_MUTEX_MOD_DISP1_PADDING0 },
+	{ { DDP_COMPONENT_PADDING1 }, MT8188_MUTEX_MOD_DISP1_PADDING1 },
+	{ { DDP_COMPONENT_PADDING2 }, MT8188_MUTEX_MOD_DISP1_PADDING2 },
+	{ { DDP_COMPONENT_PADDING3 }, MT8188_MUTEX_MOD_DISP1_PADDING3 },
+	{ { DDP_COMPONENT_PADDING4 }, MT8188_MUTEX_MOD_DISP1_PADDING4 },
+	{ { DDP_COMPONENT_PADDING5 }, MT8188_MUTEX_MOD_DISP1_PADDING5 },
+	{ { DDP_COMPONENT_PADDING6 }, MT8188_MUTEX_MOD_DISP1_PADDING6 },
+	{ { DDP_COMPONENT_PADDING7 }, MT8188_MUTEX_MOD_DISP1_PADDING7 },
+	{ { DDP_COMPONENT_MERGE1 }, MT8188_MUTEX_MOD_DISP1_VPP_MERGE0 },
+	{ { DDP_COMPONENT_MERGE2 }, MT8188_MUTEX_MOD_DISP1_VPP_MERGE1 },
+	{ { DDP_COMPONENT_MERGE3 }, MT8188_MUTEX_MOD_DISP1_VPP_MERGE2 },
+	{ { DDP_COMPONENT_MERGE4 }, MT8188_MUTEX_MOD_DISP1_VPP_MERGE3 },
+	{ { DDP_COMPONENT_MERGE5 }, MT8188_MUTEX_MOD_DISP1_VPP_MERGE4 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8188_legacy_mtx_data = {
+	.pairs = mt8188_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8188_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8192_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT8192_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_CCORR }, MT8192_MUTEX_MOD_DISP_CCORR0 },
+	{ { DDP_COMPONENT_COLOR0 }, MT8192_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_DITHER0 }, MT8192_MUTEX_MOD_DISP_DITHER0 },
+	{ { DDP_COMPONENT_GAMMA }, MT8192_MUTEX_MOD_DISP_GAMMA0 },
+	{ { DDP_COMPONENT_POSTMASK0 }, MT8192_MUTEX_MOD_DISP_POSTMASK0 },
+	{ { DDP_COMPONENT_OVL0 }, MT8192_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL_2L0 }, MT8192_MUTEX_MOD_DISP_OVL0_2L },
+	{ { DDP_COMPONENT_OVL_2L2 }, MT8192_MUTEX_MOD_DISP_OVL2_2L },
+	{ { DDP_COMPONENT_RDMA0 }, MT8192_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA4 }, MT8192_MUTEX_MOD_DISP_RDMA4 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8192_legacy_mtx_data = {
+	.pairs = mt8192_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8192_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8195_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_OVL0 }, MT8195_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_WDMA0 }, MT8195_MUTEX_MOD_DISP_WDMA0 },
+	{ { DDP_COMPONENT_RDMA0 }, MT8195_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_COLOR0 }, MT8195_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_CCORR }, MT8195_MUTEX_MOD_DISP_CCORR0 },
+	{ { DDP_COMPONENT_AAL0 }, MT8195_MUTEX_MOD_DISP_AAL0 },
+	{ { DDP_COMPONENT_GAMMA }, MT8195_MUTEX_MOD_DISP_GAMMA0 },
+	{ { DDP_COMPONENT_DITHER0 }, MT8195_MUTEX_MOD_DISP_DITHER0 },
+	{ { DDP_COMPONENT_MERGE0 }, MT8195_MUTEX_MOD_DISP_VPP_MERGE },
+	{ { DDP_COMPONENT_DSC0 }, MT8195_MUTEX_MOD_DISP_DSC_WRAP0_CORE0 },
+	{ { DDP_COMPONENT_DSI0 }, MT8195_MUTEX_MOD_DISP_DSI0 },
+	{ { DDP_COMPONENT_PWM0 }, MT8195_MUTEX_MOD_DISP_PWM0 },
+	{ { DDP_COMPONENT_DP_INTF0 }, MT8195_MUTEX_MOD_DISP_DP_INTF0 },
+	{ { DDP_COMPONENT_MDP_RDMA0 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA0 },
+	{ { DDP_COMPONENT_MDP_RDMA1 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA1 },
+	{ { DDP_COMPONENT_MDP_RDMA2 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA2 },
+	{ { DDP_COMPONENT_MDP_RDMA3 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA3 },
+	{ { DDP_COMPONENT_MDP_RDMA4 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA4 },
+	{ { DDP_COMPONENT_MDP_RDMA5 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA5 },
+	{ { DDP_COMPONENT_MDP_RDMA6 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA6 },
+	{ { DDP_COMPONENT_MDP_RDMA7 }, MT8195_MUTEX_MOD_DISP1_MDP_RDMA7 },
+	{ { DDP_COMPONENT_MERGE1 }, MT8195_MUTEX_MOD_DISP1_VPP_MERGE0 },
+	{ { DDP_COMPONENT_MERGE2 }, MT8195_MUTEX_MOD_DISP1_VPP_MERGE1 },
+	{ { DDP_COMPONENT_MERGE3 }, MT8195_MUTEX_MOD_DISP1_VPP_MERGE2 },
+	{ { DDP_COMPONENT_MERGE4 }, MT8195_MUTEX_MOD_DISP1_VPP_MERGE3 },
+	{ { DDP_COMPONENT_ETHDR_MIXER }, MT8195_MUTEX_MOD_DISP1_DISP_MIXER },
+	{ { DDP_COMPONENT_MERGE5 }, MT8195_MUTEX_MOD_DISP1_VPP_MERGE4 },
+	{ { DDP_COMPONENT_DP_INTF1 }, MT8195_MUTEX_MOD_DISP1_DP_INTF0 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8195_legacy_mtx_data = {
+	.pairs = mt8195_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8195_legacy_mtx_trig_ids)
+};
+
+static const struct mtk_drm_legacy_mtx_pairs mt8365_legacy_mtx_trig_ids[] = {
+	{ { DDP_COMPONENT_AAL0 }, MT8365_MUTEX_MOD_DISP_AAL },
+	{ { DDP_COMPONENT_CCORR }, MT8365_MUTEX_MOD_DISP_CCORR },
+	{ { DDP_COMPONENT_COLOR0 }, MT8365_MUTEX_MOD_DISP_COLOR0 },
+	{ { DDP_COMPONENT_DITHER0 }, MT8365_MUTEX_MOD_DISP_DITHER },
+	{ { DDP_COMPONENT_DPI0 }, MT8365_MUTEX_MOD_DISP_DPI0 },
+	{ { DDP_COMPONENT_DSI0 }, MT8365_MUTEX_MOD_DISP_DSI0 },
+	{ { DDP_COMPONENT_GAMMA }, MT8365_MUTEX_MOD_DISP_GAMMA },
+	{ { DDP_COMPONENT_OVL0 }, MT8365_MUTEX_MOD_DISP_OVL0 },
+	{ { DDP_COMPONENT_OVL_2L0 }, MT8365_MUTEX_MOD_DISP_OVL0_2L },
+	{ { DDP_COMPONENT_PWM0 }, MT8365_MUTEX_MOD_DISP_PWM0 },
+	{ { DDP_COMPONENT_RDMA0 }, MT8365_MUTEX_MOD_DISP_RDMA0 },
+	{ { DDP_COMPONENT_RDMA1 }, MT8365_MUTEX_MOD_DISP_RDMA1 },
+	{ { DDP_COMPONENT_WDMA0 }, MT8365_MUTEX_MOD_DISP_WDMA0 },
+};
+
+const struct mtk_drm_legacy_mtx_data mt8365_legacy_mtx_data = {
+	.pairs = mt8365_legacy_mtx_trig_ids,
+	.num_pairs = ARRAY_SIZE(mt8365_legacy_mtx_trig_ids)
+};
+
+static const struct of_device_id mtk_drm_legacy_mtk_mutex_match[] = {
+	{ .compatible = "mediatek,mt2701-disp-mutex", .data = &mt2701_legacy_mtx_data },
+	{ .compatible = "mediatek,mt2712-disp-mutex", .data = &mt2712_legacy_mtx_data },
+	{ .compatible = "mediatek,mt6795-disp-mutex", .data = &mt8173_legacy_mtx_data },
+	{ .compatible = "mediatek.mt6893-disp-mutex", .data = &mt6893_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8167-disp-mutex", .data = &mt8167_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8173-disp-mutex", .data = &mt8173_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8183-disp-mutex", .data = &mt8183_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8186-disp-mutex", .data = &mt8186_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8188-disp-mutex", .data = &mt8188_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8192-disp-mutex", .data = &mt8192_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8195-disp-mutex", .data = &mt8195_legacy_mtx_data },
+	{ .compatible = "mediatek,mt8365-disp-mutex", .data = &mt8365_legacy_mtx_data },
+	{ /* sentinel */ },
+};
+
+int mtk_drm_legacy_inject_mutex_trig_ids(struct mtk_drm_comp_list *hlist,
+					 struct device_node *mutex_node)
+{
+	struct mtk_drm_legacy_mtx_data *data;
+	const struct of_device_id *of_id;
+	struct mtk_ddp_comp *ddp_comp;
+	int i;
+
+	of_id = of_match_node(mtk_drm_legacy_mtk_mutex_match, mutex_node);
+	if (!of_id)
+		return -ENODEV;
+
+	data = (struct mtk_drm_legacy_mtx_data *)of_id->data;
+
+	for (i = 0; i < data->num_pairs; i++) {
+		const struct mtk_drm_comp_definition *comp = &data->pairs[i].comp;
+
+		hash_for_each_possible(hlist->ddp_list, ddp_comp, lnode, comp->type)
+			ddp_comp->mtx_trig_id = data->pairs[i].mtx_trig_id;
+	}
+
+	return 0;
+}
+
+u8 mtk_drm_legacy_get_ovl_adaptor_mutex_trig_id(enum mtk_ddp_comp_id ddp_type,
+						struct device_node *mutex_node)
+{
+	struct mtk_drm_legacy_mtx_data *data;
+	const struct of_device_id *of_id;
+	int i;
+
+	of_id = of_match_node(mtk_drm_legacy_mtk_mutex_match, mutex_node);
+	if (!of_id)
+		return 0;
+
+	data = (struct mtk_drm_legacy_mtx_data *)of_id->data;
+
+	for (i = 0; i < data->num_pairs; i++) {
+		const struct mtk_drm_comp_definition *comp = &data->pairs[i].comp;
+
+		if (ddp_type != comp->type)
+			continue;
+
+		return data->pairs[i].mtx_trig_id;
+	}
+
+	return 0;
+}
+
+void mtk_drm_legacy_ovl_adaptor_probe(struct device *dev, struct mtk_drm_private *priv,
+				      struct component_match **match)
+{
+	struct platform_device *ovl_adaptor;
+
+	ovl_adaptor = platform_device_register_data(dev, "mediatek-disp-ovl-adaptor",
+						    PLATFORM_DEVID_AUTO,
+						    (void *)priv, sizeof(*priv));
+
+	mtk_ddp_comp_init(&ovl_adaptor->dev, NULL, &priv->hlist, DDP_COMPONENT_DRM_OVL_ADAPTOR);
+	component_match_add(dev, match, component_compare_dev, &ovl_adaptor->dev);
+}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_legacy.h b/drivers/gpu/drm/mediatek/mtk_drm_legacy.h
index a87741ec0dcd..45bcf2674628 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_legacy.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_legacy.h
@@ -25,4 +25,12 @@ extern struct mtk_drm_path_definition mt8192_legacy_paths[];
 extern struct mtk_drm_path_definition mt8195_vdo0_legacy_paths[];
 extern struct mtk_drm_path_definition mt8195_vdo1_legacy_paths[];
 
+int mtk_drm_legacy_inject_mutex_trig_ids(struct mtk_drm_comp_list *hlist,
+					 struct device_node *mutex_node);
+u8 mtk_drm_legacy_get_ovl_adaptor_mutex_trig_id(enum mtk_ddp_comp_id ddp_type,
+						struct device_node *mutex_node);
+
+void mtk_drm_legacy_ovl_adaptor_probe(struct device *dev, struct mtk_drm_private *priv,
+				      struct component_match **match);
+
 #endif /* MTK_DRM_LEGACY_H */
-- 
2.54.0



^ permalink raw reply related

* [PATCH 32/42] drm/mediatek: mtk_crtc: Dynamically find suitable CRTC DMA device
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Now that the vblank and config components are being dynamically
selected, the only hardcoded strategy that is left is selecting
a DMA device for a CRTC that is being created.

In order to support pipelines including an embedded multimedia
controller (like MDP3) chained with the display controller and
pipelines chaining multiple display controllers together, stop
assuming that the first component in the pipeline is suited to
do DMA operations and implement support to dynamically find not
only a suitable DMA device, but also the best one.

This finds the first device that provides DMA and the first one
that is also behind an IOMMU.

The criteria for selecting the best device is to prefer a DMA
device behind an IOMMU; if not found, the first DMA capable
one is selected instead.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_crtc.c | 68 ++++++++++++++++++++++++-----
 1 file changed, 56 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index ee23a50cf4d1..3f70d11270e9 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -1131,6 +1131,60 @@ struct device *mtk_crtc_dma_dev_get(struct drm_crtc *crtc)
 	return mtk_crtc->dma_dev;
 }
 
+static int mtk_crtc_find_suitable_dma_dev(struct mtk_drm_private *priv,
+					  struct mtk_crtc *mtk_crtc,
+					  const struct mtk_drm_path_definition *path)
+{
+	struct mtk_ddp_comp *dma_comp = NULL, *dma_comp_mmu = NULL;
+	int i;
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		const struct mtk_drm_comp_definition *cdef = path[i].comp;
+		struct mtk_ddp_comp *comp;
+
+		comp = mtk_ddp_comp_find_by_id(&priv->hlist,
+					       cdef->type, cdef->inst_id);
+		if (!comp)
+			continue;
+
+		/*
+		 * Check if this is a legacy OVL_ADAPTOR component and, if so, this
+		 * has to forcefully be the DMA device for this CRTC: assign it to
+		 * dma_comp_mmu to force that.
+		 */
+		if (comp->type == MTK_DISP_OVL_ADAPTOR) {
+			dma_comp_mmu = comp;
+			break;
+		}
+
+		if (!comp->dev || !comp->dev->of_node)
+			continue;
+
+		/* Find the first device provides DMA... */
+		if (!dma_comp) {
+			if (of_property_present(comp->dev->of_node, "#dma-cells"))
+				dma_comp = comp;
+		}
+
+		/* ...and check if that device is also behind an IOMMU */
+		if (!of_property_present(comp->dev->of_node, "iommus"))
+			continue;
+
+		/* If a device behind IOMMU is found, this is the best outcome */
+		dma_comp_mmu = comp;
+		break;
+	}
+
+	if (dma_comp_mmu)
+		mtk_crtc->dma_dev = mtk_ddp_comp_dma_dev_get(dma_comp_mmu);
+	else if (dma_comp)
+		mtk_crtc->dma_dev = mtk_ddp_comp_dma_dev_get(dma_comp);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
 int mtk_crtc_create(struct drm_device *drm_dev,
 		    enum mtk_crtc_path path_sel, int priv_data_index,
 		    const struct mtk_drm_route *conn_routes,
@@ -1139,7 +1193,6 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 	struct mtk_drm_private *priv = drm_dev->dev_private;
 	const struct mtk_drm_path_definition *output_path;
 	struct device *dev = drm_dev->dev;
-	struct mtk_ddp_comp *dma_comp;
 	struct mtk_crtc *mtk_crtc;
 	unsigned int num_comp_planes = 0;
 	unsigned int max_comp_stages = 0;
@@ -1309,20 +1362,11 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 	dev_dbg(dev, "Found %u layers composed by maximum of %u stage(s) each.\n",
 		mtk_crtc->hwlayer_nr, max_comp_stages);
 
-	/*
-	 * Default to use the first component as the dma dev.
-	 * In the case of ovl_adaptor sub driver, it needs to use the
-	 * dma_dev_get function to get representative dma dev.
-	 */
-	dma_comp = mtk_ddp_comp_find_by_id(&priv->hlist,
-					   output_path->comp[0].type,
-					   output_path->comp[0].inst_id);
-	if (dma_comp == NULL) {
+	ret = mtk_crtc_find_suitable_dma_dev(priv, mtk_crtc, output_path);
+	if (ret) {
 		dev_err(dev, "Could not find appropriate DMA device!\n");
 		return -EINVAL;
 	}
-
-	mtk_crtc->dma_dev = mtk_ddp_comp_dma_dev_get(dma_comp);
 	dev_dbg(dev, "Using DMA device %pOF\n", mtk_crtc->dma_dev->of_node);
 
 	ret = mtk_crtc_init(drm_dev, mtk_crtc, crtc_i);
-- 
2.54.0



^ permalink raw reply related

* [PATCH 27/42] drm/mediatek: Add support for Display Layer Blender component
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add support for the Layer Blender hardware component, found in the
MediaTek Kompanio Ultra MT8196, Genio Pro 5100 MT8894, Dimensity
9400 MT6991 and others.

This component is responsible for managing blending of single
layer stages and therefore implements one full layer stage which
gets completed by an exDMA hardware component.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/Makefile           |   1 +
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c     |  14 +
 drivers/gpu/drm/mediatek/mtk_disp_blender.c | 318 ++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_disp_drv.h     |  15 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   3 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h      |   1 +
 include/linux/soc/mediatek/mtk-mmsys.h      |   1 +
 7 files changed, 353 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_blender.c

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index c48ac3ab08cc..a3c1285e8f33 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -3,6 +3,7 @@
 mediatek-drm-y := mtk_crtc.o \
 		  mtk_ddp_comp.o \
 		  mtk_disp_aal.o \
+		  mtk_disp_blender.o \
 		  mtk_disp_ccorr.o \
 		  mtk_disp_color.o \
 		  mtk_disp_dsc.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 8b9228084828..6a312f3ddb09 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -246,6 +246,18 @@ static const struct mtk_ddp_comp_funcs ddp_aal = {
 	.stop = mtk_aal_stop,
 };
 
+static const struct mtk_ddp_comp_funcs ddp_blender = {
+	.clk_enable = mtk_blender_clk_enable,
+	.clk_disable = mtk_blender_clk_disable,
+	.config = mtk_blender_config,
+	.start = mtk_blender_start,
+	.stop = mtk_blender_stop,
+	.layer_config = mtk_blender_layer_config,
+	.get_blend_modes = mtk_blender_get_blend_modes,
+	.layerstage_nr = mtk_blender_layerstage_nr,
+	.connect = mtk_blender_connect,
+};
+
 static const struct mtk_ddp_comp_funcs ddp_ccorr = {
 	.clk_enable = mtk_ccorr_clk_enable,
 	.clk_disable = mtk_ccorr_clk_disable,
@@ -416,6 +428,7 @@ static const struct mtk_ddp_comp_funcs ddp_ovl_adaptor = {
 
 static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_AAL] = "aal",
+	[MTK_DISP_BLENDER] = "blender",
 	[MTK_DISP_BLS] = "bls",
 	[MTK_DISP_CCORR] = "ccorr",
 	[MTK_DISP_COLOR] = "color",
@@ -441,6 +454,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 
 static const struct mtk_ddp_comp_funcs *mtk_ddp_funcs[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_AAL]			= &ddp_aal,
+	[MTK_DISP_BLENDER]		= &ddp_blender,
 	[MTK_DISP_BLS]			= NULL,
 	[MTK_DISP_CCORR]		= &ddp_ccorr,
 	[MTK_DISP_COLOR]		= &ddp_color,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_blender.c b/drivers/gpu/drm/mediatek/mtk_disp_blender.c
new file mode 100644
index 000000000000..74679b15a26c
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_blender.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <drm/drm_blend.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/soc/mediatek/mtk-cmdq.h>
+#include <linux/soc/mediatek/mtk-mmsys.h>
+
+#include "mtk_crtc.h"
+#include "mtk_ddp_comp.h"
+#include "mtk_disp_drv.h"
+#include "mtk_disp_ovl.h"
+#include "mtk_drm_drv.h"
+
+#define DISP_REG_OVL_BLD_DATAPATH_CON		0x010
+#  define OVL_BLD_BGCLR_IN_SEL			BIT(0)
+#  define OVL_BLD_BGCLR_OUT_TO_PROC		BIT(4)
+#  define OVL_BLD_BGCLR_OUT_TO_NEXT_LAYER	BIT(5)
+
+#define DISP_REG_OVL_BLD_EN			0x020
+#  define OVL_BLD_EN				BIT(0)
+#  define OVL_BLD_FORCE_RELAY_MODE		BIT(4)
+#  define OVL_BLD_RELAY_MODE			BIT(5)
+
+#define DISP_REG_OVL_BLD_RST			0x024
+#  define OVL_BLD_RST				BIT(0)
+
+#define DISP_REG_OVL_BLD_SHADOW_CTRL		0x028
+#  define OVL_BLD_BYPASS_SHADOW			BIT(2)
+
+#define DISP_REG_OVL_BLD_ROI_SIZE		0x030
+#define DISP_REG_OVL_BLD_L_EN			0x040
+#  define OVL_BLD_L_EN				BIT(0)
+
+#define DISP_REG_OVL_BLD_OFFSET			0x044
+#define DISP_REG_OVL_BLD_SRC_SIZE		0x048
+#define DISP_REG_OVL_BLD_L0_CLRFMT		0x050
+#  define OVL_BLD_CON_FLD_CLRFMT		GENMASK(3, 0)
+#  define OVL_BLD_CON_CLRFMT_MAN		BIT(4)
+#  define OVL_BLD_CON_FLD_CLRFMT_NB		GENMASK(9, 8)
+#  define OVL_BLD_CON_CLRFMT_NB_10_BIT		BIT(8)
+#  define OVL_BLD_CON_BYTE_SWAP			BIT(16)
+#  define OVL_BLD_CON_RGB_SWAP			BIT(17)
+
+#define DISP_REG_OVL_BLD_BGCLR_CLR		0x104
+#define DISP_REG_OVL_BLD_L_CON2			0x200
+#  define OVL_BLD_L_ALPHA			GENMASK(7, 0)
+#  define OVL_BLD_L_ALPHA_EN			BIT(12)
+
+#define DISP_REG_OVL_BLD_L0_ALPHA_SEL		0x208
+#  define OVL_BLD_L0_CONST			BIT(24)
+
+#define DISP_REG_OVL_BLD_L0_CLR			0x20c
+#  define OVL_BLD_BGCLR_BLACK			0xff000000
+#  define OVL_BLD_BGCLR_RED			0xffff0000
+
+#define BLENDER_MAX_SIZE 8191
+
+struct mtk_disp_blender {
+	void __iomem *regs;
+	struct clk *clk;
+	struct cmdq_client_reg cmdq_reg;
+};
+
+static void mtk_blender_unset_input_bgclr(struct mtk_disp_blender *priv)
+{
+	u32 val;
+
+	val = readl(priv->regs + DISP_REG_OVL_BLD_DATAPATH_CON);
+	val &= ~OVL_BLD_BGCLR_IN_SEL;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_DATAPATH_CON);
+}
+
+void mtk_blender_layer_config(struct device *dev, unsigned int idx,
+			      struct mtk_plane_state *state,
+			      struct cmdq_pkt *cmdq_pkt)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(dev);
+	struct mtk_plane_pending_state *pending = &state->pending;
+	u32 alpha, blend_mode, clrfmt, ignore_pixel_alpha;
+
+	/*
+	 * Deselect IN from OVL Background Color if this is the first Blender
+	 * to implicitly select input from exDMA instead.
+	 */
+	if (idx == 0)
+		mtk_blender_unset_input_bgclr(priv);
+
+	if (!pending->enable || pending->height == 0 || pending->width == 0 ||
+	    pending->x > BLENDER_MAX_SIZE || pending->y > BLENDER_MAX_SIZE) {
+		mtk_ddp_write(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs, DISP_REG_OVL_BLD_L_EN);
+		return;
+	}
+
+	mtk_ddp_write(cmdq_pkt,
+		      ((pending->height & GENMASK(15, 0)) << 16) |
+		       (pending->width & GENMASK(15, 0)),
+		      &priv->cmdq_reg, priv->regs, DISP_REG_OVL_BLD_SRC_SIZE);
+	mtk_ddp_write(cmdq_pkt,
+		      ((pending->y & GENMASK(15, 0)) << 16) | (pending->x & GENMASK(15, 0)),
+		      &priv->cmdq_reg, priv->regs, DISP_REG_OVL_BLD_OFFSET);
+
+	blend_mode = mtk_ovl_get_blend_mode(state, MTK_OVL_SUPPORT_BLEND_MODES);
+	clrfmt = mtk_ovl_fmt_convert(pending->format, blend_mode, true, false, 0,
+				     OVL_BLD_CON_CLRFMT_MAN, OVL_BLD_CON_BYTE_SWAP,
+				     OVL_BLD_CON_RGB_SWAP);
+	clrfmt |= mtk_ovl_is_10bit_rgb(pending->format) ? OVL_BLD_CON_CLRFMT_NB_10_BIT : 0;
+	mtk_ddp_write_mask(cmdq_pkt, clrfmt, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_BLD_L0_CLRFMT, OVL_BLD_CON_CLRFMT_MAN |
+			   OVL_BLD_CON_RGB_SWAP |  OVL_BLD_CON_BYTE_SWAP |
+			   OVL_BLD_CON_FLD_CLRFMT | OVL_BLD_CON_FLD_CLRFMT_NB);
+
+	if (mtk_ovl_is_ignore_pixel_alpha(state, blend_mode))
+		ignore_pixel_alpha = OVL_BLD_L0_CONST;
+	else
+		ignore_pixel_alpha = 0;
+
+	mtk_ddp_write_mask(cmdq_pkt, ignore_pixel_alpha, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_BLD_L0_ALPHA_SEL, OVL_BLD_L0_CONST);
+
+	alpha = FIELD_PREP(OVL_BLD_L_ALPHA, (state->base.alpha >> 8));
+	alpha |= OVL_BLD_L_ALPHA_EN;
+	mtk_ddp_write_mask(cmdq_pkt, alpha, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_BLD_L_CON2, OVL_BLD_L_ALPHA_EN | OVL_BLD_L_ALPHA);
+
+	mtk_ddp_write(cmdq_pkt, OVL_BLD_L_EN, &priv->cmdq_reg, priv->regs, DISP_REG_OVL_BLD_L_EN);
+}
+
+unsigned int mtk_blender_layerstage_nr(struct device *dev)
+{
+	return 1;
+}
+
+void mtk_blender_start(struct device *dev)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(dev);
+	u32 val;
+
+	/* Bypass shadow registers and enable */
+	val = readl(priv->regs + DISP_REG_OVL_BLD_SHADOW_CTRL);
+	val |= OVL_BLD_BYPASS_SHADOW;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_SHADOW_CTRL);
+
+	val = readl(priv->regs + DISP_REG_OVL_BLD_EN);
+	val |= OVL_BLD_EN;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_EN);
+}
+
+void mtk_blender_stop(struct device *dev)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(dev);
+	u32 val;
+
+	/* Disable and reset */
+	val = readl(priv->regs + DISP_REG_OVL_BLD_EN);
+	val = val & ~OVL_BLD_EN;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_EN);
+
+	val = readl(priv->regs + DISP_REG_OVL_BLD_RST);
+	val |= OVL_BLD_RST;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_RST);
+
+	val = readl(priv->regs + DISP_REG_OVL_BLD_RST);
+	val = val & ~OVL_BLD_RST;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_RST);
+}
+
+void mtk_blender_config(struct mtk_ddp_comp *comp, unsigned int w,
+			     unsigned int h, unsigned int vrefresh,
+			     unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(comp->dev);
+	u32 val;
+
+	/* Set ROI for this Blender */
+	val = ((h & GENMASK(15, 0)) << 16) | (w & GENMASK(15, 0));
+	writel(val, priv->regs + DISP_REG_OVL_BLD_ROI_SIZE);
+
+	/*
+	 * Set both background color and constant layer color to Opaque Black
+	 * (ARGB) for eventual Alpha Blending to be effective
+	 */
+	writel(OVL_BLD_BGCLR_BLACK, priv->regs + DISP_REG_OVL_BLD_BGCLR_CLR);
+	writel(OVL_BLD_BGCLR_BLACK, priv->regs + DISP_REG_OVL_BLD_L0_CLR);
+}
+
+void mtk_blender_connect(struct mtk_ddp_comp *comp, struct device *mmsys_dev,
+			      struct mtk_ddp_comp *next)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(comp->dev);
+	const u32 mask = OVL_BLD_BGCLR_OUT_TO_PROC |
+			 OVL_BLD_BGCLR_OUT_TO_NEXT_LAYER |
+			 OVL_BLD_BGCLR_IN_SEL;
+	u32 data_path = OVL_BLD_BGCLR_IN_SEL;
+	u32 val;
+
+	/* Usually the next component is either another OUTPROC or an exDMA */
+	if (next->type == MTK_DISP_OUTPROC)
+		data_path |= OVL_BLD_BGCLR_OUT_TO_PROC;
+	else
+		data_path |= OVL_BLD_BGCLR_OUT_TO_NEXT_LAYER;
+
+	val = readl(priv->regs + DISP_REG_OVL_BLD_DATAPATH_CON);
+	val = (val & ~mask) | data_path;
+	writel(val, priv->regs + DISP_REG_OVL_BLD_DATAPATH_CON);
+
+	mtk_mmsys_hw_connect(mmsys_dev,
+			     comp->type, comp->inst_id, next->type, next->inst_id);
+}
+
+int mtk_blender_clk_enable(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(comp->dev);
+
+	return clk_prepare_enable(priv->clk);
+}
+
+void mtk_blender_clk_disable(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_blender *priv = dev_get_drvdata(comp->dev);
+
+	clk_disable_unprepare(priv->clk);
+}
+
+u32 mtk_blender_get_blend_modes(struct device *dev)
+{
+	return MTK_OVL_SUPPORT_BLEND_MODES;
+}
+
+static int mtk_blender_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	return 0;
+}
+
+static void mtk_blender_unbind(struct device *dev, struct device *master, void *data)
+{
+}
+
+static const struct component_ops mtk_disp_blender_component_ops = {
+	.bind	= mtk_blender_bind,
+	.unbind = mtk_blender_unbind,
+};
+
+static int mtk_blender_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_disp_blender *priv;
+	int ret = 0;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->regs))
+		return dev_err_probe(dev, PTR_ERR(priv->regs), "Cannot get reg\n");
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clk))
+		return dev_err_probe(dev, PTR_ERR(priv->clk), "Cannot get clocks\n");
+
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+	ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
+	if (ret)
+		dev_dbg(dev, "No mediatek,gce-client-reg\n");
+#endif
+	platform_set_drvdata(pdev, priv);
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = component_add(dev, &mtk_disp_blender_component_ops);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add component\n");
+
+	return 0;
+}
+
+static void mtk_blender_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_disp_blender_component_ops);
+}
+
+static const struct of_device_id mtk_disp_blender_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8196-disp-blender" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_blender_driver_dt_match);
+
+struct platform_driver mtk_disp_blender_driver = {
+	.probe		= mtk_blender_probe,
+	.remove		= mtk_blender_remove,
+	.driver		= {
+		.name	= "mediatek-disp-blender",
+		.owner	= THIS_MODULE,
+		.of_match_table = mtk_disp_blender_driver_dt_match,
+	},
+};
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_AUTHOR("Nancy Lin <nancy.lin@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek Display Controller Layer Blender Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index 62daf5ade9d1..e834bf17d86c 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -27,6 +27,21 @@ void mtk_aal_gamma_set(struct device *dev, struct drm_crtc_state *state);
 void mtk_aal_start(struct device *dev);
 void mtk_aal_stop(struct device *dev);
 
+int mtk_blender_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_blender_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_blender_connect(struct mtk_ddp_comp *comp, struct device *mmsys_dev,
+			 struct mtk_ddp_comp *next);
+void mtk_blender_config(struct mtk_ddp_comp *comp, unsigned int w,
+			unsigned int h, unsigned int vrefresh,
+			unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
+u32 mtk_blender_get_blend_modes(struct device *dev);
+void mtk_blender_layer_config(struct device *dev, unsigned int idx,
+				   struct mtk_plane_state *state,
+				   struct cmdq_pkt *cmdq_pkt);
+unsigned int mtk_blender_layerstage_nr(struct device *dev);
+void mtk_blender_start(struct device *dev);
+void mtk_blender_stop(struct device *dev);
+
 void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state);
 int mtk_ccorr_clk_enable(struct mtk_ddp_comp *comp);
 void mtk_ccorr_clk_disable(struct mtk_ddp_comp *comp);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index ee835ebbc18c..a70392c54bb9 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -503,6 +503,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DISP_AAL},
 	{ .compatible = "mediatek,mt8192-disp-aal",
 	  .data = (void *)MTK_DISP_AAL},
+	{ .compatible = "mediatek,mt8196-disp-blender",
+	  .data = (void *)MTK_DISP_BLENDER },
 	{ .compatible = "mediatek,mt8167-disp-ccorr",
 	  .data = (void *)MTK_DISP_CCORR },
 	{ .compatible = "mediatek,mt8183-disp-ccorr",
@@ -1059,6 +1061,7 @@ static struct platform_driver mtk_drm_platform_driver = {
 
 static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_aal_driver,
+	&mtk_disp_blender_driver,
 	&mtk_disp_ccorr_driver,
 	&mtk_disp_color_driver,
 	&mtk_disp_dsc_driver,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 4028268b3cc8..f36888fd7244 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -73,6 +73,7 @@ struct mtk_drm_private {
 };
 
 extern struct platform_driver mtk_disp_aal_driver;
+extern struct platform_driver mtk_disp_blender_driver;
 extern struct platform_driver mtk_disp_ccorr_driver;
 extern struct platform_driver mtk_disp_color_driver;
 extern struct platform_driver mtk_disp_dsc_driver;
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index 0b3bef392ab8..b32f0c92f040 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -88,6 +88,7 @@ enum mtk_ddp_comp_type {
 	/* DISP Components */
 	MTK_DISP_AAL,
 	MTK_DISP_BLS,
+	MTK_DISP_BLENDER,
 	MTK_DISP_CCORR,
 	MTK_DISP_COLOR,
 	MTK_DISP_DITHER,
-- 
2.54.0



^ permalink raw reply related

* [PATCH 18/42] soc: mediatek: mtk-mmsys: Migrate to new Multimedia DDP HW indexing
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

The mediatek-drm driver is ready to switch to the new Multimedia
Subsystem Display Controller HW indexing.

Perform the switch by:
 - Adding new "from_comp_inst" and "to_comp_inst" members in the
   Multimedia Subsystem HW Routes structure (mtk_mmsys_routes);
 - Changing the MMSYS_ROUTE() macro to use those, which performs
   the migration of all SoCs in one go (as all of them use this
   macro to declare their route entries);
 - Adding new mtk_mmsys_hw_connect() and mtk_mmsys_hw_disconnect()
   functions, making use of the new HW Component Type-Instance
   indexing.

Unfortunately, this change needs to go along with the related one
in the mediatek-drm driver, otherwise functionality of the Display
Controller will regress.

The only way to make this possible in two steps is to duplicate
all of the routes structure arrays for all of the SoCs, which
would result in a total of around ~1200 lines changed twice, and
that ignores the big increase in size for this driver during the
migration process so, in order to avoid useless bloat, I opted
for an inter-dependency between the two changes: mediatek-drm
and mtk-mmsys.

Acked-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/soc/mediatek/mtk-mmsys.c       | 99 ++++++++++++++++++++------
 drivers/soc/mediatek/mtk-mmsys.h       | 14 ++--
 include/linux/soc/mediatek/mtk-mmsys.h | 16 ++---
 3 files changed, 90 insertions(+), 39 deletions(-)

diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c
index 21f05fac2cb7..24296ebcbae3 100644
--- a/drivers/soc/mediatek/mtk-mmsys.c
+++ b/drivers/soc/mediatek/mtk-mmsys.c
@@ -1,7 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2014 MediaTek Inc.
- * Author: James Liao <jamesjj.liao@mediatek.com>
+ *                    James Liao <jamesjj.liao@mediatek.com>
+ *
+ * Copyrignt (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
  */
 
 #include <linux/delay.h>
@@ -191,38 +194,90 @@ static void mtk_mmsys_update_bits(struct mtk_mmsys *mmsys, u32 offset, u32 mask,
 	writel_relaxed(tmp, mmsys->regs + offset);
 }
 
-void mtk_mmsys_ddp_connect(struct device *dev,
-			   enum mtk_ddp_comp_id cur,
-			   enum mtk_ddp_comp_id next)
+/**
+ * mtk_mmsys_hw_connect - Connect MultiMedia Subsystem (MMSYS) Hardware IPs
+ * @dev:            Device pointer
+ * @src_type:       Type of the Source IP
+ * @src_hw_inst_id: Hardware instance of the Source IP
+ * @dst_type:       Type of the Destination IP
+ * @dst_hw_inst_id: Hardware instance of the Destination IP
+ *
+ * This function connects one MultiMedia Subsystem (MMSYS) related hardware
+ * to another (in the same subsystem), depending on supported connections.
+ * In short, this connects "Source" to "Destination", as in, enables sending
+ * data from a Source IP to a Destination IP.
+ *
+ * As a final note - depending on the SoC and on the specific IPs, it may
+ * also be possible to connect multiple Sources to a single Destination.
+ *
+ * Examples below follow this format to explain hardware components:
+ *      [Source Type][Instance ID] -> [Destination Type] [Instance ID]
+ *
+ * Example 1 - Single Source to Destination
+ *                         GAMMA 0 -> DITHER 0
+ *
+ * Example 2 - Multiple Sources to Single Destination
+ *                 MDP_RDMA 0 -----\
+ *                                  |
+ *                                  v
+ *                 MDP_RDMA 1 ---> MERGE 1 \ 
+ *                 MDP_RDMA 2 ---> MERGE 2 -\
+ *                                           >> ETHDR_MIXER 0
+ *                 (other 1)  ---> MERGE 3 -/
+ *                 (other 2)  ---> MERGE 4 /
+ *
+ * Note that in Example 2, some components are not chained together, but
+ * connected in parallel to a destination.
+ */
+void mtk_mmsys_hw_connect(struct device *dev,
+			  enum mtk_ddp_comp_type src_type, u8 src_hw_inst_id,
+			  enum mtk_ddp_comp_type dst_type, u8 dst_hw_inst_id)
 {
 	struct mtk_mmsys *mmsys = dev_get_drvdata(dev);
 	const struct mtk_mmsys_routes *routes = mmsys->data->routes;
-	int i;
 
-	for (i = 0; i < mmsys->data->num_routes; i++)
-		if (cur == routes[i].from_comp && next == routes[i].to_comp)
-			mtk_mmsys_update_bits(mmsys, routes[i].addr, routes[i].mask,
-					      routes[i].val, NULL);
+	for (int i = 0; i < mmsys->data->num_routes; i++) {
+		if (src_type != routes[i].from_comp_type ||
+		    src_hw_inst_id != routes[i].from_comp_inst ||
+		    dst_type != routes[i].to_comp_type ||
+		    dst_hw_inst_id != routes[i].to_comp_inst)
+			continue;
 
-	if (mmsys->data->vsync_len)
-		mtk_mmsys_update_bits(mmsys, MT8188_VDO1_MIXER_VSYNC_LEN, GENMASK(31, 0),
-				      mmsys->data->vsync_len, NULL);
+		mtk_mmsys_update_bits(mmsys, routes[i].addr, routes[i].mask, routes[i].val, NULL);
+		dev_dbg(dev, "Connected %u-%u to %u-%u\n",
+			src_type, src_hw_inst_id, dst_type, dst_hw_inst_id);
+	}
 }
-EXPORT_SYMBOL_GPL(mtk_mmsys_ddp_connect);
-
-void mtk_mmsys_ddp_disconnect(struct device *dev,
-			      enum mtk_ddp_comp_id cur,
-			      enum mtk_ddp_comp_id next)
+EXPORT_SYMBOL_NS_GPL(mtk_mmsys_hw_connect, "MTK_MMSYS");
+
+/**
+ * mtk_mmsys_hw_disconnect - Disconnect MultiMedia Subsystem (MMSYS) Hardware IPs
+ * @dev:            Device pointer
+ * @src_type:       Type of the Source IP
+ * @src_hw_inst_id: Hardware instance of the Source IP
+ * @dst_type:       Type of the Destination IP
+ * @dst_hw_inst_id: Hardware instance of the Destination IP
+ */
+void mtk_mmsys_hw_disconnect(struct device *dev,
+			     enum mtk_ddp_comp_type src_type, u8 src_hw_inst_id,
+			     enum mtk_ddp_comp_type dst_type, u8 dst_hw_inst_id)
 {
 	struct mtk_mmsys *mmsys = dev_get_drvdata(dev);
 	const struct mtk_mmsys_routes *routes = mmsys->data->routes;
-	int i;
 
-	for (i = 0; i < mmsys->data->num_routes; i++)
-		if (cur == routes[i].from_comp && next == routes[i].to_comp)
-			mtk_mmsys_update_bits(mmsys, routes[i].addr, routes[i].mask, 0, NULL);
+	for (int i = 0; i < mmsys->data->num_routes; i++) {
+		if (src_type != routes[i].from_comp_type ||
+		    src_hw_inst_id != routes[i].from_comp_inst ||
+		    dst_type != routes[i].to_comp_type ||
+		    dst_hw_inst_id != routes[i].to_comp_inst)
+			continue;
+
+		mtk_mmsys_update_bits(mmsys, routes[i].addr, routes[i].mask, 0, NULL);
+		dev_dbg(dev, "Disconnected %u-%u from %u-%u\n",
+			src_type, src_hw_inst_id, dst_type, dst_hw_inst_id);
+	}
 }
-EXPORT_SYMBOL_GPL(mtk_mmsys_ddp_disconnect);
+EXPORT_SYMBOL_NS_GPL(mtk_mmsys_hw_disconnect, "MTK_MMSYS");
 
 void mtk_mmsys_merge_async_config(struct device *dev, int idx, int width, int height,
 				  struct cmdq_pkt *cmdq_pkt)
diff --git a/drivers/soc/mediatek/mtk-mmsys.h b/drivers/soc/mediatek/mtk-mmsys.h
index d534d43aad6f..5c9319f3a2bb 100644
--- a/drivers/soc/mediatek/mtk-mmsys.h
+++ b/drivers/soc/mediatek/mtk-mmsys.h
@@ -80,19 +80,13 @@
 
 #define MMSYS_RST_NR(bank, bit) (((bank) * 32) + (bit))
 
-/* Temporary compatibility definitions */
-#define DDP_COMPONENT_CCORR0		DDP_COMPONENT_CCORR
-#define DDP_COMPONENT_UFOE0		DDP_COMPONENT_UFOE
-#define DDP_COMPONENT_GAMMA0		DDP_COMPONENT_GAMMA
-#define DDP_COMPONENT_ETHDR_MIXER0	DDP_COMPONENT_ETHDR_MIXER
-
 /*
  * This macro adds a compile time check to make sure that the in/out
  * selection bit(s) fit in the register mask, similar to bitfield
  * macros, but this does not transform the value.
  */
 #define MMSYS_ROUTE(from, fsid, to, tsid, reg_addr, reg_mask, selection)	\
-	{ DDP_COMPONENT_##from##fsid, DDP_COMPONENT_##to##tsid, reg_addr, reg_mask,	\
+	{ MTK_DISP_##from, fsid, MTK_DISP_##to, tsid, reg_addr, reg_mask,	\
 	  (__BUILD_BUG_ON_ZERO_MSG((reg_mask) == 0, "Invalid mask") +		\
 	   __BUILD_BUG_ON_ZERO_MSG(~(reg_mask) & (selection),			\
 				   #selection " does not fit in "		\
@@ -101,8 +95,10 @@
 	}
 
 struct mtk_mmsys_routes {
-	u32 from_comp;
-	u32 to_comp;
+	u8 from_comp_type;
+	u8 from_comp_inst;
+	u8 to_comp_type;
+	u8 to_comp_inst;
 	u32 addr;
 	u32 mask;
 	u32 val;
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index bbdd0b01927d..f67f21d04163 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -118,14 +118,6 @@ enum mtk_ddp_comp_type {
 	MTK_DDP_COMP_TYPE_MAX
 };
 
-void mtk_mmsys_ddp_connect(struct device *dev,
-			   enum mtk_ddp_comp_id cur,
-			   enum mtk_ddp_comp_id next);
-
-void mtk_mmsys_ddp_disconnect(struct device *dev,
-			      enum mtk_ddp_comp_id cur,
-			      enum mtk_ddp_comp_id next);
-
 void mtk_mmsys_ddp_dpi_fmt_config(struct device *dev, u32 val);
 
 void mtk_mmsys_merge_async_config(struct device *dev, int idx, int width,
@@ -134,6 +126,14 @@ void mtk_mmsys_merge_async_config(struct device *dev, int idx, int width,
 void mtk_mmsys_hdr_config(struct device *dev, int be_width, int be_height,
 			  struct cmdq_pkt *cmdq_pkt);
 
+void mtk_mmsys_hw_connect(struct device *dev,
+			  enum mtk_ddp_comp_type src_type, u8 src_hw_inst_id,
+			  enum mtk_ddp_comp_type dst_type, u8 dst_hw_inst_id);
+
+void mtk_mmsys_hw_disconnect(struct device *dev,
+			     enum mtk_ddp_comp_type src_type, u8 src_hw_inst_id,
+			     enum mtk_ddp_comp_type dst_type, u8 dst_hw_inst_id);
+
 void mtk_mmsys_mixer_in_config(struct device *dev, int idx, bool alpha_sel, u16 alpha,
 			       u8 mode, u32 biwidth, struct cmdq_pkt *cmdq_pkt);
 
-- 
2.54.0



^ permalink raw reply related

* [PATCH 09/42] drm/mediatek: Introduce and use path/comp definition structures
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

In preparation for a bigger restructuring of Display Controller
HW sub-component handing and path building, introduce two new
mtk_drm_comp_definition and mtk_drm_path definition structures,
meant to replace all of the {main,ext,third}_path pointers and
supportive length variables.

Obviously, also change all of the functions that were previously
using the old path array pointers to use the new structures.

This made possible to loop through the path definition structures,
shortening various code paths and hence improving readability but,
as a nice side effect, now it is possible to easily extend the
maximum number of outputs that are supported by the driver.
Of course, even though that could be done by simply extending the
already present mtk_crtc_path enumeration (which now also indexes
the mtk_drm_path_definition array in mtk_mmsys_driver_data), doing
so will increase the memory footprint for all platforms (even the
ones with a single output): this is currently left unresolved, but
this change makes performing a future optimization to add dynamic
CRTC output indexing(/numbers) way easier than before.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_crtc.c     |  21 +-
 drivers/gpu/drm/mediatek/mtk_crtc.h     |   4 +-
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c |  51 +--
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h |   2 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  | 573 +++++++++++++-----------
 drivers/gpu/drm/mediatek/mtk_drm_drv.h  |  17 +-
 6 files changed, 359 insertions(+), 309 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 3f4d6ab1bfc2..5e243550d35e 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (c) 2015 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
  */
 
 #include <linux/clk.h>
@@ -1025,12 +1027,13 @@ struct device *mtk_crtc_dma_dev_get(struct drm_crtc *crtc)
 	return mtk_crtc->dma_dev;
 }
 
-int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
-		    unsigned int path_len, int priv_data_index,
+int mtk_crtc_create(struct drm_device *drm_dev,
+		    enum mtk_crtc_path path_sel, int priv_data_index,
 		    const struct mtk_drm_route *conn_routes,
 		    unsigned int num_conn_routes)
 {
 	struct mtk_drm_private *priv = drm_dev->dev_private;
+	const struct mtk_drm_path_definition *output_path;
 	struct device *dev = drm_dev->dev;
 	struct mtk_ddp_comp *dma_comp;
 	struct mtk_crtc *mtk_crtc;
@@ -1042,16 +1045,14 @@ int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
 	struct drm_crtc *tmp;
 	int crtc_i = 0;
 
-	if (!path)
-		return 0;
-
 	priv = priv->all_drm_private[priv_data_index];
+	output_path = &priv->data->output_paths[path_sel];
 
 	drm_for_each_crtc(tmp, drm_dev)
 		crtc_i++;
 
-	for (i = 0; i < path_len; i++) {
-		enum mtk_ddp_comp_id comp_id = path[i];
+	for (i = 0; i < output_path[i].len; i++) {
+		enum mtk_ddp_comp_id comp_id = output_path->comp[i].type;
 		struct device_node *node;
 		struct mtk_ddp_comp *comp;
 
@@ -1079,7 +1080,7 @@ int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
 		return -ENOMEM;
 
 	mtk_crtc->mmsys_dev = priv->mmsys_dev;
-	mtk_crtc->ddp_comp_nr = path_len;
+	mtk_crtc->ddp_comp_nr = output_path->len;
 	mtk_crtc->ddp_comp = devm_kcalloc(dev,
 					  mtk_crtc->ddp_comp_nr + (conn_routes ? 1 : 0),
 					  sizeof(*mtk_crtc->ddp_comp),
@@ -1095,7 +1096,7 @@ int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
 	}
 
 	for (i = 0, j = 0; i < mtk_crtc->ddp_comp_nr; i++, j++) {
-		unsigned int comp_id = path[i];
+		unsigned int comp_id = output_path->comp[i].type;
 		struct mtk_ddp_comp *comp;
 
 		comp = mtk_ddp_comp_find_by_id(&priv->hlist, comp_id);
@@ -1141,7 +1142,7 @@ int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
 	 * In the case of ovl_adaptor sub driver, it needs to use the
 	 * dma_dev_get function to get representative dma dev.
 	 */
-	dma_comp = mtk_ddp_comp_find_by_id(&priv->hlist, path[0]);
+	dma_comp = mtk_ddp_comp_find_by_id(&priv->hlist, output_path->comp[0].type);
 	if (dma_comp == NULL) {
 		dev_err(dev, "Could not find appropriate DMA device!\n");
 		return -EINVAL;
diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.h b/drivers/gpu/drm/mediatek/mtk_crtc.h
index a6b8697cca97..5a18b6bfa06b 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.h
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.h
@@ -15,8 +15,8 @@
 #define MTK_MIN_BPC	3
 
 void mtk_crtc_commit(struct drm_crtc *crtc);
-int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
-		    unsigned int path_len, int priv_data_index,
+int mtk_crtc_create(struct drm_device *drm_dev,
+		    enum mtk_crtc_path path_sel, int priv_data_index,
 		    const struct mtk_drm_route *conn_routes,
 		    unsigned int num_conn_routes);
 int mtk_crtc_plane_check(struct drm_crtc *crtc, struct drm_plane *plane,
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index f7103e20abe5..b0d38646ca03 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -495,17 +495,13 @@ static bool mtk_ddp_find_comp_dev_in_table(const struct mtk_drm_comp_list *hlist
 }
 
 static bool mtk_ddp_comp_find(struct device *dev,
-			      const unsigned int *path,
-			      unsigned int path_len,
+			      const struct mtk_drm_path_definition *output_path,
 			      const struct mtk_drm_comp_list *hlist)
 {
 	unsigned int i;
 
-	if (path == NULL)
-		return false;
-
-	for (i = 0U; i < path_len; i++)
-		if (mtk_ddp_find_comp_dev_in_table(hlist, path[i], dev))
+	for (i = 0U; i < output_path->len; i++)
+		if (mtk_ddp_find_comp_dev_in_table(hlist, output_path->comp[i].type, dev))
 			return true;
 
 	return false;
@@ -528,21 +524,17 @@ static int mtk_ddp_comp_find_in_route(struct device *dev,
 	return -ENODEV;
 }
 
-static bool mtk_ddp_path_available(const unsigned int *path,
-				   unsigned int path_len,
+static bool mtk_ddp_path_available(const struct mtk_drm_path_definition *output_path,
 				   struct device_node **comp_node)
 {
 	unsigned int i;
 
-	if (!path || !path_len)
-		return false;
-
-	for (i = 0U; i < path_len; i++) {
+	for (i = 0U; i < output_path->len; i++) {
 		/* OVL_ADAPTOR doesn't have a device node */
-		if (path[i] == DDP_COMPONENT_DRM_OVL_ADAPTOR)
+		if (output_path->comp[i].type == DDP_COMPONENT_DRM_OVL_ADAPTOR)
 			continue;
 
-		if (!comp_node[path[i]])
+		if (!comp_node[output_path->comp[i].type])
 			return false;
 	}
 
@@ -576,31 +568,14 @@ int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev)
 		priv_n = private->all_drm_private[j];
 		data = priv_n->data;
 
-		if (mtk_ddp_path_available(data->main_path, data->main_len,
-					   priv_n->comp_node)) {
-			if (mtk_ddp_comp_find(dev, data->main_path,
-					      data->main_len,
-					      &priv_n->hlist))
-				return BIT(i);
-			i++;
-		}
-
-		if (mtk_ddp_path_available(data->ext_path, data->ext_len,
-					   priv_n->comp_node)) {
-			if (mtk_ddp_comp_find(dev, data->ext_path,
-					      data->ext_len,
-					      &priv_n->hlist))
-				return BIT(i);
-			i++;
-		}
+		for (i = 0; i < MAX_CRTC; i++) {
+			if (!data->output_paths[i].len)
+				continue;
 
-		if (mtk_ddp_path_available(data->third_path, data->third_len,
-					   priv_n->comp_node)) {
-			if (mtk_ddp_comp_find(dev, data->third_path,
-					      data->third_len,
-					      &priv_n->hlist))
+			ret = mtk_ddp_comp_find(dev, &data->output_paths[i],
+						&priv_n->hlist);
+			if (ret)
 				return BIT(i);
-			i++;
 		}
 	}
 
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index 58a06add1368..76a2b649516d 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -15,6 +15,8 @@
 
 #include <drm/drm_modes.h>
 
+#define MTK_DISP_CONTROLLER_MAX_COMP_PER_PATH		24
+
 struct device;
 struct device_node;
 struct drm_crtc;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 18683aee61ff..09c7d038348d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -2,6 +2,10 @@
 /*
  * Copyright (c) 2015 MediaTek Inc.
  * Author: YT SHEN <yt.shen@mediatek.com>
+ *
+ * Major rework
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
  */
 
 #include <linux/aperture.h>
@@ -59,239 +63,326 @@ static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
 	.atomic_commit = drm_atomic_helper_commit,
 };
 
-static const unsigned int mt2701_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_BLS,
-	DDP_COMPONENT_DSI0,
+static const struct mtk_drm_comp_definition mt2701_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_BLS },
+	{ DDP_COMPONENT_DSI0 },
 };
 
-static const unsigned int mt2701_mtk_ddp_ext[] = {
-	DDP_COMPONENT_RDMA1,
-	DDP_COMPONENT_DPI0,
+static const struct mtk_drm_comp_definition mt2701_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_RDMA1 },
+	{ DDP_COMPONENT_DPI0 },
 };
 
-static const unsigned int mt7623_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_BLS,
-	DDP_COMPONENT_DPI0,
+struct mtk_drm_path_definition mt2701_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt2701_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt2701_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt2701_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt2701_mtk_ddp_ext),
+	},
 };
 
-static const unsigned int mt7623_mtk_ddp_ext[] = {
-	DDP_COMPONENT_RDMA1,
-	DDP_COMPONENT_DSI0,
+static const struct mtk_drm_comp_definition mt2712_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_OD0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_DPI0 },
+	{ DDP_COMPONENT_PWM0 },
 };
 
-static const unsigned int mt2712_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_OD0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_DPI0,
-	DDP_COMPONENT_PWM0,
+static const struct mtk_drm_comp_definition mt2712_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_OVL1 },
+	{ DDP_COMPONENT_COLOR1 },
+	{ DDP_COMPONENT_AAL1 },
+	{ DDP_COMPONENT_OD1 },
+	{ DDP_COMPONENT_RDMA1 },
+	{ DDP_COMPONENT_DPI1 },
+	{ DDP_COMPONENT_PWM1 },
 };
 
-static const unsigned int mt2712_mtk_ddp_ext[] = {
-	DDP_COMPONENT_OVL1,
-	DDP_COMPONENT_COLOR1,
-	DDP_COMPONENT_AAL1,
-	DDP_COMPONENT_OD1,
-	DDP_COMPONENT_RDMA1,
-	DDP_COMPONENT_DPI1,
-	DDP_COMPONENT_PWM1,
+static const struct mtk_drm_comp_definition mt2712_mtk_ddp_third[] = {
+	{ DDP_COMPONENT_RDMA2 },
+	{ DDP_COMPONENT_DSI3 },
+	{ DDP_COMPONENT_PWM2 },
 };
 
-static const unsigned int mt2712_mtk_ddp_third[] = {
-	DDP_COMPONENT_RDMA2,
-	DDP_COMPONENT_DSI3,
-	DDP_COMPONENT_PWM2,
+struct mtk_drm_path_definition mt2712_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt2712_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt2712_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt2712_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt2712_mtk_ddp_ext),
+	},
+	[CRTC_THIRD] = {
+		.comp = mt2712_mtk_ddp_third,
+		.len = ARRAY_SIZE(mt2712_mtk_ddp_third),
+	},
 };
 
-static unsigned int mt8167_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_CCORR,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_DITHER0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_DSI0,
+static const struct mtk_drm_comp_definition mt7623_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_BLS },
+	{ DDP_COMPONENT_DPI0 },
 };
 
-static const unsigned int mt8173_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_OD0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_UFOE,
-	DDP_COMPONENT_DSI0,
-	DDP_COMPONENT_PWM0,
+static const struct mtk_drm_comp_definition mt7623_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_RDMA1 },
+	{ DDP_COMPONENT_DSI0 },
 };
 
-static const unsigned int mt8173_mtk_ddp_ext[] = {
-	DDP_COMPONENT_OVL1,
-	DDP_COMPONENT_COLOR1,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_RDMA1,
-	DDP_COMPONENT_DPI0,
+struct mtk_drm_path_definition mt7623_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt7623_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt7623_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt7623_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt7623_mtk_ddp_ext),
+	},
 };
 
-static const unsigned int mt8183_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_OVL_2L0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_CCORR,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_DITHER0,
-	DDP_COMPONENT_DSI0,
+static const struct mtk_drm_comp_definition mt8167_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_CCORR },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_DITHER0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_DSI0 },
 };
 
-static const unsigned int mt8183_mtk_ddp_ext[] = {
-	DDP_COMPONENT_OVL_2L1,
-	DDP_COMPONENT_RDMA1,
-	DDP_COMPONENT_DPI0,
+struct mtk_drm_path_definition mt8167_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8167_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8167_mtk_ddp_main),
+	},
 };
 
-static const unsigned int mt8186_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_CCORR,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_POSTMASK0,
-	DDP_COMPONENT_DITHER0,
-	DDP_COMPONENT_DSI0,
+static const struct mtk_drm_comp_definition mt8173_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_OD0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_UFOE },
+	{ DDP_COMPONENT_DSI0 },
+	{ DDP_COMPONENT_PWM0 },
 };
 
-static const unsigned int mt8186_mtk_ddp_ext[] = {
-	DDP_COMPONENT_OVL_2L0,
-	DDP_COMPONENT_RDMA1,
-	DDP_COMPONENT_DPI0,
+static const struct mtk_drm_comp_definition mt8173_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_OVL1 },
+	{ DDP_COMPONENT_COLOR1 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_RDMA1 },
+	{ DDP_COMPONENT_DPI0 },
 };
 
-static const unsigned int mt8188_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_CCORR,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_POSTMASK0,
-	DDP_COMPONENT_DITHER0,
+struct mtk_drm_path_definition mt8173_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8173_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8173_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt8173_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
+	},
 };
 
-static const struct mtk_drm_route mt8188_mtk_ddp_main_routes[] = {
-	{0, DDP_COMPONENT_DP_INTF0},
-	{0, DDP_COMPONENT_DSI0},
+static const struct mtk_drm_comp_definition mt8183_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_OVL_2L0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_CCORR },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_DITHER0 },
+	{ DDP_COMPONENT_DSI0 },
+};
+
+static const struct mtk_drm_comp_definition mt8183_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_OVL_2L1 },
+	{ DDP_COMPONENT_RDMA1 },
+	{ DDP_COMPONENT_DPI0 },
+};
+
+struct mtk_drm_path_definition mt8183_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8183_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8183_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt8183_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt8183_mtk_ddp_ext),
+	},
 };
 
-static const unsigned int mt8192_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_OVL_2L0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_CCORR,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_POSTMASK0,
-	DDP_COMPONENT_DITHER0,
-	DDP_COMPONENT_DSI0,
+static const struct mtk_drm_comp_definition mt8186_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_CCORR },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_POSTMASK0 },
+	{ DDP_COMPONENT_DITHER0 },
+	{ DDP_COMPONENT_DSI0 },
 };
 
-static const unsigned int mt8192_mtk_ddp_ext[] = {
-	DDP_COMPONENT_OVL_2L2,
-	DDP_COMPONENT_RDMA4,
-	DDP_COMPONENT_DPI0,
+static const struct mtk_drm_comp_definition mt8186_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_OVL_2L0 },
+	{ DDP_COMPONENT_RDMA1 },
+	{ DDP_COMPONENT_DPI0 },
 };
 
-static const unsigned int mt8195_mtk_ddp_main[] = {
-	DDP_COMPONENT_OVL0,
-	DDP_COMPONENT_RDMA0,
-	DDP_COMPONENT_COLOR0,
-	DDP_COMPONENT_CCORR,
-	DDP_COMPONENT_AAL0,
-	DDP_COMPONENT_GAMMA,
-	DDP_COMPONENT_DITHER0,
-	DDP_COMPONENT_DSC0,
-	DDP_COMPONENT_MERGE0,
-	DDP_COMPONENT_DP_INTF0,
+struct mtk_drm_path_definition mt8186_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8186_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8186_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt8186_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt8186_mtk_ddp_ext),
+	},
 };
 
-static const unsigned int mt8195_mtk_ddp_ext[] = {
-	DDP_COMPONENT_DRM_OVL_ADAPTOR,
-	DDP_COMPONENT_MERGE5,
-	DDP_COMPONENT_DP_INTF1,
+static const struct mtk_drm_comp_definition mt8188_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_CCORR },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_POSTMASK0 },
+	{ DDP_COMPONENT_DITHER0 },
+};
+
+struct mtk_drm_path_definition mt8188_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8188_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8188_mtk_ddp_main),
+	},
+};
+
+static const struct mtk_drm_comp_definition mt8192_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_OVL_2L0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_CCORR },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_POSTMASK0 },
+	{ DDP_COMPONENT_DITHER0 },
+	{ DDP_COMPONENT_DSI0 },
+};
+
+static const struct mtk_drm_comp_definition mt8192_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_OVL_2L2 },
+	{ DDP_COMPONENT_RDMA4 },
+	{ DDP_COMPONENT_DPI0 },
+};
+
+struct mtk_drm_path_definition mt8192_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8192_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8192_mtk_ddp_main),
+	},
+	[CRTC_EXT] = {
+		.comp = mt8192_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt8192_mtk_ddp_ext),
+	},
+};
+
+static const struct mtk_drm_comp_definition mt8195_mtk_ddp_main[] = {
+	{ DDP_COMPONENT_OVL0 },
+	{ DDP_COMPONENT_RDMA0 },
+	{ DDP_COMPONENT_COLOR0 },
+	{ DDP_COMPONENT_CCORR },
+	{ DDP_COMPONENT_AAL0 },
+	{ DDP_COMPONENT_GAMMA },
+	{ DDP_COMPONENT_DITHER0 },
+	{ DDP_COMPONENT_DSC0 },
+	{ DDP_COMPONENT_MERGE0 },
+	{ DDP_COMPONENT_DP_INTF0 },
+};
+
+static const struct mtk_drm_comp_definition mt8195_mtk_ddp_ext[] = {
+	{ DDP_COMPONENT_DRM_OVL_ADAPTOR },
+	{ DDP_COMPONENT_MERGE5 },
+	{ DDP_COMPONENT_DP_INTF1 },
+};
+
+struct mtk_drm_path_definition mt8195_vdo0_legacy_paths[MAX_CRTC] = {
+	[CRTC_MAIN] = {
+		.comp = mt8195_mtk_ddp_main,
+		.len = ARRAY_SIZE(mt8195_mtk_ddp_main),
+	},
+};
+
+struct mtk_drm_path_definition mt8195_vdo1_legacy_paths[MAX_CRTC] = {
+	[CRTC_EXT] = {
+		.comp = mt8195_mtk_ddp_ext,
+		.len = ARRAY_SIZE(mt8195_mtk_ddp_ext),
+	},
 };
 
 static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
-	.main_path = mt2701_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt2701_mtk_ddp_main),
-	.ext_path = mt2701_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt2701_mtk_ddp_ext),
+	.output_paths = mt2701_legacy_paths,
 	.shadow_register = true,
 	.mmsys_dev_num = 1,
 };
 
-static const struct mtk_mmsys_driver_data mt7623_mmsys_driver_data = {
-	.main_path = mt7623_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt7623_mtk_ddp_main),
-	.ext_path = mt7623_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt7623_mtk_ddp_ext),
-	.shadow_register = true,
+static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = {
+	.output_paths = mt2712_legacy_paths,
 	.mmsys_dev_num = 1,
 };
 
-static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = {
-	.main_path = mt2712_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt2712_mtk_ddp_main),
-	.ext_path = mt2712_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt2712_mtk_ddp_ext),
-	.third_path = mt2712_mtk_ddp_third,
-	.third_len = ARRAY_SIZE(mt2712_mtk_ddp_third),
+static const struct mtk_mmsys_driver_data mt7623_mmsys_driver_data = {
+	.output_paths = mt7623_legacy_paths,
+	.shadow_register = true,
 	.mmsys_dev_num = 1,
 };
 
 static const struct mtk_mmsys_driver_data mt8167_mmsys_driver_data = {
-	.main_path = mt8167_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8167_mtk_ddp_main),
+	.output_paths = mt8167_legacy_paths,
 	.mmsys_dev_num = 1,
 };
 
 static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
-	.main_path = mt8173_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
-	.ext_path = mt8173_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
+	.output_paths = mt8173_legacy_paths,
 	.mmsys_dev_num = 1,
 };
 
 static const struct mtk_mmsys_driver_data mt8183_mmsys_driver_data = {
-	.main_path = mt8183_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8183_mtk_ddp_main),
-	.ext_path = mt8183_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt8183_mtk_ddp_ext),
+	.output_paths = mt8183_legacy_paths,
 	.mmsys_dev_num = 1,
 };
 
 static const struct mtk_mmsys_driver_data mt8186_mmsys_driver_data = {
-	.main_path = mt8186_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8186_mtk_ddp_main),
-	.ext_path = mt8186_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt8186_mtk_ddp_ext),
+	.output_paths = mt8186_legacy_paths,
 	.mmsys_dev_num = 1,
 };
 
+static const struct mtk_drm_route mt8188_mtk_ddp_main_routes[] = {
+	{ 0, DDP_COMPONENT_DP_INTF0 },
+	{ 0, DDP_COMPONENT_DSI0 },
+};
+
 static const struct mtk_mmsys_driver_data mt8188_vdosys0_driver_data = {
-	.main_path = mt8188_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8188_mtk_ddp_main),
+	.output_paths = mt8188_legacy_paths,
 	.conn_routes = mt8188_mtk_ddp_main_routes,
 	.num_conn_routes = ARRAY_SIZE(mt8188_mtk_ddp_main_routes),
 	.mmsys_dev_num = 2,
@@ -301,16 +392,12 @@ static const struct mtk_mmsys_driver_data mt8188_vdosys0_driver_data = {
 };
 
 static const struct mtk_mmsys_driver_data mt8192_mmsys_driver_data = {
-	.main_path = mt8192_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8192_mtk_ddp_main),
-	.ext_path = mt8192_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt8192_mtk_ddp_ext),
+	.output_paths = mt8192_legacy_paths,
 	.mmsys_dev_num = 1,
 };
 
 static const struct mtk_mmsys_driver_data mt8195_vdosys0_driver_data = {
-	.main_path = mt8195_mtk_ddp_main,
-	.main_len = ARRAY_SIZE(mt8195_mtk_ddp_main),
+	.output_paths = mt8195_vdo0_legacy_paths,
 	.mmsys_dev_num = 2,
 	.max_width = 8191,
 	.min_width = 1,
@@ -318,8 +405,7 @@ static const struct mtk_mmsys_driver_data mt8195_vdosys0_driver_data = {
 };
 
 static const struct mtk_mmsys_driver_data mt8195_vdosys1_driver_data = {
-	.ext_path = mt8195_mtk_ddp_ext,
-	.ext_len = ARRAY_SIZE(mt8195_mtk_ddp_ext),
+	.output_paths = mt8195_vdo1_legacy_paths,
 	.mmsys_id = 1,
 	.mmsys_dev_num = 2,
 	.max_width = 8191,
@@ -384,6 +470,7 @@ static bool mtk_drm_get_all_drm_priv(struct device *dev)
 	int i, j;
 
 	for_each_child_of_node(phandle->parent, node) {
+		const struct mtk_drm_path_definition *output_path;
 		struct platform_device *pdev;
 
 		of_id = of_match_node(mtk_drm_of_ids, node);
@@ -404,12 +491,14 @@ static bool mtk_drm_get_all_drm_priv(struct device *dev)
 		if (!temp_drm_priv)
 			continue;
 
-		if (temp_drm_priv->data->main_len)
-			all_drm_priv[CRTC_MAIN] = temp_drm_priv;
-		else if (temp_drm_priv->data->ext_len)
-			all_drm_priv[CRTC_EXT] = temp_drm_priv;
-		else if (temp_drm_priv->data->third_len)
-			all_drm_priv[CRTC_THIRD] = temp_drm_priv;
+		for (i = 0; i < MAX_CRTC; i++) {
+			output_path = &temp_drm_priv->data->output_paths[i];
+
+			if (!output_path->len)
+				continue;
+
+			all_drm_priv[i] = temp_drm_priv;
+		}
 
 		if (temp_drm_priv->mtk_drm_bound)
 			cnt++;
@@ -433,27 +522,23 @@ static bool mtk_drm_get_all_drm_priv(struct device *dev)
 
 static bool mtk_drm_find_mmsys_comp(struct mtk_drm_private *private, int comp_id)
 {
-	const struct mtk_mmsys_driver_data *drv_data = private->data;
-	int i;
+	const struct mtk_mmsys_driver_data *data = private->data;
+	int i, j;
 
-	if (drv_data->main_path)
-		for (i = 0; i < drv_data->main_len; i++)
-			if (drv_data->main_path[i] == comp_id)
-				return true;
+	for (i = 0; i < MAX_CRTC; i++) {
+		const struct mtk_drm_path_definition *output_path = &data->output_paths[i];
 
-	if (drv_data->ext_path)
-		for (i = 0; i < drv_data->ext_len; i++)
-			if (drv_data->ext_path[i] == comp_id)
-				return true;
+		for (j = 0; j < output_path->len; j++) {
+			if (output_path->comp[j].type != comp_id)
+				continue;
 
-	if (drv_data->third_path)
-		for (i = 0; i < drv_data->third_len; i++)
-			if (drv_data->third_path[i] == comp_id)
-				return true;
+			return true;
+		}
+	}
 
-	if (drv_data->num_conn_routes)
-		for (i = 0; i < drv_data->num_conn_routes; i++)
-			if (drv_data->conn_routes[i].route_ddp == comp_id)
+	if (data->num_conn_routes)
+		for (i = 0; i < data->num_conn_routes; i++)
+			if (data->conn_routes[i].route_ddp == comp_id)
 				return true;
 
 	return false;
@@ -525,30 +610,15 @@ static int mtk_drm_kms_init(struct drm_device *drm)
 			if (priv_n->data->min_height)
 				drm->mode_config.min_height = priv_n->data->min_height;
 
-			if (i == CRTC_MAIN && priv_n->data->main_len) {
-				ret = mtk_crtc_create(drm, priv_n->data->main_path,
-						      priv_n->data->main_len, j,
-						      priv_n->data->conn_routes,
-						      priv_n->data->num_conn_routes);
-				if (ret)
-					goto err_component_unbind;
-
+			if (!priv_n->data->output_paths[i].len)
 				continue;
-			} else if (i == CRTC_EXT && priv_n->data->ext_len) {
-				ret = mtk_crtc_create(drm, priv_n->data->ext_path,
-						      priv_n->data->ext_len, j, NULL, 0);
-				if (ret)
-					goto err_component_unbind;
 
-				continue;
-			} else if (i == CRTC_THIRD && priv_n->data->third_len) {
-				ret = mtk_crtc_create(drm, priv_n->data->third_path,
-						      priv_n->data->third_len, j, NULL, 0);
-				if (ret)
-					goto err_component_unbind;
+			ret = mtk_crtc_create(drm, i, j,
+					      priv_n->data->conn_routes,
+					      priv_n->data->num_conn_routes);
 
-				continue;
-			}
+			if (ret)
+				goto err_component_unbind;
 		}
 	}
 
@@ -847,7 +917,8 @@ static int mtk_drm_of_get_ddp_comp_type(struct device_node *node, enum mtk_ddp_c
 
 static int mtk_drm_of_get_ddp_ep_cid(struct device_node *node,
 				     int output_port, enum mtk_crtc_path crtc_path,
-				     struct device_node **next, unsigned int *cid)
+				     struct device_node **next,
+				     struct mtk_drm_comp_definition *comp_def)
 {
 	struct device_node *ep_dev_node, *ep_out;
 	enum mtk_ddp_comp_type comp_type;
@@ -875,7 +946,7 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device_node *node,
 	ret = mtk_drm_of_get_ddp_comp_type(ep_dev_node, &comp_type);
 	if (ret) {
 		if (mtk_ovl_adaptor_is_comp_present(ep_dev_node)) {
-			*cid = (unsigned int)DDP_COMPONENT_DRM_OVL_ADAPTOR;
+			comp_def->type = DDP_COMPONENT_DRM_OVL_ADAPTOR;
 			return 0;
 		}
 		return ret;
@@ -886,7 +957,7 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device_node *node,
 		return ret;
 
 	/* All ok! Pass the Component ID to the caller. */
-	*cid = (unsigned int)ret;
+	comp_def->type = ret;
 
 	return 0;
 }
@@ -895,8 +966,7 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device_node *node,
  * mtk_drm_of_ddp_path_build_one - Build a Display HW Pipeline for a CRTC Path
  * @dev:          The mediatek-drm device
  * @cpath:        CRTC Path relative to a VDO or MMSYS
- * @out_path:     Pointer to an array that will contain the new pipeline
- * @out_path_len: Number of entries in the pipeline array
+ * @out_path:     Pointer to the structure that will contain the new pipeline
  *
  * MediaTek SoCs can use different DDP hardware pipelines (or paths) depending
  * on the board-specific desired display configuration; this function walks
@@ -910,20 +980,19 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device_node *node,
  * * %-ENOMEM - Failure to allocate pipeline array to pass to the caller
  */
 static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path cpath,
-					 const unsigned int **out_path,
-					 unsigned int *out_path_len)
+					 struct mtk_drm_path_definition *out_path)
 {
+	struct mtk_drm_comp_definition temp_path[MTK_DISP_CONTROLLER_MAX_COMP_PER_PATH];
 	struct device_node *next = NULL, *prev, *vdo = dev->parent->of_node;
-	unsigned int temp_path[DDP_COMPONENT_DRM_ID_MAX] = { 0 };
-	unsigned int *final_ddp_path;
-	unsigned short int idx = 0;
 	bool ovl_adaptor_comp_added = false;
+	unsigned short int idx = 0;
+	size_t final_comp_sz;
 	int ret;
 
 	/* Get the first entry for the temp_path array */
 	ret = mtk_drm_of_get_ddp_ep_cid(vdo, 0, cpath, &next, &temp_path[idx]);
 	if (ret) {
-		if (next && temp_path[idx] == DDP_COMPONENT_DRM_OVL_ADAPTOR) {
+		if (next && temp_path[idx].type == DDP_COMPONENT_DRM_OVL_ADAPTOR) {
 			dev_dbg(dev, "Adding OVL Adaptor for %pOF\n", next);
 			ovl_adaptor_comp_added = true;
 		} else {
@@ -957,7 +1026,7 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 		 * to probe that component master driver of which only one instance
 		 * is needed and possible.
 		 */
-		if (temp_path[idx] == DDP_COMPONENT_DRM_OVL_ADAPTOR) {
+		if (temp_path[idx].type == DDP_COMPONENT_DRM_OVL_ADAPTOR) {
 			if (!ovl_adaptor_comp_added)
 				ovl_adaptor_comp_added = true;
 			else
@@ -973,7 +1042,7 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 		return ret;
 
 	/* If the last entry is not a final display output, the configuration is wrong */
-	switch (temp_path[idx - 1]) {
+	switch (temp_path[idx - 1].type) {
 	case DDP_COMPONENT_DP_INTF0:
 	case DDP_COMPONENT_DP_INTF1:
 	case DDP_COMPONENT_DPI0:
@@ -985,30 +1054,31 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 		break;
 	default:
 		dev_err(dev, "Invalid display hw pipeline. Last component: %d (ret=%d)\n",
-			temp_path[idx - 1], ret);
+			temp_path[idx - 1].type, ret);
 		return -EINVAL;
 	}
 
-	final_ddp_path = devm_kmemdup(dev, temp_path, idx * sizeof(temp_path[0]), GFP_KERNEL);
-	if (!final_ddp_path)
+	/* Pipeline built! */
+	out_path->len = idx;
+	final_comp_sz = out_path->len * sizeof(out_path->comp[0]);
+	out_path->comp = devm_kmemdup(dev, temp_path, final_comp_sz, GFP_KERNEL);
+	if (!out_path->comp)
 		return -ENOMEM;
 
 	dev_dbg(dev, "Display HW Pipeline built with %d components.\n", idx);
 
-	/* Pipeline built! */
-	*out_path = final_ddp_path;
-	*out_path_len = idx;
-
 	return 0;
 }
 
 static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *node,
 				     struct mtk_mmsys_driver_data *data)
 {
+	struct mtk_drm_path_definition *output_paths;
 	struct device_node *ep_node;
 	struct of_endpoint of_ep;
 	bool output_present[MAX_CRTC] = { false };
-	int ret;
+	u8 num_outputs_present = 0;
+	int i, ret;
 
 	for_each_endpoint_of_node(node, ep_node) {
 		ret = of_graph_parse_endpoint(ep_node, &of_ep);
@@ -1024,33 +1094,30 @@ static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *nod
 		}
 
 		output_present[of_ep.id] = true;
+		num_outputs_present++;
 	}
+	if (ret == 0 && num_outputs_present == 0)
+		ret = dev_err_probe(dev, -ENXIO, "No display outputs found.\n");
 
 	if (ret) {
 		of_node_put(ep_node);
 		return ret;
 	}
 
-	if (output_present[CRTC_MAIN]) {
-		ret = mtk_drm_of_ddp_path_build_one(dev, CRTC_MAIN,
-						    &data->main_path, &data->main_len);
-		if (ret && ret != -ENODEV)
-			return ret;
-	}
+	/* This can be optimized after making CRTC numbers dynamic */
+	output_paths = devm_kcalloc(dev, MAX_CRTC, sizeof(*output_paths), GFP_KERNEL);
+	if (!output_paths)
+		return -ENOMEM;
 
-	if (output_present[CRTC_EXT]) {
-		ret = mtk_drm_of_ddp_path_build_one(dev, CRTC_EXT,
-						    &data->ext_path, &data->ext_len);
-		if (ret && ret != -ENODEV)
-			return ret;
-	}
+	for (i = 0; i < MAX_CRTC; i++) {
+		if (!output_present[i])
+			continue;
 
-	if (output_present[CRTC_THIRD]) {
-		ret = mtk_drm_of_ddp_path_build_one(dev, CRTC_THIRD,
-						    &data->third_path, &data->third_len);
+		ret = mtk_drm_of_ddp_path_build_one(dev, i, &output_paths[i]);
 		if (ret && ret != -ENODEV)
 			return ret;
 	}
+	data->output_paths = output_paths;
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index a171126d580e..7dc208669c2e 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -33,13 +33,18 @@ struct mtk_drm_route {
 	const unsigned int route_ddp;
 };
 
+struct mtk_drm_comp_definition {
+	enum mtk_ddp_comp_id type;
+};
+
+struct mtk_drm_path_definition {
+	const struct mtk_drm_comp_definition *comp;
+	u8 len;
+};
+
 struct mtk_mmsys_driver_data {
-	const unsigned int *main_path;
-	unsigned int main_len;
-	const unsigned int *ext_path;
-	unsigned int ext_len;
-	const unsigned int *third_path;
-	unsigned int third_len;
+	struct mtk_drm_path_definition *output_paths;
+	u8 num_output_paths;
 	const struct mtk_drm_route *conn_routes;
 	unsigned int num_conn_routes;
 
-- 
2.54.0



^ permalink raw reply related

* [PATCH 29/42] drm/mediatek: Add support for Display Controller exDMA component
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add support for the Display Overlay extended DMA engine (exDMA) as
found in the MediaTek Kompanio Ultra MT8196, Genio Pro 5100 MT8894,
Dimensity 9400 MT6991 and others.

This is a DMA engine and its purpose is to configure the Display
Controller to read data from DRAM and also includes color transfer
functions to process pixels into a consistent color domain.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/Makefile         |   1 +
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c   |  14 +
 drivers/gpu/drm/mediatek/mtk_disp_drv.h   |  14 +
 drivers/gpu/drm/mediatek/mtk_disp_exdma.c | 344 ++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c    |   3 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h    |   1 +
 include/linux/soc/mediatek/mtk-mmsys.h    |   1 +
 7 files changed, 378 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_exdma.c

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index a3c1285e8f33..5506f306a7b6 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -7,6 +7,7 @@ mediatek-drm-y := mtk_crtc.o \
 		  mtk_disp_ccorr.o \
 		  mtk_disp_color.o \
 		  mtk_disp_dsc.o \
+		  mtk_disp_exdma.o \
 		  mtk_disp_gamma.o \
 		  mtk_disp_merge.o \
 		  mtk_disp_ovl.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 6a312f3ddb09..9896420f902d 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -308,6 +308,18 @@ static const struct mtk_ddp_comp_funcs ddp_dvo = {
 	.encoder_index = mtk_dvo_encoder_index,
 };
 
+static const struct mtk_ddp_comp_funcs ddp_exdma = {
+	.clk_enable = mtk_exdma_clk_enable,
+	.clk_disable = mtk_exdma_clk_disable,
+	.config = mtk_exdma_config,
+	.start = mtk_exdma_start,
+	.stop = mtk_exdma_stop,
+	.layer_config = mtk_exdma_layer_config,
+	.layer_nr = mtk_exdma_layer_nr,
+	.get_formats = mtk_exdma_get_formats,
+	.get_num_formats = mtk_exdma_get_num_formats,
+};
+
 static const struct mtk_ddp_comp_funcs ddp_gamma = {
 	.clk_enable = mtk_gamma_clk_enable,
 	.clk_disable = mtk_gamma_clk_disable,
@@ -434,6 +446,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_COLOR] = "color",
 	[MTK_DISP_DITHER] = "dither",
 	[MTK_DISP_DSC] = "dsc",
+	[MTK_DISP_EXDMA] = "exdma",
 	[MTK_DISP_GAMMA] = "gamma",
 	[MTK_DISP_MERGE] = "merge",
 	[MTK_DISP_MUTEX] = "mutex",
@@ -460,6 +473,7 @@ static const struct mtk_ddp_comp_funcs *mtk_ddp_funcs[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_COLOR]		= &ddp_color,
 	[MTK_DISP_DITHER]		= &ddp_dither,
 	[MTK_DISP_DSC]			= &ddp_dsc,
+	[MTK_DISP_EXDMA]		= &ddp_exdma,
 	[MTK_DISP_GAMMA]		= &ddp_gamma,
 	[MTK_DISP_MERGE]		= &ddp_merge,
 	[MTK_DISP_OD]			= &ddp_od,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index e834bf17d86c..df4357b0954b 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -82,6 +82,20 @@ void mtk_dvo_start(struct device *dev);
 void mtk_dvo_stop(struct device *dev);
 unsigned int mtk_dvo_encoder_index(struct device *dev);
 
+int mtk_exdma_clk_enable(struct mtk_ddp_comp *comp);
+void mtk_exdma_clk_disable(struct mtk_ddp_comp *comp);
+void mtk_exdma_start(struct device *dev);
+void mtk_exdma_stop(struct device *dev);
+void mtk_exdma_config(struct mtk_ddp_comp *comp, unsigned int w,
+			   unsigned int h, unsigned int vrefresh,
+			   unsigned int bpc, struct cmdq_pkt *cmdq_pkt);
+void mtk_exdma_layer_config(struct device *dev, unsigned int idx,
+				 struct mtk_plane_state *state,
+				 struct cmdq_pkt *cmdq_pkt);
+unsigned int mtk_exdma_layer_nr(struct device *dev, int pipeline_index);
+const u32 *mtk_exdma_get_formats(struct device *dev);
+size_t mtk_exdma_get_num_formats(struct device *dev);
+
 int mtk_gamma_clk_enable(struct mtk_ddp_comp *comp);
 void mtk_gamma_clk_disable(struct mtk_ddp_comp *comp);
 void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_exdma.c b/drivers/gpu/drm/mediatek/mtk_disp_exdma.c
new file mode 100644
index 000000000000..c06eb85ebc2b
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_disp_exdma.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_fourcc.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/soc/mediatek/mtk-cmdq.h>
+
+#include "mtk_disp_drv.h"
+#include "mtk_disp_ovl.h"
+#include "mtk_drm_drv.h"
+
+#define DISP_REG_OVL_EXDMA_EN_CON		0xc
+#  define OVL_EXDMA_OP_8BIT_MODE			BIT(4)
+#  define OVL_EXDMA_HG_FOVL_EXDMA_CK_ON			BIT(8)
+#  define OVL_EXDMA_HF_FOVL_EXDMA_CK_ON			BIT(10)
+#define DISP_REG_OVL_EXDMA_DATAPATH_CON		0x014
+#  define OVL_EXDMA_DATAPATH_CON_LAYER_SMI_ID_EN	BIT(0)
+#  define OVL_EXDMA_DATAPATH_CON_GCLAST_EN		BIT(24)
+#  define OVL_EXDMA_DATAPATH_CON_HDR_GCLAST_EN		BIT(25)
+#define DISP_REG_OVL_EXDMA_EN			0x020
+#  define OVL_EXDMA_EN					BIT(0)
+#  define OVL_EXDMA_FORCE_RELAY_MODE			BIT(4)
+#define DISP_REG_OVL_EXDMA_RST			0x024
+#  define OVL_EXDMA_RST					BIT(0)
+#define DISP_REG_OVL_EXDMA_SRC_CON		0x02c
+#  define OVL_EXDMA_FORCE_CONSTANT_LAYER		BIT(4)
+#define DISP_REG_OVL_EXDMA_ROI_SIZE		0x030
+#define DISP_REG_OVL_EXDMA_L0_EN		0x040
+#  define OVL_EXDMA_L0_EN				BIT(0)
+#  define OVL_EXDMA_L0_SRC				GENMASK(9, 8)
+#  define OVL_EXDMA_L0_SRC_BLENDER			0
+#  define OVL_EXDMA_L0_SRC_COLOR			1
+#  define OVL_EXDMA_L0_SRC_UFOD				2
+#  define OVL_EXDMA_L0_SRC_PQ				3
+#define DISP_REG_OVL_EXDMA_L0_OFFSET		0x044
+#define DISP_REG_OVL_EXDMA_SRC_SIZE		0x048
+#define DISP_REG_OVL_EXDMA_L0_CLRFMT		0x050
+#  define OVL_EXDMA_CON_FLD_CLRFMT			GENMASK(3, 0)
+#  define OVL_EXDMA_CON_CLRFMT_MAN			BIT(4)
+#  define OVL_EXDMA_CON_FLD_CLRFMT_NB			GENMASK(9, 8)
+#  define OVL_EXDMA_CON_CLRFMT_NB_10_BIT		BIT(8)
+#  define OVL_EXDMA_CON_BYTE_SWAP			BIT(16)
+#  define OVL_EXDMA_CON_RGB_SWAP			BIT(17)
+#define DISP_REG_OVL_EXDMA_RDMA0_CTRL		0x100
+#  define OVL_EXDMA_RDMA0_EN				BIT(0)
+#define DISP_REG_OVL_EXDMA_RDMA_BURST_CON1	0x1f4
+#  define OVL_EXDMA_RDMA_BURST_CON1_BURST16_EN		BIT(28)
+#  define OVL_EXDMA_RDMA_BURST_CON1_DDR_EN		BIT(30)
+#  define OVL_EXDMA_RDMA_BURST_CON1_DDR_ACK_EN		BIT(31)
+#define DISP_REG_OVL_EXDMA_DUMMY_REG		0x200
+#  define OVL_EXDMA_EXT_DDR_EN_OPT			BIT(2)
+#  define OVL_EXDMA_FORCE_EXT_DDR_EN			BIT(3)
+#define DISP_REG_OVL_EXDMA_GDRDY_PRD		0x208
+#define DISP_REG_OVL_EXDMA_PITCH_MSB		0x2f0
+#  define OVL_EXDMA_L0_SRC_PITCH_MSB_MASK		GENMASK(3, 0)
+#define DISP_REG_OVL_EXDMA_PITCH		0x2f4
+#  define OVL_EXDMA_L0_CONST_BLD			BIT(28)
+#  define OVL_EXDMA_L0_SRC_PITCH_MASK			GENMASK(15, 0)
+#define DISP_REG_OVL_EXDMA_L0_GUSER_EXT		0x2fc
+#  define OVL_EXDMA_RDMA0_L0_VCSEL			BIT(5)
+#  define OVL_EXDMA_RDMA0_HDR_L0_VCSEL			BIT(21)
+#define DISP_REG_OVL_EXDMA_CON			0x300
+#  define OVL_EXDMA_CON_FLD_INT_MTX_SEL			GENMASK(19, 16)
+#  define OVL_EXDMA_CON_INT_MTX_BT601_TO_RGB		6
+#  define OVL_EXDMA_CON_INT_MTX_BT709_TO_RGB		7
+#  define OVL_EXDMA_CON_INT_MTX_EN			BIT(27)
+#define DISP_REG_OVL_EXDMA_ADDR			0xf40
+#define DISP_REG_OVL_EXDMA_MOUT			0xff0
+#  define OVL_EXDMA_MOUT_OUT_DATA			BIT(0)
+#  define OVL_EXDMA_MOUT_BGCLR_OUT			BIT(1)
+
+#define OVL_EXDMA_MAX_SIZE			(8191)
+
+struct mtk_disp_exdma {
+	void __iomem *regs;
+	struct clk *clk;
+	struct cmdq_client_reg cmdq_reg;
+};
+
+static unsigned int mtk_exdma_color_convert(unsigned int color_encoding)
+{
+	switch (color_encoding) {
+	default:
+	case DRM_COLOR_YCBCR_BT709:
+		return FIELD_PREP_CONST(OVL_EXDMA_CON_FLD_INT_MTX_SEL,
+					OVL_EXDMA_CON_INT_MTX_BT709_TO_RGB);
+	case DRM_COLOR_YCBCR_BT601:
+		return FIELD_PREP_CONST(OVL_EXDMA_CON_FLD_INT_MTX_SEL,
+					OVL_EXDMA_CON_INT_MTX_BT601_TO_RGB);
+	}
+}
+
+void mtk_exdma_start(struct device *dev)
+{
+	struct mtk_disp_exdma *priv = dev_get_drvdata(dev);
+	const u32 val = OVL_EXDMA_DATAPATH_CON_LAYER_SMI_ID_EN |
+			OVL_EXDMA_DATAPATH_CON_HDR_GCLAST_EN |
+			OVL_EXDMA_DATAPATH_CON_GCLAST_EN;
+
+	writel(val, priv->regs + DISP_REG_OVL_EXDMA_DATAPATH_CON);
+	writel(OVL_EXDMA_EN, priv->regs + DISP_REG_OVL_EXDMA_EN);
+}
+
+void mtk_exdma_stop(struct device *dev)
+{
+	struct mtk_disp_exdma *priv = dev_get_drvdata(dev);
+
+	writel(0, priv->regs + DISP_REG_OVL_EXDMA_EN);
+	writel(0, priv->regs + DISP_REG_OVL_EXDMA_DATAPATH_CON);
+	writel(OVL_EXDMA_RST, priv->regs + DISP_REG_OVL_EXDMA_RST);
+	writel(0, priv->regs + DISP_REG_OVL_EXDMA_RST);
+}
+
+void mtk_exdma_layer_config(struct device *dev, unsigned int idx,
+				 struct mtk_plane_state *state,
+				 struct cmdq_pkt *cmdq_pkt)
+{
+	struct mtk_disp_exdma *priv = dev_get_drvdata(dev);
+	struct mtk_plane_pending_state *pending = &state->pending;
+	const struct drm_format_info *fmt_info = drm_format_info(pending->format);
+	const u32 blend_mode = mtk_ovl_get_blend_mode(state, MTK_OVL_SUPPORT_BLEND_MODES);
+	const bool csc_enable = (fmt_info) ? fmt_info->is_yuv : false;
+	u32 val;
+
+	if (!pending->enable || pending->height == 0 || pending->width == 0 ||
+	    pending->x > OVL_EXDMA_MAX_SIZE || pending->y > OVL_EXDMA_MAX_SIZE) {
+		mtk_ddp_write_mask(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs,
+				   DISP_REG_OVL_EXDMA_RDMA0_CTRL, OVL_EXDMA_RDMA0_EN);
+		mtk_ddp_write_mask(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs,
+				   DISP_REG_OVL_EXDMA_L0_EN, OVL_EXDMA_L0_EN);
+		return;
+	}
+
+	/* Set layer Width and Height */
+	val = ((pending->height & GENMASK(15, 0)) << 16) | (pending->width & GENMASK(15, 0));
+	mtk_ddp_write(cmdq_pkt, val, &priv->cmdq_reg,
+		      priv->regs, DISP_REG_OVL_EXDMA_ROI_SIZE);
+
+	mtk_ddp_write(cmdq_pkt, val, &priv->cmdq_reg,
+		      priv->regs, DISP_REG_OVL_EXDMA_SRC_SIZE);
+
+	/* The layer X/Y */
+	mtk_ddp_write(cmdq_pkt,
+		      ((pending->y & GENMASK(15, 0)) << 16) | (pending->x & GENMASK(15, 0)),
+		      &priv->cmdq_reg, priv->regs, DISP_REG_OVL_EXDMA_L0_OFFSET);
+
+	/* The memory address */
+	mtk_ddp_write(cmdq_pkt, pending->addr, &priv->cmdq_reg,
+		      priv->regs, DISP_REG_OVL_EXDMA_ADDR);
+
+	/* ...and the pitch. */
+	val = pending->pitch;
+	if (mtk_ovl_is_ignore_pixel_alpha(state, blend_mode))
+		val |= OVL_EXDMA_L0_CONST_BLD;
+
+	mtk_ddp_write_mask(cmdq_pkt, val, &priv->cmdq_reg, priv->regs, DISP_REG_OVL_EXDMA_PITCH,
+			   OVL_EXDMA_L0_CONST_BLD | OVL_EXDMA_L0_SRC_PITCH_MASK);
+	mtk_ddp_write_mask(cmdq_pkt, pending->pitch >> 16, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_EXDMA_PITCH_MSB, OVL_EXDMA_L0_SRC_PITCH_MSB_MASK);
+
+	val = mtk_exdma_color_convert(pending->color_encoding);
+	val |= csc_enable ? OVL_EXDMA_CON_INT_MTX_EN : 0;
+	mtk_ddp_write_mask(cmdq_pkt, val, &priv->cmdq_reg, priv->regs, DISP_REG_OVL_EXDMA_CON,
+			   OVL_EXDMA_CON_FLD_INT_MTX_SEL | OVL_EXDMA_CON_INT_MTX_EN);
+
+	val = mtk_ovl_fmt_convert(pending->format, blend_mode, true, false, 0,
+				  OVL_EXDMA_CON_CLRFMT_MAN, OVL_EXDMA_CON_BYTE_SWAP,
+				  OVL_EXDMA_CON_RGB_SWAP);
+	if (mtk_ovl_is_10bit_rgb(pending->format))
+		val |= OVL_EXDMA_CON_CLRFMT_NB_10_BIT;
+	mtk_ddp_write_mask(cmdq_pkt, val, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_EXDMA_L0_CLRFMT,
+			   OVL_EXDMA_CON_RGB_SWAP | OVL_EXDMA_CON_BYTE_SWAP |
+			   OVL_EXDMA_CON_CLRFMT_MAN | OVL_EXDMA_CON_FLD_CLRFMT |
+			   OVL_EXDMA_CON_FLD_CLRFMT_NB);
+
+	/* Virtual Channel selection */
+	val = OVL_EXDMA_RDMA0_L0_VCSEL;
+	mtk_ddp_write_mask(cmdq_pkt, val, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_EXDMA_L0_GUSER_EXT, val);
+
+	mtk_ddp_write_mask(cmdq_pkt, OVL_EXDMA_RDMA0_EN, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_EXDMA_RDMA0_CTRL, OVL_EXDMA_RDMA0_EN);
+	mtk_ddp_write_mask(cmdq_pkt, OVL_EXDMA_L0_EN, &priv->cmdq_reg, priv->regs,
+			   DISP_REG_OVL_EXDMA_L0_EN, OVL_EXDMA_L0_EN);
+}
+
+unsigned int mtk_exdma_layer_nr(struct device *dev, int pipeline_index)
+{
+	return 1;
+}
+
+void mtk_exdma_config(struct mtk_ddp_comp *comp, unsigned int w,
+			   unsigned int h, unsigned int vrefresh,
+			   unsigned int bpc, struct cmdq_pkt *cmdq_pkt)
+{
+	struct mtk_disp_exdma *priv = dev_get_drvdata(comp->dev);
+	unsigned int tmp, val, mask;
+
+	/*
+	 * This configuration enables dynamic power switching mechanism for EXDMA,
+	 * also known as "SRT mode".
+	 * Such configuration allows the system to achieve better power efficiency.
+	 */
+	val = OVL_EXDMA_RDMA_BURST_CON1_BURST16_EN | OVL_EXDMA_RDMA_BURST_CON1_DDR_ACK_EN;
+	mask = OVL_EXDMA_RDMA_BURST_CON1_BURST16_EN | OVL_EXDMA_RDMA_BURST_CON1_DDR_EN |
+	       OVL_EXDMA_RDMA_BURST_CON1_DDR_ACK_EN;
+	tmp = readl(priv->regs + DISP_REG_OVL_EXDMA_RDMA_BURST_CON1);
+	tmp = (tmp & ~mask) | val;
+	writel(tmp, priv->regs + DISP_REG_OVL_EXDMA_RDMA_BURST_CON1);
+
+	/*
+	 * The dummy register is used in the configuration of the EXDMA engine to
+	 * signal ddren_request, and get ddren_ack before accessing the DRAM to
+	 * ensure data transfers occur normally.
+	 */
+	val = OVL_EXDMA_EXT_DDR_EN_OPT | OVL_EXDMA_FORCE_EXT_DDR_EN;
+	writel(val, priv->regs + DISP_REG_OVL_EXDMA_DUMMY_REG);
+	val = OVL_EXDMA_MOUT_BGCLR_OUT;
+	mask = OVL_EXDMA_MOUT_BGCLR_OUT | OVL_EXDMA_MOUT_OUT_DATA;
+	tmp = readl(priv->regs + DISP_REG_OVL_EXDMA_MOUT);
+	tmp = (tmp & ~mask) | val;
+	writel(tmp, priv->regs + DISP_REG_OVL_EXDMA_MOUT);
+	writel(GENMASK(31, 0), priv->regs + DISP_REG_OVL_EXDMA_GDRDY_PRD);
+	val = OVL_EXDMA_HG_FOVL_EXDMA_CK_ON | OVL_EXDMA_HF_FOVL_EXDMA_CK_ON |
+	      OVL_EXDMA_OP_8BIT_MODE;
+	writel(val, priv->regs + DISP_REG_OVL_EXDMA_EN_CON);
+	writel(OVL_EXDMA_RDMA0_L0_VCSEL, priv->regs + DISP_REG_OVL_EXDMA_L0_GUSER_EXT);
+}
+
+const u32 *mtk_exdma_get_formats(struct device *dev)
+{
+	return mt8195_ovl_formats;
+}
+
+size_t mtk_exdma_get_num_formats(struct device *dev)
+{
+	return mt8195_ovl_formats_len;
+}
+
+int mtk_exdma_clk_enable(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_exdma *exdma = dev_get_drvdata(comp->dev);
+
+	return clk_prepare_enable(exdma->clk);
+}
+
+void mtk_exdma_clk_disable(struct mtk_ddp_comp *comp)
+{
+	struct mtk_disp_exdma *exdma = dev_get_drvdata(comp->dev);
+
+	clk_disable_unprepare(exdma->clk);
+}
+
+static int mtk_exdma_bind(struct device *dev, struct device *master,
+			       void *data)
+{
+	return 0;
+}
+
+static void mtk_exdma_unbind(struct device *dev, struct device *master,
+				  void *data)
+{
+}
+
+static const struct component_ops mtk_disp_exdma_component_ops = {
+	.bind	= mtk_exdma_bind,
+	.unbind = mtk_exdma_unbind,
+};
+
+static int mtk_disp_exdma_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_disp_exdma *priv;
+	int ret = 0;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(priv->regs))
+		return dev_err_probe(dev, PTR_ERR(priv->regs), "Cannot get reg resource\n");
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clk))
+		return dev_err_probe(dev, PTR_ERR(priv->clk), "Cannot get clocks\n");
+
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+	ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);
+	if (ret)
+		dev_dbg(dev, "No mediatek,gce-client-reg\n");
+#endif
+	platform_set_drvdata(pdev, priv);
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
+	ret = component_add(dev, &mtk_disp_exdma_component_ops);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add component\n");
+
+	return 0;
+}
+
+static void mtk_disp_exdma_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_disp_exdma_component_ops);
+}
+
+static const struct of_device_id mtk_disp_exdma_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8196-disp-exdma", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_disp_exdma_driver_dt_match);
+
+struct platform_driver mtk_disp_exdma_driver = {
+	.probe = mtk_disp_exdma_probe,
+	.remove = mtk_disp_exdma_remove,
+	.driver = {
+		.name = "mediatek-disp-exdma",
+		.owner = THIS_MODULE,
+		.of_match_table = mtk_disp_exdma_driver_dt_match,
+	},
+};
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_AUTHOR("Nancy Lin <nancy.lin@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek Display Controller extended DMA Engine (exDMA) Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index a70392c54bb9..9f1264bb1d5e 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -523,6 +523,8 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DISP_DITHER },
 	{ .compatible = "mediatek,mt8195-disp-dsc",
 	  .data = (void *)MTK_DISP_DSC },
+	{ .compatible = "mediatek,mt8196-disp-exdma",
+	  .data = (void *)MTK_DISP_EXDMA },
 	{ .compatible = "mediatek,mt8167-disp-gamma",
 	  .data = (void *)MTK_DISP_GAMMA, },
 	{ .compatible = "mediatek,mt8173-disp-gamma",
@@ -1065,6 +1067,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_ccorr_driver,
 	&mtk_disp_color_driver,
 	&mtk_disp_dsc_driver,
+	&mtk_disp_exdma_driver,
 	&mtk_disp_gamma_driver,
 	&mtk_disp_merge_driver,
 	&mtk_disp_ovl_adaptor_driver,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index f36888fd7244..4ef61c1e33e3 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -77,6 +77,7 @@ extern struct platform_driver mtk_disp_blender_driver;
 extern struct platform_driver mtk_disp_ccorr_driver;
 extern struct platform_driver mtk_disp_color_driver;
 extern struct platform_driver mtk_disp_dsc_driver;
+extern struct platform_driver mtk_disp_exdma_driver;
 extern struct platform_driver mtk_disp_gamma_driver;
 extern struct platform_driver mtk_disp_merge_driver;
 extern struct platform_driver mtk_disp_ovl_adaptor_driver;
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index b32f0c92f040..d5c082d920c4 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -94,6 +94,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_DITHER,
 	MTK_DISP_DSC,
 	MTK_DISP_ETHDR_MIXER,
+	MTK_DISP_EXDMA,
 	MTK_DISP_GAMMA,
 	MTK_DISP_MERGE,
 	MTK_DISP_MUTEX,
-- 
2.54.0



^ permalink raw reply related

* [PATCH 22/42] drm/mediatek: Create new mtk_dpi_common lib and move mtk_dpi code
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

As a final preparation step for adding support for the MediaTek
Digital Video Out (DVO) hardware found in newer SoCs, create a
new mtk_dpi_common library and move common code between the DPI
and the upcoming DVO in there.

This change brings no functional differences, as the new file is
built-in on the mediatek-drm object anyway.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/Makefile         |   1 +
 drivers/gpu/drm/mediatek/mtk_dpi.c        | 654 +---------------------
 drivers/gpu/drm/mediatek/mtk_dpi_common.c | 468 ++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dpi_common.h | 276 +++++++++
 4 files changed, 762 insertions(+), 637 deletions(-)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_common.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_common.h

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index f40ad5565716..34a1d4721b70 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -15,6 +15,7 @@ mediatek-drm-y := mtk_crtc.o \
 		  mtk_drm_drv.o \
 		  mtk_drm_legacy.o \
 		  mtk_dsi.o \
+		  mtk_dpi_common.o \
 		  mtk_dpi.o \
 		  mtk_ethdr.o \
 		  mtk_mdp_rdma.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index 077e8a42a044..ac972c0a9711 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -2,17 +2,16 @@
 /*
  * Copyright (c) 2014 MediaTek Inc.
  * Author: Jie Qiu <jie.qiu@mediatek.com>
+ *
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
  */
 
 #include <linux/bitfield.h>
-#include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/debugfs.h>
-#include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/media-bus-format.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/soc/mediatek/mtk-mmsys.h>
@@ -23,172 +22,12 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_edid.h>
 #include <drm/drm_of.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include "mtk_ddp_comp.h"
 #include "mtk_disp_drv.h"
+#include "mtk_dpi_common.h"
 #include "mtk_dpi_regs.h"
-#include "mtk_drm_drv.h"
-
-enum mtk_dpi_out_bit_num {
-	MTK_DPI_OUT_BIT_NUM_8BITS,
-	MTK_DPI_OUT_BIT_NUM_10BITS,
-	MTK_DPI_OUT_BIT_NUM_12BITS,
-	MTK_DPI_OUT_BIT_NUM_16BITS
-};
-
-enum mtk_dpi_out_yc_map {
-	MTK_DPI_OUT_YC_MAP_RGB,
-	MTK_DPI_OUT_YC_MAP_CYCY,
-	MTK_DPI_OUT_YC_MAP_YCYC,
-	MTK_DPI_OUT_YC_MAP_CY,
-	MTK_DPI_OUT_YC_MAP_YC
-};
-
-enum mtk_dpi_out_channel_swap {
-	MTK_DPI_OUT_CHANNEL_SWAP_RGB,
-	MTK_DPI_OUT_CHANNEL_SWAP_GBR,
-	MTK_DPI_OUT_CHANNEL_SWAP_BRG,
-	MTK_DPI_OUT_CHANNEL_SWAP_RBG,
-	MTK_DPI_OUT_CHANNEL_SWAP_GRB,
-	MTK_DPI_OUT_CHANNEL_SWAP_BGR
-};
-
-enum mtk_dpi_out_color_format {
-	MTK_DPI_COLOR_FORMAT_RGB,
-	MTK_DPI_COLOR_FORMAT_YCBCR_422,
-	MTK_DPI_COLOR_FORMAT_YCBCR_444
-};
-
-struct mtk_dpi {
-	struct drm_encoder encoder;
-	struct drm_bridge bridge;
-	struct drm_bridge *next_bridge;
-	struct drm_connector *connector;
-	void __iomem *regs;
-	struct device *dev;
-	struct device *mmsys_dev;
-	struct clk *engine_clk;
-	struct clk *pixel_clk;
-	struct clk *tvd_clk;
-	int irq;
-	struct drm_display_mode mode;
-	const struct mtk_dpi_conf *conf;
-	enum mtk_dpi_out_color_format color_format;
-	enum mtk_dpi_out_yc_map yc_map;
-	enum mtk_dpi_out_bit_num bit_num;
-	enum mtk_dpi_out_channel_swap channel_swap;
-	struct pinctrl *pinctrl;
-	struct pinctrl_state *pins_gpio;
-	struct pinctrl_state *pins_dpi;
-	u32 output_fmt;
-	int refcount;
-};
-
-static inline struct mtk_dpi *bridge_to_dpi(struct drm_bridge *b)
-{
-	return container_of(b, struct mtk_dpi, bridge);
-}
-
-enum mtk_dpi_polarity {
-	MTK_DPI_POLARITY_RISING,
-	MTK_DPI_POLARITY_FALLING,
-};
-
-struct mtk_dpi_polarities {
-	enum mtk_dpi_polarity de_pol;
-	enum mtk_dpi_polarity ck_pol;
-	enum mtk_dpi_polarity hsync_pol;
-	enum mtk_dpi_polarity vsync_pol;
-};
-
-struct mtk_dpi_sync_param {
-	u32 sync_width;
-	u32 front_porch;
-	u32 back_porch;
-	bool shift_half_line;
-};
-
-struct mtk_dpi_sync {
-	struct mtk_dpi_sync_param hsync;
-	struct mtk_dpi_sync_param vsync_l_odd;
-	struct mtk_dpi_sync_param vsync_l_even;
-	struct mtk_dpi_sync_param vsync_r_odd;
-	struct mtk_dpi_sync_param vsync_r_even;
-};
-
-struct mtk_dpi_yc_limit {
-	u16 y_top;
-	u16 y_bottom;
-	u16 c_top;
-	u16 c_bottom;
-};
-
-struct mtk_dpi_factor {
-	u32 clock;
-	u8 factor;
-};
-
-/**
- * struct mtk_dpi_conf - Configuration of mediatek dpi.
- * @dpi_factor: SoC-specific pixel clock PLL factor values.
- * @num_dpi_factor: Number of pixel clock PLL factor values.
- * @reg_h_fre_con: Register address of frequency control.
- * @max_clock_khz: Max clock frequency supported for this SoCs in khz units.
- * @edge_sel_en: Enable of edge selection.
- * @output_fmts: Array of supported output formats.
- * @num_output_fmts: Quantity of supported output formats.
- * @is_ck_de_pol: Support CK/DE polarity.
- * @swap_input_support: Support input swap function.
- * @support_direct_pin: IP supports direct connection to dpi panels.
- * @dimension_mask: Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH
- *		    (no shift).
- * @hvsize_mask: Mask of HSIZE and VSIZE mask (no shift).
- * @channel_swap_shift: Shift value of channel swap.
- * @yuv422_en_bit: Enable bit of yuv422.
- * @csc_enable_bit: Enable bit of CSC.
- * @input_2p_en_bit: Enable bit for input two pixel per round feature.
- *		     If present, implies that the feature must be enabled.
- * @pixels_per_iter: Quantity of transferred pixels per iteration.
- * @edge_cfg_in_mmsys: If the edge configuration for DPI's output needs to be set in MMSYS.
- * @clocked_by_hdmi: HDMI IP outputs clock to dpi_pixel_clk input clock, needed
- *		     for DPI registers access.
- * @output_1pixel: Enable outputting one pixel per round; if the input is two pixel per
- *                 round, the DPI hardware will internally transform it to 1T1P.
- */
-struct mtk_dpi_conf {
-	const struct mtk_dpi_factor *dpi_factor;
-	const u8 num_dpi_factor;
-	u32 reg_h_fre_con;
-	u32 max_clock_khz;
-	bool edge_sel_en;
-	const u32 *output_fmts;
-	u32 num_output_fmts;
-	bool is_ck_de_pol;
-	bool swap_input_support;
-	bool support_direct_pin;
-	u32 dimension_mask;
-	u32 hvsize_mask;
-	u32 channel_swap_shift;
-	u32 yuv422_en_bit;
-	u32 csc_enable_bit;
-	u32 input_2p_en_bit;
-	u32 pixels_per_iter;
-	bool edge_cfg_in_mmsys;
-	bool clocked_by_hdmi;
-	bool output_1pixel;
-};
-
-static void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
-{
-	u32 tmp = readl(dpi->regs + offset) & ~mask;
-
-	tmp |= (val & mask);
-	writel(tmp, dpi->regs + offset);
-}
 
 static void mtk_dpi_test_pattern_en(struct mtk_dpi *dpi, u8 type, bool enable)
 {
@@ -466,165 +305,6 @@ static void mtk_dpi_dual_edge(struct mtk_dpi *dpi)
 	}
 }
 
-static void mtk_dpi_power_off(struct mtk_dpi *dpi)
-{
-	if (WARN_ON(dpi->refcount == 0))
-		return;
-
-	if (--dpi->refcount != 0)
-		return;
-
-	clk_disable_unprepare(dpi->pixel_clk);
-	clk_disable_unprepare(dpi->tvd_clk);
-	clk_disable_unprepare(dpi->engine_clk);
-}
-
-static int mtk_dpi_power_on(struct mtk_dpi *dpi)
-{
-	int ret;
-
-	if (++dpi->refcount != 1)
-		return 0;
-
-	ret = clk_prepare_enable(dpi->engine_clk);
-	if (ret) {
-		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
-		goto err_refcount;
-	}
-
-	ret = clk_prepare_enable(dpi->tvd_clk);
-	if (ret) {
-		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
-		goto err_engine;
-	}
-
-	ret = clk_prepare_enable(dpi->pixel_clk);
-	if (ret) {
-		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
-		goto err_pixel;
-	}
-
-	return 0;
-
-err_pixel:
-	clk_disable_unprepare(dpi->tvd_clk);
-err_engine:
-	clk_disable_unprepare(dpi->engine_clk);
-err_refcount:
-	dpi->refcount--;
-	return ret;
-}
-
-static unsigned int mtk_dpi_calculate_factor(struct mtk_dpi *dpi, int mode_clk)
-{
-	const struct mtk_dpi_factor *dpi_factor = dpi->conf->dpi_factor;
-	int i;
-
-	for (i = 0; i < dpi->conf->num_dpi_factor; i++) {
-		if (mode_clk <= dpi_factor[i].clock)
-			return dpi_factor[i].factor;
-	}
-
-	/* If no match try the lowest possible factor */
-	return dpi_factor[dpi->conf->num_dpi_factor - 1].factor;
-}
-
-static void mtk_dpi_set_pixel_clk(struct mtk_dpi *dpi, struct videomode *vm, int mode_clk)
-{
-	unsigned long pll_rate;
-	unsigned int factor;
-
-	/* let pll_rate can fix the valid range of tvdpll (1G~2GHz) */
-	factor = mtk_dpi_calculate_factor(dpi, mode_clk);
-	pll_rate = vm->pixelclock * factor;
-
-	dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n",
-		pll_rate, vm->pixelclock);
-
-	clk_set_rate(dpi->tvd_clk, pll_rate);
-	pll_rate = clk_get_rate(dpi->tvd_clk);
-
-	/*
-	 * Depending on the IP version, we may output a different amount of
-	 * pixels for each iteration: divide the clock by this number and
-	 * adjust the display porches accordingly.
-	 */
-	vm->pixelclock = pll_rate / factor;
-	vm->pixelclock /= dpi->conf->pixels_per_iter;
-
-	if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
-	    (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
-		clk_set_rate(dpi->pixel_clk, vm->pixelclock * 2);
-	else
-		clk_set_rate(dpi->pixel_clk, vm->pixelclock);
-
-	vm->pixelclock = clk_get_rate(dpi->pixel_clk);
-
-	dev_dbg(dpi->dev, "Got  PLL %lu Hz, pixel clock %lu Hz\n",
-		pll_rate, vm->pixelclock);
-}
-
-static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm,
-				    struct mtk_dpi_sync *sync,
-				    struct mtk_dpi_polarities *dpi_pol,
-				    struct mtk_dpi_yc_limit *limit)
-{
-	struct drm_display_mode *mode = &dpi->mode;
-
-	drm_display_mode_to_videomode(mode, vm);
-
-	if (!dpi->conf->clocked_by_hdmi)
-		mtk_dpi_set_pixel_clk(dpi, vm, mode->clock);
-
-	dpi_pol->ck_pol = MTK_DPI_POLARITY_FALLING;
-	dpi_pol->de_pol = MTK_DPI_POLARITY_RISING;
-	dpi_pol->hsync_pol = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
-			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-	dpi_pol->vsync_pol = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
-			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-
-	/*
-	 * Depending on the IP version, we may output a different amount of
-	 * pixels for each iteration: adjust the display porches accordingly.
-	 */
-	sync->hsync.sync_width = vm->hsync_len / dpi->conf->pixels_per_iter;
-	sync->hsync.back_porch = vm->hback_porch / dpi->conf->pixels_per_iter;
-	sync->hsync.front_porch = vm->hfront_porch / dpi->conf->pixels_per_iter;
-	sync->hsync.shift_half_line = false;
-
-	sync->vsync_l_odd.sync_width = vm->vsync_len;
-	sync->vsync_l_odd.back_porch = vm->vback_porch;
-	sync->vsync_l_odd.front_porch = vm->vfront_porch;
-	sync->vsync_l_odd.shift_half_line = false;
-
-	if (vm->flags & DISPLAY_FLAGS_INTERLACED) {
-		sync->vsync_l_even = sync->vsync_l_odd;
-		sync->vsync_l_even.shift_half_line = true;
-
-		if (mode->flags & DRM_MODE_FLAG_3D_MASK) {
-			sync->vsync_r_odd = sync->vsync_l_odd;
-			sync->vsync_r_even = sync->vsync_l_odd;
-			sync->vsync_r_even.shift_half_line = true;
-		}
-	} else if (mode->flags & DRM_MODE_FLAG_3D_MASK) {
-		sync->vsync_r_odd = sync->vsync_l_odd;
-	}
-
-	if (drm_default_rgb_quant_range(&dpi->mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
-		limit->y_bottom = 0x10;
-		limit->y_top = 0xfe0;
-		limit->c_bottom = 0x10;
-		limit->c_top = 0xfe0;
-	} else {
-		limit->y_bottom = 0;
-		limit->y_top = 0xfff;
-		limit->c_bottom = 0;
-		limit->c_top = 0xfff;
-	}
-
-	return 0;
-}
-
 static void mtk_dpi_config_hw(struct mtk_dpi *dpi,
 			      struct videomode *vm, struct mtk_dpi_sync *sync,
 			      struct mtk_dpi_polarities *dpi_pol,
@@ -680,143 +360,6 @@ static void mtk_dpi_config_hw(struct mtk_dpi *dpi,
 	return;
 }
 
-static u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
-						      struct drm_bridge_state *bridge_state,
-						      struct drm_crtc_state *crtc_state,
-						      struct drm_connector_state *conn_state,
-						      unsigned int *num_output_fmts)
-{
-	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
-	u32 *output_fmts;
-
-	*num_output_fmts = 0;
-
-	if (!dpi->conf->output_fmts) {
-		dev_err(dpi->dev, "output_fmts should not be null\n");
-		return NULL;
-	}
-
-	output_fmts = kcalloc(dpi->conf->num_output_fmts, sizeof(*output_fmts),
-			     GFP_KERNEL);
-	if (!output_fmts)
-		return NULL;
-
-	*num_output_fmts = dpi->conf->num_output_fmts;
-
-	memcpy(output_fmts, dpi->conf->output_fmts,
-	       sizeof(*output_fmts) * dpi->conf->num_output_fmts);
-
-	return output_fmts;
-}
-
-static u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
-						     struct drm_bridge_state *bridge_state,
-						     struct drm_crtc_state *crtc_state,
-						     struct drm_connector_state *conn_state,
-						     u32 output_fmt,
-						     unsigned int *num_input_fmts)
-{
-	u32 *input_fmts;
-
-	*num_input_fmts = 0;
-
-	input_fmts = kcalloc(1, sizeof(*input_fmts),
-			     GFP_KERNEL);
-	if (!input_fmts)
-		return NULL;
-
-	*num_input_fmts = 1;
-	input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
-
-	return input_fmts;
-}
-
-static unsigned int mtk_dpi_bus_fmt_bit_num(unsigned int out_bus_format)
-{
-	switch (out_bus_format) {
-	default:
-	case MEDIA_BUS_FMT_RGB888_1X24:
-	case MEDIA_BUS_FMT_BGR888_1X24:
-	case MEDIA_BUS_FMT_RGB888_2X12_LE:
-	case MEDIA_BUS_FMT_RGB888_2X12_BE:
-	case MEDIA_BUS_FMT_YUYV8_1X16:
-	case MEDIA_BUS_FMT_YUV8_1X24:
-		return MTK_DPI_OUT_BIT_NUM_8BITS;
-	case MEDIA_BUS_FMT_RGB101010_1X30:
-	case MEDIA_BUS_FMT_YUYV10_1X20:
-	case MEDIA_BUS_FMT_YUV10_1X30:
-		return MTK_DPI_OUT_BIT_NUM_10BITS;
-	case MEDIA_BUS_FMT_YUYV12_1X24:
-		return MTK_DPI_OUT_BIT_NUM_12BITS;
-	}
-}
-
-static unsigned int mtk_dpi_bus_fmt_channel_swap(unsigned int out_bus_format)
-{
-	switch (out_bus_format) {
-	default:
-	case MEDIA_BUS_FMT_RGB888_1X24:
-	case MEDIA_BUS_FMT_RGB888_2X12_LE:
-	case MEDIA_BUS_FMT_RGB888_2X12_BE:
-	case MEDIA_BUS_FMT_RGB101010_1X30:
-	case MEDIA_BUS_FMT_YUYV8_1X16:
-	case MEDIA_BUS_FMT_YUYV10_1X20:
-	case MEDIA_BUS_FMT_YUYV12_1X24:
-		return MTK_DPI_OUT_CHANNEL_SWAP_RGB;
-	case MEDIA_BUS_FMT_BGR888_1X24:
-	case MEDIA_BUS_FMT_YUV8_1X24:
-	case MEDIA_BUS_FMT_YUV10_1X30:
-		return MTK_DPI_OUT_CHANNEL_SWAP_BGR;
-	}
-}
-
-static unsigned int mtk_dpi_bus_fmt_color_format(unsigned int out_bus_format)
-{
-	switch (out_bus_format) {
-	default:
-	case MEDIA_BUS_FMT_RGB888_1X24:
-	case MEDIA_BUS_FMT_BGR888_1X24:
-	case MEDIA_BUS_FMT_RGB888_2X12_LE:
-	case MEDIA_BUS_FMT_RGB888_2X12_BE:
-	case MEDIA_BUS_FMT_RGB101010_1X30:
-		return MTK_DPI_COLOR_FORMAT_RGB;
-	case MEDIA_BUS_FMT_YUYV8_1X16:
-	case MEDIA_BUS_FMT_YUYV10_1X20:
-	case MEDIA_BUS_FMT_YUYV12_1X24:
-		return MTK_DPI_COLOR_FORMAT_YCBCR_422;
-	case MEDIA_BUS_FMT_YUV8_1X24:
-	case MEDIA_BUS_FMT_YUV10_1X30:
-		return MTK_DPI_COLOR_FORMAT_YCBCR_444;
-	}
-}
-
-static int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
-				       struct drm_bridge_state *bridge_state,
-				       struct drm_crtc_state *crtc_state,
-				       struct drm_connector_state *conn_state)
-{
-	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
-	unsigned int out_bus_format;
-
-	out_bus_format = bridge_state->output_bus_cfg.format;
-
-	if (out_bus_format == MEDIA_BUS_FMT_FIXED)
-		if (dpi->conf->num_output_fmts)
-			out_bus_format = dpi->conf->output_fmts[0];
-
-	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
-		bridge_state->input_bus_cfg.format,
-		bridge_state->output_bus_cfg.format);
-
-	dpi->output_fmt = out_bus_format;
-	dpi->bit_num = mtk_dpi_bus_fmt_bit_num(out_bus_format);
-	dpi->channel_swap = mtk_dpi_bus_fmt_channel_swap(out_bus_format);
-	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
-	dpi->color_format = mtk_dpi_bus_fmt_color_format(out_bus_format);
-
-	return 0;
-}
-
 static int mtk_dpi_bridge_attach(struct drm_bridge *bridge,
 				 struct drm_encoder *encoder,
 				 enum drm_bridge_attach_flags flags)
@@ -827,15 +370,6 @@ static int mtk_dpi_bridge_attach(struct drm_bridge *bridge,
 				 &dpi->bridge, flags);
 }
 
-static void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge,
-				const struct drm_display_mode *mode,
-				const struct drm_display_mode *adjusted_mode)
-{
-	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
-
-	drm_mode_copy(&dpi->mode, adjusted_mode);
-}
-
 static void mtk_dpi_bridge_disable(struct drm_bridge *bridge)
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
@@ -895,39 +429,7 @@ static int mtk_dpi_debug_tp_show(struct seq_file *m, void *arg)
 	en = val & DPI_PAT_EN;
 	val = FIELD_GET(DPI_PAT_SEL, val);
 
-	seq_printf(m, "DPI Test Pattern: %s\n", en ? "Enabled" : "Disabled");
-
-	if (en) {
-		seq_printf(m, "Internal pattern %d: ", val);
-		switch (val) {
-		case 0:
-			seq_puts(m, "256 Vertical Gray\n");
-			break;
-		case 1:
-			seq_puts(m, "1024 Vertical Gray\n");
-			break;
-		case 2:
-			seq_puts(m, "256 Horizontal Gray\n");
-			break;
-		case 3:
-			seq_puts(m, "1024 Horizontal Gray\n");
-			break;
-		case 4:
-			seq_puts(m, "Vertical Color bars\n");
-			break;
-		case 6:
-			seq_puts(m, "Frame border\n");
-			break;
-		case 7:
-			seq_puts(m, "Dot moire\n");
-			break;
-		default:
-			seq_puts(m, "Invalid selection\n");
-			break;
-		}
-	}
-
-	return 0;
+	return mtk_dpi_common_debug_tp_show(m, en, val);
 }
 
 static ssize_t mtk_dpi_debug_tp_write(struct file *file, const char __user *ubuf,
@@ -935,23 +437,15 @@ static ssize_t mtk_dpi_debug_tp_write(struct file *file, const char __user *ubuf
 {
 	struct seq_file *m = file->private_data;
 	u32 en, type;
-	char buf[6];
-
-	if (!m || !m->private || *offp || len > sizeof(buf) - 1)
-		return -EINVAL;
-
-	memset(buf, 0, sizeof(buf));
-	if (copy_from_user(buf, ubuf, len))
-		return -EFAULT;
+	ssize_t ret;
 
-	if (sscanf(buf, "%u %u", &en, &type) != 2)
-		return -EINVAL;
-
-	if (en < 0 || en > 1 || type < 0 || type > 7)
-		return -EINVAL;
+	/* seq_file and dpi pointers are checked by mtk_dpi_common_debug_tp_write() */
+	ret = mtk_dpi_common_debug_tp_write(file, ubuf, len, offp, &en, &type);
+	if (ret < 0)
+		return ret;
 
 	mtk_dpi_test_pattern_en((struct mtk_dpi *)m->private, type, en);
-	return len;
+	return ret;
 }
 
 static int mtk_dpi_debug_tp_open(struct inode *inode, struct file *file)
@@ -1020,56 +514,9 @@ unsigned int mtk_dpi_encoder_index(struct device *dev)
 	return encoder_index;
 }
 
-static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
-{
-	struct mtk_dpi *dpi = dev_get_drvdata(dev);
-	struct drm_device *drm_dev = data;
-	struct mtk_drm_private *priv = drm_dev->dev_private;
-	int ret;
-
-	dpi->mmsys_dev = priv->mmsys_dev;
-	ret = drm_simple_encoder_init(drm_dev, &dpi->encoder,
-				      DRM_MODE_ENCODER_TMDS);
-	if (ret) {
-		dev_err(dev, "Failed to initialize decoder: %d\n", ret);
-		return ret;
-	}
-
-	ret = mtk_find_possible_crtcs(drm_dev, dpi->dev);
-	if (ret < 0)
-		goto err_cleanup;
-	dpi->encoder.possible_crtcs = ret;
-
-	ret = drm_bridge_attach(&dpi->encoder, &dpi->bridge, NULL,
-				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
-	if (ret)
-		goto err_cleanup;
-
-	dpi->connector = drm_bridge_connector_init(drm_dev, &dpi->encoder);
-	if (IS_ERR(dpi->connector)) {
-		dev_err(dev, "Unable to create bridge connector\n");
-		ret = PTR_ERR(dpi->connector);
-		goto err_cleanup;
-	}
-
-	return 0;
-
-err_cleanup:
-	drm_encoder_cleanup(&dpi->encoder);
-	return ret;
-}
-
-static void mtk_dpi_unbind(struct device *dev, struct device *master,
-			   void *data)
-{
-	struct mtk_dpi *dpi = dev_get_drvdata(dev);
-
-	drm_encoder_cleanup(&dpi->encoder);
-}
-
 static const struct component_ops mtk_dpi_component_ops = {
-	.bind = mtk_dpi_bind,
-	.unbind = mtk_dpi_unbind,
+	.bind = mtk_dpi_common_bind,
+	.unbind = mtk_dpi_common_unbind,
 };
 
 static const u32 mt8173_output_fmts[] = {
@@ -1249,84 +696,16 @@ static const struct mtk_dpi_conf mt8195_dpintf_conf = {
 
 static int mtk_dpi_probe(struct platform_device *pdev)
 {
-	struct device *dev = &pdev->dev;
 	struct mtk_dpi *dpi;
 	int ret;
 
-	dpi = devm_drm_bridge_alloc(dev, struct mtk_dpi, bridge,
-				    &mtk_dpi_bridge_funcs);
+	dpi = mtk_dpi_common_probe(pdev, &mtk_dpi_bridge_funcs);
 	if (IS_ERR(dpi))
 		return PTR_ERR(dpi);
 
-	dpi->dev = dev;
-	dpi->conf = (struct mtk_dpi_conf *)of_device_get_match_data(dev);
-	dpi->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
-
-	dpi->pinctrl = devm_pinctrl_get(&pdev->dev);
-	if (IS_ERR(dpi->pinctrl)) {
-		dpi->pinctrl = NULL;
-		dev_dbg(&pdev->dev, "Cannot find pinctrl!\n");
-	}
-	if (dpi->pinctrl) {
-		dpi->pins_gpio = pinctrl_lookup_state(dpi->pinctrl, "sleep");
-		if (IS_ERR(dpi->pins_gpio)) {
-			dpi->pins_gpio = NULL;
-			dev_dbg(&pdev->dev, "Cannot find pinctrl idle!\n");
-		}
-		if (dpi->pins_gpio)
-			pinctrl_select_state(dpi->pinctrl, dpi->pins_gpio);
-
-		dpi->pins_dpi = pinctrl_lookup_state(dpi->pinctrl, "default");
-		if (IS_ERR(dpi->pins_dpi)) {
-			dpi->pins_dpi = NULL;
-			dev_dbg(&pdev->dev, "Cannot find pinctrl active!\n");
-		}
-	}
-	dpi->regs = devm_platform_ioremap_resource(pdev, 0);
-	if (IS_ERR(dpi->regs))
-		return dev_err_probe(dev, PTR_ERR(dpi->regs),
-				     "Failed to ioremap mem resource\n");
-
-	dpi->engine_clk = devm_clk_get(dev, "engine");
-	if (IS_ERR(dpi->engine_clk))
-		return dev_err_probe(dev, PTR_ERR(dpi->engine_clk),
-				     "Failed to get engine clock\n");
-
-	dpi->pixel_clk = devm_clk_get(dev, "pixel");
-	if (IS_ERR(dpi->pixel_clk))
-		return dev_err_probe(dev, PTR_ERR(dpi->pixel_clk),
-				     "Failed to get pixel clock\n");
-
-	dpi->tvd_clk = devm_clk_get(dev, "pll");
-	if (IS_ERR(dpi->tvd_clk))
-		return dev_err_probe(dev, PTR_ERR(dpi->tvd_clk),
-				     "Failed to get tvdpll clock\n");
-
-	dpi->irq = platform_get_irq(pdev, 0);
-	if (dpi->irq < 0)
-		return dpi->irq;
-
-	dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 1, -1);
-	if (IS_ERR(dpi->next_bridge) && PTR_ERR(dpi->next_bridge) == -ENODEV) {
-		/* Old devicetree has only one endpoint */
-		dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 0, 0);
-	}
-	if (IS_ERR(dpi->next_bridge))
-		return dev_err_probe(dpi->dev, PTR_ERR(dpi->next_bridge),
-				     "Failed to get bridge\n");
-
-	platform_set_drvdata(pdev, dpi);
-
-	dpi->bridge.of_node = dev->of_node;
-	dpi->bridge.type = DRM_MODE_CONNECTOR_DPI;
-
-	ret = devm_drm_bridge_add(dev, &dpi->bridge);
-	if (ret)
-		return ret;
-
-	ret = component_add(dev, &mtk_dpi_component_ops);
+	ret = component_add(&pdev->dev, &mtk_dpi_component_ops);
 	if (ret)
-		return dev_err_probe(dev, ret, "Failed to add component.\n");
+		return dev_err_probe(&pdev->dev, ret, "Failed to add component.\n");
 
 	return 0;
 }
@@ -1357,3 +736,4 @@ struct platform_driver mtk_dpi_driver = {
 		.of_match_table = mtk_dpi_of_ids,
 	},
 };
+MODULE_IMPORT_NS("DRM_MTK_DPI");
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_common.c b/drivers/gpu/drm/mediatek/mtk_dpi_common.c
new file mode 100644
index 000000000000..b9e7b053d278
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_common.c
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "mtk_ddp_comp.h"
+#include "mtk_dpi_common.h"
+#include "mtk_drm_drv.h"
+
+void mtk_dpi_power_off(struct mtk_dpi *dpi)
+{
+	if (WARN_ON(dpi->refcount == 0))
+		return;
+
+	if (--dpi->refcount != 0)
+		return;
+
+	clk_disable_unprepare(dpi->pixel_clk);
+	clk_disable_unprepare(dpi->tvd_clk);
+	clk_disable_unprepare(dpi->engine_clk);
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_power_off, "DRM_MTK_DPI");
+
+int mtk_dpi_power_on(struct mtk_dpi *dpi)
+{
+	int ret;
+
+	if (++dpi->refcount != 1)
+		return 0;
+
+	ret = clk_prepare_enable(dpi->engine_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable engine clock: %d\n", ret);
+		goto err_refcount;
+	}
+
+	ret = clk_prepare_enable(dpi->tvd_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable tvd pll: %d\n", ret);
+		goto err_engine;
+	}
+
+	ret = clk_prepare_enable(dpi->pixel_clk);
+	if (ret) {
+		dev_err(dpi->dev, "Failed to enable pixel clock: %d\n", ret);
+		goto err_pixel;
+	}
+
+	return 0;
+
+err_pixel:
+	clk_disable_unprepare(dpi->tvd_clk);
+err_engine:
+	clk_disable_unprepare(dpi->engine_clk);
+err_refcount:
+	dpi->refcount--;
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_power_on, "DRM_MTK_DPI");
+
+static unsigned int mtk_dpi_calculate_factor(struct mtk_dpi *dpi, int mode_clk)
+{
+	const struct mtk_dpi_factor *dpi_factor = dpi->conf->dpi_factor;
+	int i;
+
+	for (i = 0; i < dpi->conf->num_dpi_factor; i++) {
+		if (mode_clk <= dpi_factor[i].clock)
+			return dpi_factor[i].factor;
+	}
+
+	/* If no match try the lowest possible factor */
+	return dpi_factor[dpi->conf->num_dpi_factor - 1].factor;
+}
+
+static void mtk_dpi_set_pixel_clk(struct mtk_dpi *dpi, struct videomode *vm, int mode_clk)
+{
+	unsigned long pll_rate;
+	unsigned int factor;
+
+	/* let pll_rate can fix the valid range of tvdpll (1G~2GHz) */
+	factor = mtk_dpi_calculate_factor(dpi, mode_clk);
+	pll_rate = vm->pixelclock * factor;
+
+	dev_dbg(dpi->dev, "Want PLL %lu Hz, pixel clock %lu Hz\n",
+		pll_rate, vm->pixelclock);
+
+	clk_set_rate(dpi->tvd_clk, pll_rate);
+	pll_rate = clk_get_rate(dpi->tvd_clk);
+
+	/*
+	 * Depending on the IP version, we may output a different amount of
+	 * pixels for each iteration: divide the clock by this number.
+	 */
+	vm->pixelclock = pll_rate / factor;
+	vm->pixelclock /= dpi->conf->pixels_per_iter;
+
+	if ((dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_LE) ||
+	    (dpi->output_fmt == MEDIA_BUS_FMT_RGB888_2X12_BE))
+		clk_set_rate(dpi->pixel_clk, vm->pixelclock * 2);
+	else
+		clk_set_rate(dpi->pixel_clk, vm->pixelclock);
+
+	vm->pixelclock = clk_get_rate(dpi->pixel_clk);
+
+	dev_dbg(dpi->dev, "Got PLL %lu Hz, pixel clock %lu Hz\n",
+		pll_rate, vm->pixelclock);
+}
+
+int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm,
+			     struct mtk_dpi_sync *sync,
+			     struct mtk_dpi_polarities *dpi_pol,
+			     struct mtk_dpi_yc_limit *limit)
+{
+	struct drm_display_mode *mode = &dpi->mode;
+
+	drm_display_mode_to_videomode(mode, vm);
+
+	if (!dpi->conf->clocked_by_hdmi)
+		mtk_dpi_set_pixel_clk(dpi, vm, mode->clock);
+
+	dpi_pol->ck_pol = MTK_DPI_POLARITY_FALLING;
+	dpi_pol->de_pol = MTK_DPI_POLARITY_RISING;
+	dpi_pol->hsync_pol = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
+			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
+	dpi_pol->vsync_pol = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
+			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
+
+	/*
+	 * Depending on the IP version, we may output a different amount of
+	 * pixels for each iteration: adjust the display porches accordingly.
+	 */
+	sync->hsync.sync_width = vm->hsync_len / dpi->conf->pixels_per_iter;
+	sync->hsync.back_porch = vm->hback_porch / dpi->conf->pixels_per_iter;
+	sync->hsync.front_porch = vm->hfront_porch / dpi->conf->pixels_per_iter;
+	sync->hsync.shift_half_line = false;
+
+	sync->vsync_l_odd.sync_width = vm->vsync_len;
+	sync->vsync_l_odd.back_porch = vm->vback_porch;
+	sync->vsync_l_odd.front_porch = vm->vfront_porch;
+	sync->vsync_l_odd.shift_half_line = false;
+
+	if (vm->flags & DISPLAY_FLAGS_INTERLACED) {
+		sync->vsync_l_even = sync->vsync_l_odd;
+		sync->vsync_l_even.shift_half_line = true;
+
+		if (mode->flags & DRM_MODE_FLAG_3D_MASK) {
+			sync->vsync_r_odd = sync->vsync_l_odd;
+			sync->vsync_r_even = sync->vsync_l_odd;
+			sync->vsync_r_even.shift_half_line = true;
+		}
+	} else if (mode->flags & DRM_MODE_FLAG_3D_MASK) {
+		sync->vsync_r_odd = sync->vsync_l_odd;
+	}
+
+	if (drm_default_rgb_quant_range(&dpi->mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
+		limit->y_bottom = 0x10;
+		limit->y_top = 0xfe0;
+		limit->c_bottom = 0x10;
+		limit->c_top = 0xfe0;
+	} else {
+		limit->y_bottom = 0;
+		limit->y_top = 0xfff;
+		limit->c_bottom = 0;
+		limit->c_top = 0xfff;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_set_display_mode, "DRM_MTK_DPI");
+
+u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+					      struct drm_bridge_state *bridge_state,
+					      struct drm_crtc_state *crtc_state,
+					      struct drm_connector_state *conn_state,
+					      u32 output_fmt,
+					      unsigned int *num_input_fmts)
+{
+	u32 *input_fmts;
+
+	*num_input_fmts = 0;
+
+	input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	*num_input_fmts = 1;
+	input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+
+	return input_fmts;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_atomic_get_input_bus_fmts, "DRM_MTK_DPI");
+
+u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+					       struct drm_bridge_state *bridge_state,
+					       struct drm_crtc_state *crtc_state,
+					       struct drm_connector_state *conn_state,
+					       unsigned int *num_output_fmts)
+{
+	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+	u32 *output_fmts;
+
+	*num_output_fmts = 0;
+
+	if (!dpi->conf->output_fmts) {
+		dev_err(dpi->dev, "output_fmts should not be null\n");
+		return NULL;
+	}
+
+	output_fmts = kcalloc(dpi->conf->num_output_fmts, sizeof(*output_fmts), GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+
+	*num_output_fmts = dpi->conf->num_output_fmts;
+
+	memcpy(output_fmts, dpi->conf->output_fmts,
+	       sizeof(*output_fmts) * dpi->conf->num_output_fmts);
+
+	return output_fmts;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_atomic_get_output_bus_fmts, "DRM_MTK_DPI");
+
+int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
+				struct drm_bridge_state *bridge_state,
+				struct drm_crtc_state *crtc_state,
+				struct drm_connector_state *conn_state)
+{
+	unsigned int out_bus_format = bridge_state->output_bus_cfg.format;
+	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+
+	if (out_bus_format == MEDIA_BUS_FMT_FIXED && dpi->conf->num_output_fmts)
+		out_bus_format = dpi->conf->output_fmts[0];
+
+	dev_dbg(dpi->dev, "input format 0x%04x, output format 0x%04x\n",
+		bridge_state->input_bus_cfg.format,
+		bridge_state->output_bus_cfg.format);
+
+	dpi->output_fmt = out_bus_format;
+	dpi->bit_num = mtk_dpi_bus_fmt_bit_num(out_bus_format);
+	dpi->channel_swap = mtk_dpi_bus_fmt_channel_swap(out_bus_format);
+	dpi->yc_map = MTK_DPI_OUT_YC_MAP_RGB;
+	dpi->color_format = mtk_dpi_bus_fmt_color_format(out_bus_format);
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_atomic_check, "DRM_MTK_DPI");
+
+void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge,
+			     const struct drm_display_mode *mode,
+			     const struct drm_display_mode *adjusted_mode)
+{
+	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+
+	drm_mode_copy(&dpi->mode, adjusted_mode);
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_bridge_mode_set, "DRM_MTK_DPI");
+
+int mtk_dpi_common_bind(struct device *dev, struct device *master, void *data)
+{
+	struct mtk_dpi *dpi = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct mtk_drm_private *priv = drm_dev->dev_private;
+	int ret;
+
+	dpi->mmsys_dev = priv->mmsys_dev;
+	ret = drm_simple_encoder_init(drm_dev, &dpi->encoder,
+				      DRM_MODE_ENCODER_TMDS);
+	if (ret) {
+		dev_err(dev, "Failed to initialize decoder: %d\n", ret);
+		return ret;
+	}
+
+	ret = mtk_find_possible_crtcs(drm_dev, dpi->dev);
+	if (ret < 0)
+		goto err_cleanup;
+	dpi->encoder.possible_crtcs = ret;
+
+	ret = drm_bridge_attach(&dpi->encoder, &dpi->bridge, NULL,
+				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+	if (ret)
+		goto err_cleanup;
+
+	dpi->connector = drm_bridge_connector_init(drm_dev, &dpi->encoder);
+	if (IS_ERR(dpi->connector)) {
+		dev_err(dev, "Unable to create bridge connector\n");
+		ret = PTR_ERR(dpi->connector);
+		goto err_cleanup;
+	}
+
+	return 0;
+
+err_cleanup:
+	drm_encoder_cleanup(&dpi->encoder);
+	return ret;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_bind, "DRM_MTK_DPI");
+
+void mtk_dpi_common_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct mtk_dpi *dpi = dev_get_drvdata(dev);
+
+	drm_encoder_cleanup(&dpi->encoder);
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_unbind, "DRM_MTK_DPI");
+
+int mtk_dpi_common_debug_tp_show(struct seq_file *m, bool en, u32 sel)
+{
+	seq_printf(m, "DPI Test Pattern: %s\n", en ? "Enabled" : "Disabled");
+
+	if (en) {
+		seq_printf(m, "Internal pattern %d: ", sel);
+
+		switch (sel) {
+		case 0:
+			seq_puts(m, "256 Vertical Gray\n");
+			break;
+		case 1:
+			seq_puts(m, "1024 Vertical Gray\n");
+			break;
+		case 2:
+			seq_puts(m, "256 Horizontal Gray\n");
+			break;
+		case 3:
+			seq_puts(m, "1024 Horizontal Gray\n");
+			break;
+		case 4:
+			seq_puts(m, "Vertical Color bars\n");
+			break;
+		case 6:
+			seq_puts(m, "Frame border\n");
+			break;
+		case 7:
+			seq_puts(m, "Dot moire\n");
+			break;
+		default:
+			seq_puts(m, "Invalid selection\n");
+			break;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_debug_tp_show, "DRM_MTK_DPI");
+
+ssize_t mtk_dpi_common_debug_tp_write(struct file *file, const char __user *ubuf,
+				      size_t len, loff_t *offp, u32 *en, u32 *type)
+{
+	struct seq_file *m = file->private_data;
+	char buf[6];
+
+	if (!m || !m->private || *offp || len > sizeof(buf) - 1)
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+	if (copy_from_user(buf, ubuf, len))
+		return -EFAULT;
+
+	if (sscanf(buf, "%u %u", en, type) != 2)
+		return -EINVAL;
+
+	if (*en < 0 || *en > 1 || *type < 0 || *type > 7)
+		return -EINVAL;
+
+	return len;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_debug_tp_write, "DRM_MTK_DPI");
+
+struct mtk_dpi *mtk_dpi_common_probe(struct platform_device *pdev,
+				     const struct drm_bridge_funcs *bridge_funcs)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_dpi *dpi;
+	int ret;
+
+	dpi = devm_drm_bridge_alloc(dev, struct mtk_dpi, bridge, bridge_funcs);
+	if (IS_ERR(dpi))
+		return dpi;
+
+	dpi->dev = dev;
+	dpi->conf = (struct mtk_dpi_conf *)of_device_get_match_data(dev);
+	dpi->output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+	dpi->pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR_OR_NULL(dpi->pinctrl)) {
+		dev_dbg(dev, "Cannot find pinctrl!\n");
+		dpi->pinctrl = NULL;
+	} else {
+		dpi->pins_gpio = pinctrl_lookup_state(dpi->pinctrl, "sleep");
+		if (IS_ERR(dpi->pins_gpio)) {
+			dpi->pins_gpio = NULL;
+			dev_dbg(dev, "Cannot find pinctrl idle!\n");
+		}
+		if (dpi->pins_gpio)
+			pinctrl_select_state(dpi->pinctrl, dpi->pins_gpio);
+
+		dpi->pins_dpi = pinctrl_lookup_state(dpi->pinctrl, "default");
+		if (IS_ERR(dpi->pins_dpi)) {
+			dpi->pins_dpi = NULL;
+			dev_dbg(dev, "Cannot find pinctrl active!\n");
+		}
+	}
+
+	dpi->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(dpi->regs))
+		return dev_err_ptr_probe(dev, PTR_ERR(dpi->regs),
+					 "Failed to ioremap mem resource\n");
+
+	dpi->engine_clk = devm_clk_get(dev, "engine");
+	if (IS_ERR(dpi->engine_clk))
+		return dev_err_ptr_probe(dev, PTR_ERR(dpi->engine_clk),
+					 "Failed to get engine clock\n");
+
+	dpi->pixel_clk = devm_clk_get(dev, "pixel");
+	if (IS_ERR(dpi->pixel_clk))
+		return dev_err_ptr_probe(dev, PTR_ERR(dpi->pixel_clk),
+					 "Failed to get pixel clock\n");
+
+	dpi->tvd_clk = devm_clk_get(dev, "pll");
+	if (IS_ERR(dpi->tvd_clk))
+		return dev_err_ptr_probe(dev, PTR_ERR(dpi->tvd_clk),
+					 "Failed to get tvdpll clock\n");
+
+	dpi->irq = platform_get_irq(pdev, 0);
+	if (dpi->irq < 0)
+		return ERR_PTR(dpi->irq);
+
+	dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 1, -1);
+	if (IS_ERR(dpi->next_bridge) && PTR_ERR(dpi->next_bridge) == -ENODEV) {
+		/* Old devicetree has only one endpoint */
+		dpi->next_bridge = devm_drm_of_get_bridge(dpi->dev, dpi->dev->of_node, 0, 0);
+	}
+	if (IS_ERR(dpi->next_bridge))
+		return dev_err_ptr_probe(dpi->dev, PTR_ERR(dpi->next_bridge),
+					 "Failed to get bridge\n");
+
+	platform_set_drvdata(pdev, dpi);
+
+	dpi->bridge.of_node = dev->of_node;
+	dpi->bridge.type = DRM_MODE_CONNECTOR_DPI;
+
+	ret = devm_drm_bridge_add(dev, &dpi->bridge);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return dpi;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_dpi_common_probe, "DRM_MTK_DPI");
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_DESCRIPTION("MediaTek DPI/DVO Common Library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_common.h b/drivers/gpu/drm/mediatek/mtk_dpi_common.h
new file mode 100644
index 000000000000..c5cc26c7900d
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_common.h
@@ -0,0 +1,276 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *               AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#ifndef _MTK_DPI_COMMON_H
+#define _MTK_DPI_COMMON_H
+
+#include <linux/clk.h>
+#include <linux/pinctrl/consumer.h>
+#include <uapi/linux/media-bus-format.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modes.h>
+#include <video/videomode.h>
+
+enum mtk_dpi_out_bit_num {
+	MTK_DPI_OUT_BIT_NUM_8BITS,
+	MTK_DPI_OUT_BIT_NUM_10BITS,
+	MTK_DPI_OUT_BIT_NUM_12BITS,
+	MTK_DPI_OUT_BIT_NUM_16BITS
+};
+
+enum mtk_dpi_out_yc_map {
+	MTK_DPI_OUT_YC_MAP_RGB,
+	MTK_DPI_OUT_YC_MAP_CYCY,
+	MTK_DPI_OUT_YC_MAP_YCYC,
+	MTK_DPI_OUT_YC_MAP_CY,
+	MTK_DPI_OUT_YC_MAP_YC
+};
+
+enum mtk_dpi_out_channel_swap {
+	MTK_DPI_OUT_CHANNEL_SWAP_RGB,
+	MTK_DPI_OUT_CHANNEL_SWAP_GBR,
+	MTK_DPI_OUT_CHANNEL_SWAP_BRG,
+	MTK_DPI_OUT_CHANNEL_SWAP_RBG,
+	MTK_DPI_OUT_CHANNEL_SWAP_GRB,
+	MTK_DPI_OUT_CHANNEL_SWAP_BGR
+};
+
+enum mtk_dpi_out_color_format {
+	MTK_DPI_COLOR_FORMAT_RGB,
+	MTK_DPI_COLOR_FORMAT_YCBCR_422,
+	MTK_DPI_COLOR_FORMAT_YCBCR_444
+};
+
+struct mtk_dpi {
+	struct drm_encoder encoder;
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct drm_connector *connector;
+	void __iomem *regs;
+	struct device *dev;
+	struct device *mmsys_dev;
+	struct clk *engine_clk;
+	struct clk *pixel_clk;
+	struct clk *tvd_clk;
+	int irq;
+	struct drm_display_mode mode;
+	const struct mtk_dpi_conf *conf;
+	enum mtk_dpi_out_color_format color_format;
+	enum mtk_dpi_out_yc_map yc_map;
+	enum mtk_dpi_out_bit_num bit_num;
+	enum mtk_dpi_out_channel_swap channel_swap;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pins_gpio;
+	struct pinctrl_state *pins_dpi;
+	u32 output_fmt;
+	s8 refcount;
+};
+
+enum mtk_dpi_polarity {
+	MTK_DPI_POLARITY_RISING,
+	MTK_DPI_POLARITY_FALLING,
+};
+
+struct mtk_dpi_polarities {
+	enum mtk_dpi_polarity de_pol;
+	enum mtk_dpi_polarity ck_pol;
+	enum mtk_dpi_polarity hsync_pol;
+	enum mtk_dpi_polarity vsync_pol;
+};
+
+struct mtk_dpi_sync_param {
+	u32 sync_width;
+	u32 front_porch;
+	u32 back_porch;
+	bool shift_half_line;
+};
+
+struct mtk_dpi_sync {
+	struct mtk_dpi_sync_param hsync;
+	struct mtk_dpi_sync_param vsync_l_odd;
+	struct mtk_dpi_sync_param vsync_l_even;
+	struct mtk_dpi_sync_param vsync_r_odd;
+	struct mtk_dpi_sync_param vsync_r_even;
+};
+
+struct mtk_dpi_yc_limit {
+	u16 y_top;
+	u16 y_bottom;
+	u16 c_top;
+	u16 c_bottom;
+};
+
+struct mtk_dpi_factor {
+	u32 clock;
+	u8 factor;
+};
+
+/**
+ * struct mtk_dpi_conf - Configuration of MediaTek DPI/DVO
+ * @dpi_factor: SoC-specific pixel clock PLL factor values.
+ * @num_dpi_factor: Number of pixel clock PLL factor values.
+ * @reg_h_fre_con: Register address of frequency control.
+ * @max_clock_khz: Max clock frequency supported for this SoCs in khz units.
+ * @edge_sel_en: Enable of edge selection.
+ * @output_fmts: Array of supported output formats.
+ * @num_output_fmts: Quantity of supported output formats.
+ * @is_ck_de_pol: Support CK/DE polarity.
+ * @swap_input_support: Support input swap function.
+ * @support_direct_pin: IP supports direct connection to dpi panels.
+ * @dimension_mask: Mask used for HWIDTH, HPORCH, VSYNC_WIDTH and VSYNC_PORCH
+ *		    (no shift).
+ * @hvsize_mask: Mask of HSIZE and VSIZE mask (no shift).
+ * @channel_swap_shift: Shift value of channel swap.
+ * @yuv422_en_bit: Enable bit of yuv422.
+ * @csc_enable_bit: Enable bit of CSC.
+ * @input_2p_en_bit: Enable bit for input two pixel per round feature.
+ *		     If present, implies that the feature must be enabled.
+ * @pixels_per_iter: Quantity of transferred pixels per iteration.
+ * @edge_cfg_in_mmsys: If the edge configuration for DPI's output needs to be set in MMSYS.
+ * @clocked_by_hdmi: HDMI IP outputs clock to dpi_pixel_clk input clock, needed
+ *		     for DPI registers access.
+ * @output_1pixel: Enable outputting one pixel per round; if the input is two pixel per
+ *                 round, the DPI hardware will internally transform it to 1T1P.
+ */
+struct mtk_dpi_conf {
+	const struct mtk_dpi_factor *dpi_factor;
+	const u8 num_dpi_factor;
+	u32 reg_h_fre_con;
+	u32 max_clock_khz;
+	bool edge_sel_en;
+	const u32 *output_fmts;
+	u32 num_output_fmts;
+	bool is_ck_de_pol;
+	bool swap_input_support;
+	bool support_direct_pin;
+	u32 dimension_mask;
+	u32 hvsize_mask;
+	u32 channel_swap_shift;
+	u32 yuv422_en_bit;
+	u32 csc_enable_bit;
+	u32 input_2p_en_bit;
+	u32 pixels_per_iter;
+	bool edge_cfg_in_mmsys;
+	bool clocked_by_hdmi;
+	bool output_1pixel;
+};
+
+static inline struct mtk_dpi *bridge_to_dpi(struct drm_bridge *b)
+{
+	return container_of(b, struct mtk_dpi, bridge);
+}
+
+static inline void mtk_dpi_mask(struct mtk_dpi *dpi, u32 offset, u32 val, u32 mask)
+{
+	u32 tmp = readl(dpi->regs + offset);
+
+	tmp &= ~mask;
+	tmp |= (val & mask);
+	writel(tmp, dpi->regs + offset);
+}
+
+static inline enum mtk_dpi_out_bit_num
+mtk_dpi_bus_fmt_bit_num(unsigned int out_bus_format)
+{
+	switch (out_bus_format) {
+	default:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_RGB888_2X12_LE:
+	case MEDIA_BUS_FMT_RGB888_2X12_BE:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+		return MTK_DPI_OUT_BIT_NUM_8BITS;
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		return MTK_DPI_OUT_BIT_NUM_10BITS;
+	case MEDIA_BUS_FMT_YUYV12_1X24:
+		return MTK_DPI_OUT_BIT_NUM_12BITS;
+	}
+}
+
+static inline enum mtk_dpi_out_channel_swap
+mtk_dpi_bus_fmt_channel_swap(unsigned int out_bus_format)
+{
+	switch (out_bus_format) {
+	default:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB888_2X12_LE:
+	case MEDIA_BUS_FMT_RGB888_2X12_BE:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_YUYV12_1X24:
+		return MTK_DPI_OUT_CHANNEL_SWAP_RGB;
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		return MTK_DPI_OUT_CHANNEL_SWAP_BGR;
+	}
+}
+
+static inline enum mtk_dpi_out_color_format
+mtk_dpi_bus_fmt_color_format(unsigned int out_bus_format)
+{
+	switch (out_bus_format) {
+	default:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_BGR888_1X24:
+	case MEDIA_BUS_FMT_RGB888_2X12_LE:
+	case MEDIA_BUS_FMT_RGB888_2X12_BE:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+		return MTK_DPI_COLOR_FORMAT_RGB;
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YUYV10_1X20:
+	case MEDIA_BUS_FMT_YUYV12_1X24:
+		return MTK_DPI_COLOR_FORMAT_YCBCR_422;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		return MTK_DPI_COLOR_FORMAT_YCBCR_444;
+	}
+}
+
+u32 *mtk_dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+					      struct drm_bridge_state *bridge_state,
+					      struct drm_crtc_state *crtc_state,
+					      struct drm_connector_state *conn_state,
+					      u32 output_fmt,
+					      unsigned int *num_input_fmts);
+u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+					       struct drm_bridge_state *bridge_state,
+					       struct drm_crtc_state *crtc_state,
+					       struct drm_connector_state *conn_state,
+					       unsigned int *num_output_fmts);
+int mtk_dpi_bridge_atomic_check(struct drm_bridge *bridge,
+				struct drm_bridge_state *bridge_state,
+				struct drm_crtc_state *crtc_state,
+				struct drm_connector_state *conn_state);
+void mtk_dpi_bridge_mode_set(struct drm_bridge *bridge,
+			     const struct drm_display_mode *mode,
+			     const struct drm_display_mode *adjusted_mode);
+
+int mtk_dpi_common_bind(struct device *dev, struct device *master, void *data);
+void mtk_dpi_common_unbind(struct device *dev, struct device *master, void *data);
+
+void mtk_dpi_power_off(struct mtk_dpi *dpi);
+int mtk_dpi_power_on(struct mtk_dpi *dpi);
+int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm,
+			     struct mtk_dpi_sync *sync,
+			     struct mtk_dpi_polarities *dpi_pol,
+			     struct mtk_dpi_yc_limit *limit);
+
+int mtk_dpi_common_debug_tp_show(struct seq_file *m, bool en, u32 sel);
+ssize_t mtk_dpi_common_debug_tp_write(struct file *file, const char __user *ubuf,
+				      size_t len, loff_t *offp, u32 *en, u32 *type);
+
+struct mtk_dpi *mtk_dpi_common_probe(struct platform_device *pdev,
+				     const struct drm_bridge_funcs *bridge_funcs);
+
+#endif /* _MTK_DPI_COMMON_H */
-- 
2.54.0



^ permalink raw reply related

* [PATCH 21/42] drm/mediatek: mtk_dpi: Fully separate HW setup from common code
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

As a preparation step for adding support for DVO and creating a
mtk_dpi_common library, fully separate all of the register writing
mechanisms from commonizable hardware setup functions.

While at it, also perform some spare code style cleanups and also
remove the mtk_dpi_config_vsync_{l,r}{odd,even}() functions and
open-code the single line found in each of those, as now all of
the parameters are clearly labeled and won't have any meaningful
impact on human readability.

This change brings no functional differences.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_dpi.c | 172 ++++++++++++++---------------
 1 file changed, 86 insertions(+), 86 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index ecd13ea782c2..077e8a42a044 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -246,33 +246,6 @@ static void mtk_dpi_config_vsync(struct mtk_dpi *dpi,
 		     dpi->conf->dimension_mask << VSYNC_FRONT_PORCH_SHIFT);
 }
 
-static void mtk_dpi_config_vsync_lodd(struct mtk_dpi *dpi,
-				      struct mtk_dpi_sync_param *sync)
-{
-	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH, DPI_TGEN_VPORCH);
-}
-
-static void mtk_dpi_config_vsync_leven(struct mtk_dpi *dpi,
-				       struct mtk_dpi_sync_param *sync)
-{
-	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_LEVEN,
-			     DPI_TGEN_VPORCH_LEVEN);
-}
-
-static void mtk_dpi_config_vsync_rodd(struct mtk_dpi *dpi,
-				      struct mtk_dpi_sync_param *sync)
-{
-	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_RODD,
-			     DPI_TGEN_VPORCH_RODD);
-}
-
-static void mtk_dpi_config_vsync_reven(struct mtk_dpi *dpi,
-				       struct mtk_dpi_sync_param *sync)
-{
-	mtk_dpi_config_vsync(dpi, sync, DPI_TGEN_VWIDTH_REVEN,
-			     DPI_TGEN_VPORCH_REVEN);
-}
-
 static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
 			       struct mtk_dpi_polarities *dpi_pol)
 {
@@ -311,30 +284,15 @@ static void mtk_dpi_config_fb_size(struct mtk_dpi *dpi, u32 width, u32 height)
 		     dpi->conf->hvsize_mask << VSIZE);
 }
 
-static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi)
+static void mtk_dpi_config_channel_limit(struct mtk_dpi *dpi, struct mtk_dpi_yc_limit *limit)
 {
-	struct mtk_dpi_yc_limit limit;
-
-	if (drm_default_rgb_quant_range(&dpi->mode) ==
-	    HDMI_QUANTIZATION_RANGE_LIMITED) {
-		limit.y_bottom = 0x10;
-		limit.y_top = 0xfe0;
-		limit.c_bottom = 0x10;
-		limit.c_top = 0xfe0;
-	} else {
-		limit.y_bottom = 0;
-		limit.y_top = 0xfff;
-		limit.c_bottom = 0;
-		limit.c_top = 0xfff;
-	}
-
-	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit.y_bottom << Y_LIMINT_BOT,
+	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
 		     Y_LIMINT_BOT_MASK);
-	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit.y_top << Y_LIMINT_TOP,
+	mtk_dpi_mask(dpi, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
 		     Y_LIMINT_TOP_MASK);
-	mtk_dpi_mask(dpi, DPI_C_LIMIT, limit.c_bottom << C_LIMIT_BOT,
+	mtk_dpi_mask(dpi, DPI_C_LIMIT, limit->c_bottom << C_LIMIT_BOT,
 		     C_LIMIT_BOT_MASK);
-	mtk_dpi_mask(dpi, DPI_C_LIMIT, limit.c_top << C_LIMIT_TOP,
+	mtk_dpi_mask(dpi, DPI_C_LIMIT, limit->c_top << C_LIMIT_TOP,
 		     C_LIMIT_TOP_MASK);
 }
 
@@ -516,7 +474,6 @@ static void mtk_dpi_power_off(struct mtk_dpi *dpi)
 	if (--dpi->refcount != 0)
 		return;
 
-	mtk_dpi_disable(dpi);
 	clk_disable_unprepare(dpi->pixel_clk);
 	clk_disable_unprepare(dpi->tvd_clk);
 	clk_disable_unprepare(dpi->engine_clk);
@@ -607,40 +564,40 @@ static void mtk_dpi_set_pixel_clk(struct mtk_dpi *dpi, struct videomode *vm, int
 		pll_rate, vm->pixelclock);
 }
 
-static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
+static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm,
 				    struct mtk_dpi_sync *sync,
-				    struct drm_display_mode *mode)
+				    struct mtk_dpi_polarities *dpi_pol,
+				    struct mtk_dpi_yc_limit *limit)
 {
-	struct mtk_dpi_polarities dpi_pol;
-	struct videomode vm = { 0 };
+	struct drm_display_mode *mode = &dpi->mode;
 
-	drm_display_mode_to_videomode(mode, &vm);
+	drm_display_mode_to_videomode(mode, vm);
 
 	if (!dpi->conf->clocked_by_hdmi)
-		mtk_dpi_set_pixel_clk(dpi, &vm, mode->clock);
+		mtk_dpi_set_pixel_clk(dpi, vm, mode->clock);
 
-	dpi_pol.ck_pol = MTK_DPI_POLARITY_FALLING;
-	dpi_pol.de_pol = MTK_DPI_POLARITY_RISING;
-	dpi_pol.hsync_pol = vm.flags & DISPLAY_FLAGS_HSYNC_HIGH ?
+	dpi_pol->ck_pol = MTK_DPI_POLARITY_FALLING;
+	dpi_pol->de_pol = MTK_DPI_POLARITY_RISING;
+	dpi_pol->hsync_pol = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
-	dpi_pol.vsync_pol = vm.flags & DISPLAY_FLAGS_VSYNC_HIGH ?
+	dpi_pol->vsync_pol = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
 			    MTK_DPI_POLARITY_FALLING : MTK_DPI_POLARITY_RISING;
 
 	/*
 	 * Depending on the IP version, we may output a different amount of
 	 * pixels for each iteration: adjust the display porches accordingly.
 	 */
-	sync->hsync.sync_width = vm.hsync_len / dpi->conf->pixels_per_iter;
-	sync->hsync.back_porch = vm.hback_porch / dpi->conf->pixels_per_iter;
-	sync->hsync.front_porch = vm.hfront_porch / dpi->conf->pixels_per_iter;
+	sync->hsync.sync_width = vm->hsync_len / dpi->conf->pixels_per_iter;
+	sync->hsync.back_porch = vm->hback_porch / dpi->conf->pixels_per_iter;
+	sync->hsync.front_porch = vm->hfront_porch / dpi->conf->pixels_per_iter;
 	sync->hsync.shift_half_line = false;
 
-	sync->vsync_l_odd.sync_width = vm.vsync_len;
-	sync->vsync_l_odd.back_porch = vm.vback_porch;
-	sync->vsync_l_odd.front_porch = vm.vfront_porch;
+	sync->vsync_l_odd.sync_width = vm->vsync_len;
+	sync->vsync_l_odd.back_porch = vm->vback_porch;
+	sync->vsync_l_odd.front_porch = vm->vfront_porch;
 	sync->vsync_l_odd.shift_half_line = false;
 
-	if (vm.flags & DISPLAY_FLAGS_INTERLACED) {
+	if (vm->flags & DISPLAY_FLAGS_INTERLACED) {
 		sync->vsync_l_even = sync->vsync_l_odd;
 		sync->vsync_l_even.shift_half_line = true;
 
@@ -653,27 +610,55 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 		sync->vsync_r_odd = sync->vsync_l_odd;
 	}
 
+	if (drm_default_rgb_quant_range(&dpi->mode) == HDMI_QUANTIZATION_RANGE_LIMITED) {
+		limit->y_bottom = 0x10;
+		limit->y_top = 0xfe0;
+		limit->c_bottom = 0x10;
+		limit->c_top = 0xfe0;
+	} else {
+		limit->y_bottom = 0;
+		limit->y_top = 0xfff;
+		limit->c_bottom = 0;
+		limit->c_top = 0xfff;
+	}
+
+	return 0;
+}
+
+static void mtk_dpi_config_hw(struct mtk_dpi *dpi,
+			      struct videomode *vm, struct mtk_dpi_sync *sync,
+			      struct mtk_dpi_polarities *dpi_pol,
+			      struct mtk_dpi_yc_limit *limit)
+{
+	struct drm_display_mode *mode = &dpi->mode;
+	u32 vactive = vm->vactive;
+
 	mtk_dpi_sw_reset(dpi, true);
-	mtk_dpi_config_pol(dpi, &dpi_pol);
+	mtk_dpi_config_pol(dpi, dpi_pol);
 
 	mtk_dpi_config_hsync(dpi, &sync->hsync);
-	mtk_dpi_config_vsync_lodd(dpi, &sync->vsync_l_odd);
-	mtk_dpi_config_vsync_rodd(dpi, &sync->vsync_r_odd);
-	mtk_dpi_config_vsync_leven(dpi, &sync->vsync_l_even);
-	mtk_dpi_config_vsync_reven(dpi, &sync->vsync_r_even);
+
+	mtk_dpi_config_vsync(dpi, &sync->vsync_l_odd,
+			     DPI_TGEN_VWIDTH, DPI_TGEN_VPORCH);
+	mtk_dpi_config_vsync(dpi, &sync->vsync_r_odd,
+			     DPI_TGEN_VWIDTH_RODD, DPI_TGEN_VPORCH_RODD);
+	mtk_dpi_config_vsync(dpi, &sync->vsync_l_even,
+			     DPI_TGEN_VWIDTH_LEVEN, DPI_TGEN_VPORCH_LEVEN);
+	mtk_dpi_config_vsync(dpi, &sync->vsync_r_even,
+			     DPI_TGEN_VWIDTH_REVEN, DPI_TGEN_VPORCH_REVEN);
 
 	mtk_dpi_config_3d(dpi, !!(mode->flags & DRM_MODE_FLAG_3D_MASK));
-	mtk_dpi_config_interface(dpi, !!(vm.flags &
-					 DISPLAY_FLAGS_INTERLACED));
-	if (vm.flags & DISPLAY_FLAGS_INTERLACED)
-		mtk_dpi_config_fb_size(dpi, vm.hactive, vm.vactive >> 1);
-	else
-		mtk_dpi_config_fb_size(dpi, vm.hactive, vm.vactive);
+	mtk_dpi_config_interface(dpi, !!(vm->flags & DISPLAY_FLAGS_INTERLACED));
+
+	if (vm->flags & DISPLAY_FLAGS_INTERLACED)
+		vactive >>= 1;
 
-	mtk_dpi_config_channel_limit(dpi);
+	mtk_dpi_config_fb_size(dpi, vm->hactive, vactive);
+	mtk_dpi_config_channel_limit(dpi, limit);
 	mtk_dpi_config_bit_num(dpi, dpi->bit_num);
 	mtk_dpi_config_channel_swap(dpi, dpi->channel_swap);
 	mtk_dpi_config_color_format(dpi, dpi->color_format);
+
 	if (dpi->conf->support_direct_pin) {
 		mtk_dpi_config_yc_map(dpi, dpi->yc_map);
 		mtk_dpi_config_2n_h_fre(dpi);
@@ -686,13 +671,13 @@ static int mtk_dpi_set_display_mode(struct mtk_dpi *dpi,
 
 		mtk_dpi_config_disable_edge(dpi);
 	}
-	if (dpi->conf->input_2p_en_bit) {
+
+	if (dpi->conf->input_2p_en_bit)
 		mtk_dpi_mask(dpi, DPI_CON, dpi->conf->input_2p_en_bit,
 			     dpi->conf->input_2p_en_bit);
-	}
-	mtk_dpi_sw_reset(dpi, false);
 
-	return 0;
+	mtk_dpi_sw_reset(dpi, false);
+	return;
 }
 
 static u32 *mtk_dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
@@ -855,6 +840,7 @@ static void mtk_dpi_bridge_disable(struct drm_bridge *bridge)
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
 
+	mtk_dpi_disable(dpi);
 	mtk_dpi_power_off(dpi);
 
 	if (dpi->pinctrl && dpi->pins_gpio)
@@ -864,13 +850,22 @@ static void mtk_dpi_bridge_disable(struct drm_bridge *bridge)
 static void mtk_dpi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+	struct mtk_dpi_polarities dpi_pol;
 	struct mtk_dpi_sync sync = { 0 };
+	struct mtk_dpi_yc_limit limit;
+	struct videomode vm;
 
 	if (dpi->pinctrl && dpi->pins_dpi)
 		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
 
 	mtk_dpi_power_on(dpi);
-	mtk_dpi_set_display_mode(dpi, &sync, &dpi->mode);
+
+	/* Set pixel clock and initialize parameters to send to the HW */
+	mtk_dpi_set_display_mode(dpi, &vm, &sync, &dpi_pol, &limit);
+
+	/* Format and send the parameters to the HW */
+	mtk_dpi_config_hw(dpi, &vm, &sync, &dpi_pol, &limit);
+
 	mtk_dpi_enable(dpi);
 }
 
@@ -999,16 +994,21 @@ void mtk_dpi_start(struct device *dev)
 {
 	struct mtk_dpi *dpi = dev_get_drvdata(dev);
 
-	if (!dpi->conf->clocked_by_hdmi)
-		mtk_dpi_power_on(dpi);
+	if (dpi->conf->clocked_by_hdmi)
+		return;
+
+	mtk_dpi_power_on(dpi);
 }
 
 void mtk_dpi_stop(struct device *dev)
 {
 	struct mtk_dpi *dpi = dev_get_drvdata(dev);
 
-	if (!dpi->conf->clocked_by_hdmi)
-		mtk_dpi_power_off(dpi);
+	if (dpi->conf->clocked_by_hdmi)
+		return;
+
+	mtk_dpi_disable(dpi);
+	mtk_dpi_power_off(dpi);
 }
 
 unsigned int mtk_dpi_encoder_index(struct device *dev)
-- 
2.54.0



^ permalink raw reply related

* [PATCH 33/42] drm/mediatek: Prepare path builder for multi-controller architecture
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

In preparation for adding support for the new display controllers
featuring multi-controller w/DirectLink architecture, expand the
path builder to recognize whether a hardware component in display
controller related output path is getting its input from internal
or from any external (to itself, but still internal to the SoC)
display controller component.

When an output path is found, it is also necessary to understand
the enablement order (in terms of mmsys/mutex other than PM) of
the controllers, as that cannot be done randomly: for this, the
initial path builder will start storing an indication to whether
a component is either in a leader or in a follower controller,
and then a new function mtk_drm_set_path_orders(), called when
binding all components, when initializing KMS, takes care of the
looping through the path and setting up the order between all
of the follower display controllers.

As of this point, no multi-controller SoCs are introduced yet,
so this code will not bring any functional change on any of the
currently supported SoCs / display controllers.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c   |   2 +
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h   |   2 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c    | 351 ++++++++++++++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_drv.h    |   2 +
 drivers/gpu/drm/mediatek/mtk_drm_legacy.c |   3 +-
 5 files changed, 332 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 0ecb942d6129..3f25f1c34633 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -680,6 +680,7 @@ static int mtk_ddp_comp_init_internal_comp(struct device *dev, struct device *co
 
 int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 		      struct mtk_drm_comp_list *hlist,
+		      u8 comp_controller_id,
 		      enum mtk_ddp_comp_type comp_type, int comp_inst_id)
 {
 	struct platform_device *comp_pdev;
@@ -695,6 +696,7 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 
 	comp->type = comp_type;
 	comp->inst_id = comp_inst_id;
+	comp->controller_id = comp_controller_id;
 	comp->funcs = mtk_ddp_funcs[comp_type];
 	/* Not all drm components have a DTS device node, such as ovl_adaptor,
 	 * which is the drm bring up sub driver
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index 1297db252821..e94bcf6922c1 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -87,6 +87,7 @@ struct mtk_ddp_comp {
 	enum mtk_ddp_comp_type type;
 	u8 inst_id;
 	u8 mtx_trig_id;
+	u8 controller_id;
 	int encoder_index;
 	const struct mtk_ddp_comp_funcs *funcs;
 
@@ -375,6 +376,7 @@ int mtk_ddp_comp_get_id(struct device_node *node,
 int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev);
 int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 		      struct mtk_drm_comp_list *hlist,
+		      u8 comp_controller_id,
 		      enum mtk_ddp_comp_type comp_type, int comp_inst_id);
 int mtk_ddp_comp_get_mutex_trigger(struct device_node *node, unsigned int index);
 enum mtk_ddp_comp_type mtk_ddp_comp_get_type(unsigned int comp_id);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 3ed0e3f45fe2..3bee730cd34f 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -185,7 +185,7 @@ static int mtk_drm_match(struct device *dev, const void *data)
 static bool mtk_drm_get_all_drm_priv(struct device *dev)
 {
 	struct mtk_drm_private *drm_priv = dev_get_drvdata(dev);
-	struct mtk_drm_private *all_drm_priv[MAX_CRTC];
+	struct mtk_drm_private *all_drm_priv[drm_priv->data->mmsys_dev_num];
 	struct mtk_drm_private *temp_drm_priv;
 	struct device_node *phandle = dev->parent->of_node;
 	const struct of_device_id *of_id;
@@ -194,8 +194,10 @@ static bool mtk_drm_get_all_drm_priv(struct device *dev)
 	unsigned int cnt = 0;
 	int i, j;
 
+	dev_vdbg(dev, "Populating private data for all controllers on ID%u\n",
+		 drm_priv->data->mmsys_id);
+
 	for_each_child_of_node(phandle->parent, node) {
-		const struct mtk_drm_path_definition *output_path;
 		struct platform_device *pdev;
 
 		of_id = of_match_node(mtk_drm_of_ids, node);
@@ -216,19 +218,12 @@ static bool mtk_drm_get_all_drm_priv(struct device *dev)
 		if (!temp_drm_priv)
 			continue;
 
-		for (i = 0; i < MAX_CRTC; i++) {
-			output_path = &temp_drm_priv->data->output_paths[i];
-
-			if (!output_path->len)
-				continue;
-
-			all_drm_priv[i] = temp_drm_priv;
-		}
+		all_drm_priv[temp_drm_priv->data->mmsys_id] = temp_drm_priv;
 
 		if (temp_drm_priv->mtk_drm_bound)
 			cnt++;
 
-		if (cnt == MAX_CRTC) {
+		if (cnt == drm_priv->data->mmsys_dev_num) {
 			of_node_put(node);
 			break;
 		}
@@ -272,6 +267,98 @@ static bool mtk_drm_find_mmsys_comp(struct mtk_drm_private *private,
 	return false;
 }
 
+static struct mtk_drm_private *
+mtk_drm_find_matching_controller(struct mtk_drm_private **all_drm_private,
+				 struct device_node *target_node)
+{
+	for (int i = 0; i < all_drm_private[0]->data->mmsys_dev_num; i++) {
+		struct mtk_drm_private *priv = all_drm_private[i];
+		struct device_node *cur_mmsys_node = priv->mmsys_dev->of_node;
+
+		if (target_node == cur_mmsys_node)
+			return priv;
+	}
+
+	return NULL;
+}
+
+static void mtk_drm_set_path_orders(struct mtk_drm_private **all_drm_private)
+{
+	struct mtk_drm_private *private = all_drm_private[0];
+	unsigned short i, j;
+
+	/*
+	 * If the SoC has multiple display controllers w/DirectLink architecture
+	 * at this point all controllers have been registered and it is now safe
+	 * to iterate through and setup the outputs order so that the data path
+	 * follows the correct controllers sequence.
+	 *
+	 * At the end of this, the controllers are always ordered 0..N hence the
+	 * data, for each different display controller output (as each support
+	 * multiple different outputs as well) always travels through consecutive
+	 * controller indices, like CTRLRx_OUTPUTy -> ... -> CTRLRx+n_OUTPUTy,
+	 * where:
+	 *
+	 *  - The first one (0) is responsible for getting input frames from DRM
+	 *    and dispatching to image processing HW(s) and/or next controller;
+	 *  - The last one (N) is responsible for output to a physical display
+	 *
+	 * Note that one controller may also output to a different out-number of
+	 * its consecutive, so a display controller-I/O sequence like
+	 *
+	 *    CTRLR0_OUTPUT0 -> CTRLR1_OUTPUT3 -> CTRLR3_OUTPUT2 (-> DISPLAY)
+	 *          0        ->        1       ->       2
+	 *
+	 * should also considered as being valid since the hardware is capable of
+	 * doing so, but this is a corner case that is currently not handled to
+	 * simplify the implementation.
+	 */
+	for (i = 0; i < MAX_CRTC; i++) {
+		unsigned short max_iterations = private->data->mmsys_dev_num;
+		bool order_found;
+
+		do {
+			order_found = false;
+
+			for (j = 0; j < private->data->mmsys_dev_num; j++) {
+				const struct mtk_drm_path_definition *src_path;
+				struct mtk_drm_private *cur_priv, *src_priv;
+				struct mtk_drm_path_definition *cur_path;
+				unsigned int src_order;
+
+				cur_priv = all_drm_private[j];
+				if (!cur_priv)
+					continue;
+
+				cur_path = &cur_priv->data->output_paths[i];
+
+				if (!cur_path->len || !cur_path->input_controller)
+					continue;
+
+				src_priv = mtk_drm_find_matching_controller(all_drm_private,
+								cur_path->input_controller);
+				if (!src_priv)
+					continue;
+
+				/*
+				 * Paths are per-output: set order for the new output
+				 * by finding the order of the same output number of
+				 * the previous controller and incrementing it by one
+				 */
+				src_path = &src_priv->data->output_paths[i];
+				src_order = src_path->len ? src_path->order : 0;
+				if (cur_path->order <= src_order) {
+					cur_path->order = src_order + 1;
+
+					/* Order found: check next CRTC now! */
+					order_found = true;
+					break;
+				}
+			}
+		} while (!order_found && max_iterations--);
+	}
+}
+
 static int mtk_drm_kms_init(struct drm_device *drm)
 {
 	struct mtk_drm_private *private = drm->dev_private;
@@ -316,6 +403,9 @@ static int mtk_drm_kms_init(struct drm_device *drm)
 	 */
 	drm_helper_move_panel_connectors_to_head(drm);
 
+	/* Set controllers order for multi-controller architecture */
+	mtk_drm_set_path_orders(private->all_drm_private);
+
 	/*
 	 * 1. We currently support two fixed data streams, each optional,
 	 *    and each statically assigned to a crtc:
@@ -653,18 +743,108 @@ static int mtk_drm_of_get_ddp_comp_type(struct device_node *node, enum mtk_ddp_c
 	return 0;
 }
 
+/**
+ * mtk_drm_of_get_ep_external_controller() - Get parent controller if external
+ * @dev:         Device pointer to leading display controller
+ * @ep_dev_node: OF Node pointer to a display controller sub-component hardware
+ *               The caller is responsible for dropping the refcount.
+ *
+ * Return: External Display Controller (mmsys) device_node or NULL if the given
+ *         sub-component resides in the same Display Controller as *dev.
+ */
+static struct device_node
+*mtk_drm_of_get_ep_external_controller(struct device *dev,
+				       struct device_node *ep_dev_node)
+{
+	struct device_node *leader_controller_node = dev->parent->of_node;
+	struct device_node *ep_parent_node;
+
+	ep_parent_node = of_get_parent(ep_dev_node);
+	if (ep_parent_node == leader_controller_node) {
+		of_node_put(ep_parent_node);
+		return NULL;
+	}
+
+	return ep_parent_node;
+}
+
+static int mtk_drm_of_get_first_input(struct device *dev, struct device_node *node,
+				      enum mtk_crtc_path crtc_endpoint,
+				      struct mtk_drm_comp_definition *comp_def)
+{
+	struct device_node *ep_dev_node, *ep_in;
+	enum mtk_ddp_comp_type comp_type;
+	int inst_id, ret;
+
+	ep_in = of_graph_get_endpoint_by_regs(node, 0, crtc_endpoint);
+	if (!ep_in)
+		return -ENOENT;
+
+	ep_dev_node = of_graph_get_port_parent(ep_in);
+	of_node_put(ep_in);
+	if (!ep_dev_node)
+		return -EINVAL;
+
+	/*
+	 * If the first input has no HW component specific driver go out with
+	 * -ENOENT: depending on the SoC (for arch gen2), this may be expected.
+	 */
+	ret = mtk_drm_of_get_ddp_comp_type(ep_dev_node, &comp_type);
+	of_node_put(ep_dev_node);
+	if (ret)
+		return -ENOENT;
+
+	inst_id = mtk_ddp_comp_get_id(ep_dev_node, comp_type);
+	if (inst_id < 0)
+		return inst_id;
+
+	/* All ok! Pass the Component ID to the caller. */
+	comp_def->type = comp_type;
+	comp_def->inst_id = inst_id;
+
+	dev_dbg(dev, "Found first input component %pOF with ID=%u SubID=%u\n",
+		ep_dev_node, comp_def->type, comp_def->inst_id);
+
+	return 0;
+}
+
+/**
+ * mtk_drm_of_get_ddp_ep_cid - Parse HW component connection information
+ * @dev:          The mediatek-drm device
+ * @node:         The device node of the display controller component to parse
+ * @output_port:  The number of the port, corresponding to an output, to parse
+ * @crtc_endpoint:The number of the current endpoint corresponding to CRTC
+ * @next:         Pointer to a struct device_node, used to pass the next node,
+ *                corresponding to the next component's input, to the caller
+ * @comp_def:     Pointer to the last, uninitialized, entry of the temporary
+ *                structure array holding the Display Controller Path that is
+ *                being built.
+ * @controller_arch_v2: Check if DirectLink architecture or legacy VDO/MMSYS
+ *
+ * Return:
+ * * %0        - Component connection parsed fully: the currently parsed Display
+ *               Controller hardware component is interconnected with a next one
+ * * %-ENOENT  - The component's remote endpoint was not found
+ * * %-EINVAL  - Component information is not valid, hence not usable
+ * * %-ENODEV  - The identified component is a valid connection, but its DT node
+ *               is disabled, hence not usable
+ * * %-EREMOTE - The component is interconnected with a next one residing in a
+ *               different Display Controller, remote to the current one, hence
+ *               cannot be added to the path of the current controller
+ */
 static int mtk_drm_of_get_ddp_ep_cid(struct device *dev, struct device_node *node,
-				     int output_port, enum mtk_crtc_path crtc_path,
+				     int output_port, enum mtk_crtc_path crtc_endpoint,
 				     struct device_node **next,
-				     struct mtk_drm_comp_definition *comp_def)
+				     struct mtk_drm_comp_definition *comp_def,
+				     bool controller_arch_v2)
 {
 	struct device_node *ep_dev_node, *ep_out;
 	enum mtk_ddp_comp_type comp_type;
 	int ret;
 
-	ep_out = of_graph_get_endpoint_by_regs(node, output_port, crtc_path);
+	ep_out = of_graph_get_endpoint_by_regs(node, output_port, crtc_endpoint);
 	if (!ep_out)
-		return -ENOENT;
+		return -EINVAL;
 
 	ep_dev_node = of_graph_get_remote_port_parent(ep_out);
 	of_node_put(ep_out);
@@ -678,6 +858,21 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device *dev, struct device_node *nod
 	 */
 	*next = ep_dev_node;
 
+	if (controller_arch_v2) {
+		struct device_node *rmt_ctrlr_node;
+
+		rmt_ctrlr_node = mtk_drm_of_get_ep_external_controller(dev, ep_dev_node);
+		if (rmt_ctrlr_node) {
+			/* The device is from a different mmsys (remote from this one) */
+			dev_dbg(dev, "Found connection to external mmsys %pOF\n",
+				rmt_ctrlr_node);
+
+			of_node_put(ep_dev_node);
+			of_node_put(rmt_ctrlr_node);
+			return -EREMOTE;
+		}
+	}
+
 	if (!of_device_is_available(ep_dev_node))
 		return -ENODEV;
 
@@ -708,9 +903,13 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device *dev, struct device_node *nod
 
 /**
  * mtk_drm_of_ddp_path_build_one - Build a Display HW Pipeline for a CRTC Path
- * @dev:          The mediatek-drm device
- * @cpath:        CRTC Path relative to a VDO or MMSYS
+ * @dev:          The mediatek-drm device, corresponding to leading controller
+ *                instance of the current display HW pipeline
+ * @node:         The device node containing the first port/endpoint
+ * @cpath:        CRTC Path relative to a VDO or MMSYS, also used as
+ *                number of the initial endpoint for this CRTC path
  * @out_path:     Pointer to the structure that will contain the new pipeline
+ * @controller_arch_v2: Check if DirectLink architecture or legacy VDO/MMSYS
  *
  * MediaTek SoCs can use different DDP hardware pipelines (or paths) depending
  * on the board-specific desired display configuration; this function walks
@@ -723,18 +922,62 @@ static int mtk_drm_of_get_ddp_ep_cid(struct device *dev, struct device_node *nod
  * * %-EINVAL - Display pipeline built but validation failed
  * * %-ENOMEM - Failure to allocate pipeline array to pass to the caller
  */
-static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path cpath,
-					 struct mtk_drm_path_definition *out_path)
+static int mtk_drm_of_ddp_path_build_one(struct device *dev, struct device_node *node,
+					 enum mtk_crtc_path cpath,
+					 struct mtk_drm_path_definition *out_path,
+					 bool controller_arch_v2)
 {
 	struct mtk_drm_comp_definition temp_path[MTK_DISP_CONTROLLER_MAX_COMP_PER_PATH];
-	struct device_node *next = NULL, *prev, *vdo = dev->parent->of_node;
+	struct device_node *next = NULL, *prev;
 	bool ovl_adaptor_comp_added = false;
 	unsigned short int idx = 0;
 	size_t final_comp_sz;
+	u8 temp_order;
 	int ret;
 
-	/* Get the first entry for the temp_path array */
-	ret = mtk_drm_of_get_ddp_ep_cid(dev, vdo, 0, cpath, &next, &temp_path[idx]);
+	dev_vdbg(dev, "Building DDP Path for CRTC%d\n", cpath);
+
+	if (controller_arch_v2) {
+		/* Check if the starting input is already a usable component */
+		ret = mtk_drm_of_get_first_input(dev, node, cpath, &temp_path[idx]);
+		if (ret == 0) {
+			idx++;
+		} else if (ret != -ENOENT) {
+			dev_err(dev, "Cannot parse first input HW component: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/*
+	 * Get the first remote for the temp_path array: for leader controllers
+	 * this will be an output, while for follower controllers this will be
+	 * an input from a DirectLink connection coming from either a leader or
+	 * a follower display controller.
+	 *
+	 * In case this is an input from any remote (leader/follower) controller
+	 * this will return EREMOTE and a different port (expressing a relay of
+	 * a crossbar) will be used to continue building the path.
+	 */
+	ret = mtk_drm_of_get_ddp_ep_cid(dev, node, 0, cpath, &next,
+					&temp_path[idx], controller_arch_v2);
+	if (ret == -EREMOTE) {
+		out_path->input_controller = mtk_drm_of_get_ep_external_controller(dev, next);
+
+		/*
+		 * Any follower controller gets the order set to 1 to avoid
+		 * iterating once again later when the actual full ordering
+		 * is calculated.
+		 */
+		temp_order = 1;
+		dev_dbg(dev, "Got external mmsys %pOF\n", out_path->input_controller);
+
+		ret = mtk_drm_of_get_ddp_ep_cid(dev, node, 2, cpath, &next,
+						&temp_path[idx], controller_arch_v2);
+	} else {
+		/* A leader controller gets, of course, its order set to 0 */
+		temp_order = 0;
+	}
+
 	if (ret) {
 		if (next && temp_path[idx].type == MTK_DISP_OVL_ADAPTOR) {
 			dev_dbg(dev, "Adding OVL Adaptor for %pOF\n", next);
@@ -743,7 +986,9 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 			if (next)
 				dev_err(dev, "Invalid component %pOF\n", next);
 			else
-				dev_err(dev, "Cannot find first endpoint for path %d\n", cpath);
+				dev_err(dev,
+					"Cannot find first endpoint for path %d on %pOF\n",
+					cpath, node);
 
 			return ret;
 		}
@@ -756,7 +1001,11 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 	 */
 	do {
 		prev = next;
-		ret = mtk_drm_of_get_ddp_ep_cid(dev, next, 1, cpath, &next, &temp_path[idx]);
+		ret = mtk_drm_of_get_ddp_ep_cid(dev, next, 1, cpath, &next,
+						&temp_path[idx], controller_arch_v2);
+		if (ret == -EREMOTE)
+			ret = mtk_drm_of_get_ddp_ep_cid(dev, prev, 3, cpath, &next,
+							&temp_path[idx], controller_arch_v2);
 		of_node_put(prev);
 		if (ret) {
 			dev_vdbg(dev, "Invalid comp reached with result %d\n", ret);
@@ -806,11 +1055,27 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 	if (!out_path->comp)
 		return -ENOMEM;
 
+	/*
+	 * Anything that is not the primary controller gets order set to 1:
+	 * this is done to avoid iterating once again later when the actual
+	 * full controllers ordering is calculated.
+	 */
+	out_path->order = temp_order;
+
 	dev_dbg(dev, "Display HW Pipeline built with %d components.\n", idx);
 
 	return 0;
 }
 
+static bool mtk_drm_of_ddp_is_arch_v2(struct device_node *cur_mmsys_node)
+{
+	for_each_child_of_node_scoped(cur_mmsys_node, mmsys_child)
+		if (of_property_present(mmsys_child, "compatible"))
+			return true;
+
+	return false;
+}
+
 static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *node,
 				     struct mtk_mmsys_driver_data *data)
 {
@@ -819,8 +1084,15 @@ static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *nod
 	struct of_endpoint of_ep;
 	bool output_present[MAX_CRTC] = { false };
 	u8 num_outputs_present = 0;
+	u8 num_outputs_skipped = 0;
+	bool controller_arch_v2;
 	int i, ret;
 
+	controller_arch_v2 = mtk_drm_of_ddp_is_arch_v2(dev->parent->of_node);
+
+	dev_dbg(dev, "Building Display Controller v%d Path starting from %pOF\n",
+		controller_arch_v2 ? 2 : 1, node);
+
 	for_each_endpoint_of_node(node, ep_node) {
 		ret = of_graph_parse_endpoint(ep_node, &of_ep);
 		if (ret) {
@@ -854,10 +1126,34 @@ static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *nod
 		if (!output_present[i])
 			continue;
 
-		ret = mtk_drm_of_ddp_path_build_one(dev, i, &output_paths[i]);
-		if (ret && ret != -ENODEV)
-			return ret;
+		ret = mtk_drm_of_ddp_path_build_one(dev, node, i, &output_paths[i],
+						    controller_arch_v2);
+		/*
+		 * For -ENODEV, this could mean that the device is not yet registered,
+		 * but that may be just because of a probe deferral, so it is possible
+		 * to continue building the path as such failures are properly handled
+		 * later when enabling outputs.
+		 * For -ENOENT, it means that the devicetree declares a partial output
+		 * which is - of course - not okay, but failing entirely is a bit too
+		 * much: do a print to advertise invalid outputs, but fail probing
+		 * only if there is no valid output at all.
+		 */
+		if (ret && ret != -ENODEV) {
+			if (ret == -ENOENT) {
+				dev_dbg(dev, "Skipping invalid output for CRTC%u\n", i);
+				num_outputs_skipped++;
+			} else {
+				dev_err(dev, "Pipeline build failure on CRTC%u\n", i);
+				return ret;
+			}
+		}
+	}
+
+	if (num_outputs_skipped == num_outputs_present) {
+		dev_err(dev, "No valid display output found!\n");
+		return -ENOENT;
 	}
+
 	data->output_paths = output_paths;
 
 	return 0;
@@ -968,6 +1264,7 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		}
 
 		ret = mtk_ddp_comp_init(dev, node, &private->hlist,
+					private->data->mmsys_id,
 					comp_type, comp_inst_id);
 		if (ret) {
 			of_node_put(node);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index 3f84384f8569..3c403bc8f4fe 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -39,7 +39,9 @@ struct mtk_drm_comp_definition {
 
 struct mtk_drm_path_definition {
 	const struct mtk_drm_comp_definition *comp;
+	struct device_node *input_controller;
 	u8 len;
+	u8 order;
 };
 
 struct mtk_mmsys_driver_data {
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_legacy.c b/drivers/gpu/drm/mediatek/mtk_drm_legacy.c
index 801e0ab43ff5..85bc432568f0 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_legacy.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_legacy.c
@@ -873,6 +873,7 @@ void mtk_drm_legacy_ovl_adaptor_probe(struct device *dev, struct mtk_drm_private
 						    PLATFORM_DEVID_AUTO,
 						    (void *)priv, sizeof(*priv));
 
-	mtk_ddp_comp_init(&ovl_adaptor->dev, NULL, &priv->hlist, MTK_DISP_OVL_ADAPTOR, 0);
+	mtk_ddp_comp_init(&ovl_adaptor->dev, NULL, &priv->hlist, priv->data->mmsys_id,
+			  MTK_DISP_OVL_ADAPTOR, 0);
 	component_match_add(dev, match, component_compare_dev, &ovl_adaptor->dev);
 }
-- 
2.54.0



^ permalink raw reply related

* [PATCH 24/42] drm/mediatek: Add support for MediaTek Digital Video Output (DVO)
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Add a new driver for the Digital Video Output (DVO) hardware found
in the MT8189, MT8196 Kompanio SoCs and their Dimensity and Genio
variants.

This version currently supports output to Embedded DisplayPort on
MT8189 and MT8196, and to DisplayPort on MT8189.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/Makefile         |   1 +
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c   |   8 +
 drivers/gpu/drm/mediatek/mtk_disp_drv.h   |   4 +
 drivers/gpu/drm/mediatek/mtk_dpi_common.c |   5 +
 drivers/gpu/drm/mediatek/mtk_dpi_common.h |  23 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c    |  11 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h    |   1 +
 drivers/gpu/drm/mediatek/mtk_dvo.c        | 682 ++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_dvo_regs.h   | 192 ++++++
 include/linux/soc/mediatek/mtk-mmsys.h    |   1 +
 10 files changed, 928 insertions(+)
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dvo.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_dvo_regs.h

diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 34a1d4721b70..c48ac3ab08cc 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -17,6 +17,7 @@ mediatek-drm-y := mtk_crtc.o \
 		  mtk_dsi.o \
 		  mtk_dpi_common.o \
 		  mtk_dpi.o \
+		  mtk_dvo.o \
 		  mtk_ethdr.o \
 		  mtk_mdp_rdma.o \
 		  mtk_padding.o \
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 3be891f740d3..f8687f161953 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -290,6 +290,12 @@ static const struct mtk_ddp_comp_funcs ddp_dsi = {
 	.encoder_index = mtk_dsi_encoder_index,
 };
 
+static const struct mtk_ddp_comp_funcs ddp_dvo = {
+	.start = mtk_dvo_start,
+	.stop = mtk_dvo_stop,
+	.encoder_index = mtk_dvo_encoder_index,
+};
+
 static const struct mtk_ddp_comp_funcs ddp_gamma = {
 	.clk_enable = mtk_gamma_clk_enable,
 	.clk_disable = mtk_gamma_clk_disable,
@@ -430,6 +436,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_DP_INTF] = "dp-intf",
 	[MTK_DISP_DPI] = "dpi",
 	[MTK_DISP_DSI] = "dsi",
+	[MTK_DISP_DVO] = "dvo",
 };
 
 static const struct mtk_ddp_comp_funcs *mtk_ddp_funcs[MTK_DDP_COMP_TYPE_MAX] = {
@@ -453,6 +460,7 @@ static const struct mtk_ddp_comp_funcs *mtk_ddp_funcs[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_DPI]			= &ddp_dpi,
 	[MTK_DISP_DP_INTF]		= &ddp_dpi,
 	[MTK_DISP_DSI]			= &ddp_dsi,
+	[MTK_DISP_DVO]			= &ddp_dvo,
 };
 
 static bool mtk_ddp_find_comp_dev_in_table(const struct mtk_drm_comp_list *hlist,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
index c4b44b761633..abdce23fab17 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h
@@ -63,6 +63,10 @@ void mtk_dsi_ddp_stop(struct device *dev);
 unsigned int mtk_dsi_encoder_index(struct device *dev);
 struct drm_dsc_config *mtk_dsi_get_dsc_config(struct device *dev);
 
+void mtk_dvo_start(struct device *dev);
+void mtk_dvo_stop(struct device *dev);
+unsigned int mtk_dvo_encoder_index(struct device *dev);
+
 int mtk_gamma_clk_enable(struct device *dev);
 void mtk_gamma_clk_disable(struct device *dev);
 void mtk_gamma_config(struct device *dev, unsigned int w,
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_common.c b/drivers/gpu/drm/mediatek/mtk_dpi_common.c
index b9e7b053d278..01bfbe4b5863 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_common.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_common.c
@@ -152,6 +152,11 @@ int mtk_dpi_set_display_mode(struct mtk_dpi *dpi, struct videomode *vm,
 	sync->hsync.front_porch = vm->hfront_porch / dpi->conf->pixels_per_iter;
 	sync->hsync.shift_half_line = false;
 
+	if (dpi->conf->quirk_hfp_bs_fix) {
+		sync->hsync.back_porch += sync->hsync.front_porch;
+		sync->hsync.front_porch = 0;
+	}
+
 	sync->vsync_l_odd.sync_width = vm->vsync_len;
 	sync->vsync_l_odd.back_porch = vm->vback_porch;
 	sync->vsync_l_odd.front_porch = vm->vfront_porch;
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi_common.h b/drivers/gpu/drm/mediatek/mtk_dpi_common.h
index c5cc26c7900d..af51efa4c9ea 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi_common.h
+++ b/drivers/gpu/drm/mediatek/mtk_dpi_common.h
@@ -17,6 +17,25 @@
 #include <drm/drm_modes.h>
 #include <video/videomode.h>
 
+enum mtk_dpi_golden_setting_level {
+	MTK_DPI_FHD_60FPS_1920 = 0,
+	MTK_DPI_FHD_60FPS_2180,
+	MTK_DPI_FHD_60FPS_2400,
+	MTK_DPI_FHD_60FPS_2520,
+	MTK_DPI_FHD_90FPS,
+	MTK_DPI_FHD_120FPS,
+	MTK_DPI_WQHD_60FPS,
+	MTK_DPI_WQHD_120FPS,
+	MTK_DPI_8K_30FPS,
+	MTK_DPI_GSL_MAX,
+};
+
+struct mtk_dpi_gs_info {
+	u32 dpi_buf_sodi_high;
+	u32 dpi_buf_sodi_low;
+};
+
+
 enum mtk_dpi_out_bit_num {
 	MTK_DPI_OUT_BIT_NUM_8BITS,
 	MTK_DPI_OUT_BIT_NUM_10BITS,
@@ -65,6 +84,7 @@ struct mtk_dpi {
 	enum mtk_dpi_out_yc_map yc_map;
 	enum mtk_dpi_out_bit_num bit_num;
 	enum mtk_dpi_out_channel_swap channel_swap;
+	enum mtk_dpi_golden_setting_level gs_level;
 	struct pinctrl *pinctrl;
 	struct pinctrl_state *pins_gpio;
 	struct pinctrl_state *pins_dpi;
@@ -137,6 +157,8 @@ struct mtk_dpi_factor {
  *		     for DPI registers access.
  * @output_1pixel: Enable outputting one pixel per round; if the input is two pixel per
  *                 round, the DPI hardware will internally transform it to 1T1P.
+ * @quirk_hfp_bs_fix: Set back porch as back+front and use front porch as an adjustment
+ *                    setting (usually zero) to fix DisplayPort BS generation.
  */
 struct mtk_dpi_conf {
 	const struct mtk_dpi_factor *dpi_factor;
@@ -159,6 +181,7 @@ struct mtk_dpi_conf {
 	bool edge_cfg_in_mmsys;
 	bool clocked_by_hdmi;
 	bool output_1pixel;
+	bool quirk_hfp_bs_fix;
 };
 
 static inline struct mtk_dpi *bridge_to_dpi(struct drm_bridge *b)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index f7cbf9b47672..ee835ebbc18c 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -621,8 +621,17 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 	  .data = (void *)MTK_DISP_DSI },
 	{ .compatible = "mediatek,mt8188-dsi",
 	  .data = (void *)MTK_DISP_DSI },
+<<<<<<< HEAD
 	{ .compatible = "mediatek,mt8196-dsi",
 	  .data = (void *)MTK_DISP_DSI },
+=======
+	{ .compatible = "mediatek,mt8189-dp-dvo",
+	  .data = (void *)MTK_DISP_DVO },
+	{ .compatible = "mediatek,mt8189-edp-dvo",
+	  .data = (void *)MTK_DISP_DVO },
+	{ .compatible = "mediatek,mt8196-edp-dvo",
+	  .data = (void *)MTK_DISP_DVO },
+>>>>>>> 60b3d568ba69 (drm/mediatek: Add support for MediaTek Digital Video Output (DVO))
 	{ }
 };
 
@@ -776,6 +785,7 @@ static int mtk_drm_of_ddp_path_build_one(struct device *dev, enum mtk_crtc_path
 	case MTK_DISP_DP_INTF:
 	case MTK_DISP_DPI:
 	case MTK_DISP_DSI:
+	case MTK_DISP_DVO:
 		break;
 	default:
 		dev_err(dev, "Invalid display hw pipeline. Last component: %u-%u (ret=%d)\n",
@@ -1059,6 +1069,7 @@ static struct platform_driver * const mtk_drm_drivers[] = {
 	&mtk_disp_rdma_driver,
 	&mtk_disp_wdma_driver,
 	&mtk_dpi_driver,
+	&mtk_dvo_driver,
 	&mtk_drm_platform_driver,
 	&mtk_dsi_driver,
 	&mtk_ethdr_driver,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index d86175180e11..4028268b3cc8 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -84,6 +84,7 @@ extern struct platform_driver mtk_disp_rdma_driver;
 extern struct platform_driver mtk_disp_wdma_driver;
 extern struct platform_driver mtk_dpi_driver;
 extern struct platform_driver mtk_dsi_driver;
+extern struct platform_driver mtk_dvo_driver;
 extern struct platform_driver mtk_ethdr_driver;
 extern struct platform_driver mtk_mdp_rdma_driver;
 extern struct platform_driver mtk_padding_driver;
diff --git a/drivers/gpu/drm/mediatek/mtk_dvo.c b/drivers/gpu/drm/mediatek/mtk_dvo.c
new file mode 100644
index 000000000000..a373db355234
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dvo.c
@@ -0,0 +1,682 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MediaTek Digital Video Out
+ *
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/debugfs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/soc/mediatek/mtk-mmsys.h>
+#include <linux/types.h>
+
+#include <video/videomode.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "mtk_drm_drv.h"
+#include "mtk_crtc.h"
+#include "mtk_disp_drv.h"
+#include "mtk_dpi_common.h"
+#include "mtk_dvo_regs.h"
+
+#define MTK_DVO_OUT_PIXITER_1T1P		0
+#define MTK_DVO_OUT_PIXITER_1T2P		1
+#define MTK_DVO_OUT_PIXITER_1T4P		2
+
+/* DVO INPUT default value is 1T2P */
+#define MTK_DVO_INPUT_MODE			2
+/* DVO OUTPUT value is 1T4P*/
+#define MTK_DVO_OUTPUT_MODE			4
+/* 1 unit = 2 group data = 2 * 1T4P = 8P */
+#define MTK_DISP_BUF_SRAM_UNIT_SIZE		8
+#define MTK_DISP_LINE_BUF_DVO_US		40
+#define MTK_BLANKING_RATIO			1
+#define MTK_DVO_EDP_MAX_CLK			297
+#define MTK_DVO_MAX_HACTIVE			3840
+#define MTK_DVO_LINE_BUFFER_SIZE		(MTK_DVO_MAX_HACTIVE / MTK_DISP_BUF_SRAM_UNIT_SIZE)
+#define MTK_DVO_BITS_PER_CYCLE			(8 * 32 * 1000000)
+#define TWAIT_SLEEP				1
+#define TWAKE_UP				5
+#define PREULTRA_HIGH_US			26
+#define PREULTRA_LOW_US				25
+#define ULTRA_HIGH_US				25
+#define ULTRA_LOW_US				23
+#define URGENT_HIGH_US				12
+#define URGENT_LOW_US				11
+
+static struct mtk_dpi_gs_info mtk_dvo_gs[MTK_DPI_GSL_MAX] = {
+	[MTK_DPI_FHD_60FPS_1920] = { 6880, 511 },
+	[MTK_DPI_8K_30FPS] = { 5255, 3899 },
+};
+
+static void mtk_dvo_test_pattern_en(struct mtk_dpi *dpi, u8 type, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = FIELD_PREP(PRE_PAT_SEL_MASK, type) | PRE_PAT_EN | PRE_PAT_FORCE_ON;
+	else
+		val = 0;
+
+	mtk_dpi_mask(dpi, DVO_PATTERN_CTRL, val, PRE_PAT_SEL_MASK | PRE_PAT_FORCE_ON | PRE_PAT_EN);
+}
+
+static void mtk_dvo_sw_reset(struct mtk_dpi *dvo, bool reset)
+{
+	mtk_dpi_mask(dvo, DVO_RET, reset ? SWRST : 0, SWRST);
+}
+
+static void mtk_dvo_enable(struct mtk_dpi *dvo)
+{
+	mtk_dpi_mask(dvo, DVO_EN, EN, EN);
+}
+
+static void mtk_dvo_disable(struct mtk_dpi *dvo)
+{
+	mtk_dpi_mask(dvo, DVO_EN, 0, EN);
+}
+
+static void mtk_dvo_irq_enable(struct mtk_dpi *dvo)
+{
+	mtk_dpi_mask(dvo, DVO_INTEN, INT_VDE_END_EN | UNDERFLOW_EN,
+		     INT_VDE_END_EN | UNDERFLOW_EN);
+}
+
+static void mtk_dvo_info_queue_start(struct mtk_dpi *dvo)
+{
+	mtk_dpi_mask(dvo, DVO_TGEN_INFOQ_LATENCY, 0,
+		     INFOQ_START_LATENCY_MASK | INFOQ_END_LATENCY_MASK);
+}
+
+static void mtk_dpi_buffer_ctrl(struct mtk_dpi *dvo)
+{
+	mtk_dpi_mask(dvo, DVO_BUF_CON0, DISP_BUF_EN, DISP_BUF_EN);
+	mtk_dpi_mask(dvo, DVO_BUF_CON0, FIFO_UNDERFLOW_DONE_BLOCK, FIFO_UNDERFLOW_DONE_BLOCK);
+}
+
+static void mtk_dvo_trailing_blank_setting(struct mtk_dpi *dvo)
+{
+	mtk_dpi_mask(dvo, DVO_TGEN_V_LAST_TRAILING_BLANK, 0x20, V_LAST_TRAILING_BLANK_MASK);
+	mtk_dpi_mask(dvo, DVO_TGEN_OUTPUT_DELAY_LINE, 0x20, EXT_TG_DLY_LINE_MASK);
+}
+
+static void mtk_dvo_get_gs_level(struct mtk_dpi *dvo)
+{
+	struct drm_display_mode *mode = &dvo->mode;
+	enum mtk_dpi_golden_setting_level *gsl = &dvo->gs_level;
+
+	if (mode->hdisplay <= 1920 && mode->vdisplay <= 1080)
+		*gsl = MTK_DPI_FHD_60FPS_1920;
+	else
+		*gsl = MTK_DPI_8K_30FPS;
+}
+
+static void mtk_dvo_sodi_setting(struct mtk_dpi *dvo, struct drm_display_mode *mode)
+{
+	const u64 sodi_total = MTK_DVO_BITS_PER_CYCLE * MTK_DISP_BUF_SRAM_UNIT_SIZE * 32ULL;
+	u64 preultra_high, preultra_low, ultra_high, ultra_low, urgent_high, urgent_low;
+	u64 sodi_high, sodi_low, sodi_high_rem, sodi_low_rem, tmp;
+	u64 dvo_fifo_size, fifo_size, total_bit;
+	u64 consume_rate, consume_rate_rem;
+	u64 fill_rate_rem, fill_rate;
+	u64 data_rate;
+	u32 mmsys_clk;
+
+	mmsys_clk = mode->clock / 1000;
+	if (!mmsys_clk) {
+		dev_err(dvo->dev, "mmclk is zero, use default value\n");
+		mmsys_clk = 273;
+	}
+
+	fill_rate = div64_u64_rem(mmsys_clk * MTK_DVO_INPUT_MODE * 30,
+		    		  32ULL * MTK_DISP_BUF_SRAM_UNIT_SIZE, &fill_rate_rem);
+	data_rate = (u64)mode->hdisplay * mode->vdisplay *
+		    div64_u64((u64)mode->clock * 1000, ((u64)mode->htotal * mode->vtotal));
+	consume_rate = div64_u64_rem(((data_rate * 30 * 5) / 4),
+				       MTK_DVO_BITS_PER_CYCLE, &consume_rate_rem);
+	total_bit = (u64)MTK_DVO_EDP_MAX_CLK * MTK_DVO_OUTPUT_MODE * 30 *
+		     MTK_DISP_LINE_BUF_DVO_US;
+
+	fifo_size = total_bit / (MTK_DISP_BUF_SRAM_UNIT_SIZE * 30);
+
+	/* DVO supports MSO mode, so calculate with three line buffers */
+	dvo_fifo_size = fifo_size + (3 * MTK_DVO_LINE_BUFFER_SIZE);
+
+	/* 1 is to round up */
+	sodi_high_rem = (u64)fill_rate_rem * MTK_DVO_BITS_PER_CYCLE;
+	tmp = (u64)consume_rate_rem * (32 * MTK_DISP_BUF_SRAM_UNIT_SIZE);
+
+	if (sodi_high_rem < tmp) {
+		fill_rate -= 1;
+		sodi_high_rem += sodi_total;
+	}
+	sodi_high_rem -= tmp;
+	sodi_high_rem *= 32 * 6;
+	sodi_high_rem /= sodi_total;
+
+	sodi_high = ((dvo_fifo_size * 30 * 5) -
+		    (32 * 6 * (fill_rate - consume_rate)) -
+		    sodi_high_rem + (5 * 32 - 1)) / (5 * 32);
+
+	sodi_low_rem = consume_rate_rem * (ULTRA_LOW_US + TWAKE_UP) + MTK_DVO_BITS_PER_CYCLE - 1;
+	sodi_low_rem /= MTK_DVO_BITS_PER_CYCLE;
+
+	sodi_low = consume_rate * (ULTRA_LOW_US + TWAKE_UP) + sodi_low_rem;
+
+	preultra_high = consume_rate * PREULTRA_HIGH_US;
+	preultra_high += (consume_rate_rem * PREULTRA_HIGH_US + MTK_DVO_BITS_PER_CYCLE - 1)
+			 / MTK_DVO_BITS_PER_CYCLE;
+			
+	preultra_low = consume_rate * PREULTRA_LOW_US;
+	preultra_low += (consume_rate_rem * PREULTRA_LOW_US + MTK_DVO_BITS_PER_CYCLE - 1)
+			/ MTK_DVO_BITS_PER_CYCLE;
+
+	ultra_high = consume_rate * ULTRA_HIGH_US;
+	ultra_high += (consume_rate_rem * ULTRA_HIGH_US + MTK_DVO_BITS_PER_CYCLE - 1)
+		      / MTK_DVO_BITS_PER_CYCLE;
+
+	ultra_low = consume_rate * ULTRA_LOW_US;
+	ultra_low += (consume_rate_rem * ULTRA_LOW_US + MTK_DVO_BITS_PER_CYCLE - 1)
+		     / MTK_DVO_BITS_PER_CYCLE;
+
+	urgent_high = consume_rate * URGENT_HIGH_US;
+	urgent_high += (consume_rate_rem * URGENT_HIGH_US + MTK_DVO_BITS_PER_CYCLE - 1)
+		       / MTK_DVO_BITS_PER_CYCLE;
+
+	urgent_low = consume_rate * URGENT_LOW_US;
+	urgent_low += (consume_rate_rem * URGENT_LOW_US + MTK_DVO_BITS_PER_CYCLE - 1)
+		      / MTK_DVO_BITS_PER_CYCLE;
+
+	mtk_dpi_mask(dvo, DVO_BUF_SODI_HIGHT, sodi_high, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_SODI_LOW, sodi_low, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_PREULTRA_HIGHT, preultra_high, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_PREULTRA_LOW, preultra_low, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_ULTRA_HIGHT, ultra_high, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_ULTRA_LOW, ultra_low, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_URGENT_HIGHT, urgent_high, DVO_DISP_BUF_MASK);
+	mtk_dpi_mask(dvo, DVO_BUF_URGENT_LOW, urgent_low, DVO_DISP_BUF_MASK);
+}
+
+static void mtk_dvo_golden_setting(struct mtk_dpi *dvo)
+{
+	struct mtk_dpi_gs_info *gs_info = NULL;
+
+	if (dvo->gs_level >= MTK_DPI_GSL_MAX) {
+		dev_err(dvo->dev, "Invalid Golden Setting level %d\n", dvo->gs_level);
+		return;
+	}
+
+	gs_info = &mtk_dvo_gs[dvo->gs_level];
+
+	mtk_dpi_mask(dvo, DVO_BUF_SODI_HIGHT, gs_info->dpi_buf_sodi_high, GENMASK(31, 0));
+	mtk_dpi_mask(dvo, DVO_BUF_SODI_LOW, gs_info->dpi_buf_sodi_low, GENMASK(31, 0));
+}
+
+static void mtk_dvo_config_hsync(struct mtk_dpi *dvo,
+				 struct mtk_dpi_sync_param *sync)
+{
+	mtk_dpi_mask(dvo, DVO_TGEN_H0, sync->sync_width << HSYNC,
+		     dvo->conf->dimension_mask << HSYNC);
+	mtk_dpi_mask(dvo, DVO_TGEN_H0, sync->front_porch << HFP,
+		     dvo->conf->dimension_mask << HFP);
+	mtk_dpi_mask(dvo, DVO_TGEN_H1, (sync->back_porch + sync->sync_width) << HSYNC2ACT,
+		     dvo->conf->dimension_mask << HSYNC2ACT);
+}
+
+static void mtk_dvo_config_vsync(struct mtk_dpi *dvo,
+				 struct mtk_dpi_sync_param *sync,
+				 u32 width_addr, u32 porch_addr)
+{
+	mtk_dpi_mask(dvo, width_addr,
+		     sync->sync_width << VSYNC,
+		     dvo->conf->dimension_mask << VSYNC);
+	mtk_dpi_mask(dvo, width_addr,
+		     sync->front_porch << VFP,
+		     dvo->conf->dimension_mask << VFP);
+	mtk_dpi_mask(dvo, porch_addr,
+		     (sync->back_porch + sync->sync_width) << VSYNC2ACT,
+	     dvo->conf->dimension_mask << VSYNC2ACT);
+}
+
+static void mtk_dvo_config_interface(struct mtk_dpi *dvo, bool inter)
+{
+	mtk_dpi_mask(dvo, DVO_CON, inter ? INTL_EN : 0, INTL_EN);
+}
+
+static void mtk_dvo_config_fb_size(struct mtk_dpi *dvo, u32 width, u32 height)
+{
+	mtk_dpi_mask(dvo, DVO_SRC_SIZE, width << SRC_HSIZE,
+		     dvo->conf->hvsize_mask << SRC_HSIZE);
+	mtk_dpi_mask(dvo, DVO_SRC_SIZE, height << SRC_VSIZE,
+		     dvo->conf->hvsize_mask << SRC_VSIZE);
+
+	mtk_dpi_mask(dvo, DVO_PIC_SIZE, width << PIC_HSIZE,
+		     dvo->conf->hvsize_mask << PIC_HSIZE);
+	mtk_dpi_mask(dvo, DVO_PIC_SIZE, height << PIC_VSIZE,
+		     dvo->conf->hvsize_mask << PIC_VSIZE);
+
+	mtk_dpi_mask(dvo, DVO_TGEN_H1, DIV_ROUND_UP(width, dvo->conf->pixels_per_iter) << HACT,
+		     dvo->conf->hvsize_mask << HACT);
+	mtk_dpi_mask(dvo, DVO_TGEN_V1, height << VACT,
+		     dvo->conf->hvsize_mask << VACT);
+}
+
+static void mtk_dvo_config_channel_limit(struct mtk_dpi *dpi, struct mtk_dpi_yc_limit *limit)
+{
+	mtk_dpi_mask(dpi, DVO_Y_LIMIT, FIELD_PREP(Y_LMT_BOT, limit->y_bottom), Y_LMT_BOT);
+	mtk_dpi_mask(dpi, DVO_Y_LIMIT, FIELD_PREP(Y_LMT_TOP, limit->y_top), Y_LMT_TOP);
+	mtk_dpi_mask(dpi, DVO_C_LIMIT, FIELD_PREP(C_LMT_BOT, limit->c_bottom), C_LMT_BOT);
+	mtk_dpi_mask(dpi, DVO_C_LIMIT, FIELD_PREP(C_LMT_TOP, limit->c_top), C_LMT_TOP);
+}
+
+static void mtk_dvo_config_channel_swap(struct mtk_dpi *dpi,
+					enum mtk_dpi_out_channel_swap swap)
+{
+	u32 val;
+
+	switch (swap) {
+	case MTK_DPI_OUT_CHANNEL_SWAP_RGB:
+		val = SWAP_RGB;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_GBR:
+		val = SWAP_GBR;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_BRG:
+		val = SWAP_BRG;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_RBG:
+		val = SWAP_RBG;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_GRB:
+		val = SWAP_GRB;
+		break;
+	case MTK_DPI_OUT_CHANNEL_SWAP_BGR:
+		val = SWAP_BGR;
+		break;
+	default:
+		val = SWAP_RGB;
+		break;
+	}
+
+	mtk_dpi_mask(dpi, DVO_OUTPUT_SET, FIELD_PREP(CH_SWAP_MASK, val), CH_SWAP_MASK);
+	return;
+}
+
+static void mtk_dvo_config_csc_enable(struct mtk_dpi *dvo, bool enable)
+{
+	mtk_dpi_mask(dvo, DVO_MATRIX_SET, enable ? BIT(1) : 0, DVO_INT_MTX_SEL_MASK);
+	mtk_dpi_mask(dvo, DVO_MATRIX_SET, enable ? DVO_CSC_EN : 0, DVO_CSC_EN);
+}
+
+static void mtk_dvo_config_yuv422_enable(struct mtk_dpi *dvo, bool enable)
+{
+	mtk_dpi_mask(dvo, DVO_YUV422_SET, enable ? DVO_YUV422_EN : 0, DVO_YUV422_EN);
+	mtk_dpi_mask(dvo, DVO_YUV422_SET, enable ? DVO_CRYCB_MAP : 0, DVO_CRYCB_MAP);
+}
+
+static void mtk_dvo_set_output_pixiter(struct mtk_dpi *dvo)
+{
+	u32 val;
+
+	switch (dvo->conf->pixels_per_iter) {
+	default:
+	case 0:
+	case 1:
+		val = MTK_DVO_OUT_PIXITER_1T1P;
+		break;
+	case 2:
+		val = MTK_DVO_OUT_PIXITER_1T2P;
+		break;
+	case 3:
+	case 4:
+		val = MTK_DVO_OUT_PIXITER_1T4P;
+		break;
+	}
+
+	/* Enable register shadow only when more than 1 pixel per iteration */
+	if (val > MTK_DVO_OUT_PIXITER_1T1P) {
+		mtk_dpi_mask(dvo, DVO_SHADOW_CTRL, 0, BYPASS_SHADOW);
+		mtk_dpi_mask(dvo, DVO_SHADOW_CTRL, FORCE_COMMIT, FORCE_COMMIT);
+	};
+
+	mtk_dpi_mask(dvo, DVO_OUTPUT_SET, val, OUT_NP_SEL);
+	return;
+}
+
+static void mtk_dvo_config_color_format(struct mtk_dpi *dvo,
+					enum mtk_dpi_out_color_format format)
+{
+	mtk_dvo_config_channel_swap(dvo, dvo->channel_swap);
+
+	dev_err(dvo->dev, "[DPTX] format:%d", format);
+
+	switch (format) {
+	case MTK_DPI_COLOR_FORMAT_YCBCR_444:
+		mtk_dvo_config_yuv422_enable(dvo, false);
+		mtk_dvo_config_csc_enable(dvo, true);
+		break;
+	case MTK_DPI_COLOR_FORMAT_YCBCR_422:
+		mtk_dvo_config_yuv422_enable(dvo, true);
+		mtk_dvo_config_csc_enable(dvo, true);
+		break;
+	case MTK_DPI_COLOR_FORMAT_RGB:
+	default:
+		mtk_dvo_config_yuv422_enable(dvo, false);
+		mtk_dvo_config_csc_enable(dvo, false);
+		break;
+	}
+}
+
+static void mtk_dpi_config_pol(struct mtk_dpi *dpi,
+			       struct mtk_dpi_polarities *dpi_pol)
+{
+	u32 pol;
+
+	pol = (dpi_pol->hsync_pol == MTK_DPI_POLARITY_RISING ? 0 : HS_INV) |
+	      (dpi_pol->vsync_pol == MTK_DPI_POLARITY_RISING ? 0 : VS_INV);
+
+	mtk_dpi_mask(dpi, DVO_OUTPUT_SET, pol, HS_INV | VS_INV);
+}
+
+static void mtk_dvo_config_hw(struct mtk_dpi *dvo,
+			      struct videomode *vm, struct mtk_dpi_sync *sync,
+			      struct mtk_dpi_polarities *dpi_pol,
+			      struct mtk_dpi_yc_limit *limit)
+{
+	struct drm_display_mode *mode = &dvo->mode;
+	u32 vactive = vm->vactive;
+
+	mtk_dvo_sw_reset(dvo, true);
+
+	mtk_dvo_irq_enable(dvo);
+
+	mtk_dpi_config_pol(dvo, dpi_pol);
+
+	mtk_dvo_config_hsync(dvo, &sync->hsync);
+	mtk_dvo_config_vsync(dvo, &sync->vsync_l_odd, DVO_TGEN_V0, DVO_TGEN_V1);
+	mtk_dvo_config_vsync(dvo, &sync->vsync_l_even, DVO_TGEN_V_EVEN0, DVO_TGEN_V_EVEN1);
+
+	mtk_dvo_config_interface(dvo, !!(vm->flags & DISPLAY_FLAGS_INTERLACED));
+
+	if (vm->flags & DISPLAY_FLAGS_INTERLACED)
+		vactive >>= 1;
+
+	mtk_dvo_config_fb_size(dvo, vm->hactive, vactive);
+
+	mtk_dvo_config_channel_limit(dvo, limit);
+	mtk_dvo_config_channel_swap(dvo, dvo->channel_swap);
+	mtk_dvo_config_color_format(dvo, dvo->color_format);
+
+	mtk_dvo_info_queue_start(dvo);
+	mtk_dpi_buffer_ctrl(dvo);
+
+	mtk_dvo_trailing_blank_setting(dvo);
+	mtk_dvo_get_gs_level(dvo);
+	mtk_dvo_golden_setting(dvo);
+	mtk_dvo_sodi_setting(dvo, mode);
+
+	mtk_dvo_set_output_pixiter(dvo);
+
+	mtk_dvo_sw_reset(dvo, false);
+
+	return;
+}
+
+static int mtk_dvo_bridge_attach(struct drm_bridge *bridge,
+				 struct drm_encoder *encoder,
+				 enum drm_bridge_attach_flags flags)
+{
+	struct mtk_dpi *dvo = bridge_to_dpi(bridge);
+	int ret;
+
+	ret = drm_bridge_attach(bridge->encoder, dvo->next_bridge,
+				&dvo->bridge, 1);
+	if (ret)
+		dev_err(dvo->dev, "Failed to attach bridge: %d\n", ret);
+
+	return ret;
+}
+
+static void mtk_dvo_bridge_disable(struct drm_bridge *bridge)
+{
+	struct mtk_dpi *dvo = bridge_to_dpi(bridge);
+
+	mtk_dvo_disable(dvo);
+	mtk_dpi_power_off(dvo);
+}
+
+static void mtk_dvo_bridge_enable(struct drm_bridge *bridge)
+{
+	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+	struct mtk_dpi_polarities dpi_pol;
+	struct mtk_dpi_sync sync = { 0 };
+	struct mtk_dpi_yc_limit limit;
+	struct videomode vm;
+
+	if (dpi->pinctrl && dpi->pins_dpi)
+		pinctrl_select_state(dpi->pinctrl, dpi->pins_dpi);
+
+	mtk_dpi_power_on(dpi);
+
+	/* Set pixel clock and initialize parameters to send to the HW */
+	mtk_dpi_set_display_mode(dpi, &vm, &sync, &dpi_pol, &limit);
+
+	/* Format and send the parameters to the HW */
+	mtk_dvo_config_hw(dpi, &vm, &sync, &dpi_pol, &limit);
+
+	mtk_dvo_enable(dpi);
+}
+
+static enum drm_mode_status
+mtk_dvo_bridge_mode_valid(struct drm_bridge *bridge,
+			  const struct drm_display_info *info,
+			  const struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static int mtk_dvo_debug_tp_show(struct seq_file *m, void *arg)
+{
+	struct mtk_dpi *dvo = m->private;
+	bool en;
+	u32 val;
+
+	if (!dvo)
+		return -EINVAL;
+
+	val = readl(dvo->regs + DVO_PATTERN_CTRL);
+	en = (val & (PRE_PAT_EN | PRE_PAT_FORCE_ON)) == (PRE_PAT_EN | PRE_PAT_FORCE_ON);
+	val = FIELD_GET(PRE_PAT_SEL_MASK, val);
+
+	return mtk_dpi_common_debug_tp_show(m, en, val);
+}
+
+static ssize_t mtk_dvo_debug_tp_write(struct file *file, const char __user *ubuf,
+				      size_t len, loff_t *offp)
+{
+	struct seq_file *m = file->private_data;
+	u32 en, type;
+	ssize_t ret;
+
+	/* seq_file and dvo pointers are checked by mtk_dpi_common_debug_tp_write() */
+	ret = mtk_dpi_common_debug_tp_write(file, ubuf, len, offp, &en, &type);
+	if (ret < 0)
+		return ret;
+
+	mtk_dvo_test_pattern_en((struct mtk_dpi *)m->private, type, en);
+	return ret;
+}
+
+static int mtk_dvo_debug_tp_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, mtk_dvo_debug_tp_show, inode->i_private);
+}
+
+static const struct file_operations mtk_dvo_debug_tp_fops = {
+	.owner = THIS_MODULE,
+	.open = mtk_dvo_debug_tp_open,
+	.read = seq_read,
+	.write = mtk_dvo_debug_tp_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static void mtk_dvo_debugfs_init(struct drm_bridge *bridge, struct dentry *root)
+{
+	struct mtk_dpi *dpi = bridge_to_dpi(bridge);
+
+	debugfs_create_file("dvo_test_pattern", 0640, root, dpi, &mtk_dvo_debug_tp_fops);
+}
+
+static const struct drm_bridge_funcs mtk_dvo_bridge_funcs = {
+	.attach = mtk_dvo_bridge_attach,
+	.mode_set = mtk_dpi_bridge_mode_set,
+	.mode_valid = mtk_dvo_bridge_mode_valid,
+	.disable = mtk_dvo_bridge_disable,
+	.enable = mtk_dvo_bridge_enable,
+	.atomic_check = mtk_dpi_bridge_atomic_check,
+	.atomic_get_output_bus_fmts = mtk_dpi_bridge_atomic_get_output_bus_fmts,
+	.atomic_get_input_bus_fmts = mtk_dpi_bridge_atomic_get_input_bus_fmts,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.debugfs_init = mtk_dvo_debugfs_init,
+};
+
+void mtk_dvo_start(struct device *dev)
+{
+	struct mtk_dpi *dvo = dev_get_drvdata(dev);
+
+	mtk_dpi_power_on(dvo);
+}
+
+void mtk_dvo_stop(struct device *dev)
+{
+	struct mtk_dpi *dvo = dev_get_drvdata(dev);
+
+	mtk_dvo_disable(dvo);
+	mtk_dpi_power_off(dvo);
+}
+
+unsigned int mtk_dvo_encoder_index(struct device *dev)
+{
+	struct mtk_dpi *dvo = dev_get_drvdata(dev);
+
+	return drm_encoder_index(&dvo->encoder);
+}
+
+static const struct component_ops mtk_dvo_component_ops = {
+	.bind = mtk_dpi_common_bind,
+	.unbind = mtk_dpi_common_unbind,
+};
+
+static const struct mtk_dpi_factor dpi_factor_mt8196_dvo[] = {
+	{ 70000 - 1, 4 }, { 200000 - 1, 2 }, { U32_MAX, 1 }
+};
+
+static const u32 mt8196_output_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_RGB888_2X12_LE,
+	MEDIA_BUS_FMT_RGB888_2X12_BE,
+	MEDIA_BUS_FMT_RGB101010_1X30,
+	MEDIA_BUS_FMT_YUYV8_1X16,
+	MEDIA_BUS_FMT_YUYV10_1X20,
+	MEDIA_BUS_FMT_YUYV12_1X24,
+	MEDIA_BUS_FMT_BGR888_1X24,
+	MEDIA_BUS_FMT_YUV8_1X24,
+	MEDIA_BUS_FMT_YUV10_1X30,
+};
+
+static const struct mtk_dpi_conf mt8189_conf = {
+	.dpi_factor = dpi_factor_mt8196_dvo,
+	.num_dpi_factor = ARRAY_SIZE(dpi_factor_mt8196_dvo),
+	.reg_h_fre_con = 0xb0,
+	.max_clock_khz = 640000,
+	.output_fmts = mt8196_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8196_output_fmts),
+	.pixels_per_iter = 4,
+	.dimension_mask = HFP_MASK,
+	.hvsize_mask = PIC_HSIZE_MASK,
+};
+
+static const struct mtk_dpi_conf mt8189_dp_conf = {
+	.dpi_factor = dpi_factor_mt8196_dvo,
+	.num_dpi_factor = ARRAY_SIZE(dpi_factor_mt8196_dvo),
+	.reg_h_fre_con = 0xb0,
+	.max_clock_khz = 640000,
+	.output_fmts = mt8196_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8196_output_fmts),
+	.pixels_per_iter = 4,
+	.dimension_mask = HFP_MASK,
+	.hvsize_mask = PIC_HSIZE_MASK,
+	.quirk_hfp_bs_fix = true,
+};
+
+static const struct mtk_dpi_conf mt8196_conf = {
+	.dpi_factor = dpi_factor_mt8196_dvo,
+	.num_dpi_factor = ARRAY_SIZE(dpi_factor_mt8196_dvo),
+	.reg_h_fre_con = 0xb0,
+	.max_clock_khz = 600000,
+	.output_fmts = mt8196_output_fmts,
+	.num_output_fmts = ARRAY_SIZE(mt8196_output_fmts),
+	.pixels_per_iter = 4,
+	.dimension_mask = HFP_MASK,
+	.hvsize_mask = PIC_HSIZE_MASK,
+};
+
+static int mtk_dvo_probe(struct platform_device *pdev)
+{
+	struct mtk_dpi *dvo;
+	int ret;
+
+	dvo = mtk_dpi_common_probe(pdev, &mtk_dvo_bridge_funcs);
+	if (IS_ERR(dvo))
+		return PTR_ERR(dvo);
+
+	ret = component_add(&pdev->dev, &mtk_dvo_component_ops);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "Failed to add component.\n");
+
+	return 0;
+}
+
+static void mtk_dvo_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &mtk_dvo_component_ops);
+}
+
+static const struct of_device_id mtk_dvo_of_ids[] = {
+	{ .compatible = "mediatek,mt8189-dp-dvo", .data = &mt8189_dp_conf },
+	{ .compatible = "mediatek,mt8189-edp-dvo", .data = &mt8189_conf },
+	{ .compatible = "mediatek,mt8196-edp-dvo", .data = &mt8196_conf },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_dvo_of_ids);
+
+struct platform_driver mtk_dvo_driver = {
+	.probe = mtk_dvo_probe,
+	.remove = mtk_dvo_remove,
+	.driver = {
+		.name = "mediatek-dvo",
+		.of_match_table = mtk_dvo_of_ids,
+	},
+};
+MODULE_IMPORT_NS("DRM_MTK_DPI");
diff --git a/drivers/gpu/drm/mediatek/mtk_dvo_regs.h b/drivers/gpu/drm/mediatek/mtk_dvo_regs.h
new file mode 100644
index 000000000000..42a6f318d991
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dvo_regs.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Jie Qiu <jie.qiu@mediatek.com>
+ */
+#ifndef __MTK_DPI_REGS_H
+#define __MTK_DPI_REGS_H
+
+#define DVO_EN			0x00
+#define EN				BIT(0)
+#define DVO_FORCE_ON			BIT(4)
+#define LINK_OFF			BIT(8)
+#define DVO_RET			0x04
+#define SWRST				BIT(0)
+#define SWRST_SEL			BIT(4)
+#define DVO_INTEN		0x08
+#define INT_VFP_START_EN		BIT(0)
+#define INT_VSYNC_START_EN		BIT(1)
+#define INT_VSYNC_END_EN		BIT(2)
+#define INT_VDE_START_EN		BIT(3)
+#define INT_VDE_END_EN			BIT(4)
+#define INT_WR_INFOQ_REG_EN		BIT(5)
+#define INT_TARGET_LINE0_EN		BIT(6)
+#define INT_TARGET_LINE1_EN		BIT(7)
+#define INT_TARGET_LINE2_EN		BIT(8)
+#define INT_TARGET_LINE3_EN		BIT(9)
+#define INT_WR_INFOQ_START_EN		BIT(10)
+#define INT_WR_INFOQ_END_EN		BIT(11)
+#define EXT_VSYNC_START_EN		BIT(12)
+#define EXT_VSYNC_END_EN		BIT(13)
+#define EXT_VDE_START_EN		BIT(14)
+#define EXT_VDE_END_EN			BIT(15)
+#define EXT_VBLANK_END_EN		BIT(16)
+#define UNDERFLOW_EN			BIT(17)
+#define INFOQ_ABORT_EN			BIT(18)
+
+#define DVO_INTSTA		0x0c
+#define INT_VFP_START_STA		BIT(0)
+#define INT_VSYNC_START_STA		BIT(1)
+#define INT_VSYNC_END_STA		BIT(2)
+#define INT_VDE_START_STA		BIT(3)
+#define INT_VDE_END_STA			BIT(4)
+#define INT_WR_INFOQ_REG_STA		BIT(5)
+#define INT_TARGET_LINE0_STA		BIT(6)
+#define INT_TARGET_LINE1_STA		BIT(7)
+#define INT_TARGET_LINE2_STA		BIT(8)
+#define INT_TARGET_LINE3_STA		BIT(9)
+#define INT_WR_INFOQ_START_STA		BIT(10)
+#define INT_WR_INFOQ_END_STA		BIT(11)
+#define EXT_VSYNC_START_STA		BIT(12)
+#define EXT_VSYNC_END_STA		BIT(13)
+#define EXT_VDE_START_STA		BIT(14)
+#define EXT_VDE_END_STA			BIT(15)
+#define EXT_VBLANK_END_STA		BIT(16)
+#define INT_UNDERFLOW_STA		BIT(17)
+#define INFOQ_ABORT_STA			BIT(18)
+
+#define DVO_CON			0x10
+#define INTL_EN				BIT(0)
+
+#define DVO_OUTPUT_SET		0x18
+#define OUT_NP_SEL			(0x3 << 0)
+#define BIT_SWAP			BIT(4)
+#define CH_SWAP_MASK			GENMASK(7, 5)
+#define SWAP_RGB			0x00
+#define SWAP_GBR			0x01
+#define SWAP_BRG			0x02
+#define SWAP_RBG			0x03
+#define SWAP_GRB			0x04
+#define SWAP_BGR			0x05
+#define PXL_SWAP			BIT(8)
+#define R_MASK				BIT(12)
+#define G_MASK				BIT(13)
+#define B_MASK				BIT(14)
+#define DE_MASK				BIT(16)
+#define HS_MASK				BIT(17)
+#define VS_MASK				BIT(18)
+#define HS_INV				BIT(20)
+#define VS_INV				BIT(21)
+
+#define DVO_SRC_SIZE		0x20
+#define SRC_HSIZE			0
+#define SRC_HSIZE_MASK			(0xffff << 0)
+#define SRC_VSIZE			16
+#define SRC_VSIZE_MASK			(0xffff << 16)
+
+#define DVO_PIC_SIZE		0x24
+#define PIC_HSIZE			0
+#define PIC_HSIZE_MASK			(0xffff << 0)
+#define PIC_VSIZE			16
+#define PIC_VSIZE_MASK			(0xffff << 16)
+
+#define DVO_TGEN_H0		0x50
+#define HFP				0
+#define HFP_MASK			(0xffff << 0)
+#define HSYNC				16
+#define HSYNC_MASK			(0xffff << 16)
+
+#define DVO_TGEN_H1		0x54
+#define HSYNC2ACT			0
+#define HSYNC2ACT_MASK			(0xffff << 0)
+#define HACT				16
+#define HACT_MASK			(0xffff << 16)
+
+#define DVO_TGEN_V0		0x58
+#define DVO_TGEN_V_EVEN0	0x64
+#define VFP				0
+#define VFP_MASK			(0xffff << 0)
+#define VSYNC				16
+#define VSYNC_MASK			(0xffff << 16)
+
+#define DVO_TGEN_V1		0x5c
+#define DVO_TGEN_V_EVEN1	0x68
+#define VSYNC2ACT			0
+#define VSYNC2ACT_MASK			(0xffff << 0)
+#define VACT				16
+#define VACT_MASK			(0xffff << 16)
+
+#define DVO_TGEN_INFOQ_LATENCY	0x80
+#define INFOQ_START_LATENCY		0
+#define INFOQ_START_LATENCY_MASK	(0xffff << 0)
+#define INFOQ_END_LATENCY		16
+#define INFOQ_END_LATENCY_MASK		(0xffff << 16)
+
+#define DVO_BUF_CON0		0x220
+#define DISP_BUF_EN			BIT(0)
+#define FIFO_UNDERFLOW_DONE_BLOCK	BIT(4)
+
+
+#define DVO_TGEN_V_LAST_TRAILING_BLANK	0x6c
+#define V_LAST_TRAILING_BLANK			0
+#define V_LAST_TRAILING_BLANK_MASK		(0xffff << 0)
+
+#define DVO_TGEN_OUTPUT_DELAY_LINE	0x7c
+#define EXT_TG_DLY_LINE				0
+#define EXT_TG_DLY_LINE_MASK			(0xffff << 0)
+
+#define DVO_MUTEX_VSYNC_SET		0x84
+#define MUTEX_VSYNC_SEL				BIT(0)
+#define MUTEX_VFP_MASK				GENMASK(19, 4)
+
+#define DVO_PATTERN_CTRL		0x100
+#define PRE_PAT_EN				BIT(0)
+#define PRE_PAT_SEL_MASK			(0x7 << 4)
+#define COLOR_BAR				(0x4<<4)
+#define PRE_PAT_FORCE_ON			BIT(8)
+
+#define DVO_PATTERN_COLOR		0x104
+#define PAT_R					(0x3ff << 0)
+#define PAT_G					(0x3ff << 10)
+#define PAT_B					(0x3ff << 20)
+
+#define DVO_Y_LIMIT			0x130
+#define Y_LMT_BOT				GENMASK(9, 0)
+#define Y_LMT_TOP				GENMASK(21, 12)
+
+#define DVO_C_LIMIT			0x134
+#define C_LMT_BOT				GENMASK(9, 0)
+#define C_LMT_TOP				GENMASK(21, 12)
+
+#define DVO_SHADOW_CTRL			0x190
+#define FORCE_COMMIT				BIT(0)
+#define BYPASS_SHADOW				BIT(1)
+#define READ_WRK_REG				BIT(2)
+
+#define DVO_SIZE			0x18
+#define DVO_TGEN_VWIDTH			0x28
+#define DVO_TGEN_VPORCH			0x2c
+#define DVO_TGEN_HPORCH			0x24
+#define DVO_TGEN_HWIDTH			0x20
+
+#define DVO_BUF_SODI_HIGHT		0x230
+#define DVO_BUF_SODI_LOW		0x234
+#define DVO_BUF_DVFS_HIGHT		0x238
+#define DVO_BUF_DVFS_LOW		0x23c
+#define DVO_BUF_PREULTRA_HIGHT		0x240
+#define DVO_BUF_PREULTRA_LOW		0x244
+#define DVO_BUF_ULTRA_HIGHT		0x248
+#define DVO_BUF_ULTRA_LOW		0x24c
+#define DVO_BUF_URGENT_HIGHT		0x250
+#define DVO_BUF_URGENT_LOW		0x254
+#define DVO_DISP_BUF_MASK		GENMASK(31,0)
+
+#define DVO_MATRIX_SET			0x140
+#define DVO_CSC_EN			BIT(0)
+#define DVO_INT_MTX_SEL_MASK		GENMASK(8, 4)
+
+#define DVO_YUV422_SET			0x170
+#define DVO_YUV422_EN			BIT(0)
+#define DVO_CRYCB_MAP			BIT(8)
+
+#endif /* __MTK_DPI_REGS_H */
diff --git a/include/linux/soc/mediatek/mtk-mmsys.h b/include/linux/soc/mediatek/mtk-mmsys.h
index f67f21d04163..0b3bef392ab8 100644
--- a/include/linux/soc/mediatek/mtk-mmsys.h
+++ b/include/linux/soc/mediatek/mtk-mmsys.h
@@ -114,6 +114,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_DPI,
 	MTK_DISP_DP_INTF,
 	MTK_DISP_DSI,
+	MTK_DISP_DVO,
 
 	MTK_DDP_COMP_TYPE_MAX
 };
-- 
2.54.0



^ permalink raw reply related

* [PATCH 34/42] drm/mediatek: Enable bring-up of multi-controller CRTC paths
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

Introduce support for actaully bringing up a full crtc path that
features multiple display controllers: this enables acquisition
and writing to multiple MMSYS and MuteX instances to connect the
multiple display controllers and enable trigger signals from the
various hardware sub-components at the same time.

This currently only works for rather rare display pipelines that
can make use of multi-controller without DirectLink architecture.

This change is also the final preparation to introduce support
for handling DirectLink Display Controller paths, found in the
MediaTek Kompanio Ultra MT8196, Genio Pro 5100 MT8894 and also
in the Dimensity 9400 MT6991, Dimensity 9500 MT6993 SoCs.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_crtc.c     | 519 ++++++++++++++++++------
 drivers/gpu/drm/mediatek/mtk_ddp_comp.c |   6 +-
 drivers/gpu/drm/mediatek/mtk_ddp_comp.h |   4 +
 3 files changed, 403 insertions(+), 126 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 3f70d11270e9..fb6ced49bb4a 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -45,6 +45,9 @@ struct mtk_crtc_hw_layer {
  * struct mtk_crtc - MediaTek specific crtc structure.
  * @base:            CRTC object
  * @enabled:         Records whether crtc_enable succeeded
+ * @controller_idmap:Array that maps a controller's MMSYS ID to mtk_crtc per-controller
+ *                   arrays indices; this array is counted by num_controllers.
+ * @num_controllers: Records number of display controllers used by this pipeline
  * @pending_needs_vblank: Records whether pending config operation needs a VBlank to finish
  * @event:           VBlank event to signal upon completion of state update
  * @hwlayers:        Array of mtk_crtc_hw_layer structures, one for each overlay plane
@@ -58,7 +61,7 @@ struct mtk_crtc_hw_layer {
  * @cb_blocking_queue: Wait queue for sending blocking command packet through CMDQ Mailbox
  * @mmsys_dev:       Pointer to the MMSYS device for configuration registers
  * @dma_dev:         Pointer to the DMA device (usually linked to an IOMMU)
- * @mutex:           Pointer to the MediaTek MuteX device for HW triggers mute/unmuting
+ * @mutex:           Pointer to the MediaTek MuteX devices for HW triggers mute/unmuting
  * @ddp_comp_nr:     Number of HW components in ddp_comp structure
  * @ddp_comp:        Array of HW components used in one Display Controller pipeline
  * @vblank_comp_idx: Index of HW component where to enable sending VBlanks
@@ -72,6 +75,8 @@ struct mtk_crtc_hw_layer {
 struct mtk_crtc {
 	struct drm_crtc			base;
 	bool				enabled;
+	u8				*controller_idmap;
+	u8				num_controllers;
 
 	bool				pending_needs_vblank;
 	struct drm_pending_vblank_event	*event;
@@ -91,7 +96,7 @@ struct mtk_crtc {
 
 	struct device			*mmsys_dev;
 	struct device			*dma_dev;
-	struct mtk_mutex		*mutex;
+	struct mtk_mutex		**mutex;
 	unsigned int			ddp_comp_nr;
 	struct mtk_ddp_comp		**ddp_comp;
 	s8				vblank_comp_idx;
@@ -161,7 +166,8 @@ static void mtk_crtc_destroy(struct drm_crtc *crtc)
 	struct mtk_crtc *mtk_crtc = to_mtk_crtc(crtc);
 	int i;
 
-	mtk_mutex_put(mtk_crtc->mutex);
+	for (i = 0; i < mtk_crtc->num_controllers; i++)
+		mtk_mutex_put(mtk_crtc->mutex[i]);
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
 	if (mtk_crtc->cmdq_client.chan) {
 		cmdq_pkt_destroy(&mtk_crtc->cmdq_client, &mtk_crtc->cmdq_handle);
@@ -361,6 +367,64 @@ static void mtk_crtc_config_layer(struct mtk_crtc *mtk_crtc,
 	return;
 }
 
+static struct mtk_drm_private
+*mtk_crtc_get_comp_priv(struct mtk_crtc *mtk_crtc, struct mtk_ddp_comp *comp)
+{
+	struct drm_crtc *crtc = &mtk_crtc->base;
+	struct mtk_drm_private *main_priv = crtc->dev->dev_private;
+
+	for (int i = 0; i < main_priv->data->mmsys_dev_num; i++) {
+		struct mtk_drm_private *priv = main_priv->all_drm_private[i];
+		bool comp_found;
+
+		if (!priv)
+			continue;
+
+		comp_found = mtk_ddp_find_comp_dev_in_table(&priv->hlist,
+							    comp->type,
+							    comp->dev);
+		if (comp_found)
+			return priv;
+	}
+
+	drm_warn(mtk_crtc->base.dev,
+		 "Could not find priv for component %u-%u - trying with main...\n",
+		 comp->type, comp->inst_id);
+
+	return main_priv;
+}
+
+static struct mtk_drm_private
+*mtk_crtc_get_controller_priv(struct mtk_crtc *mtk_crtc, unsigned int controller_id)
+{
+	struct drm_crtc *crtc = &mtk_crtc->base;
+	struct mtk_drm_private *main_priv = crtc->dev->dev_private;
+
+	for (int i = 0; i < main_priv->data->mmsys_dev_num; i++) {
+		struct mtk_drm_private *priv = main_priv->all_drm_private[i];
+
+		if (priv->data->mmsys_id == controller_id)
+			return priv;
+	}
+
+	/* This should never happen */
+	drm_warn(mtk_crtc->base.dev,
+		 "Could not find priv for Controller %u - trying with main..\n",
+		 controller_id);
+
+	return main_priv;
+}
+
+static unsigned int mtk_crtc_get_controller_map_idx(struct mtk_crtc *mtk_crtc,
+						    unsigned int controller_id)
+{
+	for (int i = 0; i < mtk_crtc->num_controllers; i++)
+		if (mtk_crtc->controller_idmap[i] == controller_id)
+			return i;
+
+	return 0;
+}
+
 static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 {
 	struct drm_crtc *crtc = &mtk_crtc->base;
@@ -371,6 +435,9 @@ static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 	struct drm_dsc_config *dsc_cfg;
 	struct drm_device *dev = mtk_crtc->base.dev;
 	unsigned int width, height, vrefresh, bpc = MTK_MAX_BPC;
+	struct mtk_drm_private *priv;
+	struct mtk_mutex *mtk_mutex;
+	u8 mutex_idx;
 	int ret;
 	int i;
 
@@ -402,10 +469,12 @@ static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 		return ret;
 	}
 
-	ret = mtk_mutex_prepare(mtk_crtc->mutex);
-	if (ret < 0) {
-		drm_err(dev, "Failed to enable mutex clock: %d\n", ret);
-		goto err_pm_runtime_put;
+	for (i = 0; i < mtk_crtc->num_controllers; i++) {
+		ret = mtk_mutex_prepare(mtk_crtc->mutex[i]);
+		if (ret < 0) {
+			drm_err(dev, "Failed to enable mutex clock: %d\n", ret);
+			goto err_pm_runtime_put;
+		}
 	}
 
 	ret = mtk_crtc_ddp_clk_enable(mtk_crtc);
@@ -417,14 +486,21 @@ static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
 		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
 		struct mtk_ddp_comp *next = mtk_crtc->ddp_comp[i + 1];
+		struct device *mmsys_dev;
+
+		priv = mtk_crtc_get_comp_priv(mtk_crtc, comp);
+		mutex_idx = mtk_crtc_get_controller_map_idx(mtk_crtc,
+							    priv->data->mmsys_id);
+		mtk_mutex = mtk_crtc->mutex[mutex_idx];
+		mmsys_dev = priv->mmsys_dev;
 
-		if (!mtk_ddp_comp_connect(comp, mtk_crtc->mmsys_dev, next))
-			mtk_mmsys_hw_connect(mtk_crtc->mmsys_dev,
+		if (!mtk_ddp_comp_connect(comp, mmsys_dev, next))
+			mtk_mmsys_hw_connect(mmsys_dev,
 					     comp->type, comp->inst_id,
 					     next->type, next->inst_id);
 
-		if (!mtk_ddp_comp_add(comp, mtk_crtc->mutex))
-			mtk_mutex_add_trigger(mtk_crtc->mutex,
+		if (!mtk_ddp_comp_add(comp, mtk_mutex))
+			mtk_mutex_add_trigger(mtk_mutex,
 					      comp->type, comp->inst_id,
 					      comp->mtx_trig_id);
 
@@ -435,13 +511,19 @@ static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 		if (comp->type == MTK_DISP_DSC && !comp_dsc)
 			comp_dsc = mtk_crtc->ddp_comp[i];
 	}
-	if (!mtk_ddp_comp_add(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
-		mtk_mutex_add_trigger(mtk_crtc->mutex,
+
+	priv = mtk_crtc_get_comp_priv(mtk_crtc, mtk_crtc->ddp_comp[i]);
+	mutex_idx = mtk_crtc_get_controller_map_idx(mtk_crtc, priv->data->mmsys_id);
+	mtk_mutex = mtk_crtc->mutex[mutex_idx];
+
+	if (!mtk_ddp_comp_add(mtk_crtc->ddp_comp[i], mtk_mutex))
+		mtk_mutex_add_trigger(mtk_mutex,
 				      mtk_crtc->ddp_comp[i]->type,
 				      mtk_crtc->ddp_comp[i]->inst_id,
 				      mtk_crtc->ddp_comp[i]->mtx_trig_id);
 
-	mtk_mutex_enable(mtk_crtc->mutex);
+	for (i = 0; i < mtk_crtc->num_controllers; i++)
+		mtk_mutex_enable(mtk_crtc->mutex[i]);
 
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
 		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
@@ -476,7 +558,8 @@ static int mtk_crtc_ddp_hw_init(struct mtk_crtc *mtk_crtc)
 	return 0;
 
 err_mutex_unprepare:
-	mtk_mutex_unprepare(mtk_crtc->mutex);
+	for (i = 0; i < mtk_crtc->num_controllers; i++)
+		mtk_mutex_unprepare(mtk_crtc->mutex[i]);
 err_pm_runtime_put:
 	pm_runtime_put(crtc->dev->dev);
 	return ret;
@@ -486,44 +569,72 @@ static void mtk_crtc_ddp_hw_fini(struct mtk_crtc *mtk_crtc)
 {
 	struct drm_device *drm = mtk_crtc->base.dev;
 	struct drm_crtc *crtc = &mtk_crtc->base;
+	struct mtk_drm_private *priv;
+	struct mtk_mutex *mtk_mutex;
 	unsigned long flags;
+	u8 mutex_idx;
 	int i;
 
+
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
 		mtk_ddp_comp_stop(mtk_crtc->ddp_comp[i]);
 		if (i == 1)
 			mtk_ddp_comp_bgclr_in_off(mtk_crtc->ddp_comp[i]);
 	}
 
-	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
-		if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
-			mtk_mutex_remove_trigger(mtk_crtc->mutex,
+	for (i = 0; i < mtk_crtc->num_controllers; i++)
+		mtk_mutex_disable(mtk_crtc->mutex[i]);
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
+
+		priv = mtk_crtc_get_comp_priv(mtk_crtc, comp);
+		mutex_idx = mtk_crtc_get_controller_map_idx(mtk_crtc,
+							    priv->data->mmsys_id);
+		mtk_mutex = mtk_crtc->mutex[mutex_idx];
+
+		if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_mutex))
+			mtk_mutex_remove_trigger(mtk_mutex,
 						 mtk_crtc->ddp_comp[i]->type,
 						 mtk_crtc->ddp_comp[i]->inst_id,
 						 mtk_crtc->ddp_comp[i]->mtx_trig_id);
-	mtk_mutex_disable(mtk_crtc->mutex);
+	}
+
 	for (i = 0; i < mtk_crtc->ddp_comp_nr - 1; i++) {
 		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
 		struct mtk_ddp_comp *next = mtk_crtc->ddp_comp[i + 1];
+		struct device *mmsys_dev;
+
+		priv = mtk_crtc_get_comp_priv(mtk_crtc, comp);
+		mutex_idx = mtk_crtc_get_controller_map_idx(mtk_crtc,
+							    priv->data->mmsys_id);
+		mtk_mutex = mtk_crtc->mutex[mutex_idx];
+		mmsys_dev = priv->mmsys_dev;
 
-		if (!mtk_ddp_comp_disconnect(comp, mtk_crtc->mmsys_dev, next))
-			mtk_mmsys_hw_disconnect(mtk_crtc->mmsys_dev,
+		if (!mtk_ddp_comp_disconnect(comp, mmsys_dev, next))
+			mtk_mmsys_hw_disconnect(mmsys_dev,
 						comp->type, comp->inst_id,
 						next->type, next->inst_id);
 
-		if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
-			mtk_mutex_remove_trigger(mtk_crtc->mutex,
+		if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_mutex))
+			mtk_mutex_remove_trigger(mtk_mutex,
 						 mtk_crtc->ddp_comp[i]->type,
 						 mtk_crtc->ddp_comp[i]->inst_id,
 						 mtk_crtc->ddp_comp[i]->mtx_trig_id);
 	}
-	if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_crtc->mutex))
-		mtk_mutex_remove_trigger(mtk_crtc->mutex,
+	priv = mtk_crtc_get_comp_priv(mtk_crtc, mtk_crtc->ddp_comp[i]);
+	mutex_idx = mtk_crtc_get_controller_map_idx(mtk_crtc, priv->data->mmsys_id);
+	mtk_mutex = mtk_crtc->mutex[mutex_idx];
+	if (!mtk_ddp_comp_remove(mtk_crtc->ddp_comp[i], mtk_mutex))
+		mtk_mutex_remove_trigger(mtk_mutex,
 					 mtk_crtc->ddp_comp[i]->type,
 					 mtk_crtc->ddp_comp[i]->inst_id,
 					 mtk_crtc->ddp_comp[i]->mtx_trig_id);
+
 	mtk_crtc_ddp_clk_disable(mtk_crtc);
-	mtk_mutex_unprepare(mtk_crtc->mutex);
+
+	for (i = 0; i < mtk_crtc->num_controllers; i++)
+		mtk_mutex_unprepare(mtk_crtc->mutex[i]);
 
 	pm_runtime_put(drm->dev);
 
@@ -640,9 +751,13 @@ static void mtk_crtc_update_config(struct mtk_crtc *mtk_crtc, bool needs_vblank)
 		mtk_crtc->pending_async_planes = true;
 
 	if (priv->data->shadow_register) {
-		mtk_mutex_acquire(mtk_crtc->mutex);
+		for (i = 0; i < mtk_crtc->num_controllers; i++)
+			mtk_mutex_acquire(mtk_crtc->mutex[i]);
+
 		mtk_crtc_ddp_config(crtc, NULL);
-		mtk_mutex_release(mtk_crtc->mutex);
+
+		for (i = 0; i < mtk_crtc->num_controllers; i++)
+			mtk_mutex_release(mtk_crtc->mutex[i]);
 	}
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
 	if (mtk_crtc->cmdq_client.chan) {
@@ -959,8 +1074,8 @@ static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
 	.atomic_disable	= mtk_crtc_atomic_disable,
 };
 
-static int mtk_crtc_init(struct drm_device *drm, struct mtk_crtc *mtk_crtc,
-			 unsigned int pipe)
+static int mtk_crtc_init_drm_crtc(struct drm_device *drm, struct mtk_crtc *mtk_crtc,
+				  unsigned int pipe)
 {
 	struct drm_plane *primary = NULL;
 	struct drm_plane *cursor = NULL;
@@ -1131,21 +1246,13 @@ struct device *mtk_crtc_dma_dev_get(struct drm_crtc *crtc)
 	return mtk_crtc->dma_dev;
 }
 
-static int mtk_crtc_find_suitable_dma_dev(struct mtk_drm_private *priv,
-					  struct mtk_crtc *mtk_crtc,
-					  const struct mtk_drm_path_definition *path)
+static int mtk_crtc_find_suitable_dma_dev(struct mtk_crtc *mtk_crtc)
 {
 	struct mtk_ddp_comp *dma_comp = NULL, *dma_comp_mmu = NULL;
 	int i;
 
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
-		const struct mtk_drm_comp_definition *cdef = path[i].comp;
-		struct mtk_ddp_comp *comp;
-
-		comp = mtk_ddp_comp_find_by_id(&priv->hlist,
-					       cdef->type, cdef->inst_id);
-		if (!comp)
-			continue;
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
 
 		/*
 		 * Check if this is a legacy OVL_ADAPTOR component and, if so, this
@@ -1185,120 +1292,263 @@ static int mtk_crtc_find_suitable_dma_dev(struct mtk_drm_private *priv,
 	return 0;
 }
 
+static int mtk_crtc_init_multi_controller_properties(struct device *dev,
+						     struct mtk_crtc *mtk_crtc)
+{
+	struct drm_crtc *crtc = &mtk_crtc->base;
+	unsigned long controllers_mask = 0;
+	struct mtk_drm_private *priv;
+	unsigned int i, j, k, z;
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
+		u8 controller_id = mtk_crtc->ddp_comp[i]->controller_id;
+
+		if (controller_id > MTK_DISP_CONTROLLER_MAX_CONTROLLERS_NUM) {
+			dev_err(mtk_crtc->mmsys_dev, "Bad controller ID %u\n",
+				controller_id);
+			return -EINVAL;
+		}
+
+		if (!(controllers_mask & BIT(controller_id)))
+			mtk_crtc->num_controllers++;
+
+		controllers_mask |= BIT(controller_id);
+
+	}
+
+	mtk_crtc->controller_idmap = devm_kmalloc_array(dev, mtk_crtc->num_controllers,
+							sizeof(*mtk_crtc->controller_idmap),
+							GFP_KERNEL | __GFP_ZERO);
+	if (!mtk_crtc->controller_idmap)
+		return -ENOMEM;
+
+	dev_info(dev, "Bringing up %d Display Controllers for CRTC %u\n",
+		 mtk_crtc->num_controllers, crtc->base.id);
+
+	/* Shortcut: if this is a single controller pipelines, avoid iterating */
+	if (mtk_crtc->num_controllers == 1) {
+		mtk_crtc->controller_idmap[0] = mtk_crtc->ddp_comp[0]->controller_id;
+		return 0;
+	}
+
+	priv = crtc->dev->dev_private;
+
+	for_each_set_bit(i, &controllers_mask, MTK_DISP_CONTROLLER_MAX_CONTROLLERS_NUM) {
+		for (j = 0; j < mtk_crtc->ddp_comp_nr; j++) {
+			struct mtk_drm_private *controller_priv;
+
+			if (mtk_crtc->ddp_comp[j]->controller_id != i)
+				continue;
+
+			/* Find the matching Controller ID's mmsys device */
+			for (k = 0; k < priv->data->mmsys_dev_num; k++) {
+				controller_priv = priv->all_drm_private[k];
+
+				if (controller_priv->data->mmsys_id ==
+				    mtk_crtc->ddp_comp[j]->controller_id)
+					break;
+			}
+			if (k == priv->data->mmsys_dev_num)
+				continue;
+
+			mtk_crtc->controller_idmap[z] = mtk_crtc->ddp_comp[j]->controller_id;
+			z++;
+
+			/*
+			 * If this is a multi-controller path, link the follower
+			 * with the leader controller to make sure that any call
+			 * to Runtime PM acts on both.
+			 */
+			if (controller_priv->mmsys_dev != mtk_crtc->mmsys_dev)
+				device_link_add(controller_priv->mmsys_dev,
+						mtk_crtc->mmsys_dev,
+						DL_FLAG_PM_RUNTIME);
+
+			if (controller_priv->mutex_dev != priv->mutex_dev)
+				device_link_add(controller_priv->mutex_dev,
+						priv->mutex_dev,
+						DL_FLAG_PM_RUNTIME);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_crtc_init_mutex(struct device *dev, struct mtk_crtc *mtk_crtc)
+{
+	unsigned int i;
+	int ret;
+
+	mtk_crtc->mutex = devm_kmalloc_array(dev, mtk_crtc->num_controllers,
+					     sizeof(*mtk_crtc->mutex), GFP_KERNEL);
+	if (!mtk_crtc->mutex)
+		return -ENOMEM;
+
+	for (i = 0; i < mtk_crtc->num_controllers; i++) {
+		u8 controller_num = mtk_crtc->controller_idmap[i];
+		struct mtk_drm_private *priv = mtk_crtc_get_controller_priv(mtk_crtc, controller_num);
+
+		if (!priv)
+			return -ENODEV;
+
+		mtk_crtc->mutex[i] = mtk_mutex_get(priv->mutex_dev);
+		if (IS_ERR(mtk_crtc->mutex[i])) {
+			ret = PTR_ERR(mtk_crtc->mutex[i]);
+			dev_err(dev, "Failed to get Controller %u MuteX: %d\n", i, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 int mtk_crtc_create(struct drm_device *drm_dev,
 		    enum mtk_crtc_path path_sel, int priv_data_index,
 		    const struct mtk_drm_route *conn_routes,
 		    unsigned int num_conn_routes)
 {
 	struct mtk_drm_private *priv = drm_dev->dev_private;
-	const struct mtk_drm_path_definition *output_path;
 	struct device *dev = drm_dev->dev;
 	struct mtk_crtc *mtk_crtc;
 	unsigned int num_comp_planes = 0;
 	unsigned int max_comp_stages = 0;
+	unsigned int output_path_len = 0;
 	int ret;
-	int i, j;
+	int i, j, k;
 	bool has_ctm = false;
 	uint gamma_lut_size = 0;
 	struct drm_crtc *tmp;
 	int crtc_i = 0;
 
 	priv = priv->all_drm_private[priv_data_index];
-	output_path = &priv->data->output_paths[path_sel];
 
 	drm_for_each_crtc(tmp, drm_dev)
 		crtc_i++;
 
-	for (i = 0; i < output_path[i].len; i++) {
-		enum mtk_ddp_comp_type comp_type = output_path->comp[i].type;
-		u8 comp_inst = output_path->comp[i].inst_id;
-		struct mtk_ddp_comp *comp;
+	for (j = 0; j < priv->data->mmsys_dev_num; j++) {
+		for (k = 0; k < priv->data->mmsys_dev_num; k++) {
+			struct mtk_drm_private *subsys_priv = priv->all_drm_private[k];
+			const struct mtk_drm_path_definition *ctrlr_path;
 
-		comp = mtk_ddp_comp_find_by_id(&priv->hlist, comp_type, comp_inst);
-		if (!comp || !comp->dev) {
-			dev_err(dev,
-				"CRTC%d: Component type=%u inst=%u not initialized\n",
-				crtc_i, comp_type, comp_inst);
-			return -ENODEV;
+			if (!subsys_priv)
+				continue;
+
+			ctrlr_path = &subsys_priv->data->output_paths[path_sel];
+			if (j != ctrlr_path->order)
+				continue;
+
+			output_path_len += ctrlr_path->len;
 		}
 	}
+	if (!output_path_len)
+		return 0;
 
 	mtk_crtc = devm_kzalloc(dev, sizeof(*mtk_crtc), GFP_KERNEL);
 	if (!mtk_crtc)
 		return -ENOMEM;
 
 	mtk_crtc->mmsys_dev = priv->mmsys_dev;
-	mtk_crtc->ddp_comp_nr = output_path->len;
+	mtk_crtc->ddp_comp_nr = output_path_len;
 	mtk_crtc->ddp_comp = devm_kcalloc(dev,
 					  mtk_crtc->ddp_comp_nr + (conn_routes ? 1 : 0),
 					  sizeof(*mtk_crtc->ddp_comp),
 					  GFP_KERNEL);
-	if (!mtk_crtc->ddp_comp)
-		return -ENOMEM;
+	if (!mtk_crtc->ddp_comp) {
+		ret = -ENOMEM;
+		goto err_free_crtc;
+	}
 
-	mtk_crtc->mutex = mtk_mutex_get(priv->mutex_dev);
-	if (IS_ERR(mtk_crtc->mutex)) {
-		ret = PTR_ERR(mtk_crtc->mutex);
-		dev_err(dev, "Failed to get mutex: %d\n", ret);
-		return ret;
+	/*
+	 * Reusing path_len to initialize the structure in the next loop to
+	 * reduce stack size of this function at least a little.
+	 */
+	output_path_len = 0;
+
+	for (j = 0; j < priv->data->mmsys_dev_num; j++) {
+		for (k = 0; k < priv->data->mmsys_dev_num; k++) {
+			struct mtk_drm_private *subsys_priv = priv->all_drm_private[k];
+			const struct mtk_drm_path_definition *ctrlr_path;
+
+			subsys_priv = priv->all_drm_private[k];
+			if (!subsys_priv)
+				continue;
+
+			ctrlr_path = &subsys_priv->data->output_paths[path_sel];
+
+			if (!ctrlr_path->len || ctrlr_path->order != j)
+				continue;
+
+			for (i = 0; i < ctrlr_path->len; i++) {
+				struct mtk_ddp_comp *comp;
+
+				comp = mtk_ddp_comp_find_by_id(&subsys_priv->hlist,
+							       ctrlr_path->comp[i].type,
+							       ctrlr_path->comp[i].inst_id);
+				if (!comp || !comp->dev) {
+					dev_err(dev,
+						"CRTC%d: Component %u-%u not initialized\n",
+						crtc_i, ctrlr_path->comp[i].type,
+						ctrlr_path->comp[i].inst_id);
+					ret = -ENODEV;
+					goto err_free_ddp_comp;
+				}
+				mtk_crtc->ddp_comp[output_path_len] = comp;
+				output_path_len++;
+			}
+		}
 	}
 
 	/* Component 0 would be valid so initialize vblank and config idx to -EINVAL */
 	mtk_crtc->vblank_comp_idx = -EINVAL;
 	mtk_crtc->config_comp_idx = -EINVAL;
 
-	for (i = 0, j = 0; i < mtk_crtc->ddp_comp_nr; i++, j++) {
-		enum mtk_ddp_comp_type comp_type = output_path->comp[i].type;
-		u8 comp_inst = output_path->comp[i].inst_id;
-		struct mtk_ddp_comp *comp;
+	for (j = 0; j < mtk_crtc->ddp_comp_nr; j++) {
+		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[j];
 
-		comp = mtk_ddp_comp_find_by_id(&priv->hlist, comp_type, comp_inst);
-		if (!comp) {
-			j--;
-			dev_dbg(dev, "Cannot find component %u-%u.\n", comp_type, comp_inst);
+		if (!comp->funcs)
 			continue;
-		}
-		mtk_crtc->ddp_comp[j] = comp;
 
-		if (comp->funcs) {
-			if (comp->funcs->gamma_set && comp->funcs->gamma_get_lut_size) {
-				unsigned int lut_sz = mtk_ddp_gamma_get_lut_size(comp);
+		if (comp->funcs->gamma_set && comp->funcs->gamma_get_lut_size) {
+			unsigned int lut_sz = mtk_ddp_gamma_get_lut_size(comp);
 
-				if (lut_sz)
-					gamma_lut_size = lut_sz;
-			}
+			if (lut_sz)
+				gamma_lut_size = lut_sz;
+		}
 
-			if (comp->funcs->ctm_set)
-				has_ctm = true;
+		if (comp->funcs->ctm_set)
+			has_ctm = true;
 
-			/*
-			 * Assumes that there can only be one vblank enabler per CRTC,
-			 * and that should there be more than one, the one that should
-			 * handle vblanks has to be the bottom-most HW component.
-			 */
-			if (mtk_crtc->vblank_comp_idx < 0 && comp->funcs->enable_vblank)
-				mtk_crtc->vblank_comp_idx = j;
+		/*
+		 * Assumes that there can only be one vblank enabler per CRTC,
+		 * and that should there be more than one, the one that should
+		 * handle vblanks has to be the bottom-most HW component.
+		 */
+		if (mtk_crtc->vblank_comp_idx < 0 && comp->funcs->enable_vblank)
+			mtk_crtc->vblank_comp_idx = j;
 
-			/*
-			 * Assumes that there can only be one main configuration
-			 * component per CRTC, and that if more than one has to
-			 * be configured for at each frame, the main one would
-			 * take care of the config chain.
-			 *
-			 * As a note, such component has specific characteristics:
-			 * - It is configurable, and supports per-layer properties;
-			 * - It is a main layer component and not a layer stage;
-			 * - It is always the first one (the bottom-most) in the
-			 *   pipeline that has the characteristics explaned above.
-			 *
-			 * Such hardware is usually an OVL, RDMA or exDMA.
-			 *
-			 * This may change in the future with more complex pipelines.
-			 */
-			if (mtk_crtc->config_comp_idx < 0 && comp->funcs->config &&
-			    comp->funcs->layer_config && comp->funcs->layer_nr)
-				mtk_crtc->config_comp_idx = j;
-		}
+		/*
+		 * Assumes that there can only be one main configuration
+		 * component per CRTC, and that if more than one has to
+		 * be configured for at each frame, the main one would
+		 * take care of the config chain.
+		 *
+		 * As a note, such component has specific characteristics:
+		 * - It is configurable, and supports per-layer properties;
+		 * - It is a main layer component and not a layer stage;
+		 * - It is always the first one (the bottom-most) in the
+		 *   pipeline that has the characteristics explaned above.
+		 *
+		 * Such hardware is usually an OVL, RDMA or exDMA.
+		 *
+		 * This may change in the future with more complex pipelines.
+		 */
+		if (mtk_crtc->config_comp_idx < 0 && comp->funcs->config &&
+		    comp->funcs->layer_config && comp->funcs->layer_nr)
+			mtk_crtc->config_comp_idx = j;
+
+		num_comp_planes += mtk_ddp_comp_layer_nr(comp, 0);
+		max_comp_stages = MAX(mtk_ddp_comp_stage_nr(comp), max_comp_stages);
 
 		mtk_ddp_comp_register_vblank_cb(comp, mtk_crtc_ddp_irq,
 						&mtk_crtc->base);
@@ -1306,7 +1556,8 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 
 	if (mtk_crtc->config_comp_idx < 0) {
 		dev_err(dev, "No HW component for layer configuration. Bailing out.\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_free_ddp_comp;
 	}
 
 	if (mtk_crtc->vblank_comp_idx < 0) {
@@ -1314,13 +1565,6 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 		mtk_crtc->vblank_comp_idx = 0;
 	}
 
-	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
-		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
-
-		num_comp_planes += mtk_ddp_comp_layer_nr(comp, 0);
-		max_comp_stages = MAX(mtk_ddp_comp_stage_nr(comp), max_comp_stages);
-	}
-
 	/*
 	 * On the older and/or simpler display controllers, each layer is single
 	 * stage, while newer ones are more complex and may have multiple stages
@@ -1335,43 +1579,55 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 	mtk_crtc->hwlayers = devm_kcalloc(dev, num_comp_planes,
 					  sizeof(*mtk_crtc->hwlayers),
 					  GFP_KERNEL);
-	if (!mtk_crtc->hwlayers)
-		return -ENOMEM;
+	if (!mtk_crtc->hwlayers) {
+		ret = -ENOMEM;
+		goto err_free_ddp_comp;
+	}
 
 	for (i = 0; i < num_comp_planes; i++) {
 		struct mtk_crtc_hw_layer *comp_plane = &mtk_crtc->hwlayers[i];
 		comp_plane->layer_stages = devm_kcalloc(dev, max_comp_stages,
 							sizeof(*comp_plane->layer_stages),
 							GFP_KERNEL);
-		if (!comp_plane->layer_stages)
-			return -ENOMEM;
+		if (!comp_plane->layer_stages) {
+			ret = -ENOMEM;
+			goto err_free_layer_stages;
+		}
 	}
 
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
 		ret = mtk_crtc_init_comp_planes(drm_dev, mtk_crtc, i, crtc_i);
 		if (ret)
-			return ret;
+			goto err_free_layer_stages;
 	}
 
 	/* Initialize multi-stage only if present */
 	if (max_comp_stages > 1) {
 		ret = mtk_crtc_init_layer_stages(mtk_crtc);
 		if (ret)
-			return ret;
+			goto err_free_layer_stages;
 	}
 	dev_dbg(dev, "Found %u layers composed by maximum of %u stage(s) each.\n",
 		mtk_crtc->hwlayer_nr, max_comp_stages);
 
-	ret = mtk_crtc_find_suitable_dma_dev(priv, mtk_crtc, output_path);
+	ret = mtk_crtc_find_suitable_dma_dev(mtk_crtc);
 	if (ret) {
 		dev_err(dev, "Could not find appropriate DMA device!\n");
 		return -EINVAL;
 	}
 	dev_dbg(dev, "Using DMA device %pOF\n", mtk_crtc->dma_dev->of_node);
 
-	ret = mtk_crtc_init(drm_dev, mtk_crtc, crtc_i);
+	ret = mtk_crtc_init_drm_crtc(drm_dev, mtk_crtc, crtc_i);
 	if (ret < 0)
-		return ret;
+		goto err_free_layer_stages;
+
+	ret = mtk_crtc_init_multi_controller_properties(dev, mtk_crtc);
+	if (ret)
+		goto err_free_layer_stages;
+
+	ret = mtk_crtc_init_mutex(dev, mtk_crtc);
+	if (ret)
+		goto err_free_idmap;
 
 	if (gamma_lut_size)
 		drm_mode_crtc_set_gamma_size(&mtk_crtc->base, gamma_lut_size);
@@ -1448,4 +1704,21 @@ int mtk_crtc_create(struct drm_device *drm_dev,
 	}
 
 	return 0;
+
+err_free_idmap:
+	devm_kfree(dev, mtk_crtc->controller_idmap);
+err_free_layer_stages:
+	for (i = 0; i < num_comp_planes; i++) {
+		struct mtk_crtc_hw_layer *comp_plane = &mtk_crtc->hwlayers[i];
+
+		if (comp_plane->layer_stages)
+			devm_kfree(dev, comp_plane->layer_stages);
+	}
+	devm_kfree(dev, mtk_crtc->hwlayers);
+err_free_ddp_comp:
+	devm_kfree(dev, mtk_crtc->ddp_comp);
+err_free_crtc:
+	devm_kfree(dev, mtk_crtc);
+
+	return ret;
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index 3f25f1c34633..a305b5f6a42d 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -505,9 +505,9 @@ static const struct mtk_ddp_comp_funcs *mtk_ddp_funcs[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_DVO]			= &ddp_dvo,
 };
 
-static bool mtk_ddp_find_comp_dev_in_table(const struct mtk_drm_comp_list *hlist,
-					   const unsigned int comp_type,
-					   struct device *dev)
+bool mtk_ddp_find_comp_dev_in_table(const struct mtk_drm_comp_list *hlist,
+				    const unsigned int comp_type,
+				    struct device *dev)
 {
 	struct mtk_ddp_comp *ddp_comp;
 
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index e94bcf6922c1..165bf83ccd10 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -15,6 +15,7 @@
 
 #include <drm/drm_modes.h>
 
+#define MTK_DISP_CONTROLLER_MAX_CONTROLLERS_NUM		7
 #define MTK_DISP_CONTROLLER_MAX_COMP_PER_PATH		24
 #define MTK_DISP_CONTROLLER_MAX_HW_COMP_INSTANCE	32
 
@@ -370,6 +371,9 @@ static inline struct mtk_ddp_comp
 	return NULL;
 }
 
+bool mtk_ddp_find_comp_dev_in_table(const struct mtk_drm_comp_list *hlist,
+				    const unsigned int comp_type,
+				    struct device *dev);
 bool mtk_ddp_comp_is_internal_comp(enum mtk_ddp_comp_type type);
 int mtk_ddp_comp_get_id(struct device_node *node,
 			enum mtk_ddp_comp_type comp_type);
-- 
2.54.0



^ permalink raw reply related

* [PATCH 36/42] drm/mediatek: Support registering disp controller device subnodes
From: AngeloGioacchino Del Regno @ 2026-07-01 12:20 UTC (permalink / raw)
  To: chunkuang.hu
  Cc: p.zabel, airlied, simona, maarten.lankhorst, mripard, tzimmermann,
	robh, krzk+dt, conor+dt, matthias.bgg, angelogioacchino.delregno,
	dri-devel, linux-mediatek, devicetree, linux-kernel,
	linux-arm-kernel, justin.yeh, jason-jh.lin, kernel
In-Reply-To: <20260701122057.19648-1-angelogioacchino.delregno@collabora.com>

In order to have a correct description of the hardware in the SoC
devicetree, look for Display Controller Subsystem sub-components
declared as subnodes of the display controller itself; to retain
compatibility with the older devicetrees, also keep looking for
sub-components in the main /soc node like before.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_drv.c | 218 ++++++++++++++-----------
 1 file changed, 121 insertions(+), 97 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 482d05071125..50b4f79295b3 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -1237,6 +1237,107 @@ static int mtk_drm_of_ddp_path_build(struct device *dev, struct device_node *nod
 	return 0;
 }
 
+static int mtk_drm_register_sibling(struct device *dev, struct mtk_drm_private *private,
+				    struct device_node *node, struct component_match **match)
+{
+	enum mtk_ddp_comp_type comp_type;
+	int comp_inst_id;
+	bool comp_found;
+	int ret;
+
+	ret = mtk_drm_of_get_ddp_comp_type(node, &comp_type);
+	if (ret)
+		return -EAGAIN;
+
+	ret = of_device_is_available(node); 
+	if (!ret) {
+		dev_dbg(dev, "Skipping disabled component %pOF\n", node);
+		return -EAGAIN;
+	}
+
+	if (comp_type == MTK_DISP_MUTEX) {
+		int id;
+
+		id = of_alias_get_id(node, "mutex");
+		if (id < 0 || id == private->data->mmsys_id) {
+			private->mutex_node = of_node_get(node);
+			dev_dbg(dev, "get mutex for mmsys %d", private->data->mmsys_id);
+		}
+		return 0;
+	}
+
+	comp_inst_id = mtk_ddp_comp_get_id(node, NULL, comp_type);
+	if (comp_inst_id < 0) {
+		dev_info(dev, "Skipping unknown component %pOF\n", node);
+		return 0;
+	}
+
+	if (comp_type == MTK_DISP_DIRECT_LINK)
+		comp_found = mtk_drm_find_directlink_comp(private);
+	else
+		comp_found = mtk_drm_find_mmsys_comp(private,
+						     comp_type, comp_inst_id);
+
+	if (!comp_found)
+		return -EAGAIN;
+
+	/*
+	 * Currently only the AAL, CCORR, COLOR, GAMMA, MERGE, OVL, RDMA, DSI, and DPI
+	 * blocks have separate component platform drivers and initialize their own
+	 * DDP component structure. The others are initialized here.
+	 */
+	if (!mtk_ddp_comp_is_internal_comp(comp_type) &&
+	    !mtk_ovl_adaptor_is_comp_present(node)) {
+		dev_info(dev, "Adding component match for %pOF\n",
+			 node);
+		drm_of_component_match_add(dev, match, component_compare_of,
+					   node);
+	}
+
+	if (comp_type == MTK_DISP_DIRECT_LINK) {
+		for_each_of_graph_port(node, port) {
+			u32 port_id;
+
+			of_property_read_u32(port, "reg", &port_id);
+			if (port_id > 1)
+				continue;
+
+			/* Even ports are inputs, odd ports are outputs */
+			if (port_id % 2)
+				comp_type = MTK_DISP_DIRECT_LINK_OUT;
+
+			for_each_of_graph_port_endpoint(port, ep) {
+				struct of_endpoint of_ep;
+
+				ret = of_graph_parse_endpoint(ep, &of_ep);
+				if (ret)
+					break;
+
+				ret = mtk_ddp_comp_init(dev, node, &private->hlist,
+							private->data->mmsys_id,
+							comp_type, of_ep.id);
+				if (ret)
+					break;
+			}
+
+			if (ret) {
+				of_node_put(node);
+				return ret;
+			}
+		}
+	} else {
+		ret = mtk_ddp_comp_init(dev, node, &private->hlist,
+					private->data->mmsys_id,
+					comp_type, comp_inst_id);
+		if (ret) {
+			of_node_put(node);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int mtk_drm_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1298,107 +1399,29 @@ static int mtk_drm_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	/* Iterate over sibling DISP function blocks */
-	for_each_child_of_node(phandle->parent, node) {
-		enum mtk_ddp_comp_type comp_type;
-		u8 comp_inst_id;
-		bool comp_found;
-
-		ret = mtk_drm_of_get_ddp_comp_type(node, &comp_type);
-		if (ret)
-			continue;
-
-		if (!of_device_is_available(node)) {
-			dev_dbg(dev, "Skipping disabled component %pOF\n",
-				node);
-			continue;
-		}
-
-		if (comp_type == MTK_DISP_MUTEX) {
-			int id;
-
-			id = of_alias_get_id(node, "mutex");
-			if (id < 0 || id == private->data->mmsys_id) {
-				private->mutex_node = of_node_get(node);
-				dev_dbg(dev, "get mutex for mmsys %d", private->data->mmsys_id);
-			}
-			continue;
-		}
-
-		comp_inst_id = mtk_ddp_comp_get_id(node, NULL, comp_type);
-		if (comp_inst_id < 0) {
-			dev_warn(dev, "Skipping unknown component %pOF\n",
-				 node);
-			continue;
-		}
-
-		if (comp_type == MTK_DISP_DIRECT_LINK)
-			comp_found = mtk_drm_find_directlink_comp(private);
-		else
-			comp_found = mtk_drm_find_mmsys_comp(private,
-							     comp_type, comp_inst_id);
-
-		if (!comp_found)
-			continue;
-
-		/*
-		 * Currently only the AAL, CCORR, COLOR, GAMMA, MERGE, OVL, RDMA, DSI, and DPI
-		 * blocks have separate component platform drivers and initialize their own
-		 * DDP component structure. The others are initialized here.
-		 */
-		if (!mtk_ddp_comp_is_internal_comp(comp_type) &&
-		    !mtk_ovl_adaptor_is_comp_present(node)) {
-			dev_info(dev, "Adding component match for %pOF\n",
-				 node);
-			drm_of_component_match_add(dev, &match, component_compare_of,
-						   node);
-		}
-
-		if (comp_type == MTK_DISP_DIRECT_LINK) {
-			for_each_of_graph_port(node, port) {
-				u32 port_id;
-
-				of_property_read_u32(port, "reg", &port_id);
-				if (port_id > 1)
-					continue;
-
-				/* Even ports are inputs, odd ports are outputs */
-				if (port_id % 2)
-					comp_type = MTK_DISP_DIRECT_LINK_OUT;
-
-				for_each_of_graph_port_endpoint(port, ep) {
-					struct of_endpoint of_ep;
-
-					ret = of_graph_parse_endpoint(ep, &of_ep);
-					if (ret)
-						break;
-
-					ret = mtk_ddp_comp_init(dev, node, &private->hlist,
-								private->data->mmsys_id,
-								comp_type, of_ep.id);
-					if (ret)
-						break;
-				}
+	for_each_child_of_node(phandle, node) {
+		ret = mtk_drm_register_sibling(dev, private, node, &match);
+		if (ret && ret != -EAGAIN)
+			goto err_node;
+	}
 
-				if (ret) {
-					of_node_put(node);
-					goto err_node;
-				}
-			}
-		} else {
-			ret = mtk_ddp_comp_init(dev, node, &private->hlist,
-						private->data->mmsys_id,
-						comp_type, comp_inst_id);
-			if (ret) {
-				of_node_put(node);
-				goto err_node;
-			}
-		}
+	/*
+	 * After the previous loop, it is expected to have all of the display
+	 * controller sibling function blocks registered and added to the list.
+	 * 
+	 * If nothing got registered this is a legacy devicetree with DISP
+	 * siblings located under the /soc node instead of being children of
+	 * the main Display Controller node.
+	 */
+	for_each_child_of_node(phandle->parent, node) {
+		ret = mtk_drm_register_sibling(dev, private, node, &match);
+		if (ret && ret != -EAGAIN)
+			goto err_node;
 	}
 
 	if (!private->mutex_node) {
 		dev_err(dev, "Failed to find disp-mutex node\n");
-		ret = -ENODEV;
-		goto err_node;
+		return -ENODEV;
 	}
 
 	/* If mtk-mutex is not a trigger source, this is an old devicetree */
@@ -1425,7 +1448,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
 err_pm:
 	pm_runtime_disable(dev);
 err_node:
-	of_node_put(private->mutex_node);
+	if (private->mutex_node)
+		of_node_put(private->mutex_node);
 	return ret;
 }
 
-- 
2.54.0



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox