[PATCH 22/42] drm/mediatek: Create new mtk_dpi_common lib and move mtk_dpi code

AngeloGioacchino Del Regno angelogioacchino.delregno at collabora.com
Wed Jul 1 05:20:37 PDT 2026


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 at 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 at mediatek.com>
+ *
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno <angelogioacchino.delregno at 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 at 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 at 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 at 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




More information about the linux-arm-kernel mailing list