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