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 
27 #include <mach/dma.h>
28 #include <mach/hardware.h>
29 #include <mach/ep93xx-regs.h>
30 
31 #include "ep93xx-pcm.h"
32 
33 static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
34 	.info			= (SNDRV_PCM_INFO_MMAP		|
35 				   SNDRV_PCM_INFO_MMAP_VALID	|
36 				   SNDRV_PCM_INFO_INTERLEAVED	|
37 				   SNDRV_PCM_INFO_BLOCK_TRANSFER),
38 
39 	.rates			= SNDRV_PCM_RATE_8000_192000,
40 	.rate_min		= SNDRV_PCM_RATE_8000,
41 	.rate_max		= SNDRV_PCM_RATE_192000,
42 
43 	.formats		= (SNDRV_PCM_FMTBIT_S16_LE |
44 				   SNDRV_PCM_FMTBIT_S24_LE |
45 				   SNDRV_PCM_FMTBIT_S32_LE),
46 
47 	.buffer_bytes_max	= 131072,
48 	.period_bytes_min	= 32,
49 	.period_bytes_max	= 32768,
50 	.periods_min		= 1,
51 	.periods_max		= 32,
52 	.fifo_size		= 32,
53 };
54 
55 struct ep93xx_runtime_data
56 {
57 	int				pointer_bytes;
58 	int				periods;
59 	int				period_bytes;
60 	struct dma_chan			*dma_chan;
61 	struct ep93xx_dma_data		dma_data;
62 };
63 
ep93xx_pcm_dma_callback(void * data)64 static void ep93xx_pcm_dma_callback(void *data)
65 {
66 	struct snd_pcm_substream *substream = data;
67 	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
68 
69 	rtd->pointer_bytes += rtd->period_bytes;
70 	rtd->pointer_bytes %= rtd->period_bytes * rtd->periods;
71 
72 	snd_pcm_period_elapsed(substream);
73 }
74 
ep93xx_pcm_dma_filter(struct dma_chan * chan,void * filter_param)75 static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param)
76 {
77 	struct ep93xx_dma_data *data = filter_param;
78 
79 	if (data->direction == ep93xx_dma_chan_direction(chan)) {
80 		chan->private = data;
81 		return true;
82 	}
83 
84 	return false;
85 }
86 
ep93xx_pcm_open(struct snd_pcm_substream * substream)87 static int ep93xx_pcm_open(struct snd_pcm_substream *substream)
88 {
89 	struct snd_soc_pcm_runtime *soc_rtd = substream->private_data;
90 	struct snd_soc_dai *cpu_dai = soc_rtd->cpu_dai;
91 	struct ep93xx_pcm_dma_params *dma_params;
92 	struct ep93xx_runtime_data *rtd;
93 	dma_cap_mask_t mask;
94 	int ret;
95 
96 	ret = snd_pcm_hw_constraint_integer(substream->runtime,
97 					    SNDRV_PCM_HW_PARAM_PERIODS);
98 	if (ret < 0)
99 		return ret;
100 
101 	snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware);
102 
103 	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
104 	if (!rtd)
105 		return -ENOMEM;
106 
107 	dma_cap_zero(mask);
108 	dma_cap_set(DMA_SLAVE, mask);
109 	dma_cap_set(DMA_CYCLIC, mask);
110 
111 	dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream);
112 	rtd->dma_data.port = dma_params->dma_port;
113 	rtd->dma_data.name = dma_params->name;
114 
115 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
116 		rtd->dma_data.direction = DMA_MEM_TO_DEV;
117 	else
118 		rtd->dma_data.direction = DMA_DEV_TO_MEM;
119 
120 	rtd->dma_chan = dma_request_channel(mask, ep93xx_pcm_dma_filter,
121 					    &rtd->dma_data);
122 	if (!rtd->dma_chan) {
123 		kfree(rtd);
124 		return -EINVAL;
125 	}
126 
127 	substream->runtime->private_data = rtd;
128 	return 0;
129 }
130 
ep93xx_pcm_close(struct snd_pcm_substream * substream)131 static int ep93xx_pcm_close(struct snd_pcm_substream *substream)
132 {
133 	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
134 
135 	dma_release_channel(rtd->dma_chan);
136 	kfree(rtd);
137 	return 0;
138 }
139 
ep93xx_pcm_dma_submit(struct snd_pcm_substream * substream)140 static int ep93xx_pcm_dma_submit(struct snd_pcm_substream *substream)
141 {
142 	struct snd_pcm_runtime *runtime = substream->runtime;
143 	struct ep93xx_runtime_data *rtd = runtime->private_data;
144 	struct dma_chan *chan = rtd->dma_chan;
145 	struct dma_device *dma_dev = chan->device;
146 	struct dma_async_tx_descriptor *desc;
147 
148 	rtd->pointer_bytes = 0;
149 	desc = dma_dev->device_prep_dma_cyclic(chan, runtime->dma_addr,
150 					       rtd->period_bytes * rtd->periods,
151 					       rtd->period_bytes,
152 					       rtd->dma_data.direction);
153 	if (!desc)
154 		return -EINVAL;
155 
156 	desc->callback = ep93xx_pcm_dma_callback;
157 	desc->callback_param = substream;
158 
159 	dmaengine_submit(desc);
160 	return 0;
161 }
162 
ep93xx_pcm_dma_flush(struct snd_pcm_substream * substream)163 static void ep93xx_pcm_dma_flush(struct snd_pcm_substream *substream)
164 {
165 	struct snd_pcm_runtime *runtime = substream->runtime;
166 	struct ep93xx_runtime_data *rtd = runtime->private_data;
167 
168 	dmaengine_terminate_all(rtd->dma_chan);
169 }
170 
ep93xx_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)171 static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream,
172 				struct snd_pcm_hw_params *params)
173 {
174 	struct snd_pcm_runtime *runtime = substream->runtime;
175 	struct ep93xx_runtime_data *rtd = runtime->private_data;
176 
177 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
178 
179 	rtd->periods = params_periods(params);
180 	rtd->period_bytes = params_period_bytes(params);
181 	return 0;
182 }
183 
ep93xx_pcm_hw_free(struct snd_pcm_substream * substream)184 static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream)
185 {
186 	snd_pcm_set_runtime_buffer(substream, NULL);
187 	return 0;
188 }
189 
ep93xx_pcm_trigger(struct snd_pcm_substream * substream,int cmd)190 static int ep93xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
191 {
192 	int ret;
193 
194 	ret = 0;
195 	switch (cmd) {
196 	case SNDRV_PCM_TRIGGER_START:
197 	case SNDRV_PCM_TRIGGER_RESUME:
198 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
199 		ret = ep93xx_pcm_dma_submit(substream);
200 		break;
201 
202 	case SNDRV_PCM_TRIGGER_STOP:
203 	case SNDRV_PCM_TRIGGER_SUSPEND:
204 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
205 		ep93xx_pcm_dma_flush(substream);
206 		break;
207 
208 	default:
209 		ret = -EINVAL;
210 		break;
211 	}
212 
213 	return ret;
214 }
215 
ep93xx_pcm_pointer(struct snd_pcm_substream * substream)216 static snd_pcm_uframes_t ep93xx_pcm_pointer(struct snd_pcm_substream *substream)
217 {
218 	struct snd_pcm_runtime *runtime = substream->runtime;
219 	struct ep93xx_runtime_data *rtd = substream->runtime->private_data;
220 
221 	/* FIXME: implement this with sub-period granularity */
222 	return bytes_to_frames(runtime, rtd->pointer_bytes);
223 }
224 
ep93xx_pcm_mmap(struct snd_pcm_substream * substream,struct vm_area_struct * vma)225 static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream,
226 			   struct vm_area_struct *vma)
227 {
228 	struct snd_pcm_runtime *runtime = substream->runtime;
229 
230 	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
231 				     runtime->dma_area,
232 				     runtime->dma_addr,
233 				     runtime->dma_bytes);
234 }
235 
236 static struct snd_pcm_ops ep93xx_pcm_ops = {
237 	.open		= ep93xx_pcm_open,
238 	.close		= ep93xx_pcm_close,
239 	.ioctl		= snd_pcm_lib_ioctl,
240 	.hw_params	= ep93xx_pcm_hw_params,
241 	.hw_free	= ep93xx_pcm_hw_free,
242 	.trigger	= ep93xx_pcm_trigger,
243 	.pointer	= ep93xx_pcm_pointer,
244 	.mmap		= ep93xx_pcm_mmap,
245 };
246 
ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm * pcm,int stream)247 static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
248 {
249 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
250 	struct snd_dma_buffer *buf = &substream->dma_buffer;
251 	size_t size = ep93xx_pcm_hardware.buffer_bytes_max;
252 
253 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
254 	buf->dev.dev = pcm->card->dev;
255 	buf->private_data = NULL;
256 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
257 					   &buf->addr, GFP_KERNEL);
258 	buf->bytes = size;
259 
260 	return (buf->area == NULL) ? -ENOMEM : 0;
261 }
262 
ep93xx_pcm_free_dma_buffers(struct snd_pcm * pcm)263 static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
264 {
265 	struct snd_pcm_substream *substream;
266 	struct snd_dma_buffer *buf;
267 	int stream;
268 
269 	for (stream = 0; stream < 2; stream++) {
270 		substream = pcm->streams[stream].substream;
271 		if (!substream)
272 			continue;
273 
274 		buf = &substream->dma_buffer;
275 		if (!buf->area)
276 			continue;
277 
278 		dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area,
279 				      buf->addr);
280 		buf->area = NULL;
281 	}
282 }
283 
284 static u64 ep93xx_pcm_dmamask = 0xffffffff;
285 
ep93xx_pcm_new(struct snd_soc_pcm_runtime * rtd)286 static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
287 {
288 	struct snd_card *card = rtd->card->snd_card;
289 	struct snd_pcm *pcm = rtd->pcm;
290 	int ret = 0;
291 
292 	if (!card->dev->dma_mask)
293 		card->dev->dma_mask = &ep93xx_pcm_dmamask;
294 	if (!card->dev->coherent_dma_mask)
295 		card->dev->coherent_dma_mask = 0xffffffff;
296 
297 	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
298 		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
299 					SNDRV_PCM_STREAM_PLAYBACK);
300 		if (ret)
301 			return ret;
302 	}
303 
304 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
305 		ret = ep93xx_pcm_preallocate_dma_buffer(pcm,
306 					SNDRV_PCM_STREAM_CAPTURE);
307 		if (ret)
308 			return ret;
309 	}
310 
311 	return 0;
312 }
313 
314 static struct snd_soc_platform_driver ep93xx_soc_platform = {
315 	.ops		= &ep93xx_pcm_ops,
316 	.pcm_new	= &ep93xx_pcm_new,
317 	.pcm_free	= &ep93xx_pcm_free_dma_buffers,
318 };
319 
ep93xx_soc_platform_probe(struct platform_device * pdev)320 static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev)
321 {
322 	return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform);
323 }
324 
ep93xx_soc_platform_remove(struct platform_device * pdev)325 static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev)
326 {
327 	snd_soc_unregister_platform(&pdev->dev);
328 	return 0;
329 }
330 
331 static struct platform_driver ep93xx_pcm_driver = {
332 	.driver = {
333 			.name = "ep93xx-pcm-audio",
334 			.owner = THIS_MODULE,
335 	},
336 
337 	.probe = ep93xx_soc_platform_probe,
338 	.remove = __devexit_p(ep93xx_soc_platform_remove),
339 };
340 
341 module_platform_driver(ep93xx_pcm_driver);
342 
343 MODULE_AUTHOR("Ryan Mallon");
344 MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
345 MODULE_LICENSE("GPL");
346 MODULE_ALIAS("platform:ep93xx-pcm-audio");
347