[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