]> git.karo-electronics.de Git - karo-tx-linux.git/blob - sound/soc/imx/imx-pcm-dma-mx2.c
ASoC: imx-pcm: Request DMA channel early
[karo-tx-linux.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         unsigned long offset;
39         struct dma_async_tx_descriptor *desc;
40         struct dma_chan *dma_chan;
41         struct imx_dma_data dma_data;
42 };
43
44 static void audio_dma_irq(void *data)
45 {
46         struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
47         struct snd_pcm_runtime *runtime = substream->runtime;
48         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
49
50         iprtd->offset += iprtd->period_bytes;
51         iprtd->offset %= iprtd->period_bytes * iprtd->periods;
52
53         snd_pcm_period_elapsed(substream);
54 }
55
56 static bool filter(struct dma_chan *chan, void *param)
57 {
58         struct imx_pcm_runtime_data *iprtd = param;
59
60         if (!imx_dma_is_general_purpose(chan))
61                 return false;
62
63         chan->private = &iprtd->dma_data;
64
65         return true;
66 }
67
68 static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream)
69 {
70         struct snd_soc_pcm_runtime *rtd = substream->private_data;
71         struct imx_pcm_dma_params *dma_params;
72         struct snd_pcm_runtime *runtime = substream->runtime;
73         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
74         dma_cap_mask_t mask;
75
76         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
77
78         iprtd->dma_data.peripheral_type = IMX_DMATYPE_SSI;
79         iprtd->dma_data.priority = DMA_PRIO_HIGH;
80         iprtd->dma_data.dma_request = dma_params->dma;
81
82         /* Try to grab a DMA channel */
83         dma_cap_zero(mask);
84         dma_cap_set(DMA_SLAVE, mask);
85         iprtd->dma_chan = dma_request_channel(mask, filter, iprtd);
86         if (!iprtd->dma_chan)
87                 return -EINVAL;
88
89         return 0;
90 }
91
92 static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
93                                 struct snd_pcm_hw_params *params)
94 {
95         struct snd_soc_pcm_runtime *rtd = substream->private_data;
96         struct snd_pcm_runtime *runtime = substream->runtime;
97         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
98         struct dma_chan *chan = iprtd->dma_chan;
99         struct imx_pcm_dma_params *dma_params;
100         struct dma_slave_config slave_config;
101         enum dma_slave_buswidth buswidth;
102         unsigned long dma_addr;
103         int ret;
104
105         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
106
107         switch (params_format(params)) {
108         case SNDRV_PCM_FORMAT_S16_LE:
109                 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
110                 break;
111         case SNDRV_PCM_FORMAT_S20_3LE:
112         case SNDRV_PCM_FORMAT_S24_LE:
113                 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
114                 break;
115         default:
116                 return 0;
117         }
118
119         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
120                 slave_config.direction = DMA_MEM_TO_DEV;
121                 slave_config.dst_addr = dma_params->dma_addr;
122                 slave_config.dst_addr_width = buswidth;
123                 slave_config.dst_maxburst = dma_params->burstsize;
124         } else {
125                 slave_config.direction = DMA_DEV_TO_MEM;
126                 slave_config.src_addr = dma_params->dma_addr;
127                 slave_config.src_addr_width = buswidth;
128                 slave_config.src_maxburst = dma_params->burstsize;
129         }
130
131         ret = dmaengine_slave_config(chan, &slave_config);
132         if (ret)
133                 return ret;
134
135
136         iprtd->periods = params_periods(params);
137         iprtd->period_bytes = params_period_bytes(params);
138         iprtd->offset = 0;
139
140         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
141
142         dma_addr = runtime->dma_addr;
143
144         iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr,
145                         iprtd->period_bytes * iprtd->periods,
146                         iprtd->period_bytes,
147                         substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
148                         DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
149         if (!iprtd->desc) {
150                 dev_err(&chan->dev->device, "cannot prepare slave dma\n");
151                 return -EINVAL;
152         }
153
154         iprtd->desc->callback = audio_dma_irq;
155         iprtd->desc->callback_param = substream;
156
157         return 0;
158 }
159
160 static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
161 {
162         struct snd_pcm_runtime *runtime = substream->runtime;
163         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
164
165         switch (cmd) {
166         case SNDRV_PCM_TRIGGER_START:
167         case SNDRV_PCM_TRIGGER_RESUME:
168         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
169                 dmaengine_submit(iprtd->desc);
170
171                 break;
172
173         case SNDRV_PCM_TRIGGER_STOP:
174         case SNDRV_PCM_TRIGGER_SUSPEND:
175         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
176                 dmaengine_terminate_all(iprtd->dma_chan);
177
178                 break;
179         default:
180                 return -EINVAL;
181         }
182
183         return 0;
184 }
185
186 static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
187 {
188         struct snd_pcm_runtime *runtime = substream->runtime;
189         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
190
191         pr_debug("%s: %ld %ld\n", __func__, iprtd->offset,
192                         bytes_to_frames(substream->runtime, iprtd->offset));
193
194         return bytes_to_frames(substream->runtime, iprtd->offset);
195 }
196
197 static struct snd_pcm_hardware snd_imx_hardware = {
198         .info = SNDRV_PCM_INFO_INTERLEAVED |
199                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
200                 SNDRV_PCM_INFO_MMAP |
201                 SNDRV_PCM_INFO_MMAP_VALID |
202                 SNDRV_PCM_INFO_PAUSE |
203                 SNDRV_PCM_INFO_RESUME,
204         .formats = SNDRV_PCM_FMTBIT_S16_LE,
205         .rate_min = 8000,
206         .channels_min = 2,
207         .channels_max = 2,
208         .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
209         .period_bytes_min = 128,
210         .period_bytes_max = 65535, /* Limited by SDMA engine */
211         .periods_min = 2,
212         .periods_max = 255,
213         .fifo_size = 0,
214 };
215
216 static int snd_imx_open(struct snd_pcm_substream *substream)
217 {
218         struct snd_pcm_runtime *runtime = substream->runtime;
219         struct imx_pcm_runtime_data *iprtd;
220         int ret;
221
222         iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
223         if (iprtd == NULL)
224                 return -ENOMEM;
225         runtime->private_data = iprtd;
226
227         ret = snd_pcm_hw_constraint_integer(substream->runtime,
228                         SNDRV_PCM_HW_PARAM_PERIODS);
229         if (ret < 0) {
230                 kfree(iprtd);
231                 return ret;
232         }
233
234         ret = imx_ssi_dma_alloc(substream);
235         if (ret < 0) {
236                 kfree(iprtd);
237                 return ret;
238         }
239
240         snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
241
242         return 0;
243 }
244
245 static int snd_imx_close(struct snd_pcm_substream *substream)
246 {
247         struct snd_pcm_runtime *runtime = substream->runtime;
248         struct imx_pcm_runtime_data *iprtd = runtime->private_data;
249
250         dma_release_channel(iprtd->dma_chan);
251         kfree(iprtd);
252
253         return 0;
254 }
255
256 static struct snd_pcm_ops imx_pcm_ops = {
257         .open           = snd_imx_open,
258         .close          = snd_imx_close,
259         .ioctl          = snd_pcm_lib_ioctl,
260         .hw_params      = snd_imx_pcm_hw_params,
261         .trigger        = snd_imx_pcm_trigger,
262         .pointer        = snd_imx_pcm_pointer,
263         .mmap           = snd_imx_pcm_mmap,
264 };
265
266 static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
267         .ops            = &imx_pcm_ops,
268         .pcm_new        = imx_pcm_new,
269         .pcm_free       = imx_pcm_free,
270 };
271
272 static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
273 {
274         struct imx_ssi *ssi = platform_get_drvdata(pdev);
275
276         ssi->dma_params_tx.burstsize = 6;
277         ssi->dma_params_rx.burstsize = 4;
278
279         return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
280 }
281
282 static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
283 {
284         snd_soc_unregister_platform(&pdev->dev);
285         return 0;
286 }
287
288 static struct platform_driver imx_pcm_driver = {
289         .driver = {
290                         .name = "imx-pcm-audio",
291                         .owner = THIS_MODULE,
292         },
293         .probe = imx_soc_platform_probe,
294         .remove = __devexit_p(imx_soc_platform_remove),
295 };
296
297 module_platform_driver(imx_pcm_driver);
298 MODULE_LICENSE("GPL");
299 MODULE_ALIAS("platform:imx-pcm-audio");