summary refs log tree commit diff
path: root/sound/soc/qcom
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/qcom')
-rw-r--r--sound/soc/qcom/Kconfig28
-rw-r--r--sound/soc/qcom/Makefile6
-rw-r--r--sound/soc/qcom/apq8016_sbc.c198
-rw-r--r--sound/soc/qcom/lpass-apq8016.c242
-rw-r--r--sound/soc/qcom/lpass-cpu.c240
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c109
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h (renamed from sound/soc/qcom/lpass-lpaif-ipq806x.h)92
-rw-r--r--sound/soc/qcom/lpass-platform.c202
-rw-r--r--sound/soc/qcom/lpass.h51
-rw-r--r--sound/soc/qcom/storm.c26
10 files changed, 916 insertions, 278 deletions
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 5f58e4f1bca9..807fedfa1c76 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -6,20 +6,38 @@ config SND_SOC_QCOM
 
 config SND_SOC_LPASS_CPU
 	tristate
-	depends on SND_SOC_QCOM
 	select REGMAP_MMIO
 
 config SND_SOC_LPASS_PLATFORM
 	tristate
-	depends on SND_SOC_QCOM
 	select REGMAP_MMIO
 
-config SND_SOC_STORM
-	tristate "ASoC I2S support for Storm boards"
-	depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
+config SND_SOC_LPASS_IPQ806X
+	tristate
+	depends on SND_SOC_QCOM
+	select SND_SOC_LPASS_CPU
+	select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_LPASS_APQ8016
+	tristate
+	depends on SND_SOC_QCOM
 	select SND_SOC_LPASS_CPU
 	select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_STORM
+	tristate "ASoC I2S support for Storm boards"
+	depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
+	select SND_SOC_LPASS_IPQ806X
 	select SND_SOC_MAX98357A
 	help
           Say Y or M if you want add support for SoC audio on the
           Qualcomm Technologies IPQ806X-based Storm board.
+
+config SND_SOC_APQ8016_SBC
+	tristate "SoC Audio support for APQ8016 SBC platforms"
+	depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
+	select SND_SOC_LPASS_APQ8016
+	help
+          Support for Qualcomm Technologies LPASS audio block in
+          APQ8016 SOC-based systems.
+          Say Y if you want to use audio devices on MI2S.
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index c5ce96c761c4..79e5c50a8f71 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -1,11 +1,17 @@
 # Platform
 snd-soc-lpass-cpu-objs := lpass-cpu.o
 snd-soc-lpass-platform-objs := lpass-platform.o
+snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
+snd-soc-lpass-apq8016-objs := lpass-apq8016.o
 
 obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
 obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
+obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
+obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
 
 # Machine
 snd-soc-storm-objs := storm.o
+snd-soc-apq8016-sbc-objs := apq8016_sbc.o
 
 obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
+obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
new file mode 100644
index 000000000000..1efdf0088ecd
--- /dev/null
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2015 The Linux Foundation. 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.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <dt-bindings/sound/apq8016-lpass.h>
+
+struct apq8016_sbc_data {
+	void __iomem *mic_iomux;
+	void __iomem *spkr_iomux;
+	struct snd_soc_dai_link dai_link[];	/* dynamically allocated */
+};
+
+#define MIC_CTRL_QUA_WS_SLAVE_SEL_10	BIT(17)
+#define MIC_CTRL_TLMM_SCLK_EN		BIT(1)
+#define	SPKR_CTL_PRI_WS_SLAVE_SEL_11	(BIT(17) | BIT(16))
+
+static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_card *card = rtd->card;
+	struct apq8016_sbc_data *pdata = snd_soc_card_get_drvdata(card);
+	int rval = 0;
+
+	switch (cpu_dai->id) {
+	case MI2S_PRIMARY:
+		writel(readl(pdata->spkr_iomux) | SPKR_CTL_PRI_WS_SLAVE_SEL_11,
+			pdata->spkr_iomux);
+		break;
+
+	case MI2S_QUATERNARY:
+		/* Configure the Quat MI2S to TLMM */
+		writel(readl(pdata->mic_iomux) | MIC_CTRL_QUA_WS_SLAVE_SEL_10 |
+			MIC_CTRL_TLMM_SCLK_EN,
+			pdata->mic_iomux);
+		break;
+
+	default:
+		dev_err(card->dev, "unsupported cpu dai configuration\n");
+		rval = -EINVAL;
+		break;
+
+	}
+
+	return rval;
+}
+
+static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
+{
+	struct device *dev = card->dev;
+	struct snd_soc_dai_link *link;
+	struct device_node *np, *codec, *cpu, *node  = dev->of_node;
+	struct apq8016_sbc_data *data;
+	int ret, num_links;
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(dev, "Error parsing card name: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	/* Populate links */
+	num_links = of_get_child_count(node);
+
+	/* Allocate the private data and the DAI link array */
+	data = devm_kzalloc(dev, sizeof(*data) + sizeof(*link) * num_links,
+			    GFP_KERNEL);
+	if (!data)
+		return ERR_PTR(-ENOMEM);
+
+	card->dai_link	= &data->dai_link[0];
+	card->num_links	= num_links;
+
+	link = data->dai_link;
+
+	for_each_child_of_node(node, np) {
+		cpu = of_get_child_by_name(np, "cpu");
+		codec = of_get_child_by_name(np, "codec");
+
+		if (!cpu || !codec) {
+			dev_err(dev, "Can't find cpu/codec DT node\n");
+			return ERR_PTR(-EINVAL);
+		}
+
+		link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
+		if (!link->cpu_of_node) {
+			dev_err(card->dev, "error getting cpu phandle\n");
+			return ERR_PTR(-EINVAL);
+		}
+
+		link->codec_of_node = of_parse_phandle(codec, "sound-dai", 0);
+		if (!link->codec_of_node) {
+			dev_err(card->dev, "error getting codec phandle\n");
+			return ERR_PTR(-EINVAL);
+		}
+
+		ret = snd_soc_of_get_dai_name(cpu, &link->cpu_dai_name);
+		if (ret) {
+			dev_err(card->dev, "error getting cpu dai name\n");
+			return ERR_PTR(ret);
+		}
+
+		ret = snd_soc_of_get_dai_name(codec, &link->codec_dai_name);
+		if (ret) {
+			dev_err(card->dev, "error getting codec dai name\n");
+			return ERR_PTR(ret);
+		}
+
+		link->platform_of_node = link->cpu_of_node;
+		/* For now we only support playback */
+		link->playback_only = true;
+
+		ret = of_property_read_string(np, "link-name", &link->name);
+		if (ret) {
+			dev_err(card->dev, "error getting codec dai_link name\n");
+			return ERR_PTR(ret);
+		}
+
+		link->stream_name = link->name;
+		link->init = apq8016_sbc_dai_init;
+		link++;
+	}
+
+	return data;
+}
+
+static int apq8016_sbc_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct snd_soc_card *card;
+	struct apq8016_sbc_data *data;
+	struct resource *res;
+
+	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return -ENOMEM;
+
+	card->dev = dev;
+	data = apq8016_sbc_parse_of(card);
+	if (IS_ERR(data)) {
+		dev_err(&pdev->dev, "Error resolving dai links: %ld\n",
+			PTR_ERR(data));
+		return PTR_ERR(data);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mic-iomux");
+	data->mic_iomux = devm_ioremap_resource(dev, res);
+	if (IS_ERR(data->mic_iomux))
+		return PTR_ERR(data->mic_iomux);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spkr-iomux");
+	data->spkr_iomux = devm_ioremap_resource(dev, res);
+	if (IS_ERR(data->spkr_iomux))
+		return PTR_ERR(data->spkr_iomux);
+
+	platform_set_drvdata(pdev, data);
+	snd_soc_card_set_drvdata(card, data);
+
+	return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static const struct of_device_id apq8016_sbc_device_id[]  = {
+	{ .compatible = "qcom,apq8016-sbc-sndcard" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, apq8016_sbc_device_id);
+
+static struct platform_driver apq8016_sbc_platform_driver = {
+	.driver = {
+		.name = "qcom-apq8016-sbc",
+		.of_match_table = of_match_ptr(apq8016_sbc_device_id),
+	},
+	.probe = apq8016_sbc_platform_probe,
+};
+module_platform_driver(apq8016_sbc_platform_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("APQ8016 ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
new file mode 100644
index 000000000000..94efc01020c4
--- /dev/null
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. 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.
+ *
+ * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
+ *
+ */
+
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/apq8016-lpass.h>
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
+	[MI2S_PRIMARY] =  {
+		.id = MI2S_PRIMARY,
+		.name = "Primary MI2S",
+		.playback = {
+			.stream_name	= "Primary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_SECONDARY] =  {
+		.id = MI2S_SECONDARY,
+		.name = "Secondary MI2S",
+		.playback = {
+			.stream_name	= "Secondary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_TERTIARY] =  {
+		.id = MI2S_TERTIARY,
+		.name = "Tertiary MI2S",
+		.capture = {
+			.stream_name	= "Tertiary Capture",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_QUATERNARY] =  {
+		.id = MI2S_QUATERNARY,
+		.name = "Quatenary MI2S",
+		.playback = {
+			.stream_name	= "Quatenary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.capture = {
+			.stream_name	= "Quatenary Capture",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+};
+
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+	struct lpass_variant *v = drvdata->variant;
+	int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+					v->rdma_channels);
+
+	if (chan >= v->rdma_channels)
+		return -EBUSY;
+
+	set_bit(chan, &drvdata->rdma_ch_bit_map);
+
+	return chan;
+}
+
+static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+	clear_bit(chan, &drvdata->rdma_ch_bit_map);
+
+	return 0;
+}
+
+static int apq8016_lpass_init(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");
+	if (IS_ERR(drvdata->pcnoc_mport_clk)) {
+		dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->pcnoc_mport_clk));
+		return PTR_ERR(drvdata->pcnoc_mport_clk);
+	}
+
+	ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");
+	if (IS_ERR(drvdata->pcnoc_sway_clk)) {
+		dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->pcnoc_sway_clk));
+		return PTR_ERR(drvdata->pcnoc_sway_clk);
+	}
+
+	ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int apq8016_lpass_exit(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(drvdata->pcnoc_mport_clk);
+	clk_disable_unprepare(drvdata->pcnoc_sway_clk);
+
+	return 0;
+}
+
+
+static struct lpass_variant apq8016_data = {
+	.i2sctrl_reg_base	= 0x1000,
+	.i2sctrl_reg_stride	= 0x1000,
+	.i2s_ports		= 4,
+	.irq_reg_base		= 0x6000,
+	.irq_reg_stride		= 0x1000,
+	.irq_ports		= 3,
+	.rdma_reg_base		= 0x8400,
+	.rdma_reg_stride	= 0x1000,
+	.rdma_channels		= 2,
+	.rdmactl_audif_start	= 1,
+	.dai_driver		= apq8016_lpass_cpu_dai_driver,
+	.num_dai		= ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+	.init			= apq8016_lpass_init,
+	.exit			= apq8016_lpass_exit,
+	.alloc_dma_channel	= apq8016_lpass_alloc_dma_channel,
+	.free_dma_channel	= apq8016_lpass_free_dma_channel,
+};
+
+static const struct of_device_id apq8016_lpass_cpu_device_id[] = {
+	{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
+	{}
+};
+MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
+
+static struct platform_driver apq8016_lpass_cpu_platform_driver = {
+	.driver	= {
+		.name		= "apq8016-lpass-cpu",
+		.of_match_table	= of_match_ptr(apq8016_lpass_cpu_device_id),
+	},
+	.probe	= asoc_qcom_lpass_cpu_platform_probe,
+	.remove	= asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(apq8016_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index dc790abaa331..23f3d59e6d09 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -14,21 +14,17 @@
  */
 
 #include <linux/clk.h>
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
 #include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 
 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
-	ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
+	if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+		return 0;
+
+	ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
 	if (ret)
 		dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
 				__func__, freq, ret);
@@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
-	ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
-	if (ret) {
-		dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
-				__func__, ret);
-		return ret;
+	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
+		ret = clk_prepare_enable(
+				drvdata->mi2s_osr_clk[dai->driver->id]);
+		if (ret) {
+			dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
+					__func__, ret);
+			return ret;
+		}
 	}
 
-	ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
+	ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
 	if (ret) {
 		dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
 				__func__, ret);
-		clk_disable_unprepare(drvdata->mi2s_osr_clk);
+		if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+			clk_disable_unprepare(
+				drvdata->mi2s_osr_clk[dai->driver->id]);
 		return ret;
 	}
 
@@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 
-	clk_disable_unprepare(drvdata->mi2s_bit_clk);
-	clk_disable_unprepare(drvdata->mi2s_osr_clk);
+	clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+
+	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
 }
 
 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
@@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
+			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+			   regval);
 	if (ret) {
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 		return ret;
 	}
 
-	ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
+	ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
+			   rate * bitwidth * 2);
 	if (ret) {
 		dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
 				__func__, rate * bitwidth * 2, ret);
@@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
 	int ret;
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+			   0);
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
@@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 	int ret;
 
 	ret = regmap_update_bits(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
 			LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
@@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_REG(drvdata->variant,
+						dai->driver->id),
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_ENABLE);
 		if (ret)
@@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_REG(drvdata->variant,
+						dai->driver->id),
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_DISABLE);
 		if (ret)
@@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
+struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
 	.set_sysclk	= lpass_cpu_daiops_set_sysclk,
 	.startup	= lpass_cpu_daiops_startup,
 	.shutdown	= lpass_cpu_daiops_shutdown,
@@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
 	.prepare	= lpass_cpu_daiops_prepare,
 	.trigger	= lpass_cpu_daiops_trigger,
 };
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
 
-static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 
 	/* ensure audio hardware is disabled */
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 
 	return ret;
 }
-
-static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
-	.playback = {
-		.stream_name	= "lpass-cpu-playback",
-		.formats	= SNDRV_PCM_FMTBIT_S16 |
-					SNDRV_PCM_FMTBIT_S24 |
-					SNDRV_PCM_FMTBIT_S32,
-		.rates		= SNDRV_PCM_RATE_8000 |
-					SNDRV_PCM_RATE_16000 |
-					SNDRV_PCM_RATE_32000 |
-					SNDRV_PCM_RATE_48000 |
-					SNDRV_PCM_RATE_96000,
-		.rate_min	= 8000,
-		.rate_max	= 96000,
-		.channels_min	= 1,
-		.channels_max	= 8,
-	},
-	.probe	= &lpass_cpu_dai_probe,
-	.ops    = &lpass_cpu_dai_ops,
-};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
 
 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 	.name = "lpass-cpu",
@@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 
 static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 
-	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
-		if (reg == LPAIF_I2SCTL_REG(i))
+	for (i = 0; i < v->i2s_ports; ++i)
+		if (reg == LPAIF_I2SCTL_REG(v, i))
 			return true;
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
-		if (reg == LPAIF_IRQEN_REG(i))
+	for (i = 0; i < v->irq_ports; ++i) {
+		if (reg == LPAIF_IRQEN_REG(v, i))
 			return true;
-		if (reg == LPAIF_IRQCLEAR_REG(i))
+		if (reg == LPAIF_IRQCLEAR_REG(v, i))
 			return true;
 	}
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
-		if (reg == LPAIF_RDMACTL_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABASE_REG(i))
+		if (reg == LPAIF_RDMABASE_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABUFF_REG(i))
+		if (reg == LPAIF_RDMABUFF_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMAPER_REG(i))
+		if (reg == LPAIF_RDMAPER_REG(v, i))
 			return true;
 	}
 
@@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 
 static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 
-	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
-		if (reg == LPAIF_I2SCTL_REG(i))
+	for (i = 0; i < v->i2s_ports; ++i)
+		if (reg == LPAIF_I2SCTL_REG(v, i))
 			return true;
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
-		if (reg == LPAIF_IRQEN_REG(i))
+	for (i = 0; i < v->irq_ports; ++i) {
+		if (reg == LPAIF_IRQEN_REG(v, i))
 			return true;
-		if (reg == LPAIF_IRQSTAT_REG(i))
+		if (reg == LPAIF_IRQSTAT_REG(v, i))
 			return true;
 	}
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
-		if (reg == LPAIF_RDMACTL_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABASE_REG(i))
+		if (reg == LPAIF_RDMABASE_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMABUFF_REG(i))
+		if (reg == LPAIF_RDMABUFF_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMACURR_REG(i))
+		if (reg == LPAIF_RDMACURR_REG(v, i))
 			return true;
-		if (reg == LPAIF_RDMAPER_REG(i))
+		if (reg == LPAIF_RDMAPER_REG(v, i))
 			return true;
 	}
 
@@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 
 static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
-		if (reg == LPAIF_IRQSTAT_REG(i))
+	for (i = 0; i < v->irq_ports; ++i)
+		if (reg == LPAIF_IRQSTAT_REG(v, i))
 			return true;
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
-		if (reg == LPAIF_RDMACURR_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i)
+		if (reg == LPAIF_RDMACURR_REG(v, i))
 			return true;
 
 	return false;
 }
 
-static const struct regmap_config lpass_cpu_regmap_config = {
+static struct regmap_config lpass_cpu_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
-	.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
 	.writeable_reg = lpass_cpu_regmap_writeable,
 	.readable_reg = lpass_cpu_regmap_readable,
 	.volatile_reg = lpass_cpu_regmap_volatile,
 	.cache_type = REGCACHE_FLAT,
 };
 
-static int lpass_cpu_platform_probe(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata;
 	struct device_node *dsp_of_node;
 	struct resource *res;
-	int ret;
+	struct lpass_variant *variant;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	char clk_name[16];
+	int ret, i, dai_id;
 
 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
 	if (dsp_of_node) {
@@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drvdata);
 
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data)
+		return -EINVAL;
+
+	drvdata->variant = (struct lpass_variant *)match->data;
+	variant = drvdata->variant;
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
-	if (!res) {
-		dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
-		return -ENODEV;
-	}
 
 	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR((void const __force *)drvdata->lpaif)) {
@@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return PTR_ERR((void const __force *)drvdata->lpaif);
 	}
 
+	lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
+						variant->rdma_channels);
+
 	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
 			&lpass_cpu_regmap_config);
 	if (IS_ERR(drvdata->lpaif_map)) {
@@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return PTR_ERR(drvdata->lpaif_map);
 	}
 
-	drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
-	if (IS_ERR(drvdata->mi2s_osr_clk)) {
-		dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
-				__func__, PTR_ERR(drvdata->mi2s_osr_clk));
-		return PTR_ERR(drvdata->mi2s_osr_clk);
-	}
-
-	drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
-	if (IS_ERR(drvdata->mi2s_bit_clk)) {
-		dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
-				__func__, PTR_ERR(drvdata->mi2s_bit_clk));
-		return PTR_ERR(drvdata->mi2s_bit_clk);
+	if (variant->init)
+		variant->init(pdev);
+
+	for (i = 0; i < variant->num_dai; i++) {
+		dai_id = variant->dai_driver[i].id;
+		if (variant->num_dai > 1)
+			sprintf(clk_name, "mi2s-osr-clk%d", i);
+		else
+			sprintf(clk_name, "mi2s-osr-clk");
+
+		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
+								clk_name);
+		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
+			dev_warn(&pdev->dev,
+				"%s() error getting mi2s-osr-clk: %ld\n",
+				__func__,
+				PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
+		}
+
+		if (variant->num_dai > 1)
+			sprintf(clk_name, "mi2s-bit-clk%d", i);
+		else
+			sprintf(clk_name, "mi2s-bit-clk");
+
+		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
+							    clk_name);
+		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
+			dev_err(&pdev->dev,
+				"%s() error getting mi2s-bit-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
+		}
 	}
 
 	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
@@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 	}
 
 	ret = devm_snd_soc_register_component(&pdev->dev,
-			&lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
+					      &lpass_cpu_comp_driver,
+					      variant->dai_driver,
+					      variant->num_dai);
 	if (ret) {
 		dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
 				__func__, ret);
@@ -459,33 +489,17 @@ err_clk:
 	clk_disable_unprepare(drvdata->ahbix_clk);
 	return ret;
 }
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
 
-static int lpass_cpu_platform_remove(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
 
+	if (drvdata->variant->exit)
+		drvdata->variant->exit(pdev);
+
 	clk_disable_unprepare(drvdata->ahbix_clk);
 
 	return 0;
 }
-
-#ifdef CONFIG_OF
-static const struct of_device_id lpass_cpu_device_id[] = {
-	{ .compatible = "qcom,lpass-cpu" },
-	{}
-};
-MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
-#endif
-
-static struct platform_driver lpass_cpu_platform_driver = {
-	.driver	= {
-		.name		= "lpass-cpu",
-		.of_match_table	= of_match_ptr(lpass_cpu_device_id),
-	},
-	.probe	= lpass_cpu_platform_probe,
-	.remove	= lpass_cpu_platform_remove,
-};
-module_platform_driver(lpass_cpu_platform_driver);
-
-MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
new file mode 100644
index 000000000000..7356d3a766d6
--- /dev/null
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. 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.
+ *
+ * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
+ * Splited out the IPQ8064 soc specific from lpass-cpu.c
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+enum lpaif_i2s_ports {
+	IPQ806X_LPAIF_I2S_PORT_CODEC_SPK,
+	IPQ806X_LPAIF_I2S_PORT_CODEC_MIC,
+	IPQ806X_LPAIF_I2S_PORT_SEC_SPK,
+	IPQ806X_LPAIF_I2S_PORT_SEC_MIC,
+	IPQ806X_LPAIF_I2S_PORT_MI2S,
+};
+
+enum lpaif_dma_channels {
+	IPQ806X_LPAIF_RDMA_CHAN_MI2S,
+	IPQ806X_LPAIF_RDMA_CHAN_PCM0,
+	IPQ806X_LPAIF_RDMA_CHAN_PCM1,
+};
+
+static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
+	.id	= IPQ806X_LPAIF_I2S_PORT_MI2S,
+	.playback = {
+		.stream_name	= "lpass-cpu-playback",
+		.formats	= SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+		.rates		= SNDRV_PCM_RATE_8000 |
+					SNDRV_PCM_RATE_16000 |
+					SNDRV_PCM_RATE_32000 |
+					SNDRV_PCM_RATE_48000 |
+					SNDRV_PCM_RATE_96000,
+		.rate_min	= 8000,
+		.rate_max	= 96000,
+		.channels_min	= 1,
+		.channels_max	= 8,
+	},
+	.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+	.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+};
+
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+	return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+}
+
+static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+	return 0;
+}
+
+struct lpass_variant ipq806x_data = {
+	.i2sctrl_reg_base	= 0x0010,
+	.i2sctrl_reg_stride	= 0x04,
+	.i2s_ports		= 5,
+	.irq_reg_base		= 0x3000,
+	.irq_reg_stride		= 0x1000,
+	.irq_ports		= 3,
+	.rdma_reg_base		= 0x6000,
+	.rdma_reg_stride	= 0x1000,
+	.rdma_channels		= 4,
+	.dai_driver		= &ipq806x_lpass_cpu_dai_driver,
+	.num_dai		= 1,
+	.alloc_dma_channel	= ipq806x_lpass_alloc_dma_channel,
+	.free_dma_channel	= ipq806x_lpass_free_dma_channel,
+};
+
+static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
+	{ .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id);
+
+static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
+	.driver	= {
+		.name		= "lpass-cpu",
+		.of_match_table	= of_match_ptr(ipq806x_lpass_cpu_device_id),
+	},
+	.probe	= asoc_qcom_lpass_cpu_platform_probe,
+	.remove	= asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(ipq806x_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-reg.h
index dc423b888842..95e22f131052 100644
--- a/sound/soc/qcom/lpass-lpaif-ipq806x.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -9,37 +9,17 @@
  * 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.
- *
- * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
  */
 
-#ifndef __LPASS_LPAIF_H__
-#define __LPASS_LPAIF_H__
-
-#define LPAIF_BANK_OFFSET		0x1000
+#ifndef __LPASS_LPAIF_REG_H__
+#define __LPASS_LPAIF_REG_H__
 
 /* LPAIF I2S */
 
-#define LPAIF_I2SCTL_REG_BASE		0x0010
-#define LPAIF_I2SCTL_REG_STRIDE		0x4
-#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
-	(LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
-
-enum lpaif_i2s_ports {
-	LPAIF_I2S_PORT_MIN		= 0,
-
-	LPAIF_I2S_PORT_CODEC_SPK	= 0,
-	LPAIF_I2S_PORT_CODEC_MIC	= 1,
-	LPAIF_I2S_PORT_SEC_SPK		= 2,
-	LPAIF_I2S_PORT_SEC_MIC		= 3,
-	LPAIF_I2S_PORT_MI2S		= 4,
-
-	LPAIF_I2S_PORT_MAX		= 4,
-	LPAIF_I2S_PORT_NUM		= 5,
-};
-
-#define LPAIF_I2SCTL_REG(port)		LPAIF_I2SCTL_REG_ADDR(0x0, (port))
+#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
+	(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
 
+#define LPAIF_I2SCTL_REG(v, port)	LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
 #define LPAIF_I2SCTL_LOOPBACK_MASK	0x8000
 #define LPAIF_I2SCTL_LOOPBACK_SHIFT	15
 #define LPAIF_I2SCTL_LOOPBACK_DISABLE	(0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
@@ -79,55 +59,36 @@ enum lpaif_i2s_ports {
 #define LPAIF_I2SCTL_BITWIDTH_32	(2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
 
 /* LPAIF IRQ */
+#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
+	(v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
 
-#define LPAIF_IRQ_REG_BASE		0x3000
-#define LPAIF_IRQ_REG_STRIDE		0x1000
-#define LPAIF_IRQ_REG_ADDR(addr, port) \
-	(LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
-
-enum lpaif_irq_ports {
-	LPAIF_IRQ_PORT_MIN		= 0,
+#define LPAIF_IRQ_PORT_HOST		0
 
-	LPAIF_IRQ_PORT_HOST		= 0,
-	LPAIF_IRQ_PORT_ADSP		= 1,
-
-	LPAIF_IRQ_PORT_MAX		= 2,
-	LPAIF_IRQ_PORT_NUM		= 3,
-};
-
-#define LPAIF_IRQEN_REG(port)		LPAIF_IRQ_REG_ADDR(0x0, (port))
-#define LPAIF_IRQSTAT_REG(port)		LPAIF_IRQ_REG_ADDR(0x4, (port))
-#define LPAIF_IRQCLEAR_REG(port)	LPAIF_IRQ_REG_ADDR(0xC, (port))
+#define LPAIF_IRQEN_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
+#define LPAIF_IRQSTAT_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
+#define LPAIF_IRQCLEAR_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
 
 #define LPAIF_IRQ_BITSTRIDE		3
+
 #define LPAIF_IRQ_PER(chan)		(1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_XRUN(chan)		(2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_ERR(chan)		(4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+
 #define LPAIF_IRQ_ALL(chan)		(7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 
 /* LPAIF DMA */
 
-#define LPAIF_RDMA_REG_BASE		0x6000
-#define LPAIF_RDMA_REG_STRIDE		0x1000
-#define LPAIF_RDMA_REG_ADDR(addr, chan) \
-	(LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
-
-enum lpaif_dma_channels {
-	LPAIF_RDMA_CHAN_MIN		= 0,
-
-	LPAIF_RDMA_CHAN_MI2S		= 0,
-	LPAIF_RDMA_CHAN_PCM0		= 1,
-	LPAIF_RDMA_CHAN_PCM1		= 2,
+#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
+	(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
 
-	LPAIF_RDMA_CHAN_MAX		= 4,
-	LPAIF_RDMA_CHAN_NUM		= 5,
-};
+#define LPAIF_RDMACTL_AUDINTF(id)	(id << LPAIF_RDMACTL_AUDINTF_SHIFT)
 
-#define LPAIF_RDMACTL_REG(chan)		LPAIF_RDMA_REG_ADDR(0x00, (chan))
-#define LPAIF_RDMABASE_REG(chan)	LPAIF_RDMA_REG_ADDR(0x04, (chan))
-#define	LPAIF_RDMABUFF_REG(chan)	LPAIF_RDMA_REG_ADDR(0x08, (chan))
-#define LPAIF_RDMACURR_REG(chan)	LPAIF_RDMA_REG_ADDR(0x0C, (chan))
-#define	LPAIF_RDMAPER_REG(chan)		LPAIF_RDMA_REG_ADDR(0x10, (chan))
+#define LPAIF_RDMACTL_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_RDMABASE_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
+#define	LPAIF_RDMABUFF_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_RDMACURR_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan))
+#define	LPAIF_RDMAPER_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
+#define	LPAIF_RDMAPERCNT_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
 
 #define LPAIF_RDMACTL_BURSTEN_MASK	0x800
 #define LPAIF_RDMACTL_BURSTEN_SHIFT	11
@@ -145,13 +106,6 @@ enum lpaif_dma_channels {
 
 #define LPAIF_RDMACTL_AUDINTF_MASK	0x0F0
 #define LPAIF_RDMACTL_AUDINTF_SHIFT	4
-#define LPAIF_RDMACTL_AUDINTF_NONE	(0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_CODEC	(1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_PCM	(2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_I2S	(3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_MI2S	(4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_HDMI	(5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_PCM	(7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
 
 #define LPAIF_RDMACTL_FIFOWM_MASK	0x00E
 #define LPAIF_RDMACTL_FIFOWM_SHIFT	1
@@ -169,4 +123,4 @@ enum lpaif_dma_channels {
 #define LPAIF_RDMACTL_ENABLE_OFF	(0 << LPAIF_RDMACTL_ENABLE_SHIFT)
 #define LPAIF_RDMACTL_ENABLE_ON		(1 << LPAIF_RDMACTL_ENABLE_SHIFT)
 
-#endif /* __LPASS_LPAIF_H__ */
+#endif /* __LPASS_LPAIF_REG_H__ */
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 2fa6280dfb23..79688aa1941a 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -13,23 +13,22 @@
  * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
  */
 
-#include <linux/compiler.h>
-#include <linux/device.h>
 #include <linux/dma-mapping.h>
-#include <linux/err.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/io.h>
 #include <linux/platform_device.h>
-#include <sound/memalloc.h>
-#include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 
+struct lpass_pcm_data {
+	int rdma_ch;
+	int i2s_port;
+};
+
 #define LPASS_PLATFORM_BUFFER_SIZE	(16 * 1024)
 #define LPASS_PLATFORM_PERIODS		2
 
@@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	snd_pcm_format_t format = params_format(params);
 	unsigned int channels = params_channels(params);
 	unsigned int regval;
 	int bitwidth;
-	int ret;
+	int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
 
 	bitwidth = snd_pcm_format_width(format);
 	if (bitwidth < 0) {
@@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
-			LPAIF_RDMACTL_AUDINTF_MI2S |
+			LPAIF_RDMACTL_AUDINTF(rdma_port) |
 			LPAIF_RDMACTL_FIFOWM_8;
 
 	switch (bitwidth) {
@@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
+			LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
@@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	int ret;
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+			LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
 	if (ret)
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
@@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	int ret;
+	struct lpass_variant *v = drvdata->variant;
+	int ret, ch = pcm_data->rdma_ch;
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMABASE_REG(v, ch),
 			runtime->dma_addr);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMABUFF_REG(v, ch),
 			(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMAPER_REG(v, ch),
 			(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 
 	ret = regmap_update_bits(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMACTL_REG(v, ch),
 			LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
@@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		int cmd)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	int ret;
+	struct lpass_variant *v = drvdata->variant;
+	int ret, ch = pcm_data->rdma_ch;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
@@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		/* clear status before enabling interrupts */
 		ret = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch));
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, ret);
@@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch),
+				LPAIF_IRQ_ALL(ch));
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 					__func__, ret);
@@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_REG(v, ch),
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_ON);
 		if (ret) {
@@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_REG(v, ch),
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_OFF);
 		if (ret) {
@@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
+				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch), 0);
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 					__func__, ret);
@@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 		struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 			snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	unsigned int base_addr, curr_addr;
-	int ret;
+	int ret, ch = pcm_data->rdma_ch;
 
 	ret = regmap_read(drvdata->lpaif_map,
-			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
+			LPAIF_RDMABASE_REG(v, ch), &base_addr);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
 				__func__, ret);
@@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 	}
 
 	ret = regmap_read(drvdata->lpaif_map,
-			LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
+			LPAIF_RDMACURR_REG(v, ch), &curr_addr);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
 				__func__, ret);
@@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
 	.mmap		= lpass_platform_pcmops_mmap,
 };
 
-static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+static irqreturn_t lpass_dma_interrupt_handler(
+			struct snd_pcm_substream *substream,
+			struct lpass_data *drvdata,
+			int chan, u32 interrupts)
 {
-	struct snd_pcm_substream *substream = data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-	struct lpass_data *drvdata =
-		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	unsigned int interrupts;
+	struct lpass_variant *v = drvdata->variant;
 	irqreturn_t ret = IRQ_NONE;
 	int rv;
 
-	rv = regmap_read(drvdata->lpaif_map,
-			LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
-	if (rv) {
-		dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
-				__func__, rv);
-		return IRQ_NONE;
-	}
-	interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
-
-	if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_PER(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_PER(chan));
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
@@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
-	if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_XRUN(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_XRUN(chan));
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
@@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
-	if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_ERR(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ERR(chan));
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
@@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 	return ret;
 }
 
+static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+{
+	struct lpass_data *drvdata = data;
+	struct lpass_variant *v = drvdata->variant;
+	unsigned int irqs;
+	int rv, chan;
+
+	rv = regmap_read(drvdata->lpaif_map,
+			LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+	if (rv) {
+		pr_err("%s() error reading from irqstat reg: %d\n",
+				__func__, rv);
+		return IRQ_NONE;
+	}
+
+	/* Handle per channel interrupts */
+	for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
+		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
+			rv = lpass_dma_interrupt_handler(
+						drvdata->substream[chan],
+						drvdata, chan, irqs);
+			if (rv != IRQ_HANDLED)
+				return rv;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
 		struct snd_soc_pcm_runtime *soc_runtime)
 {
@@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 	struct snd_pcm *pcm = soc_runtime->pcm;
 	struct snd_pcm_substream *substream =
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	int ret;
+	struct lpass_pcm_data *data;
+
+	data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (v->alloc_dma_channel)
+		data->rdma_ch = v->alloc_dma_channel(drvdata);
+
+	if (IS_ERR_VALUE(data->rdma_ch))
+		return data->rdma_ch;
+
+	drvdata->substream[data->rdma_ch] = substream;
+	data->i2s_port = cpu_dai->driver->id;
+
+	snd_soc_pcm_set_drvdata(soc_runtime, data);
 
 	soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 	soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
@@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 	if (ret)
 		return ret;
 
-	ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
-			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
-			"lpass-irq-lpaif", substream);
-	if (ret) {
-		dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
-				__func__, ret);
-		goto err_buf;
-	}
-
-	/* ensure audio hardware is disabled */
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
-	if (ret) {
-		dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
-				__func__, ret);
-		return ret;
-	}
-	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+			LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
-		return ret;
+		goto err_buf;
 	}
 
 	return 0;
@@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
 	struct snd_pcm_substream *substream =
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
+	struct lpass_variant *v = drvdata->variant;
+
+	drvdata->substream[data->rdma_ch] = NULL;
+
+	if (v->free_dma_channel)
+		v->free_dma_channel(drvdata, data->rdma_ch);
 
 	lpass_platform_free_buffer(substream, soc_runtime);
 }
@@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
 int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct lpass_variant *v = drvdata->variant;
+	int ret;
 
 	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 	if (drvdata->lpaif_irq < 0) {
@@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
+	/* ensure audio hardware is disabled */
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
+			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
+			"lpass-irq-lpaif", drvdata);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() irq request failed: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+
 	return devm_snd_soc_register_platform(&pdev->dev,
 			&lpass_platform_driver);
 }
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 5c99b3dace86..d6e86c119e74 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -22,6 +22,8 @@
 #include <linux/regmap.h>
 
 #define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
+#define LPASS_MAX_MI2S_PORTS			(8)
+#define LPASS_MAX_DMA_CHANNELS			(8)
 
 /* Both the CPU DAI and platform drivers will access this data */
 struct lpass_data {
@@ -30,10 +32,10 @@ struct lpass_data {
 	struct clk *ahbix_clk;
 
 	/* MI2S system clock */
-	struct clk *mi2s_osr_clk;
+	struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS];
 
 	/* MI2S bit clock (derived from system clock by a divider */
-	struct clk *mi2s_bit_clk;
+	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
 
 	/* low-power audio interface (LPAIF) registers */
 	void __iomem *lpaif;
@@ -43,9 +45,54 @@ struct lpass_data {
 
 	/* interrupts from the low-power audio interface (LPAIF) */
 	int lpaif_irq;
+
+	/* SOC specific variations in the LPASS IP integration */
+	struct lpass_variant *variant;
+
+	/* bit map to keep track of static channel allocations */
+	unsigned long rdma_ch_bit_map;
+
+	/* used it for handling interrupt per dma channel */
+	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
+
+	/* 8016 specific */
+	struct clk *pcnoc_mport_clk;
+	struct clk *pcnoc_sway_clk;
+};
+
+/* Vairant data per each SOC */
+struct lpass_variant {
+	u32	i2sctrl_reg_base;
+	u32	i2sctrl_reg_stride;
+	u32	i2s_ports;
+	u32	irq_reg_base;
+	u32	irq_reg_stride;
+	u32	irq_ports;
+	u32	rdma_reg_base;
+	u32	rdma_reg_stride;
+	u32	rdma_channels;
+
+	/**
+	 * on SOCs like APQ8016 the channel control bits start
+	 * at different offset to ipq806x
+	 **/
+	u32	rdmactl_audif_start;
+	/* SOC specific intialization like clocks */
+	int (*init)(struct platform_device *pdev);
+	int (*exit)(struct platform_device *pdev);
+	int (*alloc_dma_channel)(struct lpass_data *data);
+	int (*free_dma_channel)(struct lpass_data *data, int ch);
+
+	/* SOC specific dais */
+	struct snd_soc_dai_driver *dai_driver;
+	int num_dai;
 };
 
 /* register the platform driver from the CPU DAI driver */
 int asoc_qcom_lpass_platform_register(struct platform_device *);
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
+extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
 
 #endif /* __LPASS_H__ */
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c
index b8bd296190ad..2d833bffdba0 100644
--- a/sound/soc/qcom/storm.c
+++ b/sound/soc/qcom/storm.c
@@ -69,11 +69,6 @@ static struct snd_soc_dai_link storm_dai_link = {
 	.ops		= &storm_soc_ops,
 };
 
-static struct snd_soc_card storm_soc_card = {
-	.name	= "ipq806x-storm",
-	.dev	= NULL,
-};
-
 static int storm_parse_of(struct snd_soc_card *card)
 {
 	struct snd_soc_dai_link *dai_link = card->dai_link;
@@ -99,14 +94,13 @@ static int storm_parse_of(struct snd_soc_card *card)
 
 static int storm_platform_probe(struct platform_device *pdev)
 {
-	struct snd_soc_card *card = &storm_soc_card;
+	struct snd_soc_card *card;
 	int ret;
 
-	if (card->dev) {
-		dev_err(&pdev->dev, "%s() error, existing soundcard\n",
-				__func__);
-		return -ENODEV;
-	}
+	card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return -ENOMEM;
+
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
 
@@ -128,16 +122,12 @@ static int storm_platform_probe(struct platform_device *pdev)
 	}
 
 	ret = devm_snd_soc_register_card(&pdev->dev, card);
-	if (ret == -EPROBE_DEFER) {
-		card->dev = NULL;
-		return ret;
-	} else if (ret) {
+	if (ret)
 		dev_err(&pdev->dev, "%s() error registering soundcard: %d\n",
 				__func__, ret);
-		return ret;
-	}
 
-	return 0;
+	return ret;
+
 }
 
 #ifdef CONFIG_OF