blob: efd7c06ac64299ecb90796257b64711e0e5f01ca [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13
14#include <linux/dma-mapping.h>
15#include <linux/slab.h>
16#include <sound/core.h>
17#include <sound/pcm.h>
18#include <sound/pcm_params.h>
19#include <sound/soc.h>
20#include <mach/audio_dma_msm8k.h>
21#include <sound/dai.h>
Bryan Huntsman1682f242011-09-29 11:12:13 -070022#include "lpass-pcm.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023
24static const struct snd_pcm_hardware msm_pcm_hardware = {
25 .info = SNDRV_PCM_INFO_MMAP |
26 SNDRV_PCM_INFO_MMAP_VALID |
27 SNDRV_PCM_INFO_INTERLEAVED |
28 SNDRV_PCM_INFO_PAUSE |
29 SNDRV_PCM_INFO_RESUME,
30 .rates = SNDRV_PCM_RATE_8000_48000,
31 .formats = SNDRV_PCM_FMTBIT_S16_LE,
32 .period_bytes_min = 32,
33 .period_bytes_max = DMASZ/4,
34 .buffer_bytes_max = DMASZ,
35 .rate_max = 96000,
36 .rate_min = 8000,
37 .channels_min = USE_CHANNELS_MIN,
38 .channels_max = USE_CHANNELS_MAX,
39 .periods_min = 4,
40 .periods_max = 512,
41 .fifo_size = 0,
42};
43
44struct msm_pcm_data {
45 spinlock_t lock;
46 int ch;
47};
48
49/* Conventional and unconventional sample rate supported */
50static unsigned int supported_sample_rates[] = {
51 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
52};
53
54static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
55 .count = ARRAY_SIZE(supported_sample_rates),
56 .list = supported_sample_rates,
57 .mask = 0,
58};
59
60static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
61 struct snd_pcm_hw_params *params)
62{
63
64 pr_debug("%s\n", __func__);
65 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
66 return 0;
67}
68
69static irqreturn_t msm_pcm_irq(int intrsrc, void *data)
70{
71 struct snd_pcm_substream *substream = data;
72 struct snd_pcm_runtime *runtime = substream->runtime;
73 struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
74 int dma_ch = 0;
75 unsigned int has_xrun, pending;
76 int ret = IRQ_NONE;
77
78 if (prtd)
79 dma_ch = prtd->dma_ch;
80 else
81 return ret;
82
83 pr_debug("msm8660-pcm: msm_pcm_irq called\n");
84 pending = (intrsrc
85 & (UNDER_CH(dma_ch) | PER_CH(dma_ch) | ERR_CH(dma_ch)));
86 has_xrun = (pending & UNDER_CH(dma_ch));
87
88 if (unlikely(has_xrun) &&
89 substream->runtime &&
90 snd_pcm_running(substream)) {
91 pr_err("xrun\n");
92 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
93 ret = IRQ_HANDLED;
94 pending &= ~UNDER_CH(dma_ch);
95 }
96
97
98 if (pending & PER_CH(dma_ch)) {
99 ret = IRQ_HANDLED;
100 if (likely(substream->runtime &&
101 snd_pcm_running(substream))) {
102 /* end of buffer missed? loop back */
103 if (++prtd->period_index >= runtime->periods)
104 prtd->period_index = 0;
105 snd_pcm_period_elapsed(substream);
106 pr_debug("period elapsed\n");
107 }
108 pending &= ~PER_CH(dma_ch);
109 }
110
111 if (unlikely(pending
112 & (UNDER_CH(dma_ch) & PER_CH(dma_ch) & ERR_CH(dma_ch)))) {
113 if (pending & UNDER_CH(dma_ch))
114 pr_err("msm8660-pcm: DMA %x Underflow\n",
115 dma_ch);
116 if (pending & ERR_CH(dma_ch))
117 pr_err("msm8660-pcm: DMA %x Master Error\n",
118 dma_ch);
119
120 }
121 return ret;
122}
123
124static int msm_pcm_prepare(struct snd_pcm_substream *substream)
125{
126 struct snd_pcm_runtime *runtime = substream->runtime;
127 struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
128 struct dai_dma_params dma_params;
129 int dma_ch = 0;
130
131 if (prtd)
132 dma_ch = prtd->dma_ch;
133 else
134 return 0;
135
136 prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
137 prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
138 pr_debug("%s:prtd->pcm_size = %d\n", __func__, prtd->pcm_size);
139 pr_debug("%s:prtd->pcm_count = %d\n", __func__, prtd->pcm_count);
140
141 if (prtd->enabled)
142 return 0;
143
144 dma_params.src_start = runtime->dma_addr;
145 dma_params.buffer = (u8 *)runtime->dma_area;
146 dma_params.buffer_size = prtd->pcm_size;
147 dma_params.period_size = prtd->pcm_count;
148 dma_params.channels = runtime->channels;
149
150 dai_set_params(dma_ch, &dma_params);
151 register_dma_irq_handler(dma_ch, msm_pcm_irq, (void *)substream);
152
153 prtd->enabled = 1;
154 return 0;
155}
156
157static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
158{
159 struct snd_pcm_runtime *runtime = substream->runtime;
160 struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
161 int ret = 0;
162
163 pr_debug("%s\n", __func__);
164 switch (cmd) {
165 case SNDRV_PCM_TRIGGER_START:
166 case SNDRV_PCM_TRIGGER_RESUME:
167 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
168 dai_start(prtd->dma_ch);
169 break;
170 case SNDRV_PCM_TRIGGER_STOP:
171 case SNDRV_PCM_TRIGGER_SUSPEND:
172 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
173 dai_stop(prtd->dma_ch);
174 break;
175 default:
176 ret = -EINVAL;
177 }
178 return ret;
179}
180
181static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
182{
183 struct snd_pcm_runtime *runtime = substream->runtime;
184 struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
185 snd_pcm_uframes_t offset = 0;
186
187 pr_debug("%s: period_index =%d\n", __func__, prtd->period_index);
188 offset = prtd->period_index * runtime->period_size;
189 if (offset >= runtime->buffer_size)
190 offset = 0;
191 return offset;
192}
193
194static int msm_pcm_open(struct snd_pcm_substream *substream)
195{
196 struct snd_pcm_runtime *runtime = substream->runtime;
197 struct snd_soc_pcm_runtime *rtd = substream->private_data;
198 struct snd_soc_dai_link *machine = rtd->dai;
199 struct snd_soc_dai *cpu_dai = machine->cpu_dai;
200 struct msm_audio *prtd = NULL;
201 int ret = 0;
202
203 pr_debug("%s\n", __func__);
204 snd_soc_set_runtime_hwparams(substream, &msm_pcm_hardware);
205
206 ret = snd_pcm_hw_constraint_integer(runtime,
207 SNDRV_PCM_HW_PARAM_PERIODS);
208
209 if (ret < 0) {
210 pr_err("Error setting hw_constraint\n");
211 goto err;
212 }
213 ret = snd_pcm_hw_constraint_list(runtime, 0,
214 SNDRV_PCM_HW_PARAM_RATE,
215 &constraints_sample_rates);
216 if (ret < 0)
217 pr_err("Error snd_pcm_hw_constraint_list failed\n");
218
219 prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
220
221 if (prtd == NULL) {
222 pr_err("Error allocating prtd\n");
223 ret = -ENOMEM;
224 goto err;
225 }
226 prtd->dma_ch = cpu_dai->id;
227 prtd->enabled = 0;
228 runtime->dma_bytes = msm_pcm_hardware.buffer_bytes_max;
229 runtime->private_data = prtd;
230err:
231 return ret;
232}
233
234static int msm_pcm_close(struct snd_pcm_substream *substream)
235{
236 struct snd_pcm_runtime *runtime = substream->runtime;
237 struct msm_audio *prtd = (struct msm_audio *)runtime->private_data;
238 int dma_ch = 0;
239
240 if (prtd)
241 dma_ch = prtd->dma_ch;
242 else
243 return 0;
244
245 pr_debug("%s\n", __func__);
246 unregister_dma_irq_handler(dma_ch);
247 kfree(runtime->private_data);
248 return 0;
249}
250
251static int msm_pcm_mmap(struct snd_pcm_substream *substream,
252 struct vm_area_struct *vms)
253{
254 struct snd_pcm_runtime *runtime = substream->runtime;
255
256 pr_debug("%s\n", __func__);
257 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
258 pr_debug("%s: snd_msm_audio_hw_params runtime->dma_addr 0x(%x)\n",
259 __func__, (unsigned int)runtime->dma_addr);
260 pr_debug("%s: snd_msm_audio_hw_params runtime->dma_area 0x(%x)\n",
261 __func__, (unsigned int)runtime->dma_area);
262 pr_debug("%s: snd_msm_audio_hw_params runtime->dma_bytes 0x(%x)\n",
263 __func__, (unsigned int)runtime->dma_bytes);
264
265 return dma_mmap_coherent(substream->pcm->card->dev, vms,
266 runtime->dma_area,
267 runtime->dma_addr,
268 runtime->dma_bytes);
269}
270
271
272static struct snd_pcm_ops msm_pcm_ops = {
273 .open = msm_pcm_open,
274 .close = msm_pcm_close,
275 .ioctl = snd_pcm_lib_ioctl,
276 .hw_params = msm_pcm_hw_params,
277 .prepare = msm_pcm_prepare,
278 .trigger = msm_pcm_trigger,
279 .pointer = msm_pcm_pointer,
280 .mmap = msm_pcm_mmap,
281};
282
283static int pcm_preallocate_buffer(struct snd_pcm *pcm,
284 int stream)
285{
286 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
287 struct snd_dma_buffer *buf = &substream->dma_buffer;
288 size_t size = msm_pcm_hardware.buffer_bytes_max;
289 buf->dev.type = SNDRV_DMA_TYPE_DEV;
290 buf->dev.dev = pcm->card->dev;
291 buf->private_data = NULL;
292 buf->area = dma_alloc_coherent(pcm->card->dev, size,
293 &buf->addr, GFP_KERNEL);
294
295 if (!buf->area)
296 return -ENOMEM;
297
298 buf->bytes = size;
299 return 0;
300}
301
302static void msm_pcm_free_buffers(struct snd_pcm *pcm)
303{
304 struct snd_pcm_substream *substream;
305 struct snd_dma_buffer *buf;
306 int stream;
307
308 for (stream = 0; stream < 2; stream++) {
309 substream = pcm->streams[stream].substream;
310 if (!stream)
311 continue;
312
313 buf = &substream->dma_buffer;
314 if (!buf->area)
315 continue;
316
317 dma_free_coherent(pcm->card->dev, buf->bytes,
318 buf->area, buf->addr);
319 buf->area = NULL;
320 }
321}
322static u64 msm_pcm_dmamask = DMA_BIT_MASK(32);
323
324static int msm_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
325 struct snd_pcm *pcm)
326{
327 int ret = 0;
328
329 if (!card->dev->dma_mask)
330 card->dev->dma_mask = &msm_pcm_dmamask;
331 if (!card->dev->coherent_dma_mask)
332 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
333
334 if (dai->playback.channels_min) {
335 ret = pcm_preallocate_buffer(pcm,
336 SNDRV_PCM_STREAM_PLAYBACK);
337 if (ret)
338 return ret;
339 }
340 if (dai->capture.channels_min) {
341 ret = pcm_preallocate_buffer(pcm,
342 SNDRV_PCM_STREAM_CAPTURE);
343 if (ret)
344 return ret;
345 }
346 return ret;
347}
348
349struct snd_soc_platform msm8660_soc_platform = {
350 .name = "msm8660-pcm-audio",
351 .pcm_ops = &msm_pcm_ops,
352 .pcm_new = msm_pcm_new,
353 .pcm_free = msm_pcm_free_buffers,
354};
355EXPORT_SYMBOL_GPL(msm8660_soc_platform);
356
357static int __init msm_soc_platform_init(void)
358{
359 return snd_soc_register_platform(&msm8660_soc_platform);
360}
361static void __exit msm_soc_platform_exit(void)
362{
363 snd_soc_unregister_platform(&msm8660_soc_platform);
364}
365module_init(msm_soc_platform_init);
366module_exit(msm_soc_platform_exit);
367
368MODULE_DESCRIPTION("MSM PCM module");
369MODULE_LICENSE("GPL v2");