]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - sound/soc/imx/imx-pcm-dma-mx2.c
ASoC i.MX: switch to new DMA api
[~shefty/rdma-dev.git] / sound / soc / imx / imx-pcm-dma-mx2.c
1 /*
2  * imx-pcm-dma-mx2.c  --  ALSA Soc Audio Layer
3  *
4  * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
5  *
6  * This code is based on code copyrighted by Freescale,
7  * Liam Girdwood, Javier Martin and probably others.
8  *
9  *  This program is free software; you can redistribute  it and/or modify it
10  *  under  the terms of  the GNU General  Public License as published by the
11  *  Free Software Foundation;  either version 2 of the  License, or (at your
12  *  option) any later version.
13  */
14 #include <linux/clk.h>
15 #include <linux/delay.h>
16 #include <linux/device.h>
17 #include <linux/dma-mapping.h>
18 #include <linux/init.h>
19 #include <linux/interrupt.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/slab.h>
23 #include <linux/dmaengine.h>
24
25 #include <sound/core.h>
26 #include <sound/initval.h>
27 #include <sound/pcm.h>
28 #include <sound/pcm_params.h>
29 #include <sound/soc.h>
30
31 #include <mach/dma.h>
32
33 #include "imx-ssi.h"
34
35 struct imx_pcm_runtime_data {
36         int period_bytes;
37         int periods;
38         int dma;
39         unsigned long offset;
40         unsigned long size;
41         void *buf;
42         int period_time;
43         struct dma_async_tx_descriptor *desc;
44         struct dma_chan *dma_chan;
45         struct imx_dma_data dma_data;
46 };
47
48 static void audio_dma_irq(void *data)
49 {
50         struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
51         struct snd_pcm_runtime *runtime = substream->runtime;
52         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
53
54         iprtd->offset += iprtd->period_bytes;
55         iprtd->offset %= iprtd->period_bytes * iprtd->periods;
56
57         snd_pcm_period_elapsed(substream);
58 }
59
60 static bool filter(struct dma_chan *chan, void *param)
61 {
62         struct imx_pcm_runtime_data *iprtd = param;
63
64         if (!imx_dma_is_general_purpose(chan))
65                 return false;
66
67         chan->private = &iprtd->dma_data;
68
69         return true;
70 }
71
72 static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream,
73                                 struct snd_pcm_hw_params *params)
74 {
75         struct snd_soc_pcm_runtime *rtd = substream->private_data;
76         struct imx_pcm_dma_params *dma_params;
77         struct snd_pcm_runtime *runtime = substream->runtime;
78         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
79         struct dma_slave_config slave_config;
80         dma_cap_mask_t mask;
81         enum dma_slave_buswidth buswidth;
82         int ret;
83
84         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
85
86         iprtd->dma_data.peripheral_type = IMX_DMATYPE_SSI;
87         iprtd->dma_data.priority = DMA_PRIO_HIGH;
88         iprtd->dma_data.dma_request = dma_params->dma;
89
90         /* Try to grab a DMA channel */
91         dma_cap_zero(mask);
92         dma_cap_set(DMA_SLAVE, mask);
93         iprtd->dma_chan = dma_request_channel(mask, filter, iprtd);
94         if (!iprtd->dma_chan)
95                 return -EINVAL;
96
97         switch (params_format(params)) {
98         case SNDRV_PCM_FORMAT_S16_LE:
99                 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
100                 break;
101         case SNDRV_PCM_FORMAT_S20_3LE:
102         case SNDRV_PCM_FORMAT_S24_LE:
103                 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
104                 break;
105         default:
106                 return 0;
107         }
108
109         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
110                 slave_config.direction = DMA_TO_DEVICE;
111                 slave_config.dst_addr = dma_params->dma_addr;
112                 slave_config.dst_addr_width = buswidth;
113                 slave_config.dst_maxburst = dma_params->burstsize;
114         } else {
115                 slave_config.direction = DMA_FROM_DEVICE;
116                 slave_config.src_addr = dma_params->dma_addr;
117                 slave_config.src_addr_width = buswidth;
118                 slave_config.src_maxburst = dma_params->burstsize;
119         }
120
121         ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config);
122         if (ret)
123                 return ret;
124
125         return 0;
126 }
127
128 static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
129                                 struct snd_pcm_hw_params *params)
130 {
131         struct snd_soc_pcm_runtime *rtd = substream->private_data;
132         struct snd_pcm_runtime *runtime = substream->runtime;
133         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
134         unsigned long dma_addr;
135         struct dma_chan *chan;
136         struct imx_pcm_dma_params *dma_params;
137         int ret;
138
139         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
140         ret = imx_ssi_dma_alloc(substream, params);
141         if (ret)
142                 return ret;
143         chan = iprtd->dma_chan;
144
145         iprtd->size = params_buffer_bytes(params);
146         iprtd->periods = params_periods(params);
147         iprtd->period_bytes = params_period_bytes(params);
148         iprtd->offset = 0;
149         iprtd->period_time = HZ / (params_rate(params) /
150                         params_period_size(params));
151
152         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
153
154         dma_addr = runtime->dma_addr;
155
156         iprtd->buf = (unsigned int *)substream->dma_buffer.area;
157
158         iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr,
159                         iprtd->period_bytes * iprtd->periods,
160                         iprtd->period_bytes,
161                         substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
162                         DMA_TO_DEVICE : DMA_FROM_DEVICE);
163         if (!iprtd->desc) {
164                 dev_err(&chan->dev->device, "cannot prepare slave dma\n");
165                 return -EINVAL;
166         }
167
168         iprtd->desc->callback = audio_dma_irq;
169         iprtd->desc->callback_param = substream;
170
171         return 0;
172 }
173
174 static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream)
175 {
176         struct snd_pcm_runtime *runtime = substream->runtime;
177         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
178
179         if (iprtd->dma_chan) {
180                 dma_release_channel(iprtd->dma_chan);
181                 iprtd->dma_chan = NULL;
182         }
183
184         return 0;
185 }
186
187 static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
188 {
189         struct snd_soc_pcm_runtime *rtd = substream->private_data;
190         struct imx_pcm_dma_params *dma_params;
191
192         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
193
194         return 0;
195 }
196
197 static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
198 {
199         struct snd_pcm_runtime *runtime = substream->runtime;
200         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
201
202         switch (cmd) {
203         case SNDRV_PCM_TRIGGER_START:
204         case SNDRV_PCM_TRIGGER_RESUME:
205         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
206                 dmaengine_submit(iprtd->desc);
207
208                 break;
209
210         case SNDRV_PCM_TRIGGER_STOP:
211         case SNDRV_PCM_TRIGGER_SUSPEND:
212         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
213                 dmaengine_terminate_all(iprtd->dma_chan);
214
215                 break;
216         default:
217                 return -EINVAL;
218         }
219
220         return 0;
221 }
222
223 static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
224 {
225         struct snd_pcm_runtime *runtime = substream->runtime;
226         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
227
228         pr_debug("%s: %ld %ld\n", __func__, iprtd->offset,
229                         bytes_to_frames(substream->runtime, iprtd->offset));
230
231         return bytes_to_frames(substream->runtime, iprtd->offset);
232 }
233
234 static struct snd_pcm_hardware snd_imx_hardware = {
235         .info = SNDRV_PCM_INFO_INTERLEAVED |
236                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
237                 SNDRV_PCM_INFO_MMAP |
238                 SNDRV_PCM_INFO_MMAP_VALID |
239                 SNDRV_PCM_INFO_PAUSE |
240                 SNDRV_PCM_INFO_RESUME,
241         .formats = SNDRV_PCM_FMTBIT_S16_LE,
242         .rate_min = 8000,
243         .channels_min = 2,
244         .channels_max = 2,
245         .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
246         .period_bytes_min = 128,
247         .period_bytes_max = 65535, /* Limited by SDMA engine */
248         .periods_min = 2,
249         .periods_max = 255,
250         .fifo_size = 0,
251 };
252
253 static int snd_imx_open(struct snd_pcm_substream *substream)
254 {
255         struct snd_pcm_runtime *runtime = substream->runtime;
256         struct imx_pcm_runtime_data *iprtd;
257         int ret;
258
259         iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
260         if (iprtd == NULL)
261                 return -ENOMEM;
262         runtime->private_data = iprtd;
263
264         ret = snd_pcm_hw_constraint_integer(substream->runtime,
265                         SNDRV_PCM_HW_PARAM_PERIODS);
266         if (ret < 0) {
267                 kfree(iprtd);
268                 return ret;
269         }
270
271         snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
272
273         return 0;
274 }
275
276 static int snd_imx_close(struct snd_pcm_substream *substream)
277 {
278         struct snd_pcm_runtime *runtime = substream->runtime;
279         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
280
281         kfree(iprtd);
282
283         return 0;
284 }
285
286 static struct snd_pcm_ops imx_pcm_ops = {
287         .open           = snd_imx_open,
288         .close          = snd_imx_close,
289         .ioctl          = snd_pcm_lib_ioctl,
290         .hw_params      = snd_imx_pcm_hw_params,
291         .hw_free        = snd_imx_pcm_hw_free,
292         .prepare        = snd_imx_pcm_prepare,
293         .trigger        = snd_imx_pcm_trigger,
294         .pointer        = snd_imx_pcm_pointer,
295         .mmap           = snd_imx_pcm_mmap,
296 };
297
298 static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
299         .ops            = &imx_pcm_ops,
300         .pcm_new        = imx_pcm_new,
301         .pcm_free       = imx_pcm_free,
302 };
303
304 static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
305 {
306         return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
307 }
308
309 static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
310 {
311         snd_soc_unregister_platform(&pdev->dev);
312         return 0;
313 }
314
315 static struct platform_driver imx_pcm_driver = {
316         .driver = {
317                         .name = "imx-pcm-audio",
318                         .owner = THIS_MODULE,
319         },
320         .probe = imx_soc_platform_probe,
321         .remove = __devexit_p(imx_soc_platform_remove),
322 };
323
324 static int __init snd_imx_pcm_init(void)
325 {
326         return platform_driver_register(&imx_pcm_driver);
327 }
328 module_init(snd_imx_pcm_init);
329
330 static void __exit snd_imx_pcm_exit(void)
331 {
332         platform_driver_unregister(&imx_pcm_driver);
333 }
334 module_exit(snd_imx_pcm_exit);