Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/*
  2 * omap-pcm.c  --  ALSA PCM interface for the OMAP SoC
  3 *
  4 * Copyright (C) 2008 Nokia Corporation
  5 *
  6 * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
  7 *          Peter Ujfalusi <peter.ujfalusi@ti.com>
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * version 2 as published by the Free Software Foundation.
 12 *
 13 * This program is distributed in the hope that it will be useful, but
 14 * WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 21 * 02110-1301 USA
 22 *
 23 */
 24
 25#include <linux/dma-mapping.h>
 26#include <linux/slab.h>
 27#include <sound/core.h>
 28#include <sound/pcm.h>
 29#include <sound/pcm_params.h>
 30#include <sound/soc.h>
 31
 32#include <plat/dma.h>
 33#include "omap-pcm.h"
 34
 35static const struct snd_pcm_hardware omap_pcm_hardware = {
 36	.info			= SNDRV_PCM_INFO_MMAP |
 37				  SNDRV_PCM_INFO_MMAP_VALID |
 38				  SNDRV_PCM_INFO_INTERLEAVED |
 39				  SNDRV_PCM_INFO_PAUSE |
 40				  SNDRV_PCM_INFO_RESUME |
 41				  SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
 42	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
 43				  SNDRV_PCM_FMTBIT_S32_LE,
 44	.period_bytes_min	= 32,
 45	.period_bytes_max	= 64 * 1024,
 46	.periods_min		= 2,
 47	.periods_max		= 255,
 48	.buffer_bytes_max	= 128 * 1024,
 49};
 50
 51struct omap_runtime_data {
 52	spinlock_t			lock;
 53	struct omap_pcm_dma_data	*dma_data;
 54	int				dma_ch;
 55	int				period_index;
 56};
 57
 58static void omap_pcm_dma_irq(int ch, u16 stat, void *data)
 59{
 60	struct snd_pcm_substream *substream = data;
 61	struct snd_pcm_runtime *runtime = substream->runtime;
 62	struct omap_runtime_data *prtd = runtime->private_data;
 63	unsigned long flags;
 64
 65	if ((cpu_is_omap1510())) {
 66		/*
 67		 * OMAP1510 doesn't fully support DMA progress counter
 68		 * and there is no software emulation implemented yet,
 69		 * so have to maintain our own progress counters
 70		 * that can be used by omap_pcm_pointer() instead.
 71		 */
 72		spin_lock_irqsave(&prtd->lock, flags);
 73		if ((stat == OMAP_DMA_LAST_IRQ) &&
 74				(prtd->period_index == runtime->periods - 1)) {
 75			/* we are in sync, do nothing */
 76			spin_unlock_irqrestore(&prtd->lock, flags);
 77			return;
 78		}
 79		if (prtd->period_index >= 0) {
 80			if (stat & OMAP_DMA_BLOCK_IRQ) {
 81				/* end of buffer reached, loop back */
 82				prtd->period_index = 0;
 83			} else if (stat & OMAP_DMA_LAST_IRQ) {
 84				/* update the counter for the last period */
 85				prtd->period_index = runtime->periods - 1;
 86			} else if (++prtd->period_index >= runtime->periods) {
 87				/* end of buffer missed? loop back */
 88				prtd->period_index = 0;
 89			}
 90		}
 91		spin_unlock_irqrestore(&prtd->lock, flags);
 92	}
 93
 94	snd_pcm_period_elapsed(substream);
 95}
 96
 97/* this may get called several times by oss emulation */
 98static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
 99			      struct snd_pcm_hw_params *params)
100{
101	struct snd_pcm_runtime *runtime = substream->runtime;
102	struct snd_soc_pcm_runtime *rtd = substream->private_data;
103	struct omap_runtime_data *prtd = runtime->private_data;
104	struct omap_pcm_dma_data *dma_data;
105
106	int err = 0;
107
108	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
109
110	/* return if this is a bufferless transfer e.g.
111	 * codec <--> BT codec or GSM modem -- lg FIXME */
112	if (!dma_data)
113		return 0;
114
115	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
116	runtime->dma_bytes = params_buffer_bytes(params);
117
118	if (prtd->dma_data)
119		return 0;
120	prtd->dma_data = dma_data;
121	err = omap_request_dma(dma_data->dma_req, dma_data->name,
122			       omap_pcm_dma_irq, substream, &prtd->dma_ch);
123	if (!err) {
124		/*
125		 * Link channel with itself so DMA doesn't need any
126		 * reprogramming while looping the buffer
127		 */
128		omap_dma_link_lch(prtd->dma_ch, prtd->dma_ch);
129	}
130
131	return err;
132}
133
134static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
135{
136	struct snd_pcm_runtime *runtime = substream->runtime;
137	struct omap_runtime_data *prtd = runtime->private_data;
138
139	if (prtd->dma_data == NULL)
140		return 0;
141
142	omap_dma_unlink_lch(prtd->dma_ch, prtd->dma_ch);
143	omap_free_dma(prtd->dma_ch);
144	prtd->dma_data = NULL;
145
146	snd_pcm_set_runtime_buffer(substream, NULL);
147
148	return 0;
149}
150
151static int omap_pcm_prepare(struct snd_pcm_substream *substream)
152{
153	struct snd_pcm_runtime *runtime = substream->runtime;
154	struct omap_runtime_data *prtd = runtime->private_data;
155	struct omap_pcm_dma_data *dma_data = prtd->dma_data;
156	struct omap_dma_channel_params dma_params;
157	int bytes;
158
159	/* return if this is a bufferless transfer e.g.
160	 * codec <--> BT codec or GSM modem -- lg FIXME */
161	if (!prtd->dma_data)
162		return 0;
163
164	memset(&dma_params, 0, sizeof(dma_params));
165	dma_params.data_type			= dma_data->data_type;
166	dma_params.trigger			= dma_data->dma_req;
167	dma_params.sync_mode			= dma_data->sync_mode;
168	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
169		dma_params.src_amode		= OMAP_DMA_AMODE_POST_INC;
170		dma_params.dst_amode		= OMAP_DMA_AMODE_CONSTANT;
171		dma_params.src_or_dst_synch	= OMAP_DMA_DST_SYNC;
172		dma_params.src_start		= runtime->dma_addr;
173		dma_params.dst_start		= dma_data->port_addr;
174		dma_params.dst_port		= OMAP_DMA_PORT_MPUI;
175		dma_params.dst_fi		= dma_data->packet_size;
176	} else {
177		dma_params.src_amode		= OMAP_DMA_AMODE_CONSTANT;
178		dma_params.dst_amode		= OMAP_DMA_AMODE_POST_INC;
179		dma_params.src_or_dst_synch	= OMAP_DMA_SRC_SYNC;
180		dma_params.src_start		= dma_data->port_addr;
181		dma_params.dst_start		= runtime->dma_addr;
182		dma_params.src_port		= OMAP_DMA_PORT_MPUI;
183		dma_params.src_fi		= dma_data->packet_size;
184	}
185	/*
186	 * Set DMA transfer frame size equal to ALSA period size and frame
187	 * count as no. of ALSA periods. Then with DMA frame interrupt enabled,
188	 * we can transfer the whole ALSA buffer with single DMA transfer but
189	 * still can get an interrupt at each period bounary
190	 */
191	bytes = snd_pcm_lib_period_bytes(substream);
192	dma_params.elem_count	= bytes >> dma_data->data_type;
193	dma_params.frame_count	= runtime->periods;
194	omap_set_dma_params(prtd->dma_ch, &dma_params);
195
196	if ((cpu_is_omap1510()))
197		omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ |
198			      OMAP_DMA_LAST_IRQ | OMAP_DMA_BLOCK_IRQ);
199	else if (!substream->runtime->no_period_wakeup)
200		omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
201
202	if (!(cpu_class_is_omap1())) {
203		omap_set_dma_src_burst_mode(prtd->dma_ch,
204						OMAP_DMA_DATA_BURST_16);
205		omap_set_dma_dest_burst_mode(prtd->dma_ch,
206						OMAP_DMA_DATA_BURST_16);
207	}
208
209	return 0;
210}
211
212static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
213{
214	struct snd_pcm_runtime *runtime = substream->runtime;
215	struct omap_runtime_data *prtd = runtime->private_data;
216	struct omap_pcm_dma_data *dma_data = prtd->dma_data;
217	unsigned long flags;
218	int ret = 0;
219
220	spin_lock_irqsave(&prtd->lock, flags);
221	switch (cmd) {
222	case SNDRV_PCM_TRIGGER_START:
223	case SNDRV_PCM_TRIGGER_RESUME:
224	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
225		prtd->period_index = 0;
226		/* Configure McBSP internal buffer usage */
227		if (dma_data->set_threshold)
228			dma_data->set_threshold(substream);
229
230		omap_start_dma(prtd->dma_ch);
231		break;
232
233	case SNDRV_PCM_TRIGGER_STOP:
234	case SNDRV_PCM_TRIGGER_SUSPEND:
235	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
236		prtd->period_index = -1;
237		omap_stop_dma(prtd->dma_ch);
238		break;
239	default:
240		ret = -EINVAL;
241	}
242	spin_unlock_irqrestore(&prtd->lock, flags);
243
244	return ret;
245}
246
247static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
248{
249	struct snd_pcm_runtime *runtime = substream->runtime;
250	struct omap_runtime_data *prtd = runtime->private_data;
251	dma_addr_t ptr;
252	snd_pcm_uframes_t offset;
253
254	if (cpu_is_omap1510()) {
255		offset = prtd->period_index * runtime->period_size;
256	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
257		ptr = omap_get_dma_dst_pos(prtd->dma_ch);
258		offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
259	} else {
260		ptr = omap_get_dma_src_pos(prtd->dma_ch);
261		offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
262	}
263
264	if (offset >= runtime->buffer_size)
265		offset = 0;
266
267	return offset;
268}
269
270static int omap_pcm_open(struct snd_pcm_substream *substream)
271{
272	struct snd_pcm_runtime *runtime = substream->runtime;
273	struct omap_runtime_data *prtd;
274	int ret;
275
276	snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
277
278	/* Ensure that buffer size is a multiple of period size */
279	ret = snd_pcm_hw_constraint_integer(runtime,
280					    SNDRV_PCM_HW_PARAM_PERIODS);
281	if (ret < 0)
282		goto out;
283
284	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
285	if (prtd == NULL) {
286		ret = -ENOMEM;
287		goto out;
288	}
289	spin_lock_init(&prtd->lock);
290	runtime->private_data = prtd;
291
292out:
293	return ret;
294}
295
296static int omap_pcm_close(struct snd_pcm_substream *substream)
297{
298	struct snd_pcm_runtime *runtime = substream->runtime;
299
300	kfree(runtime->private_data);
301	return 0;
302}
303
304static int omap_pcm_mmap(struct snd_pcm_substream *substream,
305	struct vm_area_struct *vma)
306{
307	struct snd_pcm_runtime *runtime = substream->runtime;
308
309	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
310				     runtime->dma_area,
311				     runtime->dma_addr,
312				     runtime->dma_bytes);
313}
314
315static struct snd_pcm_ops omap_pcm_ops = {
316	.open		= omap_pcm_open,
317	.close		= omap_pcm_close,
318	.ioctl		= snd_pcm_lib_ioctl,
319	.hw_params	= omap_pcm_hw_params,
320	.hw_free	= omap_pcm_hw_free,
321	.prepare	= omap_pcm_prepare,
322	.trigger	= omap_pcm_trigger,
323	.pointer	= omap_pcm_pointer,
324	.mmap		= omap_pcm_mmap,
325};
326
327static u64 omap_pcm_dmamask = DMA_BIT_MASK(64);
328
329static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
330	int stream)
331{
332	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
333	struct snd_dma_buffer *buf = &substream->dma_buffer;
334	size_t size = omap_pcm_hardware.buffer_bytes_max;
335
336	buf->dev.type = SNDRV_DMA_TYPE_DEV;
337	buf->dev.dev = pcm->card->dev;
338	buf->private_data = NULL;
339	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
340					   &buf->addr, GFP_KERNEL);
341	if (!buf->area)
342		return -ENOMEM;
343
344	buf->bytes = size;
345	return 0;
346}
347
348static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
349{
350	struct snd_pcm_substream *substream;
351	struct snd_dma_buffer *buf;
352	int stream;
353
354	for (stream = 0; stream < 2; stream++) {
355		substream = pcm->streams[stream].substream;
356		if (!substream)
357			continue;
358
359		buf = &substream->dma_buffer;
360		if (!buf->area)
361			continue;
362
363		dma_free_writecombine(pcm->card->dev, buf->bytes,
364				      buf->area, buf->addr);
365		buf->area = NULL;
366	}
367}
368
369static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd)
370{
371	struct snd_card *card = rtd->card->snd_card;
372	struct snd_soc_dai *dai = rtd->cpu_dai;
373	struct snd_pcm *pcm = rtd->pcm;
374	int ret = 0;
375
376	if (!card->dev->dma_mask)
377		card->dev->dma_mask = &omap_pcm_dmamask;
378	if (!card->dev->coherent_dma_mask)
379		card->dev->coherent_dma_mask = DMA_BIT_MASK(64);
380
381	if (dai->driver->playback.channels_min) {
382		ret = omap_pcm_preallocate_dma_buffer(pcm,
383			SNDRV_PCM_STREAM_PLAYBACK);
384		if (ret)
385			goto out;
386	}
387
388	if (dai->driver->capture.channels_min) {
389		ret = omap_pcm_preallocate_dma_buffer(pcm,
390			SNDRV_PCM_STREAM_CAPTURE);
391		if (ret)
392			goto out;
393	}
394
395out:
396	return ret;
397}
398
399static struct snd_soc_platform_driver omap_soc_platform = {
400	.ops		= &omap_pcm_ops,
401	.pcm_new	= omap_pcm_new,
402	.pcm_free	= omap_pcm_free_dma_buffers,
403};
404
405static __devinit int omap_pcm_probe(struct platform_device *pdev)
406{
407	return snd_soc_register_platform(&pdev->dev,
408			&omap_soc_platform);
409}
410
411static int __devexit omap_pcm_remove(struct platform_device *pdev)
412{
413	snd_soc_unregister_platform(&pdev->dev);
414	return 0;
415}
416
417static struct platform_driver omap_pcm_driver = {
418	.driver = {
419			.name = "omap-pcm-audio",
420			.owner = THIS_MODULE,
421	},
422
423	.probe = omap_pcm_probe,
424	.remove = __devexit_p(omap_pcm_remove),
425};
426
427static int __init snd_omap_pcm_init(void)
428{
429	return platform_driver_register(&omap_pcm_driver);
430}
431module_init(snd_omap_pcm_init);
432
433static void __exit snd_omap_pcm_exit(void)
434{
435	platform_driver_unregister(&omap_pcm_driver);
436}
437module_exit(snd_omap_pcm_exit);
438
439MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
440MODULE_DESCRIPTION("OMAP PCM DMA module");
441MODULE_LICENSE("GPL");