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