The MDP RDMA is the DMA engine reading the framebuffer on some
MediaTek SoCs, such as the MT8188. Two instances are used, each
reading one half of the frame; the second one starts at the middle of
the line (apply_offset).

Signed-off-by: Julien Stephan <[email protected]>
---
 drivers/video/mediatek/mtk_mdp_rdma.c | 185 ++++++++++++++++++++++++++++++++++
 drivers/video/mediatek/mtk_mdp_rdma.h |  34 +++++++
 2 files changed, 219 insertions(+)

diff --git a/drivers/video/mediatek/mtk_mdp_rdma.c 
b/drivers/video/mediatek/mtk_mdp_rdma.c
new file mode 100644
index 00000000000..4664663b586
--- /dev/null
+++ b/drivers/video/mediatek/mtk_mdp_rdma.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MediaTek Video MDP RDMA Support
+ *
+ * Copyright (c) 2026 BayLibre, SAS.
+ * Author: Julien Stephan <[email protected]>
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <video.h>
+
+#include "mtk_disp_comp.h"
+#include "mtk_mdp_rdma.h"
+
+/* MDP RDMA registers (see Linux drivers/gpu/drm/mediatek/mtk_mdp_rdma.c) */
+#define MDP_RDMA_EN                    0x000
+#define FLD_ROT_ENABLE                         BIT(0)
+#define MDP_RDMA_CON                   0x020
+#define FLD_SIMPLE_MODE                                BIT(4)
+#define MDP_RDMA_GMCIF_CON             0x028
+#define FLD_COMMAND_DIV                                BIT(0)
+#define FLD_EXT_PREULTRA_EN                    BIT(3)
+#define FLD_RD_REQ_TYPE                                GENMASK(7, 4)
+#define VAL_RD_REQ_TYPE_BURST_8_ACCESS         7
+#define FLD_ULTRA_EN                           GENMASK(13, 12)
+#define VAL_ULTRA_EN_ENABLE                    1
+#define FLD_PRE_ULTRA_EN                       GENMASK(17, 16)
+#define VAL_PRE_ULTRA_EN_ENABLE                        1
+#define FLD_EXT_ULTRA_EN                       BIT(18)
+#define MDP_RDMA_SRC_CON               0x030
+#define FLD_UNIFORM_CONFIG                     BIT(17)
+#define FLD_SRC_FORMAT                         GENMASK(3, 0)
+#define MDP_RDMA_COMP_CON              0x038
+#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE  0x060
+#define FLD_MF_BKGD_WB                         GENMASK(22, 0)
+#define MDP_RDMA_MF_SRC_SIZE           0x070
+#define FLD_MF_SRC_H                           GENMASK(30, 16)
+#define FLD_MF_SRC_W                           GENMASK(14, 0)
+#define MDP_RDMA_MF_CLIP_SIZE          0x078
+#define FLD_MF_CLIP_H                          GENMASK(30, 16)
+#define FLD_MF_CLIP_W                          GENMASK(14, 0)
+#define MDP_RDMA_SRC_OFFSET_0          0x118
+#define MDP_RDMA_TRANSFORM_0           0x200
+#define MDP_RDMA_SRC_BASE_0            0xf00
+
+/*
+ * Source format 2 is named after its byte order in memory, B, G, R, A, as
+ * the datasheet does. That is the same encoding as DRM_FORMAT_XRGB8888,
+ * which Linux' rdma_fmt_convert() maps to RDMA_INPUT_FORMAT_RGBA8888: its
+ * name only records that the alpha byte comes last, the R/B order being
+ * selected separately by FLD_SWAP (left clear here).
+ */
+#define MDP_RDMA_SRC_FORMAT_BGRA8888           2
+
+/*
+ * Fields used by this driver but not present in the upstream driver, named
+ * after the datasheet:
+ * - MDP_RDMA_EN[23:4] INTERNAL_DCM_EN: dynamic clock gating of the RDMA
+ *   internal blocks, all enabled out of reset (0x00fffff0)
+ * - MDP_RDMA_TRANSFORM_0[15] BITEXTEND_WITH_ZERO: extend the LSBs with zeros
+ *   when converting 8-bit data to 10-bit
+ */
+#define FLD_INTERNAL_DCM_EN                    GENMASK(23, 4)
+#define FLD_BITEXTEND_WITH_ZERO                        BIT(15)
+#define FB_BYTES_PER_PIXEL                     4
+
+/*
+ * DRAM request settings, as programmed by Linux'
+ * mtk_mdp_rdma_fifo_config(): 8-beat burst reads plus the ultra/pre-ultra
+ * high priority requests that keep the RDMA from underrunning at high pixel
+ * clocks.
+ */
+#define MDP_RDMA_GMCIF_CON_QOS \
+       (FLD_COMMAND_DIV | FLD_EXT_PREULTRA_EN | FLD_EXT_ULTRA_EN | \
+        FIELD_PREP(FLD_RD_REQ_TYPE, VAL_RD_REQ_TYPE_BURST_8_ACCESS) | \
+        FIELD_PREP(FLD_ULTRA_EN, VAL_ULTRA_EN_ENABLE) | \
+        FIELD_PREP(FLD_PRE_ULTRA_EN, VAL_PRE_ULTRA_EN_ENABLE))
+
+int mtk_mdp_rdma_config(struct udevice *dev, struct video_uc_plat *plat,
+                       const struct display_timing *timing,
+                       unsigned int pitch, bool apply_offset)
+{
+       u32 width = timing->hactive.typ;
+       u32 height = timing->vactive.typ;
+       int offset = apply_offset ? ((width / 2) * FB_BYTES_PER_PIXEL) : 0;
+       u32 fb;
+
+       /* the RDMA source address register is 32-bit only */
+       if (plat->base != (u32)plat->base) {
+               dev_err(dev, "framebuffer base %#lx is above 4GB\n", 
plat->base);
+               return -EINVAL;
+       }
+       fb = plat->base;
+
+       /* the stride register only holds FLD_MF_BKGD_WB bits */
+       if (pitch > FLD_MF_BKGD_WB) {
+               dev_err(dev, "framebuffer pitch %u is too large\n", pitch);
+               return -EINVAL;
+       }
+
+       mtk_disp_comp_write(dev, MDP_RDMA_EN, FLD_INTERNAL_DCM_EN);
+
+       /* DRAM request type and ultra thresholds */
+       mtk_disp_comp_write(dev, MDP_RDMA_GMCIF_CON, MDP_RDMA_GMCIF_CON_QOS);
+
+       /*
+        * Set the source format. U-Boot's 32bpp framebuffer has no alpha
+        * channel and the mixer blends this layer with alpha disabled, so
+        * FLD_OUTPUT_ARGB is left clear as Linux does for such a source.
+        */
+       mtk_disp_comp_write(dev, MDP_RDMA_SRC_CON,
+                           FIELD_PREP(FLD_SRC_FORMAT,
+                                      MDP_RDMA_SRC_FORMAT_BGRA8888) |
+                           FLD_UNIFORM_CONFIG);
+
+       /* no framebuffer compression */
+       mtk_disp_comp_write(dev, MDP_RDMA_COMP_CON, 0x0);
+
+       /* set base address */
+       mtk_disp_comp_write(dev, MDP_RDMA_SRC_BASE_0, fb);
+
+       /* stride comes from the framebuffer line length */
+       mtk_disp_comp_write(dev, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE,
+                           FIELD_PREP(FLD_MF_BKGD_WB, pitch));
+
+       /*
+        * each of the two RDMAs reads one half of the frame: size and
+        * clip cover half the width, and the second RDMA (apply_offset)
+        * starts at the middle of the line
+        */
+       mtk_disp_comp_write(dev, MDP_RDMA_MF_SRC_SIZE,
+                           FIELD_PREP(FLD_MF_SRC_H, height) |
+                           FIELD_PREP(FLD_MF_SRC_W, width / 2));
+       mtk_disp_comp_write(dev, MDP_RDMA_MF_CLIP_SIZE,
+                           FIELD_PREP(FLD_MF_CLIP_H, height) |
+                           FIELD_PREP(FLD_MF_CLIP_W, width / 2));
+       mtk_disp_comp_write(dev, MDP_RDMA_SRC_OFFSET_0, offset);
+
+       /*
+        * Simple mode, plus the downstream way of feeding the 10-bit
+        * pipeline from 8-bit data: BITEXTEND_WITH_ZERO zero-extends each
+        * component, instead of MDP_RDMA_CON's FLD_OUTPUT_10B used by Linux.
+        * Only one of the two is needed; do not switch to FLD_OUTPUT_10B
+        * when re-syncing with Linux.
+        */
+       mtk_disp_comp_write(dev, MDP_RDMA_CON, FLD_SIMPLE_MODE);
+       mtk_disp_comp_write(dev, MDP_RDMA_TRANSFORM_0,
+                           FLD_BITEXTEND_WITH_ZERO);
+
+       /* enable engine */
+       mtk_disp_comp_write(dev, MDP_RDMA_EN,
+                           FLD_INTERNAL_DCM_EN | FLD_ROT_ENABLE);
+
+       return 0;
+}
+
+static int mtk_mdp_rdma_probe(struct udevice *dev)
+{
+       int ret;
+
+       ret = mtk_disp_comp_probe(dev);
+       if (ret)
+               return ret;
+
+       /* the RDMA is a DMA master: it feeds from an SMI larb */
+       return mtk_disp_comp_get_larb(dev);
+}
+
+static const struct udevice_id mtk_mdp_rdma_ids[] = {
+       { .compatible = "mediatek,mt8195-vdo1-rdma" },
+       { }
+};
+
+U_BOOT_DRIVER(mtk_mdp_rdma) = {
+       .name      = "mtk_mdp_rdma",
+       .id        = UCLASS_MISC,
+       .of_match  = mtk_mdp_rdma_ids,
+       .probe     = mtk_mdp_rdma_probe,
+       .priv_auto = sizeof(struct mtk_disp_comp_priv),
+};
diff --git a/drivers/video/mediatek/mtk_mdp_rdma.h 
b/drivers/video/mediatek/mtk_mdp_rdma.h
new file mode 100644
index 00000000000..832cf8133c5
--- /dev/null
+++ b/drivers/video/mediatek/mtk_mdp_rdma.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * MediaTek Video MDP RDMA Support
+ *
+ * Copyright (c) 2026 BayLibre, SAS.
+ * Author: Julien Stephan <[email protected]>
+ */
+
+#ifndef _MTK_MDP_RDMA_H
+#define _MTK_MDP_RDMA_H
+
+struct udevice;
+struct video_uc_plat;
+struct display_timing;
+
+/**
+ * mtk_mdp_rdma_config() - configure an RDMA to read one half of the frame
+ * @dev: RDMA device
+ * @plat: video uclass platform data, holding the framebuffer base address
+ * @timing: display timing, giving the frame size
+ * @pitch: framebuffer line length, in bytes
+ * @apply_offset: read the right half of the frame instead of the left one
+ *
+ * Each of the two RDMAs of the pipeline reads one half of the XRGB8888
+ * framebuffer and feeds it to the mixer.
+ *
+ * Return: 0 on success, -EINVAL if the framebuffer is above 4 GiB or its
+ * pitch is too large
+ */
+int mtk_mdp_rdma_config(struct udevice *dev, struct video_uc_plat *plat,
+                       const struct display_timing *timing,
+                       unsigned int pitch, bool apply_offset);
+
+#endif /* _MTK_MDP_RDMA_H */

-- 
2.55.0

Reply via email to