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