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