blob: 575839d6bf3b5186002de63ff8ad932426a537aa [file] [log] [blame]
Shiv Maliyappanahalli0f6c1db2012-01-19 15:14:46 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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#include <linux/init.h>
14#include <linux/err.h>
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/time.h>
18#include <linux/wait.h>
19#include <linux/platform_device.h>
20#include <linux/slab.h>
21#include <sound/core.h>
22#include <sound/soc.h>
23#include <sound/soc-dapm.h>
24#include <sound/pcm.h>
25#include <sound/initval.h>
26#include <sound/control.h>
27#include <asm/dma.h>
28#include <linux/dma-mapping.h>
29#include <linux/android_pmem.h>
Tejas Shikhare727416f2012-02-14 17:46:31 -080030#include <sound/snd_compress_params.h>
31#include <sound/compress_offload.h>
32#include <sound/compress_driver.h>
Tejas Shikhare394dc962012-03-02 16:46:24 -080033#include <sound/timer.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
35#include "msm-pcm-q6.h"
36#include "msm-pcm-routing.h"
37
38static struct audio_locks the_locks;
39
40struct snd_msm {
Asish Bhattacharya0ec76182011-07-29 16:58:11 +053041 struct msm_audio *prtd;
42 unsigned volume;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043};
Asish Bhattacharya0ec76182011-07-29 16:58:11 +053044static struct snd_msm lpa_audio;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
46static struct snd_pcm_hardware msm_pcm_hardware = {
47 .info = (SNDRV_PCM_INFO_MMAP |
48 SNDRV_PCM_INFO_BLOCK_TRANSFER |
49 SNDRV_PCM_INFO_MMAP_VALID |
50 SNDRV_PCM_INFO_INTERLEAVED |
51 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
52 .formats = SNDRV_PCM_FMTBIT_S16_LE,
Asish Bhattacharyad69d2842011-10-14 14:53:30 +053053 .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054 .rate_min = 8000,
55 .rate_max = 48000,
56 .channels_min = 1,
57 .channels_max = 2,
58 .buffer_bytes_max = 2 * 1024 * 1024,
59/* TODO: Check on the lowest period size we can support */
60 .period_bytes_min = 128 * 1024,
61 .period_bytes_max = 512 * 1024,
62 .periods_min = 4,
63 .periods_max = 16,
64 .fifo_size = 0,
65};
66
67/* Conventional and unconventional sample rate supported */
68static unsigned int supported_sample_rates[] = {
69 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
70};
71
72static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
73 .count = ARRAY_SIZE(supported_sample_rates),
74 .list = supported_sample_rates,
75 .mask = 0,
76};
77
78static void event_handler(uint32_t opcode,
79 uint32_t token, uint32_t *payload, void *priv)
80{
81 struct msm_audio *prtd = priv;
82 struct snd_pcm_substream *substream = prtd->substream;
83 struct snd_pcm_runtime *runtime = substream->runtime;
84 struct audio_aio_write_param param;
Asish Bhattacharya49831e42011-10-14 12:36:55 +053085 struct audio_buffer *buf = NULL;
Asish Bhattacharyaa4a4baa2011-10-12 13:58:41 +053086 unsigned long flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087 int i = 0;
88
89 pr_debug("%s\n", __func__);
Asish Bhattacharyaa4a4baa2011-10-12 13:58:41 +053090 spin_lock_irqsave(&the_locks.event_lock, flag);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 switch (opcode) {
92 case ASM_DATA_EVENT_WRITE_DONE: {
93 uint32_t *ptrmem = (uint32_t *)&param;
94 pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
95 pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
96 prtd->pcm_irq_pos += prtd->pcm_count;
Sriranjan Srikantamb52f0072012-03-24 11:23:04 -070097 if (prtd->pcm_irq_pos >= prtd->pcm_size)
98 prtd->pcm_irq_pos = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099 if (atomic_read(&prtd->start))
100 snd_pcm_period_elapsed(substream);
Tejas Shikhare394dc962012-03-02 16:46:24 -0800101 else
102 if (substream->timer_running)
103 snd_timer_interrupt(substream->timer, 1);
104
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 atomic_inc(&prtd->out_count);
106 wake_up(&the_locks.write_wait);
Asish Bhattacharya6a96f372011-07-13 20:01:43 +0530107 if (!atomic_read(&prtd->start)) {
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530108 atomic_set(&prtd->pending_buffer, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 break;
Asish Bhattacharya6a96f372011-07-13 20:01:43 +0530110 } else
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530111 atomic_set(&prtd->pending_buffer, 0);
Tejas Shikhare6fd0e002012-01-31 21:17:29 -0800112 if (runtime->status->hw_ptr >= runtime->control->appl_ptr)
113 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114 pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
115 __func__, prtd->pcm_count);
116
Asish Bhattacharya49831e42011-10-14 12:36:55 +0530117 buf = prtd->audio_client->port[IN].buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118 param.paddr = (unsigned long)buf[0].phys
119 + (prtd->out_head * prtd->pcm_count);
120 param.len = prtd->pcm_count;
121 param.msw_ts = 0;
122 param.lsw_ts = 0;
123 param.flags = NO_TIMESTAMP;
124 param.uid = (unsigned long)buf[0].phys
125 + (prtd->out_head * prtd->pcm_count);
126 for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
127 i++, ++ptrmem)
128 pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
129 if (q6asm_async_write(prtd->audio_client,
130 &param) < 0)
131 pr_err("%s:q6asm_async_write failed\n",
132 __func__);
133 else
134 prtd->out_head =
135 (prtd->out_head + 1) & (runtime->periods - 1);
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530136 atomic_set(&prtd->pending_buffer, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137 break;
138 }
139 case ASM_DATA_CMDRSP_EOS:
140 pr_debug("ASM_DATA_CMDRSP_EOS\n");
141 prtd->cmd_ack = 1;
142 wake_up(&the_locks.eos_wait);
143 break;
144 case APR_BASIC_RSP_RESULT: {
145 switch (payload[0]) {
146 case ASM_SESSION_CMD_RUN: {
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530147 if (!atomic_read(&prtd->pending_buffer))
Asish Bhattacharya6a96f372011-07-13 20:01:43 +0530148 break;
Tejas Shikhare6fd0e002012-01-31 21:17:29 -0800149 if (runtime->status->hw_ptr >=
150 runtime->control->appl_ptr)
151 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 pr_debug("%s:writing %d bytes"
153 " of buffer to dsp\n",
154 __func__, prtd->pcm_count);
Asish Bhattacharya49831e42011-10-14 12:36:55 +0530155 buf = prtd->audio_client->port[IN].buf;
Asish Bhattacharya43e46a32011-09-22 19:38:50 +0530156 param.paddr = (unsigned long)buf[prtd->out_head].phys;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 param.len = prtd->pcm_count;
158 param.msw_ts = 0;
159 param.lsw_ts = 0;
160 param.flags = NO_TIMESTAMP;
Asish Bhattacharya43e46a32011-09-22 19:38:50 +0530161 param.uid = (unsigned long)buf[prtd->out_head].phys;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 if (q6asm_async_write(prtd->audio_client,
163 &param) < 0)
164 pr_err("%s:q6asm_async_write failed\n",
165 __func__);
166 else
167 prtd->out_head =
168 (prtd->out_head + 1)
169 & (runtime->periods - 1);
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530170 atomic_set(&prtd->pending_buffer, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 }
172 break;
Asish Bhattacharya44b64112011-09-08 18:56:19 +0530173 case ASM_STREAM_CMD_FLUSH:
174 pr_debug("ASM_STREAM_CMD_FLUSH\n");
175 prtd->cmd_ack = 1;
176 wake_up(&the_locks.eos_wait);
177 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178 default:
179 break;
180 }
181 break;
182 }
183 default:
184 pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
185 break;
186 }
Asish Bhattacharyaa4a4baa2011-10-12 13:58:41 +0530187 spin_unlock_irqrestore(&the_locks.event_lock, flag);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188}
189
190static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
191{
192 struct snd_pcm_runtime *runtime = substream->runtime;
193 struct msm_audio *prtd = runtime->private_data;
194 int ret;
195
196 pr_debug("%s\n", __func__);
197 prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
198 prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
199 prtd->pcm_irq_pos = 0;
200 /* rate and channels are sent to audio driver */
201 prtd->samp_rate = runtime->rate;
202 prtd->channel_mode = runtime->channels;
Asish Bhattacharya44b64112011-09-08 18:56:19 +0530203 prtd->out_head = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 if (prtd->enabled)
205 return 0;
206
207 ret = q6asm_media_format_block_pcm(prtd->audio_client, runtime->rate,
208 runtime->channels);
209 if (ret < 0)
210 pr_debug("%s: CMD Format block failed\n", __func__);
211
212 atomic_set(&prtd->out_count, runtime->periods);
213 prtd->enabled = 1;
214 prtd->cmd_ack = 0;
215 return 0;
216}
217
218static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
219{
220 int ret = 0;
221 struct snd_pcm_runtime *runtime = substream->runtime;
222 struct msm_audio *prtd = runtime->private_data;
223 pr_debug("%s\n", __func__);
224 switch (cmd) {
225 case SNDRV_PCM_TRIGGER_START:
Asish Bhattacharyaa4a4baa2011-10-12 13:58:41 +0530226 prtd->pcm_irq_pos = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 case SNDRV_PCM_TRIGGER_RESUME:
228 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
229 pr_debug("SNDRV_PCM_TRIGGER_START\n");
230 q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
231 atomic_set(&prtd->start, 1);
232 break;
233 case SNDRV_PCM_TRIGGER_STOP:
234 pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
235 atomic_set(&prtd->start, 0);
236 if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
237 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238 break;
239 case SNDRV_PCM_TRIGGER_SUSPEND:
240 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
241 pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
242 q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
243 atomic_set(&prtd->start, 0);
244 break;
245 default:
246 ret = -EINVAL;
247 break;
248 }
249
250 return ret;
251}
252
253static int msm_pcm_open(struct snd_pcm_substream *substream)
254{
255 struct snd_pcm_runtime *runtime = substream->runtime;
256 struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
257 struct msm_audio *prtd;
Sriranjan Srikantam5285a212011-09-06 19:09:19 -0700258 struct asm_softpause_params softpause = {
259 .enable = SOFT_PAUSE_ENABLE,
260 .period = SOFT_PAUSE_PERIOD,
261 .step = SOFT_PAUSE_STEP,
262 .rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
263 };
264 struct asm_softvolume_params softvol = {
265 .period = SOFT_VOLUME_PERIOD,
266 .step = SOFT_VOLUME_STEP,
267 .rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
268 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 int ret = 0;
270
271 pr_debug("%s\n", __func__);
272 prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
273 if (prtd == NULL) {
274 pr_err("Failed to allocate memory for msm_audio\n");
275 return -ENOMEM;
276 }
277 runtime->hw = msm_pcm_hardware;
278 prtd->substream = substream;
279 prtd->audio_client = q6asm_audio_client_alloc(
280 (app_cb)event_handler, prtd);
281 if (!prtd->audio_client) {
282 pr_debug("%s: Could not allocate memory\n", __func__);
283 kfree(prtd);
284 return -ENOMEM;
285 }
286 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
287 ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM);
288 if (ret < 0) {
289 pr_err("%s: pcm out open failed\n", __func__);
290 q6asm_audio_client_free(prtd->audio_client);
291 kfree(prtd);
292 return -ENOMEM;
293 }
294 ret = q6asm_set_io_mode(prtd->audio_client, ASYNC_IO_MODE);
295 if (ret < 0) {
296 pr_err("%s: Set IO mode failed\n", __func__);
297 q6asm_audio_client_free(prtd->audio_client);
298 kfree(prtd);
299 return -ENOMEM;
300 }
301 }
302 /* Capture path */
303 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
304 return -EPERM;
305 pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
306 prtd->session_id = prtd->audio_client->session;
307 msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
308 prtd->session_id, substream->stream);
309
310 ret = snd_pcm_hw_constraint_list(runtime, 0,
311 SNDRV_PCM_HW_PARAM_RATE,
312 &constraints_sample_rates);
313 if (ret < 0)
314 pr_debug("snd_pcm_hw_constraint_list failed\n");
315 /* Ensure that buffer size is a multiple of period size */
316 ret = snd_pcm_hw_constraint_integer(runtime,
317 SNDRV_PCM_HW_PARAM_PERIODS);
318 if (ret < 0)
319 pr_debug("snd_pcm_hw_constraint_integer failed\n");
320
321 prtd->dsp_cnt = 0;
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530322 atomic_set(&prtd->pending_buffer, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 runtime->private_data = prtd;
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530324 lpa_audio.prtd = prtd;
325 lpa_set_volume(lpa_audio.volume);
Sriranjan Srikantam5285a212011-09-06 19:09:19 -0700326 ret = q6asm_set_softpause(lpa_audio.prtd->audio_client, &softpause);
327 if (ret < 0)
328 pr_err("%s: Send SoftPause Param failed ret=%d\n",
329 __func__, ret);
330 ret = q6asm_set_softvolume(lpa_audio.prtd->audio_client, &softvol);
331 if (ret < 0)
332 pr_err("%s: Send SoftVolume Param failed ret=%d\n",
333 __func__, ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334
335 return 0;
336}
337
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530338int lpa_set_volume(unsigned volume)
339{
340 int rc = 0;
341 if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
342 rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
343 if (rc < 0) {
344 pr_err("%s: Send Volume command failed"
345 " rc=%d\n", __func__, rc);
346 }
347 }
348 lpa_audio.volume = volume;
349 return rc;
350}
351
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
353{
354 struct snd_pcm_runtime *runtime = substream->runtime;
355 struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
356 struct msm_audio *prtd = runtime->private_data;
357 int dir = 0;
Tejas Shikhare6fd0e002012-01-31 21:17:29 -0800358 int rc = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
Tejas Shikhare6fd0e002012-01-31 21:17:29 -0800360 /*
361 If routing is still enabled, we need to issue EOS to
362 the DSP
363 To issue EOS to dsp, we need to be run state otherwise
364 EOS is not honored.
365 */
366 if (msm_routing_check_backend_enabled(soc_prtd->dai_link->be_id)) {
367 rc = q6asm_run(prtd->audio_client, 0, 0, 0);
368 atomic_set(&prtd->pending_buffer, 0);
369 prtd->cmd_ack = 0;
370 q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
371 pr_debug("%s\n", __func__);
372 rc = wait_event_timeout(the_locks.eos_wait,
373 prtd->cmd_ack, 5 * HZ);
374 if (rc < 0)
375 pr_err("EOS cmd timeout\n");
376 prtd->pcm_irq_pos = 0;
377 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378
379 dir = IN;
Asish Bhattacharya31bd80d2012-01-04 02:30:13 +0530380 atomic_set(&prtd->pending_buffer, 0);
Asish Bhattacharya0ec76182011-07-29 16:58:11 +0530381 lpa_audio.prtd = NULL;
Asish Bhattacharya49831e42011-10-14 12:36:55 +0530382 q6asm_cmd(prtd->audio_client, CMD_CLOSE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 q6asm_audio_client_buf_free_contiguous(dir,
384 prtd->audio_client);
385
386 pr_debug("%s\n", __func__);
387 msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
388 SNDRV_PCM_STREAM_PLAYBACK);
389 pr_debug("%s\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390 q6asm_audio_client_free(prtd->audio_client);
391 kfree(prtd);
392
393 return 0;
394}
395
396static int msm_pcm_close(struct snd_pcm_substream *substream)
397{
398 int ret = 0;
399
400 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
401 ret = msm_pcm_playback_close(substream);
402 return ret;
403}
404
405static int msm_pcm_prepare(struct snd_pcm_substream *substream)
406{
407 int ret = 0;
408
409 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
410 ret = msm_pcm_playback_prepare(substream);
411 return ret;
412}
413
414static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
415{
416
417 struct snd_pcm_runtime *runtime = substream->runtime;
418 struct msm_audio *prtd = runtime->private_data;
419
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420 pr_debug("%s: pcm_irq_pos = %d\n", __func__, prtd->pcm_irq_pos);
421 return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
422}
423
424static int msm_pcm_mmap(struct snd_pcm_substream *substream,
425 struct vm_area_struct *vma)
426{
Harmandeep Singh2f7c23c2011-11-04 11:57:35 -0700427 int result = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428 struct snd_pcm_runtime *runtime = substream->runtime;
429 struct msm_audio *prtd = runtime->private_data;
430
431 pr_debug("%s\n", __func__);
432 prtd->mmap_flag = 1;
Harmandeep Singh2f7c23c2011-11-04 11:57:35 -0700433
434 if (runtime->dma_addr && runtime->dma_bytes) {
435 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
436 result = remap_pfn_range(vma, vma->vm_start,
437 runtime->dma_addr >> PAGE_SHIFT,
438 runtime->dma_bytes,
439 vma->vm_page_prot);
440 } else {
441 pr_err("Physical address or size of buf is NULL");
442 return -EINVAL;
443 }
444 return result;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445}
446
447static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
448 struct snd_pcm_hw_params *params)
449{
450 struct snd_pcm_runtime *runtime = substream->runtime;
451 struct msm_audio *prtd = runtime->private_data;
452 struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
453 struct audio_buffer *buf;
454 int dir, ret;
455
456 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
457 dir = IN;
458 else
459 return -EPERM;
460 ret = q6asm_audio_client_buf_alloc_contiguous(dir,
461 prtd->audio_client,
462 runtime->hw.period_bytes_min,
463 runtime->hw.periods_max);
464 if (ret < 0) {
465 pr_err("Audio Start: Buffer Allocation failed \
466 rc = %d\n", ret);
467 return -ENOMEM;
468 }
469 buf = prtd->audio_client->port[dir].buf;
Shiv Maliyappanahalli0f6c1db2012-01-19 15:14:46 -0800470
471 if (buf == NULL || buf[0].data == NULL)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472 return -ENOMEM;
473
474 pr_debug("%s:buf = %p\n", __func__, buf);
475 dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
476 dma_buf->dev.dev = substream->pcm->card->dev;
477 dma_buf->private_data = NULL;
478 dma_buf->area = buf[0].data;
479 dma_buf->addr = buf[0].phys;
480 dma_buf->bytes = runtime->hw.buffer_bytes_max;
481 if (!dma_buf->area)
482 return -ENOMEM;
483
484 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
485 return 0;
486}
487
Asish Bhattacharya7706e8e2011-08-16 11:46:44 +0530488static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
489 unsigned int cmd, void *arg)
490{
491 int rc = 0;
492 struct snd_pcm_runtime *runtime = substream->runtime;
493 struct msm_audio *prtd = runtime->private_data;
Tejas Shikhare727416f2012-02-14 17:46:31 -0800494 uint64_t timestamp;
495 uint64_t temp;
Asish Bhattacharya7706e8e2011-08-16 11:46:44 +0530496
497 switch (cmd) {
Tejas Shikhare727416f2012-02-14 17:46:31 -0800498 case SNDRV_COMPRESS_TSTAMP: {
499 struct snd_compr_tstamp tstamp;
500 pr_debug("SNDRV_COMPRESS_TSTAMP\n");
501
502 memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
503 timestamp = q6asm_get_session_time(prtd->audio_client);
504 if (timestamp < 0) {
505 pr_err("%s: Get Session Time return value =%lld\n",
506 __func__, timestamp);
507 return -EAGAIN;
508 }
509 temp = (timestamp * 2 * runtime->channels);
510 temp = temp * (runtime->rate/1000);
511 temp = div_u64(temp, 1000);
512 tstamp.sampling_rate = runtime->rate;
513 tstamp.rendered = (size_t)(temp & 0xFFFFFFFF);
514 tstamp.decoded = (size_t)((temp >> 32) & 0xFFFFFFFF);
515 tstamp.timestamp = timestamp;
516 pr_debug("%s: bytes_consumed:lsb = %d, msb = %d,"
517 "timestamp = %lld,\n",
518 __func__, tstamp.rendered, tstamp.decoded,
519 tstamp.timestamp);
520 if (copy_to_user((void *) arg, &tstamp,
521 sizeof(struct snd_compr_tstamp)))
522 return -EFAULT;
523 return 0;
524 }
Asish Bhattacharya7706e8e2011-08-16 11:46:44 +0530525 case SNDRV_PCM_IOCTL1_RESET:
Tejas Shikhare6fd0e002012-01-31 21:17:29 -0800526 prtd->cmd_ack = 0;
Asish Bhattacharya7706e8e2011-08-16 11:46:44 +0530527 rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
528 if (rc < 0)
529 pr_err("%s: flush cmd failed rc=%d\n", __func__, rc);
Asish Bhattacharya44b64112011-09-08 18:56:19 +0530530 rc = wait_event_timeout(the_locks.eos_wait,
531 prtd->cmd_ack, 5 * HZ);
532 if (rc < 0)
533 pr_err("Flush cmd timeout\n");
534 prtd->pcm_irq_pos = 0;
Asish Bhattacharya7706e8e2011-08-16 11:46:44 +0530535 break;
536 default:
537 break;
538 }
539 return snd_pcm_lib_ioctl(substream, cmd, arg);
540}
541
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542static struct snd_pcm_ops msm_pcm_ops = {
543 .open = msm_pcm_open,
544 .hw_params = msm_pcm_hw_params,
545 .close = msm_pcm_close,
Asish Bhattacharya7706e8e2011-08-16 11:46:44 +0530546 .ioctl = msm_pcm_ioctl,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 .prepare = msm_pcm_prepare,
548 .trigger = msm_pcm_trigger,
549 .pointer = msm_pcm_pointer,
550 .mmap = msm_pcm_mmap,
551};
552
553static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
554{
555 struct snd_card *card = rtd->card->snd_card;
556 int ret = 0;
557
558 if (!card->dev->coherent_dma_mask)
559 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
560 return ret;
561}
562
563static struct snd_soc_platform_driver msm_soc_platform = {
564 .ops = &msm_pcm_ops,
565 .pcm_new = msm_asoc_pcm_new,
566};
567
568static __devinit int msm_pcm_probe(struct platform_device *pdev)
569{
570 dev_info(&pdev->dev, "%s: dev name %s\n",
571 __func__, dev_name(&pdev->dev));
572 return snd_soc_register_platform(&pdev->dev,
573 &msm_soc_platform);
574}
575
576static int msm_pcm_remove(struct platform_device *pdev)
577{
578 snd_soc_unregister_platform(&pdev->dev);
579 return 0;
580}
581
582static struct platform_driver msm_pcm_driver = {
583 .driver = {
584 .name = "msm-pcm-lpa",
585 .owner = THIS_MODULE,
586 },
587 .probe = msm_pcm_probe,
588 .remove = __devexit_p(msm_pcm_remove),
589};
590
591static int __init msm_soc_platform_init(void)
592{
Asish Bhattacharyaa4a4baa2011-10-12 13:58:41 +0530593 spin_lock_init(&the_locks.event_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594 init_waitqueue_head(&the_locks.enable_wait);
595 init_waitqueue_head(&the_locks.eos_wait);
596 init_waitqueue_head(&the_locks.write_wait);
597 init_waitqueue_head(&the_locks.read_wait);
598
599 return platform_driver_register(&msm_pcm_driver);
600}
601module_init(msm_soc_platform_init);
602
603static void __exit msm_soc_platform_exit(void)
604{
605 platform_driver_unregister(&msm_pcm_driver);
606}
607module_exit(msm_soc_platform_exit);
608
609MODULE_DESCRIPTION("PCM module platform driver");
610MODULE_LICENSE("GPL v2");