1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 BayLibre, SAS
4  * Author: Neil Armstrong <narmstrong@baylibre.com>
5  */
6 
7 #include <linux/bitfield.h>
8 #include <linux/dma-mapping.h>
9 
10 #include "meson_drv.h"
11 #include "meson_registers.h"
12 #include "meson_rdma.h"
13 
14 /*
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.
20  */
21 
22 #define RDMA_DESC_SIZE	(sizeof(uint32_t) * 2)
23 
meson_rdma_init(struct meson_drm * priv)24 int meson_rdma_init(struct meson_drm *priv)
25 {
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;
34 	}
35 
36 	priv->rdma.offset = 0;
37 
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));
44 
45 	return 0;
46 }
47 
meson_rdma_free(struct meson_drm * priv)48 void meson_rdma_free(struct meson_drm *priv)
49 {
50 	if (!priv->rdma.addr && !priv->rdma.addr_dma)
51 		return;
52 
53 	meson_rdma_stop(priv);
54 
55 	dma_free_coherent(priv->dev, SZ_4K,
56 			  priv->rdma.addr, priv->rdma.addr_dma);
57 
58 	priv->rdma.addr = NULL;
59 	priv->rdma.addr_dma = (dma_addr_t)0;
60 }
61 
meson_rdma_setup(struct meson_drm * priv)62 void meson_rdma_setup(struct meson_drm *priv)
63 {
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));
69 }
70 
meson_rdma_stop(struct meson_drm * priv)71 void meson_rdma_stop(struct meson_drm *priv)
72 {
73 	writel_bits_relaxed(RDMA_IRQ_CLEAR_CHAN1,
74 			    RDMA_IRQ_CLEAR_CHAN1,
75 			    priv->io_base + _REG(RDMA_CTRL));
76 
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));
82 }
83 
meson_rdma_reset(struct meson_drm * priv)84 void meson_rdma_reset(struct meson_drm *priv)
85 {
86 	meson_rdma_stop(priv);
87 
88 	priv->rdma.offset = 0;
89 }
90 
meson_rdma_writel(struct meson_drm * priv,uint32_t val,uint32_t reg)91 static void meson_rdma_writel(struct meson_drm *priv, uint32_t val,
92 			      uint32_t reg)
93 {
94 	if (priv->rdma.offset >= (SZ_4K / RDMA_DESC_SIZE)) {
95 		dev_warn_once(priv->dev, "%s: overflow\n", __func__);
96 		return;
97 	}
98 
99 	priv->rdma.addr[priv->rdma.offset++] = reg;
100 	priv->rdma.addr[priv->rdma.offset++] = val;
101 }
102 
103 /*
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.
108  */
meson_rdma_writel_sync(struct meson_drm * priv,uint32_t val,uint32_t reg)109 void meson_rdma_writel_sync(struct meson_drm *priv, uint32_t val, uint32_t reg)
110 {
111 	meson_rdma_writel(priv, val, reg);
112 
113 	writel_relaxed(val, priv->io_base + _REG(reg));
114 }
115 
meson_rdma_flush(struct meson_drm * priv)116 void meson_rdma_flush(struct meson_drm *priv)
117 {
118 	meson_rdma_stop(priv);
119 
120 	/* Start of Channel 1 register writes buffer */
121 	writel(priv->rdma.addr_dma,
122 	       priv->io_base + _REG(RDMA_AHB_START_ADDR_1));
123 
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));
127 
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));
133 
134 	priv->rdma.offset = 0;
135 }
136