ASoC: msm7x27A: Add mmap support for playback

Change-Id: I5a7eaa519b8146d2697b3c65ea1a08a3c8feacb3
Signed-off-by: Asish Bhattacharya <asishb@codeaurora.org>
Signed-off-by: Phani Kumar Allada <pallad@codeaurora.org>
diff --git a/sound/soc/msm/msm7k-pcm.c b/sound/soc/msm/msm7k-pcm.c
index 1f36e3b..6ef924f 100644
--- a/sound/soc/msm/msm7k-pcm.c
+++ b/sound/soc/msm/msm7k-pcm.c
@@ -1,6 +1,6 @@
 /* linux/sound/soc/msm/msm7k-pcm.c
  *
- * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2009, 2012 Code Aurora Forum. All rights reserved.
  *
  * All source code in this file is licensed under the following license except
  * where indicated.
@@ -109,18 +109,20 @@
 }
 
 static struct snd_pcm_hardware msm_pcm_playback_hardware = {
-	.info =                 SNDRV_PCM_INFO_INTERLEAVED,
+	.info =                 SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED,
 	.formats =              USE_FORMATS,
 	.rates =                USE_RATE,
 	.rate_min =             USE_RATE_MIN,
 	.rate_max =             USE_RATE_MAX,
 	.channels_min =         USE_CHANNELS_MIN,
 	.channels_max =         USE_CHANNELS_MAX,
-	.buffer_bytes_max =     MAX_BUFFER_PLAYBACK_SIZE,
-	.period_bytes_min =     64,
-	.period_bytes_max =     MAX_PERIOD_SIZE,
-	.periods_min =          USE_PERIODS_MIN,
-	.periods_max =          USE_PERIODS_MAX,
+	.buffer_bytes_max =     4800 * 2,
+	.period_bytes_min =     4800,
+	.period_bytes_max =     4800,
+	.periods_min =          2,
+	.periods_max =          2,
 	.fifo_size =            0,
 };
 
@@ -151,10 +153,36 @@
 	.mask = 0,
 };
 
+static void msm_pcm_enqueue_data(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	unsigned int period_size;
+
+	pr_debug("prtd->out_tail =%d mmap_flag=%d\n",
+			prtd->out_tail, prtd->mmap_flag);
+	period_size = snd_pcm_lib_period_bytes(substream);
+	alsa_dsp_send_buffer(prtd, prtd->out_tail, period_size);
+	prtd->out_tail ^= 1;
+	++copy_count;
+	prtd->period++;
+	if (unlikely(prtd->period >= runtime->periods))
+		prtd->period = 0;
+
+}
+
 static void playback_event_handler(void *data)
 {
 	struct msm_audio *prtd = data;
 	snd_pcm_period_elapsed(prtd->playback_substream);
+	if (prtd->mmap_flag) {
+		if (prtd->dir == SNDRV_PCM_STREAM_CAPTURE)
+			return;
+		if (!prtd->stopped)
+			msm_pcm_enqueue_data(prtd->playback_substream);
+		else
+			prtd->out_needed++;
+	}
 }
 
 static void capture_event_handler(void *data)
@@ -177,6 +205,26 @@
 	prtd->out_sample_rate = runtime->rate;
 	prtd->out_channel_mode = runtime->channels;
 
+	if (prtd->enabled | !(prtd->mmap_flag))
+		return 0;
+
+	prtd->data = substream->dma_buffer.area;
+	prtd->phys = substream->dma_buffer.addr;
+	prtd->out[0].data = prtd->data + 0;
+	prtd->out[0].addr = prtd->phys + 0;
+	prtd->out[0].size = BUFSZ;
+	prtd->out[1].data = prtd->data + BUFSZ;
+	prtd->out[1].addr = prtd->phys + BUFSZ;
+	prtd->out[1].size = BUFSZ;
+
+	prtd->out[0].used = prtd->pcm_count;
+	prtd->out[1].used = prtd->pcm_count;
+
+	mutex_lock(&the_locks.lock);
+	alsa_audio_configure(prtd);
+	mutex_unlock(&the_locks.lock);
+
+
 	return 0;
 }
 
@@ -231,16 +279,55 @@
 
 static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	unsigned long flag = 0;
 	int ret = 0;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			|| !prtd->mmap_flag)
+			break;
+		if (!prtd->out_needed) {
+			prtd->stopped = 0;
+			break;
+		}
+		spin_lock_irqsave(&the_locks.write_dsp_lock, flag);
+		if (prtd->running == 1) {
+			if (prtd->stopped == 1) {
+				prtd->stopped = 0;
+				prtd->period = 0;
+				if (prtd->pcm_irq_pos == 0) {
+					prtd->out_tail = 0;
+					msm_pcm_enqueue_data(
+						prtd->playback_substream);
+					prtd->out_needed--;
+				} else {
+					prtd->out_tail = 1;
+					msm_pcm_enqueue_data(
+						prtd->playback_substream);
+					prtd->out_needed--;
+				}
+				if (prtd->out_needed) {
+					prtd->out_tail ^= 1;
+					msm_pcm_enqueue_data(
+						prtd->playback_substream);
+					prtd->out_needed--;
+				}
+			}
+		}
+		spin_unlock_irqrestore(&the_locks.write_dsp_lock, flag);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			|| !prtd->mmap_flag)
+			break;
+		prtd->stopped = 1;
 		break;
 	default:
 		ret = -EINVAL;
@@ -376,7 +463,6 @@
 	fbytes = frames_to_bytes(runtime, frames);
 	rc = alsa_send_buffer(prtd, buf, fbytes, NULL);
 	++copy_count;
-	prtd->pcm_buf_pos += fbytes;
 	if (copy_count == 1) {
 		mutex_lock(&the_locks.lock);
 		alsa_audio_configure(prtd);
@@ -469,10 +555,27 @@
 		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
 		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
 	}
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 	return 0;
 
 }
 
+int msm_pcm_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+
+	prtd->out_head = 0; /* point to First buffer on startup */
+	prtd->mmap_flag = 1;
+	runtime->dma_bytes = snd_pcm_lib_period_bytes(substream)*2;
+	dma_mmap_coherent(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+	return 0;
+}
+
 static struct snd_pcm_ops msm_pcm_ops = {
 	.open           = msm_pcm_open,
 	.copy		= msm_pcm_copy,
@@ -482,6 +585,7 @@
 	.prepare        = msm_pcm_prepare,
 	.trigger        = msm_pcm_trigger,
 	.pointer        = msm_pcm_pointer,
+	.mmap		= msm_pcm_mmap,
 };
 
 static int pcm_preallocate_dma_buffer(struct snd_pcm *pcm,