* [PATCH] video: lcdif_drv: add ported driver from linux
@ 2025-11-13 8:55 Michael Grzeschik
2025-11-14 13:14 ` Sascha Hauer
0 siblings, 1 reply; 2+ messages in thread
From: Michael Grzeschik @ 2025-11-13 8:55 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX
This patch is adding support for the for the LCDIFv3 LCD controller
found on i.MX8MP and i.MX93 SoCs.
It is currently supporting DRM_FORMAT_XRGB8888 media_bus format for now.
While porting from linux mainline the following changes were made:
- limited parallel support to DRM_FORMAT_XBGR8888
- added support for MEDIA_BUS_FMT_RGB888_1X24 bus_format
- when converting yuv to rgb mode only support limited range BT.601
- also support PARA_LINE_PATTERN for BGR888 on MEDIA_BUS_FMT_RGB888_1X24
- added an initial flush on atomic_enable
This was tested on i.MX93.
Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
drivers/video/Kconfig | 7 +
drivers/video/Makefile | 1 +
drivers/video/lcdif_drv.c | 74 +++++++
drivers/video/lcdif_drv.h | 39 ++++
drivers/video/lcdif_kms.c | 495 +++++++++++++++++++++++++++++++++++++++++++++
drivers/video/lcdif_regs.h | 266 ++++++++++++++++++++++++
6 files changed, 882 insertions(+)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index b2eccd5db7fe05999f3fe64db214fb569b3fb1df..739eb721253351ff0ab028aca094e5f1e4bcf487 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -52,6 +52,13 @@ config DRIVER_VIDEO_IMX_IPU_OVERLAY
bool "i.MX31/35 framebuffer overlay support"
depends on DRIVER_VIDEO_IMX_IPU && (ARCH_IMX35 || ARCH_IMX31)
+config DRIVER_VIDEO_LCDIF
+ bool "i.MX9 framebuffer driver"
+ depends on ARCH_IMX9 || ARCH_IMX93
+ help
+ Add support for the LCDIFv3 LCD controller found on
+ i.MX8MP and i.MX93 SoCs.
+
config DRIVER_VIDEO_STM
bool "i.MX23/28 framebuffer driver"
depends on ARCH_MXS
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 470a5abaa450b6477b7cf5a78b2a747f9a3ec743..e4f3f2a88561c230ffbe2b708fa24d23420c84d5 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_DRIVER_VIDEO_STM32_LTDC) += stm32_ltdc.o
obj-$(CONFIG_DRIVER_VIDEO_STM32_DSI) += stm32_dsi.o
obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o
obj-$(CONFIG_DRIVER_VIDEO_IMX_IPU) += imx-ipu-fb.o
+obj-$(CONFIG_DRIVER_VIDEO_LCDIF) += lcdif_drv.o lcdif_kms.o
obj-$(CONFIG_DRIVER_VIDEO_PXA) += pxa.o
obj-$(CONFIG_DRIVER_VIDEO_SDL) += sdl.o
obj-$(CONFIG_DRIVER_VIDEO_BCM283X) += bcm2835.o
diff --git a/drivers/video/lcdif_drv.c b/drivers/video/lcdif_drv.c
new file mode 100644
index 0000000000000000000000000000000000000000..c89f4c197f7d9cce6df1885743fe50ad78e57547
--- /dev/null
+++ b/drivers/video/lcdif_drv.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ *
+ * This code is based on drivers/gpu/drm/mxsfb/mxsfb*
+ */
+
+#include <linux/clk.h>
+#include <dma.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <video/videomode.h>
+
+#include "lcdif_drv.h"
+#include "lcdif_regs.h"
+
+#include <fb.h>
+#include <video/vpl.h>
+
+static int lcdif_probe(struct device *dev)
+{
+ struct lcdif_drm_private *lcdif;
+ struct resource *res;
+ int ret;
+
+ lcdif = xzalloc(sizeof(*lcdif));
+ if (!lcdif)
+ return -ENOMEM;
+
+ lcdif->dev = dev;
+
+ res = dev_get_resource(dev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res))
+ return PTR_ERR(res);
+
+ lcdif->base = IOMEM(res->start);
+ if (IS_ERR(lcdif->base))
+ return PTR_ERR(lcdif->base);
+
+ lcdif->clk = clk_get(lcdif->dev, "pix");
+ if (IS_ERR(lcdif->clk))
+ return PTR_ERR(lcdif->clk);
+
+ lcdif->clk_axi = clk_get(lcdif->dev, "axi");
+ if (IS_ERR(lcdif->clk_axi))
+ return PTR_ERR(lcdif->clk_axi);
+
+ lcdif->clk_disp_axi = clk_get(lcdif->dev, "disp_axi");
+ if (IS_ERR(lcdif->clk_disp_axi))
+ return PTR_ERR(lcdif->clk_disp_axi);
+
+ ret = lcdif_kms_init(lcdif);
+ if (ret < 0) {
+ dev_err(lcdif->dev, "Failed to initialize KMS pipeline\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id lcdif_dt_ids[] = {
+ { .compatible = "fsl,imx8mp-lcdif" },
+ { .compatible = "fsl,imx93-lcdif" },
+ { /* sentinel */ }
+};
+
+static struct driver lcdif_platform_driver = {
+ .probe = lcdif_probe,
+ .name = "imx-lcdif",
+ .of_compatible = lcdif_dt_ids,
+};
+device_platform_driver(lcdif_platform_driver);
diff --git a/drivers/video/lcdif_drv.h b/drivers/video/lcdif_drv.h
new file mode 100644
index 0000000000000000000000000000000000000000..991ba273ccf59b921a5c10d6ccd5d9bfe2db7504
--- /dev/null
+++ b/drivers/video/lcdif_drv.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ *
+ * i.MX8MP/i.MXRT LCDIFv3 LCD controller driver.
+ */
+
+#ifndef __LCDIF_DRV_H__
+#define __LCDIF_DRV_H__
+
+#include <video/vpl.h>
+
+struct clk;
+
+struct lcdif_drm_private {
+ void __iomem *base; /* registers */
+
+ int id;
+
+ u32 line_length;
+ u32 max_yres;
+ int crtc_endpoint_id;
+ struct device_node *port;
+ struct fb_info info;
+
+ dma_addr_t paddr;
+
+ struct clk *clk;
+ struct clk *clk_axi;
+ struct clk *clk_disp_axi;
+
+ struct device *dev;
+ struct vpl vpl;
+ struct fb_videomode *mode;
+};
+
+int lcdif_kms_init(struct lcdif_drm_private *lcdif);
+
+#endif /* __LCDIF_DRV_H__ */
diff --git a/drivers/video/lcdif_kms.c b/drivers/video/lcdif_kms.c
new file mode 100644
index 0000000000000000000000000000000000000000..033df231869a61df19f0cd60dbee0124aeec45bc
--- /dev/null
+++ b/drivers/video/lcdif_kms.c
@@ -0,0 +1,495 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ *
+ * This code is based on drivers/gpu/drm/mxsfb/mxsfb*
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <video/media-bus-format.h>
+
+#include <video/drm/drm_connector.h>
+#include <of_graph.h>
+#include <fb.h>
+#include <dma.h>
+
+#include <video/vpl.h>
+#include <video/videomode.h>
+
+#include <video/fourcc.h>
+
+#include "lcdif_drv.h"
+#include "lcdif_regs.h"
+
+struct lcdif_crtc_state {
+ u32 bus_format;
+ u32 bus_flags;
+};
+
+static void lcdif_set_formats(struct lcdif_drm_private *lcdif,
+ const u32 format,
+ const u32 bus_format)
+{
+ bool in_yuv = false;
+ bool out_yuv = false;
+
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ writel(DISP_PARA_LINE_PATTERN_RGB565,
+ lcdif->base + LCDC_V8_DISP_PARA);
+ break;
+ case MEDIA_BUS_FMT_BGR888_1X24:
+ writel(DISP_PARA_LINE_PATTERN_BGR888,
+ lcdif->base + LCDC_V8_DISP_PARA);
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ writel(DISP_PARA_LINE_PATTERN_RGB888,
+ lcdif->base + LCDC_V8_DISP_PARA);
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ writel(DISP_PARA_LINE_PATTERN_UYVY_H,
+ lcdif->base + LCDC_V8_DISP_PARA);
+ out_yuv = true;
+ break;
+ default:
+ dev_err(lcdif->dev, "Unknown media bus format 0x%x\n", bus_format);
+ break;
+ }
+
+ switch (format) {
+ /* RGB Formats */
+ case DRM_FORMAT_RGB565:
+ writel(CTRLDESCL0_5_BPP_16_RGB565,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ break;
+ case DRM_FORMAT_RGB888:
+ writel(CTRLDESCL0_5_BPP_24_RGB888,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ writel(CTRLDESCL0_5_BPP_16_ARGB1555,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ break;
+ case DRM_FORMAT_XRGB4444:
+ writel(CTRLDESCL0_5_BPP_16_ARGB4444,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ break;
+ case DRM_FORMAT_XBGR8888:
+ writel(CTRLDESCL0_5_BPP_32_ABGR8888,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ break;
+ case DRM_FORMAT_XRGB8888:
+ writel(CTRLDESCL0_5_BPP_32_ARGB8888,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ break;
+
+ /* YUV Formats */
+ case DRM_FORMAT_YUYV:
+ writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_VY2UY1,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ in_yuv = true;
+ break;
+ case DRM_FORMAT_YVYU:
+ writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_UY2VY1,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ in_yuv = true;
+ break;
+ case DRM_FORMAT_UYVY:
+ writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_Y2VY1U,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ in_yuv = true;
+ break;
+ case DRM_FORMAT_VYUY:
+ writel(CTRLDESCL0_5_BPP_YCbCr422 | CTRLDESCL0_5_YUV_FORMAT_Y2UY1V,
+ lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ in_yuv = true;
+ break;
+
+ default:
+ dev_err(lcdif->dev, "Unknown pixel format 0x%x\n", format);
+ break;
+ }
+
+ /*
+ * The CSC differentiates between "YCbCr" and "YUV", but the reference
+ * manual doesn't detail how they differ. Experiments showed that the
+ * luminance value is unaffected, only the calculations involving chroma
+ * values differ. The YCbCr mode behaves as expected, with chroma values
+ * being offset by 128. The YUV mode isn't fully understood.
+ */
+ if (!in_yuv && out_yuv) {
+ /* RGB -> YCbCr */
+ writel(CSC0_CTRL_CSC_MODE_RGB2YCbCr,
+ lcdif->base + LCDC_V8_CSC0_CTRL);
+
+ /*
+ * CSC: BT.601 Limited Range RGB to YCbCr coefficients.
+ *
+ * |Y | | 0.2568 0.5041 0.0979| |R| |16 |
+ * |Cb| = |-0.1482 -0.2910 0.4392| * |G| + |128|
+ * |Cr| | 0.4392 0.4392 -0.3678| |B| |128|
+ */
+ writel(CSC0_COEF0_A2(0x081) | CSC0_COEF0_A1(0x041),
+ lcdif->base + LCDC_V8_CSC0_COEF0);
+ writel(CSC0_COEF1_B1(0x7db) | CSC0_COEF1_A3(0x019),
+ lcdif->base + LCDC_V8_CSC0_COEF1);
+ writel(CSC0_COEF2_B3(0x070) | CSC0_COEF2_B2(0x7b6),
+ lcdif->base + LCDC_V8_CSC0_COEF2);
+ writel(CSC0_COEF3_C2(0x7a2) | CSC0_COEF3_C1(0x070),
+ lcdif->base + LCDC_V8_CSC0_COEF3);
+ writel(CSC0_COEF4_D1(0x010) | CSC0_COEF4_C3(0x7ee),
+ lcdif->base + LCDC_V8_CSC0_COEF4);
+ writel(CSC0_COEF5_D3(0x080) | CSC0_COEF5_D2(0x080),
+ lcdif->base + LCDC_V8_CSC0_COEF5);
+ } else if (in_yuv && !out_yuv) {
+ /* YCbCr -> RGB */
+ /*
+ * BT.601 limited range:
+ *
+ * |R| |1.1644 0.0000 1.5960| |Y - 16 |
+ * |G| = |1.1644 -0.3917 -0.8129| * |Cb - 128|
+ * |B| |1.1644 2.0172 0.0000| |Cr - 128|
+ */
+ const u32 coeffs[6] = {
+ CSC0_COEF0_A1(0x12a) | CSC0_COEF0_A2(0x000),
+ CSC0_COEF1_A3(0x199) | CSC0_COEF1_B1(0x12a),
+ CSC0_COEF2_B2(0x79c) | CSC0_COEF2_B3(0x730),
+ CSC0_COEF3_C1(0x12a) | CSC0_COEF3_C2(0x204),
+ CSC0_COEF4_C3(0x000) | CSC0_COEF4_D1(0x1f0),
+ CSC0_COEF5_D2(0x180) | CSC0_COEF5_D3(0x180),
+ };
+
+ writel(CSC0_CTRL_CSC_MODE_YCbCr2RGB,
+ lcdif->base + LCDC_V8_CSC0_CTRL);
+
+ writel(coeffs[0], lcdif->base + LCDC_V8_CSC0_COEF0);
+ writel(coeffs[1], lcdif->base + LCDC_V8_CSC0_COEF1);
+ writel(coeffs[2], lcdif->base + LCDC_V8_CSC0_COEF2);
+ writel(coeffs[3], lcdif->base + LCDC_V8_CSC0_COEF3);
+ writel(coeffs[4], lcdif->base + LCDC_V8_CSC0_COEF4);
+ writel(coeffs[5], lcdif->base + LCDC_V8_CSC0_COEF5);
+ } else {
+ /* RGB -> RGB, YCbCr -> YCbCr: bypass colorspace converter. */
+ writel(CSC0_CTRL_BYPASS, lcdif->base + LCDC_V8_CSC0_CTRL);
+ }
+}
+
+static void lcdif_set_mode(struct lcdif_drm_private *lcdif,
+ struct drm_display_mode *m,
+ u32 bus_flags)
+{
+ u32 ctrl = 0;
+
+ if (m->flags & DRM_MODE_FLAG_NHSYNC)
+ ctrl |= CTRL_INV_HS;
+ if (m->flags & DRM_MODE_FLAG_NVSYNC)
+ ctrl |= CTRL_INV_VS;
+ if (bus_flags & DRM_BUS_FLAG_DE_LOW)
+ ctrl |= CTRL_INV_DE;
+ if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+ ctrl |= CTRL_INV_PXCK;
+
+ writel(ctrl, lcdif->base + LCDC_V8_CTRL);
+
+ writel(DISP_SIZE_DELTA_Y(m->vdisplay) |
+ DISP_SIZE_DELTA_X(m->hdisplay),
+ lcdif->base + LCDC_V8_DISP_SIZE);
+
+ writel(HSYN_PARA_BP_H(m->htotal - m->hsync_end) |
+ HSYN_PARA_FP_H(m->hsync_start - m->hdisplay),
+ lcdif->base + LCDC_V8_HSYN_PARA);
+
+ writel(VSYN_PARA_BP_V(m->vtotal - m->vsync_end) |
+ VSYN_PARA_FP_V(m->vsync_start - m->vdisplay),
+ lcdif->base + LCDC_V8_VSYN_PARA);
+
+ writel(VSYN_HSYN_WIDTH_PW_V(m->vsync_end - m->vsync_start) |
+ VSYN_HSYN_WIDTH_PW_H(m->hsync_end - m->hsync_start),
+ lcdif->base + LCDC_V8_VSYN_HSYN_WIDTH);
+
+ writel(CTRLDESCL0_1_HEIGHT(m->vdisplay) |
+ CTRLDESCL0_1_WIDTH(m->hdisplay),
+ lcdif->base + LCDC_V8_CTRLDESCL0_1);
+
+ /*
+ * Undocumented P_SIZE and T_SIZE register but those written in the
+ * downstream kernel those registers control the AXI burst size. As of
+ * now there are two known values:
+ * 1 - 128Byte
+ * 2 - 256Byte
+ * Downstream set it to 256B burst size to improve the memory
+ * efficiency so set it here too.
+ */
+ /* NOTE: Since this driver is currently fixed to DRM_FORMAT_XRGB8888
+ * we asume a stride of vdisplay * 4
+ */
+ ctrl = CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) |
+ CTRLDESCL0_3_PITCH(m->hdisplay * 4);
+ writel(ctrl, lcdif->base + LCDC_V8_CTRLDESCL0_3);
+}
+
+static void lcdif_enable_controller(struct lcdif_drm_private *lcdif)
+{
+ u32 reg;
+
+ /* Set FIFO Panic watermarks, low 1/3, high 2/3 . */
+ writel(FIELD_PREP(PANIC0_THRES_LOW_MASK, 1 * PANIC0_THRES_MAX / 3) |
+ FIELD_PREP(PANIC0_THRES_HIGH_MASK, 2 * PANIC0_THRES_MAX / 3),
+ lcdif->base + LCDC_V8_PANIC0_THRES);
+
+ /*
+ * Enable FIFO Panic, this does not generate interrupt, but
+ * boosts NoC priority based on FIFO Panic watermarks.
+ */
+ writel(INT_ENABLE_D1_PLANE_PANIC_EN,
+ lcdif->base + LCDC_V8_INT_ENABLE_D1);
+
+ reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
+ reg |= DISP_PARA_DISP_ON;
+ writel(reg, lcdif->base + LCDC_V8_DISP_PARA);
+
+ reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ reg |= CTRLDESCL0_5_EN;
+ writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
+}
+
+static void lcdif_disable_controller(struct lcdif_drm_private *lcdif)
+{
+ u32 reg;
+ int ret;
+
+ reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ reg &= ~CTRLDESCL0_5_EN;
+ writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
+
+ ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5,
+ reg, !(reg & CTRLDESCL0_5_EN),
+ 36000); /* Wait ~2 frame times max */
+ if (ret)
+ dev_err(lcdif->dev, "Failed to disable controller!\n");
+
+ reg = readl(lcdif->base + LCDC_V8_DISP_PARA);
+ reg &= ~DISP_PARA_DISP_ON;
+ writel(reg, lcdif->base + LCDC_V8_DISP_PARA);
+
+ /* Disable FIFO Panic NoC priority booster. */
+ writel(0, lcdif->base + LCDC_V8_INT_ENABLE_D1);
+}
+
+static void lcdif_reset_block(struct lcdif_drm_private *lcdif)
+{
+ writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_SET);
+ readl(lcdif->base + LCDC_V8_CTRL);
+ writel(CTRL_SW_RESET, lcdif->base + LCDC_V8_CTRL + REG_CLR);
+ readl(lcdif->base + LCDC_V8_CTRL);
+}
+
+static void lcdif_crtc_mode_set_nofb(struct lcdif_drm_private *lcdif,
+ struct drm_display_mode *m,
+ struct lcdif_crtc_state *lcdif_crtc_state)
+{
+ dev_dbg(lcdif->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
+ m->clock, (int)(clk_get_rate(lcdif->clk) / 1000));
+ dev_dbg(lcdif->dev, "Bridge bus_flags: 0x%08X\n",
+ lcdif_crtc_state->bus_flags);
+ dev_dbg(lcdif->dev, "Mode flags: 0x%08X\n", m->flags);
+
+ /* Mandatory eLCDIF reset as per the Reference Manual */
+ lcdif_reset_block(lcdif);
+
+ /* NOTE: This driver is currently fixed to DRM_FORMAT_XRGB8888 */
+ lcdif_set_formats(lcdif, DRM_FORMAT_XRGB8888, lcdif_crtc_state->bus_format);
+
+ lcdif_set_mode(lcdif, m, lcdif_crtc_state->bus_flags);
+}
+
+static void lcdif_crtc_atomic_flush(struct fb_info *info)
+{
+ struct lcdif_drm_private *lcdif = container_of(info, struct lcdif_drm_private, info);
+ u32 reg;
+
+ reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
+ writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
+}
+
+static void lcdif_crtc_atomic_enable(struct lcdif_drm_private *lcdif,
+ struct drm_display_mode *mode,
+ struct lcdif_crtc_state *vcstate)
+{
+ dma_addr_t paddr;
+ u32 reg;
+
+ clk_set_rate(lcdif->clk, mode->clock * 1000);
+
+ lcdif_crtc_mode_set_nofb(lcdif, mode, vcstate);
+
+ /* Write cur_buf as well to avoid an initial corrupt frame */
+ paddr = lcdif->paddr;
+ if (paddr) {
+ writel(lower_32_bits(paddr),
+ lcdif->base + LCDC_V8_CTRLDESCL_LOW0_4);
+ writel(CTRLDESCL_HIGH0_4_ADDR_HIGH(upper_32_bits(paddr)),
+ lcdif->base + LCDC_V8_CTRLDESCL_HIGH0_4);
+ /* initial flush of the current data */
+ reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ reg |= CTRLDESCL0_5_SHADOW_LOAD_EN;
+ writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5);
+ }
+ lcdif_enable_controller(lcdif);
+}
+
+static void lcdif_enable_fb_controller(struct fb_info *info)
+{
+ struct lcdif_drm_private *lcdif = container_of(info, struct lcdif_drm_private, info);
+ struct drm_display_mode mode = {};
+ struct lcdif_crtc_state vcstate = {
+ .bus_format = 0,
+ .bus_flags = 0,
+ };
+ struct drm_display_info display_info = {};
+ int ret;
+
+ if (!info->mode) {
+ dev_err(lcdif->dev, "no modes, cannot enable\n");
+ return;
+ }
+
+ fb_videomode_to_drm_display_mode(info->mode, &mode);
+
+ ret = vpl_ioctl(&lcdif->vpl, lcdif->id, VPL_GET_BUS_FORMAT, &vcstate.bus_format);
+ if (ret < 0) {
+ dev_err(lcdif->dev, "Cannot determine bus format\n");
+ return;
+ }
+
+ ret = vpl_ioctl(&lcdif->vpl, lcdif->id, VPL_GET_DISPLAY_INFO, &display_info);
+ if (ret < 0) {
+ dev_err(lcdif->dev, "Cannot get display info\n");
+ return;
+ }
+
+ vcstate.bus_flags = display_info.bus_flags;
+
+ dev_info(lcdif->dev, "vp%d: bus_format: 0x%08x bus_flags: 0x%08x\n",
+ lcdif->id, vcstate.bus_format, display_info.bus_flags);
+
+ vpl_ioctl_prepare(&lcdif->vpl, lcdif->id, info->mode);
+
+ lcdif_crtc_atomic_enable(lcdif, &mode, &vcstate);
+
+ vpl_ioctl_enable(&lcdif->vpl, lcdif->id);
+}
+
+static void lcdif_disable_fb_controller(struct fb_info *info)
+{
+ struct lcdif_drm_private *lcdif = container_of(info, struct lcdif_drm_private, info);
+
+ lcdif_disable_controller(lcdif);
+}
+
+static struct fb_ops lcdif_fb_ops = {
+ .fb_enable = lcdif_enable_fb_controller,
+ .fb_disable = lcdif_disable_fb_controller,
+ .fb_flush = lcdif_crtc_atomic_flush,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+static struct fb_bitfield red = { .offset = 16, .length = 8, };
+static struct fb_bitfield green = { .offset = 8, .length = 8, };
+static struct fb_bitfield blue = { .offset = 0, .length = 8, };
+static struct fb_bitfield transp = { .offset = 24, .length = 8, };
+
+static int lcdif_register_fb(struct lcdif_drm_private *lcdif)
+{
+ struct fb_info *info = &lcdif->info;
+ u32 xmax = 0, ymax = 0;
+ int i, ret;
+
+ info->fbops = &lcdif_fb_ops;
+ info->bits_per_pixel = 32;
+ info->red = red;
+ info->green = green;
+ info->blue = blue;
+ info->transp = transp;
+ info->dev.parent = lcdif->dev;
+
+ ret = vpl_ioctl(&lcdif->vpl, 0, VPL_GET_VIDEOMODES, &info->modes);
+ if (ret) {
+ dev_err(lcdif->dev, "failed to get modes: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ if (info->modes.num_modes) {
+ for (i = 0; i < info->modes.num_modes; i++) {
+ xmax = max(xmax, info->modes.modes[i].xres);
+ ymax = max(ymax, info->modes.modes[i].yres);
+ }
+ info->xres = info->modes.modes[info->modes.native_mode].xres;
+ info->yres = info->modes.modes[info->modes.native_mode].yres;
+ } else {
+ dev_notice(lcdif->dev, "no modes found on lcdif%d\n", lcdif->id);
+ xmax = info->xres = 640;
+ ymax = info->yres = 480;
+ }
+
+ lcdif->line_length = xmax * (info->bits_per_pixel >> 3);
+ lcdif->max_yres = ymax;
+
+ info->line_length = lcdif->line_length;
+ info->screen_base = dma_alloc_writecombine(DMA_DEVICE_BROKEN,
+ info->line_length * lcdif->max_yres,
+ &lcdif->paddr);
+
+ if (!info->screen_base)
+ return -ENOMEM;
+
+ ret = register_framebuffer(info);
+ if (ret)
+ return ret;
+
+ dev_info(lcdif->dev, "Registered %s on LCDIF%d, type primary\n",
+ info->cdev.name, lcdif->id);
+
+ return 0;
+}
+
+int lcdif_kms_init(struct lcdif_drm_private *lcdif)
+{
+ struct device *dev;
+ struct device_node *port;
+ struct device_node *ep;
+ struct of_endpoint endpoint;
+ int ret;
+
+ dev = lcdif->dev;
+
+ port = of_graph_get_port_by_id(dev->of_node, 0);
+ if (!port) {
+ dev_err(lcdif->dev, "no port node found for video_port0\n");
+ return -ENOENT;
+ }
+
+ for_each_child_of_node(port, ep) {
+ of_graph_parse_endpoint(ep, &endpoint);
+ lcdif->crtc_endpoint_id = endpoint.id;
+ }
+
+ lcdif->port = port;
+ lcdif->vpl.node = dev->of_node;
+
+ ret = vpl_register(&lcdif->vpl);
+ if (ret)
+ return ret;
+
+ lcdif_register_fb(lcdif);
+
+ return 0;
+}
diff --git a/drivers/video/lcdif_regs.h b/drivers/video/lcdif_regs.h
new file mode 100644
index 0000000000000000000000000000000000000000..91ef697f494d580957f4ef43c0e675c4cdca92e4
--- /dev/null
+++ b/drivers/video/lcdif_regs.h
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022 Marek Vasut <marex@denx.de>
+ *
+ * i.MX8MP/i.MXRT LCDIF LCD controller driver.
+ */
+
+#ifndef __LCDIF_REGS_H__
+#define __LCDIF_REGS_H__
+
+#define REG_SET 4
+#define REG_CLR 8
+
+/* V8 register set */
+#define LCDC_V8_CTRL 0x00
+#define LCDC_V8_DISP_PARA 0x10
+#define LCDC_V8_DISP_SIZE 0x14
+#define LCDC_V8_HSYN_PARA 0x18
+#define LCDC_V8_VSYN_PARA 0x1c
+#define LCDC_V8_VSYN_HSYN_WIDTH 0x20
+#define LCDC_V8_INT_STATUS_D0 0x24
+#define LCDC_V8_INT_ENABLE_D0 0x28
+#define LCDC_V8_INT_STATUS_D1 0x30
+#define LCDC_V8_INT_ENABLE_D1 0x34
+#define LCDC_V8_CTRLDESCL0_1 0x200
+#define LCDC_V8_CTRLDESCL0_3 0x208
+#define LCDC_V8_CTRLDESCL_LOW0_4 0x20c
+#define LCDC_V8_CTRLDESCL_HIGH0_4 0x210
+#define LCDC_V8_CTRLDESCL0_5 0x214
+#define LCDC_V8_CSC0_CTRL 0x21c
+#define LCDC_V8_CSC0_COEF0 0x220
+#define LCDC_V8_CSC0_COEF1 0x224
+#define LCDC_V8_CSC0_COEF2 0x228
+#define LCDC_V8_CSC0_COEF3 0x22c
+#define LCDC_V8_CSC0_COEF4 0x230
+#define LCDC_V8_CSC0_COEF5 0x234
+#define LCDC_V8_PANIC0_THRES 0x238
+
+#define CTRL_SFTRST BIT(31)
+#define CTRL_CLKGATE BIT(30)
+#define CTRL_BYPASS_COUNT BIT(19)
+#define CTRL_VSYNC_MODE BIT(18)
+#define CTRL_DOTCLK_MODE BIT(17)
+#define CTRL_DATA_SELECT BIT(16)
+#define CTRL_BUS_WIDTH_16 (0 << 10)
+#define CTRL_BUS_WIDTH_8 (1 << 10)
+#define CTRL_BUS_WIDTH_18 (2 << 10)
+#define CTRL_BUS_WIDTH_24 (3 << 10)
+#define CTRL_BUS_WIDTH_MASK (0x3 << 10)
+#define CTRL_WORD_LENGTH_16 (0 << 8)
+#define CTRL_WORD_LENGTH_8 (1 << 8)
+#define CTRL_WORD_LENGTH_18 (2 << 8)
+#define CTRL_WORD_LENGTH_24 (3 << 8)
+#define CTRL_MASTER BIT(5)
+#define CTRL_DF16 BIT(3)
+#define CTRL_DF18 BIT(2)
+#define CTRL_DF24 BIT(1)
+#define CTRL_RUN BIT(0)
+
+#define CTRL1_RECOVER_ON_UNDERFLOW BIT(24)
+#define CTRL1_FIFO_CLEAR BIT(21)
+#define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16)
+#define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf)
+#define CTRL1_CUR_FRAME_DONE_IRQ_EN BIT(13)
+#define CTRL1_CUR_FRAME_DONE_IRQ BIT(9)
+
+#define CTRL2_SET_OUTSTANDING_REQS_1 0
+#define CTRL2_SET_OUTSTANDING_REQS_2 (0x1 << 21)
+#define CTRL2_SET_OUTSTANDING_REQS_4 (0x2 << 21)
+#define CTRL2_SET_OUTSTANDING_REQS_8 (0x3 << 21)
+#define CTRL2_SET_OUTSTANDING_REQS_16 (0x4 << 21)
+#define CTRL2_SET_OUTSTANDING_REQS_MASK (0x7 << 21)
+
+#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16)
+#define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff)
+#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff)
+#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff)
+
+#define VDCTRL0_ENABLE_PRESENT BIT(28)
+#define VDCTRL0_VSYNC_ACT_HIGH BIT(27)
+#define VDCTRL0_HSYNC_ACT_HIGH BIT(26)
+#define VDCTRL0_DOTCLK_ACT_FALLING BIT(25)
+#define VDCTRL0_ENABLE_ACT_HIGH BIT(24)
+#define VDCTRL0_VSYNC_PERIOD_UNIT BIT(21)
+#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT BIT(20)
+#define VDCTRL0_HALF_LINE BIT(19)
+#define VDCTRL0_HALF_LINE_MODE BIT(18)
+#define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
+#define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff)
+
+#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
+#define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff)
+
+#define VDCTRL3_MUX_SYNC_SIGNALS BIT(29)
+#define VDCTRL3_VSYNC_ONLY BIT(28)
+#define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16)
+#define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff)
+#define SET_VERT_WAIT_CNT(x) ((x) & 0xffff)
+#define GET_VERT_WAIT_CNT(x) ((x) & 0xffff)
+
+#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */
+#define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */
+#define VDCTRL4_SYNC_SIGNALS_ON BIT(18)
+#define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff)
+
+#define DEBUG0_HSYNC BIT(26)
+#define DEBUG0_VSYNC BIT(25)
+
+#define AS_CTRL_PS_DISABLE BIT(23)
+#define AS_CTRL_ALPHA_INVERT BIT(20)
+#define AS_CTRL_ALPHA(a) (((a) & 0xff) << 8)
+#define AS_CTRL_FORMAT_RGB565 (0xe << 4)
+#define AS_CTRL_FORMAT_RGB444 (0xd << 4)
+#define AS_CTRL_FORMAT_RGB555 (0xc << 4)
+#define AS_CTRL_FORMAT_ARGB4444 (0x9 << 4)
+#define AS_CTRL_FORMAT_ARGB1555 (0x8 << 4)
+#define AS_CTRL_FORMAT_RGB888 (0x4 << 4)
+#define AS_CTRL_FORMAT_ARGB8888 (0x0 << 4)
+#define AS_CTRL_ENABLE_COLORKEY BIT(3)
+#define AS_CTRL_ALPHA_CTRL_ROP (3 << 1)
+#define AS_CTRL_ALPHA_CTRL_MULTIPLY (2 << 1)
+#define AS_CTRL_ALPHA_CTRL_OVERRIDE (1 << 1)
+#define AS_CTRL_ALPHA_CTRL_EMBEDDED (0 << 1)
+#define AS_CTRL_AS_ENABLE BIT(0)
+
+/* V8 register set */
+#define CTRL_SW_RESET BIT(31)
+#define CTRL_FETCH_START_OPTION_FPV 0
+#define CTRL_FETCH_START_OPTION_PWV BIT(8)
+#define CTRL_FETCH_START_OPTION_BPV BIT(9)
+#define CTRL_FETCH_START_OPTION_RESV GENMASK(9, 8)
+#define CTRL_FETCH_START_OPTION_MASK GENMASK(9, 8)
+#define CTRL_NEG BIT(4)
+#define CTRL_INV_PXCK BIT(3)
+#define CTRL_INV_DE BIT(2)
+#define CTRL_INV_VS BIT(1)
+#define CTRL_INV_HS BIT(0)
+
+#define DISP_PARA_DISP_ON BIT(31)
+#define DISP_PARA_SWAP_EN BIT(30)
+#define DISP_PARA_LINE_PATTERN_UYVY_H (0xd << 26)
+#define DISP_PARA_LINE_PATTERN_RGB565 (0x7 << 26)
+#define DISP_PARA_LINE_PATTERN_BGR888 (0x5 << 26)
+#define DISP_PARA_LINE_PATTERN_RGB888 (0x0 << 26)
+#define DISP_PARA_LINE_PATTERN_MASK GENMASK(29, 26)
+#define DISP_PARA_DISP_MODE_MASK GENMASK(25, 24)
+#define DISP_PARA_BGND_R_MASK GENMASK(23, 16)
+#define DISP_PARA_BGND_G_MASK GENMASK(15, 8)
+#define DISP_PARA_BGND_B_MASK GENMASK(7, 0)
+
+#define DISP_SIZE_DELTA_Y(n) (((n) & 0xffff) << 16)
+#define DISP_SIZE_DELTA_Y_MASK GENMASK(31, 16)
+#define DISP_SIZE_DELTA_X(n) ((n) & 0xffff)
+#define DISP_SIZE_DELTA_X_MASK GENMASK(15, 0)
+
+#define HSYN_PARA_BP_H(n) (((n) & 0xffff) << 16)
+#define HSYN_PARA_BP_H_MASK GENMASK(31, 16)
+#define HSYN_PARA_FP_H(n) ((n) & 0xffff)
+#define HSYN_PARA_FP_H_MASK GENMASK(15, 0)
+
+#define VSYN_PARA_BP_V(n) (((n) & 0xffff) << 16)
+#define VSYN_PARA_BP_V_MASK GENMASK(31, 16)
+#define VSYN_PARA_FP_V(n) ((n) & 0xffff)
+#define VSYN_PARA_FP_V_MASK GENMASK(15, 0)
+
+#define VSYN_HSYN_WIDTH_PW_V(n) (((n) & 0xffff) << 16)
+#define VSYN_HSYN_WIDTH_PW_V_MASK GENMASK(31, 16)
+#define VSYN_HSYN_WIDTH_PW_H(n) ((n) & 0xffff)
+#define VSYN_HSYN_WIDTH_PW_H_MASK GENMASK(15, 0)
+
+#define INT_STATUS_D0_FIFO_EMPTY BIT(24)
+#define INT_STATUS_D0_DMA_DONE BIT(16)
+#define INT_STATUS_D0_DMA_ERR BIT(8)
+#define INT_STATUS_D0_VS_BLANK BIT(2)
+#define INT_STATUS_D0_UNDERRUN BIT(1)
+#define INT_STATUS_D0_VSYNC BIT(0)
+
+#define INT_ENABLE_D0_FIFO_EMPTY_EN BIT(24)
+#define INT_ENABLE_D0_DMA_DONE_EN BIT(16)
+#define INT_ENABLE_D0_DMA_ERR_EN BIT(8)
+#define INT_ENABLE_D0_VS_BLANK_EN BIT(2)
+#define INT_ENABLE_D0_UNDERRUN_EN BIT(1)
+#define INT_ENABLE_D0_VSYNC_EN BIT(0)
+
+#define INT_STATUS_D1_PLANE_PANIC BIT(0)
+
+#define INT_ENABLE_D1_PLANE_PANIC_EN BIT(0)
+
+#define CTRLDESCL0_1_HEIGHT(n) (((n) & 0xffff) << 16)
+#define CTRLDESCL0_1_HEIGHT_MASK GENMASK(31, 16)
+#define CTRLDESCL0_1_WIDTH(n) ((n) & 0xffff)
+#define CTRLDESCL0_1_WIDTH_MASK GENMASK(15, 0)
+
+#define CTRLDESCL0_3_P_SIZE(n) (((n) << 20) & CTRLDESCL0_3_P_SIZE_MASK)
+#define CTRLDESCL0_3_P_SIZE_MASK GENMASK(22, 20)
+#define CTRLDESCL0_3_T_SIZE(n) (((n) << 16) & CTRLDESCL0_3_T_SIZE_MASK)
+#define CTRLDESCL0_3_T_SIZE_MASK GENMASK(17, 16)
+#define CTRLDESCL0_3_PITCH(n) ((n) & 0xffff)
+#define CTRLDESCL0_3_PITCH_MASK GENMASK(15, 0)
+
+#define CTRLDESCL_HIGH0_4_ADDR_HIGH(n) ((n) & 0xf)
+#define CTRLDESCL_HIGH0_4_ADDR_HIGH_MASK GENMASK(3, 0)
+
+#define CTRLDESCL0_5_EN BIT(31)
+#define CTRLDESCL0_5_SHADOW_LOAD_EN BIT(30)
+#define CTRLDESCL0_5_BPP_16_RGB565 (0x4 << 24)
+#define CTRLDESCL0_5_BPP_16_ARGB1555 (0x5 << 24)
+#define CTRLDESCL0_5_BPP_16_ARGB4444 (0x6 << 24)
+#define CTRLDESCL0_5_BPP_YCbCr422 (0x7 << 24)
+#define CTRLDESCL0_5_BPP_24_RGB888 (0x8 << 24)
+#define CTRLDESCL0_5_BPP_32_ARGB8888 (0x9 << 24)
+#define CTRLDESCL0_5_BPP_32_ABGR8888 (0xa << 24)
+#define CTRLDESCL0_5_BPP_MASK GENMASK(27, 24)
+#define CTRLDESCL0_5_YUV_FORMAT_Y2VY1U (0x0 << 14)
+#define CTRLDESCL0_5_YUV_FORMAT_Y2UY1V (0x1 << 14)
+#define CTRLDESCL0_5_YUV_FORMAT_VY2UY1 (0x2 << 14)
+#define CTRLDESCL0_5_YUV_FORMAT_UY2VY1 (0x3 << 14)
+#define CTRLDESCL0_5_YUV_FORMAT_MASK GENMASK(15, 14)
+
+#define CSC0_CTRL_CSC_MODE_YUV2RGB (0x0 << 1)
+#define CSC0_CTRL_CSC_MODE_YCbCr2RGB (0x1 << 1)
+#define CSC0_CTRL_CSC_MODE_RGB2YUV (0x2 << 1)
+#define CSC0_CTRL_CSC_MODE_RGB2YCbCr (0x3 << 1)
+#define CSC0_CTRL_CSC_MODE_MASK GENMASK(2, 1)
+#define CSC0_CTRL_BYPASS BIT(0)
+
+#define CSC0_COEF0_A2(n) (((n) << 16) & CSC0_COEF0_A2_MASK)
+#define CSC0_COEF0_A2_MASK GENMASK(26, 16)
+#define CSC0_COEF0_A1(n) ((n) & CSC0_COEF0_A1_MASK)
+#define CSC0_COEF0_A1_MASK GENMASK(10, 0)
+
+#define CSC0_COEF1_B1(n) (((n) << 16) & CSC0_COEF1_B1_MASK)
+#define CSC0_COEF1_B1_MASK GENMASK(26, 16)
+#define CSC0_COEF1_A3(n) ((n) & CSC0_COEF1_A3_MASK)
+#define CSC0_COEF1_A3_MASK GENMASK(10, 0)
+
+#define CSC0_COEF2_B3(n) (((n) << 16) & CSC0_COEF2_B3_MASK)
+#define CSC0_COEF2_B3_MASK GENMASK(26, 16)
+#define CSC0_COEF2_B2(n) ((n) & CSC0_COEF2_B2_MASK)
+#define CSC0_COEF2_B2_MASK GENMASK(10, 0)
+
+#define CSC0_COEF3_C2(n) (((n) << 16) & CSC0_COEF3_C2_MASK)
+#define CSC0_COEF3_C2_MASK GENMASK(26, 16)
+#define CSC0_COEF3_C1(n) ((n) & CSC0_COEF3_C1_MASK)
+#define CSC0_COEF3_C1_MASK GENMASK(10, 0)
+
+#define CSC0_COEF4_D1(n) (((n) << 16) & CSC0_COEF4_D1_MASK)
+#define CSC0_COEF4_D1_MASK GENMASK(24, 16)
+#define CSC0_COEF4_C3(n) ((n) & CSC0_COEF4_C3_MASK)
+#define CSC0_COEF4_C3_MASK GENMASK(10, 0)
+
+#define CSC0_COEF5_D3(n) (((n) << 16) & CSC0_COEF5_D3_MASK)
+#define CSC0_COEF5_D3_MASK GENMASK(24, 16)
+#define CSC0_COEF5_D2(n) ((n) & CSC0_COEF5_D2_MASK)
+#define CSC0_COEF5_D2_MASK GENMASK(8, 0)
+
+#define PANIC0_THRES_LOW_MASK GENMASK(24, 16)
+#define PANIC0_THRES_HIGH_MASK GENMASK(8, 0)
+#define PANIC0_THRES_MAX 511
+
+#define LCDIF_MIN_XRES 120
+#define LCDIF_MIN_YRES 120
+#define LCDIF_MAX_XRES 0xffff
+#define LCDIF_MAX_YRES 0xffff
+
+#endif /* __LCDIF_REGS_H__ */
---
base-commit: 0633a4db9d2351234b565b55978ce068b387eb2d
change-id: 20251113-imx-lcdif-e7915e9f0ed0
Best regards,
--
Michael Grzeschik <m.grzeschik@pengutronix.de>
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] video: lcdif_drv: add ported driver from linux
2025-11-13 8:55 [PATCH] video: lcdif_drv: add ported driver from linux Michael Grzeschik
@ 2025-11-14 13:14 ` Sascha Hauer
0 siblings, 0 replies; 2+ messages in thread
From: Sascha Hauer @ 2025-11-14 13:14 UTC (permalink / raw)
To: BAREBOX, Michael Grzeschik
On Thu, 13 Nov 2025 09:55:15 +0100, Michael Grzeschik wrote:
> This patch is adding support for the for the LCDIFv3 LCD controller
> found on i.MX8MP and i.MX93 SoCs.
>
> It is currently supporting DRM_FORMAT_XRGB8888 media_bus format for now.
>
> While porting from linux mainline the following changes were made:
>
> [...]
Applied, thanks!
[1/1] video: lcdif_drv: add ported driver from linux
https://git.pengutronix.de/cgit/barebox/commit/?id=e5e276142f5a (link may not be stable)
Best regards,
--
Sascha Hauer <s.hauer@pengutronix.de>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-11-14 13:15 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-13 8:55 [PATCH] video: lcdif_drv: add ported driver from linux Michael Grzeschik
2025-11-14 13:14 ` Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox