• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * dma.c  --  ALSA Soc Audio Layer
3  *
4  * (c) 2006 Wolfson Microelectronics PLC.
5  * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
6  *
7  * Copyright 2004-2005 Simtec Electronics
8  *	http://armlinux.simtec.co.uk/
9  *	Ben Dooks <ben@simtec.co.uk>
10  *
11  *  This program is free software; you can redistribute  it and/or modify it
12  *  under  the terms of  the GNU General  Public License as published by the
13  *  Free Software Foundation;  either version 2 of the  License, or (at your
14  *  option) any later version.
15  */
16 
17 #include <linux/slab.h>
18 #include <linux/dma-mapping.h>
19 #include <linux/module.h>
20 
21 #include <sound/soc.h>
22 #include <sound/pcm_params.h>
23 
24 #include <asm/dma.h>
25 #include <mach/hardware.h>
26 #include <mach/dma.h>
27 
28 #include "dma.h"
29 
30 #define ST_RUNNING		(1<<0)
31 #define ST_OPENED		(1<<1)
32 
33 static const struct snd_pcm_hardware dma_hardware = {
34 	.info			= SNDRV_PCM_INFO_INTERLEAVED |
35 				    SNDRV_PCM_INFO_BLOCK_TRANSFER |
36 				    SNDRV_PCM_INFO_MMAP |
37 				    SNDRV_PCM_INFO_MMAP_VALID,
38 	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
39 				    SNDRV_PCM_FMTBIT_U16_LE |
40 				    SNDRV_PCM_FMTBIT_U8 |
41 				    SNDRV_PCM_FMTBIT_S8,
42 	.channels_min		= 2,
43 	.channels_max		= 2,
44 	.buffer_bytes_max	= 128*1024,
45 	.period_bytes_min	= PAGE_SIZE,
46 	.period_bytes_max	= PAGE_SIZE*2,
47 	.periods_min		= 2,
48 	.periods_max		= 128,
49 	.fifo_size		= 32,
50 };
51 
52 struct runtime_data {
53 	spinlock_t lock;
54 	int state;
55 	unsigned int dma_loaded;
56 	unsigned int dma_period;
57 	dma_addr_t dma_start;
58 	dma_addr_t dma_pos;
59 	dma_addr_t dma_end;
60 	struct s3c_dma_params *params;
61 };
62 
63 static void audio_buffdone(void *data);
64 
65 /* dma_enqueue
66  *
67  * place a dma buffer onto the queue for the dma system
68  * to handle.
69  */
dma_enqueue(struct snd_pcm_substream * substream)70 static void dma_enqueue(struct snd_pcm_substream *substream)
71 {
72 	struct runtime_data *prtd = substream->runtime->private_data;
73 	dma_addr_t pos = prtd->dma_pos;
74 	unsigned int limit;
75 	struct samsung_dma_prep dma_info;
76 
77 	pr_debug("Entered %s\n", __func__);
78 
79 	limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
80 
81 	pr_debug("%s: loaded %d, limit %d\n",
82 				__func__, prtd->dma_loaded, limit);
83 
84 	dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE);
85 	dma_info.direction =
86 		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK
87 		? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
88 	dma_info.fp = audio_buffdone;
89 	dma_info.fp_param = substream;
90 	dma_info.period = prtd->dma_period;
91 	dma_info.len = prtd->dma_period*limit;
92 
93 	while (prtd->dma_loaded < limit) {
94 		pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
95 
96 		if ((pos + dma_info.period) > prtd->dma_end) {
97 			dma_info.period  = prtd->dma_end - pos;
98 			pr_debug("%s: corrected dma len %ld\n",
99 					__func__, dma_info.period);
100 		}
101 
102 		dma_info.buf = pos;
103 		prtd->params->ops->prepare(prtd->params->ch, &dma_info);
104 
105 		prtd->dma_loaded++;
106 		pos += prtd->dma_period;
107 		if (pos >= prtd->dma_end)
108 			pos = prtd->dma_start;
109 	}
110 
111 	prtd->dma_pos = pos;
112 }
113 
audio_buffdone(void * data)114 static void audio_buffdone(void *data)
115 {
116 	struct snd_pcm_substream *substream = data;
117 	struct runtime_data *prtd = substream->runtime->private_data;
118 
119 	pr_debug("Entered %s\n", __func__);
120 
121 	if (prtd->state & ST_RUNNING) {
122 		prtd->dma_pos += prtd->dma_period;
123 		if (prtd->dma_pos >= prtd->dma_end)
124 			prtd->dma_pos = prtd->dma_start;
125 
126 		if (substream)
127 			snd_pcm_period_elapsed(substream);
128 
129 		spin_lock(&prtd->lock);
130 		if (!samsung_dma_has_circular()) {
131 			prtd->dma_loaded--;
132 			dma_enqueue(substream);
133 		}
134 		spin_unlock(&prtd->lock);
135 	}
136 }
137 
dma_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)138 static int dma_hw_params(struct snd_pcm_substream *substream,
139 	struct snd_pcm_hw_params *params)
140 {
141 	struct snd_pcm_runtime *runtime = substream->runtime;
142 	struct runtime_data *prtd = runtime->private_data;
143 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
144 	unsigned long totbytes = params_buffer_bytes(params);
145 	struct s3c_dma_params *dma =
146 		snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
147 	struct samsung_dma_req req;
148 	struct samsung_dma_config config;
149 
150 	pr_debug("Entered %s\n", __func__);
151 
152 	/* return if this is a bufferless transfer e.g.
153 	 * codec <--> BT codec or GSM modem -- lg FIXME */
154 	if (!dma)
155 		return 0;
156 
157 	/* this may get called several times by oss emulation
158 	 * with different params -HW */
159 	if (prtd->params == NULL) {
160 		/* prepare DMA */
161 		prtd->params = dma;
162 
163 		pr_debug("params %p, client %p, channel %d\n", prtd->params,
164 			prtd->params->client, prtd->params->channel);
165 
166 		prtd->params->ops = samsung_dma_get_ops();
167 
168 		req.cap = (samsung_dma_has_circular() ?
169 			DMA_CYCLIC : DMA_SLAVE);
170 		req.client = prtd->params->client;
171 		config.direction =
172 			(substream->stream == SNDRV_PCM_STREAM_PLAYBACK
173 			? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
174 		config.width = prtd->params->dma_size;
175 		config.fifo = prtd->params->dma_addr;
176 		prtd->params->ch = prtd->params->ops->request(
177 				prtd->params->channel, &req, rtd->cpu_dai->dev,
178 				prtd->params->ch_name);
179 		prtd->params->ops->config(prtd->params->ch, &config);
180 	}
181 
182 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
183 
184 	runtime->dma_bytes = totbytes;
185 
186 	spin_lock_irq(&prtd->lock);
187 	prtd->dma_loaded = 0;
188 	prtd->dma_period = params_period_bytes(params);
189 	prtd->dma_start = runtime->dma_addr;
190 	prtd->dma_pos = prtd->dma_start;
191 	prtd->dma_end = prtd->dma_start + totbytes;
192 	spin_unlock_irq(&prtd->lock);
193 
194 	return 0;
195 }
196 
dma_hw_free(struct snd_pcm_substream * substream)197 static int dma_hw_free(struct snd_pcm_substream *substream)
198 {
199 	struct runtime_data *prtd = substream->runtime->private_data;
200 
201 	pr_debug("Entered %s\n", __func__);
202 
203 	snd_pcm_set_runtime_buffer(substream, NULL);
204 
205 	if (prtd->params) {
206 		prtd->params->ops->flush(prtd->params->ch);
207 		prtd->params->ops->release(prtd->params->ch,
208 					prtd->params->client);
209 		prtd->params = NULL;
210 	}
211 
212 	return 0;
213 }
214 
dma_prepare(struct snd_pcm_substream * substream)215 static int dma_prepare(struct snd_pcm_substream *substream)
216 {
217 	struct runtime_data *prtd = substream->runtime->private_data;
218 	int ret = 0;
219 
220 	pr_debug("Entered %s\n", __func__);
221 
222 	/* return if this is a bufferless transfer e.g.
223 	 * codec <--> BT codec or GSM modem -- lg FIXME */
224 	if (!prtd->params)
225 		return 0;
226 
227 	/* flush the DMA channel */
228 	prtd->params->ops->flush(prtd->params->ch);
229 
230 	prtd->dma_loaded = 0;
231 	prtd->dma_pos = prtd->dma_start;
232 
233 	/* enqueue dma buffers */
234 	dma_enqueue(substream);
235 
236 	return ret;
237 }
238 
dma_trigger(struct snd_pcm_substream * substream,int cmd)239 static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
240 {
241 	struct runtime_data *prtd = substream->runtime->private_data;
242 	int ret = 0;
243 
244 	pr_debug("Entered %s\n", __func__);
245 
246 	spin_lock(&prtd->lock);
247 
248 	switch (cmd) {
249 	case SNDRV_PCM_TRIGGER_START:
250 		prtd->state |= ST_RUNNING;
251 		prtd->params->ops->trigger(prtd->params->ch);
252 		break;
253 
254 	case SNDRV_PCM_TRIGGER_STOP:
255 		prtd->state &= ~ST_RUNNING;
256 		prtd->params->ops->stop(prtd->params->ch);
257 		break;
258 
259 	default:
260 		ret = -EINVAL;
261 		break;
262 	}
263 
264 	spin_unlock(&prtd->lock);
265 
266 	return ret;
267 }
268 
269 static snd_pcm_uframes_t
dma_pointer(struct snd_pcm_substream * substream)270 dma_pointer(struct snd_pcm_substream *substream)
271 {
272 	struct snd_pcm_runtime *runtime = substream->runtime;
273 	struct runtime_data *prtd = runtime->private_data;
274 	unsigned long res;
275 
276 	pr_debug("Entered %s\n", __func__);
277 
278 	res = prtd->dma_pos - prtd->dma_start;
279 
280 	pr_debug("Pointer offset: %lu\n", res);
281 
282 	/* we seem to be getting the odd error from the pcm library due
283 	 * to out-of-bounds pointers. this is maybe due to the dma engine
284 	 * not having loaded the new values for the channel before being
285 	 * called... (todo - fix )
286 	 */
287 
288 	if (res >= snd_pcm_lib_buffer_bytes(substream)) {
289 		if (res == snd_pcm_lib_buffer_bytes(substream))
290 			res = 0;
291 	}
292 
293 	return bytes_to_frames(substream->runtime, res);
294 }
295 
dma_open(struct snd_pcm_substream * substream)296 static int dma_open(struct snd_pcm_substream *substream)
297 {
298 	struct snd_pcm_runtime *runtime = substream->runtime;
299 	struct runtime_data *prtd;
300 
301 	pr_debug("Entered %s\n", __func__);
302 
303 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
304 	snd_soc_set_runtime_hwparams(substream, &dma_hardware);
305 
306 	prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
307 	if (prtd == NULL)
308 		return -ENOMEM;
309 
310 	spin_lock_init(&prtd->lock);
311 
312 	runtime->private_data = prtd;
313 	return 0;
314 }
315 
dma_close(struct snd_pcm_substream * substream)316 static int dma_close(struct snd_pcm_substream *substream)
317 {
318 	struct snd_pcm_runtime *runtime = substream->runtime;
319 	struct runtime_data *prtd = runtime->private_data;
320 
321 	pr_debug("Entered %s\n", __func__);
322 
323 	if (!prtd)
324 		pr_debug("dma_close called with prtd == NULL\n");
325 
326 	kfree(prtd);
327 
328 	return 0;
329 }
330 
dma_mmap(struct snd_pcm_substream * substream,struct vm_area_struct * vma)331 static int dma_mmap(struct snd_pcm_substream *substream,
332 	struct vm_area_struct *vma)
333 {
334 	struct snd_pcm_runtime *runtime = substream->runtime;
335 
336 	pr_debug("Entered %s\n", __func__);
337 
338 	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
339 				     runtime->dma_area,
340 				     runtime->dma_addr,
341 				     runtime->dma_bytes);
342 }
343 
344 static struct snd_pcm_ops dma_ops = {
345 	.open		= dma_open,
346 	.close		= dma_close,
347 	.ioctl		= snd_pcm_lib_ioctl,
348 	.hw_params	= dma_hw_params,
349 	.hw_free	= dma_hw_free,
350 	.prepare	= dma_prepare,
351 	.trigger	= dma_trigger,
352 	.pointer	= dma_pointer,
353 	.mmap		= dma_mmap,
354 };
355 
preallocate_dma_buffer(struct snd_pcm * pcm,int stream)356 static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
357 {
358 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
359 	struct snd_dma_buffer *buf = &substream->dma_buffer;
360 	size_t size = dma_hardware.buffer_bytes_max;
361 
362 	pr_debug("Entered %s\n", __func__);
363 
364 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
365 	buf->dev.dev = pcm->card->dev;
366 	buf->private_data = NULL;
367 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
368 					   &buf->addr, GFP_KERNEL);
369 	if (!buf->area)
370 		return -ENOMEM;
371 	buf->bytes = size;
372 	return 0;
373 }
374 
dma_free_dma_buffers(struct snd_pcm * pcm)375 static void dma_free_dma_buffers(struct snd_pcm *pcm)
376 {
377 	struct snd_pcm_substream *substream;
378 	struct snd_dma_buffer *buf;
379 	int stream;
380 
381 	pr_debug("Entered %s\n", __func__);
382 
383 	for (stream = 0; stream < 2; stream++) {
384 		substream = pcm->streams[stream].substream;
385 		if (!substream)
386 			continue;
387 
388 		buf = &substream->dma_buffer;
389 		if (!buf->area)
390 			continue;
391 
392 		dma_free_writecombine(pcm->card->dev, buf->bytes,
393 				      buf->area, buf->addr);
394 		buf->area = NULL;
395 	}
396 }
397 
398 static u64 dma_mask = DMA_BIT_MASK(32);
399 
dma_new(struct snd_soc_pcm_runtime * rtd)400 static int dma_new(struct snd_soc_pcm_runtime *rtd)
401 {
402 	struct snd_card *card = rtd->card->snd_card;
403 	struct snd_pcm *pcm = rtd->pcm;
404 	int ret = 0;
405 
406 	pr_debug("Entered %s\n", __func__);
407 
408 	if (!card->dev->dma_mask)
409 		card->dev->dma_mask = &dma_mask;
410 	if (!card->dev->coherent_dma_mask)
411 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
412 
413 	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
414 		ret = preallocate_dma_buffer(pcm,
415 			SNDRV_PCM_STREAM_PLAYBACK);
416 		if (ret)
417 			goto out;
418 	}
419 
420 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
421 		ret = preallocate_dma_buffer(pcm,
422 			SNDRV_PCM_STREAM_CAPTURE);
423 		if (ret)
424 			goto out;
425 	}
426 out:
427 	return ret;
428 }
429 
430 static struct snd_soc_platform_driver samsung_asoc_platform = {
431 	.ops		= &dma_ops,
432 	.pcm_new	= dma_new,
433 	.pcm_free	= dma_free_dma_buffers,
434 };
435 
asoc_dma_platform_register(struct device * dev)436 int asoc_dma_platform_register(struct device *dev)
437 {
438 	return snd_soc_register_platform(dev, &samsung_asoc_platform);
439 }
440 EXPORT_SYMBOL_GPL(asoc_dma_platform_register);
441 
asoc_dma_platform_unregister(struct device * dev)442 void asoc_dma_platform_unregister(struct device *dev)
443 {
444 	snd_soc_unregister_platform(dev);
445 }
446 EXPORT_SYMBOL_GPL(asoc_dma_platform_unregister);
447 
448 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
449 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
450 MODULE_LICENSE("GPL");
451