ALSA: hda - Add new codec ALC283 ALC290 support
[linux-3.10.git] / sound / soc / omap / omap-pcm.c
1 /*
2  * omap-pcm.c  --  ALSA PCM interface for the OMAP SoC
3  *
4  * Copyright (C) 2008 Nokia Corporation
5  *
6  * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
7  *          Peter Ujfalusi <peter.ujfalusi@ti.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * version 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 #include <linux/dma-mapping.h>
26 #include <linux/slab.h>
27 #include <linux/module.h>
28 #include <linux/omap-dma.h>
29 #include <sound/core.h>
30 #include <sound/pcm.h>
31 #include <sound/pcm_params.h>
32 #include <sound/dmaengine_pcm.h>
33 #include <sound/soc.h>
34
35 #include "omap-pcm.h"
36
37 static const struct snd_pcm_hardware omap_pcm_hardware = {
38         .info                   = SNDRV_PCM_INFO_MMAP |
39                                   SNDRV_PCM_INFO_MMAP_VALID |
40                                   SNDRV_PCM_INFO_INTERLEAVED |
41                                   SNDRV_PCM_INFO_PAUSE |
42                                   SNDRV_PCM_INFO_RESUME |
43                                   SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
44         .formats                = SNDRV_PCM_FMTBIT_S16_LE |
45                                   SNDRV_PCM_FMTBIT_S32_LE,
46         .period_bytes_min       = 32,
47         .period_bytes_max       = 64 * 1024,
48         .periods_min            = 2,
49         .periods_max            = 255,
50         .buffer_bytes_max       = 128 * 1024,
51 };
52
53 static int omap_pcm_get_dma_buswidth(int num_bits)
54 {
55         int buswidth;
56
57         switch (num_bits) {
58         case 16:
59                 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
60                 break;
61         case 32:
62                 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
63                 break;
64         default:
65                 buswidth = -EINVAL;
66                 break;
67         }
68         return buswidth;
69 }
70
71
72 /* this may get called several times by oss emulation */
73 static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
74                               struct snd_pcm_hw_params *params)
75 {
76         struct snd_pcm_runtime *runtime = substream->runtime;
77         struct snd_soc_pcm_runtime *rtd = substream->private_data;
78         struct omap_pcm_dma_data *dma_data;
79         struct dma_slave_config config;
80         struct dma_chan *chan;
81         int err = 0;
82
83         dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
84
85         /* return if this is a bufferless transfer e.g.
86          * codec <--> BT codec or GSM modem -- lg FIXME */
87         if (!dma_data)
88                 return 0;
89
90         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
91         runtime->dma_bytes = params_buffer_bytes(params);
92
93         chan = snd_dmaengine_pcm_get_chan(substream);
94         if (!chan)
95                 return -EINVAL;
96
97         /* fills in addr_width and direction */
98         err = snd_hwparams_to_dma_slave_config(substream, params, &config);
99         if (err)
100                 return err;
101
102         /* Override the *_dma addr_width if requested by the DAI driver */
103         if (dma_data->data_type) {
104                 int buswidth = omap_pcm_get_dma_buswidth(dma_data->data_type);
105
106                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
107                         config.dst_addr_width = buswidth;
108                 else
109                         config.src_addr_width = buswidth;
110         }
111
112         config.src_addr = dma_data->port_addr;
113         config.dst_addr = dma_data->port_addr;
114         config.src_maxburst = dma_data->packet_size;
115         config.dst_maxburst = dma_data->packet_size;
116
117         return dmaengine_slave_config(chan, &config);
118 }
119
120 static int omap_pcm_hw_free(struct snd_pcm_substream *substream)
121 {
122         snd_pcm_set_runtime_buffer(substream, NULL);
123         return 0;
124 }
125
126 static int omap_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
127 {
128         struct snd_soc_pcm_runtime *rtd = substream->private_data;
129         struct omap_pcm_dma_data *dma_data;
130         int ret = 0;
131
132         dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
133
134         switch (cmd) {
135         case SNDRV_PCM_TRIGGER_START:
136         case SNDRV_PCM_TRIGGER_RESUME:
137         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
138                 /* Configure McBSP internal buffer usage */
139                 if (dma_data->set_threshold)
140                         dma_data->set_threshold(substream);
141                 break;
142
143         case SNDRV_PCM_TRIGGER_STOP:
144         case SNDRV_PCM_TRIGGER_SUSPEND:
145         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
146                 break;
147         default:
148                 ret = -EINVAL;
149         }
150
151         if (ret == 0)
152                 ret = snd_dmaengine_pcm_trigger(substream, cmd);
153
154         return ret;
155 }
156
157 static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
158 {
159         snd_pcm_uframes_t offset;
160
161         if (cpu_is_omap1510())
162                 offset = snd_dmaengine_pcm_pointer_no_residue(substream);
163         else
164                 offset = snd_dmaengine_pcm_pointer(substream);
165
166         return offset;
167 }
168
169 static int omap_pcm_open(struct snd_pcm_substream *substream)
170 {
171         struct snd_pcm_runtime *runtime = substream->runtime;
172         struct snd_soc_pcm_runtime *rtd = substream->private_data;
173         struct omap_pcm_dma_data *dma_data;
174         int ret;
175
176         snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware);
177
178         /* Ensure that buffer size is a multiple of period size */
179         ret = snd_pcm_hw_constraint_integer(runtime,
180                                             SNDRV_PCM_HW_PARAM_PERIODS);
181         if (ret < 0)
182                 return ret;
183
184         dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
185         ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn,
186                                      &dma_data->dma_req);
187         return ret;
188 }
189
190 static int omap_pcm_close(struct snd_pcm_substream *substream)
191 {
192         snd_dmaengine_pcm_close(substream);
193         return 0;
194 }
195
196 static int omap_pcm_mmap(struct snd_pcm_substream *substream,
197         struct vm_area_struct *vma)
198 {
199         struct snd_pcm_runtime *runtime = substream->runtime;
200
201         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
202                                      runtime->dma_area,
203                                      runtime->dma_addr,
204                                      runtime->dma_bytes);
205 }
206
207 static struct snd_pcm_ops omap_pcm_ops = {
208         .open           = omap_pcm_open,
209         .close          = omap_pcm_close,
210         .ioctl          = snd_pcm_lib_ioctl,
211         .hw_params      = omap_pcm_hw_params,
212         .hw_free        = omap_pcm_hw_free,
213         .trigger        = omap_pcm_trigger,
214         .pointer        = omap_pcm_pointer,
215         .mmap           = omap_pcm_mmap,
216 };
217
218 static u64 omap_pcm_dmamask = DMA_BIT_MASK(64);
219
220 static int omap_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
221         int stream)
222 {
223         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
224         struct snd_dma_buffer *buf = &substream->dma_buffer;
225         size_t size = omap_pcm_hardware.buffer_bytes_max;
226
227         buf->dev.type = SNDRV_DMA_TYPE_DEV;
228         buf->dev.dev = pcm->card->dev;
229         buf->private_data = NULL;
230         buf->area = dma_alloc_writecombine(pcm->card->dev, size,
231                                            &buf->addr, GFP_KERNEL);
232         if (!buf->area)
233                 return -ENOMEM;
234
235         buf->bytes = size;
236         return 0;
237 }
238
239 static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
240 {
241         struct snd_pcm_substream *substream;
242         struct snd_dma_buffer *buf;
243         int stream;
244
245         for (stream = 0; stream < 2; stream++) {
246                 substream = pcm->streams[stream].substream;
247                 if (!substream)
248                         continue;
249
250                 buf = &substream->dma_buffer;
251                 if (!buf->area)
252                         continue;
253
254                 dma_free_writecombine(pcm->card->dev, buf->bytes,
255                                       buf->area, buf->addr);
256                 buf->area = NULL;
257         }
258 }
259
260 static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd)
261 {
262         struct snd_card *card = rtd->card->snd_card;
263         struct snd_pcm *pcm = rtd->pcm;
264         int ret = 0;
265
266         if (!card->dev->dma_mask)
267                 card->dev->dma_mask = &omap_pcm_dmamask;
268         if (!card->dev->coherent_dma_mask)
269                 card->dev->coherent_dma_mask = DMA_BIT_MASK(64);
270
271         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
272                 ret = omap_pcm_preallocate_dma_buffer(pcm,
273                         SNDRV_PCM_STREAM_PLAYBACK);
274                 if (ret)
275                         goto out;
276         }
277
278         if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
279                 ret = omap_pcm_preallocate_dma_buffer(pcm,
280                         SNDRV_PCM_STREAM_CAPTURE);
281                 if (ret)
282                         goto out;
283         }
284
285 out:
286         /* free preallocated buffers in case of error */
287         if (ret)
288                 omap_pcm_free_dma_buffers(pcm);
289
290         return ret;
291 }
292
293 static struct snd_soc_platform_driver omap_soc_platform = {
294         .ops            = &omap_pcm_ops,
295         .pcm_new        = omap_pcm_new,
296         .pcm_free       = omap_pcm_free_dma_buffers,
297 };
298
299 static __devinit int omap_pcm_probe(struct platform_device *pdev)
300 {
301         return snd_soc_register_platform(&pdev->dev,
302                         &omap_soc_platform);
303 }
304
305 static int __devexit omap_pcm_remove(struct platform_device *pdev)
306 {
307         snd_soc_unregister_platform(&pdev->dev);
308         return 0;
309 }
310
311 static struct platform_driver omap_pcm_driver = {
312         .driver = {
313                         .name = "omap-pcm-audio",
314                         .owner = THIS_MODULE,
315         },
316
317         .probe = omap_pcm_probe,
318         .remove = __devexit_p(omap_pcm_remove),
319 };
320
321 module_platform_driver(omap_pcm_driver);
322
323 MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
324 MODULE_DESCRIPTION("OMAP PCM DMA module");
325 MODULE_LICENSE("GPL");
326 MODULE_ALIAS("platform:omap-pcm-audio");