diff options
Diffstat (limited to 'sound/soc/davinci/davinci-pcm.c')
-rw-r--r-- | sound/soc/davinci/davinci-pcm.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c new file mode 100644 index 000000000000..6a76927c9971 --- /dev/null +++ b/sound/soc/davinci/davinci-pcm.c | |||
@@ -0,0 +1,389 @@ | |||
1 | /* | ||
2 | * ALSA PCM interface for the TI DAVINCI processor | ||
3 | * | ||
4 | * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> | ||
5 | * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.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 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | |||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/pcm_params.h> | ||
21 | #include <sound/soc.h> | ||
22 | |||
23 | #include <asm/dma.h> | ||
24 | |||
25 | #include "davinci-pcm.h" | ||
26 | |||
27 | #define DAVINCI_PCM_DEBUG 0 | ||
28 | #if DAVINCI_PCM_DEBUG | ||
29 | #define DPRINTK(x...) printk(KERN_DEBUG x) | ||
30 | #else | ||
31 | #define DPRINTK(x...) | ||
32 | #endif | ||
33 | |||
34 | static struct snd_pcm_hardware davinci_pcm_hardware = { | ||
35 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
36 | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | | ||
37 | SNDRV_PCM_INFO_PAUSE), | ||
38 | .formats = (SNDRV_PCM_FMTBIT_S16_LE), | ||
39 | .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | | ||
40 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | | ||
41 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | | ||
42 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | | ||
43 | SNDRV_PCM_RATE_KNOT), | ||
44 | .rate_min = 8000, | ||
45 | .rate_max = 96000, | ||
46 | .channels_min = 2, | ||
47 | .channels_max = 2, | ||
48 | .buffer_bytes_max = 128 * 1024, | ||
49 | .period_bytes_min = 32, | ||
50 | .period_bytes_max = 8 * 1024, | ||
51 | .periods_min = 16, | ||
52 | .periods_max = 255, | ||
53 | .fifo_size = 0, | ||
54 | }; | ||
55 | |||
56 | struct davinci_runtime_data { | ||
57 | spinlock_t lock; | ||
58 | int period; /* current DMA period */ | ||
59 | int master_lch; /* Master DMA channel */ | ||
60 | int slave_lch; /* Slave DMA channel */ | ||
61 | struct davinci_pcm_dma_params *params; /* DMA params */ | ||
62 | }; | ||
63 | |||
64 | static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) | ||
65 | { | ||
66 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | ||
67 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
68 | int lch = prtd->slave_lch; | ||
69 | unsigned int period_size; | ||
70 | unsigned int dma_offset; | ||
71 | dma_addr_t dma_pos; | ||
72 | dma_addr_t src, dst; | ||
73 | unsigned short src_bidx, dst_bidx; | ||
74 | unsigned int data_type; | ||
75 | unsigned int count; | ||
76 | |||
77 | period_size = snd_pcm_lib_period_bytes(substream); | ||
78 | dma_offset = prtd->period * period_size; | ||
79 | dma_pos = runtime->dma_addr + dma_offset; | ||
80 | |||
81 | DPRINTK("audio_set_dma_params_play channel = %d dma_ptr = %x " | ||
82 | "period_size=%x\n", lch, dma_pos, period_size); | ||
83 | |||
84 | data_type = prtd->params->data_type; | ||
85 | count = period_size / data_type; | ||
86 | |||
87 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
88 | src = dma_pos; | ||
89 | dst = prtd->params->dma_addr; | ||
90 | src_bidx = data_type; | ||
91 | dst_bidx = 0; | ||
92 | } else { | ||
93 | src = prtd->params->dma_addr; | ||
94 | dst = dma_pos; | ||
95 | src_bidx = 0; | ||
96 | dst_bidx = data_type; | ||
97 | } | ||
98 | |||
99 | davinci_set_dma_src_params(lch, src, INCR, W8BIT); | ||
100 | davinci_set_dma_dest_params(lch, dst, INCR, W8BIT); | ||
101 | davinci_set_dma_src_index(lch, src_bidx, 0); | ||
102 | davinci_set_dma_dest_index(lch, dst_bidx, 0); | ||
103 | davinci_set_dma_transfer_params(lch, data_type, count, 1, 0, ASYNC); | ||
104 | |||
105 | prtd->period++; | ||
106 | if (unlikely(prtd->period >= runtime->periods)) | ||
107 | prtd->period = 0; | ||
108 | } | ||
109 | |||
110 | static void davinci_pcm_dma_irq(int lch, u16 ch_status, void *data) | ||
111 | { | ||
112 | struct snd_pcm_substream *substream = data; | ||
113 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | ||
114 | |||
115 | DPRINTK("lch=%d, status=0x%x\n", lch, ch_status); | ||
116 | |||
117 | if (unlikely(ch_status != DMA_COMPLETE)) | ||
118 | return; | ||
119 | |||
120 | if (snd_pcm_running(substream)) { | ||
121 | snd_pcm_period_elapsed(substream); | ||
122 | |||
123 | spin_lock(&prtd->lock); | ||
124 | davinci_pcm_enqueue_dma(substream); | ||
125 | spin_unlock(&prtd->lock); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) | ||
130 | { | ||
131 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | ||
132 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
133 | struct davinci_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data; | ||
134 | int tcc = TCC_ANY; | ||
135 | int ret; | ||
136 | |||
137 | if (!dma_data) | ||
138 | return -ENODEV; | ||
139 | |||
140 | prtd->params = dma_data; | ||
141 | |||
142 | /* Request master DMA channel */ | ||
143 | ret = davinci_request_dma(prtd->params->channel, prtd->params->name, | ||
144 | davinci_pcm_dma_irq, substream, | ||
145 | &prtd->master_lch, &tcc, EVENTQ_0); | ||
146 | if (ret) | ||
147 | return ret; | ||
148 | |||
149 | /* Request slave DMA channel */ | ||
150 | ret = davinci_request_dma(PARAM_ANY, "Link", | ||
151 | NULL, NULL, &prtd->slave_lch, &tcc, EVENTQ_0); | ||
152 | if (ret) { | ||
153 | davinci_free_dma(prtd->master_lch); | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | /* Link slave DMA channel in loopback */ | ||
158 | davinci_dma_link_lch(prtd->slave_lch, prtd->slave_lch); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
164 | { | ||
165 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | ||
166 | int ret = 0; | ||
167 | |||
168 | spin_lock(&prtd->lock); | ||
169 | |||
170 | switch (cmd) { | ||
171 | case SNDRV_PCM_TRIGGER_START: | ||
172 | case SNDRV_PCM_TRIGGER_RESUME: | ||
173 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
174 | davinci_start_dma(prtd->master_lch); | ||
175 | break; | ||
176 | case SNDRV_PCM_TRIGGER_STOP: | ||
177 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
178 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
179 | davinci_stop_dma(prtd->master_lch); | ||
180 | break; | ||
181 | default: | ||
182 | ret = -EINVAL; | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | spin_unlock(&prtd->lock); | ||
187 | |||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | static int davinci_pcm_prepare(struct snd_pcm_substream *substream) | ||
192 | { | ||
193 | struct davinci_runtime_data *prtd = substream->runtime->private_data; | ||
194 | struct paramentry_descriptor temp; | ||
195 | |||
196 | prtd->period = 0; | ||
197 | davinci_pcm_enqueue_dma(substream); | ||
198 | |||
199 | /* Get slave channel dma params for master channel startup */ | ||
200 | davinci_get_dma_params(prtd->slave_lch, &temp); | ||
201 | davinci_set_dma_params(prtd->master_lch, &temp); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static snd_pcm_uframes_t | ||
207 | davinci_pcm_pointer(struct snd_pcm_substream *substream) | ||
208 | { | ||
209 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
210 | struct davinci_runtime_data *prtd = runtime->private_data; | ||
211 | unsigned int offset; | ||
212 | dma_addr_t count; | ||
213 | dma_addr_t src, dst; | ||
214 | |||
215 | spin_lock(&prtd->lock); | ||
216 | |||
217 | davinci_dma_getposition(prtd->master_lch, &src, &dst); | ||
218 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
219 | count = src - runtime->dma_addr; | ||
220 | else | ||
221 | count = dst - runtime->dma_addr;; | ||
222 | |||
223 | spin_unlock(&prtd->lock); | ||
224 | |||
225 | offset = bytes_to_frames(runtime, count); | ||
226 | if (offset >= runtime->buffer_size) | ||
227 | offset = 0; | ||
228 | |||
229 | return offset; | ||
230 | } | ||
231 | |||
232 | static int davinci_pcm_open(struct snd_pcm_substream *substream) | ||
233 | { | ||
234 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
235 | struct davinci_runtime_data *prtd; | ||
236 | int ret = 0; | ||
237 | |||
238 | snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware); | ||
239 | |||
240 | prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); | ||
241 | if (prtd == NULL) | ||
242 | return -ENOMEM; | ||
243 | |||
244 | spin_lock_init(&prtd->lock); | ||
245 | |||
246 | runtime->private_data = prtd; | ||
247 | |||
248 | ret = davinci_pcm_dma_request(substream); | ||
249 | if (ret) { | ||
250 | printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n"); | ||
251 | kfree(prtd); | ||
252 | } | ||
253 | |||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | static int davinci_pcm_close(struct snd_pcm_substream *substream) | ||
258 | { | ||
259 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
260 | struct davinci_runtime_data *prtd = runtime->private_data; | ||
261 | |||
262 | davinci_dma_unlink_lch(prtd->slave_lch, prtd->slave_lch); | ||
263 | |||
264 | davinci_free_dma(prtd->slave_lch); | ||
265 | davinci_free_dma(prtd->master_lch); | ||
266 | |||
267 | kfree(prtd); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int davinci_pcm_hw_params(struct snd_pcm_substream *substream, | ||
273 | struct snd_pcm_hw_params *hw_params) | ||
274 | { | ||
275 | return snd_pcm_lib_malloc_pages(substream, | ||
276 | params_buffer_bytes(hw_params)); | ||
277 | } | ||
278 | |||
279 | static int davinci_pcm_hw_free(struct snd_pcm_substream *substream) | ||
280 | { | ||
281 | return snd_pcm_lib_free_pages(substream); | ||
282 | } | ||
283 | |||
284 | static int davinci_pcm_mmap(struct snd_pcm_substream *substream, | ||
285 | struct vm_area_struct *vma) | ||
286 | { | ||
287 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
288 | |||
289 | return dma_mmap_writecombine(substream->pcm->card->dev, vma, | ||
290 | runtime->dma_area, | ||
291 | runtime->dma_addr, | ||
292 | runtime->dma_bytes); | ||
293 | } | ||
294 | |||
295 | struct snd_pcm_ops davinci_pcm_ops = { | ||
296 | .open = davinci_pcm_open, | ||
297 | .close = davinci_pcm_close, | ||
298 | .ioctl = snd_pcm_lib_ioctl, | ||
299 | .hw_params = davinci_pcm_hw_params, | ||
300 | .hw_free = davinci_pcm_hw_free, | ||
301 | .prepare = davinci_pcm_prepare, | ||
302 | .trigger = davinci_pcm_trigger, | ||
303 | .pointer = davinci_pcm_pointer, | ||
304 | .mmap = davinci_pcm_mmap, | ||
305 | }; | ||
306 | |||
307 | static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
308 | { | ||
309 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
310 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
311 | size_t size = davinci_pcm_hardware.buffer_bytes_max; | ||
312 | |||
313 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
314 | buf->dev.dev = pcm->card->dev; | ||
315 | buf->private_data = NULL; | ||
316 | buf->area = dma_alloc_writecombine(pcm->card->dev, size, | ||
317 | &buf->addr, GFP_KERNEL); | ||
318 | |||
319 | DPRINTK("preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", | ||
320 | (void *) buf->area, (void *) buf->addr, size); | ||
321 | |||
322 | if (!buf->area) | ||
323 | return -ENOMEM; | ||
324 | |||
325 | buf->bytes = size; | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static void davinci_pcm_free(struct snd_pcm *pcm) | ||
330 | { | ||
331 | struct snd_pcm_substream *substream; | ||
332 | struct snd_dma_buffer *buf; | ||
333 | int stream; | ||
334 | |||
335 | for (stream = 0; stream < 2; stream++) { | ||
336 | substream = pcm->streams[stream].substream; | ||
337 | if (!substream) | ||
338 | continue; | ||
339 | |||
340 | buf = &substream->dma_buffer; | ||
341 | if (!buf->area) | ||
342 | continue; | ||
343 | |||
344 | dma_free_writecombine(pcm->card->dev, buf->bytes, | ||
345 | buf->area, buf->addr); | ||
346 | buf->area = NULL; | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static u64 davinci_pcm_dmamask = 0xffffffff; | ||
351 | |||
352 | static int davinci_pcm_new(struct snd_card *card, | ||
353 | struct snd_soc_codec_dai *dai, struct snd_pcm *pcm) | ||
354 | { | ||
355 | int ret; | ||
356 | |||
357 | if (!card->dev->dma_mask) | ||
358 | card->dev->dma_mask = &davinci_pcm_dmamask; | ||
359 | if (!card->dev->coherent_dma_mask) | ||
360 | card->dev->coherent_dma_mask = 0xffffffff; | ||
361 | |||
362 | if (dai->playback.channels_min) { | ||
363 | ret = davinci_pcm_preallocate_dma_buffer(pcm, | ||
364 | SNDRV_PCM_STREAM_PLAYBACK); | ||
365 | if (ret) | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | if (dai->capture.channels_min) { | ||
370 | ret = davinci_pcm_preallocate_dma_buffer(pcm, | ||
371 | SNDRV_PCM_STREAM_CAPTURE); | ||
372 | if (ret) | ||
373 | return ret; | ||
374 | } | ||
375 | |||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | struct snd_soc_platform davinci_soc_platform = { | ||
380 | .name = "davinci-audio", | ||
381 | .pcm_ops = &davinci_pcm_ops, | ||
382 | .pcm_new = davinci_pcm_new, | ||
383 | .pcm_free = davinci_pcm_free, | ||
384 | }; | ||
385 | EXPORT_SYMBOL_GPL(davinci_soc_platform); | ||
386 | |||
387 | MODULE_AUTHOR("Vladimir Barinov"); | ||
388 | MODULE_DESCRIPTION("TI DAVINCI PCM DMA module"); | ||
389 | MODULE_LICENSE("GPL"); | ||