ASoC: msm: Add Secondary AUXPCM CPU driver

Enable support for AUX PCM over secondary audio interface by
adding CPU dai which configures QDSP6 AFE on secondary AUX
PCM ports 12 and 13.
Configure the GPIO for Secondary AUXPCM interface.

Change-Id: I8db7e8c967c667923a11992e9fab41ab3b4cc292
Signed-off-by: Shiv Maliyappanahalli <smaliyap@codeaurora.org>
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
index 96795a3..ec8d73e 100644
--- a/include/sound/apr_audio.h
+++ b/include/sound/apr_audio.h
@@ -59,6 +59,9 @@
 #define INT_FM_TX 0x3005		/* index = 29 */
 #define RT_PROXY_PORT_001_RX	0x2000    /* index = 30 */
 #define RT_PROXY_PORT_001_TX	0x2001    /* index = 31 */
+#define SECONDARY_PCM_RX 12			/* index = 32 */
+#define SECONDARY_PCM_TX 13			/* index = 33 */
+
 
 #define AFE_PORT_INVALID 0xFFFF
 #define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index 8cdcc18..f93af1f 100644
--- a/include/sound/q6afe.h
+++ b/include/sound/q6afe.h
@@ -68,6 +68,8 @@
 	IDX_INT_FM_TX = 29,
 	IDX_RT_PROXY_PORT_001_RX = 30,
 	IDX_RT_PROXY_PORT_001_TX = 31,
+	IDX_SECONDARY_PCM_RX = 32,
+	IDX_SECONDARY_PCM_TX = 33,
 	AFE_MAX_PORTS
 };
 
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index 7060677..4a6f28a 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -53,6 +53,11 @@
 #define GPIO_AUX_PCM_SYNC 65
 #define GPIO_AUX_PCM_CLK 66
 
+#define GPIO_SEC_AUX_PCM_DOUT 28
+#define GPIO_SEC_AUX_PCM_DIN 27
+#define GPIO_SEC_AUX_PCM_SYNC 26
+#define GPIO_SEC_AUX_PCM_CLK 25
+
 #define TABLA_EXT_CLK_RATE 12288000
 
 #define TABLA_MBHC_DEF_BUTTONS 8
@@ -145,6 +150,9 @@
 #define LPA_IF_REG_BASE (LPA_IF_BASE + 0x00000000)
 #define LPASS_SIF_MUX_ADDR  (SIF_MUX_REG_BASE + 0x00004000)
 #define LPAIF_SPARE_ADDR (LPA_IF_REG_BASE + 0x00000070)
+#define SEC_PCM_PORT_SLC_ADDR 0x00802074
+/* bits 2:0 should be updated with 100 to select SDC2 */
+#define SEC_PCM_PORT_SLC_VALUE 0x4
 /* SIF & SPARE MUX Values */
 #define MSM_SIF_FUNC_PCM              0
 #define MSM_SIF_FUNC_I2S_MIC        1
@@ -169,6 +177,7 @@
 static int msm9615_i2s_rx_ch = 1;
 static int msm9615_i2s_tx_ch = 1;
 static int msm9615_i2s_spk_control;
+
 /* SIF mux bit mask & shift */
 #define LPASS_SIF_MUX_CTL_PRI_MUX_SEL_BMSK                   0x30000
 #define LPASS_SIF_MUX_CTL_PRI_MUX_SEL_SHFT                      0x10
@@ -241,6 +250,10 @@
 
 static u32 top_spk_pamp_gpio  = PM8018_GPIO_PM_TO_SYS(3);
 static u32 bottom_spk_pamp_gpio = PM8018_GPIO_PM_TO_SYS(5);
+
+void *sif_virt_addr;
+void *secpcm_portslc_virt_addr;
+
 static int mdm9615_spk_control;
 static int mdm9615_ext_bottom_spk_pamp;
 static int mdm9615_ext_top_spk_pamp;
@@ -249,6 +262,7 @@
 
 static int mdm9615_btsco_rate = BTSCO_RATE_8KHZ;
 static int mdm9615_btsco_ch = 1;
+static atomic_t msm9615_sec_auxpcm_ref;
 
 static struct clk *codec_clk;
 static int clk_users;
@@ -1647,6 +1661,63 @@
 	return 0;
 }
 
+static int mdm9615_sec_aux_pcm_get_gpios(void)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	ret = gpio_request(GPIO_SEC_AUX_PCM_DOUT, "SEC_AUX PCM DOUT");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): SEC_AUX PCM DOUT",
+		       __func__, GPIO_SEC_AUX_PCM_DOUT);
+		goto fail_dout;
+	}
+
+	ret = gpio_request(GPIO_SEC_AUX_PCM_DIN, "SEC_AUX PCM DIN");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): SEC_AUX PCM DIN",
+		       __func__, GPIO_SEC_AUX_PCM_DIN);
+		goto fail_din;
+	}
+
+	ret = gpio_request(GPIO_SEC_AUX_PCM_SYNC, "SEC_AUX PCM SYNC");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): SEC_AUX PCM SYNC",
+		       __func__, GPIO_SEC_AUX_PCM_SYNC);
+		goto fail_sync;
+	}
+
+	ret = gpio_request(GPIO_SEC_AUX_PCM_CLK, "SEC_AUX PCM CLK");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): SEC_AUX PCM CLK",
+		       __func__, GPIO_SEC_AUX_PCM_CLK);
+		goto fail_clk;
+	}
+
+	return 0;
+
+fail_clk:
+	gpio_free(GPIO_SEC_AUX_PCM_SYNC);
+fail_sync:
+	gpio_free(GPIO_SEC_AUX_PCM_DIN);
+fail_din:
+	gpio_free(GPIO_SEC_AUX_PCM_DOUT);
+fail_dout:
+
+	return ret;
+}
+
+static int mdm9615_sec_aux_pcm_free_gpios(void)
+{
+	gpio_free(GPIO_SEC_AUX_PCM_DIN);
+	gpio_free(GPIO_SEC_AUX_PCM_DOUT);
+	gpio_free(GPIO_SEC_AUX_PCM_SYNC);
+	gpio_free(GPIO_SEC_AUX_PCM_CLK);
+
+	return 0;
+}
+
 static int mdm9615_startup(struct snd_pcm_substream *substream)
 {
 	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
@@ -1654,6 +1725,26 @@
 	return 0;
 }
 
+void msm9615_config_sif_mux(u8 value)
+{
+	u32 sif_shadow  = 0x00000;
+
+	sif_shadow = (sif_shadow & LPASS_SIF_MUX_CTL_SEC_MUX_SEL_BMSK) |
+		     (value << LPASS_SIF_MUX_CTL_SEC_MUX_SEL_SHFT);
+	iowrite32(sif_shadow, sif_virt_addr);
+	/* Dont read SIF register. Device crashes. */
+	pr_debug("%s() SIF Reg = 0x%x\n", __func__, sif_shadow);
+}
+
+void msm9615_config_port_select(void)
+{
+	pr_debug("%s() port select defualt = 0x%x\n",
+		 __func__, ioread32(secpcm_portslc_virt_addr));
+	iowrite32(SEC_PCM_PORT_SLC_VALUE, secpcm_portslc_virt_addr);
+	pr_debug("%s() port select after updating = 0x%x\n",
+		 __func__, ioread32(secpcm_portslc_virt_addr));
+}
+
 static int mdm9615_auxpcm_startup(struct snd_pcm_substream *substream)
 {
 	int ret = 0;
@@ -1674,6 +1765,31 @@
 	mdm9615_aux_pcm_free_gpios();
 }
 
+static int mdm9615_sec_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	pr_debug("%s(): substream = %s\n", __func__, substream->name);
+	if (atomic_inc_return(&msm9615_sec_auxpcm_ref) == 1) {
+		ret = mdm9615_sec_aux_pcm_get_gpios();
+		if (ret < 0) {
+			pr_err("%s: SEC Aux PCM GPIO request failed\n",
+			       __func__);
+			return -EINVAL;
+		}
+		msm9615_config_sif_mux(MSM_SIF_FUNC_PCM);
+		msm9615_config_port_select();
+	}
+	return 0;
+}
+
+static void mdm9615_sec_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+	pr_debug("%s(): substream = %s\n", __func__, substream->name);
+	if (atomic_dec_return(&msm9615_sec_auxpcm_ref) == 0)
+		mdm9615_sec_aux_pcm_free_gpios();
+}
+
 static void mdm9615_shutdown(struct snd_pcm_substream *substream)
 {
 	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
@@ -1691,6 +1807,12 @@
 	.shutdown = mdm9615_auxpcm_shutdown,
 };
 
+static struct snd_soc_ops mdm9615_sec_auxpcm_be_ops = {
+	.startup = mdm9615_sec_auxpcm_startup,
+	.shutdown = mdm9615_sec_auxpcm_shutdown,
+};
+
+
 /* Digital audio interface glue - connects codec <---> CPU */
 static struct snd_soc_dai_link mdm9615_dai_common[] = {
 	/* FrontEnd DAI Links */
@@ -1856,6 +1978,31 @@
 		.be_hw_params_fixup = mdm9615_auxpcm_be_params_fixup,
 	},
 
+	/* SECONDARY AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_SEC_AUXPCM_RX,
+		.stream_name = "SEC AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6.12",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+		.be_hw_params_fixup = mdm9615_auxpcm_be_params_fixup,
+		.ops = &mdm9615_sec_auxpcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SEC_AUXPCM_TX,
+		.stream_name = "SEC AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6.13",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		.be_hw_params_fixup = mdm9615_auxpcm_be_params_fixup,
+		.ops = &mdm9615_sec_auxpcm_be_ops,
+	},
 };
 
 static struct snd_soc_dai_link mdm9615_dai_i2s_tabla[] = {
@@ -2045,6 +2192,9 @@
 
 	msm9x15_i2s_ctl.sif_virt_addr = ioremap(LPASS_SIF_MUX_ADDR, 4);
 	msm9x15_i2s_ctl.spare_virt_addr = ioremap(LPAIF_SPARE_ADDR, 4);
+	atomic_set(&msm9615_sec_auxpcm_ref, 0);
+	sif_virt_addr = ioremap(LPASS_SIF_MUX_ADDR, 4);
+	secpcm_portslc_virt_addr = ioremap(SEC_PCM_PORT_SLC_ADDR, 4);
 
 	return ret;
 
@@ -2062,6 +2212,8 @@
 	kfree(mbhc_cfg.calibration);
 	iounmap(msm9x15_i2s_ctl.sif_virt_addr);
 	iounmap(msm9x15_i2s_ctl.spare_virt_addr);
+	iounmap(sif_virt_addr);
+	iounmap(secpcm_portslc_virt_addr);
 
 }
 module_exit(mdm9615_audio_exit);
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 965559f..f3e5186 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -48,9 +48,11 @@
 };
 
 static struct clk *pcm_clk;
+static struct clk *sec_pcm_clk;
 static DEFINE_MUTEX(aux_pcm_mutex);
 static int aux_pcm_count;
 static struct msm_dai_auxpcm_pdata *auxpcm_plat_data;
+static struct msm_dai_auxpcm_pdata *sec_auxpcm_plat_data;
 
 static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
@@ -558,6 +560,48 @@
 	return 0;
 }
 
+static int msm_dai_q6_sec_auxpcm_hw_params(
+				struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+			(struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+
+	pr_debug("%s\n", __func__);
+	if (params_channels(params) != 1) {
+		dev_err(dai->dev, "SEC AUX PCM supports only mono stream\n");
+		return -EINVAL;
+	}
+	dai_data->channels = params_channels(params);
+
+	dai_data->rate = params_rate(params);
+	switch (dai_data->rate) {
+	case 8000:
+		dai_data->port_config.pcm.mode = auxpcm_pdata->mode_8k.mode;
+		dai_data->port_config.pcm.sync = auxpcm_pdata->mode_8k.sync;
+		dai_data->port_config.pcm.frame = auxpcm_pdata->mode_8k.frame;
+		dai_data->port_config.pcm.quant = auxpcm_pdata->mode_8k.quant;
+		dai_data->port_config.pcm.slot = auxpcm_pdata->mode_8k.slot;
+		dai_data->port_config.pcm.data = auxpcm_pdata->mode_8k.data;
+		break;
+	case 16000:
+		dai_data->port_config.pcm.mode = auxpcm_pdata->mode_16k.mode;
+		dai_data->port_config.pcm.sync = auxpcm_pdata->mode_16k.sync;
+		dai_data->port_config.pcm.frame = auxpcm_pdata->mode_16k.frame;
+		dai_data->port_config.pcm.quant = auxpcm_pdata->mode_16k.quant;
+		dai_data->port_config.pcm.slot = auxpcm_pdata->mode_16k.slot;
+		dai_data->port_config.pcm.data = auxpcm_pdata->mode_16k.data;
+		break;
+	default:
+		dev_err(dai->dev, "AUX PCM supports only 8kHz and 16kHz sampling rate\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
 {
@@ -683,6 +727,52 @@
 	mutex_unlock(&aux_pcm_mutex);
 }
 
+static void msm_dai_q6_sec_auxpcm_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	int rc = 0;
+
+	pr_debug("%s\n", __func__);
+	mutex_lock(&aux_pcm_mutex);
+
+	if (aux_pcm_count == 0) {
+		dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. Just"
+				" return\n", __func__, dai->id);
+		mutex_unlock(&aux_pcm_mutex);
+		return;
+	}
+
+	aux_pcm_count--;
+
+	if (aux_pcm_count > 0) {
+		dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
+			__func__, dai->id, aux_pcm_count);
+		mutex_unlock(&aux_pcm_mutex);
+		return;
+	} else if (aux_pcm_count < 0) {
+		dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+			" aux_pcm_count = %d < 0\n",
+			__func__, dai->id, aux_pcm_count);
+		aux_pcm_count = 0;
+		mutex_unlock(&aux_pcm_mutex);
+		return;
+	}
+
+	pr_debug("%s: dai->id = %d aux_pcm_count = %d\n", __func__,
+			dai->id, aux_pcm_count);
+
+	clk_disable_unprepare(sec_pcm_clk);
+	rc = afe_close(SECONDARY_PCM_RX); /* can block */
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close PCM_RX  AFE port\n");
+
+	rc = afe_close(SECONDARY_PCM_TX);
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+
+	mutex_unlock(&aux_pcm_mutex);
+}
+
 static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
 				struct snd_soc_dai *dai)
 {
@@ -769,7 +859,7 @@
 	if (dai_data->rate == 8000) {
 		pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
 	} else if (dai_data->rate == 16000) {
-		pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
+		pcm_clk_rate = auxpcm_pdata->mode_16k.pcm_clk_rate;
 	} else {
 		dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__,
 			  dai_data->rate);
@@ -790,6 +880,88 @@
 	return rc;
 }
 
+static int msm_dai_q6_sec_auxpcm_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+	struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+			(struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+	unsigned long pcm_clk_rate;
+
+	pr_info("%s\n", __func__);
+
+	mutex_lock(&aux_pcm_mutex);
+
+	if (aux_pcm_count == 2) {
+		dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 2. Just"
+			" return.\n", __func__, dai->id);
+		mutex_unlock(&aux_pcm_mutex);
+		return 0;
+	} else if (aux_pcm_count > 2) {
+		dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+			" aux_pcm_count = %d > 2\n",
+			__func__, dai->id, aux_pcm_count);
+		mutex_unlock(&aux_pcm_mutex);
+		return 0;
+	}
+
+	aux_pcm_count++;
+	if (aux_pcm_count == 2)  {
+		dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d after "
+			" increment\n", __func__, dai->id, aux_pcm_count);
+		mutex_unlock(&aux_pcm_mutex);
+		return 0;
+	}
+
+	pr_debug("%s:dai->id:%d  aux_pcm_count = %d. opening afe\n",
+			__func__, dai->id, aux_pcm_count);
+
+	rc = afe_q6_interface_prepare();
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to open AFE APR\n");
+
+	/*
+	 * For AUX PCM Interface the below sequence of clk
+	 * settings and afe_open is a strict requirement.
+	 *
+	 * Also using afe_open instead of afe_port_start_nowait
+	 * to make sure the port is open before deasserting the
+	 * clock line. This is required because pcm register is
+	 * not written before clock deassert. Hence the hw does
+	 * not get updated with new setting if the below clock
+	 * assert/deasset and afe_open sequence is not followed.
+	 */
+
+	clk_reset(sec_pcm_clk, CLK_RESET_ASSERT);
+
+	afe_open(SECONDARY_PCM_RX, &dai_data->port_config, dai_data->rate);
+
+	afe_open(SECONDARY_PCM_TX, &dai_data->port_config, dai_data->rate);
+	if (dai_data->rate == 8000) {
+		pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
+	} else if (dai_data->rate == 16000) {
+		pcm_clk_rate = auxpcm_pdata->mode_16k.pcm_clk_rate;
+	} else {
+		dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__,
+			  dai_data->rate);
+		return -EINVAL;
+	}
+
+	rc = clk_set_rate(sec_pcm_clk, pcm_clk_rate);
+	if (rc < 0) {
+		pr_err("%s: clk_set_rate failed\n", __func__);
+		return rc;
+	}
+
+	clk_prepare_enable(sec_pcm_clk);
+	clk_reset(sec_pcm_clk, CLK_RESET_DEASSERT);
+
+	mutex_unlock(&aux_pcm_mutex);
+
+	return rc;
+}
+
 static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
 {
@@ -941,6 +1113,55 @@
 	return rc;
 }
 
+static int msm_dai_q6_dai_sec_auxpcm_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data;
+	int rc = 0;
+	struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+			(struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+
+	pr_info("%s\n", __func__);
+
+	mutex_lock(&aux_pcm_mutex);
+
+	if (!sec_auxpcm_plat_data)
+		sec_auxpcm_plat_data = auxpcm_pdata;
+	else if (sec_auxpcm_plat_data != auxpcm_pdata) {
+		dev_err(dai->dev, "AUX PCM RX and TX devices does not have"
+				" same platform data sec_auxpcm_plat_data\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * The clk name for AUX PCM operation is passed as platform
+	 * data to the cpu driver, since cpu drive is unaware of any
+	 * boarc specific configuration.
+	 */
+	if (!sec_pcm_clk) {
+
+		sec_pcm_clk = clk_get(dai->dev, auxpcm_pdata->clk);
+		if (IS_ERR(sec_pcm_clk)) {
+			pr_err("%s: could not get sec_pcm_clk\n", __func__);
+			sec_pcm_clk = NULL;
+			return -ENODEV;
+		}
+	}
+
+	mutex_unlock(&aux_pcm_mutex);
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
+
+	if (!dai_data) {
+		dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+		dai->id);
+		rc = -ENOMEM;
+	} else
+		dev_set_drvdata(dai->dev, dai_data);
+
+	pr_debug("%s : probe done for dai->id %d\n", __func__, dai->id);
+	return rc;
+}
+
 static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
 {
 	struct msm_dai_q6_dai_data *dai_data;
@@ -990,6 +1211,56 @@
 	return 0;
 }
 
+static int msm_dai_q6_dai_sec_auxpcm_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data;
+	int rc;
+
+	pr_debug("%s\n", __func__);
+	dai_data = dev_get_drvdata(dai->dev);
+
+	mutex_lock(&aux_pcm_mutex);
+
+	if (aux_pcm_count == 0) {
+		dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count is 0. clean"
+				" up and return\n", __func__, dai->id);
+		goto done;
+	}
+
+	aux_pcm_count--;
+
+	if (aux_pcm_count > 0) {
+		dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d\n",
+			__func__, dai->id, aux_pcm_count);
+		goto done;
+	} else if (aux_pcm_count < 0) {
+		dev_err(dai->dev, "%s(): ERROR: dai->id %d"
+			" aux_pcm_count = %d < 0\n",
+			__func__, dai->id, aux_pcm_count);
+		goto done;
+	}
+
+	dev_dbg(dai->dev, "%s(): dai->id %d aux_pcm_count = %d."
+			"closing afe\n",
+		__func__, dai->id, aux_pcm_count);
+
+	rc = afe_close(SECONDARY_PCM_RX); /* can block */
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close AUX PCM RX AFE port\n");
+
+	rc = afe_close(SECONDARY_PCM_TX);
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close AUX PCM TX AFE port\n");
+
+done:
+	kfree(dai_data);
+	snd_soc_unregister_dai(dai->dev);
+
+	mutex_unlock(&aux_pcm_mutex);
+
+	return 0;
+}
+
 static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai)
 {
 	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
@@ -1219,6 +1490,13 @@
 	.shutdown	= msm_dai_q6_auxpcm_shutdown,
 };
 
+static struct snd_soc_dai_ops msm_dai_q6_sec_auxpcm_ops = {
+	.prepare	= msm_dai_q6_sec_auxpcm_prepare,
+	.trigger	= msm_dai_q6_auxpcm_trigger,
+	.hw_params	= msm_dai_q6_sec_auxpcm_hw_params,
+	.shutdown	= msm_dai_q6_sec_auxpcm_shutdown,
+};
+
 static struct snd_soc_dai_driver msm_dai_q6_i2s_rx_dai = {
 	.playback = {
 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
@@ -1425,6 +1703,34 @@
 	.remove = msm_dai_q6_dai_auxpcm_remove,
 };
 
+static struct snd_soc_dai_driver msm_dai_q6_sec_aux_pcm_rx_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,
+		.rate_max = 16000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_sec_auxpcm_ops,
+	.probe = msm_dai_q6_dai_sec_auxpcm_probe,
+	.remove = msm_dai_q6_dai_sec_auxpcm_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_sec_aux_pcm_tx_dai = {
+	.capture = {
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,
+		.rate_max = 16000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_sec_auxpcm_ops,
+	.probe = msm_dai_q6_dai_sec_auxpcm_probe,
+	.remove = msm_dai_q6_dai_sec_auxpcm_remove,
+};
+
 /* Channel min and max are initialized base on platform data */
 static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai = {
 	.playback = {
@@ -1545,6 +1851,14 @@
 				&msm_dai_q6_aux_pcm_tx_dai);
 		break;
 
+	case SECONDARY_PCM_RX:
+		rc = snd_soc_register_dai(&pdev->dev,
+				&msm_dai_q6_sec_aux_pcm_rx_dai);
+		break;
+	case SECONDARY_PCM_TX:
+		rc = snd_soc_register_dai(&pdev->dev,
+				&msm_dai_q6_sec_aux_pcm_tx_dai);
+		break;
 	case SLIMBUS_0_RX:
 	case SLIMBUS_4_RX:
 		rc = snd_soc_register_dai(&pdev->dev,
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 217158c..e3a1317 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -176,6 +176,8 @@
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
 	{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
+	{ SECONDARY_PCM_RX, 0, 0, 0, 0, 0},
+	{ SECONDARY_PCM_TX, 0, 0, 0, 0, 0},
 };
 
 
@@ -1168,6 +1170,15 @@
 	msm_routing_put_audio_mixer),
 };
 
+static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
 static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
 	SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
 		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
@@ -1181,6 +1192,9 @@
 	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
 		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
 		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
 	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
 		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
 		msm_routing_put_audio_mixer),
@@ -1303,6 +1317,18 @@
 	msm_routing_put_voice_mixer),
 };
 
+static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
 static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
 	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_HDMI_RX,
 	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -1352,6 +1378,9 @@
 	SOC_SINGLE_EXT("AUX_PCM_TX_Voice", MSM_BACKEND_DAI_AUXPCM_TX,
 	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new tx_volte_mixer_controls[] = {
@@ -1370,6 +1399,9 @@
 	SOC_SINGLE_EXT("AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_AUXPCM_TX,
 	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
@@ -1391,6 +1423,9 @@
 	SOC_SINGLE_EXT("AUX_PCM_TX_Voip", MSM_BACKEND_DAI_AUXPCM_TX,
 	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
 };
 
 static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
@@ -1824,6 +1859,10 @@
 
 	SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_AUX_PCM_RX", "SEC AUX PCM Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_AUX_PCM_TX", "SEC AUX PCM Capture",
+			    0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0),
@@ -1856,6 +1895,8 @@
 	mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
 	SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
 	auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)),
 	/* incall */
 	SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
 			incall_music_delivery_mixer_controls,
@@ -1887,6 +1928,10 @@
 				SND_SOC_NOPM, 0, 0,
 				aux_pcm_rx_voice_mixer_controls,
 				ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX_Voice Mixer",
+			   SND_SOC_NOPM, 0, 0,
+			   sec_aux_pcm_rx_voice_mixer_controls,
+			   ARRAY_SIZE(sec_aux_pcm_rx_voice_mixer_controls)),
 	SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer",
 				SND_SOC_NOPM, 0, 0,
 				hdmi_rx_voice_mixer_controls,
@@ -1993,6 +2038,7 @@
 	{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
 	{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
 	{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
 
 	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
 	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -2026,6 +2072,10 @@
 	{"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
 	{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
 
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"},
+
 	{"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
@@ -2056,6 +2106,11 @@
 	{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
 	{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
 
+	{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
+
 	{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
 	{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
@@ -2068,12 +2123,14 @@
 	{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
 	{"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
 	{"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
+	{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
 	{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
 	{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
 	{"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
 	{"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
 	{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
 	{"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"},
+	{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
 	{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
 	{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
 	{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
@@ -2081,6 +2138,7 @@
 	{"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
 	{"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
 	{"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
+	{"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"},
 
 	{"VOIP_UL", NULL, "Voip_Tx Mixer"},
 	{"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index e1a0e6c..847b730 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -26,6 +26,8 @@
 #define LPASS_BE_AFE_PCM_TX "(Backend) RT_PROXY_DAI_002_TX"
 #define LPASS_BE_AUXPCM_RX "(Backend) AUX_PCM_RX"
 #define LPASS_BE_AUXPCM_TX "(Backend) AUX_PCM_TX"
+#define LPASS_BE_SEC_AUXPCM_RX "(Backend) SEC_AUX_PCM_RX"
+#define LPASS_BE_SEC_AUXPCM_TX "(Backend) SEC_AUX_PCM_TX"
 #define LPASS_BE_VOICE_PLAYBACK_TX "(Backend) VOICE_PLAYBACK_TX"
 #define LPASS_BE_INCALL_RECORD_RX "(Backend) INCALL_RECORD_TX"
 #define LPASS_BE_INCALL_RECORD_TX "(Backend) INCALL_RECORD_RX"
@@ -95,6 +97,8 @@
 	MSM_BACKEND_DAI_EXTPROC_RX,
 	MSM_BACKEND_DAI_EXTPROC_TX,
 	MSM_BACKEND_DAI_EXTPROC_EC_TX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX,
 	MSM_BACKEND_DAI_MAX,
 };
 
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index ce81cca..7b16adb 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -142,6 +142,7 @@
 	switch (port_id) {
 	case PRIMARY_I2S_RX:
 	case PCM_RX:
+	case SECONDARY_PCM_RX:
 	case SECONDARY_I2S_RX:
 	case MI2S_RX:
 	case HDMI_RX:
@@ -160,6 +161,7 @@
 
 	case PRIMARY_I2S_TX:
 	case PCM_TX:
+	case SECONDARY_PCM_TX:
 	case SECONDARY_I2S_TX:
 	case MI2S_TX:
 	case DIGI_MIC_TX:
@@ -193,6 +195,8 @@
 	case PRIMARY_I2S_TX:
 	case PCM_RX:
 	case PCM_TX:
+	case SECONDARY_PCM_RX:
+	case SECONDARY_PCM_TX:
 	case SECONDARY_I2S_RX:
 	case SECONDARY_I2S_TX:
 	case MI2S_RX:
@@ -261,6 +265,8 @@
 	case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
 	case PCM_RX: return IDX_PCM_RX;
 	case PCM_TX: return IDX_PCM_TX;
+	case SECONDARY_PCM_RX: return IDX_SECONDARY_PCM_RX;
+	case SECONDARY_PCM_TX: return IDX_SECONDARY_PCM_TX;
 	case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
 	case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
 	case MI2S_RX: return IDX_MI2S_RX;
@@ -327,6 +333,8 @@
 		break;
 	case PCM_RX:
 	case PCM_TX:
+	case SECONDARY_PCM_RX:
+	case SECONDARY_PCM_TX:
 	default:
 		ret_size = SIZEOF_CFG_CMD(afe_port_pcm_cfg);
 		break;