• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ALSA PCM interface for the Stetch s6000 family
3  *
4  * Author:      Daniel Gloeckner, <dg@emlix.com>
5  * Copyright:   (C) 2009 emlix GmbH <info@emlix.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 #include <linux/interrupt.h>
18 
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/pcm_params.h>
22 #include <sound/soc.h>
23 
24 #include <asm/dma.h>
25 #include <variant/dmac.h>
26 
27 #include "s6000-pcm.h"
28 
29 #define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
30 #define S6_PCM_PREALLOCATE_MAX  (2048 * 1024)
31 
32 static struct snd_pcm_hardware s6000_pcm_hardware = {
33 	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
34 		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
35 		 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
36 	.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
37 	.rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
38 		  SNDRV_PCM_RATE_8000_192000),
39 	.rate_min = 0,
40 	.rate_max = 1562500,
41 	.channels_min = 2,
42 	.channels_max = 8,
43 	.buffer_bytes_max = 0x7ffffff0,
44 	.period_bytes_min = 16,
45 	.period_bytes_max = 0xfffff0,
46 	.periods_min = 2,
47 	.periods_max = 1024, /* no limit */
48 	.fifo_size = 0,
49 };
50 
51 struct s6000_runtime_data {
52 	spinlock_t lock;
53 	int period;		/* current DMA period */
54 };
55 
s6000_pcm_enqueue_dma(struct snd_pcm_substream * substream)56 static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
57 {
58 	struct snd_pcm_runtime *runtime = substream->runtime;
59 	struct s6000_runtime_data *prtd = runtime->private_data;
60 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
61 	struct s6000_pcm_dma_params *par;
62 	int channel;
63 	unsigned int period_size;
64 	unsigned int dma_offset;
65 	dma_addr_t dma_pos;
66 	dma_addr_t src, dst;
67 
68 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
69 
70 	period_size = snd_pcm_lib_period_bytes(substream);
71 	dma_offset = prtd->period * period_size;
72 	dma_pos = runtime->dma_addr + dma_offset;
73 
74 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
75 		src = dma_pos;
76 		dst = par->sif_out;
77 		channel = par->dma_out;
78 	} else {
79 		src = par->sif_in;
80 		dst = dma_pos;
81 		channel = par->dma_in;
82 	}
83 
84 	if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
85 				    DMA_INDEX_CHNL(channel)))
86 		return;
87 
88 	if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
89 		printk(KERN_ERR "s6000-pcm: fifo full\n");
90 		return;
91 	}
92 
93 	BUG_ON(period_size & 15);
94 	s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
95 			src, dst, period_size);
96 
97 	prtd->period++;
98 	if (unlikely(prtd->period >= runtime->periods))
99 		prtd->period = 0;
100 }
101 
s6000_pcm_irq(int irq,void * data)102 static irqreturn_t s6000_pcm_irq(int irq, void *data)
103 {
104 	struct snd_pcm *pcm = data;
105 	struct snd_soc_pcm_runtime *runtime = pcm->private_data;
106 	struct s6000_runtime_data *prtd;
107 	unsigned int has_xrun;
108 	int i, ret = IRQ_NONE;
109 
110 	for (i = 0; i < 2; ++i) {
111 		struct snd_pcm_substream *substream = pcm->streams[i].substream;
112 		struct s6000_pcm_dma_params *params =
113 					snd_soc_dai_get_dma_data(runtime->cpu_dai, substream);
114 		u32 channel;
115 		unsigned int pending;
116 
117 		if (substream == SNDRV_PCM_STREAM_PLAYBACK)
118 			channel = params->dma_out;
119 		else
120 			channel = params->dma_in;
121 
122 		has_xrun = params->check_xrun(runtime->cpu_dai);
123 
124 		if (!channel)
125 			continue;
126 
127 		if (unlikely(has_xrun & (1 << i)) &&
128 		    substream->runtime &&
129 		    snd_pcm_running(substream)) {
130 			dev_dbg(pcm->dev, "xrun\n");
131 			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
132 			ret = IRQ_HANDLED;
133 		}
134 
135 		pending = s6dmac_int_sources(DMA_MASK_DMAC(channel),
136 					     DMA_INDEX_CHNL(channel));
137 
138 		if (pending & 1) {
139 			ret = IRQ_HANDLED;
140 			if (likely(substream->runtime &&
141 				   snd_pcm_running(substream))) {
142 				snd_pcm_period_elapsed(substream);
143 				dev_dbg(pcm->dev, "period elapsed %x %x\n",
144 				       s6dmac_cur_src(DMA_MASK_DMAC(channel),
145 						   DMA_INDEX_CHNL(channel)),
146 				       s6dmac_cur_dst(DMA_MASK_DMAC(channel),
147 						   DMA_INDEX_CHNL(channel)));
148 				prtd = substream->runtime->private_data;
149 				spin_lock(&prtd->lock);
150 				s6000_pcm_enqueue_dma(substream);
151 				spin_unlock(&prtd->lock);
152 			}
153 		}
154 
155 		if (unlikely(pending & ~7)) {
156 			if (pending & (1 << 3))
157 				printk(KERN_WARNING
158 				       "s6000-pcm: DMA %x Underflow\n",
159 				       channel);
160 			if (pending & (1 << 4))
161 				printk(KERN_WARNING
162 				       "s6000-pcm: DMA %x Overflow\n",
163 				       channel);
164 			if (pending & 0x1e0)
165 				printk(KERN_WARNING
166 				       "s6000-pcm: DMA %x Master Error "
167 				       "(mask %x)\n",
168 				       channel, pending >> 5);
169 
170 		}
171 	}
172 
173 	return ret;
174 }
175 
s6000_pcm_start(struct snd_pcm_substream * substream)176 static int s6000_pcm_start(struct snd_pcm_substream *substream)
177 {
178 	struct s6000_runtime_data *prtd = substream->runtime->private_data;
179 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
180 	struct s6000_pcm_dma_params *par;
181 	unsigned long flags;
182 	int srcinc;
183 	u32 dma;
184 
185 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
186 
187 	spin_lock_irqsave(&prtd->lock, flags);
188 
189 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
190 		srcinc = 1;
191 		dma = par->dma_out;
192 	} else {
193 		srcinc = 0;
194 		dma = par->dma_in;
195 	}
196 	s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
197 			   1 /* priority 1 (0 is max) */,
198 			   0 /* peripheral requests w/o xfer length mode */,
199 			   srcinc /* source address increment */,
200 			   srcinc^1 /* destination address increment */,
201 			   0 /* chunksize 0 (skip impossible on this dma) */,
202 			   0 /* source skip after chunk (impossible) */,
203 			   0 /* destination skip after chunk (impossible) */,
204 			   4 /* 16 byte burst size */,
205 			   -1 /* don't conserve bandwidth */,
206 			   0 /* low watermark irq descriptor threshold */,
207 			   0 /* disable hardware timestamps */,
208 			   1 /* enable channel */);
209 
210 	s6000_pcm_enqueue_dma(substream);
211 	s6000_pcm_enqueue_dma(substream);
212 
213 	spin_unlock_irqrestore(&prtd->lock, flags);
214 
215 	return 0;
216 }
217 
s6000_pcm_stop(struct snd_pcm_substream * substream)218 static int s6000_pcm_stop(struct snd_pcm_substream *substream)
219 {
220 	struct s6000_runtime_data *prtd = substream->runtime->private_data;
221 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
222 	struct s6000_pcm_dma_params *par;
223 	unsigned long flags;
224 	u32 channel;
225 
226 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
227 
228 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
229 		channel = par->dma_out;
230 	else
231 		channel = par->dma_in;
232 
233 	s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
234 				  DMA_INDEX_CHNL(channel), 0);
235 
236 	spin_lock_irqsave(&prtd->lock, flags);
237 
238 	s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
239 
240 	spin_unlock_irqrestore(&prtd->lock, flags);
241 
242 	return 0;
243 }
244 
s6000_pcm_trigger(struct snd_pcm_substream * substream,int cmd)245 static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
246 {
247 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
248 	struct s6000_pcm_dma_params *par;
249 	int ret;
250 
251 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
252 
253 	ret = par->trigger(substream, cmd, 0);
254 	if (ret < 0)
255 		return ret;
256 
257 	switch (cmd) {
258 	case SNDRV_PCM_TRIGGER_START:
259 	case SNDRV_PCM_TRIGGER_RESUME:
260 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
261 		ret = s6000_pcm_start(substream);
262 		break;
263 	case SNDRV_PCM_TRIGGER_STOP:
264 	case SNDRV_PCM_TRIGGER_SUSPEND:
265 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
266 		ret = s6000_pcm_stop(substream);
267 		break;
268 	default:
269 		ret = -EINVAL;
270 	}
271 	if (ret < 0)
272 		return ret;
273 
274 	return par->trigger(substream, cmd, 1);
275 }
276 
s6000_pcm_prepare(struct snd_pcm_substream * substream)277 static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
278 {
279 	struct s6000_runtime_data *prtd = substream->runtime->private_data;
280 
281 	prtd->period = 0;
282 
283 	return 0;
284 }
285 
s6000_pcm_pointer(struct snd_pcm_substream * substream)286 static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
287 {
288 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
289 	struct s6000_pcm_dma_params *par;
290 	struct snd_pcm_runtime *runtime = substream->runtime;
291 	struct s6000_runtime_data *prtd = runtime->private_data;
292 	unsigned long flags;
293 	unsigned int offset;
294 	dma_addr_t count;
295 
296 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
297 
298 	spin_lock_irqsave(&prtd->lock, flags);
299 
300 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
301 		count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
302 				       DMA_INDEX_CHNL(par->dma_out));
303 	else
304 		count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
305 				       DMA_INDEX_CHNL(par->dma_in));
306 
307 	count -= runtime->dma_addr;
308 
309 	spin_unlock_irqrestore(&prtd->lock, flags);
310 
311 	offset = bytes_to_frames(runtime, count);
312 	if (unlikely(offset >= runtime->buffer_size))
313 		offset = 0;
314 
315 	return offset;
316 }
317 
s6000_pcm_open(struct snd_pcm_substream * substream)318 static int s6000_pcm_open(struct snd_pcm_substream *substream)
319 {
320 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
321 	struct s6000_pcm_dma_params *par;
322 	struct snd_pcm_runtime *runtime = substream->runtime;
323 	struct s6000_runtime_data *prtd;
324 	int ret;
325 
326 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
327 	snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
328 
329 	ret = snd_pcm_hw_constraint_step(runtime, 0,
330 					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
331 	if (ret < 0)
332 		return ret;
333 	ret = snd_pcm_hw_constraint_step(runtime, 0,
334 					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
335 	if (ret < 0)
336 		return ret;
337 	ret = snd_pcm_hw_constraint_integer(runtime,
338 					    SNDRV_PCM_HW_PARAM_PERIODS);
339 	if (ret < 0)
340 		return ret;
341 
342 	if (par->same_rate) {
343 		int rate;
344 		spin_lock(&par->lock); /* needed? */
345 		rate = par->rate;
346 		spin_unlock(&par->lock);
347 		if (rate != -1) {
348 			ret = snd_pcm_hw_constraint_minmax(runtime,
349 							SNDRV_PCM_HW_PARAM_RATE,
350 							rate, rate);
351 			if (ret < 0)
352 				return ret;
353 		}
354 	}
355 
356 	prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
357 	if (prtd == NULL)
358 		return -ENOMEM;
359 
360 	spin_lock_init(&prtd->lock);
361 
362 	runtime->private_data = prtd;
363 
364 	return 0;
365 }
366 
s6000_pcm_close(struct snd_pcm_substream * substream)367 static int s6000_pcm_close(struct snd_pcm_substream *substream)
368 {
369 	struct snd_pcm_runtime *runtime = substream->runtime;
370 	struct s6000_runtime_data *prtd = runtime->private_data;
371 
372 	kfree(prtd);
373 
374 	return 0;
375 }
376 
s6000_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)377 static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
378 				 struct snd_pcm_hw_params *hw_params)
379 {
380 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
381 	struct s6000_pcm_dma_params *par;
382 	int ret;
383 	ret = snd_pcm_lib_malloc_pages(substream,
384 				       params_buffer_bytes(hw_params));
385 	if (ret < 0) {
386 		printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
387 		return ret;
388 	}
389 
390 	par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
391 
392 	if (par->same_rate) {
393 		spin_lock(&par->lock);
394 		if (par->rate == -1 ||
395 		    !(par->in_use & ~(1 << substream->stream))) {
396 			par->rate = params_rate(hw_params);
397 			par->in_use |= 1 << substream->stream;
398 		} else if (params_rate(hw_params) != par->rate) {
399 			snd_pcm_lib_free_pages(substream);
400 			par->in_use &= ~(1 << substream->stream);
401 			ret = -EBUSY;
402 		}
403 		spin_unlock(&par->lock);
404 	}
405 	return ret;
406 }
407 
s6000_pcm_hw_free(struct snd_pcm_substream * substream)408 static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
409 {
410 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
411 	struct s6000_pcm_dma_params *par =
412 		snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
413 
414 	spin_lock(&par->lock);
415 	par->in_use &= ~(1 << substream->stream);
416 	if (!par->in_use)
417 		par->rate = -1;
418 	spin_unlock(&par->lock);
419 
420 	return snd_pcm_lib_free_pages(substream);
421 }
422 
423 static struct snd_pcm_ops s6000_pcm_ops = {
424 	.open = 	s6000_pcm_open,
425 	.close = 	s6000_pcm_close,
426 	.ioctl = 	snd_pcm_lib_ioctl,
427 	.hw_params = 	s6000_pcm_hw_params,
428 	.hw_free = 	s6000_pcm_hw_free,
429 	.trigger =	s6000_pcm_trigger,
430 	.prepare = 	s6000_pcm_prepare,
431 	.pointer = 	s6000_pcm_pointer,
432 };
433 
s6000_pcm_free(struct snd_pcm * pcm)434 static void s6000_pcm_free(struct snd_pcm *pcm)
435 {
436 	struct snd_soc_pcm_runtime *runtime = pcm->private_data;
437 	struct s6000_pcm_dma_params *params =
438 		snd_soc_dai_get_dma_data(runtime->cpu_dai,
439 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
440 
441 	free_irq(params->irq, pcm);
442 	snd_pcm_lib_preallocate_free_for_all(pcm);
443 }
444 
445 static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
446 
s6000_pcm_new(struct snd_soc_pcm_runtime * runtime)447 static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime)
448 {
449 	struct snd_card *card = runtime->card->snd_card;
450 	struct snd_pcm *pcm = runtime->pcm;
451 	struct s6000_pcm_dma_params *params;
452 	int res;
453 
454 	params = snd_soc_dai_get_dma_data(runtime->cpu_dai,
455 			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
456 
457 	if (!card->dev->dma_mask)
458 		card->dev->dma_mask = &s6000_pcm_dmamask;
459 	if (!card->dev->coherent_dma_mask)
460 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
461 
462 	if (params->dma_in) {
463 		s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
464 				    DMA_INDEX_CHNL(params->dma_in));
465 		s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
466 				   DMA_INDEX_CHNL(params->dma_in));
467 	}
468 
469 	if (params->dma_out) {
470 		s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
471 				    DMA_INDEX_CHNL(params->dma_out));
472 		s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
473 				   DMA_INDEX_CHNL(params->dma_out));
474 	}
475 
476 	res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
477 			  "s6000-audio", pcm);
478 	if (res) {
479 		printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
480 		return res;
481 	}
482 
483 	res = snd_pcm_lib_preallocate_pages_for_all(pcm,
484 						    SNDRV_DMA_TYPE_DEV,
485 						    card->dev,
486 						    S6_PCM_PREALLOCATE_SIZE,
487 						    S6_PCM_PREALLOCATE_MAX);
488 	if (res)
489 		printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
490 
491 	spin_lock_init(&params->lock);
492 	params->in_use = 0;
493 	params->rate = -1;
494 	return 0;
495 }
496 
497 static struct snd_soc_platform_driver s6000_soc_platform = {
498 	.ops =		&s6000_pcm_ops,
499 	.pcm_new = 	s6000_pcm_new,
500 	.pcm_free = 	s6000_pcm_free,
501 };
502 
s6000_soc_platform_probe(struct platform_device * pdev)503 static int s6000_soc_platform_probe(struct platform_device *pdev)
504 {
505 	return snd_soc_register_platform(&pdev->dev, &s6000_soc_platform);
506 }
507 
s6000_soc_platform_remove(struct platform_device * pdev)508 static int s6000_soc_platform_remove(struct platform_device *pdev)
509 {
510 	snd_soc_unregister_platform(&pdev->dev);
511 	return 0;
512 }
513 
514 static struct platform_driver s6000_pcm_driver = {
515 	.driver = {
516 			.name = "s6000-pcm-audio",
517 			.owner = THIS_MODULE,
518 	},
519 
520 	.probe = s6000_soc_platform_probe,
521 	.remove = s6000_soc_platform_remove,
522 };
523 
524 module_platform_driver(s6000_pcm_driver);
525 
526 MODULE_AUTHOR("Daniel Gloeckner");
527 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
528 MODULE_LICENSE("GPL");
529