Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/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 #include <sound/dmaengine_pcm.h>
31
32 #include <mach/dma.h>
33
34 #include "imx-pcm.h"
35
36 static bool filter(struct dma_chan *chan, void *param)
37 {
38         if (!imx_dma_is_general_purpose(chan))
39                 return false;
40
41         chan->private = param;
42
43         return true;
44 }
45
46 static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
47                                 struct snd_pcm_hw_params *params)
48 {
49         struct snd_soc_pcm_runtime *rtd = substream->private_data;
50         struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
51         struct imx_pcm_dma_params *dma_params;
52         struct dma_slave_config slave_config;
53         int ret;
54
55         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
56
57         ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
58         if (ret)
59                 return ret;
60
61         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
62                 slave_config.dst_addr = dma_params->dma_addr;
63                 slave_config.dst_maxburst = dma_params->burstsize;
64         } else {
65                 slave_config.src_addr = dma_params->dma_addr;
66                 slave_config.src_maxburst = dma_params->burstsize;
67         }
68
69         ret = dmaengine_slave_config(chan, &slave_config);
70         if (ret)
71                 return ret;
72
73         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
74
75         return 0;
76 }
77
78 static struct snd_pcm_hardware snd_imx_hardware = {
79         .info = SNDRV_PCM_INFO_INTERLEAVED |
80                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
81                 SNDRV_PCM_INFO_MMAP |
82                 SNDRV_PCM_INFO_MMAP_VALID |
83                 SNDRV_PCM_INFO_PAUSE |
84                 SNDRV_PCM_INFO_RESUME,
85         .formats = SNDRV_PCM_FMTBIT_S16_LE,
86         .rate_min = 8000,
87         .channels_min = 2,
88         .channels_max = 2,
89         .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
90         .period_bytes_min = 128,
91         .period_bytes_max = 65535, /* Limited by SDMA engine */
92         .periods_min = 2,
93         .periods_max = 255,
94         .fifo_size = 0,
95 };
96
97 static int snd_imx_open(struct snd_pcm_substream *substream)
98 {
99         struct snd_soc_pcm_runtime *rtd = substream->private_data;
100         struct imx_pcm_dma_params *dma_params;
101         struct imx_dma_data *dma_data;
102         int ret;
103
104         snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
105
106         dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
107
108         dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
109         dma_data->peripheral_type = IMX_DMATYPE_SSI;
110         dma_data->priority = DMA_PRIO_HIGH;
111         dma_data->dma_request = dma_params->dma;
112
113         ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
114         if (ret) {
115                 kfree(dma_data);
116                 return 0;
117         }
118
119         snd_dmaengine_pcm_set_data(substream, dma_data);
120
121         return 0;
122 }
123
124 static int snd_imx_close(struct snd_pcm_substream *substream)
125 {
126         struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
127
128         snd_dmaengine_pcm_close(substream);
129         kfree(dma_data);
130
131         return 0;
132 }
133
134 static struct snd_pcm_ops imx_pcm_ops = {
135         .open           = snd_imx_open,
136         .close          = snd_imx_close,
137         .ioctl          = snd_pcm_lib_ioctl,
138         .hw_params      = snd_imx_pcm_hw_params,
139         .trigger        = snd_dmaengine_pcm_trigger,
140         .pointer        = snd_dmaengine_pcm_pointer,
141         .mmap           = snd_imx_pcm_mmap,
142 };
143
144 static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
145         .ops            = &imx_pcm_ops,
146         .pcm_new        = imx_pcm_new,
147         .pcm_free       = imx_pcm_free,
148 };
149
150 static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
151 {
152         return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
153 }
154
155 static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
156 {
157         snd_soc_unregister_platform(&pdev->dev);
158         return 0;
159 }
160
161 static struct platform_driver imx_pcm_driver = {
162         .driver = {
163                         .name = "imx-pcm-audio",
164                         .owner = THIS_MODULE,
165         },
166         .probe = imx_soc_platform_probe,
167         .remove = __devexit_p(imx_soc_platform_remove),
168 };
169
170 module_platform_driver(imx_pcm_driver);
171 MODULE_LICENSE("GPL");
172 MODULE_ALIAS("platform:imx-pcm-audio");