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 
<[email protected]>
---
 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 <[email protected]>
+ *
+ * Copyright (c) 2026 Collabora Ltd.
+ *                    AngeloGioacchino Del Regno 
<[email protected]>
  */
 
 #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 
<[email protected]>
+ */
+
+#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 
<[email protected]>");
+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 
<[email protected]>
+ */
+
+#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


Reply via email to