WIP FPC-III support
[linux/fpc-iii.git] / drivers / gpu / drm / meson / meson_rdma.c
blob130382178c6310db0ba9e56f1c01f306f4cdeaf5
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2019 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 */
7 #include <linux/bitfield.h>
8 #include <linux/dma-mapping.h>
10 #include "meson_drv.h"
11 #include "meson_registers.h"
12 #include "meson_rdma.h"
15 * The VPU embeds a "Register DMA" that can write a sequence of registers
16 * on the VPU AHB bus, either manually or triggered by an internal IRQ
17 * event like VSYNC or a line input counter.
18 * The initial implementation handles a single channel (over 8), triggered
19 * by the VSYNC irq and does not handle the RDMA irq.
22 #define RDMA_DESC_SIZE (sizeof(uint32_t) * 2)
24 int meson_rdma_init(struct meson_drm *priv)
26 if (!priv->rdma.addr) {
27 /* Allocate a PAGE buffer */
28 priv->rdma.addr =
29 dma_alloc_coherent(priv->dev, SZ_4K,
30 &priv->rdma.addr_dma,
31 GFP_KERNEL);
32 if (!priv->rdma.addr)
33 return -ENOMEM;
36 priv->rdma.offset = 0;
38 writel_relaxed(RDMA_CTRL_SW_RESET,
39 priv->io_base + _REG(RDMA_CTRL));
40 writel_relaxed(RDMA_DEFAULT_CONFIG |
41 FIELD_PREP(RDMA_CTRL_AHB_WR_BURST, 3) |
42 FIELD_PREP(RDMA_CTRL_AHB_RD_BURST, 0),
43 priv->io_base + _REG(RDMA_CTRL));
45 return 0;
48 void meson_rdma_free(struct meson_drm *priv)
50 if (!priv->rdma.addr && !priv->rdma.addr_dma)
51 return;
53 meson_rdma_stop(priv);
55 dma_free_coherent(priv->dev, SZ_4K,
56 priv->rdma.addr, priv->rdma.addr_dma);
58 priv->rdma.addr = NULL;
59 priv->rdma.addr_dma = (dma_addr_t)0;
62 void meson_rdma_setup(struct meson_drm *priv)
64 /* Channel 1: Write Flag, No Address Increment */
65 writel_bits_relaxed(RDMA_ACCESS_RW_FLAG_CHAN1 |
66 RDMA_ACCESS_ADDR_INC_CHAN1,
67 RDMA_ACCESS_RW_FLAG_CHAN1,
68 priv->io_base + _REG(RDMA_ACCESS_AUTO));
71 void meson_rdma_stop(struct meson_drm *priv)
73 writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
74 RDMA_IRQ_CLEAR_CHAN1,
75 priv->io_base + _REG(RDMA_CTRL));
77 /* Stop Channel 1 */
78 writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
79 FIELD_PREP(RDMA_ACCESS_ADDR_INC_CHAN1,
80 RDMA_ACCESS_TRIGGER_STOP),
81 priv->io_base + _REG(RDMA_ACCESS_AUTO));
84 void meson_rdma_reset(struct meson_drm *priv)
86 meson_rdma_stop(priv);
88 priv->rdma.offset = 0;
91 static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
92 uint32_t reg)
94 if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {
95 dev_warn_once(priv->dev, "%s: overflow\n", __func__);
96 return;
99 priv->rdma.addr[priv->rdma.offset++] = reg;
100 priv->rdma.addr[priv->rdma.offset++] = val;
104 * This will add the register to the RDMA buffer and write it to the
105 * hardware at the same time.
106 * When meson_rdma_flush is called, the RDMA will replay the register
107 * writes in order.
109 void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
111 meson_rdma_writel(priv, val, reg);
113 writel_relaxed(val, priv->io_base + _REG(reg));
116 void meson_rdma_flush(struct meson_drm *priv)
118 meson_rdma_stop(priv);
120 /* Start of Channel 1 register writes buffer */
121 writel(priv->rdma.addr_dma,
122 priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
124 /* Last byte on Channel 1 register writes buffer */
125 writel(priv->rdma.addr_dma + (priv->rdma.offset * RDMA_DESC_SIZE) - 1,
126 priv->io_base + _REG(RDMA_AHB_END_ADDR_1));
128 /* Trigger Channel 1 on VSYNC event */
129 writel_bits_relaxed(RDMA_ACCESS_TRIGGER_CHAN1,
130 FIELD_PREP(RDMA_ACCESS_TRIGGER_CHAN1,
131 RDMA_ACCESS_TRIGGER_VSYNC),
132 priv->io_base + _REG(RDMA_ACCESS_AUTO));
134 priv->rdma.offset = 0;