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 | 213 ++++++++++++++++++++++++++++++++++
 drivers/video/mediatek/mtk_mdp_rdma.h |  20 ++++
 2 files changed, 233 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..21e0f8ffca5
--- /dev/null
+++ b/drivers/video/mediatek/mtk_mdp_rdma.c
@@ -0,0 +1,213 @@
+// 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 <dm/ofnode.h>
+#include <dt-bindings/memory/mtk-memory-port.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <linux/bitfield.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/values used by this driver but not present in the upstream driver */
+#define FLD_INTERNAL_DCM_EN                    GENMASK(23, 4)
+#define FLD_BITEXTEND_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: TRANSFORM_0 bit 15 zero-extends each
+        * component instead of MDP_RDMA_CON's FLD_OUTPUT_10B used by Linux.
+        */
+       mtk_disp_comp_write(dev, MDP_RDMA_CON, FLD_SIMPLE_MODE);
+       mtk_disp_comp_write(dev, MDP_RDMA_TRANSFORM_0, FLD_BITEXTEND_ZERO);
+
+       /* enable engine */
+       mtk_disp_comp_write(dev, MDP_RDMA_EN,
+                           FLD_INTERNAL_DCM_EN | FLD_ROT_ENABLE);
+
+       return 0;
+}
+
+/*
+ * The RDMA is the DMA master, so the SMI larb feeding it must be ungated for
+ * its DRAM access. The RDMA points at its IOMMU (M4U) port through "iommus";
+ * MTK_M4U_TO_LARB() extracts the larb id from that port. Resolve the matching
+ * larb in the IOMMU's "mediatek,larbs" list by its "mediatek,larb-id"; the
+ * component helper then enables and disables it together with the RDMA.
+ */
+static int mtk_mdp_rdma_get_larb(struct udevice *dev)
+{
+       struct mtk_disp_comp_priv *priv = dev_get_priv(dev);
+       struct ofnode_phandle_args args;
+       ofnode iommu, node;
+       u32 larb_id, id;
+       int i, ret;
+
+       ret = dev_read_phandle_with_args(dev, "iommus", "#iommu-cells", 0, 0,
+                                        &args);
+       if (ret) {
+               dev_err(dev, "cannot parse iommus: %d\n", ret);
+               return ret;
+       }
+       iommu = args.node;
+       larb_id = MTK_M4U_TO_LARB(args.args[0]);
+
+       for (i = 0; ; i++) {
+               node = ofnode_parse_phandle(iommu, "mediatek,larbs", i);
+               if (!ofnode_valid(node)) {
+                       dev_err(dev, "no larb %u in iommu\n", larb_id);
+                       return -ENODEV;
+               }
+               if (!ofnode_read_u32(node, "mediatek,larb-id", &id) &&
+                   id == larb_id)
+                       break;
+       }
+
+       return uclass_get_device_by_ofnode(UCLASS_MEMORY, node, &priv->larb);
+}
+
+static int mtk_mdp_rdma_probe(struct udevice *dev)
+{
+       int ret;
+
+       ret = mtk_disp_comp_probe(dev);
+       if (ret)
+               return ret;
+
+       return mtk_mdp_rdma_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..ceeaa8df3ac
--- /dev/null
+++ b/drivers/video/mediatek/mtk_mdp_rdma.h
@@ -0,0 +1,20 @@
+/* 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;
+
+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