ASOC: msm8x60: support to device_mute is added.

Device mute control is added which can mute the
respective dev_id to volume zero.
msm_device_mute() in user space will use this
control.

CRs-Fixed: 295399
Signed-off-by: Laxminath Kasam <lkasam@codeaurora.org>
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index b9436bf..133a065 100644
--- a/include/sound/q6afe.h
+++ b/include/sound/q6afe.h
@@ -64,4 +64,5 @@
 int afe_port_start_nowait(u16 port_id, union afe_port_config *afe_config,
 	u32 rate);
 int afe_port_stop_nowait(int port_id);
+int afe_apply_gain(u16 port_id, u16 gain);
 #endif /* __Q6AFE_H__ */
diff --git a/sound/soc/msm/msm8x60.c b/sound/soc/msm/msm8x60.c
index 59597f9..7c34b22 100644
--- a/sound/soc/msm/msm8x60.c
+++ b/sound/soc/msm/msm8x60.c
@@ -911,7 +911,57 @@
 	}
 	return rc;
 }
+static int msm_device_mute_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = msm_snddev_devcount();
+	return 0;
+}
 
+static int msm_device_mute_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	return 0;
+}
+
+static int msm_device_mute_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int dev_id = ucontrol->value.integer.value[0];
+	int mute = ucontrol->value.integer.value[1];
+	struct msm_snddev_info *dev_info;
+	int rc = 0;
+	u16 gain = 0x2000;
+
+	dev_info = audio_dev_ctrl_find_dev(dev_id);
+	if (IS_ERR(dev_info)) {
+		rc = PTR_ERR(dev_info);
+		pr_err("%s: audio_dev_ctrl_find_dev failed. %ld\n",
+			__func__, PTR_ERR(dev_info));
+		return rc;
+	}
+	if (!(dev_info->capability & SNDDEV_CAP_TX)) {
+		rc = -EINVAL;
+		return rc;
+	}
+	if (mute)
+		gain = 0;
+
+	pr_debug("%s:dev_name = %s dev_id = %d, gain = %hX\n",
+			__func__, dev_info->name, dev_id, gain);
+	rc = afe_apply_gain(dev_info->copp_id, gain);
+	if (rc < 0) {
+		pr_err("%s : device %s not able to set device gain "
+				"control.", __func__, dev_info->name);
+		return rc;
+	}
+	pr_debug("Muting/Unmuting device id %d(%s)\n", dev_id, dev_info->name);
+
+	return rc;
+}
 static struct snd_kcontrol_new snd_dev_controls[AUDIO_DEV_CTL_MAX_DEV];
 
 static int snd_dev_ctl_index(int idx)
@@ -976,6 +1026,8 @@
 	MSM_EXT("Reset", msm_reset_info,
 			msm_reset_get, msm_reset_put, 0),
 	MSM_EXT("ANC", msm_anc_info, msm_anc_get, msm_anc_put, 0),
+	MSM_EXT("Device_Mute", msm_device_mute_info,
+			msm_device_mute_get, msm_device_mute_put, 0),
 };
 
 static struct snd_kcontrol_new snd_msm_secondary_controls[] = {
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 500ceb5..5c47159 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -72,6 +72,7 @@
 			case AFE_PORT_CMD_SET_PARAM:
 			case AFE_PSEUDOPORT_CMD_START:
 			case AFE_PSEUDOPORT_CMD_STOP:
+			case AFE_PORT_CMD_APPLY_GAIN:
 				atomic_set(&this_afe.state, 0);
 				wake_up(&this_afe.wait);
 				break;
@@ -488,6 +489,66 @@
 	return ret;
 }
 
+int afe_apply_gain(u16 port_id, u16 gain)
+{
+	struct afe_port_gain_command set_gain;
+	int ret = 0;
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: AFE is not opened\n", __func__);
+		ret = -EPERM;
+		goto fail_cmd;
+	}
+
+	if (afe_validate_port(port_id) < 0) {
+		pr_err("%s: Failed : Invalid Port id = %d\n", __func__,
+				port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	/* RX ports numbers are even .TX ports numbers are odd. */
+	if (port_id % 2 == 0) {
+		pr_err("%s: Failed : afe apply gain only for TX ports."
+			" port_id %d\n", __func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	pr_debug("%s: %d %hX\n", __func__, port_id, gain);
+
+	set_gain.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	set_gain.hdr.pkt_size = sizeof(set_gain);
+	set_gain.hdr.src_port = 0;
+	set_gain.hdr.dest_port = 0;
+	set_gain.hdr.token = 0;
+	set_gain.hdr.opcode = AFE_PORT_CMD_APPLY_GAIN;
+
+	set_gain.port_id		= port_id;
+	set_gain.gain	= gain;
+
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &set_gain);
+	if (ret < 0) {
+		pr_err("%s: AFE Gain set failed for port %d\n",
+					__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait,
+		(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (ret < 0) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return ret;
+}
 int afe_start_pseudo_port(u16 port_id)
 {
 	int ret = 0;