diff mbox

[6/8] ASoC: AMD: add AMD ASoC ACP 2.x DMA driver

Message ID 1450897275-17152-6-git-send-email-alexander.deucher@amd.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Deucher Dec. 23, 2015, 7:01 p.m. UTC
From: Maruthi Srinivas Bayyavarapu <Maruthi.Bayyavarapu@amd.com>

ACP IP has internal DMA controller with multiple channels which
can be programmed in cyclic/non cyclic manner. ACP can generate
interrupt upon completion of DMA transfer, if required.
The PCM driver provides the platform DMA component to ALSA core.

Signed-off-by: Maruthi Bayyavarapu <maruthi.bayyavarapu@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Reviewed-by: Murali Krishna Vemuri <murali-krishna.vemuri@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
---

v2: Changes
    1. Squash ACP DMA abstraction and DMA patches together
    2. Fix IRQ_NONE case in dma_irq_handler
    3. Replace ternery operations
    4. Add loop timeout

 sound/soc/Kconfig           |   1 +
 sound/soc/Makefile          |   1 +
 sound/soc/amd/Kconfig       |   4 +
 sound/soc/amd/Makefile      |   3 +
 sound/soc/amd/acp-pcm-dma.c | 924 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/amd/acp.h         | 119 ++++++
 6 files changed, 1052 insertions(+)
 create mode 100644 sound/soc/amd/Kconfig
 create mode 100644 sound/soc/amd/Makefile
 create mode 100644 sound/soc/amd/acp-pcm-dma.c
 create mode 100644 sound/soc/amd/acp.h

Comments

Mark Brown Jan. 5, 2016, 6:43 p.m. UTC | #1
On Wed, Dec 23, 2015 at 02:01:13PM -0500, Alex Deucher wrote:

> --- /dev/null
> +++ b/sound/soc/amd/Kconfig
> @@ -0,0 +1,4 @@
> +config SND_SOC_AMD_ACP
> +	tristate "AMD Audio Coprocessor support"
> +	help
> +	 This option enables ACP DMA support on AMD platform.

This has no dependencies?

> +
> +	/* Designware I2S driver requries proper capabilities
> +	 * from mmACP_I2SMICSP_COMP_PARAM_1 register. The register
> +	 * reports playback and capture capabilities though the
> +	 * MIC instance of DW I2S controller supports capture only
> +	 * Provide a workaround by masking the capability into a
> +	 * scratch register and provide scratch register offset as
> +	 * though it is mmACP_I2SMICSP_COMP_PARAM_1
> +	 */
> +
> +	val = acp_reg_read(acp_mmio, mmACP_I2SMICSP_COMP_PARAM_1);
> +	val = val & ~BIT(5);
> +	acp_reg_write(val, acp_mmio, mmACP_SCRATCH_REG_0);

Ugh, right.  So the hardware doesn't actually have the register moved at
all.  Why are we doing this, if the capabilities really are buggy the
more idiomatic thing would be to provide an override for the
capabilities via platform data?  Requiring some other driver to poke the
hardware to set the capabilities is a very roundabout way to deal with
things.

We should probably revert that quirk unless I'm missing something here...

> +++ b/sound/soc/amd/acp.h
> @@ -0,0 +1,119 @@
> +#ifndef __ACP_HW_H
> +#define __ACP_HW_H
> +
> +#include "include/acp_2_2_d.h"
> +#include "include/acp_2_2_sh_mask.h"

I can't find these headers anywhere in the kernel tree or earlier in
this patch series, this will break the build.  The fact that they've got
include in the filename is also a bit interesting...

> +#define PAGE_SIZE_4K				4096

SZ_4K exists for this.

> +#define PAGE_SIZE_4K_ENABLE			0x02
> +
> +#define PLAYBACK_PTE_OFFSET			10
> +#define CAPTURE_PTE_OFFSET			0

These defines could all use namespacing, as could some of the others
later than don't mention ACP.
Mark Brown Jan. 5, 2016, 6:51 p.m. UTC | #2
On Tue, Jan 05, 2016 at 06:43:43PM +0000, Mark Brown wrote:
> On Wed, Dec 23, 2015 at 02:01:13PM -0500, Alex Deucher wrote:

> > +#include "include/acp_2_2_d.h"
> > +#include "include/acp_2_2_sh_mask.h"

> I can't find these headers anywhere in the kernel tree or earlier in
> this patch series, this will break the build.  The fact that they've got
> include in the filename is also a bit interesting...

Ugh, your cover letter says (after pages of changelog) that they're in a
patch 5 which you didn't send :(  Please don't do this, I can't apply or
review code that isn't sent to me.
Alex Deucher Jan. 8, 2016, 11:03 p.m. UTC | #3
On Tue, Jan 5, 2016 at 1:43 PM, Mark Brown <broonie@kernel.org> wrote:
> On Wed, Dec 23, 2015 at 02:01:13PM -0500, Alex Deucher wrote:
>
>> --- /dev/null
>> +++ b/sound/soc/amd/Kconfig
>> @@ -0,0 +1,4 @@
>> +config SND_SOC_AMD_ACP
>> +     tristate "AMD Audio Coprocessor support"
>> +     help
>> +      This option enables ACP DMA support on AMD platform.
>
> This has no dependencies?

No dependencies.  The ACP device discovery is triggered from GPU
driver, but the ACP driver has no direct dependencies on it.
Theoretically it could act as a standalone driver.

Alex

>
>> +
>> +     /* Designware I2S driver requries proper capabilities
>> +      * from mmACP_I2SMICSP_COMP_PARAM_1 register. The register
>> +      * reports playback and capture capabilities though the
>> +      * MIC instance of DW I2S controller supports capture only
>> +      * Provide a workaround by masking the capability into a
>> +      * scratch register and provide scratch register offset as
>> +      * though it is mmACP_I2SMICSP_COMP_PARAM_1
>> +      */
>> +
>> +     val = acp_reg_read(acp_mmio, mmACP_I2SMICSP_COMP_PARAM_1);
>> +     val = val & ~BIT(5);
>> +     acp_reg_write(val, acp_mmio, mmACP_SCRATCH_REG_0);
>
> Ugh, right.  So the hardware doesn't actually have the register moved at
> all.  Why are we doing this, if the capabilities really are buggy the
> more idiomatic thing would be to provide an override for the
> capabilities via platform data?  Requiring some other driver to poke the
> hardware to set the capabilities is a very roundabout way to deal with
> things.
>
> We should probably revert that quirk unless I'm missing something here...
>
>> +++ b/sound/soc/amd/acp.h
>> @@ -0,0 +1,119 @@
>> +#ifndef __ACP_HW_H
>> +#define __ACP_HW_H
>> +
>> +#include "include/acp_2_2_d.h"
>> +#include "include/acp_2_2_sh_mask.h"
>
> I can't find these headers anywhere in the kernel tree or earlier in
> this patch series, this will break the build.  The fact that they've got
> include in the filename is also a bit interesting...
>
>> +#define PAGE_SIZE_4K                         4096
>
> SZ_4K exists for this.
>
>> +#define PAGE_SIZE_4K_ENABLE                  0x02
>> +
>> +#define PLAYBACK_PTE_OFFSET                  10
>> +#define CAPTURE_PTE_OFFSET                   0
>
> These defines could all use namespacing, as could some of the others
> later than don't mention ACP.
Mark Brown Jan. 9, 2016, 7:41 p.m. UTC | #4
On Fri, Jan 08, 2016 at 06:03:19PM -0500, Alex Deucher wrote:
> On Tue, Jan 5, 2016 at 1:43 PM, Mark Brown <broonie@kernel.org> wrote:
> > On Wed, Dec 23, 2015 at 02:01:13PM -0500, Alex Deucher wrote:

> >> --- /dev/null
> >> +++ b/sound/soc/amd/Kconfig
> >> @@ -0,0 +1,4 @@
> >> +config SND_SOC_AMD_ACP
> >> +     tristate "AMD Audio Coprocessor support"
> >> +     help
> >> +      This option enables ACP DMA support on AMD platform.

> > This has no dependencies?

> No dependencies.  The ACP device discovery is triggered from GPU
> driver, but the ACP driver has no direct dependencies on it.
> Theoretically it could act as a standalone driver.

In that case there's no need to merge everything through the DRM tree,
each bit can go in separately.  That makes life a bit easier process
wise, people will need to merge the two to run things but that's fine.
Alex Deucher Jan. 9, 2016, 7:53 p.m. UTC | #5
On Sat, Jan 9, 2016 at 2:41 PM, Mark Brown <broonie@kernel.org> wrote:
> On Fri, Jan 08, 2016 at 06:03:19PM -0500, Alex Deucher wrote:
>> On Tue, Jan 5, 2016 at 1:43 PM, Mark Brown <broonie@kernel.org> wrote:
>> > On Wed, Dec 23, 2015 at 02:01:13PM -0500, Alex Deucher wrote:
>
>> >> --- /dev/null
>> >> +++ b/sound/soc/amd/Kconfig
>> >> @@ -0,0 +1,4 @@
>> >> +config SND_SOC_AMD_ACP
>> >> +     tristate "AMD Audio Coprocessor support"
>> >> +     help
>> >> +      This option enables ACP DMA support on AMD platform.
>
>> > This has no dependencies?
>
>> No dependencies.  The ACP device discovery is triggered from GPU
>> driver, but the ACP driver has no direct dependencies on it.
>> Theoretically it could act as a standalone driver.
>
> In that case there's no need to merge everything through the DRM tree,
> each bit can go in separately.  That makes life a bit easier process
> wise, people will need to merge the two to run things but that's fine.

Sounds good.  Thanks!

Alex
diff mbox

Patch

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7ff7d88..a34e9e9 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -38,6 +38,7 @@  config SND_SOC_TOPOLOGY
 
 # All the supported SoCs
 source "sound/soc/adi/Kconfig"
+source "sound/soc/amd/Kconfig"
 source "sound/soc/atmel/Kconfig"
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/bcm/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8eb06db..a79a4c7 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -18,6 +18,7 @@  obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/
 obj-$(CONFIG_SND_SOC)	+= generic/
 obj-$(CONFIG_SND_SOC)	+= adi/
+obj-$(CONFIG_SND_SOC)	+= amd/
 obj-$(CONFIG_SND_SOC)	+= atmel/
 obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= bcm/
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
new file mode 100644
index 0000000..78187eb
--- /dev/null
+++ b/sound/soc/amd/Kconfig
@@ -0,0 +1,4 @@ 
+config SND_SOC_AMD_ACP
+	tristate "AMD Audio Coprocessor support"
+	help
+	 This option enables ACP DMA support on AMD platform.
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
new file mode 100644
index 0000000..1a66ec0
--- /dev/null
+++ b/sound/soc/amd/Makefile
@@ -0,0 +1,3 @@ 
+snd-soc-acp-pcm-objs	:= acp-pcm-dma.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
new file mode 100644
index 0000000..0494ac8
--- /dev/null
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -0,0 +1,924 @@ 
+/*
+ * AMD ALSA SoC PCM Driver for ACP 2.x
+ *
+ * Copyright 2014-2015 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/module.h>
+#include <linux/delay.h>
+
+#include <sound/soc.h>
+
+#include "acp.h"
+
+#define PLAYBACK_MIN_NUM_PERIODS    2
+#define PLAYBACK_MAX_NUM_PERIODS    2
+#define PLAYBACK_MAX_PERIOD_SIZE    16384
+#define PLAYBACK_MIN_PERIOD_SIZE    1024
+#define CAPTURE_MIN_NUM_PERIODS     2
+#define CAPTURE_MAX_NUM_PERIODS     2
+#define CAPTURE_MAX_PERIOD_SIZE     16384
+#define CAPTURE_MIN_PERIOD_SIZE     1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |
+		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 1,
+	.channels_max = 8,
+	.rates = SNDRV_PCM_RATE_8000_96000,
+	.rate_min = 8000,
+	.rate_max = 96000,
+	.buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE,
+	.period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE,
+	.period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE,
+	.periods_min = PLAYBACK_MIN_NUM_PERIODS,
+	.periods_max = PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp_pcm_hardware_capture = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH |
+	    SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |
+		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 1,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_8000_48000,
+	.rate_min = 8000,
+	.rate_max = 48000,
+	.buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min = CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max = CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min = CAPTURE_MIN_NUM_PERIODS,
+	.periods_max = CAPTURE_MAX_NUM_PERIODS,
+};
+
+struct audio_drv_data {
+	struct snd_pcm_substream *play_stream;
+	struct snd_pcm_substream *capture_stream;
+	void __iomem *acp_mmio;
+};
+
+static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg)
+{
+	return readl(acp_mmio + (reg * 4));
+}
+
+static void acp_reg_write(u32 val, void __iomem *acp_mmio, u32 reg)
+{
+	writel(val, acp_mmio + (reg * 4));
+}
+
+/* Configure a given dma channel parameters - enable/disble,
+ * number of descriptors, priority
+ */
+static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num,
+				   u16 dscr_strt_idx, u16 num_dscrs,
+				   enum acp_dma_priority_level priority_level)
+{
+	u32 dma_ctrl;
+
+	/* disable the channel run field */
+	dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRun_MASK;
+	acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+
+	/* program a DMA channel with first descriptor to be processed. */
+	acp_reg_write((ACP_DMA_DSCR_STRT_IDX_0__DMAChDscrStrtIdx_MASK
+			& dscr_strt_idx),
+			acp_mmio, mmACP_DMA_DSCR_STRT_IDX_0 + ch_num);
+
+	/* program a DMA channel with the number of descriptors to be
+	 * processed in the transfer
+	*/
+	acp_reg_write(ACP_DMA_DSCR_CNT_0__DMAChDscrCnt_MASK & num_dscrs,
+		acp_mmio, mmACP_DMA_DSCR_CNT_0 + ch_num);
+
+	/* set DMA channel priority */
+	acp_reg_write(priority_level, acp_mmio, mmACP_DMA_PRIO_0 + ch_num);
+}
+
+/* Initialize a dma descriptor in SRAM based on descritor information passed */
+static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
+					  u16 descr_idx,
+					  acp_dma_dscr_transfer_t *descr_info)
+{
+	u32 sram_offset;
+
+	sram_offset = (descr_idx * sizeof(acp_dma_dscr_transfer_t));
+
+	/* program the source base address. */
+	acp_reg_write(sram_offset, acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+	acp_reg_write(descr_info->src,	acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+	/* program the destination base address. */
+	acp_reg_write(sram_offset + 4,	acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+	acp_reg_write(descr_info->dest, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+
+	/* program the number of bytes to be transferred for this descriptor. */
+	acp_reg_write(sram_offset + 8,	acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+	acp_reg_write(descr_info->xfer_val, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+}
+
+/* Initialize the DMA descriptor information for transfer between
+ * system memory <-> ACP SRAM
+ */
+static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio,
+					   u32 size, int direction,
+					   u32 pte_offset)
+{
+	u16 i;
+	u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
+	acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL];
+
+	for (i = 0; i < NUM_DSCRS_PER_CHANNEL; i++) {
+		dmadscr[i].xfer_val = 0;
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i;
+			dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS +
+					(size / 2) - (i * (size/2));
+			dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
+				+ (pte_offset * PAGE_SIZE_4K) + (i * (size/2));
+			dmadscr[i].xfer_val |=
+			(ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) |
+			(size / 2);
+		} else {
+			dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i;
+			dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS +
+					(i * (size/2));
+			dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS
+						+ (pte_offset * PAGE_SIZE_4K) +
+						(i * (size/2));
+			dmadscr[i].xfer_val |=
+			BIT(22) |
+			(ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) |
+			(size / 2);
+		}
+		config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
+						&dmadscr[i]);
+	}
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM,
+					PLAYBACK_START_DMA_DESCR_CH12,
+					NUM_DSCRS_PER_CHANNEL,
+					ACP_DMA_PRIORITY_LEVEL_NORMAL);
+	else
+		config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM,
+					CAPTURE_START_DMA_DESCR_CH14,
+					NUM_DSCRS_PER_CHANNEL,
+					ACP_DMA_PRIORITY_LEVEL_NORMAL);
+}
+
+/* Initialize the DMA descriptor information for transfer between
+ * ACP SRAM <-> I2S
+ */
+static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio,
+					   u32 size, int direction)
+{
+
+	u16 i;
+	u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH13;
+	acp_dma_dscr_transfer_t dmadscr[NUM_DSCRS_PER_CHANNEL];
+
+	for (i = 0; i < NUM_DSCRS_PER_CHANNEL; i++) {
+		dmadscr[i].xfer_val = 0;
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH13 + i;
+			dmadscr[i].src = ACP_SHARED_RAM_BANK_1_ADDRESS +
+					 (i * (size/2));
+			/* dmadscr[i].dest is unused by hardware. */
+			dmadscr[i].dest = 0;
+			dmadscr[i].xfer_val |= BIT(22) | (TO_ACP_I2S_1 << 16) |
+						(size / 2);
+		} else {
+			dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i;
+			/* dmadscr[i].src is unused by hardware. */
+			dmadscr[i].src = 0;
+			dmadscr[i].dest = ACP_SHARED_RAM_BANK_5_ADDRESS +
+					(i * (size / 2));
+			dmadscr[i].xfer_val |= BIT(22) |
+					(FROM_ACP_I2S_1 << 16) | (size / 2);
+		}
+		config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx,
+						&dmadscr[i]);
+	}
+	/* Configure the DMA channel with the above descriptore */
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		config_acp_dma_channel(acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
+					PLAYBACK_START_DMA_DESCR_CH13,
+					NUM_DSCRS_PER_CHANNEL,
+					ACP_DMA_PRIORITY_LEVEL_NORMAL);
+	else
+		config_acp_dma_channel(acp_mmio, I2S_TO_ACP_DMA_CH_NUM,
+					CAPTURE_START_DMA_DESCR_CH15,
+					NUM_DSCRS_PER_CHANNEL,
+					ACP_DMA_PRIORITY_LEVEL_NORMAL);
+}
+
+/* Create page table entries in ACP SRAM for the allocated memory */
+static void acp_pte_config(void __iomem *acp_mmio, struct page *pg,
+			   u16 num_of_pages, u32 pte_offset)
+{
+	u16 page_idx;
+	u64 addr;
+	u32 low;
+	u32 high;
+	u32 offset;
+
+	offset	= ACP_DAGB_GRP_SRBM_SRAM_BASE_OFFSET + (pte_offset * 8);
+	for (page_idx = 0; page_idx < (num_of_pages); page_idx++) {
+		/* Load the low address of page int ACP SRAM through SRBM */
+		acp_reg_write((offset + (page_idx * 8)),
+			acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+		addr = page_to_phys(pg);
+
+		low = lower_32_bits(addr);
+		high = upper_32_bits(addr);
+
+		acp_reg_write(low, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+
+		/* Load the High address of page int ACP SRAM through SRBM */
+		acp_reg_write((offset + (page_idx * 8) + 4),
+			acp_mmio, mmACP_SRBM_Targ_Idx_Addr);
+
+		/* page enable in ACP */
+		high |= BIT(31);
+		acp_reg_write(high, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
+
+		/* Move to next physically contiguos page */
+		pg++;
+	}
+}
+
+static void config_acp_dma(void __iomem *acp_mmio,
+			   struct audio_substream_data *audio_config)
+{
+	u32 pte_offset;
+
+	if (audio_config->direction == SNDRV_PCM_STREAM_PLAYBACK)
+		pte_offset = PLAYBACK_PTE_OFFSET;
+	else
+		pte_offset = CAPTURE_PTE_OFFSET;
+
+	acp_pte_config(acp_mmio, audio_config->pg, audio_config->num_of_pages,
+			pte_offset);
+
+	/* Configure System memory <-> ACP SRAM DMA descriptors */
+	set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size,
+				       audio_config->direction, pte_offset);
+
+	/* Configure ACP SRAM <-> I2S DMA descriptors */
+	set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size,
+					audio_config->direction);
+}
+
+/* Start a given DMA channel transfer */
+static void acp_dma_start(void __iomem *acp_mmio,
+			 u16 ch_num, bool is_circular)
+{
+	u32 dma_ctrl;
+
+	/* read the dma control register and disable the channel run field */
+	dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+
+	/* Invalidating the DAGB cache */
+	acp_reg_write(1, acp_mmio, mmACP_DAGB_ATU_CTRL);
+
+	/* configure the DMA channel and start the DMA transfer
+	 * set dmachrun bit to start the transfer and enable the
+	 * interrupt on completion of the dma transfer
+	 */
+	dma_ctrl |= ACP_DMA_CNTL_0__DMAChRun_MASK;
+
+	switch (ch_num) {
+	case ACP_TO_I2S_DMA_CH_NUM:
+	case ACP_TO_SYSRAM_CH_NUM:
+	case I2S_TO_ACP_DMA_CH_NUM:
+		dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
+		break;
+	default:
+		dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
+		break;
+	}
+
+	/* enable  for ACP SRAM to/from I2S DMA channel */
+	if (is_circular == true)
+		dma_ctrl |= ACP_DMA_CNTL_0__Circular_DMA_En_MASK;
+	else
+		dma_ctrl &= ~ACP_DMA_CNTL_0__Circular_DMA_En_MASK;
+
+	acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+}
+
+/* Stop a given DMA channel transfer */
+static int acp_dma_stop(void __iomem *acp_mmio, u8 ch_num)
+{
+	u32 dma_ctrl;
+	u32 dma_ch_sts;
+	u32 count = ACP_DMA_RESET_TIME;
+
+	dma_ctrl = acp_reg_read(acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+
+	/* clear the dma control register fields before writing zero
+	 * in reset bit
+	*/
+	dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRun_MASK;
+	dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
+
+	acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS);
+
+	if (dma_ch_sts & BIT(ch_num)) {
+		/* set the reset bit for this channel to stop the dma
+		*  transfer
+		*/
+		dma_ctrl |= ACP_DMA_CNTL_0__DMAChRst_MASK;
+		acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0 + ch_num);
+	}
+
+	/* check the channel status bit for some time and return the status */
+	while (true) {
+		dma_ch_sts = acp_reg_read(acp_mmio, mmACP_DMA_CH_STS);
+		if (!(dma_ch_sts & BIT(ch_num))) {
+			/* clear the reset flag after successfully stopping
+			* the dma transfer and break from the loop
+			*/
+			dma_ctrl &= ~ACP_DMA_CNTL_0__DMAChRst_MASK;
+
+			acp_reg_write(dma_ctrl, acp_mmio, mmACP_DMA_CNTL_0
+								+ ch_num);
+			break;
+		}
+		if (--count == 0) {
+			pr_err("Failed to stop ACP DMA channel : %d\n", ch_num);
+			return -ETIMEDOUT;
+		}
+		udelay(100);
+	}
+	return 0;
+}
+
+/* Initialize and bring ACP hardware to default state. */
+static int acp_init(void __iomem *acp_mmio)
+{
+	u32 val, count, sram_pte_offset;
+
+	/* Assert Soft reset of ACP */
+	val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+
+	val |= ACP_SOFT_RESET__SoftResetAud_MASK;
+	acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
+
+	count = ACP_SOFT_RESET_DONE_TIME_OUT_VALUE;
+	while (true) {
+		val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+		if (ACP_SOFT_RESET__SoftResetAudDone_MASK ==
+		    (val & ACP_SOFT_RESET__SoftResetAudDone_MASK))
+			break;
+		if (--count == 0) {
+			pr_err("Failed to reset ACP\n");
+			return -ETIMEDOUT;
+		}
+		udelay(100);
+	}
+
+	/* Enable clock to ACP and wait until the clock is enabled */
+	val = acp_reg_read(acp_mmio, mmACP_CONTROL);
+	val = val | ACP_CONTROL__ClkEn_MASK;
+	acp_reg_write(val, acp_mmio, mmACP_CONTROL);
+
+	count = ACP_CLOCK_EN_TIME_OUT_VALUE;
+
+	while (true) {
+		val = acp_reg_read(acp_mmio, mmACP_STATUS);
+		if (val & (u32) 0x1)
+			break;
+		if (--count == 0) {
+			pr_err("Failed to reset ACP\n");
+			return -ETIMEDOUT;
+		}
+		udelay(100);
+	}
+
+	/* Deassert the SOFT RESET flags */
+	val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+	val &= ~ACP_SOFT_RESET__SoftResetAud_MASK;
+	acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
+
+	/* initiailize Onion control DAGB register */
+	acp_reg_write(ONION_CNTL_DEFAULT, acp_mmio, mmACP_AXI2DAGB_ONION_CNTL);
+
+	/* initiailize Garlic control DAGB registers */
+	acp_reg_write(GARLIC_CNTL_DEFAULT, acp_mmio,
+			mmACP_AXI2DAGB_GARLIC_CNTL);
+
+	sram_pte_offset = ACP_DAGB_GRP_SRAM_BASE_ADDRESS |
+			ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBSnoopSel_MASK |
+			ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBTargetMemSel_MASK |
+			ACP_DAGB_BASE_ADDR_GRP_1__AXI2DAGBGrpEnable_MASK;
+	acp_reg_write(sram_pte_offset,  acp_mmio, mmACP_DAGB_BASE_ADDR_GRP_1);
+	acp_reg_write(PAGE_SIZE_4K_ENABLE, acp_mmio,
+			mmACP_DAGB_PAGE_SIZE_GRP_1);
+
+	acp_reg_write(ACP_SRAM_BASE_ADDRESS, acp_mmio,
+			mmACP_DMA_DESC_BASE_ADDR);
+
+	/* Num of descriptiors in SRAM 0x4, means 256 descriptors;(64 * 4) */
+	acp_reg_write(0x4, acp_mmio, mmACP_DMA_DESC_MAX_NUM_DSCR);
+	acp_reg_write(ACP_EXTERNAL_INTR_CNTL__DMAIOCMask_MASK,
+		acp_mmio, mmACP_EXTERNAL_INTR_CNTL);
+
+	/* Designware I2S driver requries proper capabilities
+	 * from mmACP_I2SMICSP_COMP_PARAM_1 register. The register
+	 * reports playback and capture capabilities though the
+	 * MIC instance of DW I2S controller supports capture only
+	 * Provide a workaround by masking the capability into a
+	 * scratch register and provide scratch register offset as
+	 * though it is mmACP_I2SMICSP_COMP_PARAM_1
+	 */
+
+	val = acp_reg_read(acp_mmio, mmACP_I2SMICSP_COMP_PARAM_1);
+	val = val & ~BIT(5);
+	acp_reg_write(val, acp_mmio, mmACP_SCRATCH_REG_0);
+	return 0;
+}
+
+/* Deintialize ACP */
+static int acp_deinit(void __iomem *acp_mmio)
+{
+	u32 val;
+	u32 count;
+
+	/* Assert Soft reset of ACP */
+	val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+
+	val |= ACP_SOFT_RESET__SoftResetAud_MASK;
+	acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
+
+	count = ACP_SOFT_RESET_DONE_TIME_OUT_VALUE;
+	while (true) {
+		val = acp_reg_read(acp_mmio, mmACP_SOFT_RESET);
+		if (ACP_SOFT_RESET__SoftResetAudDone_MASK ==
+		    (val & ACP_SOFT_RESET__SoftResetAudDone_MASK))
+			break;
+		if (--count == 0) {
+			pr_err("Failed to reset ACP\n");
+			return -ETIMEDOUT;
+		}
+		udelay(100);
+	}
+	/** Disable ACP clock */
+	val = acp_reg_read(acp_mmio, mmACP_CONTROL);
+	val &= ~ACP_CONTROL__ClkEn_MASK;
+	acp_reg_write(val, acp_mmio, mmACP_CONTROL);
+
+	count = ACP_CLOCK_EN_TIME_OUT_VALUE;
+
+	while (true) {
+		val = acp_reg_read(acp_mmio, mmACP_STATUS);
+		if (!(val & (u32) 0x1))
+			break;
+		if (--count == 0) {
+			pr_err("Failed to reset ACP\n");
+			return -ETIMEDOUT;
+		}
+		udelay(100);
+	}
+	return 0;
+}
+
+/* ACP DMA irq handler routine for playback, capture usecases */
+static irqreturn_t dma_irq_handler(int irq, void *arg)
+{
+	u16 dscr_idx;
+	u32 intr_flag, ext_intr_status;
+	struct audio_drv_data *irq_data;
+	void __iomem *acp_mmio;
+	struct device *dev = arg;
+	bool valid_irq = false;
+
+	irq_data = dev_get_drvdata(dev);
+	acp_mmio = irq_data->acp_mmio;
+
+	ext_intr_status = acp_reg_read(acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+	intr_flag = (((ext_intr_status &
+		      ACP_EXTERNAL_INTR_STAT__DMAIOCStat_MASK) >>
+		     ACP_EXTERNAL_INTR_STAT__DMAIOCStat__SHIFT));
+
+	if ((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) != 0) {
+		valid_irq = true;
+		if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) ==
+				PLAYBACK_START_DMA_DESCR_CH13)
+			dscr_idx = PLAYBACK_START_DMA_DESCR_CH12;
+		else
+			dscr_idx = PLAYBACK_END_DMA_DESCR_CH12;
+		config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx,
+				       1, 0);
+		acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
+
+		snd_pcm_period_elapsed(irq_data->play_stream);
+
+		acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_CH_NUM)) << 16,
+				acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+	}
+
+	if ((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) != 0) {
+		valid_irq = true;
+		if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_15) ==
+				CAPTURE_START_DMA_DESCR_CH15)
+			dscr_idx = CAPTURE_END_DMA_DESCR_CH14;
+		else
+			dscr_idx = CAPTURE_START_DMA_DESCR_CH14;
+		config_acp_dma_channel(acp_mmio, ACP_TO_SYSRAM_CH_NUM, dscr_idx,
+				       1, 0);
+		acp_dma_start(acp_mmio, ACP_TO_SYSRAM_CH_NUM, false);
+
+		acp_reg_write((intr_flag & BIT(I2S_TO_ACP_DMA_CH_NUM)) << 16,
+				acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+	}
+
+	if ((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) != 0) {
+		valid_irq = true;
+		snd_pcm_period_elapsed(irq_data->capture_stream);
+		acp_reg_write((intr_flag & BIT(ACP_TO_SYSRAM_CH_NUM)) << 16,
+				acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+	}
+
+	if (valid_irq)
+		return IRQ_HANDLED;
+	else
+		return IRQ_NONE;
+}
+
+static int acp_dma_open(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *prtd = substream->private_data;
+	struct audio_drv_data *intr_data = dev_get_drvdata(prtd->platform->dev);
+
+	struct audio_substream_data *adata =
+		kzalloc(sizeof(struct audio_substream_data), GFP_KERNEL);
+	if (adata == NULL)
+		return -ENOMEM;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = acp_pcm_hardware_playback;
+	else
+		runtime->hw = acp_pcm_hardware_capture;
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(prtd->platform->dev, "set integer constraint failed\n");
+		return ret;
+	}
+
+	adata->acp_mmio = intr_data->acp_mmio;
+	runtime->private_data = adata;
+
+	/* Enable ACP irq, when neither playback or capture streams are
+	 * active by the time when a new stream is being opened.
+	 * This enablement is not required for another stream, if current
+	 * stream is not closed
+	*/
+	if (!intr_data->play_stream && !intr_data->capture_stream)
+		acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		intr_data->play_stream = substream;
+	else
+		intr_data->capture_stream = substream;
+
+	return 0;
+}
+
+static int acp_dma_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	int status;
+	uint64_t size;
+	struct snd_dma_buffer *dma_buffer;
+	struct page *pg;
+	struct snd_pcm_runtime *runtime;
+	struct audio_substream_data *rtd;
+
+	dma_buffer = &substream->dma_buffer;
+
+	runtime = substream->runtime;
+	rtd = runtime->private_data;
+
+	if (WARN_ON(!rtd))
+		return -EINVAL;
+
+	size = params_buffer_bytes(params);
+	status = snd_pcm_lib_malloc_pages(substream, size);
+	if (status < 0)
+		return status;
+
+	memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+	pg = virt_to_page(substream->dma_buffer.area);
+
+	if (pg != NULL) {
+		/* Save for runtime private data */
+		rtd->pg = pg;
+		rtd->order = get_order(size);
+
+		/* Fill the page table entries in ACP SRAM */
+		rtd->pg = pg;
+		rtd->size = size;
+		rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+		rtd->direction = substream->stream;
+
+		config_acp_dma(rtd->acp_mmio, rtd);
+		status = 0;
+	} else {
+		status = -ENOMEM;
+	}
+	return status;
+}
+
+static int acp_dma_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream)
+{
+	u16 dscr;
+	u32 mul, dma_config, period_bytes;
+	u32 pos = 0;
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_substream_data *rtd = runtime->private_data;
+
+	period_bytes = frames_to_bytes(runtime, runtime->period_size);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13);
+
+		if (dscr == PLAYBACK_START_DMA_DESCR_CH13)
+			mul = 0;
+		else
+			mul = 1;
+		pos =  (mul * period_bytes);
+	} else {
+		dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14);
+		if (dma_config != 0) {
+			dscr = acp_reg_read(rtd->acp_mmio,
+						mmACP_DMA_CUR_DSCR_14);
+			if (dscr == CAPTURE_START_DMA_DESCR_CH14)
+				mul = 1;
+			else
+				mul = 2;
+			pos = (mul * period_bytes);
+		}
+
+		if (pos >= (2 * period_bytes))
+			pos = 0;
+
+	}
+	return bytes_to_frames(runtime, pos);
+}
+
+static int acp_dma_mmap(struct snd_pcm_substream *substream,
+			struct vm_area_struct *vma)
+{
+	return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+static int acp_dma_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_substream_data *rtd = runtime->private_data;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
+					PLAYBACK_START_DMA_DESCR_CH12,
+					NUM_DSCRS_PER_CHANNEL, 0);
+		config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM,
+					PLAYBACK_START_DMA_DESCR_CH13,
+					NUM_DSCRS_PER_CHANNEL, 0);
+		/* Fill ACP SRAM (2 periods) with zeros from System RAM
+		 * which is zero-ed in hw_params
+		*/
+		acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false);
+
+		/* ACP SRAM (2 periods of buffer size) is intially filled with
+		 * zeros. Before rendering starts, 2nd half of SRAM will be
+		 * filled with valid audio data DMA'ed from first half of system
+		 * RAM and 1st half of SRAM will be filled with Zeros. This is
+		 * the initial scenario when redering starts from SRAM. Later
+		 * on, 2nd half of system memory will be DMA'ed to 1st half of
+		 * SRAM, 1st half of system memory will be DMA'ed to 2nd half of
+		 * SRAM in ping-pong way till rendering stops.
+		*/
+		config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM,
+					PLAYBACK_START_DMA_DESCR_CH12,
+					1, 0);
+	} else {
+		config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM,
+					CAPTURE_START_DMA_DESCR_CH14,
+					NUM_DSCRS_PER_CHANNEL, 0);
+		config_acp_dma_channel(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM,
+					CAPTURE_START_DMA_DESCR_CH15,
+					NUM_DSCRS_PER_CHANNEL, 0);
+	}
+	return 0;
+}
+
+static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret;
+	u32 loops = 1000;
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *prtd = substream->private_data;
+	struct audio_substream_data *rtd = runtime->private_data;
+
+	if (!rtd)
+		return -EINVAL;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			acp_dma_start(rtd->acp_mmio,
+						SYSRAM_TO_ACP_CH_NUM, false);
+			while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) &
+						BIT(SYSRAM_TO_ACP_CH_NUM)) {
+				if (!loops--) {
+					dev_err(prtd->platform->dev,
+						"acp dma start timeout\n");
+					return -ETIMEDOUT;
+				}
+				cpu_relax();
+			}
+
+			acp_dma_start(rtd->acp_mmio,
+					ACP_TO_I2S_DMA_CH_NUM, true);
+
+		} else {
+			acp_dma_start(rtd->acp_mmio,
+					    I2S_TO_ACP_DMA_CH_NUM, true);
+		}
+		ret = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		/* Need to stop only circular DMA channels :
+		 * ACP_TO_I2S_DMA_CH_NUM / I2S_TO_ACP_DMA_CH_NUM. Non-circular
+		 * channels will stopped automatically after its transfer
+		 * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM
+		 */
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = acp_dma_stop(rtd->acp_mmio,
+					ACP_TO_I2S_DMA_CH_NUM);
+		else
+			ret = acp_dma_stop(rtd->acp_mmio,
+					I2S_TO_ACP_DMA_CH_NUM);
+		break;
+	default:
+		ret = -EINVAL;
+
+	}
+	return ret;
+}
+
+static int acp_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+	return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+							SNDRV_DMA_TYPE_DEV,
+							NULL, MIN_BUFFER,
+							MAX_BUFFER);
+}
+
+static int acp_dma_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_substream_data *rtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *prtd = substream->private_data;
+	struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev);
+
+	kfree(rtd);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		adata->play_stream = NULL;
+	else
+		adata->capture_stream = NULL;
+
+	/* Disable ACP irq, when the current stream is being closed and
+	 * another stream is also not active.
+	*/
+	if (!adata->play_stream && !adata->capture_stream)
+		acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
+
+	return 0;
+}
+
+static struct snd_pcm_ops acp_dma_ops = {
+	.open = acp_dma_open,
+	.close = acp_dma_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = acp_dma_hw_params,
+	.hw_free = acp_dma_hw_free,
+	.trigger = acp_dma_trigger,
+	.pointer = acp_dma_pointer,
+	.mmap = acp_dma_mmap,
+	.prepare = acp_dma_prepare,
+};
+
+static struct snd_soc_platform_driver acp_asoc_platform = {
+	.ops = &acp_dma_ops,
+	.pcm_new = acp_dma_new,
+};
+
+static int acp_audio_probe(struct platform_device *pdev)
+{
+	int status;
+	struct audio_drv_data *audio_drv_data;
+	struct resource *res;
+
+	audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data),
+					GFP_KERNEL);
+	if (audio_drv_data == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	audio_drv_data->acp_mmio = devm_ioremap_resource(&pdev->dev, res);
+
+	/* The following members gets populated in device 'open'
+	 * function. Till then interrupts are disabled in 'acp_init'
+	 * and device doesn't generate any interrupts.
+	 */
+
+	audio_drv_data->play_stream = NULL;
+	audio_drv_data->capture_stream = NULL;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+		return -ENODEV;
+	}
+
+	status = devm_request_irq(&pdev->dev, res->start, dma_irq_handler,
+					0, "ACP_IRQ", &pdev->dev);
+	if (status) {
+		dev_err(&pdev->dev, "ACP IRQ request failed\n");
+		return status;
+	}
+
+	dev_set_drvdata(&pdev->dev, audio_drv_data);
+
+	/* Initialize the ACP */
+	acp_init(audio_drv_data->acp_mmio);
+
+	status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform);
+	if (status != 0) {
+		dev_err(&pdev->dev, "Fail to register ALSA platform device\n");
+		return status;
+	}
+
+	return status;
+}
+
+static int acp_audio_remove(struct platform_device *pdev)
+{
+	struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev);
+
+	acp_deinit(adata->acp_mmio);
+	snd_soc_unregister_platform(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver acp_dma_driver = {
+	.probe = acp_audio_probe,
+	.remove = acp_audio_remove,
+	.driver = {
+		.name = "acp_audio_dma",
+	},
+};
+
+module_platform_driver(acp_dma_driver);
+
+MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
+MODULE_DESCRIPTION("AMD ACP PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:acp-dma-audio");
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
new file mode 100644
index 0000000..1a57291
--- /dev/null
+++ b/sound/soc/amd/acp.h
@@ -0,0 +1,119 @@ 
+#ifndef __ACP_HW_H
+#define __ACP_HW_H
+
+#include "include/acp_2_2_d.h"
+#include "include/acp_2_2_sh_mask.h"
+
+#define PAGE_SIZE_4K				4096
+#define PAGE_SIZE_4K_ENABLE			0x02
+
+#define PLAYBACK_PTE_OFFSET			10
+#define CAPTURE_PTE_OFFSET			0
+
+#define GARLIC_CNTL_DEFAULT			0x00000FB4
+#define ONION_CNTL_DEFAULT			0x00000FB4
+
+#define ACP_PHYSICAL_BASE			0x14000
+
+/* Playback SRAM address (as a destination in dma descriptor) */
+#define ACP_SHARED_RAM_BANK_1_ADDRESS		0x4002000
+
+/* Capture SRAM address (as a source in dma descriptor) */
+#define ACP_SHARED_RAM_BANK_5_ADDRESS		0x400A000
+
+#define ACP_DMA_RESET_TIME			10000
+#define ACP_CLOCK_EN_TIME_OUT_VALUE		0x000000FF
+#define ACP_SOFT_RESET_DONE_TIME_OUT_VALUE	0x000000FF
+#define ACP_DMA_COMPLETE_TIME_OUT_VALUE		0x000000FF
+
+#define ACP_SRAM_BASE_ADDRESS			0x4000000
+#define ACP_DAGB_GRP_SRAM_BASE_ADDRESS		0x4001000
+#define ACP_DAGB_GRP_SRBM_SRAM_BASE_OFFSET	0x1000
+#define ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS	0x00000000
+#define ACP_INTERNAL_APERTURE_WINDOW_4_ADDRESS	0x01800000
+
+#define TO_ACP_I2S_1   0x2
+#define TO_ACP_I2S_2   0x4
+#define FROM_ACP_I2S_1 0xa
+#define FROM_ACP_I2S_2 0xb
+
+#define ACP_TILE_ON_MASK                0x03
+#define ACP_TILE_OFF_MASK               0x02
+#define ACP_TILE_ON_RETAIN_REG_MASK     0x1f
+#define ACP_TILE_OFF_RETAIN_REG_MASK    0x20
+
+#define ACP_TILE_P1_MASK                0x3e
+#define ACP_TILE_P2_MASK                0x3d
+#define ACP_TILE_DSP0_MASK              0x3b
+#define ACP_TILE_DSP1_MASK              0x37
+
+#define ACP_TILE_DSP2_MASK              0x2f
+/* Playback DMA channels */
+#define SYSRAM_TO_ACP_CH_NUM 12
+#define ACP_TO_I2S_DMA_CH_NUM 13
+
+/* Capture DMA channels */
+#define ACP_TO_SYSRAM_CH_NUM 14
+#define I2S_TO_ACP_DMA_CH_NUM 15
+
+#define NUM_DSCRS_PER_CHANNEL 2
+
+#define PLAYBACK_START_DMA_DESCR_CH12 0
+#define PLAYBACK_END_DMA_DESCR_CH12 1
+#define PLAYBACK_START_DMA_DESCR_CH13 2
+#define PLAYBACK_END_DMA_DESCR_CH13 3
+
+#define CAPTURE_START_DMA_DESCR_CH14 4
+#define CAPTURE_END_DMA_DESCR_CH14 5
+#define CAPTURE_START_DMA_DESCR_CH15 6
+#define CAPTURE_END_DMA_DESCR_CH15 7
+
+enum acp_dma_priority_level {
+	/* 0x0 Specifies the DMA channel is given normal priority */
+	ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0,
+	/* 0x1 Specifies the DMA channel is given high priority */
+	ACP_DMA_PRIORITY_LEVEL_HIGH = 0x1,
+	ACP_DMA_PRIORITY_LEVEL_FORCESIZE = 0xFF
+};
+
+struct audio_substream_data {
+	struct page *pg;
+	unsigned int order;
+	u16 num_of_pages;
+	u16 direction;
+	uint64_t size;
+	void __iomem *acp_mmio;
+};
+
+enum {
+	ACP_TILE_P1 = 0,
+	ACP_TILE_P2,
+	ACP_TILE_DSP0,
+	ACP_TILE_DSP1,
+	ACP_TILE_DSP2,
+};
+
+enum {
+	ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION = 0x0,
+	ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC = 0x1,
+	ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM = 0x8,
+	ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM = 0x9,
+	ACP_DMA_ATTRIBUTES_FORCE_SIZE = 0xF
+};
+
+typedef struct acp_dma_dscr_transfer {
+	/* Specifies the source memory location for the DMA data transfer. */
+	u32 src;
+	/* Specifies the destination memory location to where the data will
+	 * be transferred.
+	*/
+	u32 dest;
+	/* Specifies the number of bytes need to be transferred
+	* from source to destination memory.Transfer direction & IOC enable
+	*/
+	u32 xfer_val;
+	/* Reserved for future use */
+	u32 reserved;
+} acp_dma_dscr_transfer_t;
+
+#endif /*__ACP_HW_H */