Commit | Line | Data |
---|---|---|
310355c1 VB |
1 | /* |
2 | * ALSA PCM interface for the TI DAVINCI processor | |
3 | * | |
d6b52039 | 4 | * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> |
310355c1 | 5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> |
1e224f32 | 6 | * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.com> |
310355c1 VB |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/dma-mapping.h> | |
9cd28ab0 | 18 | #include <linux/kernel.h> |
b8ec56d8 | 19 | #include <linux/genalloc.h> |
3ad7a42d | 20 | #include <linux/platform_data/edma.h> |
310355c1 VB |
21 | |
22 | #include <sound/core.h> | |
23 | #include <sound/pcm.h> | |
24 | #include <sound/pcm_params.h> | |
25 | #include <sound/soc.h> | |
26 | ||
27 | #include <asm/dma.h> | |
28 | ||
29 | #include "davinci-pcm.h" | |
30 | ||
1e224f32 TK |
31 | #ifdef DEBUG |
32 | static void print_buf_info(int slot, char *name) | |
33 | { | |
34 | struct edmacc_param p; | |
35 | if (slot < 0) | |
36 | return; | |
37 | edma_read_slot(slot, &p); | |
38 | printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n", | |
39 | name, slot, p.opt, p.src, p.a_b_cnt, p.dst); | |
40 | printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n", | |
41 | p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt); | |
42 | } | |
43 | #else | |
44 | static void print_buf_info(int slot, char *name) | |
45 | { | |
46 | } | |
47 | #endif | |
48 | ||
49 | static struct snd_pcm_hardware pcm_hardware_playback = { | |
310355c1 VB |
50 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
51 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | |
52e2c5d3 BG |
52 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME| |
53 | SNDRV_PCM_INFO_BATCH), | |
310355c1 VB |
54 | .buffer_bytes_max = 128 * 1024, |
55 | .period_bytes_min = 32, | |
56 | .period_bytes_max = 8 * 1024, | |
57 | .periods_min = 16, | |
58 | .periods_max = 255, | |
59 | .fifo_size = 0, | |
60 | }; | |
61 | ||
1e224f32 TK |
62 | static struct snd_pcm_hardware pcm_hardware_capture = { |
63 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | |
64 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | |
52e2c5d3 BG |
65 | SNDRV_PCM_INFO_PAUSE | |
66 | SNDRV_PCM_INFO_BATCH), | |
1e224f32 TK |
67 | .buffer_bytes_max = 128 * 1024, |
68 | .period_bytes_min = 32, | |
69 | .period_bytes_max = 8 * 1024, | |
70 | .periods_min = 16, | |
71 | .periods_max = 255, | |
72 | .fifo_size = 0, | |
73 | }; | |
74 | ||
75 | /* | |
76 | * How ping/pong works.... | |
77 | * | |
78 | * Playback: | |
79 | * ram_params - copys 2*ping_size from start of SDRAM to iram, | |
80 | * links to ram_link2 | |
81 | * ram_link2 - copys rest of SDRAM to iram in ping_size units, | |
82 | * links to ram_link | |
83 | * ram_link - copys entire SDRAM to iram in ping_size uints, | |
84 | * links to self | |
85 | * | |
86 | * asp_params - same as asp_link[0] | |
87 | * asp_link[0] - copys from lower half of iram to asp port | |
88 | * links to asp_link[1], triggers iram copy event on completion | |
89 | * asp_link[1] - copys from upper half of iram to asp port | |
90 | * links to asp_link[0], triggers iram copy event on completion | |
91 | * triggers interrupt only needed to let upper SOC levels update position | |
92 | * in stream on completion | |
93 | * | |
94 | * When playback is started: | |
95 | * ram_params started | |
96 | * asp_params started | |
97 | * | |
98 | * Capture: | |
99 | * ram_params - same as ram_link, | |
100 | * links to ram_link | |
101 | * ram_link - same as playback | |
102 | * links to self | |
103 | * | |
104 | * asp_params - same as playback | |
105 | * asp_link[0] - same as playback | |
106 | * asp_link[1] - same as playback | |
107 | * | |
108 | * When capture is started: | |
109 | * asp_params started | |
110 | */ | |
310355c1 VB |
111 | struct davinci_runtime_data { |
112 | spinlock_t lock; | |
113 | int period; /* current DMA period */ | |
1587ea31 TK |
114 | int asp_channel; /* Master DMA channel */ |
115 | int asp_link[2]; /* asp parameter link channel, ping/pong */ | |
310355c1 | 116 | struct davinci_pcm_dma_params *params; /* DMA params */ |
1e224f32 TK |
117 | int ram_channel; |
118 | int ram_link; | |
119 | int ram_link2; | |
120 | struct edmacc_param asp_params; | |
121 | struct edmacc_param ram_params; | |
310355c1 VB |
122 | }; |
123 | ||
10ab3bfd BG |
124 | static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream) |
125 | { | |
126 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
127 | struct snd_pcm_runtime *runtime = substream->runtime; | |
128 | ||
129 | prtd->period++; | |
130 | if (unlikely(prtd->period >= runtime->periods)) | |
131 | prtd->period = 0; | |
132 | } | |
133 | ||
134 | static void davinci_pcm_period_reset(struct snd_pcm_substream *substream) | |
135 | { | |
136 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
137 | ||
138 | prtd->period = 0; | |
139 | } | |
1e224f32 TK |
140 | /* |
141 | * Not used with ping/pong | |
142 | */ | |
310355c1 VB |
143 | static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) |
144 | { | |
145 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
146 | struct snd_pcm_runtime *runtime = substream->runtime; | |
310355c1 VB |
147 | unsigned int period_size; |
148 | unsigned int dma_offset; | |
149 | dma_addr_t dma_pos; | |
150 | dma_addr_t src, dst; | |
151 | unsigned short src_bidx, dst_bidx; | |
4fa9c1a5 | 152 | unsigned short src_cidx, dst_cidx; |
310355c1 | 153 | unsigned int data_type; |
6a99fb5f | 154 | unsigned short acnt; |
310355c1 | 155 | unsigned int count; |
4fa9c1a5 | 156 | unsigned int fifo_level; |
310355c1 VB |
157 | |
158 | period_size = snd_pcm_lib_period_bytes(substream); | |
159 | dma_offset = prtd->period * period_size; | |
160 | dma_pos = runtime->dma_addr + dma_offset; | |
4fa9c1a5 | 161 | fifo_level = prtd->params->fifo_level; |
310355c1 | 162 | |
9cd28ab0 | 163 | pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " |
be4ff961 BG |
164 | "dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos, |
165 | period_size); | |
310355c1 VB |
166 | |
167 | data_type = prtd->params->data_type; | |
168 | count = period_size / data_type; | |
4fa9c1a5 | 169 | if (fifo_level) |
7c21a781 | 170 | count /= fifo_level; |
310355c1 VB |
171 | |
172 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
173 | src = dma_pos; | |
174 | dst = prtd->params->dma_addr; | |
175 | src_bidx = data_type; | |
2952b27e | 176 | dst_bidx = 4; |
7c21a781 | 177 | src_cidx = data_type * fifo_level; |
4fa9c1a5 | 178 | dst_cidx = 0; |
310355c1 VB |
179 | } else { |
180 | src = prtd->params->dma_addr; | |
181 | dst = dma_pos; | |
182 | src_bidx = 0; | |
183 | dst_bidx = data_type; | |
4fa9c1a5 | 184 | src_cidx = 0; |
7c21a781 | 185 | dst_cidx = data_type * fifo_level; |
310355c1 VB |
186 | } |
187 | ||
6a99fb5f | 188 | acnt = prtd->params->acnt; |
be4ff961 BG |
189 | edma_set_src(prtd->asp_link[0], src, INCR, W8BIT); |
190 | edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT); | |
4fa9c1a5 | 191 | |
be4ff961 BG |
192 | edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx); |
193 | edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx); | |
4fa9c1a5 C |
194 | |
195 | if (!fifo_level) | |
be4ff961 BG |
196 | edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0, |
197 | ASYNC); | |
4fa9c1a5 | 198 | else |
2952b27e | 199 | edma_set_transfer_params(prtd->asp_link[0], acnt, |
7c21a781 MB |
200 | fifo_level, |
201 | count, fifo_level, | |
2952b27e | 202 | ABSYNC); |
310355c1 VB |
203 | } |
204 | ||
1587ea31 | 205 | static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) |
310355c1 VB |
206 | { |
207 | struct snd_pcm_substream *substream = data; | |
208 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
209 | ||
1e224f32 | 210 | print_buf_info(prtd->ram_channel, "i ram_channel"); |
1587ea31 | 211 | pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status); |
310355c1 | 212 | |
26209379 | 213 | if (unlikely(ch_status != EDMA_DMA_COMPLETE)) |
310355c1 VB |
214 | return; |
215 | ||
216 | if (snd_pcm_running(substream)) { | |
52e2c5d3 | 217 | spin_lock(&prtd->lock); |
1e224f32 TK |
218 | if (prtd->ram_channel < 0) { |
219 | /* No ping/pong must fix up link dma data*/ | |
1e224f32 | 220 | davinci_pcm_enqueue_dma(substream); |
1e224f32 | 221 | } |
52e2c5d3 BG |
222 | davinci_pcm_period_elapsed(substream); |
223 | spin_unlock(&prtd->lock); | |
310355c1 | 224 | snd_pcm_period_elapsed(substream); |
1e224f32 TK |
225 | } |
226 | } | |
227 | ||
b8ec56d8 MP |
228 | #ifdef CONFIG_GENERIC_ALLOCATOR |
229 | static int allocate_sram(struct snd_pcm_substream *substream, | |
230 | struct gen_pool *sram_pool, unsigned size, | |
1e224f32 TK |
231 | struct snd_pcm_hardware *ppcm) |
232 | { | |
233 | struct snd_dma_buffer *buf = &substream->dma_buffer; | |
234 | struct snd_dma_buffer *iram_dma = NULL; | |
235 | dma_addr_t iram_phys = 0; | |
236 | void *iram_virt = NULL; | |
237 | ||
238 | if (buf->private_data || !size) | |
239 | return 0; | |
240 | ||
241 | ppcm->period_bytes_max = size; | |
1112b9e2 | 242 | iram_virt = gen_pool_dma_alloc(sram_pool, size, &iram_phys); |
1e224f32 TK |
243 | if (!iram_virt) |
244 | goto exit1; | |
245 | iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); | |
246 | if (!iram_dma) | |
247 | goto exit2; | |
248 | iram_dma->area = iram_virt; | |
249 | iram_dma->addr = iram_phys; | |
250 | memset(iram_dma->area, 0, size); | |
251 | iram_dma->bytes = size; | |
252 | buf->private_data = iram_dma; | |
253 | return 0; | |
254 | exit2: | |
255 | if (iram_virt) | |
b8ec56d8 | 256 | gen_pool_free(sram_pool, (unsigned)iram_virt, size); |
1e224f32 TK |
257 | exit1: |
258 | return -ENOMEM; | |
259 | } | |
310355c1 | 260 | |
b8ec56d8 MP |
261 | static void davinci_free_sram(struct snd_pcm_substream *substream, |
262 | struct snd_dma_buffer *iram_dma) | |
263 | { | |
264 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
265 | struct gen_pool *sram_pool = prtd->params->sram_pool; | |
266 | ||
267 | gen_pool_free(sram_pool, (unsigned) iram_dma->area, iram_dma->bytes); | |
268 | } | |
269 | #else | |
270 | static int allocate_sram(struct snd_pcm_substream *substream, | |
271 | struct gen_pool *sram_pool, unsigned size, | |
272 | struct snd_pcm_hardware *ppcm) | |
273 | { | |
274 | return 0; | |
275 | } | |
276 | ||
277 | static void davinci_free_sram(struct snd_pcm_substream *substream, | |
278 | struct snd_dma_buffer *iram_dma) | |
279 | { | |
280 | } | |
281 | #endif | |
282 | ||
1e224f32 TK |
283 | /* |
284 | * Only used with ping/pong. | |
285 | * This is called after runtime->dma_addr, period_bytes and data_type are valid | |
286 | */ | |
287 | static int ping_pong_dma_setup(struct snd_pcm_substream *substream) | |
288 | { | |
289 | unsigned short ram_src_cidx, ram_dst_cidx; | |
290 | struct snd_pcm_runtime *runtime = substream->runtime; | |
291 | struct davinci_runtime_data *prtd = runtime->private_data; | |
292 | struct snd_dma_buffer *iram_dma = | |
293 | (struct snd_dma_buffer *)substream->dma_buffer.private_data; | |
294 | struct davinci_pcm_dma_params *params = prtd->params; | |
295 | unsigned int data_type = params->data_type; | |
296 | unsigned int acnt = params->acnt; | |
297 | /* divide by 2 for ping/pong */ | |
298 | unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1; | |
1e224f32 TK |
299 | unsigned int fifo_level = prtd->params->fifo_level; |
300 | unsigned int count; | |
301 | if ((data_type == 0) || (data_type > 4)) { | |
302 | printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); | |
303 | return -EINVAL; | |
304 | } | |
305 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
306 | dma_addr_t asp_src_pong = iram_dma->addr + ping_size; | |
307 | ram_src_cidx = ping_size; | |
308 | ram_dst_cidx = -ping_size; | |
be4ff961 | 309 | edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT); |
1e224f32 | 310 | |
be4ff961 BG |
311 | edma_set_src_index(prtd->asp_link[0], data_type, |
312 | data_type * fifo_level); | |
313 | edma_set_src_index(prtd->asp_link[1], data_type, | |
314 | data_type * fifo_level); | |
1e224f32 | 315 | |
be4ff961 | 316 | edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT); |
1e224f32 TK |
317 | } else { |
318 | dma_addr_t asp_dst_pong = iram_dma->addr + ping_size; | |
319 | ram_src_cidx = -ping_size; | |
320 | ram_dst_cidx = ping_size; | |
be4ff961 | 321 | edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT); |
1e224f32 | 322 | |
be4ff961 BG |
323 | edma_set_dest_index(prtd->asp_link[0], data_type, |
324 | data_type * fifo_level); | |
325 | edma_set_dest_index(prtd->asp_link[1], data_type, | |
326 | data_type * fifo_level); | |
1e224f32 | 327 | |
be4ff961 | 328 | edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT); |
1e224f32 TK |
329 | } |
330 | ||
331 | if (!fifo_level) { | |
332 | count = ping_size / data_type; | |
333 | edma_set_transfer_params(prtd->asp_link[0], acnt, count, | |
334 | 1, 0, ASYNC); | |
335 | edma_set_transfer_params(prtd->asp_link[1], acnt, count, | |
336 | 1, 0, ASYNC); | |
337 | } else { | |
338 | count = ping_size / (data_type * fifo_level); | |
339 | edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level, | |
340 | count, fifo_level, ABSYNC); | |
341 | edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level, | |
342 | count, fifo_level, ABSYNC); | |
343 | } | |
344 | ||
be4ff961 BG |
345 | edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx); |
346 | edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx); | |
347 | edma_set_transfer_params(prtd->ram_link, ping_size, 2, | |
1e224f32 TK |
348 | runtime->periods, 2, ASYNC); |
349 | ||
350 | /* init master params */ | |
351 | edma_read_slot(prtd->asp_link[0], &prtd->asp_params); | |
352 | edma_read_slot(prtd->ram_link, &prtd->ram_params); | |
353 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
354 | struct edmacc_param p_ram; | |
355 | /* Copy entire iram buffer before playback started */ | |
356 | prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1); | |
357 | /* 0 dst_bidx */ | |
358 | prtd->ram_params.src_dst_bidx = (ping_size << 1); | |
359 | /* 0 dst_cidx */ | |
360 | prtd->ram_params.src_dst_cidx = (ping_size << 1); | |
361 | prtd->ram_params.ccnt = 1; | |
362 | ||
363 | /* Skip 1st period */ | |
364 | edma_read_slot(prtd->ram_link, &p_ram); | |
365 | p_ram.src += (ping_size << 1); | |
366 | p_ram.ccnt -= 1; | |
367 | edma_write_slot(prtd->ram_link2, &p_ram); | |
368 | /* | |
369 | * When 1st started, ram -> iram dma channel will fill the | |
370 | * entire iram. Then, whenever a ping/pong asp buffer finishes, | |
371 | * 1/2 iram will be filled. | |
372 | */ | |
373 | prtd->ram_params.link_bcntrld = | |
374 | EDMA_CHAN_SLOT(prtd->ram_link2) << 5; | |
375 | } | |
376 | return 0; | |
377 | } | |
378 | ||
379 | /* 1 asp tx or rx channel using 2 parameter channels | |
380 | * 1 ram to/from iram channel using 1 parameter channel | |
381 | * | |
382 | * Playback | |
383 | * ram copy channel kicks off first, | |
384 | * 1st ram copy of entire iram buffer completion kicks off asp channel | |
385 | * asp tcc always kicks off ram copy of 1/2 iram buffer | |
386 | * | |
387 | * Record | |
388 | * asp channel starts, tcc kicks off ram copy | |
389 | */ | |
390 | static int request_ping_pong(struct snd_pcm_substream *substream, | |
391 | struct davinci_runtime_data *prtd, | |
392 | struct snd_dma_buffer *iram_dma) | |
393 | { | |
394 | dma_addr_t asp_src_ping; | |
395 | dma_addr_t asp_dst_ping; | |
be4ff961 | 396 | int ret; |
1e224f32 TK |
397 | struct davinci_pcm_dma_params *params = prtd->params; |
398 | ||
399 | /* Request ram master channel */ | |
be4ff961 | 400 | ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, |
1e224f32 | 401 | davinci_pcm_dma_irq, substream, |
48519f0a | 402 | prtd->params->ram_chan_q); |
be4ff961 | 403 | if (ret < 0) |
1e224f32 TK |
404 | goto exit1; |
405 | ||
406 | /* Request ram link channel */ | |
be4ff961 | 407 | ret = prtd->ram_link = edma_alloc_slot( |
1e224f32 | 408 | EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); |
be4ff961 | 409 | if (ret < 0) |
1e224f32 TK |
410 | goto exit2; |
411 | ||
be4ff961 | 412 | ret = prtd->asp_link[1] = edma_alloc_slot( |
1e224f32 | 413 | EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); |
be4ff961 | 414 | if (ret < 0) |
1e224f32 TK |
415 | goto exit3; |
416 | ||
417 | prtd->ram_link2 = -1; | |
418 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
be4ff961 | 419 | ret = prtd->ram_link2 = edma_alloc_slot( |
1e224f32 | 420 | EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); |
be4ff961 | 421 | if (ret < 0) |
1e224f32 TK |
422 | goto exit4; |
423 | } | |
424 | /* circle ping-pong buffers */ | |
425 | edma_link(prtd->asp_link[0], prtd->asp_link[1]); | |
426 | edma_link(prtd->asp_link[1], prtd->asp_link[0]); | |
427 | /* circle ram buffers */ | |
428 | edma_link(prtd->ram_link, prtd->ram_link); | |
429 | ||
430 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
431 | asp_src_ping = iram_dma->addr; | |
432 | asp_dst_ping = params->dma_addr; /* fifo */ | |
433 | } else { | |
434 | asp_src_ping = params->dma_addr; /* fifo */ | |
435 | asp_dst_ping = iram_dma->addr; | |
310355c1 | 436 | } |
1e224f32 | 437 | /* ping */ |
be4ff961 BG |
438 | edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT); |
439 | edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT); | |
440 | edma_set_src_index(prtd->asp_link[0], 0, 0); | |
441 | edma_set_dest_index(prtd->asp_link[0], 0, 0); | |
1e224f32 | 442 | |
be4ff961 | 443 | edma_read_slot(prtd->asp_link[0], &prtd->asp_params); |
1e224f32 | 444 | prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); |
fb1e9703 BG |
445 | prtd->asp_params.opt |= TCCHEN | |
446 | EDMA_TCC(prtd->ram_channel & 0x3f); | |
be4ff961 | 447 | edma_write_slot(prtd->asp_link[0], &prtd->asp_params); |
1e224f32 TK |
448 | |
449 | /* pong */ | |
be4ff961 BG |
450 | edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT); |
451 | edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT); | |
452 | edma_set_src_index(prtd->asp_link[1], 0, 0); | |
453 | edma_set_dest_index(prtd->asp_link[1], 0, 0); | |
1e224f32 | 454 | |
be4ff961 | 455 | edma_read_slot(prtd->asp_link[1], &prtd->asp_params); |
1e224f32 TK |
456 | prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); |
457 | /* interrupt after every pong completion */ | |
458 | prtd->asp_params.opt |= TCINTEN | TCCHEN | | |
fb1e9703 | 459 | EDMA_TCC(prtd->ram_channel & 0x3f); |
be4ff961 | 460 | edma_write_slot(prtd->asp_link[1], &prtd->asp_params); |
1e224f32 TK |
461 | |
462 | /* ram */ | |
be4ff961 BG |
463 | edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT); |
464 | edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT); | |
1e224f32 TK |
465 | pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u," |
466 | "for asp:%u %u %u\n", __func__, | |
467 | prtd->ram_channel, prtd->ram_link, prtd->ram_link2, | |
468 | prtd->asp_channel, prtd->asp_link[0], | |
469 | prtd->asp_link[1]); | |
470 | return 0; | |
471 | exit4: | |
472 | edma_free_channel(prtd->asp_link[1]); | |
473 | prtd->asp_link[1] = -1; | |
474 | exit3: | |
475 | edma_free_channel(prtd->ram_link); | |
476 | prtd->ram_link = -1; | |
477 | exit2: | |
478 | edma_free_channel(prtd->ram_channel); | |
479 | prtd->ram_channel = -1; | |
480 | exit1: | |
be4ff961 | 481 | return ret; |
310355c1 VB |
482 | } |
483 | ||
484 | static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) | |
485 | { | |
1e224f32 | 486 | struct snd_dma_buffer *iram_dma; |
310355c1 | 487 | struct davinci_runtime_data *prtd = substream->runtime->private_data; |
1e224f32 | 488 | struct davinci_pcm_dma_params *params = prtd->params; |
be4ff961 | 489 | int ret; |
310355c1 | 490 | |
1e224f32 TK |
491 | if (!params) |
492 | return -ENODEV; | |
310355c1 | 493 | |
1e224f32 | 494 | /* Request asp master DMA channel */ |
be4ff961 | 495 | ret = prtd->asp_channel = edma_alloc_channel(params->channel, |
48519f0a SN |
496 | davinci_pcm_dma_irq, substream, |
497 | prtd->params->asp_chan_q); | |
be4ff961 | 498 | if (ret < 0) |
1e224f32 TK |
499 | goto exit1; |
500 | ||
501 | /* Request asp link channels */ | |
be4ff961 | 502 | ret = prtd->asp_link[0] = edma_alloc_slot( |
1e224f32 | 503 | EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); |
be4ff961 | 504 | if (ret < 0) |
1e224f32 TK |
505 | goto exit2; |
506 | ||
507 | iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data; | |
508 | if (iram_dma) { | |
509 | if (request_ping_pong(substream, prtd, iram_dma) == 0) | |
510 | return 0; | |
511 | printk(KERN_WARNING "%s: dma channel allocation failed," | |
512 | "not using sram\n", __func__); | |
310355c1 | 513 | } |
82075af6 DB |
514 | |
515 | /* Issue transfer completion IRQ when the channel completes a | |
516 | * transfer, then always reload from the same slot (by a kind | |
517 | * of loopback link). The completion IRQ handler will update | |
518 | * the reload slot with a new buffer. | |
519 | * | |
520 | * REVISIT save p_ram here after setting up everything except | |
521 | * the buffer and its length (ccnt) ... use it as a template | |
522 | * so davinci_pcm_enqueue_dma() takes less time in IRQ. | |
523 | */ | |
be4ff961 | 524 | edma_read_slot(prtd->asp_link[0], &prtd->asp_params); |
1e224f32 TK |
525 | prtd->asp_params.opt |= TCINTEN | |
526 | EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); | |
be4ff961 BG |
527 | prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5; |
528 | edma_write_slot(prtd->asp_link[0], &prtd->asp_params); | |
310355c1 | 529 | return 0; |
1e224f32 TK |
530 | exit2: |
531 | edma_free_channel(prtd->asp_channel); | |
532 | prtd->asp_channel = -1; | |
533 | exit1: | |
be4ff961 | 534 | return ret; |
310355c1 VB |
535 | } |
536 | ||
537 | static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |
538 | { | |
539 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
540 | int ret = 0; | |
541 | ||
542 | spin_lock(&prtd->lock); | |
543 | ||
544 | switch (cmd) { | |
545 | case SNDRV_PCM_TRIGGER_START: | |
ef39eb6f BG |
546 | edma_start(prtd->asp_channel); |
547 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | |
548 | prtd->ram_channel >= 0) { | |
549 | /* copy 1st iram buffer */ | |
550 | edma_start(prtd->ram_channel); | |
551 | } | |
552 | break; | |
310355c1 VB |
553 | case SNDRV_PCM_TRIGGER_RESUME: |
554 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
2b7b250d | 555 | edma_resume(prtd->asp_channel); |
310355c1 VB |
556 | break; |
557 | case SNDRV_PCM_TRIGGER_STOP: | |
558 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
559 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
2b7b250d | 560 | edma_pause(prtd->asp_channel); |
310355c1 VB |
561 | break; |
562 | default: | |
563 | ret = -EINVAL; | |
564 | break; | |
565 | } | |
566 | ||
567 | spin_unlock(&prtd->lock); | |
568 | ||
569 | return ret; | |
570 | } | |
571 | ||
572 | static int davinci_pcm_prepare(struct snd_pcm_substream *substream) | |
573 | { | |
574 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | |
310355c1 | 575 | |
52e2c5d3 | 576 | davinci_pcm_period_reset(substream); |
1e224f32 TK |
577 | if (prtd->ram_channel >= 0) { |
578 | int ret = ping_pong_dma_setup(substream); | |
579 | if (ret < 0) | |
580 | return ret; | |
581 | ||
582 | edma_write_slot(prtd->ram_channel, &prtd->ram_params); | |
583 | edma_write_slot(prtd->asp_channel, &prtd->asp_params); | |
584 | ||
585 | print_buf_info(prtd->ram_channel, "ram_channel"); | |
586 | print_buf_info(prtd->ram_link, "ram_link"); | |
587 | print_buf_info(prtd->ram_link2, "ram_link2"); | |
588 | print_buf_info(prtd->asp_channel, "asp_channel"); | |
589 | print_buf_info(prtd->asp_link[0], "asp_link[0]"); | |
590 | print_buf_info(prtd->asp_link[1], "asp_link[1]"); | |
591 | ||
bb5b5fd4 BG |
592 | /* |
593 | * There is a phase offset of 2 periods between the position | |
594 | * used by dma setup and the position reported in the pointer | |
595 | * function. | |
596 | * | |
597 | * The phase offset, when not using ping-pong buffers, is due to | |
598 | * the two consecutive calls to davinci_pcm_enqueue_dma() below. | |
599 | * | |
600 | * Whereas here, with ping-pong buffers, the phase is due to | |
601 | * there being an entire buffer transfer complete before the | |
602 | * first dma completion event triggers davinci_pcm_dma_irq(). | |
603 | */ | |
52e2c5d3 BG |
604 | davinci_pcm_period_elapsed(substream); |
605 | davinci_pcm_period_elapsed(substream); | |
606 | ||
1e224f32 TK |
607 | return 0; |
608 | } | |
310355c1 | 609 | davinci_pcm_enqueue_dma(substream); |
52e2c5d3 | 610 | davinci_pcm_period_elapsed(substream); |
310355c1 | 611 | |
82075af6 | 612 | /* Copy self-linked parameter RAM entry into master channel */ |
1e224f32 TK |
613 | edma_read_slot(prtd->asp_link[0], &prtd->asp_params); |
614 | edma_write_slot(prtd->asp_channel, &prtd->asp_params); | |
6e541475 | 615 | davinci_pcm_enqueue_dma(substream); |
52e2c5d3 | 616 | davinci_pcm_period_elapsed(substream); |
310355c1 VB |
617 | |
618 | return 0; | |
619 | } | |
620 | ||
621 | static snd_pcm_uframes_t | |
622 | davinci_pcm_pointer(struct snd_pcm_substream *substream) | |
623 | { | |
624 | struct snd_pcm_runtime *runtime = substream->runtime; | |
625 | struct davinci_runtime_data *prtd = runtime->private_data; | |
626 | unsigned int offset; | |
1587ea31 | 627 | int asp_count; |
52e2c5d3 | 628 | unsigned int period_size = snd_pcm_lib_period_bytes(substream); |
310355c1 | 629 | |
bb5b5fd4 BG |
630 | /* |
631 | * There is a phase offset of 2 periods between the position used by dma | |
632 | * setup and the position reported in the pointer function. Either +2 in | |
633 | * the dma setup or -2 here in the pointer function (with wrapping, | |
634 | * both) accounts for this offset -- choose the latter since it makes | |
635 | * the first-time setup clearer. | |
636 | */ | |
310355c1 | 637 | spin_lock(&prtd->lock); |
52e2c5d3 | 638 | asp_count = prtd->period - 2; |
310355c1 VB |
639 | spin_unlock(&prtd->lock); |
640 | ||
52e2c5d3 BG |
641 | if (asp_count < 0) |
642 | asp_count += runtime->periods; | |
643 | asp_count *= period_size; | |
644 | ||
1587ea31 | 645 | offset = bytes_to_frames(runtime, asp_count); |
310355c1 VB |
646 | if (offset >= runtime->buffer_size) |
647 | offset = 0; | |
648 | ||
649 | return offset; | |
650 | } | |
651 | ||
652 | static int davinci_pcm_open(struct snd_pcm_substream *substream) | |
653 | { | |
654 | struct snd_pcm_runtime *runtime = substream->runtime; | |
655 | struct davinci_runtime_data *prtd; | |
1e224f32 | 656 | struct snd_pcm_hardware *ppcm; |
310355c1 | 657 | int ret = 0; |
81ac55aa | 658 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
5f712b2b | 659 | struct davinci_pcm_dma_params *pa; |
57512c64 | 660 | struct davinci_pcm_dma_params *params; |
5f712b2b | 661 | |
f0fba2ad | 662 | pa = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); |
57512c64 | 663 | if (!pa) |
81ac55aa | 664 | return -ENODEV; |
57512c64 | 665 | params = &pa[substream->stream]; |
310355c1 | 666 | |
1e224f32 TK |
667 | ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? |
668 | &pcm_hardware_playback : &pcm_hardware_capture; | |
b8ec56d8 | 669 | allocate_sram(substream, params->sram_pool, params->sram_size, ppcm); |
1e224f32 | 670 | snd_soc_set_runtime_hwparams(substream, ppcm); |
6a90d536 TK |
671 | /* ensure that buffer size is a multiple of period size */ |
672 | ret = snd_pcm_hw_constraint_integer(runtime, | |
673 | SNDRV_PCM_HW_PARAM_PERIODS); | |
674 | if (ret < 0) | |
675 | return ret; | |
310355c1 VB |
676 | |
677 | prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); | |
678 | if (prtd == NULL) | |
679 | return -ENOMEM; | |
680 | ||
681 | spin_lock_init(&prtd->lock); | |
81ac55aa | 682 | prtd->params = params; |
1e224f32 TK |
683 | prtd->asp_channel = -1; |
684 | prtd->asp_link[0] = prtd->asp_link[1] = -1; | |
685 | prtd->ram_channel = -1; | |
686 | prtd->ram_link = -1; | |
687 | prtd->ram_link2 = -1; | |
310355c1 VB |
688 | |
689 | runtime->private_data = prtd; | |
690 | ||
691 | ret = davinci_pcm_dma_request(substream); | |
692 | if (ret) { | |
693 | printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n"); | |
694 | kfree(prtd); | |
695 | } | |
696 | ||
697 | return ret; | |
698 | } | |
699 | ||
700 | static int davinci_pcm_close(struct snd_pcm_substream *substream) | |
701 | { | |
702 | struct snd_pcm_runtime *runtime = substream->runtime; | |
703 | struct davinci_runtime_data *prtd = runtime->private_data; | |
704 | ||
1e224f32 TK |
705 | if (prtd->ram_channel >= 0) |
706 | edma_stop(prtd->ram_channel); | |
707 | if (prtd->asp_channel >= 0) | |
708 | edma_stop(prtd->asp_channel); | |
709 | if (prtd->asp_link[0] >= 0) | |
710 | edma_unlink(prtd->asp_link[0]); | |
711 | if (prtd->asp_link[1] >= 0) | |
712 | edma_unlink(prtd->asp_link[1]); | |
713 | if (prtd->ram_link >= 0) | |
714 | edma_unlink(prtd->ram_link); | |
715 | ||
716 | if (prtd->asp_link[0] >= 0) | |
717 | edma_free_slot(prtd->asp_link[0]); | |
718 | if (prtd->asp_link[1] >= 0) | |
719 | edma_free_slot(prtd->asp_link[1]); | |
720 | if (prtd->asp_channel >= 0) | |
721 | edma_free_channel(prtd->asp_channel); | |
722 | if (prtd->ram_link >= 0) | |
723 | edma_free_slot(prtd->ram_link); | |
724 | if (prtd->ram_link2 >= 0) | |
725 | edma_free_slot(prtd->ram_link2); | |
726 | if (prtd->ram_channel >= 0) | |
727 | edma_free_channel(prtd->ram_channel); | |
310355c1 VB |
728 | |
729 | kfree(prtd); | |
730 | ||
731 | return 0; | |
732 | } | |
733 | ||
734 | static int davinci_pcm_hw_params(struct snd_pcm_substream *substream, | |
735 | struct snd_pcm_hw_params *hw_params) | |
736 | { | |
737 | return snd_pcm_lib_malloc_pages(substream, | |
738 | params_buffer_bytes(hw_params)); | |
739 | } | |
740 | ||
741 | static int davinci_pcm_hw_free(struct snd_pcm_substream *substream) | |
742 | { | |
743 | return snd_pcm_lib_free_pages(substream); | |
744 | } | |
745 | ||
746 | static int davinci_pcm_mmap(struct snd_pcm_substream *substream, | |
747 | struct vm_area_struct *vma) | |
748 | { | |
749 | struct snd_pcm_runtime *runtime = substream->runtime; | |
750 | ||
751 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | |
752 | runtime->dma_area, | |
753 | runtime->dma_addr, | |
754 | runtime->dma_bytes); | |
755 | } | |
756 | ||
b2a19d02 | 757 | static struct snd_pcm_ops davinci_pcm_ops = { |
310355c1 VB |
758 | .open = davinci_pcm_open, |
759 | .close = davinci_pcm_close, | |
760 | .ioctl = snd_pcm_lib_ioctl, | |
761 | .hw_params = davinci_pcm_hw_params, | |
762 | .hw_free = davinci_pcm_hw_free, | |
763 | .prepare = davinci_pcm_prepare, | |
764 | .trigger = davinci_pcm_trigger, | |
765 | .pointer = davinci_pcm_pointer, | |
766 | .mmap = davinci_pcm_mmap, | |
767 | }; | |
768 | ||
1e224f32 TK |
769 | static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, |
770 | size_t size) | |
310355c1 VB |
771 | { |
772 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | |
773 | struct snd_dma_buffer *buf = &substream->dma_buffer; | |
310355c1 VB |
774 | |
775 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | |
776 | buf->dev.dev = pcm->card->dev; | |
777 | buf->private_data = NULL; | |
778 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | |
779 | &buf->addr, GFP_KERNEL); | |
780 | ||
9cd28ab0 AB |
781 | pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, " |
782 | "size=%d\n", (void *) buf->area, (void *) buf->addr, size); | |
310355c1 VB |
783 | |
784 | if (!buf->area) | |
785 | return -ENOMEM; | |
786 | ||
787 | buf->bytes = size; | |
788 | return 0; | |
789 | } | |
790 | ||
791 | static void davinci_pcm_free(struct snd_pcm *pcm) | |
792 | { | |
793 | struct snd_pcm_substream *substream; | |
794 | struct snd_dma_buffer *buf; | |
795 | int stream; | |
796 | ||
797 | for (stream = 0; stream < 2; stream++) { | |
1e224f32 | 798 | struct snd_dma_buffer *iram_dma; |
310355c1 VB |
799 | substream = pcm->streams[stream].substream; |
800 | if (!substream) | |
801 | continue; | |
802 | ||
803 | buf = &substream->dma_buffer; | |
804 | if (!buf->area) | |
805 | continue; | |
806 | ||
807 | dma_free_writecombine(pcm->card->dev, buf->bytes, | |
808 | buf->area, buf->addr); | |
809 | buf->area = NULL; | |
4726a57b | 810 | iram_dma = buf->private_data; |
1e224f32 | 811 | if (iram_dma) { |
b8ec56d8 | 812 | davinci_free_sram(substream, iram_dma); |
1e224f32 TK |
813 | kfree(iram_dma); |
814 | } | |
310355c1 VB |
815 | } |
816 | } | |
817 | ||
552d1ef6 | 818 | static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd) |
310355c1 | 819 | { |
552d1ef6 | 820 | struct snd_card *card = rtd->card->snd_card; |
552d1ef6 | 821 | struct snd_pcm *pcm = rtd->pcm; |
310355c1 VB |
822 | int ret; |
823 | ||
c9bd5e69 RK |
824 | ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); |
825 | if (ret) | |
826 | return ret; | |
310355c1 | 827 | |
25e9e756 | 828 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { |
310355c1 | 829 | ret = davinci_pcm_preallocate_dma_buffer(pcm, |
1e224f32 TK |
830 | SNDRV_PCM_STREAM_PLAYBACK, |
831 | pcm_hardware_playback.buffer_bytes_max); | |
310355c1 VB |
832 | if (ret) |
833 | return ret; | |
834 | } | |
835 | ||
25e9e756 | 836 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { |
310355c1 | 837 | ret = davinci_pcm_preallocate_dma_buffer(pcm, |
1e224f32 TK |
838 | SNDRV_PCM_STREAM_CAPTURE, |
839 | pcm_hardware_capture.buffer_bytes_max); | |
310355c1 VB |
840 | if (ret) |
841 | return ret; | |
842 | } | |
843 | ||
844 | return 0; | |
845 | } | |
846 | ||
f0fba2ad LG |
847 | static struct snd_soc_platform_driver davinci_soc_platform = { |
848 | .ops = &davinci_pcm_ops, | |
310355c1 VB |
849 | .pcm_new = davinci_pcm_new, |
850 | .pcm_free = davinci_pcm_free, | |
851 | }; | |
310355c1 | 852 | |
f08095a4 | 853 | int davinci_soc_platform_register(struct device *dev) |
958e792c | 854 | { |
70e7a023 | 855 | return devm_snd_soc_register_platform(dev, &davinci_soc_platform); |
958e792c | 856 | } |
f08095a4 | 857 | EXPORT_SYMBOL_GPL(davinci_soc_platform_register); |
958e792c | 858 | |
310355c1 VB |
859 | MODULE_AUTHOR("Vladimir Barinov"); |
860 | MODULE_DESCRIPTION("TI DAVINCI PCM DMA module"); | |
861 | MODULE_LICENSE("GPL"); |