diff mbox series

[v1,3/3] ASoC: amd: acp: Add legacy audio driver support for Rembrandt platform

Message ID 20220707161142.491034-4-Vsujithkumar.Reddy@amd.com (mailing list archive)
State Accepted
Commit e8a33a94078560df73761f6d6147a25bda07605c
Headers show
Series ADD legacy audio driver support for rembrandt | expand

Commit Message

V sujith kumar Reddy July 7, 2022, 4:11 p.m. UTC
Add i2s and dmic support for Rembrandt platform,
Add machine support for nau8825, max98360 and rt5682s,rt1019 codec
in legacy driver for rembrandt platform.
Here codec is in a slave mode.

Signed-off-by: V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
---
 sound/soc/amd/acp/Kconfig            |  11 +
 sound/soc/amd/acp/Makefile           |   2 +
 sound/soc/amd/acp/acp-i2s.c          | 137 ++++++++-
 sound/soc/amd/acp/acp-legacy-mach.c  |  32 +++
 sound/soc/amd/acp/acp-mach-common.c  |  86 +++++-
 sound/soc/amd/acp/acp-mach.h         |   6 +
 sound/soc/amd/acp/acp-pci.c          |   6 +
 sound/soc/amd/acp/acp-platform.c     |  16 +-
 sound/soc/amd/acp/acp-rembrandt.c    | 401 +++++++++++++++++++++++++++
 sound/soc/amd/acp/amd.h              |  62 ++++-
 sound/soc/amd/acp/chip_offset_byte.h |  28 ++
 11 files changed, 781 insertions(+), 6 deletions(-)
 create mode 100644 sound/soc/amd/acp/acp-rembrandt.c

Comments

Christophe JAILLET July 31, 2022, 3:41 p.m. UTC | #1
Hi,

this patch has already reached -next, but a few nit below.

Le 07/07/2022 à 18:11, V sujith kumar Reddy a écrit :
> Add i2s and dmic support for Rembrandt platform,
> Add machine support for nau8825, max98360 and rt5682s,rt1019 codec
> in legacy driver for rembrandt platform.
> Here codec is in a slave mode.
> 
> Signed-off-by: V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
> ---
>   sound/soc/amd/acp/Kconfig            |  11 +
>   sound/soc/amd/acp/Makefile           |   2 +
>   sound/soc/amd/acp/acp-i2s.c          | 137 ++++++++-
>   sound/soc/amd/acp/acp-legacy-mach.c  |  32 +++
>   sound/soc/amd/acp/acp-mach-common.c  |  86 +++++-
>   sound/soc/amd/acp/acp-mach.h         |   6 +
>   sound/soc/amd/acp/acp-pci.c          |   6 +
>   sound/soc/amd/acp/acp-platform.c     |  16 +-
>   sound/soc/amd/acp/acp-rembrandt.c    | 401 +++++++++++++++++++++++++++
>   sound/soc/amd/acp/amd.h              |  62 ++++-
>   sound/soc/amd/acp/chip_offset_byte.h |  28 ++
>   11 files changed, 781 insertions(+), 6 deletions(-)
>   create mode 100644 sound/soc/amd/acp/acp-rembrandt.c
> 

[...]

> diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
> new file mode 100644
> index 000000000000..2b57c0ca4e99
> --- /dev/null
> +++ b/sound/soc/amd/acp/acp-rembrandt.c
> @@ -0,0 +1,401 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
> +//
> +// This file is provided under a dual BSD/GPLv2 license. When using or
> +// redistributing this file, you may do so under either license.

These lines are useless. There is already a SPDX-License-Identifier just 
above.

> +//
> +// Copyright(c) 2022 Advanced Micro Devices, Inc.
> +//
> +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
> +//          V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
> +/*
> + * Hardware interface for Renoir ACP block
> + */
> +

[...]

> +static int rembrandt_audio_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct acp_chip_info *chip;
> +	struct acp_dev_data *adata;
> +	struct resource *res;
> +
> +	chip = dev_get_platdata(&pdev->dev);
> +	if (!chip || !chip->base) {
> +		dev_err(&pdev->dev, "ACP chip data is NULL\n");
> +		return -ENODEV;
> +	}
> +
> +	if (chip->acp_rev != ACP6X_DEV) {
> +		dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
> +		return -ENODEV;
> +	}
> +
> +	rmb_acp_init(chip->base);

Should rmb_acp_deinit() be called if an error occurs below?
Or a devm_add_action_or_reset() + .remove() simplification?

(this is called in the .remove() function)

> +
> +	adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
> +	if (!adata)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENODEV;
> +	}
> +
> +	adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (!adata->acp_base)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
> +		return -ENODEV;
> +	}
> +
> +	adata->i2s_irq = res->start;
> +	adata->dev = dev;
> +	adata->dai_driver = acp_rmb_dai;
> +	adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
> +	adata->rsrc = &rsrc;
> +
> +	adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
> +	acp_machine_select(adata);
> +
> +	dev_set_drvdata(dev, adata);
> +	acp6x_enable_interrupts(adata);
> +	acp_platform_register(dev);
> +
> +	return 0;
> +}
> +
> +static int rembrandt_audio_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct acp_dev_data *adata = dev_get_drvdata(dev);
> +	struct acp_chip_info *chip;
> +
> +	chip = dev_get_platdata(&pdev->dev);
> +	if (!chip || !chip->base) {
> +		dev_err(&pdev->dev, "ACP chip data is NULL\n");
> +		return -ENODEV;
> +	}

These tests and dev_err and return look useless.
The same is already tested at the biginning of the probe and if it 
fails, the probe will fail, right?

> +
> +	rmb_acp_deinit(chip->base);
> +
> +	acp6x_disable_interrupts(adata);
> +	acp_platform_unregister(dev);
> +	return 0;
> +}

[...]
V sujith kumar Reddy Aug. 1, 2022, 5:11 a.m. UTC | #2
On 7/31/2022 9:11 PM, Christophe JAILLET wrote:
> [CAUTION: External Email]
>
> Hi,
>
> this patch has already reached -next, but a few nit below.
>
> Le 07/07/2022 à 18:11, V sujith kumar Reddy a écrit :
>> Add i2s and dmic support for Rembrandt platform,
>> Add machine support for nau8825, max98360 and rt5682s,rt1019 codec
>> in legacy driver for rembrandt platform.
>> Here codec is in a slave mode.
>>
>> Signed-off-by: V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
>> ---
>>   sound/soc/amd/acp/Kconfig            |  11 +
>>   sound/soc/amd/acp/Makefile           |   2 +
>>   sound/soc/amd/acp/acp-i2s.c          | 137 ++++++++-
>>   sound/soc/amd/acp/acp-legacy-mach.c  |  32 +++
>>   sound/soc/amd/acp/acp-mach-common.c  |  86 +++++-
>>   sound/soc/amd/acp/acp-mach.h         |   6 +
>>   sound/soc/amd/acp/acp-pci.c          |   6 +
>>   sound/soc/amd/acp/acp-platform.c     |  16 +-
>>   sound/soc/amd/acp/acp-rembrandt.c    | 401 +++++++++++++++++++++++++++
>>   sound/soc/amd/acp/amd.h              |  62 ++++-
>>   sound/soc/amd/acp/chip_offset_byte.h |  28 ++
>>   11 files changed, 781 insertions(+), 6 deletions(-)
>>   create mode 100644 sound/soc/amd/acp/acp-rembrandt.c
>>
>
> [...]
>
>> diff --git a/sound/soc/amd/acp/acp-rembrandt.c 
>> b/sound/soc/amd/acp/acp-rembrandt.c
>> new file mode 100644
>> index 000000000000..2b57c0ca4e99
>> --- /dev/null
>> +++ b/sound/soc/amd/acp/acp-rembrandt.c
>> @@ -0,0 +1,401 @@
>> +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
>> +//
>> +// This file is provided under a dual BSD/GPLv2 license. When using or
>> +// redistributing this file, you may do so under either license.
>
> These lines are useless. There is already a SPDX-License-Identifier just
> above.
>
>> +//
>> +// Copyright(c) 2022 Advanced Micro Devices, Inc.
>> +//
>> +// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
>> +//          V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
>> +/*
>> + * Hardware interface for Renoir ACP block
>> + */
>> +
>
> [...]
>
>> +static int rembrandt_audio_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct acp_chip_info *chip;
>> +     struct acp_dev_data *adata;
>> +     struct resource *res;
>> +
>> +     chip = dev_get_platdata(&pdev->dev);
>> +     if (!chip || !chip->base) {
>> +             dev_err(&pdev->dev, "ACP chip data is NULL\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     if (chip->acp_rev != ACP6X_DEV) {
>> +             dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", 
>> chip->acp_rev);
>> +             return -ENODEV;
>> +     }
>> +
>> +     rmb_acp_init(chip->base);
>
> Should rmb_acp_deinit() be called if an error occurs below?
> Or a devm_add_action_or_reset() + .remove() simplification?
>
> (this is called in the .remove() function)


Yes,we can check the error status ,we will do it up in a cleanup patch.

>
>> +
>> +     adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), 
>> GFP_KERNEL);
>> +     if (!adata)
>> +             return -ENOMEM;
>> +
>> +     res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 
>> "acp_mem");
>> +     if (!res) {
>> +             dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     adata->acp_base = devm_ioremap(&pdev->dev, res->start, 
>> resource_size(res));
>> +     if (!adata->acp_base)
>> +             return -ENOMEM;
>> +
>> +     res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 
>> "acp_dai_irq");
>> +     if (!res) {
>> +             dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     adata->i2s_irq = res->start;
>> +     adata->dev = dev;
>> +     adata->dai_driver = acp_rmb_dai;
>> +     adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
>> +     adata->rsrc = &rsrc;
>> +
>> +     adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
>> +     acp_machine_select(adata);
>> +
>> +     dev_set_drvdata(dev, adata);
>> +     acp6x_enable_interrupts(adata);
>> +     acp_platform_register(dev);
>> +
>> +     return 0;
>> +}
>> +
>> +static int rembrandt_audio_remove(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct acp_dev_data *adata = dev_get_drvdata(dev);
>> +     struct acp_chip_info *chip;
>> +
>> +     chip = dev_get_platdata(&pdev->dev);
>> +     if (!chip || !chip->base) {
>> +             dev_err(&pdev->dev, "ACP chip data is NULL\n");
>> +             return -ENODEV;
>> +     }
>
> These tests and dev_err and return look useless.
> The same is already tested at the biginning of the probe and if it
> fails, the probe will fail, right?


yes ,agreed we will do it in a cleanup patch as it is merged.

>
>> +
>> +     rmb_acp_deinit(chip->base);
>> +
>> +     acp6x_disable_interrupts(adata);
>> +     acp_platform_unregister(dev);
>> +     return 0;
>> +}
>
> [...]
>
diff mbox series

Patch

diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 7e56d2644105..ce0037810743 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -40,6 +40,17 @@  config SND_AMD_ASOC_RENOIR
 	help
 	  This option enables Renoir I2S support on AMD platform.
 
+config SND_AMD_ASOC_REMBRANDT
+	tristate "AMD ACP ASOC Rembrandt Support"
+	select SND_SOC_AMD_ACP_PCM
+	select SND_SOC_AMD_ACP_I2S
+	select SND_SOC_AMD_ACP_PDM
+	depends on X86 && PCI
+	help
+	  This option enables Rembrandt I2S support on AMD platform.
+	  Say Y if you want to enable AUDIO on Rembrandt
+	  If unsure select "N".
+
 config SND_SOC_AMD_MACH_COMMON
 	tristate
 	depends on X86 && PCI && I2C
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 657ddfadf0bb..d9abb0ee5218 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -12,6 +12,7 @@  snd-acp-pci-objs     := acp-pci.o
 
 #platform specific driver
 snd-acp-renoir-objs     := acp-renoir.o
+snd-acp-rembrandt-objs  := acp-rembrandt.o
 
 #machine specific driver
 snd-acp-mach-objs     := acp-mach-common.o
@@ -24,6 +25,7 @@  obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
 obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
 
 obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
 
 obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
 obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
index a736c00db86e..393f729ef561 100644
--- a/sound/soc/amd/acp/acp-i2s.c
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -30,11 +30,14 @@  static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
 {
 	struct device *dev = dai->component->dev;
 	struct acp_dev_data *adata;
+	struct acp_resource *rsrc;
 	u32 val;
 	u32 xfer_resolution;
 	u32 reg_val;
+	u32 lrclk_div_val, bclk_div_val;
 
 	adata = snd_soc_dai_get_drvdata(dai);
+	rsrc = adata->rsrc;
 
 	/* These values are as per Hardware Spec */
 	switch (params_format(params)) {
@@ -63,6 +66,9 @@  static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
 		case I2S_SP_INSTANCE:
 			reg_val = ACP_I2STDM_ITER;
 			break;
+		case I2S_HS_INSTANCE:
+			reg_val = ACP_HSTDM_ITER;
+			break;
 		default:
 			dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 			return -EINVAL;
@@ -75,6 +81,9 @@  static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
 		case I2S_SP_INSTANCE:
 			reg_val = ACP_I2STDM_IRER;
 			break;
+		case I2S_HS_INSTANCE:
+			reg_val = ACP_HSTDM_IRER;
+			break;
 		default:
 			dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 			return -EINVAL;
@@ -86,6 +95,74 @@  static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
 	val = val | (xfer_resolution  << 3);
 	writel(val, adata->acp_base + reg_val);
 
+	if (rsrc->soc_mclk) {
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			switch (params_rate(params)) {
+			case 8000:
+				bclk_div_val = 768;
+				break;
+			case 16000:
+				bclk_div_val = 384;
+				break;
+			case 24000:
+				bclk_div_val = 256;
+				break;
+			case 32000:
+				bclk_div_val = 192;
+				break;
+			case 44100:
+			case 48000:
+				bclk_div_val = 128;
+				break;
+			case 88200:
+			case 96000:
+				bclk_div_val = 64;
+				break;
+			case 192000:
+				bclk_div_val = 32;
+				break;
+			default:
+				return -EINVAL;
+			}
+			lrclk_div_val = 32;
+			break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			switch (params_rate(params)) {
+			case 8000:
+				bclk_div_val = 384;
+				break;
+			case 16000:
+				bclk_div_val = 192;
+				break;
+			case 24000:
+				bclk_div_val = 128;
+				break;
+			case 32000:
+				bclk_div_val = 96;
+				break;
+			case 44100:
+			case 48000:
+				bclk_div_val = 64;
+				break;
+			case 88200:
+			case 96000:
+				bclk_div_val = 32;
+				break;
+			case 192000:
+				bclk_div_val = 16;
+				break;
+			default:
+				return -EINVAL;
+			}
+			lrclk_div_val = 64;
+			break;
+		default:
+			return -EINVAL;
+		}
+		adata->lrclk_div = lrclk_div_val;
+		adata->bclk_div = bclk_div_val;
+	}
 	return 0;
 }
 
@@ -94,6 +171,7 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 	struct acp_stream *stream = substream->runtime->private_data;
 	struct device *dev = dai->component->dev;
 	struct acp_dev_data *adata = dev_get_drvdata(dev);
+	struct acp_resource *rsrc = adata->rsrc;
 	u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
 
 	period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
@@ -118,6 +196,12 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 				ier_val = ACP_I2STDM_IER;
 				buf_reg = ACP_I2S_TX_RINGBUFSIZE;
 				break;
+			case I2S_HS_INSTANCE:
+				water_val = ACP_HS_TX_INTR_WATERMARK_SIZE;
+				reg_val = ACP_HSTDM_ITER;
+				ier_val = ACP_HSTDM_IER;
+				buf_reg = ACP_HS_TX_RINGBUFSIZE;
+				break;
 			default:
 				dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 				return -EINVAL;
@@ -136,6 +220,12 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 				ier_val = ACP_I2STDM_IER;
 				buf_reg = ACP_I2S_RX_RINGBUFSIZE;
 				break;
+			case I2S_HS_INSTANCE:
+				water_val = ACP_HS_RX_INTR_WATERMARK_SIZE;
+				reg_val = ACP_HSTDM_IRER;
+				ier_val = ACP_HSTDM_IER;
+				buf_reg = ACP_HS_RX_RINGBUFSIZE;
+				break;
 			default:
 				dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 				return -EINVAL;
@@ -147,6 +237,8 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 		val = val | BIT(0);
 		writel(val, adata->acp_base + reg_val);
 		writel(1, adata->acp_base + ier_val);
+		if (rsrc->soc_mclk)
+			acp_set_i2s_clk(adata, dai->driver->id);
 		return 0;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -159,6 +251,9 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 			case I2S_SP_INSTANCE:
 				reg_val = ACP_I2STDM_ITER;
 				break;
+			case I2S_HS_INSTANCE:
+				reg_val = ACP_HSTDM_ITER;
+				break;
 			default:
 				dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 				return -EINVAL;
@@ -172,6 +267,9 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 			case I2S_SP_INSTANCE:
 				reg_val = ACP_I2STDM_IRER;
 				break;
+			case I2S_HS_INSTANCE:
+				reg_val = ACP_HSTDM_IRER;
+				break;
 			default:
 				dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 				return -EINVAL;
@@ -187,6 +285,9 @@  static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
 		if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
 		    !(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
 			writel(0, adata->acp_base + ACP_I2STDM_IER);
+		if (!(readl(adata->acp_base + ACP_HSTDM_ITER) & BIT(0)) &&
+		    !(readl(adata->acp_base + ACP_HSTDM_IRER) & BIT(0)))
+			writel(0, adata->acp_base + ACP_HSTDM_IER);
 		return 0;
 	default:
 		return -EINVAL;
@@ -247,6 +348,27 @@  static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
 			writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
 		}
 		break;
+	case I2S_HS_INSTANCE:
+		if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+			reg_dma_size = ACP_HS_TX_DMA_SIZE;
+			acp_fifo_addr = rsrc->sram_pte_offset +
+				HS_PB_FIFO_ADDR_OFFSET;
+			reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+			reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+
+			phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+			writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR);
+		} else {
+			reg_dma_size = ACP_HS_RX_DMA_SIZE;
+			acp_fifo_addr = rsrc->sram_pte_offset +
+					HS_CAPT_FIFO_ADDR_OFFSET;
+			reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+			reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+
+			phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+			writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR);
+		}
+		break;
 	default:
 		dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 		return -EINVAL;
@@ -260,7 +382,9 @@  static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
 	ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
 			BIT(BT_RX_THRESHOLD(rsrc->offset)) |
 			BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
-			BIT(BT_TX_THRESHOLD(rsrc->offset));
+			BIT(BT_TX_THRESHOLD(rsrc->offset)) |
+			BIT(HS_RX_THRESHOLD(rsrc->offset)) |
+			BIT(HS_TX_THRESHOLD(rsrc->offset));
 
 	writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
 
@@ -299,6 +423,17 @@  static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d
 			stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
 		}
 		break;
+	case I2S_HS_INSTANCE:
+		if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+			irq_bit = BIT(HS_TX_THRESHOLD(rsrc->offset));
+			stream->pte_offset = ACP_SRAM_HS_PB_PTE_OFFSET;
+			stream->fifo_offset = HS_PB_FIFO_ADDR_OFFSET;
+		} else {
+			irq_bit = BIT(HS_RX_THRESHOLD(rsrc->offset));
+			stream->pte_offset = ACP_SRAM_HS_CP_PTE_OFFSET;
+			stream->fifo_offset = HS_CAPT_FIFO_ADDR_OFFSET;
+		}
+		break;
 	default:
 		dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
 		return -EINVAL;
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
index 7f04a048ca3a..1f4878ff7d37 100644
--- a/sound/soc/amd/acp/acp-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -47,6 +47,28 @@  static struct acp_card_drvdata rt5682s_rt1019_data = {
 	.dmic_codec_id = DMIC,
 };
 
+static struct acp_card_drvdata max_nau8825_data = {
+	.hs_cpu_id = I2S_HS,
+	.amp_cpu_id = I2S_HS,
+	.dmic_cpu_id = DMIC,
+	.hs_codec_id = NAU8825,
+	.amp_codec_id = MAX98360A,
+	.dmic_codec_id = DMIC,
+	.soc_mclk = true,
+	.platform = REMBRANDT,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
+	.hs_cpu_id = I2S_HS,
+	.amp_cpu_id = I2S_HS,
+	.dmic_cpu_id = DMIC,
+	.hs_codec_id = RT5682S,
+	.amp_codec_id = RT1019,
+	.dmic_codec_id = DMIC,
+	.soc_mclk = true,
+	.platform = REMBRANDT,
+};
+
 static const struct snd_kcontrol_new acp_controls[] = {
 	SOC_DAPM_PIN_SWITCH("Headphone Jack"),
 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -112,6 +134,14 @@  static const struct platform_device_id board_ids[] = {
 		.name = "acp3xalc5682s1019",
 		.driver_data = (kernel_ulong_t)&rt5682s_rt1019_data,
 	},
+	{
+		.name = "rmb-nau8825-max",
+		.driver_data = (kernel_ulong_t)&max_nau8825_data,
+	},
+	{
+		.name = "rmb-rt5682s-rt1019",
+		.driver_data = (kernel_ulong_t)&rt5682s_rt1019_rmb_data,
+	},
 	{ }
 };
 static struct platform_driver acp_asoc_audio = {
@@ -130,4 +160,6 @@  MODULE_DESCRIPTION("ACP chrome audio support");
 MODULE_ALIAS("platform:acp3xalc56821019");
 MODULE_ALIAS("platform:acp3xalc5682sm98360");
 MODULE_ALIAS("platform:acp3xalc5682s1019");
+MODULE_ALIAS("platform:rmb-nau8825-max");
+MODULE_ALIAS("platform:rmb-rt5682s-rt1019");
 MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index 86145398fa25..f0c49127aad1 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -545,6 +545,12 @@  static struct snd_soc_dai_link_component platform_component[] = {
 	}
 };
 
+static struct snd_soc_dai_link_component platform_rmb_component[] = {
+	{
+		.name = "acp_asoc_rembrandt.0",
+	}
+};
+
 static struct snd_soc_dai_link_component sof_component[] = {
 	{
 		 .name = "0000:04:00.5",
@@ -553,6 +559,8 @@  static struct snd_soc_dai_link_component sof_component[] = {
 
 SND_SOC_DAILINK_DEF(i2s_sp,
 	DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-sp")));
+SND_SOC_DAILINK_DEF(i2s_hs,
+		    DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs")));
 SND_SOC_DAILINK_DEF(sof_sp,
 	DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
 SND_SOC_DAILINK_DEF(sof_hs,
@@ -774,6 +782,40 @@  int acp_legacy_dai_links_create(struct snd_soc_card *card)
 		i++;
 	}
 
+	if (drv_data->hs_cpu_id == I2S_HS) {
+		links[i].name = "acp-headset-codec";
+		links[i].id = HEADSET_BE_ID;
+		links[i].cpus = i2s_hs;
+		links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+		if (drv_data->platform == REMBRANDT) {
+			links[i].platforms = platform_rmb_component;
+			links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+		} else {
+			links[i].platforms = platform_component;
+			links[i].num_platforms = ARRAY_SIZE(platform_component);
+		}
+		links[i].dpcm_playback = 1;
+		links[i].dpcm_capture = 1;
+		if (!drv_data->hs_codec_id) {
+			/* Use dummy codec if codec id not specified */
+			links[i].codecs = dummy_codec;
+			links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+		}
+		if (drv_data->hs_codec_id == NAU8825) {
+			links[i].codecs = nau8825;
+			links[i].num_codecs = ARRAY_SIZE(nau8825);
+			links[i].init = acp_card_nau8825_init;
+			links[i].ops = &acp_card_nau8825_ops;
+		}
+		if (drv_data->hs_codec_id == RT5682S) {
+			links[i].codecs = rt5682s;
+			links[i].num_codecs = ARRAY_SIZE(rt5682s);
+			links[i].init = acp_card_rt5682s_init;
+			links[i].ops = &acp_card_rt5682s_ops;
+		}
+		i++;
+	}
+
 	if (drv_data->amp_cpu_id == I2S_SP) {
 		links[i].name = "acp-amp-codec";
 		links[i].id = AMP_BE_ID;
@@ -804,6 +846,41 @@  int acp_legacy_dai_links_create(struct snd_soc_card *card)
 		i++;
 	}
 
+	if (drv_data->amp_cpu_id == I2S_HS) {
+		links[i].name = "acp-amp-codec";
+		links[i].id = AMP_BE_ID;
+		links[i].cpus = i2s_hs;
+		links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+		if (drv_data->platform == REMBRANDT) {
+			links[i].platforms = platform_rmb_component;
+			links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+		} else {
+			links[i].platforms = platform_component;
+			links[i].num_platforms = ARRAY_SIZE(platform_component);
+		}
+		links[i].dpcm_playback = 1;
+		if (!drv_data->amp_codec_id) {
+			/* Use dummy codec if codec id not specified */
+			links[i].codecs = dummy_codec;
+			links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+		}
+		if (drv_data->amp_codec_id == MAX98360A) {
+			links[i].codecs = max98360a;
+			links[i].num_codecs = ARRAY_SIZE(max98360a);
+			links[i].ops = &acp_card_maxim_ops;
+			links[i].init = acp_card_maxim_init;
+		}
+		if (drv_data->amp_codec_id == RT1019) {
+			links[i].codecs = rt1019;
+			links[i].num_codecs = ARRAY_SIZE(rt1019);
+			links[i].ops = &acp_card_rt1019_ops;
+			links[i].init = acp_card_rt1019_init;
+			card->codec_conf = rt1019_conf;
+			card->num_configs = ARRAY_SIZE(rt1019_conf);
+		}
+		i++;
+	}
+
 	if (drv_data->dmic_cpu_id == DMIC) {
 		links[i].name = "acp-dmic-codec";
 		links[i].id = DMIC_BE_ID;
@@ -817,8 +894,13 @@  int acp_legacy_dai_links_create(struct snd_soc_card *card)
 		}
 		links[i].cpus = pdm_dmic;
 		links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
-		links[i].platforms = platform_component;
-		links[i].num_platforms = ARRAY_SIZE(platform_component);
+		if (drv_data->platform == REMBRANDT) {
+			links[i].platforms = platform_rmb_component;
+			links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+		} else {
+			links[i].platforms = platform_component;
+			links[i].num_platforms = ARRAY_SIZE(platform_component);
+		}
 		links[i].ops = &acp_card_dmic_ops;
 		links[i].dpcm_capture = 1;
 	}
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index c95ee1c52eb1..20583ef902df 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -41,6 +41,11 @@  enum codec_endpoints {
 	NAU8825,
 };
 
+enum platform_end_point {
+	RENOIR = 0,
+	REMBRANDT,
+};
+
 struct acp_card_drvdata {
 	unsigned int hs_cpu_id;
 	unsigned int amp_cpu_id;
@@ -49,6 +54,7 @@  struct acp_card_drvdata {
 	unsigned int amp_codec_id;
 	unsigned int dmic_codec_id;
 	unsigned int dai_fmt;
+	unsigned int platform;
 	struct clk *wclk;
 	struct clk *bclk;
 	bool soc_mclk;
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index c893963ee2d0..c03bcd31fc95 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -82,6 +82,12 @@  static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
 		chip->name = "acp_asoc_renoir";
 		chip->acp_rev = ACP3X_DEV;
 		break;
+	case 0x6f:
+		res_acp = acp3x_res;
+		num_res = ARRAY_SIZE(acp3x_res);
+		chip->name = "acp_asoc_rembrandt";
+		chip->acp_rev = ACP6X_DEV;
+		break;
 	default:
 		dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
 		return -EINVAL;
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
index e93c9e478cfa..327e17736dbd 100644
--- a/sound/soc/amd/acp/acp-platform.c
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -94,11 +94,14 @@  static irqreturn_t i2s_irq_handler(int irq, void *data)
 	struct acp_resource *rsrc = adata->rsrc;
 	struct acp_stream *stream;
 	u16 i2s_flag = 0;
-	u32 val, i;
+	u32 val, val1, i;
 
 	if (!adata)
 		return IRQ_NONE;
 
+	if (adata->rsrc->no_of_ctrls == 2)
+		val1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1)));
+
 	val = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
 
 	for (i = 0; i < ACP_MAX_STREAM; i++) {
@@ -110,8 +113,16 @@  static irqreturn_t i2s_irq_handler(int irq, void *data)
 			i2s_flag = 1;
 			break;
 		}
+		if (adata->rsrc->no_of_ctrls == 2) {
+			if (stream && (val1 & stream->irq_bit)) {
+				writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata,
+				       (rsrc->irqp_used - 1)));
+				snd_pcm_period_elapsed(stream->substream);
+				i2s_flag = 1;
+				break;
+			}
+		}
 	}
-
 	if (i2s_flag)
 		return IRQ_HANDLED;
 
@@ -132,6 +143,7 @@  static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream
 	reg_val = rsrc->sram_pte_offset;
 	writel(reg_val | BIT(31), adata->acp_base + pte_reg);
 	writel(PAGE_SIZE_4K_ENABLE,  adata->acp_base + pte_size);
+	writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
 }
 
 static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
new file mode 100644
index 000000000000..2b57c0ca4e99
--- /dev/null
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -0,0 +1,401 @@ 
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//          V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_asoc_rembrandt"
+
+#define ACP6X_PGFSM_CONTROL			0x1024
+#define ACP6X_PGFSM_STATUS			0x1028
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK		0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK		0x00
+#define ACP_PGFSM_STATUS_MASK			0x03
+#define ACP_POWERED_ON				0x00
+#define ACP_POWER_ON_IN_PROGRESS		0x01
+#define ACP_POWERED_OFF				0x02
+#define ACP_POWER_OFF_IN_PROGRESS		0x03
+
+#define ACP_ERROR_MASK				0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK		0xFFFFFFFF
+
+
+static int rmb_acp_init(void __iomem *base);
+static int rmb_acp_deinit(void __iomem *base);
+
+static struct acp_resource rsrc = {
+	.offset = 0,
+	.no_of_ctrls = 2,
+	.irqp_used = 1,
+	.soc_mclk = true,
+	.irq_reg_offset = 0x1a00,
+	.i2s_pin_cfg_offset = 0x1440,
+	.i2s_mode = 0x0a,
+	.scratch_reg_offset = 0x12800,
+	.sram_pte_offset = 0x03802800,
+};
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+	.num_codecs = 1,
+	.codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+	.num_codecs = 1,
+	.codecs = {"MX98360A"}
+};
+
+static struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
+	{
+		.id = "10508825",
+		.drv_name = "rmb-nau8825-max",
+		.machine_quirk = snd_soc_acpi_codec_list,
+		.quirk_data = &amp_max,
+	},
+	{
+		.id = "AMDI0007",
+		.drv_name = "rembrandt-acp",
+	},
+	{
+		.id = "RTL5682",
+		.drv_name = "rmb-rt5682s-rt1019",
+		.machine_quirk = snd_soc_acpi_codec_list,
+		.quirk_data = &amp_rt1019,
+	},
+	{},
+};
+
+static struct snd_soc_dai_driver acp_rmb_dai[] = {
+{
+	.name = "acp-i2s-sp",
+	.id = I2S_SP_INSTANCE,
+	.playback = {
+		.stream_name = "I2S SP Playback",
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 8,
+		.rate_min = 8000,
+		.rate_max = 96000,
+	},
+	.capture = {
+		.stream_name = "I2S SP Capture",
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &asoc_acp_cpu_dai_ops,
+	.probe = &asoc_acp_i2s_probe,
+},
+{
+	.name = "acp-i2s-bt",
+	.id = I2S_BT_INSTANCE,
+	.playback = {
+		.stream_name = "I2S BT Playback",
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 8,
+		.rate_min = 8000,
+		.rate_max = 96000,
+	},
+	.capture = {
+		.stream_name = "I2S BT Capture",
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &asoc_acp_cpu_dai_ops,
+	.probe = &asoc_acp_i2s_probe,
+},
+{
+	.name = "acp-i2s-hs",
+	.id = I2S_HS_INSTANCE,
+	.playback = {
+		.stream_name = "I2S HS Playback",
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 8,
+		.rate_min = 8000,
+		.rate_max = 96000,
+	},
+	.capture = {
+		.stream_name = "I2S HS Capture",
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+			   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 8,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &asoc_acp_cpu_dai_ops,
+	.probe = &asoc_acp_i2s_probe,
+},
+{
+	.name = "acp-pdm-dmic",
+	.id = DMIC_INSTANCE,
+	.capture = {
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp6x_power_on(void __iomem *base)
+{
+	u32 val;
+	int timeout;
+
+	val = readl(base + ACP6X_PGFSM_STATUS);
+
+	if (val == ACP_POWERED_ON)
+		return 0;
+
+	if ((val & ACP_PGFSM_STATUS_MASK) !=
+				ACP_POWER_ON_IN_PROGRESS)
+		writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+		       base + ACP6X_PGFSM_CONTROL);
+	timeout = 0;
+	while (++timeout < 500) {
+		val = readl(base + ACP6X_PGFSM_STATUS);
+		if (!val)
+			return 0;
+		udelay(1);
+	}
+	return -ETIMEDOUT;
+}
+
+static int acp6x_power_off(void __iomem *base)
+{
+	u32 val;
+	int timeout;
+
+	writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+	       base + ACP6X_PGFSM_CONTROL);
+	timeout = 0;
+	while (++timeout < 500) {
+		val = readl(base + ACP6X_PGFSM_STATUS);
+		if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+			return 0;
+		udelay(1);
+	}
+	return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *base)
+{
+	u32 val;
+	int timeout;
+
+	writel(1, base + ACP_SOFT_RESET);
+	timeout = 0;
+	while (++timeout < 500) {
+		val = readl(base + ACP_SOFT_RESET);
+		if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+			break;
+		cpu_relax();
+	}
+	writel(0, base + ACP_SOFT_RESET);
+	timeout = 0;
+	while (++timeout < 500) {
+		val = readl(base + ACP_SOFT_RESET);
+		if (!val)
+			return 0;
+		cpu_relax();
+	}
+	return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(struct acp_dev_data *adata)
+{
+	struct acp_resource *rsrc = adata->rsrc;
+	u32 ext_intr_ctrl;
+
+	writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
+	ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+	ext_intr_ctrl |= ACP_ERROR_MASK;
+	writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+}
+
+static void acp6x_disable_interrupts(struct acp_dev_data *adata)
+{
+	struct acp_resource *rsrc = adata->rsrc;
+
+	writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
+	       ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+	writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
+}
+
+static int rmb_acp_init(void __iomem *base)
+{
+	int ret;
+
+	/* power on */
+	ret = acp6x_power_on(base);
+	if (ret) {
+		pr_err("ACP power on failed\n");
+		return ret;
+	}
+	writel(0x01, base + ACP_CONTROL);
+
+	/* Reset */
+	ret = acp6x_reset(base);
+	if (ret) {
+		pr_err("ACP reset failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rmb_acp_deinit(void __iomem *base)
+{
+	int ret = 0;
+
+	/* Reset */
+	ret = acp6x_reset(base);
+	if (ret) {
+		pr_err("ACP reset failed\n");
+		return ret;
+	}
+
+	writel(0x00, base + ACP_CONTROL);
+
+	/* power off */
+	ret = acp6x_power_off(base);
+	if (ret) {
+		pr_err("ACP power off failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rembrandt_audio_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct acp_chip_info *chip;
+	struct acp_dev_data *adata;
+	struct resource *res;
+
+	chip = dev_get_platdata(&pdev->dev);
+	if (!chip || !chip->base) {
+		dev_err(&pdev->dev, "ACP chip data is NULL\n");
+		return -ENODEV;
+	}
+
+	if (chip->acp_rev != ACP6X_DEV) {
+		dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+		return -ENODEV;
+	}
+
+	rmb_acp_init(chip->base);
+
+	adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
+	if (!adata)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+		return -ENODEV;
+	}
+
+	adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!adata->acp_base)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+		return -ENODEV;
+	}
+
+	adata->i2s_irq = res->start;
+	adata->dev = dev;
+	adata->dai_driver = acp_rmb_dai;
+	adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
+	adata->rsrc = &rsrc;
+
+	adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
+	acp_machine_select(adata);
+
+	dev_set_drvdata(dev, adata);
+	acp6x_enable_interrupts(adata);
+	acp_platform_register(dev);
+
+	return 0;
+}
+
+static int rembrandt_audio_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct acp_dev_data *adata = dev_get_drvdata(dev);
+	struct acp_chip_info *chip;
+
+	chip = dev_get_platdata(&pdev->dev);
+	if (!chip || !chip->base) {
+		dev_err(&pdev->dev, "ACP chip data is NULL\n");
+		return -ENODEV;
+	}
+
+	rmb_acp_deinit(chip->base);
+
+	acp6x_disable_interrupts(adata);
+	acp_platform_unregister(dev);
+	return 0;
+}
+
+static struct platform_driver rembrandt_driver = {
+	.probe = rembrandt_audio_probe,
+	.remove = rembrandt_audio_remove,
+	.driver = {
+		.name = "acp_asoc_rembrandt",
+	},
+};
+
+module_platform_driver(rembrandt_driver);
+
+MODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
+MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index 186cb8b26175..af9603724a68 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -19,10 +19,12 @@ 
 #include "chip_offset_byte.h"
 
 #define ACP3X_DEV			3
+#define ACP6X_DEV			6
 
 #define I2S_SP_INSTANCE			0x00
 #define I2S_BT_INSTANCE			0x01
 #define DMIC_INSTANCE			0x02
+#define I2S_HS_INSTANCE			0x03
 
 #define MEM_WINDOW_START		0x4080000
 
@@ -38,23 +40,31 @@ 
 #define I2S_TX_THRESHOLD(base)	THRESHOLD(8, base)
 #define BT_TX_THRESHOLD(base)	THRESHOLD(6, base)
 #define BT_RX_THRESHOLD(base)	THRESHOLD(5, base)
+#define HS_TX_THRESHOLD(base)	THRESHOLD(4, base)
+#define HS_RX_THRESHOLD(base)	THRESHOLD(3, base)
 
 #define ACP_SRAM_SP_PB_PTE_OFFSET	0x0
 #define ACP_SRAM_SP_CP_PTE_OFFSET	0x100
 #define ACP_SRAM_BT_PB_PTE_OFFSET	0x200
 #define ACP_SRAM_BT_CP_PTE_OFFSET	0x300
 #define ACP_SRAM_PDM_PTE_OFFSET		0x400
+#define ACP_SRAM_HS_PB_PTE_OFFSET       0x500
+#define ACP_SRAM_HS_CP_PTE_OFFSET       0x600
 #define PAGE_SIZE_4K_ENABLE		0x2
 
 #define I2S_SP_TX_MEM_WINDOW_START	0x4000000
 #define I2S_SP_RX_MEM_WINDOW_START	0x4020000
 #define I2S_BT_TX_MEM_WINDOW_START	0x4040000
 #define I2S_BT_RX_MEM_WINDOW_START	0x4060000
+#define I2S_HS_TX_MEM_WINDOW_START      0x40A0000
+#define I2S_HS_RX_MEM_WINDOW_START      0x40C0000
 
 #define SP_PB_FIFO_ADDR_OFFSET		0x500
 #define SP_CAPT_FIFO_ADDR_OFFSET	0x700
 #define BT_PB_FIFO_ADDR_OFFSET		0x900
 #define BT_CAPT_FIFO_ADDR_OFFSET	0xB00
+#define HS_PB_FIFO_ADDR_OFFSET		0xD00
+#define HS_CAPT_FIFO_ADDR_OFFSET	0xF00
 #define PLAYBACK_MIN_NUM_PERIODS	2
 #define PLAYBACK_MAX_NUM_PERIODS	8
 #define PLAYBACK_MAX_PERIOD_SIZE	8192
@@ -72,7 +82,7 @@ 
 
 #define ACP3x_ITER_IRER_SAMP_LEN_MASK	0x38
 
-#define ACP_MAX_STREAM			6
+#define ACP_MAX_STREAM			8
 
 struct acp_chip_info {
 	char *name;		/* Platform name */
@@ -95,6 +105,7 @@  struct acp_resource {
 	int offset;
 	int no_of_ctrls;
 	int irqp_used;
+	bool soc_mclk;
 	u32 irq_reg_offset;
 	u32 i2s_pin_cfg_offset;
 	int i2s_mode;
@@ -117,9 +128,23 @@  struct acp_dev_data {
 	struct snd_soc_acpi_mach *machines;
 	struct platform_device *mach_dev;
 
+	u32 bclk_div;
+	u32 lrclk_div;
+
 	struct acp_resource *rsrc;
 };
 
+union acp_i2stdm_mstrclkgen {
+	struct {
+		u32 i2stdm_master_mode : 1;
+		u32 i2stdm_format_mode : 1;
+		u32 i2stdm_lrclk_div_val : 9;
+		u32 i2stdm_bclk_div_val : 11;
+		u32:10;
+	} bitfields, bits;
+	u32  u32_all;
+};
+
 extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
 extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
 
@@ -146,6 +171,10 @@  static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
 			high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
 			low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
 			break;
+		case I2S_HS_INSTANCE:
+			high = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
+			low = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
+			break;
 		default:
 			dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
 			return -EINVAL;
@@ -160,6 +189,10 @@  static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
 			high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
 			low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
 			break;
+		case I2S_HS_INSTANCE:
+			high = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
+			low = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
+			break;
 		case DMIC_INSTANCE:
 			high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
 			low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
@@ -175,4 +208,31 @@  static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
 	return byte_count;
 }
 
+static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id)
+{
+	union acp_i2stdm_mstrclkgen mclkgen;
+	u32 master_reg;
+
+	switch (dai_id) {
+	case I2S_SP_INSTANCE:
+		master_reg = ACP_I2STDM0_MSTRCLKGEN;
+		break;
+	case I2S_BT_INSTANCE:
+		master_reg = ACP_I2STDM1_MSTRCLKGEN;
+		break;
+	case I2S_HS_INSTANCE:
+		master_reg = ACP_I2STDM2_MSTRCLKGEN;
+		break;
+	default:
+		master_reg = ACP_I2STDM0_MSTRCLKGEN;
+		break;
+	}
+
+	mclkgen.bits.i2stdm_master_mode = 0x1;
+	mclkgen.bits.i2stdm_format_mode = 0x00;
+
+	mclkgen.bits.i2stdm_bclk_div_val = adata->bclk_div;
+	mclkgen.bits.i2stdm_lrclk_div_val = adata->lrclk_div;
+	writel(mclkgen.u32_all, adata->acp_base + master_reg);
+}
 #endif
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
index fff7e80475ba..ce3948e0679c 100644
--- a/sound/soc/amd/acp/chip_offset_byte.h
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -66,6 +66,24 @@ 
 #define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH             0x2084
 #define ACP_BT_TX_LINEARPOSITIONCNTR_LOW              0x2088
 #define ACP_BT_TX_INTR_WATERMARK_SIZE                 0x208C
+#define ACP_HS_RX_RINGBUFADDR			      0x3A90
+#define ACP_HS_RX_RINGBUFSIZE			      0x3A94
+#define ACP_HS_RX_LINKPOSITIONCNTR		      0x3A98
+#define ACP_HS_RX_FIFOADDR			      0x3A9C
+#define ACP_HS_RX_FIFOSIZE			      0x3AA0
+#define ACP_HS_RX_DMA_SIZE			      0x3AA4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH	      0x3AA8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW	      0x3AAC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE		      0x3AB0
+#define ACP_HS_TX_RINGBUFADDR			      0x3AB4
+#define ACP_HS_TX_RINGBUFSIZE			      0x3AB8
+#define ACP_HS_TX_LINKPOSITIONCNTR		      0x3ABC
+#define ACP_HS_TX_FIFOADDR			      0x3AC0
+#define ACP_HS_TX_FIFOSIZE			      0x3AC4
+#define ACP_HS_TX_DMA_SIZE			      0x3AC8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH	      0x3ACC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW	      0x3AD0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE		      0x3AD4
 
 #define ACP_I2STDM_IER                                0x2400
 #define ACP_I2STDM_IRER                               0x2404
@@ -81,6 +99,13 @@ 
 #define ACP_BTTDM_ITER                                0x280C
 #define ACP_BTTDM_TXFRMT                              0x2810
 
+/* Registers from ACP_HS_TDM block */
+#define ACP_HSTDM_IER                                 0x2814
+#define ACP_HSTDM_IRER                                0x2818
+#define ACP_HSTDM_RXFRMT                              0x281C
+#define ACP_HSTDM_ITER                                0x2820
+#define ACP_HSTDM_TXFRMT                              0x2824
+
 /* Registers from ACP_WOV_PDM block */
 
 #define ACP_WOV_PDM_ENABLE                            0x2C04
@@ -101,4 +126,7 @@ 
 #define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN             0x2C64
 #define ACP_WOV_ERROR_STATUS_REGISTER                 0x2C68
 
+#define ACP_I2STDM0_MSTRCLKGEN			      0x2414
+#define ACP_I2STDM1_MSTRCLKGEN			      0x2418
+#define ACP_I2STDM2_MSTRCLKGEN			      0x241C
 #endif