1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00
openmptcprouter/6.12/target/linux/bcm27xx/patches-6.12/950-0367-drm-Add-RP1-DPI-driver.patch
Ycarus (Yannick Chabanois) bdb9b0046f Add bcm27xx 6.12 test support
2024-12-20 14:17:26 +01:00

1595 lines
62 KiB
Diff

From a01ae3437e5dd10e82112976f3ae78284a1013a0 Mon Sep 17 00:00:00 2001
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Date: Tue, 19 Sep 2023 17:51:49 +0100
Subject: [PATCH 367/697] drm: Add RP1 DPI driver
Add support for the RP1 DPI hardware.
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
drm/rp1: depends on, instead of select, MFD_RP1
According to kconfig-language.txt [1], select should be used only for
"non-visible symbols ... and for symbols with no dependencies". Since
MFD_RP1 both is visible and has a dependency, "select" should not be
used and "depends on" should be used instead.
In particular, this fixes the build of this kernel tree on NixOS, where
its kernel config system will try to answer 'M' to as many config as
possible.
[1] https://www.kernel.org/doc/html/latest/kbuild/kconfig-language.html
Signed-off-by: Ratchanan Srirattanamet <peathot@hotmail.com>
drm: rp1: VEC and DPI drivers: Fix bug #5901
Rework probe() to use devm_drm_dev_alloc(), embedding the DRM
device in the DPI or VEC device as now seems to be recommended.
Change order of resource allocation and driver initialization.
This prevents it trying to write to an unmapped register during
clean-up, which previously could crash.
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
drm: rp1: dpi: Add support for MEDIA_BUS_FMT_RGB565_1X24_CPADHI
This new format corresponds to the Raspberry Pi legacy DPI mode 3.
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
drm: rp1: rp1-dpi: Add DRM_FORMAT_ARGB8888 and DRM_FORMAT_ABGR8888
Android requires this.
As the underlying hardware doesn't support alpha blending,
we ignore the alpha value.
Signed-off-by: Jan Kehren <jan.kehren@emteria.com>
---
drivers/gpu/drm/rp1/rp1-dpi/Kconfig | 11 +
drivers/gpu/drm/rp1/rp1-dpi/Makefile | 5 +
drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 415 ++++++++++++++++++
drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 69 +++
drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c | 510 ++++++++++++++++++++++
drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 504 +++++++++++++++++++++
6 files changed, 1514 insertions(+)
create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Kconfig
create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Makefile
create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
--- /dev/null
+++ b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DRM_RP1_DPI
+ tristate "DRM Support for RP1 DPI"
+ depends on DRM && MFD_RP1
+ select DRM_GEM_DMA_HELPER
+ select DRM_KMS_HELPER
+ select DRM_VRAM_HELPER
+ select DRM_TTM
+ select DRM_TTM_HELPER
+ help
+ Choose this option to enable Video Out on RP1
--- /dev/null
+++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o
+
+obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o
--- /dev/null
+++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DRM Driver for DPI output on Raspberry Pi RP1
+ *
+ * Copyright (c) 2023 Raspberry Pi Limited.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/printk.h>
+#include <linux/console.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/cred.h>
+#include <linux/media-bus-format.h>
+#include <linux/pinctrl/consumer.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_mm.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fbdev_ttm.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_of.h>
+
+#include "rp1_dpi.h"
+
+/*
+ * Default bus format, where not specified by a connector/bridge
+ * and not overridden by the OF property "default_bus_fmt".
+ * This value is for compatibility with vc4 and VGA666-style boards,
+ * even though RP1 hardware cannot achieve the full 18-bit depth
+ * with that pinout (MEDIA_BUS_FMT_RGB666_1X24_CPADHI is preferred).
+ */
+static unsigned int default_bus_fmt = MEDIA_BUS_FMT_RGB666_1X18;
+module_param(default_bus_fmt, uint, 0644);
+
+/* -------------------------------------------------------------- */
+
+static void rp1dpi_pipe_update(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *old_state)
+{
+ struct drm_pending_vblank_event *event;
+ unsigned long flags;
+ struct drm_framebuffer *fb = pipe->plane.state->fb;
+ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
+ struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
+ struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
+ bool can_update = fb && dma_obj && dpi && dpi->pipe_enabled;
+
+ /* (Re-)start DPI-DMA where required; and update FB address */
+ if (can_update) {
+ if (!dpi->dpi_running || fb->format->format != dpi->cur_fmt) {
+ if (dpi->dpi_running &&
+ fb->format->format != dpi->cur_fmt) {
+ rp1dpi_hw_stop(dpi);
+ dpi->dpi_running = false;
+ }
+ if (!dpi->dpi_running) {
+ rp1dpi_hw_setup(dpi,
+ fb->format->format,
+ dpi->bus_fmt,
+ dpi->de_inv,
+ &pipe->crtc.state->mode);
+ dpi->dpi_running = true;
+ }
+ dpi->cur_fmt = fb->format->format;
+ drm_crtc_vblank_on(&pipe->crtc);
+ }
+ rp1dpi_hw_update(dpi, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
+ }
+
+ /* Arm VBLANK event (or call it immediately in some error cases) */
+ spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
+ event = pipe->crtc.state->event;
+ if (event) {
+ pipe->crtc.state->event = NULL;
+ if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
+ drm_crtc_arm_vblank_event(&pipe->crtc, event);
+ else
+ drm_crtc_send_vblank_event(&pipe->crtc, event);
+ }
+ spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
+}
+
+static void rp1dpi_pipe_enable(struct drm_simple_display_pipe *pipe,
+ struct drm_crtc_state *crtc_state,
+ struct drm_plane_state *plane_state)
+{
+ static const unsigned int M = 1000000;
+ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
+ struct drm_connector *conn;
+ struct drm_connector_list_iter conn_iter;
+ unsigned int fpix, fdiv, fvco;
+ int ret;
+
+ /* Look up the connector attached to DPI so we can get the
+ * bus_format. Ideally the bridge would tell us the
+ * bus_format we want, but it doesn't yet, so assume that it's
+ * uniform throughout the bridge chain.
+ */
+ dev_info(&dpi->pdev->dev, __func__);
+ drm_connector_list_iter_begin(pipe->encoder.dev, &conn_iter);
+ drm_for_each_connector_iter(conn, &conn_iter) {
+ if (conn->encoder == &pipe->encoder) {
+ dpi->de_inv = !!(conn->display_info.bus_flags &
+ DRM_BUS_FLAG_DE_LOW);
+ dpi->clk_inv = !!(conn->display_info.bus_flags &
+ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE);
+ if (conn->display_info.num_bus_formats)
+ dpi->bus_fmt = conn->display_info.bus_formats[0];
+ break;
+ }
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ /* Set DPI clock to desired frequency. Currently (experimentally)
+ * we take control of the VideoPLL, to ensure we can generate it
+ * accurately. NB: this prevents concurrent use of DPI and VEC!
+ * Magic numbers ensure the parent clock is within [100MHz, 200MHz]
+ * with VCO in [1GHz, 1.33GHz]. The initial divide is by 6, 8 or 10.
+ */
+ fpix = 1000 * pipe->crtc.state->mode.clock;
+ fpix = clamp(fpix, 1 * M, 200 * M);
+ fdiv = fpix;
+ while (fdiv < 100 * M)
+ fdiv *= 2;
+ fvco = fdiv * 2 * DIV_ROUND_UP(500 * M, fdiv);
+ ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLCORE], fvco);
+ if (ret)
+ dev_err(&dpi->pdev->dev, "Failed to set PLL VCO to %u (%d)", fvco, ret);
+ ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLDIV], fdiv);
+ if (ret)
+ dev_err(&dpi->pdev->dev, "Failed to set PLL output to %u (%d)", fdiv, ret);
+ ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_DPI], fpix);
+ if (ret)
+ dev_err(&dpi->pdev->dev, "Failed to set DPI clock to %u (%d)", fpix, ret);
+
+ rp1dpi_vidout_setup(dpi, dpi->clk_inv);
+ clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLCORE]);
+ clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLDIV]);
+ pinctrl_pm_select_default_state(&dpi->pdev->dev);
+ clk_prepare_enable(dpi->clocks[RP1DPI_CLK_DPI]);
+ dev_info(&dpi->pdev->dev, "Want %u /%u %u /%u %u; got VCO=%lu DIV=%lu DPI=%lu",
+ fvco, fvco / fdiv, fdiv, fdiv / fpix, fpix,
+ clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLCORE]),
+ clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLDIV]),
+ clk_get_rate(dpi->clocks[RP1DPI_CLK_DPI]));
+
+ /* Start DPI-DMA. pipe already has the new crtc and plane state. */
+ dpi->pipe_enabled = true;
+ dpi->cur_fmt = 0xdeadbeef;
+ rp1dpi_pipe_update(pipe, 0);
+}
+
+static void rp1dpi_pipe_disable(struct drm_simple_display_pipe *pipe)
+{
+ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
+
+ dev_info(&dpi->pdev->dev, __func__);
+ drm_crtc_vblank_off(&pipe->crtc);
+ if (dpi->dpi_running) {
+ rp1dpi_hw_stop(dpi);
+ dpi->dpi_running = false;
+ }
+ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
+ pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
+ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLDIV]);
+ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLCORE]);
+ dpi->pipe_enabled = false;
+}
+
+static int rp1dpi_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
+{
+ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
+
+ if (dpi)
+ rp1dpi_hw_vblank_ctrl(dpi, 1);
+
+ return 0;
+}
+
+static void rp1dpi_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
+{
+ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
+
+ if (dpi)
+ rp1dpi_hw_vblank_ctrl(dpi, 0);
+}
+
+static const struct drm_simple_display_pipe_funcs rp1dpi_pipe_funcs = {
+ .enable = rp1dpi_pipe_enable,
+ .update = rp1dpi_pipe_update,
+ .disable = rp1dpi_pipe_disable,
+ .enable_vblank = rp1dpi_pipe_enable_vblank,
+ .disable_vblank = rp1dpi_pipe_disable_vblank,
+};
+
+static const struct drm_mode_config_funcs rp1dpi_mode_funcs = {
+ .fb_create = drm_gem_fb_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void rp1dpi_stopall(struct drm_device *drm)
+{
+ if (drm->dev_private) {
+ struct rp1_dpi *dpi = drm->dev_private;
+
+ if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) {
+ rp1dpi_hw_stop(dpi);
+ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
+ dpi->dpi_running = false;
+ }
+ rp1dpi_vidout_poweroff(dpi);
+ pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
+ }
+}
+
+DEFINE_DRM_GEM_DMA_FOPS(rp1dpi_fops);
+
+static struct drm_driver rp1dpi_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+ .fops = &rp1dpi_fops,
+ .name = "drm-rp1-dpi",
+ .desc = "drm-rp1-dpi",
+ .date = "0",
+ .major = 1,
+ .minor = 0,
+ DRM_GEM_DMA_DRIVER_OPS,
+ .release = rp1dpi_stopall,
+};
+
+static const u32 rp1dpi_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_RGB565
+};
+
+static int rp1dpi_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rp1_dpi *dpi;
+ struct drm_bridge *bridge = NULL;
+ struct drm_panel *panel;
+ int i, ret;
+
+ dev_info(dev, __func__);
+ ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0,
+ &panel, &bridge);
+ if (ret) {
+ dev_info(dev, "%s: bridge not found\n", __func__);
+ return -EPROBE_DEFER;
+ }
+ if (panel) {
+ bridge = devm_drm_panel_bridge_add(dev, panel);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
+
+ dpi = devm_drm_dev_alloc(dev, &rp1dpi_driver, struct rp1_dpi, drm);
+ if (IS_ERR(dpi)) {
+ ret = PTR_ERR(dpi);
+ dev_err(dev, "%s devm_drm_dev_alloc %d", __func__, ret);
+ return ret;
+ }
+ dpi->pdev = pdev;
+
+ dpi->bus_fmt = default_bus_fmt;
+ ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt);
+
+ for (i = 0; i < RP1DPI_NUM_HW_BLOCKS; i++) {
+ dpi->hw_base[i] =
+ devm_ioremap_resource(dev,
+ platform_get_resource(dpi->pdev, IORESOURCE_MEM, i));
+ if (IS_ERR(dpi->hw_base[i])) {
+ dev_err(dev, "Error memory mapping regs[%d]\n", i);
+ return PTR_ERR(dpi->hw_base[i]);
+ }
+ }
+ ret = platform_get_irq(dpi->pdev, 0);
+ if (ret > 0)
+ ret = devm_request_irq(dev, ret, rp1dpi_hw_isr,
+ IRQF_SHARED, "rp1-dpi", dpi);
+ if (ret) {
+ dev_err(dev, "Unable to request interrupt\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < RP1DPI_NUM_CLOCKS; i++) {
+ static const char * const myclocknames[RP1DPI_NUM_CLOCKS] = {
+ "dpiclk", "plldiv", "pllcore"
+ };
+ dpi->clocks[i] = devm_clk_get(dev, myclocknames[i]);
+ if (IS_ERR(dpi->clocks[i])) {
+ dev_err(dev, "Unable to request clock %s\n", myclocknames[i]);
+ return PTR_ERR(dpi->clocks[i]);
+ }
+ }
+
+ ret = drmm_mode_config_init(&dpi->drm);
+ if (ret)
+ goto done_err;
+
+ /* Now we have all our resources, finish driver initialization */
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ init_completion(&dpi->finished);
+ dpi->drm.dev_private = dpi;
+ platform_set_drvdata(pdev, &dpi->drm);
+
+ dpi->drm.mode_config.max_width = 4096;
+ dpi->drm.mode_config.max_height = 4096;
+ dpi->drm.mode_config.preferred_depth = 32;
+ dpi->drm.mode_config.prefer_shadow = 0;
+ dpi->drm.mode_config.quirk_addfb_prefer_host_byte_order = true;
+ dpi->drm.mode_config.funcs = &rp1dpi_mode_funcs;
+ drm_vblank_init(&dpi->drm, 1);
+
+ ret = drm_simple_display_pipe_init(&dpi->drm,
+ &dpi->pipe,
+ &rp1dpi_pipe_funcs,
+ rp1dpi_formats,
+ ARRAY_SIZE(rp1dpi_formats),
+ NULL, NULL);
+ if (!ret)
+ ret = drm_simple_display_pipe_attach_bridge(&dpi->pipe, bridge);
+ if (ret)
+ goto done_err;
+
+ drm_mode_config_reset(&dpi->drm);
+
+ ret = drm_dev_register(&dpi->drm, 0);
+ if (ret)
+ return ret;
+
+ drm_fbdev_ttm_setup(&dpi->drm, 32);
+ return ret;
+
+done_err:
+ dev_err(dev, "%s fail %d\n", __func__, ret);
+ return ret;
+}
+
+static void rp1dpi_platform_remove(struct platform_device *pdev)
+{
+ struct drm_device *drm = platform_get_drvdata(pdev);
+
+ rp1dpi_stopall(drm);
+ drm_dev_unregister(drm);
+ drm_atomic_helper_shutdown(drm);
+ drm_dev_put(drm);
+}
+
+static void rp1dpi_platform_shutdown(struct platform_device *pdev)
+{
+ struct drm_device *drm = platform_get_drvdata(pdev);
+
+ rp1dpi_stopall(drm);
+}
+
+static const struct of_device_id rp1dpi_of_match[] = {
+ {
+ .compatible = "raspberrypi,rp1dpi",
+ },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, rp1dpi_of_match);
+
+static struct platform_driver rp1dpi_platform_driver = {
+ .probe = rp1dpi_platform_probe,
+ .remove = rp1dpi_platform_remove,
+ .shutdown = rp1dpi_platform_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = rp1dpi_of_match,
+ },
+};
+
+module_platform_driver(rp1dpi_platform_driver);
+
+MODULE_AUTHOR("Nick Hollinghurst");
+MODULE_DESCRIPTION("DRM driver for DPI output on Raspberry Pi RP1");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * DRM Driver for DSI output on Raspberry Pi RP1
+ *
+ * Copyright (c) 2023 Raspberry Pi Limited.
+ */
+
+#include <linux/types.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <drm/drm_device.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#define MODULE_NAME "drm-rp1-dpi"
+#define DRIVER_NAME "drm-rp1-dpi"
+
+/* ---------------------------------------------------------------------- */
+
+#define RP1DPI_HW_BLOCK_DPI 0
+#define RP1DPI_HW_BLOCK_CFG 1
+#define RP1DPI_NUM_HW_BLOCKS 2
+
+#define RP1DPI_CLK_DPI 0
+#define RP1DPI_CLK_PLLDIV 1
+#define RP1DPI_CLK_PLLCORE 2
+#define RP1DPI_NUM_CLOCKS 3
+
+/* ---------------------------------------------------------------------- */
+
+struct rp1_dpi {
+ /* DRM base and platform device pointer */
+ struct drm_device drm;
+ struct platform_device *pdev;
+
+ /* Framework and helper objects */
+ struct drm_simple_display_pipe pipe;
+ struct drm_connector connector;
+
+ /* Clocks: Video PLL, its primary divider, and DPI clock. */
+ struct clk *clocks[RP1DPI_NUM_CLOCKS];
+
+ /* Block (DPI, VOCFG) base addresses, and current state */
+ void __iomem *hw_base[RP1DPI_NUM_HW_BLOCKS];
+ u32 cur_fmt;
+ u32 bus_fmt;
+ bool de_inv, clk_inv;
+ bool dpi_running, pipe_enabled;
+ struct completion finished;
+};
+
+/* ---------------------------------------------------------------------- */
+/* Functions to control the DPI/DMA block */
+
+void rp1dpi_hw_setup(struct rp1_dpi *dpi,
+ u32 in_format,
+ u32 bus_format,
+ bool de_inv,
+ struct drm_display_mode const *mode);
+void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride);
+void rp1dpi_hw_stop(struct rp1_dpi *dpi);
+int rp1dpi_hw_busy(struct rp1_dpi *dpi);
+irqreturn_t rp1dpi_hw_isr(int irq, void *dev);
+void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable);
+
+/* ---------------------------------------------------------------------- */
+/* Functions to control the VIDEO OUT CFG block and check RP1 platform */
+
+void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge);
+void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi);
--- /dev/null
+++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DRM Driver for DPI output on Raspberry Pi RP1
+ *
+ * Copyright (c) 2023 Raspberry Pi Limited.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/rp1_platform.h>
+
+#include "rp1_dpi.h"
+
+// =============================================================================
+// Register : VIDEO_OUT_CFG_SEL
+// JTAG access : synchronous
+// Description : Selects source: VEC or DPI
+#define VIDEO_OUT_CFG_SEL_OFFSET 0x00000000
+#define VIDEO_OUT_CFG_SEL_BITS 0x00000013
+#define VIDEO_OUT_CFG_SEL_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_SEL_PCLK_INV
+// Description : Select dpi_pclk output port polarity inversion.
+#define VIDEO_OUT_CFG_SEL_PCLK_INV_RESET 0x0
+#define VIDEO_OUT_CFG_SEL_PCLK_INV_BITS 0x00000010
+#define VIDEO_OUT_CFG_SEL_PCLK_INV_MSB 4
+#define VIDEO_OUT_CFG_SEL_PCLK_INV_LSB 4
+#define VIDEO_OUT_CFG_SEL_PCLK_INV_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_SEL_PAD_MUX
+// Description : VEC 1 DPI 0
+#define VIDEO_OUT_CFG_SEL_PAD_MUX_RESET 0x0
+#define VIDEO_OUT_CFG_SEL_PAD_MUX_BITS 0x00000002
+#define VIDEO_OUT_CFG_SEL_PAD_MUX_MSB 1
+#define VIDEO_OUT_CFG_SEL_PAD_MUX_LSB 1
+#define VIDEO_OUT_CFG_SEL_PAD_MUX_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_SEL_VDAC_MUX
+// Description : VEC 1 DPI 0
+#define VIDEO_OUT_CFG_SEL_VDAC_MUX_RESET 0x0
+#define VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS 0x00000001
+#define VIDEO_OUT_CFG_SEL_VDAC_MUX_MSB 0
+#define VIDEO_OUT_CFG_SEL_VDAC_MUX_LSB 0
+#define VIDEO_OUT_CFG_SEL_VDAC_MUX_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_VDAC_CFG
+// JTAG access : synchronous
+// Description : Configure SNPS VDAC
+#define VIDEO_OUT_CFG_VDAC_CFG_OFFSET 0x00000004
+#define VIDEO_OUT_CFG_VDAC_CFG_BITS 0x1fffffff
+#define VIDEO_OUT_CFG_VDAC_CFG_RESET 0x0003ffff
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_ENCTR
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_RESET 0x0
+#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_BITS 0x1c000000
+#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_MSB 28
+#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_LSB 26
+#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_ENSC
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_RESET 0x0
+#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_BITS 0x03800000
+#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_MSB 25
+#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_LSB 23
+#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_ENDAC
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_RESET 0x0
+#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_BITS 0x00700000
+#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_MSB 22
+#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_LSB 20
+#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_ENVBG
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_RESET 0x0
+#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_BITS 0x00080000
+#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_MSB 19
+#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_LSB 19
+#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_RESET 0x0
+#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_BITS 0x00040000
+#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_MSB 18
+#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_LSB 18
+#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC2GC
+// Description : dac2 gain control
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_RESET 0x3f
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_BITS 0x0003f000
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_MSB 17
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_LSB 12
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC1GC
+// Description : dac1 gain control
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_RESET 0x3f
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_BITS 0x00000fc0
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_MSB 11
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_LSB 6
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC0GC
+// Description : dac0 gain control
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_RESET 0x3f
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_BITS 0x0000003f
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_MSB 5
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_LSB 0
+#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_VDAC_STATUS
+// JTAG access : synchronous
+// Description : Read VDAC status
+#define VIDEO_OUT_CFG_VDAC_STATUS_OFFSET 0x00000008
+#define VIDEO_OUT_CFG_VDAC_STATUS_BITS 0x00000017
+#define VIDEO_OUT_CFG_VDAC_STATUS_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_RESET 0x0
+#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_BITS 0x00000010
+#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_MSB 4
+#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_LSB 4
+#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT
+// Description : None
+#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_RESET "-"
+#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_BITS 0x00000007
+#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_MSB 2
+#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_LSB 0
+#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_ACCESS "RO"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_MEM_PD
+// JTAG access : synchronous
+// Description : Control memory power down
+#define VIDEO_OUT_CFG_MEM_PD_OFFSET 0x0000000c
+#define VIDEO_OUT_CFG_MEM_PD_BITS 0x00000003
+#define VIDEO_OUT_CFG_MEM_PD_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_MEM_PD_VEC
+// Description : None
+#define VIDEO_OUT_CFG_MEM_PD_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_MEM_PD_VEC_BITS 0x00000002
+#define VIDEO_OUT_CFG_MEM_PD_VEC_MSB 1
+#define VIDEO_OUT_CFG_MEM_PD_VEC_LSB 1
+#define VIDEO_OUT_CFG_MEM_PD_VEC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_MEM_PD_DPI
+// Description : None
+#define VIDEO_OUT_CFG_MEM_PD_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_MEM_PD_DPI_BITS 0x00000001
+#define VIDEO_OUT_CFG_MEM_PD_DPI_MSB 0
+#define VIDEO_OUT_CFG_MEM_PD_DPI_LSB 0
+#define VIDEO_OUT_CFG_MEM_PD_DPI_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_TEST_OVERRIDE
+// JTAG access : synchronous
+// Description : None
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_OFFSET 0x00000010
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_BITS 0xffffffff
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_PAD
+// Description : None
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_RESET 0x0
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_BITS 0x80000000
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_MSB 31
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_LSB 31
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC
+// Description : None
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_RESET 0x0
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS 0x40000000
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_MSB 30
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_LSB 30
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL
+// Description : None
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_RESET 0x00000000
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_BITS 0x3fffffff
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_MSB 29
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_LSB 0
+#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_INTR
+// JTAG access : synchronous
+// Description : Raw Interrupts
+#define VIDEO_OUT_CFG_INTR_OFFSET 0x00000014
+#define VIDEO_OUT_CFG_INTR_BITS 0x00000003
+#define VIDEO_OUT_CFG_INTR_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTR_DPI
+// Description : None
+#define VIDEO_OUT_CFG_INTR_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_INTR_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_INTR_DPI_MSB 1
+#define VIDEO_OUT_CFG_INTR_DPI_LSB 1
+#define VIDEO_OUT_CFG_INTR_DPI_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTR_VEC
+// Description : None
+#define VIDEO_OUT_CFG_INTR_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_INTR_VEC_BITS 0x00000001
+#define VIDEO_OUT_CFG_INTR_VEC_MSB 0
+#define VIDEO_OUT_CFG_INTR_VEC_LSB 0
+#define VIDEO_OUT_CFG_INTR_VEC_ACCESS "RO"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_INTE
+// JTAG access : synchronous
+// Description : Interrupt Enable
+#define VIDEO_OUT_CFG_INTE_OFFSET 0x00000018
+#define VIDEO_OUT_CFG_INTE_BITS 0x00000003
+#define VIDEO_OUT_CFG_INTE_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTE_DPI
+// Description : None
+#define VIDEO_OUT_CFG_INTE_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_INTE_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_INTE_DPI_MSB 1
+#define VIDEO_OUT_CFG_INTE_DPI_LSB 1
+#define VIDEO_OUT_CFG_INTE_DPI_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTE_VEC
+// Description : None
+#define VIDEO_OUT_CFG_INTE_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_INTE_VEC_BITS 0x00000001
+#define VIDEO_OUT_CFG_INTE_VEC_MSB 0
+#define VIDEO_OUT_CFG_INTE_VEC_LSB 0
+#define VIDEO_OUT_CFG_INTE_VEC_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_INTF
+// JTAG access : synchronous
+// Description : Interrupt Force
+#define VIDEO_OUT_CFG_INTF_OFFSET 0x0000001c
+#define VIDEO_OUT_CFG_INTF_BITS 0x00000003
+#define VIDEO_OUT_CFG_INTF_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTF_DPI
+// Description : None
+#define VIDEO_OUT_CFG_INTF_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_INTF_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_INTF_DPI_MSB 1
+#define VIDEO_OUT_CFG_INTF_DPI_LSB 1
+#define VIDEO_OUT_CFG_INTF_DPI_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTF_VEC
+// Description : None
+#define VIDEO_OUT_CFG_INTF_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_INTF_VEC_BITS 0x00000001
+#define VIDEO_OUT_CFG_INTF_VEC_MSB 0
+#define VIDEO_OUT_CFG_INTF_VEC_LSB 0
+#define VIDEO_OUT_CFG_INTF_VEC_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_INTS
+// JTAG access : synchronous
+// Description : Interrupt status after masking & forcing
+#define VIDEO_OUT_CFG_INTS_OFFSET 0x00000020
+#define VIDEO_OUT_CFG_INTS_BITS 0x00000003
+#define VIDEO_OUT_CFG_INTS_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTS_DPI
+// Description : None
+#define VIDEO_OUT_CFG_INTS_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_INTS_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_INTS_DPI_MSB 1
+#define VIDEO_OUT_CFG_INTS_DPI_LSB 1
+#define VIDEO_OUT_CFG_INTS_DPI_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_INTS_VEC
+// Description : None
+#define VIDEO_OUT_CFG_INTS_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_INTS_VEC_BITS 0x00000001
+#define VIDEO_OUT_CFG_INTS_VEC_MSB 0
+#define VIDEO_OUT_CFG_INTS_VEC_LSB 0
+#define VIDEO_OUT_CFG_INTS_VEC_ACCESS "RO"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_BLOCK_ID
+// JTAG access : synchronous
+// Description : Block Identifier
+// Hexadecimal representation of "VOCF"
+#define VIDEO_OUT_CFG_BLOCK_ID_OFFSET 0x00000024
+#define VIDEO_OUT_CFG_BLOCK_ID_BITS 0xffffffff
+#define VIDEO_OUT_CFG_BLOCK_ID_RESET 0x564f4346
+#define VIDEO_OUT_CFG_BLOCK_ID_MSB 31
+#define VIDEO_OUT_CFG_BLOCK_ID_LSB 0
+#define VIDEO_OUT_CFG_BLOCK_ID_ACCESS "RO"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_INSTANCE_ID
+// JTAG access : synchronous
+// Description : Block Instance Identifier
+#define VIDEO_OUT_CFG_INSTANCE_ID_OFFSET 0x00000028
+#define VIDEO_OUT_CFG_INSTANCE_ID_BITS 0x0000000f
+#define VIDEO_OUT_CFG_INSTANCE_ID_RESET 0x00000000
+#define VIDEO_OUT_CFG_INSTANCE_ID_MSB 3
+#define VIDEO_OUT_CFG_INSTANCE_ID_LSB 0
+#define VIDEO_OUT_CFG_INSTANCE_ID_ACCESS "RO"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_RSTSEQ_AUTO
+// JTAG access : synchronous
+// Description : None
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_OFFSET 0x0000002c
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BITS 0x00000007
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_RESET 0x00000007
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC
+// Description : 1 = reset is controlled by the sequencer
+// 0 = reset is controlled by rstseq_ctrl
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_RESET 0x1
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_BITS 0x00000004
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_MSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_LSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI
+// Description : 1 = reset is controlled by the sequencer
+// 0 = reset is controlled by rstseq_ctrl
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_RESET 0x1
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_MSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_LSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER
+// Description : 1 = reset is controlled by the sequencer
+// 0 = reset is controlled by rstseq_ctrl
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_RESET 0x1
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_BITS 0x00000001
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_MSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_LSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_RSTSEQ_PARALLEL
+// JTAG access : synchronous
+// Description : None
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_OFFSET 0x00000030
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BITS 0x00000007
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_RESET 0x00000006
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC
+// Description : Is this reset parallel (i.e. not part of the sequence)
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_RESET 0x1
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_BITS 0x00000004
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_MSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_LSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI
+// Description : Is this reset parallel (i.e. not part of the sequence)
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_RESET 0x1
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_MSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_LSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER
+// Description : Is this reset parallel (i.e. not part of the sequence)
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS 0x00000001
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_RSTSEQ_CTRL
+// JTAG access : synchronous
+// Description : None
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_OFFSET 0x00000034
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BITS 0x00000007
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC
+// Description : 1 = keep the reset asserted
+// 0 = keep the reset deasserted
+// This is ignored if rstseq_auto=1
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_BITS 0x00000004
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_MSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_LSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI
+// Description : 1 = keep the reset asserted
+// 0 = keep the reset deasserted
+// This is ignored if rstseq_auto=1
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_MSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_LSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER
+// Description : 1 = keep the reset asserted
+// 0 = keep the reset deasserted
+// This is ignored if rstseq_auto=1
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_BITS 0x00000001
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_MSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_LSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_RSTSEQ_TRIG
+// JTAG access : synchronous
+// Description : None
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_OFFSET 0x00000038
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BITS 0x00000007
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC
+// Description : Pulses the reset output
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_BITS 0x00000004
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_MSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_LSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_ACCESS "SC"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI
+// Description : Pulses the reset output
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_MSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_LSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER
+// Description : Pulses the reset output
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_BITS 0x00000001
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_MSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_LSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
+// =============================================================================
+// Register : VIDEO_OUT_CFG_RSTSEQ_DONE
+// JTAG access : synchronous
+// Description : None
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_OFFSET 0x0000003c
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_BITS 0x00000007
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_RESET 0x00000000
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_VEC
+// Description : Indicates the current state of the reset
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_BITS 0x00000004
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_MSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_LSB 2
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_DPI
+// Description : Indicates the current state of the reset
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_BITS 0x00000002
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_MSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_LSB 1
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_ACCESS "RO"
+// -----------------------------------------------------------------------------
+// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER
+// Description : Indicates the current state of the reset
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_RESET 0x0
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_BITS 0x00000001
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_MSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_LSB 0
+#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
+// =============================================================================
+
+#define CFG_WRITE(reg, val) writel((val), dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
+#define CFG_READ(reg) readl(dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
+
+void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge)
+{
+ /*
+ * We assume DPI and VEC can't be used at the same time (due to
+ * clashing requirements for PLL_VIDEO, and potentially for VDAC).
+ * We therefore leave VEC memories powered down.
+ */
+ CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_VEC_BITS);
+ CFG_WRITE(VIDEO_OUT_CFG_TEST_OVERRIDE,
+ VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS);
+
+ /* DPI->Pads; DPI->VDAC; optionally flip PCLK polarity */
+ CFG_WRITE(VIDEO_OUT_CFG_SEL,
+ drive_negedge ? VIDEO_OUT_CFG_SEL_PCLK_INV_BITS : 0);
+
+ /* configure VDAC for 3 channels, bandgap on, 710mV swing */
+ CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
+
+ /* enable DPI interrupt */
+ CFG_WRITE(VIDEO_OUT_CFG_INTE, VIDEO_OUT_CFG_INTE_DPI_BITS);
+}
+
+void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi)
+{
+ /* disable DPI interrupt */
+ CFG_WRITE(VIDEO_OUT_CFG_INTE, 0);
+
+ /* Ensure VDAC is turned off; power down DPI,VEC memories */
+ CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
+ CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_BITS);
+}
--- /dev/null
+++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * DRM Driver for DPI output on Raspberry Pi RP1
+ *
+ * Copyright (c) 2023 Raspberry Pi Limited.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/media-bus-format.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+
+#include "rp1_dpi.h"
+
+// --- DPI DMA REGISTERS ---
+
+// Control
+#define DPI_DMA_CONTROL 0x0
+#define DPI_DMA_CONTROL_ARM_SHIFT 0
+#define DPI_DMA_CONTROL_ARM_MASK BIT(DPI_DMA_CONTROL_ARM_SHIFT)
+#define DPI_DMA_CONTROL_ALIGN16_SHIFT 2
+#define DPI_DMA_CONTROL_ALIGN16_MASK BIT(DPI_DMA_CONTROL_ALIGN16_SHIFT)
+#define DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT 1
+#define DPI_DMA_CONTROL_AUTO_REPEAT_MASK BIT(DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT)
+#define DPI_DMA_CONTROL_HIGH_WATER_SHIFT 3
+#define DPI_DMA_CONTROL_HIGH_WATER_MASK (0x1FF << DPI_DMA_CONTROL_HIGH_WATER_SHIFT)
+#define DPI_DMA_CONTROL_DEN_POL_SHIFT 12
+#define DPI_DMA_CONTROL_DEN_POL_MASK BIT(DPI_DMA_CONTROL_DEN_POL_SHIFT)
+#define DPI_DMA_CONTROL_HSYNC_POL_SHIFT 13
+#define DPI_DMA_CONTROL_HSYNC_POL_MASK BIT(DPI_DMA_CONTROL_HSYNC_POL_SHIFT)
+#define DPI_DMA_CONTROL_VSYNC_POL_SHIFT 14
+#define DPI_DMA_CONTROL_VSYNC_POL_MASK BIT(DPI_DMA_CONTROL_VSYNC_POL_SHIFT)
+#define DPI_DMA_CONTROL_COLORM_SHIFT 15
+#define DPI_DMA_CONTROL_COLORM_MASK BIT(DPI_DMA_CONTROL_COLORM_SHIFT)
+#define DPI_DMA_CONTROL_SHUTDN_SHIFT 16
+#define DPI_DMA_CONTROL_SHUTDN_MASK BIT(DPI_DMA_CONTROL_SHUTDN_SHIFT)
+#define DPI_DMA_CONTROL_HBP_EN_SHIFT 17
+#define DPI_DMA_CONTROL_HBP_EN_MASK BIT(DPI_DMA_CONTROL_HBP_EN_SHIFT)
+#define DPI_DMA_CONTROL_HFP_EN_SHIFT 18
+#define DPI_DMA_CONTROL_HFP_EN_MASK BIT(DPI_DMA_CONTROL_HFP_EN_SHIFT)
+#define DPI_DMA_CONTROL_VBP_EN_SHIFT 19
+#define DPI_DMA_CONTROL_VBP_EN_MASK BIT(DPI_DMA_CONTROL_VBP_EN_SHIFT)
+#define DPI_DMA_CONTROL_VFP_EN_SHIFT 20
+#define DPI_DMA_CONTROL_VFP_EN_MASK BIT(DPI_DMA_CONTROL_VFP_EN_SHIFT)
+#define DPI_DMA_CONTROL_HSYNC_EN_SHIFT 21
+#define DPI_DMA_CONTROL_HSYNC_EN_MASK BIT(DPI_DMA_CONTROL_HSYNC_EN_SHIFT)
+#define DPI_DMA_CONTROL_VSYNC_EN_SHIFT 22
+#define DPI_DMA_CONTROL_VSYNC_EN_MASK BIT(DPI_DMA_CONTROL_VSYNC_EN_SHIFT)
+#define DPI_DMA_CONTROL_FORCE_IMMED_SHIFT 23
+#define DPI_DMA_CONTROL_FORCE_IMMED_MASK BIT(DPI_DMA_CONTROL_FORCE_IMMED_SHIFT)
+#define DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT 24
+#define DPI_DMA_CONTROL_FORCE_DRAIN_MASK BIT(DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT)
+#define DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT 25
+#define DPI_DMA_CONTROL_FORCE_EMPTY_MASK BIT(DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT)
+
+// IRQ_ENABLES
+#define DPI_DMA_IRQ_EN 0x04
+#define DPI_DMA_IRQ_EN_DMA_READY_SHIFT 0
+#define DPI_DMA_IRQ_EN_DMA_READY_MASK BIT(DPI_DMA_IRQ_EN_DMA_READY_SHIFT)
+#define DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT 1
+#define DPI_DMA_IRQ_EN_UNDERFLOW_MASK BIT(DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT)
+#define DPI_DMA_IRQ_EN_FRAME_START_SHIFT 2
+#define DPI_DMA_IRQ_EN_FRAME_START_MASK BIT(DPI_DMA_IRQ_EN_FRAME_START_SHIFT)
+#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT 3
+#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_MASK BIT(DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT)
+#define DPI_DMA_IRQ_EN_TE_SHIFT 4
+#define DPI_DMA_IRQ_EN_TE_MASK BIT(DPI_DMA_IRQ_EN_TE_SHIFT)
+#define DPI_DMA_IRQ_EN_ERROR_SHIFT 5
+#define DPI_DMA_IRQ_EN_ERROR_MASK BIT(DPI_DMA_IRQ_EN_ERROR_SHIFT)
+#define DPI_DMA_IRQ_EN_MATCH_SHIFT 6
+#define DPI_DMA_IRQ_EN_MATCH_MASK BIT(DPI_DMA_IRQ_EN_MATCH_SHIFT)
+#define DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT 16
+#define DPI_DMA_IRQ_EN_MATCH_LINE_MASK (0xFFF << DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT)
+
+// IRQ_FLAGS
+#define DPI_DMA_IRQ_FLAGS 0x08
+#define DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT 0
+#define DPI_DMA_IRQ_FLAGS_DMA_READY_MASK BIT(DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT)
+#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT 1
+#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK BIT(DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT)
+#define DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT 2
+#define DPI_DMA_IRQ_FLAGS_FRAME_START_MASK BIT(DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT)
+#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT 3
+#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK BIT(DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT)
+#define DPI_DMA_IRQ_FLAGS_TE_SHIFT 4
+#define DPI_DMA_IRQ_FLAGS_TE_MASK BIT(DPI_DMA_IRQ_FLAGS_TE_SHIFT)
+#define DPI_DMA_IRQ_FLAGS_ERROR_SHIFT 5
+#define DPI_DMA_IRQ_FLAGS_ERROR_MASK BIT(DPI_DMA_IRQ_FLAGS_ERROR_SHIFT)
+#define DPI_DMA_IRQ_FLAGS_MATCH_SHIFT 6
+#define DPI_DMA_IRQ_FLAGS_MATCH_MASK BIT(DPI_DMA_IRQ_FLAGS_MATCH_SHIFT)
+
+// QOS
+#define DPI_DMA_QOS 0xC
+#define DPI_DMA_QOS_DQOS_SHIFT 0
+#define DPI_DMA_QOS_DQOS_MASK (0xF << DPI_DMA_QOS_DQOS_SHIFT)
+#define DPI_DMA_QOS_ULEV_SHIFT 4
+#define DPI_DMA_QOS_ULEV_MASK (0xF << DPI_DMA_QOS_ULEV_SHIFT)
+#define DPI_DMA_QOS_UQOS_SHIFT 8
+#define DPI_DMA_QOS_UQOS_MASK (0xF << DPI_DMA_QOS_UQOS_SHIFT)
+#define DPI_DMA_QOS_LLEV_SHIFT 12
+#define DPI_DMA_QOS_LLEV_MASK (0xF << DPI_DMA_QOS_LLEV_SHIFT)
+#define DPI_DMA_QOS_LQOS_SHIFT 16
+#define DPI_DMA_QOS_LQOS_MASK (0xF << DPI_DMA_QOS_LQOS_SHIFT)
+
+// Panics
+#define DPI_DMA_PANICS 0x38
+#define DPI_DMA_PANICS_UPPER_COUNT_SHIFT 0
+#define DPI_DMA_PANICS_UPPER_COUNT_MASK \
+ (0x0000FFFF << DPI_DMA_PANICS_UPPER_COUNT_SHIFT)
+#define DPI_DMA_PANICS_LOWER_COUNT_SHIFT 16
+#define DPI_DMA_PANICS_LOWER_COUNT_MASK \
+ (0x0000FFFF << DPI_DMA_PANICS_LOWER_COUNT_SHIFT)
+
+// DMA Address Lower:
+#define DPI_DMA_DMA_ADDR_L 0x10
+
+// DMA Address Upper:
+#define DPI_DMA_DMA_ADDR_H 0x40
+
+// DMA stride
+#define DPI_DMA_DMA_STRIDE 0x14
+
+// Visible Area
+#define DPI_DMA_VISIBLE_AREA 0x18
+#define DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT 0
+#define DPI_DMA_VISIBLE_AREA_ROWSM1_MASK (0x0FFF << DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT)
+#define DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT 16
+#define DPI_DMA_VISIBLE_AREA_COLSM1_MASK (0x0FFF << DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT)
+
+// Sync width
+#define DPI_DMA_SYNC_WIDTH 0x1C
+#define DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT 0
+#define DPI_DMA_SYNC_WIDTH_ROWSM1_MASK (0x0FFF << DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT)
+#define DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT 16
+#define DPI_DMA_SYNC_WIDTH_COLSM1_MASK (0x0FFF << DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT)
+
+// Back porch
+#define DPI_DMA_BACK_PORCH 0x20
+#define DPI_DMA_BACK_PORCH_ROWSM1_SHIFT 0
+#define DPI_DMA_BACK_PORCH_ROWSM1_MASK (0x0FFF << DPI_DMA_BACK_PORCH_ROWSM1_SHIFT)
+#define DPI_DMA_BACK_PORCH_COLSM1_SHIFT 16
+#define DPI_DMA_BACK_PORCH_COLSM1_MASK (0x0FFF << DPI_DMA_BACK_PORCH_COLSM1_SHIFT)
+
+// Front porch
+#define DPI_DMA_FRONT_PORCH 0x24
+#define DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT 0
+#define DPI_DMA_FRONT_PORCH_ROWSM1_MASK (0x0FFF << DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT)
+#define DPI_DMA_FRONT_PORCH_COLSM1_SHIFT 16
+#define DPI_DMA_FRONT_PORCH_COLSM1_MASK (0x0FFF << DPI_DMA_FRONT_PORCH_COLSM1_SHIFT)
+
+// Input masks
+#define DPI_DMA_IMASK 0x2C
+#define DPI_DMA_IMASK_R_SHIFT 0
+#define DPI_DMA_IMASK_R_MASK (0x3FF << DPI_DMA_IMASK_R_SHIFT)
+#define DPI_DMA_IMASK_G_SHIFT 10
+#define DPI_DMA_IMASK_G_MASK (0x3FF << DPI_DMA_IMASK_G_SHIFT)
+#define DPI_DMA_IMASK_B_SHIFT 20
+#define DPI_DMA_IMASK_B_MASK (0x3FF << DPI_DMA_IMASK_B_SHIFT)
+
+// Output Masks
+#define DPI_DMA_OMASK 0x30
+#define DPI_DMA_OMASK_R_SHIFT 0
+#define DPI_DMA_OMASK_R_MASK (0x3FF << DPI_DMA_OMASK_R_SHIFT)
+#define DPI_DMA_OMASK_G_SHIFT 10
+#define DPI_DMA_OMASK_G_MASK (0x3FF << DPI_DMA_OMASK_G_SHIFT)
+#define DPI_DMA_OMASK_B_SHIFT 20
+#define DPI_DMA_OMASK_B_MASK (0x3FF << DPI_DMA_OMASK_B_SHIFT)
+
+// Shifts
+#define DPI_DMA_SHIFT 0x28
+#define DPI_DMA_SHIFT_IR_SHIFT 0
+#define DPI_DMA_SHIFT_IR_MASK (0x1F << DPI_DMA_SHIFT_IR_SHIFT)
+#define DPI_DMA_SHIFT_IG_SHIFT 5
+#define DPI_DMA_SHIFT_IG_MASK (0x1F << DPI_DMA_SHIFT_IG_SHIFT)
+#define DPI_DMA_SHIFT_IB_SHIFT 10
+#define DPI_DMA_SHIFT_IB_MASK (0x1F << DPI_DMA_SHIFT_IB_SHIFT)
+#define DPI_DMA_SHIFT_OR_SHIFT 15
+#define DPI_DMA_SHIFT_OR_MASK (0x1F << DPI_DMA_SHIFT_OR_SHIFT)
+#define DPI_DMA_SHIFT_OG_SHIFT 20
+#define DPI_DMA_SHIFT_OG_MASK (0x1F << DPI_DMA_SHIFT_OG_SHIFT)
+#define DPI_DMA_SHIFT_OB_SHIFT 25
+#define DPI_DMA_SHIFT_OB_MASK (0x1F << DPI_DMA_SHIFT_OB_SHIFT)
+
+// Scaling
+#define DPI_DMA_RGBSZ 0x34
+#define DPI_DMA_RGBSZ_BPP_SHIFT 16
+#define DPI_DMA_RGBSZ_BPP_MASK (0x3 << DPI_DMA_RGBSZ_BPP_SHIFT)
+#define DPI_DMA_RGBSZ_R_SHIFT 0
+#define DPI_DMA_RGBSZ_R_MASK (0xF << DPI_DMA_RGBSZ_R_SHIFT)
+#define DPI_DMA_RGBSZ_G_SHIFT 4
+#define DPI_DMA_RGBSZ_G_MASK (0xF << DPI_DMA_RGBSZ_G_SHIFT)
+#define DPI_DMA_RGBSZ_B_SHIFT 8
+#define DPI_DMA_RGBSZ_B_MASK (0xF << DPI_DMA_RGBSZ_B_SHIFT)
+
+// Status
+#define DPI_DMA_STATUS 0x3c
+
+#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
+
+static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg)
+{
+ void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
+
+ return readl(addr);
+}
+
+static void rp1dpi_hw_write(struct rp1_dpi *dpi, unsigned int reg, unsigned int val)
+{
+ void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
+
+ writel(val, addr);
+}
+
+int rp1dpi_hw_busy(struct rp1_dpi *dpi)
+{
+ return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0;
+}
+
+/* Table of supported input (in-memory/DMA) pixel formats. */
+struct rp1dpi_ipixfmt {
+ u32 format; /* DRM format code */
+ u32 mask; /* RGB masks (10 bits each, left justified) */
+ u32 shift; /* RGB MSB positions in the memory word */
+ u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */
+};
+
+#define IMASK_RGB(r, g, b) (BITS(DPI_DMA_IMASK_R, r) | \
+ BITS(DPI_DMA_IMASK_G, g) | \
+ BITS(DPI_DMA_IMASK_B, b))
+#define OMASK_RGB(r, g, b) (BITS(DPI_DMA_OMASK_R, r) | \
+ BITS(DPI_DMA_OMASK_G, g) | \
+ BITS(DPI_DMA_OMASK_B, b))
+#define ISHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_IR, r) | \
+ BITS(DPI_DMA_SHIFT_IG, g) | \
+ BITS(DPI_DMA_SHIFT_IB, b))
+#define OSHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_OR, r) | \
+ BITS(DPI_DMA_SHIFT_OG, g) | \
+ BITS(DPI_DMA_SHIFT_OB, b))
+
+static const struct rp1dpi_ipixfmt my_formats[] = {
+ {
+ .format = DRM_FORMAT_XRGB8888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(23, 15, 7),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
+ },
+ {
+ .format = DRM_FORMAT_XBGR8888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(7, 15, 23),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
+ },
+ {
+ .format = DRM_FORMAT_ARGB8888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(23, 15, 7),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
+ },
+ {
+ .format = DRM_FORMAT_ABGR8888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(7, 15, 23),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
+ },
+ {
+ .format = DRM_FORMAT_RGB888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(23, 15, 7),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
+ },
+ {
+ .format = DRM_FORMAT_BGR888,
+ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
+ .shift = ISHIFT_RGB(7, 15, 23),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
+ },
+ {
+ .format = DRM_FORMAT_RGB565,
+ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
+ .shift = ISHIFT_RGB(15, 10, 4),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
+ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
+ },
+ {
+ .format = DRM_FORMAT_BGR565,
+ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
+ .shift = ISHIFT_RGB(4, 10, 15),
+ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
+ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
+ }
+};
+
+static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz)
+{
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ if (*shift == ISHIFT_RGB(15, 10, 4)) {
+ /* When framebuffer is RGB565, we can output RGB565 */
+ *shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0);
+ *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
+ return OMASK_RGB(0x3fc, 0x3fc, 0);
+ }
+
+ /* due to a HW limitation, bit-depth is effectively RGB535 */
+ *shift |= OSHIFT_RGB(19, 14, 6);
+ *imask &= IMASK_RGB(0x3e0, 0x380, 0x3e0);
+ *rgbsz = BITS(DPI_DMA_RGBSZ_G, 5) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
+ return OMASK_RGB(0x3e0, 0x39c, 0x3e0);
+
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_BGR666_1X18:
+ /* due to a HW limitation, bit-depth is effectively RGB444 */
+ *shift |= OSHIFT_RGB(23, 15, 7);
+ *imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0);
+ *rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
+ return OMASK_RGB(0x330, 0x3c0, 0x3c0);
+
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_BGR888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ /* The full 24 bits can be output. Note that RP1's internal wiring means
+ * that 8.8.8 to GPIO pads can share with 10.10.10 to the onboard VDAC.
+ */
+ *shift |= OSHIFT_RGB(29, 19, 9);
+ return OMASK_RGB(0x3fc, 0x3fc, 0x3fc);
+
+ case MEDIA_BUS_FMT_RGB565_1X24_CPADHI:
+ /* This should match Raspberry Pi legacy "mode 3" */
+ *shift |= OSHIFT_RGB(26, 17, 6);
+ *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
+ return OMASK_RGB(0x3e0, 0x3f0, 0x3e0);
+
+ default:
+ /* RGB666_1x24_CPADHI, BGR666_1X24_CPADHI and "mode 4" formats */
+ *shift |= OSHIFT_RGB(27, 17, 7);
+ *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
+ return OMASK_RGB(0x3f0, 0x3f0, 0x3f0);
+ }
+}
+
+#define BUS_FMT_IS_BGR(fmt) ( \
+ ((fmt) == MEDIA_BUS_FMT_BGR666_1X18) || \
+ ((fmt) == MEDIA_BUS_FMT_BGR666_1X24_CPADHI) || \
+ ((fmt) == MEDIA_BUS_FMT_BGR888_1X24))
+
+void rp1dpi_hw_setup(struct rp1_dpi *dpi,
+ u32 in_format, u32 bus_format, bool de_inv,
+ struct drm_display_mode const *mode)
+{
+ u32 shift, imask, omask, rgbsz;
+ int i;
+
+ pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC",
+ __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format,
+ mode->hdisplay, mode->vdisplay,
+ mode->htotal, mode->vtotal,
+ mode->clock,
+ (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+',
+ (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+',
+ de_inv ? '-' : '+',
+ dpi->clk_inv ? '-' : '+');
+
+ /*
+ * Configure all DPI/DMA block registers, except base address.
+ * DMA will not actually start until a FB base address is specified
+ * using rp1dpi_hw_update().
+ */
+ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
+ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
+ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
+
+ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
+ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
+ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
+
+ /* In these registers, "back porch" time includes sync width */
+ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
+ BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
+ BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
+
+ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
+ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
+ BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
+
+ /* Input to output pixel format conversion */
+ for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
+ if (my_formats[i].format == in_format)
+ break;
+ }
+ if (i >= ARRAY_SIZE(my_formats)) {
+ pr_err("%s: bad input format\n", __func__);
+ i = 4;
+ }
+ if (BUS_FMT_IS_BGR(bus_format))
+ i ^= 1;
+ shift = my_formats[i].shift;
+ imask = my_formats[i].mask;
+ rgbsz = my_formats[i].rgbsz;
+ omask = set_output_format(bus_format, &shift, &imask, &rgbsz);
+
+ rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask);
+ rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask);
+ rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift);
+ rp1dpi_hw_write(dpi, DPI_DMA_RGBSZ, rgbsz);
+
+ rp1dpi_hw_write(dpi, DPI_DMA_QOS,
+ BITS(DPI_DMA_QOS_DQOS, 0x0) |
+ BITS(DPI_DMA_QOS_ULEV, 0xb) |
+ BITS(DPI_DMA_QOS_UQOS, 0x2) |
+ BITS(DPI_DMA_QOS_LLEV, 0x8) |
+ BITS(DPI_DMA_QOS_LQOS, 0x7));
+
+ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1);
+ rp1dpi_hw_vblank_ctrl(dpi, 1);
+
+ i = rp1dpi_hw_busy(dpi);
+ if (i)
+ pr_warn("%s: Unexpectedly busy at start!", __func__);
+
+ rp1dpi_hw_write(dpi, DPI_DMA_CONTROL,
+ BITS(DPI_DMA_CONTROL_ARM, !i) |
+ BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) |
+ BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) |
+ BITS(DPI_DMA_CONTROL_DEN_POL, de_inv) |
+ BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) |
+ BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) |
+ BITS(DPI_DMA_CONTROL_COLORM, 0) |
+ BITS(DPI_DMA_CONTROL_SHUTDN, 0) |
+ BITS(DPI_DMA_CONTROL_HBP_EN, (mode->htotal != mode->hsync_end)) |
+ BITS(DPI_DMA_CONTROL_HFP_EN, (mode->hsync_start != mode->hdisplay)) |
+ BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_end)) |
+ BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) |
+ BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start)) |
+ BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start)));
+}
+
+void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride)
+{
+ u64 a = addr + offset;
+
+ /*
+ * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(),
+ * DMA starts immediately; if already running, the buffer will flip at
+ * the next vertical sync event.
+ */
+ rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride);
+ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32);
+ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
+}
+
+void rp1dpi_hw_stop(struct rp1_dpi *dpi)
+{
+ u32 ctrl;
+
+ /*
+ * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
+ * the current and any queued frame to end. "Force drain" flags are not used,
+ * as they seem to prevent DMA from re-starting properly; it's safer to wait.
+ */
+ reinit_completion(&dpi->finished);
+ ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL);
+ ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
+ rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl);
+ if (!wait_for_completion_timeout(&dpi->finished, HZ / 10))
+ drm_err(&dpi->drm, "%s: timed out waiting for idle\n", __func__);
+ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0);
+}
+
+void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable)
+{
+ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN,
+ BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) |
+ BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) |
+ BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
+ BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
+}
+
+irqreturn_t rp1dpi_hw_isr(int irq, void *dev)
+{
+ struct rp1_dpi *dpi = dev;
+ u32 u = rp1dpi_hw_read(dpi, DPI_DMA_IRQ_FLAGS);
+
+ if (u) {
+ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, u);
+ if (dpi) {
+ if (u & DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK)
+ drm_err_ratelimited(&dpi->drm,
+ "Underflow! (panics=0x%08x)\n",
+ rp1dpi_hw_read(dpi, DPI_DMA_PANICS));
+ if (u & DPI_DMA_IRQ_FLAGS_DMA_READY_MASK)
+ drm_crtc_handle_vblank(&dpi->pipe.crtc);
+ if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
+ complete(&dpi->finished);
+ }
+ }
+ return u ? IRQ_HANDLED : IRQ_NONE;
+}