ASoC: msm8960: Add support for AUX PCM Interface

Auxiliary PCM Interface is external PCM interface on msm that
can be used instead of internal codec as a way to connect an
external codec to the msm.

Enable support for AUX PCM. Current setting on PCM interface is,
2.048 MHz bit clock, mono stream, 8Khz sample rate, with MSM in
master mode. AUX PCM uses GPIO's to interface with external
codec device.

Adds platform data for cpu dai since cpu dai needs to know
which clock on MSM to use, clock rate to use and other
configuration parameters.

Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index c9d5270..217e1a5 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -261,6 +261,21 @@
 	.pull = GPIOMUX_PULL_NONE,
 };
 
+static struct gpiomux_setting audio_auxpcm[] = {
+	/* Suspended state */
+	{
+		.func = GPIOMUX_FUNC_GPIO,
+		.drv = GPIOMUX_DRV_2MA,
+		.pull = GPIOMUX_PULL_NONE,
+	},
+	/* Active state */
+	{
+		.func = GPIOMUX_FUNC_1,
+		.drv = GPIOMUX_DRV_2MA,
+		.pull = GPIOMUX_PULL_NONE,
+	},
+};
+
 #ifdef CONFIG_KS8851
 static struct gpiomux_setting gpio_eth_config = {
 	.pull = GPIOMUX_PULL_NONE,
@@ -412,6 +427,38 @@
 		},
 	},
 };
+
+static struct msm_gpiomux_config msm8960_audio_auxpcm_configs[] __initdata = {
+	{
+		.gpio = 63,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+			[GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+		},
+	},
+	{
+		.gpio = 64,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+			[GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+		},
+	},
+	{
+		.gpio = 65,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+			[GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+		},
+	},
+	{
+		.gpio = 66,
+		.settings = {
+			[GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+			[GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+		},
+	},
+};
+
 static struct gpiomux_setting wcnss_5wire_suspend_cfg = {
 	.func = GPIOMUX_FUNC_GPIO,
 	.drv  = GPIOMUX_DRV_2MA,
@@ -2205,6 +2252,9 @@
 	msm_gpiomux_install(msm8960_audio_codec_configs,
 			ARRAY_SIZE(msm8960_audio_codec_configs));
 
+	msm_gpiomux_install(msm8960_audio_auxpcm_configs,
+			ARRAY_SIZE(msm8960_audio_auxpcm_configs));
+
 #ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
 	msm_gpiomux_install(msm8960_hdmi_configs,
 			ARRAY_SIZE(msm8960_hdmi_configs));
@@ -3297,6 +3347,8 @@
 	&msm_cpudai_bt_tx,
 	&msm_cpudai_fm_rx,
 	&msm_cpudai_fm_tx,
+	&msm_cpudai_auxpcm_rx,
+	&msm_cpudai_auxpcm_tx,
 	&msm_cpu_fe,
 	&msm_stub_codec,
 	&msm_voice,
@@ -3338,6 +3390,8 @@
 	&msm_cpudai_bt_tx,
 	&msm_cpudai_fm_rx,
 	&msm_cpudai_fm_tx,
+	&msm_cpudai_auxpcm_rx,
+	&msm_cpudai_auxpcm_tx,
 	&msm_cpu_fe,
 	&msm_stub_codec,
 	&msm_kgsl_3d0,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index a2c55d5..2cdf01d 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -29,6 +29,8 @@
 #include <mach/rpm.h>
 #include <mach/msm_bus_board.h>
 #include <mach/msm_memtypes.h>
+#include <sound/msm-dai-q6.h>
+#include <sound/apr_audio.h>
 #include "clock.h"
 #include "devices.h"
 #include "devices-msm8x60.h"
@@ -1143,6 +1145,34 @@
 	.id     = 0x3005,
 };
 
+/*
+ * Machine specific data for AUX PCM Interface
+ * which the driver will  be unware of.
+ */
+struct msm_dai_auxpcm_pdata auxpcm_rx_pdata = {
+	.clk = "pcm_clk",
+	.mode = AFE_PCM_CFG_MODE_PCM,
+	.sync = AFE_PCM_CFG_SYNC_INT,
+	.frame = AFE_PCM_CFG_FRM_256BPF,
+	.quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD,
+	.slot = 0,
+	.data = AFE_PCM_CFG_CDATAOE_MASTER,
+	.pcm_clk_rate = 2048000,
+};
+
+struct platform_device msm_cpudai_auxpcm_rx = {
+	.name = "msm-dai-q6",
+	.id = 2,
+	.dev = {
+		.platform_data = &auxpcm_rx_pdata,
+	},
+};
+
+struct platform_device msm_cpudai_auxpcm_tx = {
+	.name = "msm-dai-q6",
+	.id = 3,
+};
+
 struct platform_device msm_cpu_fe = {
 	.name	= "msm-dai-fe",
 	.id	= -1,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 9884637..80783d1 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -142,6 +142,8 @@
 extern struct platform_device msm_cpudai_bt_tx;
 extern struct platform_device msm_cpudai_fm_rx;
 extern struct platform_device msm_cpudai_fm_tx;
+extern struct platform_device msm_cpudai_auxpcm_rx;
+extern struct platform_device msm_cpudai_auxpcm_tx;
 extern struct platform_device msm_cpu_fe;
 extern struct platform_device msm_stub_codec;
 extern struct platform_device msm_voice;
diff --git a/include/sound/msm-dai-q6.h b/include/sound/msm-dai-q6.h
new file mode 100644
index 0000000..cec7e65
--- /dev/null
+++ b/include/sound/msm-dai-q6.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_DAI_Q6_PDATA_H__
+
+#define __MSM_DAI_Q6_PDATA_H__
+
+struct msm_dai_auxpcm_pdata {
+	const char *clk;
+	u16 mode;
+	u16 sync;
+	u16 frame;
+	u16 quant;
+	u16 slot;
+	u16 data;
+	int pcm_clk_rate;
+};
+
+#endif
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 400c82f..f53be39 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -17,12 +17,15 @@
 #include <linux/mfd/wcd9310/core.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/apr_audio.h>
 #include <sound/q6afe.h>
 #include <sound/q6adm.h>
+#include <sound/msm-dai-q6.h>
+#include <mach/clk.h>
 
 enum {
 	STATUS_PORT_STARTED, /* track if AFE port has started */
@@ -36,6 +39,8 @@
 	union afe_port_config port_config;
 };
 
+static struct clk *pcm_clk;
+
 static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
 				    struct snd_soc_dai *dai, int stream)
 {
@@ -167,6 +172,35 @@
 
 	return 0;
 }
+static int msm_dai_q6_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;
+
+	if (params_channels(params) != 1) {
+		dev_err(dai->dev, "AUX PCM supports only mono stream\n");
+		return -EINVAL;
+	}
+	dai_data->channels = params_channels(params);
+
+	if (params_rate(params) != 8000) {
+		dev_err(dai->dev, "AUX PCM supports only 8KHz sampling rate\n");
+		return -EINVAL;
+	}
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.pcm.mode = auxpcm_pdata->mode;
+	dai_data->port_config.pcm.sync = auxpcm_pdata->sync;
+	dai_data->port_config.pcm.frame = auxpcm_pdata->frame;
+	dai_data->port_config.pcm.quant = auxpcm_pdata->quant;
+	dai_data->port_config.pcm.slot = auxpcm_pdata->slot;
+	dai_data->port_config.pcm.data = auxpcm_pdata->data;
+
+	return 0;
+}
 
 static int get_frame_size(u16 rate, u16 ch)
 {
@@ -258,6 +292,33 @@
 	return rc;
 }
 
+static void msm_dai_q6_auxpcm_shutdown(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;
+
+	rc = adm_close(dai->id);
+	if (IS_ERR_VALUE(rc))
+		dev_err(dai->dev, "fail to close ADM COPP\n");
+
+	pr_debug("%s: dai->id = %d", __func__, dai->id);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		clk_disable(pcm_clk);
+		rc = afe_close(dai->id); /* can block */
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AFE port\n");
+		pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+			*dai_data->status_mask);
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+
+		rc = afe_close(PCM_TX);
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+	}
+}
+
 static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
 				struct snd_soc_dai *dai)
 {
@@ -279,7 +340,75 @@
 			*dai_data->status_mask);
 		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
 	}
-};
+}
+
+static int msm_dai_q6_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;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = adm_open_mixer(dai->id, 1, dai_data->rate,
+			dai_data->channels, DEFAULT_COPP_TOPOLOGY);
+
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to open ADM\n");
+		else {
+			rc = afe_q6_interface_prepare();
+			if (IS_ERR_VALUE(rc))
+				dev_err(dai->dev, "fail to open AFE APR\n");
+		}
+
+		rc = adm_open_mixer(PCM_TX, 2, dai_data->rate,
+				dai_data->channels, DEFAULT_COPP_TOPOLOGY);
+
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to open ADM\n");
+		else {
+			rc = afe_q6_interface_prepare();
+			if (IS_ERR_VALUE(rc))
+				dev_err(dai->dev, "fail to open AFE APR\n");
+		}
+		pr_debug("%s:dai->id:%d dai_data->status_mask = %ld\n",
+			__func__, dai->id, *dai_data->status_mask);
+
+		/*
+		 * 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(pcm_clk, CLK_RESET_ASSERT);
+
+		afe_open(dai->id, &dai_data->port_config,
+			dai_data->rate);
+		set_bit(STATUS_PORT_STARTED,
+			dai_data->status_mask);
+
+		afe_open(PCM_TX, &dai_data->port_config, dai_data->rate);
+
+		rc = clk_set_rate(pcm_clk, auxpcm_pdata->pcm_clk_rate);
+		if (rc < 0) {
+			pr_err("%s: clk_set_rate failed\n", __func__);
+			return rc;
+		}
+
+		clk_enable(pcm_clk);
+		clk_reset(pcm_clk, CLK_RESET_DEASSERT);
+
+	}
+	return rc;
+}
 
 static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
 		struct snd_soc_dai *dai)
@@ -307,6 +436,45 @@
 	return rc;
 }
 
+static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	pr_debug("%s:port:%d  cmd:%d dai_data->status_mask = %ld",
+		__func__, dai->id, cmd, *dai_data->status_mask);
+
+	switch (cmd) {
+
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/* afe_open will be called from prepare */
+		return 0;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+
+			clk_disable(pcm_clk);
+			afe_port_stop_nowait(dai->id);
+			clear_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+
+			afe_port_stop_nowait(PCM_TX);
+		}
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+
+}
+
 static int msm_dai_q6_trigger(struct snd_pcm_substream *substream, int cmd,
 		struct snd_soc_dai *dai)
 {
@@ -346,6 +514,64 @@
 
 	return rc;
 }
+static int msm_dai_q6_dai_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;
+
+	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);
+
+	/*
+	 * 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.
+	 */
+	pcm_clk = clk_get(NULL, auxpcm_pdata->clk);
+	if (IS_ERR(pcm_clk)) {
+		pr_err("%s: could not get pcm_clk\n", __func__);
+		return PTR_ERR(pcm_clk);
+		kfree(dai_data);
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data;
+	int rc;
+
+	dai_data = dev_get_drvdata(dai->dev);
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(dai->id); /* can block */
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AFE port\n");
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+
+		rc = afe_close(PCM_TX);
+		if (IS_ERR_VALUE(rc))
+			dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+	}
+
+
+	kfree(dai_data);
+	snd_soc_unregister_dai(dai->dev);
+
+	return 0;
+}
 
 static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
 {
@@ -397,6 +623,13 @@
 	.shutdown	= msm_dai_q6_shutdown,
 };
 
+static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = {
+	.prepare	= msm_dai_q6_auxpcm_prepare,
+	.trigger	= msm_dai_q6_auxpcm_trigger,
+	.hw_params	= msm_dai_q6_auxpcm_hw_params,
+	.shutdown	= msm_dai_q6_auxpcm_shutdown,
+};
+
 static struct snd_soc_dai_driver msm_dai_q6_i2s_rx_dai = {
 	.playback = {
 		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
@@ -559,6 +792,31 @@
 	.remove = msm_dai_q6_dai_remove,
 };
 
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_rx_dai = {
+	.playback = {
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,
+		.rate_max = 8000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_auxpcm_ops,
+	.probe = msm_dai_q6_dai_auxpcm_probe,
+	.remove = msm_dai_q6_dai_auxpcm_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_tx_dai = {
+	.capture = {
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,
+		.rate_max = 8000,
+		.rate_min = 8000,
+	},
+};
+
 /* To do: change to register DAIs as batch */
 static __devinit int msm_dai_q6_dev_probe(struct platform_device *pdev)
 {
@@ -573,6 +831,14 @@
 	case PRIMARY_I2S_TX:
 		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_i2s_tx_dai);
 		break;
+	case PCM_RX:
+		rc = snd_soc_register_dai(&pdev->dev,
+				&msm_dai_q6_aux_pcm_rx_dai);
+		break;
+	case PCM_TX:
+		rc = snd_soc_register_dai(&pdev->dev,
+				&msm_dai_q6_aux_pcm_tx_dai);
+		break;
 	case HDMI_RX:
 		rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_hdmi_rx_dai);
 		break;
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 358dbdb..f136646 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -57,6 +57,7 @@
 	AUDIO_MIXER_INT_BT_SCO_RX,
 	AUDIO_MIXER_INT_FM_RX,
 	AUDIO_MIXER_AFE_PCM_RX,
+	AUDIO_MIXER_AUX_PCM_RX,
 	AUDIO_MIXER_MAX,
 };
 
@@ -97,6 +98,8 @@
 	INT_FM_TX,
 	RT_PROXY_PORT_001_RX,
 	RT_PROXY_PORT_001_TX,
+	PCM_RX,
+	PCM_TX,
 };
 
 /* This array is indexed by back-end DAI ID defined in msm-pcm-routing.h
@@ -114,6 +117,8 @@
 	{ INT_FM_TX, 0, NULL, 0, 0},
 	{ RT_PROXY_PORT_001_RX, 0, NULL, 0, 0},
 	{ RT_PROXY_PORT_001_TX, 0, NULL, 0, 0},
+	{ PCM_RX, 0, NULL, 0, 0},
+	{ PCM_TX, 0, NULL, 0, 0},
 };
 
 
@@ -144,6 +149,8 @@
 	{INT_FM_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
 	/* AUDIO_MIXER_AFE_PCM_RX */
 	{RT_PROXY_PORT_001_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
+	/* AUDIO_MIXER_AUX_PCM_RX */
+	{PCM_RX, 0, SNDRV_PCM_STREAM_PLAYBACK},
 };
 
 /* Reuse audio_mixer_data struct but ignore mixer type field
@@ -541,6 +548,17 @@
 	msm_routing_put_audio_mixer),
 };
 
+static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", AUDIO_MIXER_AUX_PCM_RX ,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", AUDIO_MIXER_AUX_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", AUDIO_MIXER_AUX_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 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", AUDIO_MIXER_MM_UL1,
@@ -549,6 +567,9 @@
 	SOC_SINGLE_EXT("SLIM_0_TX", AUDIO_MIXER_MM_UL1,
 	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_audio_mixer,
 	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", AUDIO_MIXER_MM_UL1,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
 	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", AUDIO_MIXER_MM_UL1,
 	MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_audio_mixer,
 	msm_routing_put_audio_mixer),
@@ -602,6 +623,15 @@
 	msm_routing_put_voice_mixer),
 };
 
+static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_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_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
 static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
 	SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
 	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -615,6 +645,9 @@
 	SOC_SINGLE_EXT("AFE_PCM_TX_Voice", MSM_BACKEND_DAI_AFE_PCM_TX,
 	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	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),
 };
 
 static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
@@ -630,6 +663,9 @@
 	SOC_SINGLE_EXT("AFE_PCM_TX_Voip", MSM_BACKEND_DAI_AFE_PCM_TX,
 	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
 	msm_routing_put_voice_mixer),
+	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),
 };
 
 static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
@@ -700,6 +736,8 @@
 				0, 0, 0 , 0),
 	SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
 				0, 0, 0, 0),
+	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),
 	/* Switch Definitions */
 	SND_SOC_DAPM_SWITCH("SBUS_0_RX", SND_SOC_NOPM, 0, 0,
 				&fm_switch_mixer_controls),
@@ -714,6 +752,8 @@
 	mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
 	SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
 	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)),
 	/* Voice Mixer */
 	SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
 				SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
@@ -730,6 +770,10 @@
 				SND_SOC_NOPM, 0, 0,
 				afe_pcm_rx_voice_mixer_controls,
 				ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				aux_pcm_rx_voice_mixer_controls,
+				ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
 	SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
 				SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
 				ARRAY_SIZE(tx_voice_mixer_controls)),
@@ -765,6 +809,7 @@
 
 	{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
 	{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
 
 	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
 	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -789,6 +834,11 @@
 	{"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
 	{"MM_UL2", NULL, "MultiMedia2 Mixer"},
 
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
+
 	{"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
 	{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
 	{"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
@@ -805,15 +855,22 @@
 	{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
 	{"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
 
+	{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
+
 	{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
 	{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
 	{"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"},
 	{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
 	{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
 	{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
 	{"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_UL", NULL, "Voip_Tx Mixer"},
 	{"SLIMBUS_0_RX", "Switch", "SLIM0_DL_HL"},
 	{"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index 1e90867..cac2fc6 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -24,6 +24,8 @@
 #define LPASS_BE_INT_FM_TX "(Backend) INT_FM_TX"
 #define LPASS_BE_AFE_PCM_RX "(Backend) RT_PROXY_DAI_001_RX"
 #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"
 
 enum {
 	MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
@@ -48,6 +50,8 @@
 	MSM_BACKEND_DAI_INT_FM_TX,
 	MSM_BACKEND_DAI_AFE_PCM_RX,
 	MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX,
 	MSM_BACKEND_DAI_MAX,
 };
 
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index bf01c5e..5b54bac 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -48,6 +48,11 @@
 #define TOP_SPK_AMP_POS		0x4
 #define TOP_SPK_AMP_NEG		0x8
 
+#define GPIO_AUX_PCM_DOUT 63
+#define GPIO_AUX_PCM_DIN 64
+#define GPIO_AUX_PCM_SYNC 65
+#define GPIO_AUX_PCM_CLK 66
+
 static int msm8960_spk_control;
 static int msm8960_ext_bottom_spk_pamp;
 static int msm8960_ext_top_spk_pamp;
@@ -708,7 +713,76 @@
 
 	return 0;
 }
+static int msm8960_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
 
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	/* PCM only supports mono output with 8khz sample rate */
+	rate->min = rate->max = 8000;
+	channels->min = channels->max = 1;
+
+	return 0;
+}
+static int msm8960_aux_pcm_get_gpios(void)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
+				__func__, GPIO_AUX_PCM_DOUT);
+		goto fail_dout;
+	}
+
+	ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
+				__func__, GPIO_AUX_PCM_DIN);
+		goto fail_din;
+	}
+
+	ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
+				__func__, GPIO_AUX_PCM_SYNC);
+		goto fail_sync;
+	}
+	ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
+	if (ret < 0) {
+		pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
+				__func__, GPIO_AUX_PCM_CLK);
+		goto fail_clk;
+	}
+
+	return 0;
+
+fail_clk:
+	gpio_free(GPIO_AUX_PCM_SYNC);
+fail_sync:
+	gpio_free(GPIO_AUX_PCM_DIN);
+fail_din:
+	gpio_free(GPIO_AUX_PCM_DOUT);
+fail_dout:
+
+	return ret;
+}
+
+static int msm8960_aux_pcm_free_gpios(void)
+{
+	gpio_free(GPIO_AUX_PCM_DIN);
+	gpio_free(GPIO_AUX_PCM_DOUT);
+	gpio_free(GPIO_AUX_PCM_SYNC);
+	gpio_free(GPIO_AUX_PCM_CLK);
+
+	return 0;
+}
 static int msm8960_startup(struct snd_pcm_substream *substream)
 {
 	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
@@ -716,6 +790,26 @@
 	return 0;
 }
 
+static int msm8960_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	pr_debug("%s(): substream = %s\n", __func__, substream->name);
+	ret = msm8960_aux_pcm_get_gpios();
+	if (ret < 0) {
+		pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void msm8960_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+
+	pr_debug("%s(): substream = %s\n", __func__, substream->name);
+	msm8960_aux_pcm_free_gpios();
+}
+
 static void msm8960_shutdown(struct snd_pcm_substream *substream)
 {
 	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
@@ -727,6 +821,11 @@
 	.shutdown = msm8960_shutdown,
 };
 
+static struct snd_soc_ops msm8960_auxpcm_be_ops = {
+	.startup = msm8960_auxpcm_startup,
+	.shutdown = msm8960_auxpcm_shutdown,
+};
+
 /* Digital audio interface glue - connects codec <---> CPU */
 static struct snd_soc_dai_link msm8960_dai[] = {
 	/* FrontEnd DAI Links */
@@ -926,6 +1025,30 @@
 		.no_pcm = 1,
 		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
 	},
+	/* AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_AUXPCM_RX,
+		.stream_name = "AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6.2",
+		.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_AUXPCM_RX,
+		.be_hw_params_fixup = msm8960_auxpcm_be_params_fixup,
+		.ops = &msm8960_auxpcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_AUXPCM_TX,
+		.stream_name = "AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6.3",
+		.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_AUXPCM_TX,
+		.be_hw_params_fixup = msm8960_auxpcm_be_params_fixup,
+	},
 };
 
 struct snd_soc_card snd_soc_card_msm8960 = {