ASoC: msm8960: Add LPA volume control

LPA volume is controlled via dsp stream volume support
available. Add TLV mixer control to set LPA stream volume

Signed-off-by: Asish Bhattacharya <asishb@codeaurora.org>
diff --git a/sound/soc/msm/msm-pcm-lpa.c b/sound/soc/msm/msm-pcm-lpa.c
index 9248b04..f7aa7a6 100644
--- a/sound/soc/msm/msm-pcm-lpa.c
+++ b/sound/soc/msm/msm-pcm-lpa.c
@@ -34,9 +34,10 @@
 static struct audio_locks the_locks;
 
 struct snd_msm {
-	struct snd_card *card;
-	struct snd_pcm *pcm;
+	struct msm_audio *prtd;
+	unsigned volume;
 };
+static struct snd_msm lpa_audio;
 
 static struct snd_pcm_hardware msm_pcm_hardware = {
 	.info =                 (SNDRV_PCM_INFO_MMAP |
@@ -280,10 +281,26 @@
 	prtd->dsp_cnt = 0;
 	prtd->pending_buffer = 1;
 	runtime->private_data = prtd;
+	lpa_audio.prtd = prtd;
+	lpa_set_volume(lpa_audio.volume);
 
 	return 0;
 }
 
+int lpa_set_volume(unsigned volume)
+{
+	int rc = 0;
+	if (lpa_audio.prtd && lpa_audio.prtd->audio_client) {
+		rc = q6asm_set_volume(lpa_audio.prtd->audio_client, volume);
+		if (rc < 0) {
+			pr_err("%s: Send Volume command failed"
+					" rc=%d\n", __func__, rc);
+		}
+	}
+	lpa_audio.volume = volume;
+	return rc;
+}
+
 static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -301,6 +318,7 @@
 		prtd->cmd_ack, 5 * HZ);
 	if (ret < 0)
 		pr_err("%s: CMD_EOS failed\n", __func__);
+	lpa_audio.prtd = NULL;
 	q6asm_audio_client_buf_free_contiguous(dir,
 				prtd->audio_client);
 
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 39529c4..097bd1c 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -60,6 +60,13 @@
 static const DECLARE_TLV_DB_SCALE(fm_rx_vol_gain, 0,
 			INT_FM_RX_VOL_MAX_STEPS, 0);
 
+#define INT_LPA_RX_VOL_MAX_STEPS 0x100
+#define INT_LPA_RX_VOL_GAIN 0x3FFF
+
+static int msm_route_lpa_vol_control;
+static const DECLARE_TLV_DB_SCALE(lpa_rx_vol_gain, 0,
+			INT_LPA_RX_VOL_MAX_STEPS, 0);
+
 /* Tx mixer session is stored based on BE DAI ID
  * Need to map to actual AFE port ID since AFE port
  * ID can get really large.
@@ -405,6 +412,23 @@
 	return 0;
 }
 
+static int msm_routing_get_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_route_lpa_vol_control;
+	return 0;
+}
+
+static int msm_routing_set_lpa_vol_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	if (!lpa_set_volume(ucontrol->value.integer.value[0]))
+		msm_route_lpa_vol_control =
+			ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
 static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
 	SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_PRI_I2S_RX ,
 	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -542,6 +566,12 @@
 	msm_routing_set_fm_vol_mixer, fm_rx_vol_gain),
 };
 
+static const struct snd_kcontrol_new lpa_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("LPA RX Volume", SND_SOC_NOPM, 0,
+	INT_LPA_RX_VOL_GAIN, 0, msm_routing_get_lpa_vol_mixer,
+	msm_routing_set_lpa_vol_mixer, lpa_rx_vol_gain),
+};
+
 static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
 	/* Frontend AIF */
 	/* Widget name equals to Front-End DAI name<Need confirmation>,
@@ -707,6 +737,10 @@
 	snd_soc_add_platform_controls(platform,
 				int_fm_vol_mixer_controls,
 			ARRAY_SIZE(int_fm_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform,
+				lpa_vol_mixer_controls,
+			ARRAY_SIZE(lpa_vol_mixer_controls));
 	return 0;
 }
 
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index c45f95b..7dacebc 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -78,4 +78,5 @@
 	int stream_type);
 void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
 
+int lpa_set_volume(unsigned volume);
 #endif /*_MSM_PCM_H*/