xref: /linux/sound/soc/cirrus/ep93xx-pcm.c (revision 0ed275eff31029c39355828cb48c46c0a006e2f8)
1*0ed275efSAlexander Shiyan /*
2*0ed275efSAlexander Shiyan  * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface
3*0ed275efSAlexander Shiyan  *
4*0ed275efSAlexander Shiyan  * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
5*0ed275efSAlexander Shiyan  * Copyright (C) 2006 Applied Data Systems
6*0ed275efSAlexander Shiyan  *
7*0ed275efSAlexander Shiyan  * Rewritten for the SoC audio subsystem (Based on PXA2xx code):
8*0ed275efSAlexander Shiyan  *   Copyright (c) 2008 Ryan Mallon
9*0ed275efSAlexander Shiyan  *
10*0ed275efSAlexander Shiyan  * This program is free software; you can redistribute it and/or modify
11*0ed275efSAlexander Shiyan  * it under the terms of the GNU General Public License version 2 as
12*0ed275efSAlexander Shiyan  * published by the Free Software Foundation.
13*0ed275efSAlexander Shiyan  */
14*0ed275efSAlexander Shiyan 
15*0ed275efSAlexander Shiyan #include <linux/module.h>
16*0ed275efSAlexander Shiyan #include <linux/init.h>
17*0ed275efSAlexander Shiyan #include <linux/device.h>
18*0ed275efSAlexander Shiyan #include <linux/slab.h>
19*0ed275efSAlexander Shiyan #include <linux/dmaengine.h>
20*0ed275efSAlexander Shiyan #include <linux/dma-mapping.h>
21*0ed275efSAlexander Shiyan 
22*0ed275efSAlexander Shiyan #include <sound/core.h>
23*0ed275efSAlexander Shiyan #include <sound/pcm.h>
24*0ed275efSAlexander Shiyan #include <sound/pcm_params.h>
25*0ed275efSAlexander Shiyan #include <sound/soc.h>
26*0ed275efSAlexander Shiyan #include <sound/dmaengine_pcm.h>
27*0ed275efSAlexander Shiyan 
28*0ed275efSAlexander Shiyan #include <mach/dma.h>
29*0ed275efSAlexander Shiyan #include <mach/hardware.h>
30*0ed275efSAlexander Shiyan #include <mach/ep93xx-regs.h>
31*0ed275efSAlexander Shiyan 
32*0ed275efSAlexander Shiyan #include "ep93xx-pcm.h"
33*0ed275efSAlexander Shiyan 
34*0ed275efSAlexander Shiyan static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
35*0ed275efSAlexander Shiyan 	.info			= (SNDRV_PCM_INFO_MMAP		|
36*0ed275efSAlexander Shiyan 				   SNDRV_PCM_INFO_MMAP_VALID	|
37*0ed275efSAlexander Shiyan 				   SNDRV_PCM_INFO_INTERLEAVED	|
38*0ed275efSAlexander Shiyan 				   SNDRV_PCM_INFO_BLOCK_TRANSFER),
39*0ed275efSAlexander Shiyan 
40*0ed275efSAlexander Shiyan 	.rates			= SNDRV_PCM_RATE_8000_192000,
41*0ed275efSAlexander Shiyan 	.rate_min		= SNDRV_PCM_RATE_8000,
42*0ed275efSAlexander Shiyan 	.rate_max		= SNDRV_PCM_RATE_192000,
43*0ed275efSAlexander Shiyan 
44*0ed275efSAlexander Shiyan 	.formats		= (SNDRV_PCM_FMTBIT_S16_LE |
45*0ed275efSAlexander Shiyan 				   SNDRV_PCM_FMTBIT_S24_LE |
46*0ed275efSAlexander Shiyan 				   SNDRV_PCM_FMTBIT_S32_LE),
47*0ed275efSAlexander Shiyan 
48*0ed275efSAlexander Shiyan 	.buffer_bytes_max	= 131072,
49*0ed275efSAlexander Shiyan 	.period_bytes_min	= 32,
50*0ed275efSAlexander Shiyan 	.period_bytes_max	= 32768,
51*0ed275efSAlexander Shiyan 	.periods_min		= 1,
52*0ed275efSAlexander Shiyan 	.periods_max		= 32,
53*0ed275efSAlexander Shiyan 	.fifo_size		= 32,
54*0ed275efSAlexander Shiyan };
55*0ed275efSAlexander Shiyan 
56*0ed275efSAlexander Shiyan static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
57*0ed275efSAlexander Shiyan {
58*0ed275efSAlexander Shiyan 	struct ep93xx_dma_data *data = filter_param;
59*0ed275efSAlexander Shiyan 
60*0ed275efSAlexander Shiyan 	if (data->direction == ep93xx_dma_chan_direction(chan)) {
61*0ed275efSAlexander Shiyan 		chan->private = data;
62*0ed275efSAlexander Shiyan 		return true;
63*0ed275efSAlexander Shiyan 	}
64*0ed275efSAlexander Shiyan 
65*0ed275efSAlexander Shiyan 	return false;
66*0ed275efSAlexander Shiyan }
67*0ed275efSAlexander Shiyan 
68*0ed275efSAlexander Shiyan static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
69*0ed275efSAlexander Shiyan {
70*0ed275efSAlexander Shiyan 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
71*0ed275efSAlexander Shiyan 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
72*0ed275efSAlexander Shiyan 	struct ep93xx_pcm_dma_params *dma_params;
73*0ed275efSAlexander Shiyan 	struct ep93xx_dma_data *dma_data;
74*0ed275efSAlexander Shiyan 	int ret;
75*0ed275efSAlexander Shiyan 
76*0ed275efSAlexander Shiyan 	snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
77*0ed275efSAlexander Shiyan 
78*0ed275efSAlexander Shiyan 	dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL);
79*0ed275efSAlexander Shiyan 	if (!dma_data)
80*0ed275efSAlexander Shiyan 		return -ENOMEM;
81*0ed275efSAlexander Shiyan 
82*0ed275efSAlexander Shiyan 	dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
83*0ed275efSAlexander Shiyan 	dma_data->port = dma_params->dma_port;
84*0ed275efSAlexander Shiyan 	dma_data->name = dma_params->name;
85*0ed275efSAlexander Shiyan 	dma_data->direction = snd_pcm_substream_to_dma_direction(substream);
86*0ed275efSAlexander Shiyan 
87*0ed275efSAlexander Shiyan 	ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data);
88*0ed275efSAlexander Shiyan 	if (ret) {
89*0ed275efSAlexander Shiyan 		kfree(dma_data);
90*0ed275efSAlexander Shiyan 		return ret;
91*0ed275efSAlexander Shiyan 	}
92*0ed275efSAlexander Shiyan 
93*0ed275efSAlexander Shiyan 	snd_dmaengine_pcm_set_data(substream, dma_data);
94*0ed275efSAlexander Shiyan 
95*0ed275efSAlexander Shiyan 	return 0;
96*0ed275efSAlexander Shiyan }
97*0ed275efSAlexander Shiyan 
98*0ed275efSAlexander Shiyan static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
99*0ed275efSAlexander Shiyan {
100*0ed275efSAlexander Shiyan 	struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
101*0ed275efSAlexander Shiyan 
102*0ed275efSAlexander Shiyan 	snd_dmaengine_pcm_close(substream);
103*0ed275efSAlexander Shiyan 	kfree(dma_data);
104*0ed275efSAlexander Shiyan 	return 0;
105*0ed275efSAlexander Shiyan }
106*0ed275efSAlexander Shiyan 
107*0ed275efSAlexander Shiyan static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
108*0ed275efSAlexander Shiyan 				struct snd_pcm_hw_params *params)
109*0ed275efSAlexander Shiyan {
110*0ed275efSAlexander Shiyan 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
111*0ed275efSAlexander Shiyan 
112*0ed275efSAlexander Shiyan 	return 0;
113*0ed275efSAlexander Shiyan }
114*0ed275efSAlexander Shiyan 
115*0ed275efSAlexander Shiyan static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
116*0ed275efSAlexander Shiyan {
117*0ed275efSAlexander Shiyan 	snd_pcm_set_runtime_buffer(substream, NULL);
118*0ed275efSAlexander Shiyan 	return 0;
119*0ed275efSAlexander Shiyan }
120*0ed275efSAlexander Shiyan 
121*0ed275efSAlexander Shiyan static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
122*0ed275efSAlexander Shiyan 			   struct vm_area_struct *vma)
123*0ed275efSAlexander Shiyan {
124*0ed275efSAlexander Shiyan 	struct snd_pcm_runtime *runtime = substream->runtime;
125*0ed275efSAlexander Shiyan 
126*0ed275efSAlexander Shiyan 	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
127*0ed275efSAlexander Shiyan 				     runtime->dma_area,
128*0ed275efSAlexander Shiyan 				     runtime->dma_addr,
129*0ed275efSAlexander Shiyan 				     runtime->dma_bytes);
130*0ed275efSAlexander Shiyan }
131*0ed275efSAlexander Shiyan 
132*0ed275efSAlexander Shiyan static struct snd_pcm_ops ep93xx_pcm_ops = {
133*0ed275efSAlexander Shiyan 	.open		= ep93xx_pcm_open,
134*0ed275efSAlexander Shiyan 	.close		= ep93xx_pcm_close,
135*0ed275efSAlexander Shiyan 	.ioctl		= snd_pcm_lib_ioctl,
136*0ed275efSAlexander Shiyan 	.hw_params	= ep93xx_pcm_hw_params,
137*0ed275efSAlexander Shiyan 	.hw_free	= ep93xx_pcm_hw_free,
138*0ed275efSAlexander Shiyan 	.trigger	= snd_dmaengine_pcm_trigger,
139*0ed275efSAlexander Shiyan 	.pointer	= snd_dmaengine_pcm_pointer_no_residue,
140*0ed275efSAlexander Shiyan 	.mmap		= ep93xx_pcm_mmap,
141*0ed275efSAlexander Shiyan };
142*0ed275efSAlexander Shiyan 
143*0ed275efSAlexander Shiyan static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
144*0ed275efSAlexander Shiyan {
145*0ed275efSAlexander Shiyan 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
146*0ed275efSAlexander Shiyan 	struct snd_dma_buffer *buf = &substream->dma_buffer;
147*0ed275efSAlexander Shiyan 	size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
148*0ed275efSAlexander Shiyan 
149*0ed275efSAlexander Shiyan 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
150*0ed275efSAlexander Shiyan 	buf->dev.dev = pcm->card->dev;
151*0ed275efSAlexander Shiyan 	buf->private_data = NULL;
152*0ed275efSAlexander Shiyan 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
153*0ed275efSAlexander Shiyan 					   &buf->addr, GFP_KERNEL);
154*0ed275efSAlexander Shiyan 	buf->bytes = size;
155*0ed275efSAlexander Shiyan 
156*0ed275efSAlexander Shiyan 	return (buf->area == NULL) ? -ENOMEM : 0;
157*0ed275efSAlexander Shiyan }
158*0ed275efSAlexander Shiyan 
159*0ed275efSAlexander Shiyan static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
160*0ed275efSAlexander Shiyan {
161*0ed275efSAlexander Shiyan 	struct snd_pcm_substream *substream;
162*0ed275efSAlexander Shiyan 	struct snd_dma_buffer *buf;
163*0ed275efSAlexander Shiyan 	int stream;
164*0ed275efSAlexander Shiyan 
165*0ed275efSAlexander Shiyan 	for (stream = 0; stream < 2; stream++) {
166*0ed275efSAlexander Shiyan 		substream = pcm->streams[stream].substream;
167*0ed275efSAlexander Shiyan 		if (!substream)
168*0ed275efSAlexander Shiyan 			continue;
169*0ed275efSAlexander Shiyan 
170*0ed275efSAlexander Shiyan 		buf = &substream->dma_buffer;
171*0ed275efSAlexander Shiyan 		if (!buf->area)
172*0ed275efSAlexander Shiyan 			continue;
173*0ed275efSAlexander Shiyan 
174*0ed275efSAlexander Shiyan 		dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
175*0ed275efSAlexander Shiyan 				      buf->addr);
176*0ed275efSAlexander Shiyan 		buf->area = NULL;
177*0ed275efSAlexander Shiyan 	}
178*0ed275efSAlexander Shiyan }
179*0ed275efSAlexander Shiyan 
180*0ed275efSAlexander Shiyan static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32);
181*0ed275efSAlexander Shiyan 
182*0ed275efSAlexander Shiyan static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
183*0ed275efSAlexander Shiyan {
184*0ed275efSAlexander Shiyan 	struct snd_card *card = rtd->card->snd_card;
185*0ed275efSAlexander Shiyan 	struct snd_pcm *pcm = rtd->pcm;
186*0ed275efSAlexander Shiyan 	int ret = 0;
187*0ed275efSAlexander Shiyan 
188*0ed275efSAlexander Shiyan 	if (!card->dev->dma_mask)
189*0ed275efSAlexander Shiyan 		card->dev->dma_mask = &ep93xx_pcm_dmamask;
190*0ed275efSAlexander Shiyan 	if (!card->dev->coherent_dma_mask)
191*0ed275efSAlexander Shiyan 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
192*0ed275efSAlexander Shiyan 
193*0ed275efSAlexander Shiyan 	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
194*0ed275efSAlexander Shiyan 		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
195*0ed275efSAlexander Shiyan 					SNDRV_PCM_STREAM_PLAYBACK);
196*0ed275efSAlexander Shiyan 		if (ret)
197*0ed275efSAlexander Shiyan 			return ret;
198*0ed275efSAlexander Shiyan 	}
199*0ed275efSAlexander Shiyan 
200*0ed275efSAlexander Shiyan 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
201*0ed275efSAlexander Shiyan 		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
202*0ed275efSAlexander Shiyan 					SNDRV_PCM_STREAM_CAPTURE);
203*0ed275efSAlexander Shiyan 		if (ret)
204*0ed275efSAlexander Shiyan 			return ret;
205*0ed275efSAlexander Shiyan 	}
206*0ed275efSAlexander Shiyan 
207*0ed275efSAlexander Shiyan 	return 0;
208*0ed275efSAlexander Shiyan }
209*0ed275efSAlexander Shiyan 
210*0ed275efSAlexander Shiyan static struct snd_soc_platform_driver ep93xx_soc_platform = {
211*0ed275efSAlexander Shiyan 	.ops		= &ep93xx_pcm_ops,
212*0ed275efSAlexander Shiyan 	.pcm_new	= &ep93xx_pcm_new,
213*0ed275efSAlexander Shiyan 	.pcm_free	= &ep93xx_pcm_free_dma_buffers,
214*0ed275efSAlexander Shiyan };
215*0ed275efSAlexander Shiyan 
216*0ed275efSAlexander Shiyan static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
217*0ed275efSAlexander Shiyan {
218*0ed275efSAlexander Shiyan 	return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
219*0ed275efSAlexander Shiyan }
220*0ed275efSAlexander Shiyan 
221*0ed275efSAlexander Shiyan static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
222*0ed275efSAlexander Shiyan {
223*0ed275efSAlexander Shiyan 	snd_soc_unregister_platform(&pdev->dev);
224*0ed275efSAlexander Shiyan 	return 0;
225*0ed275efSAlexander Shiyan }
226*0ed275efSAlexander Shiyan 
227*0ed275efSAlexander Shiyan static struct platform_driver ep93xx_pcm_driver = {
228*0ed275efSAlexander Shiyan 	.driver = {
229*0ed275efSAlexander Shiyan 			.name = "ep93xx-pcm-audio",
230*0ed275efSAlexander Shiyan 			.owner = THIS_MODULE,
231*0ed275efSAlexander Shiyan 	},
232*0ed275efSAlexander Shiyan 
233*0ed275efSAlexander Shiyan 	.probe = ep93xx_soc_platform_probe,
234*0ed275efSAlexander Shiyan 	.remove = __devexit_p(ep93xx_soc_platform_remove),
235*0ed275efSAlexander Shiyan };
236*0ed275efSAlexander Shiyan 
237*0ed275efSAlexander Shiyan module_platform_driver(ep93xx_pcm_driver);
238*0ed275efSAlexander Shiyan 
239*0ed275efSAlexander Shiyan MODULE_AUTHOR("Ryan Mallon");
240*0ed275efSAlexander Shiyan MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
241*0ed275efSAlexander Shiyan MODULE_LICENSE("GPL");
242*0ed275efSAlexander Shiyan MODULE_ALIAS("platform:ep93xx-pcm-audio");
243