[PATCH v2 08/14] video: mediatek: add dpi1 component

Julien Stephan <[email protected]>
Newsgroups org.u-boot-project.lists.u-boot
Message-ID <20260826-add-genio-510-and-genio-700-hdmi-v2-8-68ebb1bf17df@baylibre.com>
DPI is one of the components of the video pipeline on some MediaTek
SoCs, such as the MT8188: it is the timing generator interface
feeding the HDMI TX. Sync polarities, porches and the interlace mode
are taken from the struct display_timing selected from the EDID.

Signed-off-by: Julien Stephan <[email protected]>
---
 drivers/video/mediatek/mtk_dpi.c      | 379 ++++++++++++++++++++++++++++++++++
 drivers/video/mediatek/mtk_dpi.h      |  20 ++
 drivers/video/mediatek/mtk_dpi_regs.h | 113 ++++++++++
 3 files changed, 512 insertions(+)

diff --git a/drivers/video/mediatek/mtk_dpi.c b/drivers/video/mediatek/mtk_dpi.c
new file mode 100644
index 00000000000..7d72ab300e6
--- /dev/null
+++ b/drivers/video/mediatek/mtk_dpi.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek MT8188 DPI support
+ *
+ * Copyright (c) 2026 BayLibre, SAS.
+ * Author: Julien Stephan <[email protected]>
+ */
+
+#include <asm/io.h>
+#include <dm.h>
+#include <video.h>
+
+#include "mtk_disp_comp.h"
+#include "mtk_dpi.h"
+#include "mtk_dpi_regs.h"
+
+enum mtk_dpintf_out_bit_num {
+	MTK_DPINTF_OUT_BIT_NUM_8BITS,
+	MTK_DPINTF_OUT_BIT_NUM_10BITS,
+	MTK_DPINTF_OUT_BIT_NUM_12BITS,
+	MTK_DPINTF_OUT_BIT_NUM_16BITS
+};
+
+enum mtk_dpintf_out_yc_map {
+	MTK_DPINTF_OUT_YC_MAP_RGB,
+	MTK_DPINTF_OUT_YC_MAP_CYCY,
+	MTK_DPINTF_OUT_YC_MAP_YCYC,
+	MTK_DPINTF_OUT_YC_MAP_CY,
+	MTK_DPINTF_OUT_YC_MAP_YC
+};
+
+enum mtk_dpintf_out_channel_swap {
+	MTK_DPINTF_OUT_CHANNEL_SWAP_RGB,
+	MTK_DPINTF_OUT_CHANNEL_SWAP_GBR,
+	MTK_DPINTF_OUT_CHANNEL_SWAP_BRG,
+	MTK_DPINTF_OUT_CHANNEL_SWAP_RBG,
+	MTK_DPINTF_OUT_CHANNEL_SWAP_GRB,
+	MTK_DPINTF_OUT_CHANNEL_SWAP_BGR
+};
+
+enum mtk_dpintf_polarity {
+	MTK_DPINTF_POLARITY_RISING,
+	MTK_DPINTF_POLARITY_FALLING,
+};
+
+struct mtk_dpintf_polarities {
+	enum mtk_dpintf_polarity de_pol;
+	enum mtk_dpintf_polarity ck_pol;
+	enum mtk_dpintf_polarity hsync_pol;
+	enum mtk_dpintf_polarity vsync_pol;
+};
+
+struct mtk_dpintf_sync_param {
+	u32 sync_width;
+	u32 front_porch;
+	u32 back_porch;
+	bool shift_half_line;
+};
+
+struct mtk_dpintf_yc_limit {
+	u16 y_top;
+	u16 y_bottom;
+	u16 c_top;
+	u16 c_bottom;
+};
+
+static void mtk_dpi_mask(struct udevice *dev, u32 offset, u32 val, u32 mask)
+{
+	struct mtk_disp_comp_priv *priv = dev_get_priv(dev);
+	void __iomem *addr = priv->base + offset;
+	u32 tmp = readl(addr) & ~mask;
+
+	tmp |= (val & mask);
+	writel(tmp, addr);
+}
+
+static void mtk_dpi_sw_reset(struct udevice *dev, bool reset)
+{
+	mtk_dpi_mask(dev, DPI_RST, reset ? RST : 0, RST);
+}
+
+static void mtk_dpi_config_hsync(struct udevice *dev,
+				 struct mtk_dpintf_sync_param *sync)
+{
+	mtk_dpi_mask(dev, DPI_TGEN_HWIDTH, sync->sync_width << HPW, HPW_MASK);
+	mtk_dpi_mask(dev, DPI_TGEN_HPORCH, sync->back_porch << HBP, HBP_MASK);
+	mtk_dpi_mask(dev, DPI_TGEN_HPORCH, sync->front_porch << HFP, HFP_MASK);
+}
+
+static void mtk_dpi_config_vsync(struct udevice *dev,
+				 struct mtk_dpintf_sync_param *sync,
+				 u32 width_addr, u32 porch_addr)
+{
+	mtk_dpi_mask(dev, width_addr, sync->sync_width << VSYNC_WIDTH_SHIFT,
+		     VSYNC_WIDTH_MASK);
+	mtk_dpi_mask(dev, width_addr,
+		     sync->shift_half_line << VSYNC_HALF_LINE_SHIFT,
+		     VSYNC_HALF_LINE_MASK);
+	mtk_dpi_mask(dev, porch_addr,
+		     sync->back_porch << VSYNC_BACK_PORCH_SHIFT,
+		     VSYNC_BACK_PORCH_MASK);
+	mtk_dpi_mask(dev, porch_addr,
+		     sync->front_porch << VSYNC_FRONT_PORCH_SHIFT,
+		     VSYNC_FRONT_PORCH_MASK);
+}
+
+static void mtk_dpi_config_vsync_lodd(struct udevice *dev,
+				      struct mtk_dpintf_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dev, sync, DPI_TGEN_VWIDTH, DPI_TGEN_VPORCH);
+}
+
+static void mtk_dpi_config_vsync_leven(struct udevice *dev,
+				       struct mtk_dpintf_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dev, sync, DPI_TGEN_VWIDTH_LEVEN,
+			     DPI_TGEN_VPORCH_LEVEN);
+}
+
+static void mtk_dpi_config_vsync_rodd(struct udevice *dev,
+				      struct mtk_dpintf_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dev, sync, DPI_TGEN_VWIDTH_RODD,
+			     DPI_TGEN_VPORCH_RODD);
+}
+
+static void mtk_dpi_config_vsync_reven(struct udevice *dev,
+				       struct mtk_dpintf_sync_param *sync)
+{
+	mtk_dpi_config_vsync(dev, sync, DPI_TGEN_VWIDTH_REVEN,
+			     DPI_TGEN_VPORCH_REVEN);
+}
+
+static void mtk_dpi_config_pol(struct udevice *dev,
+			       struct mtk_dpintf_polarities *dpi_pol)
+{
+	unsigned int pol;
+
+	pol = (dpi_pol->ck_pol == MTK_DPINTF_POLARITY_RISING ? 0 : CK_POL) |
+	      (dpi_pol->de_pol == MTK_DPINTF_POLARITY_RISING ? 0 : DE_POL) |
+	      (dpi_pol->hsync_pol == MTK_DPINTF_POLARITY_RISING ?
+	       0 : HSYNC_POL) |
+	      (dpi_pol->vsync_pol == MTK_DPINTF_POLARITY_RISING ?
+	       0 : VSYNC_POL);
+	mtk_dpi_mask(dev, DPI_OUTPUT_SETTING, pol,
+		     CK_POL | DE_POL | HSYNC_POL | VSYNC_POL);
+}
+
+static void mtk_dpi_config_interface(struct udevice *dev, bool inter)
+{
+	mtk_dpi_mask(dev, DPI_CON, inter ? INTL_EN : 0, INTL_EN);
+}
+
+static void mtk_dpi_config_input_2p(struct udevice *dev, bool input_2p)
+{
+	mtk_dpi_mask(dev, DPI_CON, input_2p ? INPUT_2P_EN : 0, INPUT_2P_EN);
+}
+
+static void mtk_dpi_config_output_1t1p(struct udevice *dev, bool output_1t1p)
+{
+	mtk_dpi_mask(dev, DPI_CON, output_1t1p ? OUTPUT_1T1P_EN : 0,
+		     OUTPUT_1T1P_EN);
+}
+
+static void mtk_dpi_config_fb_size(struct udevice *dev, u32 width, u32 height)
+{
+	mtk_dpi_mask(dev, DPI_SIZE, width << HSIZE, HSIZE_MASK);
+	mtk_dpi_mask(dev, DPI_SIZE, height << VSIZE, VSIZE_MASK);
+}
+
+static void mtk_dpi_config_channel_limit(struct udevice *dev,
+					 struct mtk_dpintf_yc_limit *limit)
+{
+	mtk_dpi_mask(dev, DPI_Y_LIMIT, limit->y_bottom << Y_LIMINT_BOT,
+		     Y_LIMINT_BOT_MASK);
+	mtk_dpi_mask(dev, DPI_Y_LIMIT, limit->y_top << Y_LIMINT_TOP,
+		     Y_LIMINT_TOP_MASK);
+	mtk_dpi_mask(dev, DPI_C_LIMIT, limit->c_bottom << C_LIMIT_BOT,
+		     C_LIMIT_BOT_MASK);
+	mtk_dpi_mask(dev, DPI_C_LIMIT, limit->c_top << C_LIMIT_TOP,
+		     C_LIMIT_TOP_MASK);
+}
+
+static void mtk_dpi_config_bit_num(struct udevice *dev,
+				   enum mtk_dpintf_out_bit_num num)
+{
+	u32 val;
+
+	switch (num) {
+	case MTK_DPINTF_OUT_BIT_NUM_8BITS:
+		val = OUT_BIT_8;
+		break;
+	case MTK_DPINTF_OUT_BIT_NUM_10BITS:
+		val = OUT_BIT_10;
+		break;
+	case MTK_DPINTF_OUT_BIT_NUM_12BITS:
+		val = OUT_BIT_12;
+		break;
+	case MTK_DPINTF_OUT_BIT_NUM_16BITS:
+		val = OUT_BIT_16;
+		break;
+	default:
+		val = OUT_BIT_8;
+		break;
+	}
+
+	mtk_dpi_mask(dev, DPI_OUTPUT_SETTING, val << OUT_BIT, OUT_BIT_MASK);
+}
+
+static void mtk_dpi_config_yc_map(struct udevice *dev,
+				  enum mtk_dpintf_out_yc_map map)
+{
+	u32 val;
+
+	switch (map) {
+	case MTK_DPINTF_OUT_YC_MAP_RGB:
+		val = YC_MAP_RGB;
+		break;
+	case MTK_DPINTF_OUT_YC_MAP_CYCY:
+		val = YC_MAP_CYCY;
+		break;
+	case MTK_DPINTF_OUT_YC_MAP_YCYC:
+		val = YC_MAP_YCYC;
+		break;
+	case MTK_DPINTF_OUT_YC_MAP_CY:
+		val = YC_MAP_CY;
+		break;
+	case MTK_DPINTF_OUT_YC_MAP_YC:
+		val = YC_MAP_YC;
+		break;
+	default:
+		val = YC_MAP_RGB;
+		break;
+	}
+
+	mtk_dpi_mask(dev, DPI_OUTPUT_SETTING, val << YC_MAP, YC_MAP_MASK);
+}
+
+static void mtk_dpi_config_channel_swap(struct udevice *dev,
+					enum mtk_dpintf_out_channel_swap swap)
+{
+	u32 val;
+
+	switch (swap) {
+	case MTK_DPINTF_OUT_CHANNEL_SWAP_RGB:
+		val = SWAP_RGB;
+		break;
+	case MTK_DPINTF_OUT_CHANNEL_SWAP_GBR:
+		val = SWAP_GBR;
+		break;
+	case MTK_DPINTF_OUT_CHANNEL_SWAP_BRG:
+		val = SWAP_BRG;
+		break;
+	case MTK_DPINTF_OUT_CHANNEL_SWAP_RBG:
+		val = SWAP_RBG;
+		break;
+	case MTK_DPINTF_OUT_CHANNEL_SWAP_GRB:
+		val = SWAP_GRB;
+		break;
+	case MTK_DPINTF_OUT_CHANNEL_SWAP_BGR:
+		val = SWAP_BGR;
+		break;
+	default:
+		val = SWAP_RGB;
+		break;
+	}
+
+	mtk_dpi_mask(dev, DPI_OUTPUT_SETTING, val << CH_SWAP, CH_SWAP_MASK);
+}
+
+static void mtk_dpi_config_csc_enable(struct udevice *dev, bool enable)
+{
+	mtk_dpi_mask(dev, DPI_CON, enable ? CSC_ENABLE : 0, CSC_ENABLE);
+}
+
+static void mtk_dpi_config_swap_input(struct udevice *dev, bool enable)
+{
+	mtk_dpi_mask(dev, DPI_CON, enable ? IN_RB_SWAP : 0, IN_RB_SWAP);
+}
+
+static void mtk_dpi_internal_matrix_sel(struct udevice *dev, bool enable)
+{
+	mtk_dpi_config_csc_enable(dev, enable);
+	if (enable)
+		mtk_dpi_mask(dev, DPI_MATRIX_SET, RGB_TO_BT709,
+			     INT_MATRIX_SEL_MASK);
+}
+
+void mtk_dpi_hw_enable(struct udevice *dev)
+{
+	mtk_dpi_mask(dev, DPI_EN, EN, EN);
+}
+
+void mtk_dpi_hw_disable(struct udevice *dev)
+{
+	mtk_dpi_mask(dev, DPI_EN, 0, EN);
+}
+
+void mtk_dpi_config(struct udevice *dev, const struct display_timing *timing,
+		    bool rgb)
+{
+	struct mtk_dpintf_yc_limit limit;
+	struct mtk_dpintf_polarities dpi_pol;
+	struct mtk_dpintf_sync_param hsync;
+	struct mtk_dpintf_sync_param vsync_lodd = { 0 };
+	struct mtk_dpintf_sync_param vsync_leven = { 0 };
+	struct mtk_dpintf_sync_param vsync_rodd = { 0 };
+	struct mtk_dpintf_sync_param vsync_reven = { 0 };
+	bool interlaced = timing->flags & DISPLAY_FLAGS_INTERLACED;
+
+	limit.c_bottom = 0x0010;
+	limit.c_top = 0x0FE0;
+	limit.y_bottom = 0x0010;
+	limit.y_top = 0x0FE0;
+
+	dpi_pol.ck_pol = MTK_DPINTF_POLARITY_FALLING;
+	dpi_pol.de_pol = MTK_DPINTF_POLARITY_RISING;
+	dpi_pol.hsync_pol = timing->flags & DISPLAY_FLAGS_HSYNC_HIGH ?
+			    MTK_DPINTF_POLARITY_RISING :
+			    MTK_DPINTF_POLARITY_FALLING;
+	dpi_pol.vsync_pol = timing->flags & DISPLAY_FLAGS_VSYNC_HIGH ?
+			    MTK_DPINTF_POLARITY_RISING :
+			    MTK_DPINTF_POLARITY_FALLING;
+
+	hsync.sync_width = timing->hsync_len.typ;
+	hsync.back_porch = timing->hback_porch.typ;
+	hsync.front_porch = timing->hfront_porch.typ;
+	hsync.shift_half_line = false;
+
+	vsync_lodd.sync_width = timing->vsync_len.typ;
+	vsync_lodd.back_porch = timing->vback_porch.typ;
+	vsync_lodd.front_porch = timing->vfront_porch.typ;
+	vsync_lodd.shift_half_line = false;
+
+	if (interlaced) {
+		vsync_leven = vsync_lodd;
+		vsync_leven.shift_half_line = true;
+	}
+
+	mtk_dpi_sw_reset(dev, true);
+	mtk_dpi_config_pol(dev, &dpi_pol);
+
+	mtk_dpi_config_hsync(dev, &hsync);
+	mtk_dpi_config_vsync_lodd(dev, &vsync_lodd);
+	mtk_dpi_config_vsync_rodd(dev, &vsync_rodd);
+	mtk_dpi_config_vsync_leven(dev, &vsync_leven);
+	mtk_dpi_config_vsync_reven(dev, &vsync_reven);
+
+	mtk_dpi_config_interface(dev, interlaced);
+	if (interlaced)
+		mtk_dpi_config_fb_size(dev, timing->hactive.typ,
+				       timing->vactive.typ / 2);
+	else
+		mtk_dpi_config_fb_size(dev, timing->hactive.typ,
+				       timing->vactive.typ);
+
+	mtk_dpi_config_input_2p(dev, true);
+	mtk_dpi_config_output_1t1p(dev, true);
+	mtk_dpi_config_channel_limit(dev, &limit);
+	mtk_dpi_config_bit_num(dev, MTK_DPINTF_OUT_BIT_NUM_8BITS);
+	mtk_dpi_config_channel_swap(dev, MTK_DPINTF_OUT_CHANNEL_SWAP_RGB);
+	mtk_dpi_internal_matrix_sel(dev, !rgb);
+	mtk_dpi_config_yc_map(dev, MTK_DPINTF_OUT_YC_MAP_RGB);
+	mtk_dpi_config_swap_input(dev, false);
+	mtk_dpi_sw_reset(dev, false);
+}
+
+static const struct udevice_id mtk_dpi_ids[] = {
+	{ .compatible = "mediatek,mt8188-dpi" },
+	{}
+};
+
+U_BOOT_DRIVER(mtk_dpi) = {
+	.name	   = "mtk_dpi",
+	.id	   = UCLASS_MISC,
+	.of_match  = mtk_dpi_ids,
+	.probe	   = mtk_disp_comp_probe,
+	.priv_auto = sizeof(struct mtk_disp_comp_priv),
+};
diff --git a/drivers/video/mediatek/mtk_dpi.h b/drivers/video/mediatek/mtk_dpi.h
new file mode 100644
index 00000000000..ccce83ec8d3
--- /dev/null
+++ b/drivers/video/mediatek/mtk_dpi.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Mediatek MT8188 DPI Support
+ *
+ * Copyright (c) 2026 BayLibre, SAS.
+ * Author: Julien Stephan <[email protected]>
+ */
+
+#ifndef _MTK_DPI_H
+#define _MTK_DPI_H
+
+struct display_timing;
+struct udevice;
+
+void mtk_dpi_hw_enable(struct udevice *dev);
+void mtk_dpi_hw_disable(struct udevice *dev);
+void mtk_dpi_config(struct udevice *dev, const struct display_timing *timing,
+		    bool rgb);
+
+#endif
diff --git a/drivers/video/mediatek/mtk_dpi_regs.h b/drivers/video/mediatek/mtk_dpi_regs.h
new file mode 100644
index 00000000000..54e370460f0
--- /dev/null
+++ b/drivers/video/mediatek/mtk_dpi_regs.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2026 MediaTek Inc.
+ * Copyright (c) 2026 BayLibre, SAS
+ */
+
+#ifndef __MTK_DPI_REGS_H
+#define __MTK_DPI_REGS_H
+
+#define DPI_EN				0x00
+#define EN				(0x1 << 0)
+
+#define DPI_RST				0x04
+#define RST				(0x1 << 0)
+
+#define DPI_INTEN			0x08
+#define INT_VSYNC_EN			(0x1 << 0)
+
+#define DPI_CON				0x10
+#define IN_RB_SWAP			(0x1 << 1)
+#define INTL_EN				(0x1 << 2)
+#define TDFP_EN				(0x1 << 3)
+#define YUV422_EN			(0x1 << 5)
+#define CSC_ENABLE			(0x1 << 6)
+#define OUTPUT_1T1P_EN			(0x1 << 24)
+#define INPUT_2P_EN			(0x1 << 25)
+
+#define DPI_OUTPUT_SETTING		0x14
+#define CH_SWAP				0
+#define CH_SWAP_MASK			(0x7 << 0)
+#define SWAP_RGB			0x00
+#define SWAP_GBR			0x01
+#define SWAP_BRG			0x02
+#define SWAP_RBG			0x03
+#define SWAP_GRB			0x04
+#define SWAP_BGR			0x05
+#define DE_POL				(0x1 << 12)
+#define HSYNC_POL			(0x1 << 13)
+#define VSYNC_POL			(0x1 << 14)
+#define CK_POL				(0x1 << 15)
+#define OUT_BIT				18
+#define OUT_BIT_MASK			(0x3 << 18)
+#define OUT_BIT_8			0x00
+#define OUT_BIT_10			0x01
+#define OUT_BIT_12			0x02
+#define OUT_BIT_16			0x03
+#define YC_MAP				20
+#define YC_MAP_MASK			(0x7 << 20)
+#define YC_MAP_RGB			0x00
+#define YC_MAP_CYCY			0x04
+#define YC_MAP_YCYC			0x05
+#define YC_MAP_CY			0x06
+#define YC_MAP_YC			0x07
+
+#define DPI_SIZE			0x18
+#define HSIZE				0
+#define HSIZE_MASK			(0x1FFF << 0)
+#define VSIZE				16
+#define VSIZE_MASK			(0x1FFF << 16)
+
+#define DPI_DDR_SETTING			0x1C
+#define DDR_EN				(0x1 << 0)
+#define DDR_4PHASE			(0x1 << 2)
+
+#define DPI_TGEN_HWIDTH			0x20
+#define HPW				0
+#define HPW_MASK			(0xFFF << 0)
+
+#define DPI_TGEN_HPORCH			0x24
+#define HBP				0
+#define HBP_MASK			(0xFFF << 0)
+#define HFP				16
+#define HFP_MASK			(0xFFF << 16)
+
+#define DPI_TGEN_VWIDTH			0x28
+#define DPI_TGEN_VPORCH			0x2C
+
+#define VSYNC_WIDTH_SHIFT		0
+#define VSYNC_WIDTH_MASK		(0xFFF << 0)
+#define VSYNC_HALF_LINE_SHIFT		16
+#define VSYNC_HALF_LINE_MASK		(0xFFF << 16)
+#define VSYNC_BACK_PORCH_SHIFT		0
+#define VSYNC_BACK_PORCH_MASK		(0xFFF << 0)
+#define VSYNC_FRONT_PORCH_SHIFT		16
+#define VSYNC_FRONT_PORCH_MASK		(0xFFF << 16)
+
+#define DPI_TGEN_VWIDTH_LEVEN		0x68
+#define DPI_TGEN_VPORCH_LEVEN		0x6C
+#define DPI_TGEN_VWIDTH_RODD		0x70
+#define DPI_TGEN_VPORCH_RODD		0x74
+#define DPI_TGEN_VWIDTH_REVEN		0x78
+#define DPI_TGEN_VPORCH_REVEN		0x7C
+
+#define DPI_Y_LIMIT			0x98
+#define Y_LIMINT_BOT			0
+#define Y_LIMINT_BOT_MASK		(0xFFF << 0)
+#define Y_LIMINT_TOP			16
+#define Y_LIMINT_TOP_MASK		(0xFFF << 16)
+
+#define DPI_C_LIMIT			0x9C
+#define C_LIMIT_BOT			0
+#define C_LIMIT_BOT_MASK		(0xFFF << 0)
+#define C_LIMIT_TOP			16
+#define C_LIMIT_TOP_MASK		(0xFFF << 16)
+
+#define DPI_MATRIX_SET			0xB4
+#define INT_MATRIX_SEL_MASK		(0x1F << 0)
+#define RGB_TO_BT709			0x03
+
+#define DPI_PATTERN0			0xF00
+#define DPI_PATTERN_EN			BIT(0)
+#define DPI_PATTERN_COLOR_BAR		(0x4 << 4)
+#endif /* __MTK_DPI_REGS_H */

-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.