ASoC: wm8994: Ensure microphone detection state is reset on removal
[cascardo/linux.git] / sound / soc / spear / spear_pcm.c
1 /*
2  * ALSA PCM interface for ST SPEAr Processors
3  *
4  * sound/soc/spear/spear_pcm.c
5  *
6  * Copyright (C) 2012 ST Microelectronics
7  * Rajeev Kumar<rajeev-dlh.kumar@st.com>
8  *
9  * This file is licensed under the terms of the GNU General Public
10  * License version 2. This program is licensed "as is" without any
11  * warranty of any kind, whether express or implied.
12  */
13
14 #include <linux/module.h>
15 #include <linux/dmaengine.h>
16 #include <linux/dma-mapping.h>
17 #include <linux/init.h>
18 #include <linux/platform_device.h>
19 #include <linux/scatterlist.h>
20 #include <linux/slab.h>
21 #include <sound/core.h>
22 #include <sound/dmaengine_pcm.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/soc.h>
26 #include <sound/spear_dma.h>
27
28 static struct snd_pcm_hardware spear_pcm_hardware = {
29         .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
30                  SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
31                  SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
32         .buffer_bytes_max = 16 * 1024, /* max buffer size */
33         .period_bytes_min = 2 * 1024, /* 1 msec data minimum period size */
34         .period_bytes_max = 2 * 1024, /* maximum period size */
35         .periods_min = 1, /* min # periods */
36         .periods_max = 8, /* max # of periods */
37         .fifo_size = 0, /* fifo size in bytes */
38 };
39
40 static int spear_pcm_hw_params(struct snd_pcm_substream *substream,
41                 struct snd_pcm_hw_params *params)
42 {
43         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
44
45         return 0;
46 }
47
48 static int spear_pcm_hw_free(struct snd_pcm_substream *substream)
49 {
50         snd_pcm_set_runtime_buffer(substream, NULL);
51
52         return 0;
53 }
54
55 static int spear_pcm_open(struct snd_pcm_substream *substream)
56 {
57         struct snd_soc_pcm_runtime *rtd = substream->private_data;
58
59         struct spear_dma_data *dma_data = (struct spear_dma_data *)
60                 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
61         int ret;
62
63         ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware);
64         if (ret)
65                 return ret;
66
67         return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter,
68                                 dma_data);
69 }
70
71 static int spear_pcm_mmap(struct snd_pcm_substream *substream,
72                 struct vm_area_struct *vma)
73 {
74         struct snd_pcm_runtime *runtime = substream->runtime;
75
76         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
77                         runtime->dma_area, runtime->dma_addr,
78                         runtime->dma_bytes);
79 }
80
81 static struct snd_pcm_ops spear_pcm_ops = {
82         .open           = spear_pcm_open,
83         .close          = snd_dmaengine_pcm_close_release_chan,
84         .ioctl          = snd_pcm_lib_ioctl,
85         .hw_params      = spear_pcm_hw_params,
86         .hw_free        = spear_pcm_hw_free,
87         .trigger        = snd_dmaengine_pcm_trigger,
88         .pointer        = snd_dmaengine_pcm_pointer,
89         .mmap           = spear_pcm_mmap,
90 };
91
92 static int
93 spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
94                 size_t size)
95 {
96         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
97         struct snd_dma_buffer *buf = &substream->dma_buffer;
98
99         buf->dev.type = SNDRV_DMA_TYPE_DEV;
100         buf->dev.dev = pcm->card->dev;
101         buf->private_data = NULL;
102
103         buf->area = dma_alloc_writecombine(pcm->card->dev, size,
104                         &buf->addr, GFP_KERNEL);
105         if (!buf->area)
106                 return -ENOMEM;
107
108         dev_info(buf->dev.dev,
109                         " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
110                         (void *)buf->area, (void *)buf->addr, size);
111
112         buf->bytes = size;
113         return 0;
114 }
115
116 static void spear_pcm_free(struct snd_pcm *pcm)
117 {
118         struct snd_pcm_substream *substream;
119         struct snd_dma_buffer *buf;
120         int stream;
121
122         for (stream = 0; stream < 2; stream++) {
123                 substream = pcm->streams[stream].substream;
124                 if (!substream)
125                         continue;
126
127                 buf = &substream->dma_buffer;
128                 if (!buf || !buf->area)
129                         continue;
130
131                 dma_free_writecombine(pcm->card->dev, buf->bytes,
132                                 buf->area, buf->addr);
133                 buf->area = NULL;
134         }
135 }
136
137 static u64 spear_pcm_dmamask = DMA_BIT_MASK(32);
138
139 static int spear_pcm_new(struct snd_soc_pcm_runtime *rtd)
140 {
141         struct snd_card *card = rtd->card->snd_card;
142         int ret;
143
144         if (!card->dev->dma_mask)
145                 card->dev->dma_mask = &spear_pcm_dmamask;
146         if (!card->dev->coherent_dma_mask)
147                 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
148
149         if (rtd->cpu_dai->driver->playback.channels_min) {
150                 ret = spear_pcm_preallocate_dma_buffer(rtd->pcm,
151                                 SNDRV_PCM_STREAM_PLAYBACK,
152                                 spear_pcm_hardware.buffer_bytes_max);
153                 if (ret)
154                         return ret;
155         }
156
157         if (rtd->cpu_dai->driver->capture.channels_min) {
158                 ret = spear_pcm_preallocate_dma_buffer(rtd->pcm,
159                                 SNDRV_PCM_STREAM_CAPTURE,
160                                 spear_pcm_hardware.buffer_bytes_max);
161                 if (ret)
162                         return ret;
163         }
164
165         return 0;
166 }
167
168 static struct snd_soc_platform_driver spear_soc_platform = {
169         .ops            =       &spear_pcm_ops,
170         .pcm_new        =       spear_pcm_new,
171         .pcm_free       =       spear_pcm_free,
172 };
173
174 static int spear_soc_platform_probe(struct platform_device *pdev)
175 {
176         return snd_soc_register_platform(&pdev->dev, &spear_soc_platform);
177 }
178
179 static int spear_soc_platform_remove(struct platform_device *pdev)
180 {
181         snd_soc_unregister_platform(&pdev->dev);
182
183         return 0;
184 }
185
186 static struct platform_driver spear_pcm_driver = {
187         .driver = {
188                 .name = "spear-pcm-audio",
189                 .owner = THIS_MODULE,
190         },
191
192         .probe = spear_soc_platform_probe,
193         .remove = spear_soc_platform_remove,
194 };
195
196 module_platform_driver(spear_pcm_driver);
197
198 MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
199 MODULE_DESCRIPTION("SPEAr PCM DMA module");
200 MODULE_LICENSE("GPL");
201 MODULE_ALIAS("platform:spear-pcm-audio");