]>
Commit | Line | Data |
---|---|---|
bfe834be KM |
1 | /* |
2 | * Renesas R-Car Audio DMAC support | |
3 | * | |
4 | * Copyright (C) 2015 Renesas Electronics Corp. | |
5 | * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
288f392e | 11 | #include <linux/delay.h> |
72adc61f | 12 | #include <linux/of_dma.h> |
bfe834be KM |
13 | #include "rsnd.h" |
14 | ||
288f392e KM |
15 | /* |
16 | * Audio DMAC peri peri register | |
17 | */ | |
18 | #define PDMASAR 0x00 | |
19 | #define PDMADAR 0x04 | |
20 | #define PDMACHCR 0x0c | |
21 | ||
22 | /* PDMACHCR */ | |
23 | #define PDMACHCR_DE (1 << 0) | |
24 | ||
3e5afa73 KM |
25 | |
26 | struct rsnd_dmaen { | |
27 | struct dma_chan *chan; | |
07b7acb5 | 28 | dma_cookie_t cookie; |
4821d914 | 29 | unsigned int dma_len; |
3e5afa73 KM |
30 | }; |
31 | ||
32 | struct rsnd_dmapp { | |
33 | int dmapp_id; | |
34 | u32 chcr; | |
35 | }; | |
36 | ||
37 | struct rsnd_dma { | |
940e9479 | 38 | struct rsnd_mod mod; |
edce5c49 KM |
39 | struct rsnd_mod *mod_from; |
40 | struct rsnd_mod *mod_to; | |
3e5afa73 KM |
41 | dma_addr_t src_addr; |
42 | dma_addr_t dst_addr; | |
43 | union { | |
44 | struct rsnd_dmaen en; | |
45 | struct rsnd_dmapp pp; | |
46 | } dma; | |
47 | }; | |
48 | ||
288f392e KM |
49 | struct rsnd_dma_ctrl { |
50 | void __iomem *base; | |
940e9479 | 51 | int dmaen_num; |
288f392e KM |
52 | int dmapp_num; |
53 | }; | |
54 | ||
55 | #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) | |
940e9479 | 56 | #define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod) |
3e5afa73 KM |
57 | #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) |
58 | #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) | |
288f392e | 59 | |
9b6ea250 KM |
60 | /* for DEBUG */ |
61 | static struct rsnd_mod_ops mem_ops = { | |
62 | .name = "mem", | |
63 | }; | |
64 | ||
65 | static struct rsnd_mod mem = { | |
66 | }; | |
67 | ||
288f392e KM |
68 | /* |
69 | * Audio DMAC | |
70 | */ | |
9b99e9a7 KM |
71 | static void __rsnd_dmaen_complete(struct rsnd_mod *mod, |
72 | struct rsnd_dai_stream *io) | |
bfe834be | 73 | { |
75defee0 | 74 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
75defee0 KM |
75 | bool elapsed = false; |
76 | unsigned long flags; | |
bfe834be KM |
77 | |
78 | /* | |
79 | * Renesas sound Gen1 needs 1 DMAC, | |
80 | * Gen2 needs 2 DMAC. | |
81 | * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. | |
82 | * But, Audio-DMAC-peri-peri doesn't have interrupt, | |
83 | * and this driver is assuming that here. | |
bfe834be | 84 | */ |
75defee0 KM |
85 | spin_lock_irqsave(&priv->lock, flags); |
86 | ||
c20c6704 | 87 | if (rsnd_io_is_working(io)) |
a97a06c7 | 88 | elapsed = true; |
bfe834be | 89 | |
75defee0 | 90 | spin_unlock_irqrestore(&priv->lock, flags); |
bfe834be | 91 | |
75defee0 KM |
92 | if (elapsed) |
93 | rsnd_dai_period_elapsed(io); | |
bfe834be KM |
94 | } |
95 | ||
9b99e9a7 KM |
96 | static void rsnd_dmaen_complete(void *data) |
97 | { | |
98 | struct rsnd_mod *mod = data; | |
99 | ||
100 | rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); | |
101 | } | |
102 | ||
edce5c49 KM |
103 | static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, |
104 | struct rsnd_mod *mod_from, | |
105 | struct rsnd_mod *mod_to) | |
106 | { | |
107 | if ((!mod_from && !mod_to) || | |
108 | (mod_from && mod_to)) | |
109 | return NULL; | |
110 | ||
111 | if (mod_from) | |
112 | return rsnd_mod_dma_req(io, mod_from); | |
113 | else | |
114 | return rsnd_mod_dma_req(io, mod_to); | |
115 | } | |
116 | ||
497debaa KM |
117 | static int rsnd_dmaen_stop(struct rsnd_mod *mod, |
118 | struct rsnd_dai_stream *io, | |
119 | struct rsnd_priv *priv) | |
bfe834be | 120 | { |
76c80b5b | 121 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); |
0d00a521 KM |
122 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); |
123 | ||
c20c6704 | 124 | if (dmaen->chan) |
edce5c49 | 125 | dmaengine_terminate_all(dmaen->chan); |
edce5c49 KM |
126 | |
127 | return 0; | |
128 | } | |
129 | ||
130 | static int rsnd_dmaen_nolock_stop(struct rsnd_mod *mod, | |
131 | struct rsnd_dai_stream *io, | |
132 | struct rsnd_priv *priv) | |
133 | { | |
134 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); | |
135 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | |
136 | ||
137 | /* | |
138 | * DMAEngine release uses mutex lock. | |
139 | * Thus, it shouldn't be called under spinlock. | |
140 | * Let's call it under nolock_start | |
141 | */ | |
142 | if (dmaen->chan) | |
143 | dma_release_channel(dmaen->chan); | |
144 | ||
145 | dmaen->chan = NULL; | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod, | |
151 | struct rsnd_dai_stream *io, | |
152 | struct rsnd_priv *priv) | |
153 | { | |
154 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); | |
155 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | |
156 | struct device *dev = rsnd_priv_to_dev(priv); | |
157 | ||
158 | if (dmaen->chan) { | |
159 | dev_err(dev, "it already has dma channel\n"); | |
160 | return -EIO; | |
161 | } | |
162 | ||
163 | /* | |
164 | * DMAEngine request uses mutex lock. | |
165 | * Thus, it shouldn't be called under spinlock. | |
166 | * Let's call it under nolock_start | |
167 | */ | |
168 | dmaen->chan = rsnd_dmaen_request_channel(io, | |
169 | dma->mod_from, | |
170 | dma->mod_to); | |
171 | if (IS_ERR_OR_NULL(dmaen->chan)) { | |
edce5c49 KM |
172 | dmaen->chan = NULL; |
173 | dev_err(dev, "can't get dma channel\n"); | |
c409c2a9 | 174 | return -EIO; |
edce5c49 | 175 | } |
497debaa KM |
176 | |
177 | return 0; | |
bfe834be KM |
178 | } |
179 | ||
497debaa KM |
180 | static int rsnd_dmaen_start(struct rsnd_mod *mod, |
181 | struct rsnd_dai_stream *io, | |
182 | struct rsnd_priv *priv) | |
bfe834be | 183 | { |
76c80b5b | 184 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); |
0d00a521 | 185 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); |
bfe834be KM |
186 | struct snd_pcm_substream *substream = io->substream; |
187 | struct device *dev = rsnd_priv_to_dev(priv); | |
188 | struct dma_async_tx_descriptor *desc; | |
edce5c49 | 189 | struct dma_slave_config cfg = {}; |
aaf4fce0 | 190 | int is_play = rsnd_io_is_play(io); |
edce5c49 KM |
191 | int ret; |
192 | ||
193 | cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | |
194 | cfg.src_addr = dma->src_addr; | |
195 | cfg.dst_addr = dma->dst_addr; | |
196 | cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | |
197 | cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | |
198 | ||
199 | dev_dbg(dev, "%s[%d] %pad -> %pad\n", | |
200 | rsnd_mod_name(mod), rsnd_mod_id(mod), | |
201 | &cfg.src_addr, &cfg.dst_addr); | |
202 | ||
203 | ret = dmaengine_slave_config(dmaen->chan, &cfg); | |
204 | if (ret < 0) | |
205 | return ret; | |
bfe834be | 206 | |
0d00a521 | 207 | desc = dmaengine_prep_dma_cyclic(dmaen->chan, |
c20c6704 KM |
208 | substream->runtime->dma_addr, |
209 | snd_pcm_lib_buffer_bytes(substream), | |
210 | snd_pcm_lib_period_bytes(substream), | |
aaf4fce0 | 211 | is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, |
bfe834be KM |
212 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
213 | ||
214 | if (!desc) { | |
215 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | |
497debaa | 216 | return -EIO; |
bfe834be KM |
217 | } |
218 | ||
3c68565b | 219 | desc->callback = rsnd_dmaen_complete; |
497debaa | 220 | desc->callback_param = rsnd_mod_get(dma); |
bfe834be | 221 | |
c20c6704 | 222 | dmaen->dma_len = snd_pcm_lib_buffer_bytes(substream); |
4821d914 | 223 | |
07b7acb5 KM |
224 | dmaen->cookie = dmaengine_submit(desc); |
225 | if (dmaen->cookie < 0) { | |
bfe834be | 226 | dev_err(dev, "dmaengine_submit() fail\n"); |
497debaa | 227 | return -EIO; |
bfe834be KM |
228 | } |
229 | ||
0d00a521 | 230 | dma_async_issue_pending(dmaen->chan); |
497debaa KM |
231 | |
232 | return 0; | |
bfe834be KM |
233 | } |
234 | ||
72adc61f KM |
235 | struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, |
236 | struct rsnd_mod *mod, char *name) | |
237 | { | |
161ba1f1 | 238 | struct dma_chan *chan = NULL; |
72adc61f KM |
239 | struct device_node *np; |
240 | int i = 0; | |
241 | ||
242 | for_each_child_of_node(of_node, np) { | |
161ba1f1 KM |
243 | if (i == rsnd_mod_id(mod) && (!chan)) |
244 | chan = of_dma_request_slave_channel(np, name); | |
72adc61f KM |
245 | i++; |
246 | } | |
247 | ||
161ba1f1 | 248 | /* It should call of_node_put(), since, it is rsnd_xxx_of_node() */ |
72adc61f KM |
249 | of_node_put(of_node); |
250 | ||
251 | return chan; | |
252 | } | |
253 | ||
81ecbb65 | 254 | static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, |
b99305d2 | 255 | struct rsnd_dma *dma, |
3c68565b | 256 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) |
bfe834be | 257 | { |
9b99e9a7 | 258 | struct rsnd_priv *priv = rsnd_io_to_priv(io); |
940e9479 | 259 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); |
edce5c49 KM |
260 | struct dma_chan *chan; |
261 | ||
262 | /* try to get DMAEngine channel */ | |
263 | chan = rsnd_dmaen_request_channel(io, mod_from, mod_to); | |
264 | if (IS_ERR_OR_NULL(chan)) { | |
265 | /* | |
266 | * DMA failed. try to PIO mode | |
267 | * see | |
268 | * rsnd_ssi_fallback() | |
269 | * rsnd_rdai_continuance_probe() | |
270 | */ | |
271 | return -EAGAIN; | |
72adc61f | 272 | } |
bfe834be | 273 | |
edce5c49 | 274 | dma_release_channel(chan); |
bfe834be | 275 | |
940e9479 KM |
276 | dmac->dmaen_num++; |
277 | ||
bfe834be | 278 | return 0; |
bfe834be KM |
279 | } |
280 | ||
07b7acb5 KM |
281 | static int rsnd_dmaen_pointer(struct rsnd_mod *mod, |
282 | struct rsnd_dai_stream *io, | |
283 | snd_pcm_uframes_t *pointer) | |
284 | { | |
285 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | |
286 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); | |
287 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | |
288 | struct dma_tx_state state; | |
289 | enum dma_status status; | |
290 | unsigned int pos = 0; | |
291 | ||
292 | status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state); | |
293 | if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { | |
294 | if (state.residue > 0 && state.residue <= dmaen->dma_len) | |
295 | pos = dmaen->dma_len - state.residue; | |
296 | } | |
297 | *pointer = bytes_to_frames(runtime, pos); | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
497debaa | 302 | static struct rsnd_mod_ops rsnd_dmaen_ops = { |
ddea1b2e | 303 | .name = "audmac", |
edce5c49 KM |
304 | .nolock_start = rsnd_dmaen_nolock_start, |
305 | .nolock_stop = rsnd_dmaen_nolock_stop, | |
3c68565b KM |
306 | .start = rsnd_dmaen_start, |
307 | .stop = rsnd_dmaen_stop, | |
07b7acb5 | 308 | .pointer= rsnd_dmaen_pointer, |
3c68565b KM |
309 | }; |
310 | ||
288f392e KM |
311 | /* |
312 | * Audio DMAC peri peri | |
313 | */ | |
314 | static const u8 gen2_id_table_ssiu[] = { | |
315 | 0x00, /* SSI00 */ | |
316 | 0x04, /* SSI10 */ | |
317 | 0x08, /* SSI20 */ | |
318 | 0x0c, /* SSI3 */ | |
319 | 0x0d, /* SSI4 */ | |
320 | 0x0e, /* SSI5 */ | |
321 | 0x0f, /* SSI6 */ | |
322 | 0x10, /* SSI7 */ | |
323 | 0x11, /* SSI8 */ | |
324 | 0x12, /* SSI90 */ | |
325 | }; | |
326 | static const u8 gen2_id_table_scu[] = { | |
327 | 0x2d, /* SCU_SRCI0 */ | |
328 | 0x2e, /* SCU_SRCI1 */ | |
329 | 0x2f, /* SCU_SRCI2 */ | |
330 | 0x30, /* SCU_SRCI3 */ | |
331 | 0x31, /* SCU_SRCI4 */ | |
332 | 0x32, /* SCU_SRCI5 */ | |
333 | 0x33, /* SCU_SRCI6 */ | |
334 | 0x34, /* SCU_SRCI7 */ | |
335 | 0x35, /* SCU_SRCI8 */ | |
336 | 0x36, /* SCU_SRCI9 */ | |
337 | }; | |
338 | static const u8 gen2_id_table_cmd[] = { | |
339 | 0x37, /* SCU_CMD0 */ | |
340 | 0x38, /* SCU_CMD1 */ | |
341 | }; | |
342 | ||
9b99e9a7 KM |
343 | static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, |
344 | struct rsnd_mod *mod) | |
288f392e | 345 | { |
288f392e KM |
346 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); |
347 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | |
348 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | |
349 | const u8 *entry = NULL; | |
350 | int id = rsnd_mod_id(mod); | |
351 | int size = 0; | |
352 | ||
353 | if (mod == ssi) { | |
354 | entry = gen2_id_table_ssiu; | |
355 | size = ARRAY_SIZE(gen2_id_table_ssiu); | |
356 | } else if (mod == src) { | |
357 | entry = gen2_id_table_scu; | |
358 | size = ARRAY_SIZE(gen2_id_table_scu); | |
359 | } else if (mod == dvc) { | |
360 | entry = gen2_id_table_cmd; | |
361 | size = ARRAY_SIZE(gen2_id_table_cmd); | |
362 | } | |
363 | ||
ee057d2e KM |
364 | if ((!entry) || (size <= id)) { |
365 | struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); | |
288f392e | 366 | |
ee057d2e KM |
367 | dev_err(dev, "unknown connection (%s[%d])\n", |
368 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
369 | ||
370 | /* use non-prohibited SRS number as error */ | |
371 | return 0x00; /* SSI00 */ | |
372 | } | |
288f392e KM |
373 | |
374 | return entry[id]; | |
375 | } | |
376 | ||
9b99e9a7 KM |
377 | static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io, |
378 | struct rsnd_mod *mod_from, | |
288f392e KM |
379 | struct rsnd_mod *mod_to) |
380 | { | |
9b99e9a7 KM |
381 | return (rsnd_dmapp_get_id(io, mod_from) << 24) + |
382 | (rsnd_dmapp_get_id(io, mod_to) << 16); | |
288f392e KM |
383 | } |
384 | ||
385 | #define rsnd_dmapp_addr(dmac, dma, reg) \ | |
0d00a521 KM |
386 | (dmac->base + 0x20 + reg + \ |
387 | (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) | |
288f392e KM |
388 | static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) |
389 | { | |
940e9479 | 390 | struct rsnd_mod *mod = rsnd_mod_get(dma); |
288f392e KM |
391 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
392 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | |
393 | struct device *dev = rsnd_priv_to_dev(priv); | |
394 | ||
395 | dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); | |
396 | ||
397 | iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); | |
398 | } | |
399 | ||
400 | static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) | |
401 | { | |
940e9479 | 402 | struct rsnd_mod *mod = rsnd_mod_get(dma); |
288f392e KM |
403 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
404 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | |
405 | ||
406 | return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); | |
407 | } | |
408 | ||
62a10498 KM |
409 | static void rsnd_dmapp_bset(struct rsnd_dma *dma, u32 data, u32 mask, u32 reg) |
410 | { | |
411 | struct rsnd_mod *mod = rsnd_mod_get(dma); | |
412 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | |
413 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | |
9986943e | 414 | void __iomem *addr = rsnd_dmapp_addr(dmac, dma, reg); |
62a10498 KM |
415 | u32 val = ioread32(addr); |
416 | ||
417 | val &= ~mask; | |
418 | val |= (data & mask); | |
419 | ||
420 | iowrite32(val, addr); | |
421 | } | |
422 | ||
497debaa KM |
423 | static int rsnd_dmapp_stop(struct rsnd_mod *mod, |
424 | struct rsnd_dai_stream *io, | |
425 | struct rsnd_priv *priv) | |
288f392e | 426 | { |
76c80b5b | 427 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); |
288f392e KM |
428 | int i; |
429 | ||
62a10498 | 430 | rsnd_dmapp_bset(dma, 0, PDMACHCR_DE, PDMACHCR); |
288f392e KM |
431 | |
432 | for (i = 0; i < 1024; i++) { | |
62a10498 | 433 | if (0 == (rsnd_dmapp_read(dma, PDMACHCR) & PDMACHCR_DE)) |
9c66eedc | 434 | return 0; |
288f392e KM |
435 | udelay(1); |
436 | } | |
497debaa | 437 | |
9c66eedc | 438 | return -EIO; |
288f392e KM |
439 | } |
440 | ||
497debaa KM |
441 | static int rsnd_dmapp_start(struct rsnd_mod *mod, |
442 | struct rsnd_dai_stream *io, | |
443 | struct rsnd_priv *priv) | |
288f392e | 444 | { |
76c80b5b | 445 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); |
0d00a521 KM |
446 | struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); |
447 | ||
288f392e KM |
448 | rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); |
449 | rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); | |
0d00a521 | 450 | rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); |
497debaa KM |
451 | |
452 | return 0; | |
288f392e KM |
453 | } |
454 | ||
81ecbb65 | 455 | static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, |
b99305d2 | 456 | struct rsnd_dma *dma, |
81ecbb65 | 457 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) |
288f392e | 458 | { |
0d00a521 | 459 | struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); |
9b99e9a7 | 460 | struct rsnd_priv *priv = rsnd_io_to_priv(io); |
288f392e KM |
461 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); |
462 | struct device *dev = rsnd_priv_to_dev(priv); | |
463 | ||
0d00a521 | 464 | dmapp->dmapp_id = dmac->dmapp_num; |
9b99e9a7 | 465 | dmapp->chcr = rsnd_dmapp_get_chcr(io, mod_from, mod_to) | PDMACHCR_DE; |
288f392e KM |
466 | |
467 | dmac->dmapp_num++; | |
468 | ||
6ec6fb6f GU |
469 | dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", |
470 | dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); | |
288f392e KM |
471 | |
472 | return 0; | |
473 | } | |
474 | ||
497debaa | 475 | static struct rsnd_mod_ops rsnd_dmapp_ops = { |
ddea1b2e | 476 | .name = "audmac-pp", |
288f392e KM |
477 | .start = rsnd_dmapp_start, |
478 | .stop = rsnd_dmapp_stop, | |
288f392e KM |
479 | .quit = rsnd_dmapp_stop, |
480 | }; | |
481 | ||
482 | /* | |
483 | * Common DMAC Interface | |
484 | */ | |
485 | ||
747c71b1 KM |
486 | /* |
487 | * DMA read/write register offset | |
488 | * | |
489 | * RSND_xxx_I_N for Audio DMAC input | |
490 | * RSND_xxx_O_N for Audio DMAC output | |
491 | * RSND_xxx_I_P for Audio DMAC peri peri input | |
492 | * RSND_xxx_O_P for Audio DMAC peri peri output | |
493 | * | |
494 | * ex) R-Car H2 case | |
495 | * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out | |
496 | * SSI : 0xec541000 / 0xec241008 / 0xec24100c | |
497 | * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 | |
498 | * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 | |
499 | * CMD : 0xec500000 / / 0xec008000 0xec308000 | |
500 | */ | |
501 | #define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) | |
502 | #define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) | |
503 | ||
504 | #define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | |
505 | #define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | |
506 | ||
507 | #define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | |
508 | #define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | |
509 | ||
510 | #define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) | |
511 | #define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) | |
512 | ||
513 | #define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) | |
514 | #define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) | |
515 | ||
516 | #define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) | |
517 | #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) | |
518 | ||
519 | static dma_addr_t | |
9b99e9a7 | 520 | rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, |
747c71b1 KM |
521 | struct rsnd_mod *mod, |
522 | int is_play, int is_from) | |
523 | { | |
9b99e9a7 | 524 | struct rsnd_priv *priv = rsnd_io_to_priv(io); |
747c71b1 | 525 | struct device *dev = rsnd_priv_to_dev(priv); |
747c71b1 KM |
526 | phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); |
527 | phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); | |
528 | int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); | |
529 | int use_src = !!rsnd_io_to_mod_src(io); | |
9269e3c3 | 530 | int use_cmd = !!rsnd_io_to_mod_dvc(io) || |
70fb1052 | 531 | !!rsnd_io_to_mod_mix(io) || |
9269e3c3 | 532 | !!rsnd_io_to_mod_ctu(io); |
747c71b1 KM |
533 | int id = rsnd_mod_id(mod); |
534 | struct dma_addr { | |
535 | dma_addr_t out_addr; | |
536 | dma_addr_t in_addr; | |
537 | } dma_addrs[3][2][3] = { | |
538 | /* SRC */ | |
b65cb7a5 | 539 | /* Capture */ |
747c71b1 | 540 | {{{ 0, 0 }, |
747c71b1 KM |
541 | { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, |
542 | { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, | |
543 | /* Playback */ | |
544 | {{ 0, 0, }, | |
545 | { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, | |
546 | { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } | |
547 | }, | |
548 | /* SSI */ | |
549 | /* Capture */ | |
550 | {{{ RDMA_SSI_O_N(ssi, id), 0 }, | |
551 | { RDMA_SSIU_O_P(ssi, id), 0 }, | |
552 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | |
553 | /* Playback */ | |
554 | {{ 0, RDMA_SSI_I_N(ssi, id) }, | |
555 | { 0, RDMA_SSIU_I_P(ssi, id) }, | |
556 | { 0, RDMA_SSIU_I_P(ssi, id) } } | |
557 | }, | |
558 | /* SSIU */ | |
559 | /* Capture */ | |
560 | {{{ RDMA_SSIU_O_N(ssi, id), 0 }, | |
561 | { RDMA_SSIU_O_P(ssi, id), 0 }, | |
562 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | |
563 | /* Playback */ | |
564 | {{ 0, RDMA_SSIU_I_N(ssi, id) }, | |
565 | { 0, RDMA_SSIU_I_P(ssi, id) }, | |
566 | { 0, RDMA_SSIU_I_P(ssi, id) } } }, | |
567 | }; | |
568 | ||
569 | /* it shouldn't happen */ | |
9269e3c3 | 570 | if (use_cmd && !use_src) |
747c71b1 KM |
571 | dev_err(dev, "DVC is selected without SRC\n"); |
572 | ||
573 | /* use SSIU or SSI ? */ | |
b415b4d3 | 574 | if (is_ssi && rsnd_ssi_use_busif(io)) |
747c71b1 KM |
575 | is_ssi++; |
576 | ||
577 | return (is_from) ? | |
9269e3c3 KM |
578 | dma_addrs[is_ssi][is_play][use_src + use_cmd].out_addr : |
579 | dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr; | |
747c71b1 KM |
580 | } |
581 | ||
9b99e9a7 | 582 | static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io, |
747c71b1 KM |
583 | struct rsnd_mod *mod, |
584 | int is_play, int is_from) | |
585 | { | |
9b99e9a7 KM |
586 | struct rsnd_priv *priv = rsnd_io_to_priv(io); |
587 | ||
747c71b1 KM |
588 | /* |
589 | * gen1 uses default DMA addr | |
590 | */ | |
591 | if (rsnd_is_gen1(priv)) | |
592 | return 0; | |
593 | ||
594 | if (!mod) | |
595 | return 0; | |
596 | ||
9b99e9a7 | 597 | return rsnd_gen2_dma_addr(io, mod, is_play, is_from); |
747c71b1 KM |
598 | } |
599 | ||
7dfb4919 | 600 | #define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */ |
940e9479 | 601 | static void rsnd_dma_of_path(struct rsnd_mod *this, |
9b99e9a7 | 602 | struct rsnd_dai_stream *io, |
bfe834be KM |
603 | int is_play, |
604 | struct rsnd_mod **mod_from, | |
605 | struct rsnd_mod **mod_to) | |
606 | { | |
bfe834be KM |
607 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); |
608 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | |
9269e3c3 | 609 | struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); |
70fb1052 | 610 | struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); |
bfe834be KM |
611 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); |
612 | struct rsnd_mod *mod[MOD_MAX]; | |
7dfb4919 KM |
613 | struct rsnd_mod *mod_start, *mod_end; |
614 | struct rsnd_priv *priv = rsnd_mod_to_priv(this); | |
615 | struct device *dev = rsnd_priv_to_dev(priv); | |
40854c64 | 616 | int nr, i, idx; |
bfe834be | 617 | |
7dfb4919 KM |
618 | if (!ssi) |
619 | return; | |
bfe834be | 620 | |
7dfb4919 KM |
621 | nr = 0; |
622 | for (i = 0; i < MOD_MAX; i++) { | |
bfe834be | 623 | mod[i] = NULL; |
7dfb4919 KM |
624 | nr += !!rsnd_io_to_mod(io, i); |
625 | } | |
bfe834be KM |
626 | |
627 | /* | |
7dfb4919 KM |
628 | * [S] -*-> [E] |
629 | * [S] -*-> SRC -o-> [E] | |
630 | * [S] -*-> SRC -> DVC -o-> [E] | |
631 | * [S] -*-> SRC -> CTU -> MIX -> DVC -o-> [E] | |
632 | * | |
633 | * playback [S] = mem | |
634 | * [E] = SSI | |
bfe834be | 635 | * |
7dfb4919 KM |
636 | * capture [S] = SSI |
637 | * [E] = mem | |
bfe834be | 638 | * |
7dfb4919 KM |
639 | * -*-> Audio DMAC |
640 | * -o-> Audio DMAC peri peri | |
bfe834be | 641 | */ |
7dfb4919 KM |
642 | mod_start = (is_play) ? NULL : ssi; |
643 | mod_end = (is_play) ? ssi : NULL; | |
bfe834be | 644 | |
40854c64 KM |
645 | idx = 0; |
646 | mod[idx++] = mod_start; | |
7dfb4919 KM |
647 | for (i = 1; i < nr; i++) { |
648 | if (src) { | |
40854c64 | 649 | mod[idx++] = src; |
bfe834be | 650 | src = NULL; |
9269e3c3 | 651 | } else if (ctu) { |
40854c64 | 652 | mod[idx++] = ctu; |
9269e3c3 | 653 | ctu = NULL; |
70fb1052 | 654 | } else if (mix) { |
40854c64 | 655 | mod[idx++] = mix; |
70fb1052 | 656 | mix = NULL; |
7dfb4919 | 657 | } else if (dvc) { |
40854c64 | 658 | mod[idx++] = dvc; |
bfe834be KM |
659 | dvc = NULL; |
660 | } | |
bfe834be | 661 | } |
40854c64 | 662 | mod[idx] = mod_end; |
bfe834be | 663 | |
7dfb4919 KM |
664 | /* |
665 | * | SSI | SRC | | |
666 | * -------------+-----+-----+ | |
667 | * is_play | o | * | | |
668 | * !is_play | * | o | | |
669 | */ | |
670 | if ((this == ssi) == (is_play)) { | |
40854c64 KM |
671 | *mod_from = mod[idx - 1]; |
672 | *mod_to = mod[idx]; | |
bfe834be | 673 | } else { |
7dfb4919 KM |
674 | *mod_from = mod[0]; |
675 | *mod_to = mod[1]; | |
676 | } | |
677 | ||
678 | dev_dbg(dev, "module connection (this is %s[%d])\n", | |
679 | rsnd_mod_name(this), rsnd_mod_id(this)); | |
40854c64 | 680 | for (i = 0; i <= idx; i++) { |
7dfb4919 | 681 | dev_dbg(dev, " %s[%d]%s\n", |
9b6ea250 KM |
682 | rsnd_mod_name(mod[i] ? mod[i] : &mem), |
683 | rsnd_mod_id (mod[i] ? mod[i] : &mem), | |
684 | (mod[i] == *mod_from) ? " from" : | |
685 | (mod[i] == *mod_to) ? " to" : ""); | |
bfe834be KM |
686 | } |
687 | } | |
688 | ||
81cb7124 KM |
689 | static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, |
690 | struct rsnd_mod **dma_mod) | |
3c68565b | 691 | { |
7dfb4919 KM |
692 | struct rsnd_mod *mod_from = NULL; |
693 | struct rsnd_mod *mod_to = NULL; | |
9b99e9a7 | 694 | struct rsnd_priv *priv = rsnd_io_to_priv(io); |
8537483a | 695 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); |
ddea1b2e | 696 | struct device *dev = rsnd_priv_to_dev(priv); |
81cb7124 | 697 | struct rsnd_dma *dma; |
497debaa KM |
698 | struct rsnd_mod_ops *ops; |
699 | enum rsnd_mod_type type; | |
b99305d2 | 700 | int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, |
81ecbb65 | 701 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); |
3c68565b | 702 | int is_play = rsnd_io_is_play(io); |
940e9479 | 703 | int ret, dma_id; |
3c68565b | 704 | |
8537483a KM |
705 | /* |
706 | * DMA failed. try to PIO mode | |
707 | * see | |
708 | * rsnd_ssi_fallback() | |
709 | * rsnd_rdai_continuance_probe() | |
710 | */ | |
711 | if (!dmac) | |
355cb84f | 712 | return -EAGAIN; |
232c00b6 | 713 | |
940e9479 | 714 | rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to); |
3c68565b | 715 | |
288f392e | 716 | /* for Gen2 */ |
81ecbb65 | 717 | if (mod_from && mod_to) { |
497debaa | 718 | ops = &rsnd_dmapp_ops; |
81ecbb65 | 719 | attach = rsnd_dmapp_attach; |
940e9479 | 720 | dma_id = dmac->dmapp_num; |
497debaa | 721 | type = RSND_MOD_AUDMAPP; |
81ecbb65 | 722 | } else { |
497debaa | 723 | ops = &rsnd_dmaen_ops; |
81ecbb65 | 724 | attach = rsnd_dmaen_attach; |
940e9479 | 725 | dma_id = dmac->dmaen_num; |
497debaa | 726 | type = RSND_MOD_AUDMA; |
81ecbb65 | 727 | } |
288f392e KM |
728 | |
729 | /* for Gen1, overwrite */ | |
81ecbb65 | 730 | if (rsnd_is_gen1(priv)) { |
497debaa | 731 | ops = &rsnd_dmaen_ops; |
81ecbb65 | 732 | attach = rsnd_dmaen_attach; |
940e9479 | 733 | dma_id = dmac->dmaen_num; |
497debaa | 734 | type = RSND_MOD_AUDMA; |
81ecbb65 | 735 | } |
3c68565b | 736 | |
81cb7124 KM |
737 | dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); |
738 | if (!dma) | |
739 | return -ENOMEM; | |
940e9479 | 740 | |
81cb7124 | 741 | *dma_mod = rsnd_mod_get(dma); |
940e9479 | 742 | |
81cb7124 KM |
743 | ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, |
744 | rsnd_mod_get_status, type, dma_id); | |
745 | if (ret < 0) | |
746 | return ret; | |
c90269c1 | 747 | |
81cb7124 KM |
748 | dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", |
749 | rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), | |
9b6ea250 KM |
750 | rsnd_mod_name(mod_from ? mod_from : &mem), |
751 | rsnd_mod_id (mod_from ? mod_from : &mem), | |
752 | rsnd_mod_name(mod_to ? mod_to : &mem), | |
753 | rsnd_mod_id (mod_to ? mod_to : &mem)); | |
81cb7124 KM |
754 | |
755 | ret = attach(io, dma, mod_from, mod_to); | |
756 | if (ret < 0) | |
757 | return ret; | |
232c00b6 | 758 | |
81cb7124 KM |
759 | dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); |
760 | dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); | |
761 | dma->mod_from = mod_from; | |
762 | dma->mod_to = mod_to; | |
763 | ||
764 | return 0; | |
765 | } | |
766 | ||
767 | int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, | |
768 | struct rsnd_mod **dma_mod) | |
769 | { | |
770 | if (!(*dma_mod)) { | |
771 | int ret = rsnd_dma_alloc(io, mod, dma_mod); | |
355cb84f | 772 | |
355cb84f KM |
773 | if (ret < 0) |
774 | return ret; | |
775 | } | |
776 | ||
81cb7124 | 777 | return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type); |
3c68565b | 778 | } |
288f392e | 779 | |
2ea6b074 | 780 | int rsnd_dma_probe(struct rsnd_priv *priv) |
288f392e | 781 | { |
2ea6b074 | 782 | struct platform_device *pdev = rsnd_priv_to_pdev(priv); |
288f392e KM |
783 | struct device *dev = rsnd_priv_to_dev(priv); |
784 | struct rsnd_dma_ctrl *dmac; | |
785 | struct resource *res; | |
786 | ||
787 | /* | |
788 | * for Gen1 | |
789 | */ | |
790 | if (rsnd_is_gen1(priv)) | |
791 | return 0; | |
792 | ||
793 | /* | |
794 | * for Gen2 | |
795 | */ | |
796 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); | |
797 | dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); | |
798 | if (!dmac || !res) { | |
799 | dev_err(dev, "dma allocate failed\n"); | |
8537483a | 800 | return 0; /* it will be PIO mode */ |
288f392e KM |
801 | } |
802 | ||
803 | dmac->dmapp_num = 0; | |
804 | dmac->base = devm_ioremap_resource(dev, res); | |
805 | if (IS_ERR(dmac->base)) | |
806 | return PTR_ERR(dmac->base); | |
807 | ||
808 | priv->dma = dmac; | |
809 | ||
9b6ea250 KM |
810 | /* dummy mem mod for debug */ |
811 | return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); | |
288f392e | 812 | } |