1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2019 BayLibre, SAS
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
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 */
29 dma_alloc_coherent(priv
->dev
, SZ_4K
,
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
));
48 void meson_rdma_free(struct meson_drm
*priv
)
50 if (!priv
->rdma
.addr
&& !priv
->rdma
.addr_dma
)
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
,
75 priv
->io_base
+ _REG(RDMA_CTRL
));
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
,
94 if (priv
->rdma
.offset
>= (SZ_4K
/ RDMA_DESC_SIZE
)) {
95 dev_warn_once(priv
->dev
, "%s: overflow\n", __func__
);
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
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;