aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/amd
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/amd')
-rw-r--r--sound/soc/amd/Kconfig145
-rw-r--r--sound/soc/amd/Makefile17
-rw-r--r--sound/soc/amd/acp-config.c337
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c489
-rw-r--r--sound/soc/amd/acp-es8336.c320
-rw-r--r--sound/soc/amd/acp-pcm-dma.c104
-rw-r--r--sound/soc/amd/acp-rt5645.c40
-rw-r--r--sound/soc/amd/acp.h16
-rw-r--r--sound/soc/amd/acp/Kconfig188
-rw-r--r--sound/soc/amd/acp/Makefile50
-rw-r--r--sound/soc/amd/acp/acp-i2s.c697
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c645
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c245
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c1796
-rw-r--r--sound/soc/amd/acp/acp-mach.h139
-rw-r--r--sound/soc/amd/acp/acp-pci.c300
-rw-r--r--sound/soc/amd/acp/acp-pdm.c187
-rw-r--r--sound/soc/amd/acp/acp-platform.c358
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c247
-rw-r--r--sound/soc/amd/acp/acp-renoir.c191
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c531
-rw-r--r--sound/soc/amd/acp/acp-sdw-mach-common.c98
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c452
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c174
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c452
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h12
-rw-r--r--sound/soc/amd/acp/acp63.c298
-rw-r--r--sound/soc/amd/acp/acp70.c227
-rw-r--r--sound/soc/amd/acp/acp_common.h19
-rw-r--r--sound/soc/amd/acp/amd-acp63-acpi-match.c144
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c160
-rw-r--r--sound/soc/amd/acp/amd-acpi-mach.c93
-rw-r--r--sound/soc/amd/acp/amd-sdw-acpi.c62
-rw-r--r--sound/soc/amd/acp/amd.h365
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h146
-rw-r--r--sound/soc/amd/acp/soc_amd_sdw_common.h60
-rw-r--r--sound/soc/amd/acp3x-rt5682-max9836.c108
-rw-r--r--sound/soc/amd/mach-config.h37
-rw-r--r--sound/soc/amd/ps/Makefile11
-rw-r--r--sound/soc/amd/ps/acp63.h441
-rw-r--r--sound/soc/amd/ps/pci-ps.c742
-rw-r--r--sound/soc/amd/ps/ps-common.c475
-rw-r--r--sound/soc/amd/ps/ps-mach.c80
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c463
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c799
-rw-r--r--sound/soc/amd/raven/Makefile6
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c21
-rw-r--r--sound/soc/amd/raven/acp3x-pcm-dma.c51
-rw-r--r--sound/soc/amd/raven/acp3x.h3
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c40
-rw-r--r--sound/soc/amd/renoir/Makefile6
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c55
-rw-r--r--sound/soc/amd/renoir/acp3x-rn.c8
-rw-r--r--sound/soc/amd/renoir/rn-pci-acp3x.c73
-rw-r--r--sound/soc/amd/renoir/rn_acp3x.h5
-rw-r--r--sound/soc/amd/rpl/Makefile5
-rw-r--r--sound/soc/amd/rpl/rpl-pci-acp6x.c227
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x.h36
-rw-r--r--sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h30
-rw-r--r--sound/soc/amd/vangogh/Makefile11
-rw-r--r--sound/soc/amd/vangogh/acp5x-i2s.c416
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c490
-rw-r--r--sound/soc/amd/vangogh/acp5x-pcm-dma.c513
-rw-r--r--sound/soc/amd/vangogh/acp5x.h224
-rw-r--r--sound/soc/amd/vangogh/pci-acp5x.c342
-rw-r--r--sound/soc/amd/vangogh/vg_chip_offset_byte.h337
-rw-r--r--sound/soc/amd/yc/Makefile9
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c705
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c455
-rw-r--r--sound/soc/amd/yc/acp6x.h110
-rw-r--r--sound/soc/amd/yc/acp6x_chip_offset_byte.h444
-rw-r--r--sound/soc/amd/yc/pci-acp6x.c350
72 files changed, 17656 insertions, 276 deletions
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index a6ce000fac3f..c7daae392d74 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -5,14 +5,16 @@ config SND_SOC_AMD_ACP
This option enables ACP DMA support on AMD platform.
config SND_SOC_AMD_CZ_DA7219MX98357_MACH
- tristate "AMD CZ support for DA7219 and MAX9835"
+ tristate "AMD CZ support for DA7219, RT5682 and MAX9835"
+ select CLK_FIXED_FCH
select SND_SOC_DA7219
+ select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
select SND_SOC_ADAU7002
select REGULATOR
- depends on SND_SOC_AMD_ACP && I2C && GPIOLIB
+ depends on SND_SOC_AMD_ACP && I2C && GPIOLIB && ACPI
help
- This option enables machine driver for DA7219 and MAX9835.
+ This option enables machine driver for DA7219, RT5682 and MAX9835.
config SND_SOC_AMD_CZ_RT5645_MACH
tristate "AMD CZ support for RT5645"
@@ -21,6 +23,18 @@ config SND_SOC_AMD_CZ_RT5645_MACH
help
This option enables machine driver for rt5645.
+config SND_SOC_AMD_ST_ES8336_MACH
+ tristate "AMD ST support for ES8336"
+ select SND_SOC_ACPI if ACPI
+ select SND_SOC_ES8316
+ depends on SND_SOC_AMD_ACP && ACPI
+ depends on I2C
+ help
+ This option enables machine driver for Jadeite platform
+ using es8336 codec.
+ Say m if you have such a device.
+ If unsure select "N".
+
config SND_SOC_AMD_ACP3x
tristate "AMD Audio Coprocessor-v3.x support"
depends on X86 && PCI
@@ -29,17 +43,20 @@ config SND_SOC_AMD_ACP3x
config SND_SOC_AMD_RV_RT5682_MACH
tristate "AMD RV support for RT5682"
+ select CLK_FIXED_FCH
select SND_SOC_RT5682_I2C
select SND_SOC_MAX98357A
select SND_SOC_CROS_EC_CODEC
select I2C_CROS_EC_TUNNEL
select SND_SOC_RT1015
- depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC
+ select SND_SOC_RT1015P
+ depends on SND_SOC_AMD_ACP3x && I2C && CROS_EC && GPIOLIB
help
This option enables machine driver for RT5682 and MAX9835.
config SND_SOC_AMD_RENOIR
tristate "AMD Audio Coprocessor - Renoir support"
+ select SND_AMD_ACP_CONFIG
depends on X86 && PCI
help
This option enables ACP support for Renoir platform
@@ -47,6 +64,124 @@ config SND_SOC_AMD_RENOIR
config SND_SOC_AMD_RENOIR_MACH
tristate "AMD Renoir support for DMIC"
select SND_SOC_DMIC
- depends on SND_SOC_AMD_RENOIR
+ depends on SND_SOC_AMD_RENOIR && GPIOLIB
help
This option enables machine driver for DMIC
+
+config SND_SOC_AMD_ACP5x
+ tristate "AMD Audio Coprocessor-v5.x I2S support"
+ depends on X86 && PCI
+ select SND_AMD_ACP_CONFIG
+ help
+ This option enables ACP v5.x support on AMD platform
+
+ By enabling this flag build will trigger for ACP PCI driver,
+ ACP DMA driver, CPU DAI driver.
+
+config SND_SOC_AMD_VANGOGH_MACH
+ tristate "AMD Vangogh support for NAU8821/CS35L41/MAX98388"
+ select SND_SOC_NAU8821
+ select SND_SOC_CS35L41_SPI
+ select SND_SOC_MAX98388
+ select SND_AMD_ACP_CONFIG
+ depends on SND_SOC_AMD_ACP5x && I2C && SPI_MASTER
+ help
+ This option enables machine driver for Vangogh platform
+ using NAU8821 and either CS35L41 or MAX98388 codecs.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP6x
+ tristate "AMD Audio Coprocessor-v6.x Yellow Carp support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e ACP v6.x support on
+ AMD Yellow Carp platform. By enabling this flag build will be
+ triggered for ACP PCI driver, ACP PDM DMA driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_YC_MACH
+ tristate "AMD YC support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_ACP6x && ACPI
+ help
+ This option enables machine driver for Yellow Carp platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_AMD_ACP_CONFIG
+ tristate "AMD ACP configuration selection"
+ select SND_SOC_ACPI if ACPI
+ help
+ This option adds an auto detection to determine which ACP
+ driver modules to use
+
+source "sound/soc/amd/acp/Kconfig"
+
+config SND_SOC_AMD_RPL_ACP6x
+ tristate "AMD Audio Coprocessor-v6.2 RPL support"
+ depends on X86 && PCI
+ help
+ This option enables Audio Coprocessor i.e. ACP v6.2 support on
+ AMD RPL platform. By enabling this flag build will be
+ triggered for ACP PCI driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_ACP63_TOPLEVEL
+ tristate "support for AMD platforms with ACP version >= 6.3"
+ default SND_AMD_ACP_CONFIG
+ depends on SND_AMD_ACP_CONFIG
+ depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
+ depends on X86 || COMPILE_TEST
+ help
+ This adds support for AMD platforms with ACP version >= 6.3.
+ Say Y if you have such a device.
+ If unsure select "N".
+
+if SND_SOC_AMD_ACP63_TOPLEVEL
+
+config SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ tristate
+ select SND_AMD_SOUNDWIRE_ACPI if ACPI
+
+config SND_SOC_AMD_SOUNDWIRE
+ tristate "Support for SoundWire based AMD platforms"
+ default SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on ACPI
+ depends on SOUNDWIRE_AMD
+ help
+ This adds support for SoundWire for AMD platforms.
+ Say Y if you want to enable SoundWire links with SOF.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS
+ tristate "AMD Audio Coprocessor-v6.3/v7.0/v7.1 support"
+ select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
+ select SND_SOC_ACPI_AMD_MATCH
+ depends on X86 && PCI && ACPI
+ help
+ This option enables Audio Coprocessor i.e ACP6.3/ACP7.0/ACP7.1
+ variants support. By enabling this flag build will be triggered
+ for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire DMA
+ driver.
+ Say m if you have such a device.
+ If unsure select "N".
+
+config SND_SOC_AMD_PS_MACH
+ tristate "AMD PINK SARDINE support for DMIC"
+ select SND_SOC_DMIC
+ depends on SND_SOC_AMD_PS
+ help
+ This option enables machine driver for Pink Sardine platform
+ using dmic. ACP IP has PDM Decoder block with DMA controller.
+ DMIC can be connected directly to ACP IP.
+ Say m if you have such a device.
+ If unsure select "N".
+
+endif
diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile
index e6df2f72a2a1..4f89d962cce2 100644
--- a/sound/soc/amd/Makefile
+++ b/sound/soc/amd/Makefile
@@ -1,12 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-only
-acp_audio_dma-objs := acp-pcm-dma.o
-snd-soc-acp-da7219mx98357-mach-objs := acp-da7219-max98357a.o
-snd-soc-acp-rt5645-mach-objs := acp-rt5645.o
-snd-soc-acp-rt5682-mach-objs := acp3x-rt5682-max9836.o
+acp_audio_dma-y := acp-pcm-dma.o
+snd-soc-acp-da7219mx98357-mach-y := acp-da7219-max98357a.o
+snd-soc-acp-rt5645-mach-y := acp-rt5645.o
+snd-soc-acp-es8336-mach-y := acp-es8336.o
+snd-soc-acp-rt5682-mach-y := acp3x-rt5682-max9836.o
+snd-acp-config-y := acp-config.o
obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o
obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o
obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o
+obj-$(CONFIG_SND_SOC_AMD_ST_ES8336_MACH) += snd-soc-acp-es8336-mach.o
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/
+obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
+obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += rpl/
+obj-$(CONFIG_SND_SOC_AMD_PS) += ps/
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
new file mode 100644
index 000000000000..365209ea53f3
--- /dev/null
+++ b/sound/soc/amd/acp-config.c
@@ -0,0 +1,337 @@
+// 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) 2021, 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/* ACP machine configuration module */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "../sof/amd/acp.h"
+#include "mach-config.h"
+
+#define ACP_7_0_REV 0x70
+
+static int acp_quirk_data;
+
+static const struct config_entry config_table[] = {
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Majolica-CZN"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_SOF,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
+ },
+ },
+ {}
+ },
+ },
+ {
+ .flags = FLAG_AMD_LEGACY,
+ .device = ACP_PCI_DEV_ID,
+ .dmi_table = (const struct dmi_system_id []) {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+ },
+ },
+ {}
+ },
+ },
+};
+
+static int snd_amd_acp_acpi_find_config(struct pci_dev *pci)
+{
+ const union acpi_object *obj;
+ int acp_flag = FLAG_AMD_LEGACY_ONLY_DMIC;
+
+ if (!acpi_dev_get_property(ACPI_COMPANION(&pci->dev), "acp-audio-config-flag",
+ ACPI_TYPE_INTEGER, &obj))
+ acp_flag = obj->integer.value;
+
+ return acp_flag;
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci)
+{
+ const struct config_entry *table = config_table;
+ u16 device = pci->device;
+ int i;
+
+ /* Do not enable FLAGS on older platforms with Rev Id zero
+ * For platforms which has ACP 7.0 or higher, read the acp
+ * config flag from BIOS ACPI table and for older platforms
+ * read it from DMI tables.
+ */
+ if (!pci->revision)
+ return 0;
+ else if (pci->revision >= ACP_7_0_REV)
+ return snd_amd_acp_acpi_find_config(pci);
+
+ for (i = 0; i < ARRAY_SIZE(config_table); i++, table++) {
+ if (table->device != device)
+ continue;
+ if (table->dmi_table && !dmi_check_system(table->dmi_table))
+ continue;
+ acp_quirk_data = table->flags;
+ return table->flags;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(snd_amd_acp_find_config);
+
+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_codecs amp_max98388 = {
+ .num_codecs = 1,
+ .codecs = {"ADS8388"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "10EC5682",
+ .drv_name = "rt5682-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-max",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-rt1019",
+ .pdata = (void *)&acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-rn-rt5682-rt1019.tplg",
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-dsp",
+ .pdata = (void *)&acp_quirk_data,
+ .fw_filename = "sof-rn.ri",
+ .sof_tplg_filename = "sof-acp.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[] = {
+ {
+ .id = "NVTN2020",
+ .drv_name = "nau8821-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max98388,
+ .fw_filename = "sof-vangogh.ri",
+ .sof_tplg_filename = "sof-vangogh-nau8821-max.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_vangogh_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[] = {
+ {
+ .id = "AMDI1019",
+ .drv_name = "rmb-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-acp-rmb.tplg",
+ },
+ {
+ .id = "10508825",
+ .drv_name = "nau8825-max",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-rmb-nau8825-max98360.tplg",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rt5682s-hs-rt1019",
+ .pdata = &acp_quirk_data,
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ .fw_filename = "sof-rmb.ri",
+ .sof_tplg_filename = "sof-rmb-rt5682s-rt1019.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_rmb_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[] = {
+ {
+ .id = "AMDI1019",
+ .drv_name = "acp63-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-acp_6_3.ri",
+ .sof_tplg_filename = "sof-acp_6_3.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[] = {
+ {
+ .id = "AMDI1010",
+ .drv_name = "acp70-dsp",
+ .pdata = &acp_quirk_data,
+ .fw_filename = "sof-acp_7_0.ri",
+ .sof_tplg_filename = "sof-acp_7_0.tplg",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_machines);
+
+MODULE_DESCRIPTION("AMD ACP Machine Configuration Module");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index a7702e64ec51..42aa009c4e13 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -1,27 +1,8 @@
-/*
- * Machine driver for AMD ACP Audio engine using DA7219 & MAX98357 codec
- *
- * Copyright 2017 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- */
+// SPDX-License-Identifier: MIT
+//
+// Machine driver for AMD ACP Audio engine using DA7219, RT5682 & MAX98357 codec
+//
+//Copyright 2017-2021 Advanced Micro Devices, Inc.
#include <sound/core.h>
#include <sound/soc.h>
@@ -40,21 +21,40 @@
#include "acp.h"
#include "../codecs/da7219.h"
-#include "../codecs/da7219-aad.h"
+#include "../codecs/rt5682.h"
#define CZ_PLAT_CLK 48000000
#define DUAL_CHANNEL 2
+#define RT5682_PLL_FREQ (48000 * 512)
static struct snd_soc_jack cz_jack;
+static struct snd_soc_jack_pin cz_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Line Out",
+ .mask = SND_JACK_LINEOUT,
+ },
+};
+
static struct clk *da7219_dai_wclk;
static struct clk *da7219_dai_bclk;
-extern bool bt_uart_enable;
+static struct clk *rt5682_dai_wclk;
+static struct clk *rt5682_dai_bclk;
+
+void *acp_soc_is_rltk_max(struct device *dev);
static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
{
int ret;
struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct snd_soc_component *component = codec_dai->component;
dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
@@ -73,14 +73,21 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- da7219_dai_wclk = clk_get(component->dev, "da7219-dai-wclk");
- da7219_dai_bclk = clk_get(component->dev, "da7219-dai-bclk");
-
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_LINEOUT |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &cz_jack, NULL, 0);
+ da7219_dai_wclk = devm_clk_get(component->dev, "da7219-dai-wclk");
+ if (IS_ERR(da7219_dai_wclk))
+ return PTR_ERR(da7219_dai_wclk);
+
+ da7219_dai_bclk = devm_clk_get(component->dev, "da7219-dai-bclk");
+ if (IS_ERR(da7219_dai_bclk))
+ return PTR_ERR(da7219_dai_bclk);
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
@@ -91,7 +98,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
- da7219_aad_jack_det(component, &cz_jack);
+ snd_soc_component_set_jack(component, &cz_jack, NULL);
return 0;
}
@@ -99,7 +106,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd)
static int da7219_clk_enable(struct snd_pcm_substream *substream)
{
int ret = 0;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
/*
* Set wclk to 48000 because the rate constraint of this driver is
@@ -123,6 +130,98 @@ static void da7219_clk_disable(void)
clk_disable_unprepare(da7219_dai_bclk);
}
+static int cz_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+
+ dev_info(codec_dai->dev, "codec dai name = %s\n", codec_dai->name);
+
+ /* Set codec sysclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev,
+ "Failed to set rt5682 SYSCLK: %d\n", ret);
+ return ret;
+ }
+ /* set codec PLL */
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ CZ_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set rt5682 PLL: %d\n", ret);
+ return ret;
+ }
+
+ rt5682_dai_wclk = devm_clk_get(component->dev, "rt5682-dai-wclk");
+ if (IS_ERR(rt5682_dai_wclk))
+ return PTR_ERR(rt5682_dai_wclk);
+
+ rt5682_dai_bclk = devm_clk_get(component->dev, "rt5682-dai-bclk");
+ if (IS_ERR(rt5682_dai_bclk))
+ return PTR_ERR(rt5682_dai_bclk);
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+ snd_jack_set_key(cz_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+ ret = snd_soc_component_set_jack(component, &cz_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int rt5682_clk_enable(struct snd_pcm_substream *substream)
+{
+ int ret;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+
+ /*
+ * Set wclk to 48000 because the rate constraint of this driver is
+ * 48000. ADAU7002 spec: "The ADAU7002 requires a BCLK rate that is
+ * minimum of 64x the LRCLK sample rate." RT5682 is the only clk
+ * source so for all codecs we have to limit bclk to 64X lrclk.
+ */
+ ret = clk_set_rate(rt5682_dai_wclk, 48000);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting wclk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(rt5682_dai_bclk, 48000 * 64);
+ if (ret) {
+ dev_err(rtd->dev, "Error setting bclk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(rt5682_dai_wclk);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't enable wclk %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static void rt5682_clk_disable(void)
+{
+ clk_disable_unprepare(rt5682_dai_wclk);
+}
+
static const unsigned int channels[] = {
DUAL_CHANNEL,
};
@@ -146,7 +245,7 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = {
static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -167,7 +266,7 @@ static int cz_da7219_play_startup(struct snd_pcm_substream *substream)
static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -189,7 +288,7 @@ static int cz_da7219_cap_startup(struct snd_pcm_substream *substream)
static int cz_max_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -210,7 +309,7 @@ static int cz_max_startup(struct snd_pcm_substream *substream)
static int cz_dmic0_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -231,7 +330,7 @@ static int cz_dmic0_startup(struct snd_pcm_substream *substream)
static int cz_dmic1_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -255,6 +354,118 @@ static void cz_da7219_shutdown(struct snd_pcm_substream *substream)
da7219_clk_disable();
}
+static int cz_rt5682_play_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_cap_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL1;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_max_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->play_i2s_instance = I2S_BT_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_dmic0_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_BT_INSTANCE;
+ return rt5682_clk_enable(substream);
+}
+
+static int cz_rt5682_dmic1_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_platform_info *machine = snd_soc_card_get_drvdata(card);
+
+ /*
+ * On this platform for PCM device we support stereo
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return rt5682_clk_enable(substream);
+}
+
+static void cz_rt5682_shutdown(struct snd_pcm_substream *substream)
+{
+ rt5682_clk_disable();
+}
+
static const struct snd_soc_ops cz_da7219_play_ops = {
.startup = cz_da7219_play_startup,
.shutdown = cz_da7219_shutdown,
@@ -280,31 +491,59 @@ static const struct snd_soc_ops cz_dmic1_cap_ops = {
.shutdown = cz_da7219_shutdown,
};
+static const struct snd_soc_ops cz_rt5682_play_ops = {
+ .startup = cz_rt5682_play_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_cap_ops = {
+ .startup = cz_rt5682_cap_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_max_play_ops = {
+ .startup = cz_rt5682_max_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_dmic0_cap_ops = {
+ .startup = cz_rt5682_dmic0_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
+static const struct snd_soc_ops cz_rt5682_dmic1_cap_ops = {
+ .startup = cz_rt5682_dmic1_startup,
+ .shutdown = cz_rt5682_shutdown,
+};
+
SND_SOC_DAILINK_DEF(designware1,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
SND_SOC_DAILINK_DEF(designware2,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
SND_SOC_DAILINK_DEF(designware3,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3")));
SND_SOC_DAILINK_DEF(dlgs,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", "da7219-hifi")));
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
SND_SOC_DAILINK_DEF(mx,
DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
SND_SOC_DAILINK_DEF(adau,
DAILINK_COMP_ARRAY(COMP_CODEC("ADAU7002:00", "adau7002-hifi")));
SND_SOC_DAILINK_DEF(platform,
- DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
static struct snd_soc_dai_link cz_dai_7219_98357[] = {
{
.name = "amd-da7219-play",
.stream_name = "Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = cz_da7219_init,
- .dpcm_playback = 1,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_da7219_play_ops,
SND_SOC_DAILINK_REG(designware1, dlgs, platform),
},
@@ -312,8 +551,9 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "amd-da7219-cap",
.stream_name = "Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .dpcm_capture = 1,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_da7219_cap_ops,
SND_SOC_DAILINK_REG(designware2, dlgs, platform),
},
@@ -321,8 +561,9 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "amd-max98357-play",
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .dpcm_playback = 1,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_max_play_ops,
SND_SOC_DAILINK_REG(designware3, mx, platform),
},
@@ -331,8 +572,9 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "dmic0",
.stream_name = "DMIC0 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .dpcm_capture = 1,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_dmic0_cap_ops,
SND_SOC_DAILINK_REG(designware3, adau, platform),
},
@@ -341,16 +583,74 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = {
.name = "dmic1",
.stream_name = "DMIC1 Capture",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
- .dpcm_capture = 1,
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
.ops = &cz_dmic1_cap_ops,
SND_SOC_DAILINK_REG(designware2, adau, platform),
},
};
+static struct snd_soc_dai_link cz_dai_5682_98357[] = {
+ {
+ .name = "amd-rt5682-play",
+ .stream_name = "Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .init = cz_rt5682_init,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_play_ops,
+ SND_SOC_DAILINK_REG(designware1, rt5682, platform),
+ },
+ {
+ .name = "amd-rt5682-cap",
+ .stream_name = "Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, rt5682, platform),
+ },
+ {
+ .name = "amd-max98357-play",
+ .stream_name = "HiFi Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .playback_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_max_play_ops,
+ SND_SOC_DAILINK_REG(designware3, mx, platform),
+ },
+ {
+ /* C panel DMIC */
+ .name = "dmic0",
+ .stream_name = "DMIC0 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_dmic0_cap_ops,
+ SND_SOC_DAILINK_REG(designware3, adau, platform),
+ },
+ {
+ /* A/B panel DMIC */
+ .name = "dmic1",
+ .stream_name = "DMIC1 Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .capture_only = 1,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .ops = &cz_rt5682_dmic1_cap_ops,
+ SND_SOC_DAILINK_REG(designware2, adau, platform),
+ },
+};
+
static const struct snd_soc_dapm_widget cz_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speakers", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
};
@@ -363,9 +663,18 @@ static const struct snd_soc_dapm_route cz_audio_route[] = {
{"PDM_DAT", NULL, "Int Mic"},
};
+static const struct snd_soc_dapm_route cz_rt5682_audio_route[] = {
+ {"Headphones", NULL, "HPOL"},
+ {"Headphones", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Speakers", NULL, "Speaker"},
+ {"PDM_DAT", NULL, "Int Mic"},
+};
+
static const struct snd_kcontrol_new cz_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphones"),
SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Line Out"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
};
@@ -383,6 +692,28 @@ static struct snd_soc_card cz_card = {
.num_controls = ARRAY_SIZE(cz_mc_controls),
};
+static struct snd_soc_card cz_rt5682_card = {
+ .name = "acpr5682m98357",
+ .owner = THIS_MODULE,
+ .dai_link = cz_dai_5682_98357,
+ .num_links = ARRAY_SIZE(cz_dai_5682_98357),
+ .dapm_widgets = cz_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cz_widgets),
+ .dapm_routes = cz_rt5682_audio_route,
+ .controls = cz_mc_controls,
+ .num_controls = ARRAY_SIZE(cz_mc_controls),
+};
+
+void *acp_soc_is_rltk_max(struct device *dev)
+{
+ const struct acpi_device_id *match;
+
+ match = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!match)
+ return NULL;
+ return (void *)match->driver_data;
+}
+
static struct regulator_consumer_supply acp_da7219_supplies[] = {
REGULATOR_SUPPLY("VDD", "i2c-DLGS7219:00"),
REGULATOR_SUPPLY("VDDMIC", "i2c-DLGS7219:00"),
@@ -402,7 +733,7 @@ static struct regulator_config acp_da7219_cfg = {
.init_data = &acp_da7219_data,
};
-static struct regulator_ops acp_da7219_ops = {
+static const struct regulator_ops acp_da7219_ops = {
};
static const struct regulator_desc acp_da7219_desc = {
@@ -420,39 +751,44 @@ static int cz_probe(struct platform_device *pdev)
struct snd_soc_card *card;
struct acp_platform_info *machine;
struct regulator_dev *rdev;
-
- acp_da7219_cfg.dev = &pdev->dev;
- rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc,
- &acp_da7219_cfg);
- if (IS_ERR(rdev)) {
- dev_err(&pdev->dev, "Failed to register regulator: %d\n",
- (int)PTR_ERR(rdev));
- return -EINVAL;
+ struct device *dev = &pdev->dev;
+
+ card = (struct snd_soc_card *)acp_soc_is_rltk_max(dev);
+ if (!card)
+ return -ENODEV;
+ if (!strcmp(card->name, "acpd7219m98357")) {
+ acp_da7219_cfg.dev = &pdev->dev;
+ rdev = devm_regulator_register(&pdev->dev, &acp_da7219_desc,
+ &acp_da7219_cfg);
+ if (IS_ERR(rdev)) {
+ dev_err(&pdev->dev, "Failed to register regulator: %d\n",
+ (int)PTR_ERR(rdev));
+ return -EINVAL;
+ }
}
machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info),
GFP_KERNEL);
if (!machine)
return -ENOMEM;
- card = &cz_card;
- cz_card.dev = &pdev->dev;
+ card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
- ret = devm_snd_soc_register_card(&pdev->dev, &cz_card);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
- dev_err(&pdev->dev,
- "devm_snd_soc_register_card(%s) failed: %d\n",
- cz_card.name, ret);
- return ret;
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
}
- bt_uart_enable = !device_property_read_bool(&pdev->dev,
- "bt-pad-enable");
+ acp_bt_uart_enable = !device_property_read_bool(&pdev->dev,
+ "bt-pad-enable");
return 0;
}
#ifdef CONFIG_ACPI
static const struct acpi_device_id cz_audio_acpi_match[] = {
- { "AMD7219", 0 },
+ { "AMD7219", (unsigned long)&cz_card },
+ { "AMDI5682", (unsigned long)&cz_rt5682_card},
{},
};
MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match);
@@ -470,5 +806,6 @@ static struct platform_driver cz_pcm_driver = {
module_platform_driver(cz_pcm_driver);
MODULE_AUTHOR("akshu.agrawal@amd.com");
-MODULE_DESCRIPTION("DA7219 & MAX98357A audio support");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("DA7219, RT5682 & MAX98357A audio support");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
new file mode 100644
index 000000000000..b16dde0e2987
--- /dev/null
+++ b/sound/soc/amd/acp-es8336.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Stoney platform using ES8336 Codec
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+#include "acp.h"
+
+#define DUAL_CHANNEL 2
+#define DRV_NAME "acp2x_mach"
+#define ST_JADEITE 1
+#define ES8336_PLL_FREQ (48000 * 256)
+
+static unsigned long acp2x_machine_id;
+static struct snd_soc_jack st_jack;
+static struct device *codec_dev;
+static struct gpio_desc *gpio_pa;
+
+static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpiod_set_value_cansleep(gpio_pa, true);
+ else
+ gpiod_set_value_cansleep(gpio_pa, false);
+
+ return 0;
+}
+
+static struct snd_soc_jack_pin st_es8316_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int st_es8336_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct snd_soc_component *codec;
+
+ codec = snd_soc_rtd_to_codec(rtd, 0)->component;
+ card = rtd->card;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset", SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &st_jack, st_es8316_jack_pins,
+ ARRAY_SIZE(st_es8316_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+ snd_jack_set_key(st_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ ret = snd_soc_component_set_jack(codec, &st_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static const unsigned int st_channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int st_rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list st_constraints_rates = {
+ .count = ARRAY_SIZE(st_rates),
+ .list = st_rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list st_constraints_channels = {
+ .count = ARRAY_SIZE(st_channels),
+ .list = st_channels,
+ .mask = 0,
+};
+
+static int st_es8336_codec_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ card = rtd->card;
+ machine = snd_soc_card_get_drvdata(card);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, ES8336_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &st_constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &st_constraints_rates);
+
+ machine->play_i2s_instance = I2S_MICSP_INSTANCE;
+ machine->cap_i2s_instance = I2S_MICSP_INSTANCE;
+ machine->capture_channel = CAP_CHANNEL0;
+ return 0;
+}
+
+static const struct snd_soc_ops st_es8336_ops = {
+ .startup = st_es8336_codec_startup,
+};
+
+SND_SOC_DAILINK_DEF(designware1,
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+SND_SOC_DAILINK_DEF(platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
+
+static struct snd_soc_dai_link st_dai_es8336[] = {
+ {
+ .name = "amdes8336",
+ .stream_name = "ES8336 HiFi Play",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBP_CFP,
+ .trigger_stop = SND_SOC_TRIGGER_ORDER_LDC,
+ .init = st_es8336_init,
+ .ops = &st_es8336_ops,
+ SND_SOC_DAILINK_REG(designware1, codec, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget st_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ sof_es8316_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route st_audio_route[] = {
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"MIC1", NULL, "Headset Mic"},
+ {"MIC2", NULL, "Internal Mic"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+static const struct snd_kcontrol_new st_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static const struct acpi_gpio_params pa_enable_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
+ { "pa-enable-gpios", &pa_enable_gpio, 1 },
+ { }
+};
+
+static int st_es8336_late_probe(struct snd_soc_card *card)
+{
+ struct acpi_device *adev;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev("ESSX8336", NULL, -1);
+ if (!adev)
+ return -ENODEV;
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev) {
+ dev_err(card->dev, "can not find codec dev\n");
+ return -ENODEV;
+ }
+
+ ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios);
+ if (ret)
+ dev_warn(card->dev, "Failed to add driver gpios\n");
+
+ gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio_pa)) {
+ ret = dev_err_probe(card->dev, PTR_ERR(gpio_pa),
+ "could not get pa-enable GPIO\n");
+ put_device(codec_dev);
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_card st_card = {
+ .name = "acpes8336",
+ .owner = THIS_MODULE,
+ .dai_link = st_dai_es8336,
+ .num_links = ARRAY_SIZE(st_dai_es8336),
+ .dapm_widgets = st_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(st_widgets),
+ .dapm_routes = st_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(st_audio_route),
+ .controls = st_mc_controls,
+ .num_controls = ARRAY_SIZE(st_mc_controls),
+ .late_probe = st_es8336_late_probe,
+};
+
+static int st_es8336_quirk_cb(const struct dmi_system_id *id)
+{
+ acp2x_machine_id = ST_JADEITE;
+ return 1;
+}
+
+static const struct dmi_system_id st_es8336_quirk_table[] = {
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMD"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jadeite"),
+ },
+ },
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "IP3 Technology CO.,Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN1D"),
+ },
+ },
+ {
+ .callback = st_es8336_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Standard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ASN10"),
+ },
+ },
+ {}
+};
+
+static int st_es8336_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct snd_soc_card *card;
+ struct acp_platform_info *machine;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct acp_platform_info), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ dmi_check_system(st_es8336_quirk_table);
+ switch (acp2x_machine_id) {
+ case ST_JADEITE:
+ card = &st_card;
+ st_card.dev = &pdev->dev;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, &st_card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id st_audio_acpi_match[] = {
+ {"AMDI8336", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, st_audio_acpi_match);
+#endif
+
+static struct platform_driver st_mach_driver = {
+ .driver = {
+ .name = "st-es8316",
+ .acpi_match_table = ACPI_PTR(st_audio_acpi_match),
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = st_es8336_probe,
+};
+
+module_platform_driver(st_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("st-es8316 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index 143155a840ac..897dde630022 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -36,8 +36,8 @@
#define ST_MIN_BUFFER ST_MAX_BUFFER
#define DRV_NAME "acp_audio_dma"
-bool bt_uart_enable = true;
-EXPORT_SYMBOL(bt_uart_enable);
+bool acp_bt_uart_enable = true;
+EXPORT_SYMBOL(acp_bt_uart_enable);
static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
@@ -156,7 +156,7 @@ static void config_acp_dma_channel(void __iomem *acp_mmio, u8 ch_num,
acp_reg_write(priority_level, acp_mmio, mmACP_DMA_PRIO_0 + ch_num);
}
-/* Initialize a dma descriptor in SRAM based on descritor information passed */
+/* Initialize a dma descriptor in SRAM based on descriptor information passed */
static void config_dma_descriptor_in_sram(void __iomem *acp_mmio,
u16 descr_idx,
acp_dma_dscr_transfer_t *descr_info)
@@ -288,7 +288,7 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size,
&dmadscr[i]);
}
pre_config_reset(acp_mmio, ch);
- /* Configure the DMA channel with the above descriptore */
+ /* Configure the DMA channel with the above descriptor */
config_acp_dma_channel(acp_mmio, ch, dma_dscr_idx - 1,
NUM_DSCRS_PER_CHANNEL,
ACP_DMA_PRIORITY_LEVEL_NORMAL);
@@ -322,7 +322,7 @@ static void acp_pte_config(void __iomem *acp_mmio, dma_addr_t addr,
high |= BIT(31);
acp_reg_write(high, acp_mmio, mmACP_SRBM_Targ_Idx_Data);
- /* Move to next physically contiguos page */
+ /* Move to next physically contiguous page */
addr += PAGE_SIZE;
}
}
@@ -433,6 +433,7 @@ static void acp_dma_start(void __iomem *acp_mmio, u16 ch_num, bool is_circular)
case I2S_TO_ACP_DMA_CH_NUM:
case ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM:
case I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM:
+ case ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM:
dma_ctrl |= ACP_DMA_CNTL_0__DMAChIOCEn_MASK;
break;
default:
@@ -596,17 +597,17 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
acp_reg_write(val, acp_mmio, mmACP_SOFT_RESET);
/* For BT instance change pins from UART to BT */
- if (!bt_uart_enable) {
+ if (!acp_bt_uart_enable) {
val = acp_reg_read(acp_mmio, mmACP_BT_UART_PAD_SEL);
val |= ACP_BT_UART_PAD_SELECT_MASK;
acp_reg_write(val, acp_mmio, mmACP_BT_UART_PAD_SEL);
}
- /* initiailize Onion control DAGB register */
+ /* initialize Onion control DAGB register */
acp_reg_write(ACP_ONION_CNTL_DEFAULT, acp_mmio,
mmACP_AXI2DAGB_ONION_CNTL);
- /* initiailize Garlic control DAGB registers */
+ /* initialize Garlic control DAGB registers */
acp_reg_write(ACP_GARLIC_CNTL_DEFAULT, acp_mmio,
mmACP_AXI2DAGB_GARLIC_CNTL);
@@ -621,7 +622,7 @@ static int acp_init(void __iomem *acp_mmio, u32 asic_type)
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) */
+ /* Num of descriptors 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);
@@ -710,6 +711,13 @@ static irqreturn_t dma_irq_handler(int irq, void *arg)
acp_mmio, mmACP_EXTERNAL_INTR_STAT);
}
+ if ((intr_flag & BIT(ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM)) != 0) {
+ valid_irq = true;
+ snd_pcm_period_elapsed(irq_data->play_i2s_micsp_stream);
+ acp_reg_write((intr_flag & BIT(ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM)) << 16,
+ acp_mmio, mmACP_EXTERNAL_INTR_STAT);
+ }
+
if ((intr_flag & BIT(ACP_TO_I2S_DMA_BT_INSTANCE_CH_NUM)) != 0) {
valid_irq = true;
snd_pcm_period_elapsed(irq_data->play_i2sbt_stream);
@@ -807,7 +815,8 @@ static int acp_dma_open(struct snd_soc_component *component,
* stream is not closed
*/
if (!intr_data->play_i2ssp_stream && !intr_data->capture_i2ssp_stream &&
- !intr_data->play_i2sbt_stream && !intr_data->capture_i2sbt_stream)
+ !intr_data->play_i2sbt_stream && !intr_data->capture_i2sbt_stream &&
+ !intr_data->play_i2s_micsp_stream)
acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -840,7 +849,7 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
u32 val = 0;
struct snd_pcm_runtime *runtime;
struct audio_substream_data *rtd;
- struct snd_soc_pcm_runtime *prtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *prtd = snd_soc_substream_to_rtd(substream);
struct audio_drv_data *adata = dev_get_drvdata(component->dev);
struct snd_soc_card *card = prtd->card;
struct acp_platform_info *pinfo = snd_soc_card_get_drvdata(card);
@@ -867,6 +876,9 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
case I2S_BT_INSTANCE:
val |= ACP_I2S_BT_16BIT_RESOLUTION_EN;
break;
+ case I2S_MICSP_INSTANCE:
+ val |= ACP_I2S_MICSP_16BIT_RESOLUTION_EN;
+ break;
case I2S_SP_INSTANCE:
default:
val |= ACP_I2S_SP_16BIT_RESOLUTION_EN;
@@ -876,6 +888,7 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
case I2S_BT_INSTANCE:
val |= ACP_I2S_BT_16BIT_RESOLUTION_EN;
break;
+ case I2S_MICSP_INSTANCE:
case I2S_SP_INSTANCE:
default:
val |= ACP_I2S_MIC_16BIT_RESOLUTION_EN;
@@ -901,6 +914,27 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
mmACP_I2S_BT_TRANSMIT_BYTE_CNT_LOW;
adata->play_i2sbt_stream = substream;
break;
+ case I2S_MICSP_INSTANCE:
+ switch (adata->asic_type) {
+ case CHIP_STONEY:
+ rtd->pte_offset = ACP_ST_PLAYBACK_PTE_OFFSET;
+ break;
+ default:
+ rtd->pte_offset = ACP_PLAYBACK_PTE_OFFSET;
+ }
+ rtd->ch1 = SYSRAM_TO_ACP_MICSP_INSTANCE_CH_NUM;
+ rtd->ch2 = ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM;
+ rtd->sram_bank = ACP_SRAM_BANK_1_ADDRESS;
+ rtd->destination = TO_ACP_I2S_2;
+ rtd->dma_dscr_idx_1 = PLAYBACK_START_DMA_DESCR_CH4;
+ rtd->dma_dscr_idx_2 = PLAYBACK_START_DMA_DESCR_CH5;
+ rtd->byte_cnt_high_reg_offset =
+ mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_HIGH;
+ rtd->byte_cnt_low_reg_offset =
+ mmACP_I2S_MICSP_TRANSMIT_BYTE_CNT_LOW;
+
+ adata->play_i2s_micsp_stream = substream;
+ break;
case I2S_SP_INSTANCE:
default:
switch (adata->asic_type) {
@@ -939,6 +973,7 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
rtd->dma_curr_dscr = mmACP_DMA_CUR_DSCR_11;
adata->capture_i2sbt_stream = substream;
break;
+ case I2S_MICSP_INSTANCE:
case I2S_SP_INSTANCE:
default:
rtd->pte_offset = ACP_CAPTURE_PTE_OFFSET;
@@ -969,7 +1004,7 @@ static int acp_dma_hw_params(struct snd_soc_component *component,
acp_set_sram_bank_state(rtd->acp_mmio, 0, true);
/* Save for runtime private data */
- rtd->dma_addr = substream->dma_buffer.addr;
+ rtd->dma_addr = runtime->dma_addr;
rtd->order = get_order(size);
/* Fill the page table entries in ACP SRAM */
@@ -1003,6 +1038,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
struct snd_pcm_runtime *runtime = substream->runtime;
struct audio_substream_data *rtd = runtime->private_data;
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
if (!rtd)
return -EINVAL;
@@ -1023,7 +1059,7 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
}
if (bytescount > 0) {
delay = do_div(bytescount, period_bytes);
- runtime->delay = bytes_to_frames(runtime, delay);
+ adata->delay += bytes_to_frames(runtime, delay);
}
} else {
buffersize = frames_to_bytes(runtime, runtime->buffer_size);
@@ -1035,11 +1071,15 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
return bytes_to_frames(runtime, pos);
}
-static int acp_dma_mmap(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static snd_pcm_sframes_t acp_dma_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
{
- return snd_pcm_lib_default_mmap(substream, vma);
+ struct audio_drv_data *adata = dev_get_drvdata(component->dev);
+ snd_pcm_sframes_t delay = adata->delay;
+
+ adata->delay = 0;
+
+ return delay;
}
static int acp_dma_prepare(struct snd_soc_component *component,
@@ -1155,6 +1195,9 @@ static int acp_dma_close(struct snd_soc_component *component,
case I2S_BT_INSTANCE:
adata->play_i2sbt_stream = NULL;
break;
+ case I2S_MICSP_INSTANCE:
+ adata->play_i2s_micsp_stream = NULL;
+ break;
case I2S_SP_INSTANCE:
default:
adata->play_i2ssp_stream = NULL;
@@ -1176,6 +1219,7 @@ static int acp_dma_close(struct snd_soc_component *component,
case I2S_BT_INSTANCE:
adata->capture_i2sbt_stream = NULL;
break;
+ case I2S_MICSP_INSTANCE:
case I2S_SP_INSTANCE:
default:
adata->capture_i2ssp_stream = NULL;
@@ -1192,7 +1236,8 @@ static int acp_dma_close(struct snd_soc_component *component,
* another stream is also not active.
*/
if (!adata->play_i2ssp_stream && !adata->capture_i2ssp_stream &&
- !adata->play_i2sbt_stream && !adata->capture_i2sbt_stream)
+ !adata->play_i2sbt_stream && !adata->capture_i2sbt_stream &&
+ !adata->play_i2s_micsp_stream)
acp_reg_write(0, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB);
kfree(rtd);
return 0;
@@ -1205,16 +1250,15 @@ static const struct snd_soc_component_driver acp_asoc_platform = {
.hw_params = acp_dma_hw_params,
.trigger = acp_dma_trigger,
.pointer = acp_dma_pointer,
- .mmap = acp_dma_mmap,
+ .delay = acp_dma_delay,
.prepare = acp_dma_prepare,
.pcm_construct = acp_dma_new,
};
static int acp_audio_probe(struct platform_device *pdev)
{
- int status;
+ int status, irq;
struct audio_drv_data *audio_drv_data;
- struct resource *res;
const u32 *pdata = pdev->dev.platform_data;
if (!pdata) {
@@ -1241,16 +1285,15 @@ static int acp_audio_probe(struct platform_device *pdev)
audio_drv_data->capture_i2ssp_stream = NULL;
audio_drv_data->play_i2sbt_stream = NULL;
audio_drv_data->capture_i2sbt_stream = NULL;
+ audio_drv_data->play_i2s_micsp_stream = NULL;
audio_drv_data->asic_type = *pdata;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
return -ENODEV;
- }
- status = devm_request_irq(&pdev->dev, res->start, dma_irq_handler,
+ status = devm_request_irq(&pdev->dev, irq, dma_irq_handler,
0, "ACP_IRQ", &pdev->dev);
if (status) {
dev_err(&pdev->dev, "ACP IRQ request failed\n");
@@ -1280,7 +1323,7 @@ static int acp_audio_probe(struct platform_device *pdev)
return status;
}
-static int acp_audio_remove(struct platform_device *pdev)
+static void acp_audio_remove(struct platform_device *pdev)
{
int status;
struct audio_drv_data *adata = dev_get_drvdata(&pdev->dev);
@@ -1289,8 +1332,6 @@ static int acp_audio_remove(struct platform_device *pdev)
if (status)
dev_err(&pdev->dev, "ACP Deinit failed status:%d\n", status);
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
static int acp_pcm_resume(struct device *dev)
@@ -1331,6 +1372,11 @@ static int acp_pcm_resume(struct device *dev)
config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
}
if (adata->asic_type != CHIP_CARRIZO) {
+ if (adata->play_i2s_micsp_stream &&
+ adata->play_i2s_micsp_stream->runtime) {
+ rtd = adata->play_i2s_micsp_stream->runtime->private_data;
+ config_acp_dma(adata->acp_mmio, rtd, adata->asic_type);
+ }
if (adata->play_i2sbt_stream &&
adata->play_i2sbt_stream->runtime) {
rtd = adata->play_i2sbt_stream->runtime->private_data;
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
index d6ba94677ac2..11d373169380 100644
--- a/sound/soc/amd/acp-rt5645.c
+++ b/sound/soc/amd/acp-rt5645.c
@@ -42,13 +42,23 @@
#define CZ_PLAT_CLK 24000000
static struct snd_soc_jack cz_jack;
+static struct snd_soc_jack_pin cz_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
static int cz_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
CZ_PLAT_CLK, params_rate(params) * 512);
@@ -73,14 +83,16 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card;
struct snd_soc_component *codec;
- codec = asoc_rtd_to_codec(rtd, 0)->component;
+ codec = snd_soc_rtd_to_codec(rtd, 0)->component;
card = rtd->card;
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &cz_jack, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &cz_jack,
+ cz_jack_pins,
+ ARRAY_SIZE(cz_jack_pins));
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
@@ -91,27 +103,27 @@ static int cz_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static struct snd_soc_ops cz_aif1_ops = {
+static const struct snd_soc_ops cz_aif1_ops = {
.hw_params = cz_aif1_hw_params,
};
SND_SOC_DAILINK_DEF(designware1,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
SND_SOC_DAILINK_DEF(designware2,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
SND_SOC_DAILINK_DEF(codec,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
SND_SOC_DAILINK_DEF(platform,
- DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
static struct snd_soc_dai_link cz_dai_rt5650[] = {
{
.name = "amd-rt5645-play",
.stream_name = "RT5645_AIF1",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = cz_init,
.ops = &cz_aif1_ops,
SND_SOC_DAILINK_REG(designware1, codec, platform),
@@ -120,7 +132,7 @@ static struct snd_soc_dai_link cz_dai_rt5650[] = {
.name = "amd-rt5645-cap",
.stream_name = "RT5645_AIF1",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.ops = &cz_aif1_ops,
SND_SOC_DAILINK_REG(designware2, codec, platform),
},
diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h
index e5ab6c6040a6..b29bef90f886 100644
--- a/sound/soc/amd/acp.h
+++ b/sound/soc/amd/acp.h
@@ -55,6 +55,7 @@
#define I2S_SP_INSTANCE 0x01
#define I2S_BT_INSTANCE 0x02
+#define I2S_MICSP_INSTANCE 0x03
#define CAP_CHANNEL0 0x00
#define CAP_CHANNEL1 0x01
@@ -85,6 +86,10 @@
#define I2S_TO_ACP_DMA_BT_INSTANCE_CH_NUM 10
#define ACP_TO_SYSRAM_BT_INSTANCE_CH_NUM 11
+/* Playback DMA channels for I2S MICSP instance */
+#define SYSRAM_TO_ACP_MICSP_INSTANCE_CH_NUM 4
+#define ACP_TO_I2S_DMA_MICSP_INSTANCE_CH_NUM 5
+
#define NUM_DSCRS_PER_CHANNEL 2
#define PLAYBACK_START_DMA_DESCR_CH12 0
@@ -108,8 +113,15 @@
#define CAPTURE_START_DMA_DESCR_CH11 14
#define CAPTURE_END_DMA_DESCR_CH11 15
+/* I2S MICSP Instance DMA Descriptors */
+#define PLAYBACK_START_DMA_DESCR_CH4 0
+#define PLAYBACK_END_DMA_DESCR_CH4 1
+#define PLAYBACK_START_DMA_DESCR_CH5 2
+#define PLAYBACK_END_DMA_DESCR_CH5 3
+
#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209
#define ACP_I2S_MIC_16BIT_RESOLUTION_EN 0x01
+#define ACP_I2S_MICSP_16BIT_RESOLUTION_EN 0x01
#define ACP_I2S_SP_16BIT_RESOLUTION_EN 0x02
#define ACP_I2S_BT_16BIT_RESOLUTION_EN 0x04
#define ACP_BT_UART_PAD_SELECT_MASK 0x1
@@ -149,8 +161,10 @@ struct audio_drv_data {
struct snd_pcm_substream *capture_i2ssp_stream;
struct snd_pcm_substream *play_i2sbt_stream;
struct snd_pcm_substream *capture_i2sbt_stream;
+ struct snd_pcm_substream *play_i2s_micsp_stream;
void __iomem *acp_mmio;
u32 asic_type;
+ snd_pcm_sframes_t delay;
};
/*
@@ -204,4 +218,6 @@ typedef struct acp_dma_dscr_transfer {
u32 reserved;
} acp_dma_dscr_transfer_t;
+extern bool acp_bt_uart_enable;
+
#endif /*__ACP_HW_H */
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
new file mode 100644
index 000000000000..b9432052c638
--- /dev/null
+++ b/sound/soc/amd/acp/Kconfig
@@ -0,0 +1,188 @@
+# 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
+#
+
+config SND_SOC_AMD_ACP_COMMON
+ tristate "AMD Audio ACP Common support"
+ select SND_AMD_ACP_CONFIG
+ depends on X86 && PCI
+ help
+ This option enables common modules for Audio-Coprocessor i.e. ACP
+ IP block on AMD platforms.
+
+config SND_SOC_ACPI_AMD_MATCH
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+if SND_SOC_AMD_ACP_COMMON
+
+config SND_SOC_AMD_ACP_PDM
+ tristate
+
+config SND_SOC_AMD_ACP_LEGACY_COMMON
+ tristate
+
+config SND_SOC_AMD_ACP_I2S
+ tristate
+
+config SND_SOC_AMD_ACPI_MACH
+ tristate
+
+config SND_SOC_AMD_ACP_PCM
+ tristate
+ select SND_SOC_ACPI if ACPI
+
+config SND_SOC_AMD_ACP_PCI
+ tristate "AMD ACP PCI Driver Support"
+ depends on X86 && PCI
+ depends on ACPI
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This options enables generic PCI driver for ACP device.
+
+config SND_AMD_ASOC_RENOIR
+ tristate "AMD ACP ASOC Renoir Support"
+ depends on ACPI
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ depends on X86 && PCI
+ help
+ This option enables Renoir I2S support on AMD platform.
+
+config SND_AMD_ASOC_REMBRANDT
+ tristate "AMD ACP ASOC Rembrandt Support"
+ depends on ACPI
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ depends on AMD_NODE
+ 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_AMD_ASOC_ACP63
+ tristate "AMD ACP ASOC ACP6.3 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ depends on AMD_NODE
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This option enables Acp6.3 I2S support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP6.3
+ If unsure select "N".
+
+config SND_AMD_ASOC_ACP70
+ tristate "AMD ACP ASOC Acp7.0 Support"
+ depends on X86 && PCI
+ depends on ACPI
+ depends on AMD_NODE
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ select SND_SOC_AMD_ACP_LEGACY_COMMON
+ select SND_SOC_AMD_ACPI_MACH
+ help
+ This option enables Acp7.0 PDM support on AMD platform.
+ Say Y if you want to enable AUDIO on ACP7.0
+ If unsure select "N".
+
+config SND_SOC_AMD_MACH_COMMON
+ tristate
+ depends on X86 && PCI && I2C
+ select CLK_FIXED_FCH
+ select SND_SOC_RT5682_I2C
+ select SND_SOC_DMIC
+ select SND_SOC_RT1019
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5682S
+ select SND_SOC_NAU8825
+ select SND_SOC_NAU8821
+ select SND_SOC_MAX98388
+ help
+ This option enables common Machine driver module for ACP.
+
+config SND_SOC_AMD_LEGACY_MACH
+ tristate "AMD Legacy Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ help
+ This option enables legacy sound card support for ACP audio.
+
+config SND_SOC_AMD_SOF_MACH
+ tristate "AMD SOF Machine Driver Support"
+ depends on X86 && PCI && I2C
+ select SND_SOC_AMD_MACH_COMMON
+ help
+ This option enables SOF sound card support for ACP audio.
+
+config SND_SOC_AMD_SDW_MACH_COMMON
+ tristate
+ help
+ This option enables common SoundWire Machine driver module for
+ AMD platforms.
+
+config SND_SOC_AMD_SOF_SDW_MACH
+ tristate "AMD SOF Soundwire Machine Driver Support"
+ depends on X86 && PCI && ACPI
+ depends on SOUNDWIRE
+ select SND_SOC_AMD_SDW_MACH_COMMON
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_DMIC
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ help
+ This option enables SOF sound card support for SoundWire enabled
+ AMD platforms along with ACP PDM controller.
+ Say Y if you want to enable SoundWire based machine driver support
+ on AMD platform.
+ If unsure select "N".
+
+config SND_SOC_AMD_LEGACY_SDW_MACH
+ tristate "AMD Legacy(No DSP) Soundwire Machine Driver Support"
+ depends on X86 && PCI && ACPI
+ depends on SOUNDWIRE
+ select SND_SOC_AMD_SDW_MACH_COMMON
+ select SND_SOC_SDW_UTILS
+ select SND_SOC_DMIC
+ select SND_SOC_RT711_SDW
+ select SND_SOC_RT711_SDCA_SDW
+ select SND_SOC_RT712_SDCA_SDW
+ select SND_SOC_RT712_SDCA_DMIC_SDW
+ select SND_SOC_RT1316_SDW
+ select SND_SOC_RT1320_SDW
+ select SND_SOC_RT715_SDW
+ select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
+ help
+ This option enables Legacy(No DSP) sound card support for SoundWire
+ enabled AMD platforms along with ACP PDM controller.
+ Say Y if you want to enable SoundWire based machine driver support
+ on AMD platform.
+ If unsure select "N".
+
+endif # SND_SOC_AMD_ACP_COMMON
+
+config SND_AMD_SOUNDWIRE_ACPI
+ tristate
+ depends on ACPI
+ help
+ This options enables ACPI helper functions for SoundWire
+ interface for AMD platforms.
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
new file mode 100644
index 000000000000..08220b9a3802
--- /dev/null
+++ b/sound/soc/amd/acp/Makefile
@@ -0,0 +1,50 @@
+# 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#common acp driver
+snd-acp-pcm-y := acp-platform.o
+snd-acp-i2s-y := acp-i2s.o
+snd-acp-pdm-y := acp-pdm.o
+snd-acp-legacy-common-y := acp-legacy-common.o
+snd-acp-pci-y := acp-pci.o
+snd-amd-sdw-acpi-y := amd-sdw-acpi.o
+snd-amd-acpi-mach-y := amd-acpi-mach.o
+
+#platform specific driver
+snd-acp-renoir-y := acp-renoir.o
+snd-acp-rembrandt-y := acp-rembrandt.o
+snd-acp63-y := acp63.o
+snd-acp70-y := acp70.o
+
+#machine specific driver
+snd-acp-mach-y := acp-mach-common.o
+snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
+snd-acp-sof-mach-y := acp-sof-mach.o
+snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
+snd-acp-sdw-mach-y := acp-sdw-mach-common.o
+snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
+snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_LEGACY_COMMON) += snd-acp-legacy-common.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
+obj-$(CONFIG_SND_SOC_AMD_ACPI_MACH) += snd-amd-acpi-mach.o
+
+obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP63) += snd-acp63.o
+obj-$(CONFIG_SND_AMD_ASOC_ACP70) += snd-acp70.o
+
+obj-$(CONFIG_SND_AMD_SOUNDWIRE_ACPI) += snd-amd-sdw-acpi.o
+obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_MACH) += snd-acp-sof-mach.o
+obj-$(CONFIG_SND_SOC_ACPI_AMD_MATCH) += snd-soc-acpi-amd-match.o
+obj-$(CONFIG_SND_SOC_AMD_SDW_MACH_COMMON) += snd-acp-sdw-mach.o
+obj-$(CONFIG_SND_SOC_AMD_SOF_SDW_MACH) += snd-acp-sdw-sof-mach.o
+obj-$(CONFIG_SND_SOC_AMD_LEGACY_SDW_MACH) += snd-acp-sdw-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
new file mode 100644
index 000000000000..70fa54d568ef
--- /dev/null
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -0,0 +1,697 @@
+// 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) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio I2S controller
+ */
+
+#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 <linux/bitfield.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_i2s_playcap"
+#define I2S_MASTER_MODE_ENABLE 1
+#define LRCLK_DIV_FIELD GENMASK(10, 2)
+#define BCLK_DIV_FIELD GENMASK(23, 11)
+#define ACP63_LRCLK_DIV_FIELD GENMASK(12, 2)
+#define ACP63_BCLK_DIV_FIELD GENMASK(23, 13)
+
+static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id)
+{
+ u32 i2s_clk_reg, val;
+
+ switch (dai_id) {
+ case I2S_SP_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ case I2S_BT_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM1_MSTRCLKGEN;
+ break;
+ case I2S_HS_INSTANCE:
+ i2s_clk_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ default:
+ i2s_clk_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ val = I2S_MASTER_MODE_ENABLE;
+ if (chip->tdm_mode)
+ val |= BIT(1);
+
+ switch (chip->acp_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div);
+ val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div);
+ break;
+ default:
+ val |= FIELD_PREP(LRCLK_DIV_FIELD, chip->lrclk_div);
+ val |= FIELD_PREP(BCLK_DIV_FIELD, chip->bclk_div);
+ }
+ writel(val, chip->base + i2s_clk_reg);
+}
+
+static int acp_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct device *dev = cpu_dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int mode;
+
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ chip->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ chip->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip;
+ struct acp_stream *stream;
+ int slot_len, no_of_slots;
+
+ chip = dev_get_drvdata(dev->parent);
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported bitdepth %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ case ACP_RMB_PCI_ID:
+ switch (slots) {
+ case 1 ... 7:
+ no_of_slots = slots;
+ break;
+ case 8:
+ no_of_slots = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported slots %d\n", slots);
+ return -EINVAL;
+ }
+ break;
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ switch (slots) {
+ case 1 ... 31:
+ no_of_slots = slots;
+ break;
+ case 32:
+ no_of_slots = 0;
+ break;
+ default:
+ dev_err(dev, "Unsupported slots %d\n", slots);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev);
+ return -EINVAL;
+ }
+
+ slots = no_of_slots;
+
+ spin_lock_irq(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ case ACP_RMB_PCI_ID:
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ chip->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ chip->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 15) | (slot_len << 18);
+ break;
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ chip->tdm_tx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 13) | (slot_len << 18);
+ else if (rx_mask && stream->dir == SNDRV_PCM_STREAM_CAPTURE)
+ chip->tdm_rx_fmt[stream->dai_id - 1] =
+ FRM_LEN | (slots << 13) | (slot_len << 18);
+ break;
+ default:
+ dev_err(dev, "Unknown chip revision %d\n", chip->acp_rev);
+ spin_unlock_irq(&chip->acp_lock);
+ return -EINVAL;
+ }
+ }
+ spin_unlock_irq(&chip->acp_lock);
+ return 0;
+}
+
+static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip;
+ struct acp_resource *rsrc;
+ u32 val;
+ u32 xfer_resolution;
+ u32 reg_val, fmt_reg, tdm_fmt;
+ u32 lrclk_div_val, bclk_div_val;
+
+ chip = dev_get_platdata(dev);
+ rsrc = chip->rsrc;
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ chip->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution;
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+ chip->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution;
+ }
+
+ val = readl(chip->base + reg_val);
+ val &= ~ACP3x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (xfer_resolution << 3);
+ writel(val, chip->base + reg_val);
+
+ if (chip->tdm_mode) {
+ val = readl(chip->base + reg_val);
+ writel(val | BIT(1), chip->base + reg_val);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tdm_fmt = chip->tdm_tx_fmt[dai->driver->id - 1];
+ else
+ tdm_fmt = chip->tdm_rx_fmt[dai->driver->id - 1];
+ writel(tdm_fmt, chip->base + fmt_reg);
+ }
+
+ 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;
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 48000:
+ case 96000:
+ case 192000:
+ switch (params_channels(params)) {
+ case 2:
+ break;
+ case 4:
+ bclk_div_val = bclk_div_val >> 1;
+ lrclk_div_val = lrclk_div_val << 1;
+ break;
+ case 8:
+ bclk_div_val = bclk_div_val >> 2;
+ lrclk_div_val = lrclk_div_val << 2;
+ break;
+ case 16:
+ bclk_div_val = bclk_div_val >> 3;
+ lrclk_div_val = lrclk_div_val << 3;
+ break;
+ case 32:
+ bclk_div_val = bclk_div_val >> 4;
+ lrclk_div_val = lrclk_div_val << 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported channels %#x\n",
+ params_channels(params));
+ }
+ break;
+ default:
+ break;
+ }
+ chip->lrclk_div = lrclk_div_val;
+ chip->bclk_div = bclk_div_val;
+ }
+ return 0;
+}
+
+static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ struct acp_resource *rsrc = chip->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);
+ buf_size = frames_to_bytes(substream->runtime, substream->runtime->buffer_size);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ stream->bytescount = acp_get_byte_count(chip, stream->dai_id, substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_TX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_BTTDM_ITER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_TX_RINGBUFSIZE(chip);
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_TX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE(chip);
+ 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;
+ }
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ water_val = ACP_BT_RX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_BTTDM_IRER;
+ ier_val = ACP_BTTDM_IER;
+ buf_reg = ACP_BT_RX_RINGBUFSIZE(chip);
+ break;
+ case I2S_SP_INSTANCE:
+ water_val = ACP_I2S_RX_INTR_WATERMARK_SIZE(chip);
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE(chip);
+ 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;
+ }
+ }
+
+ writel(period_bytes, chip->base + water_val);
+ writel(buf_size, chip->base + buf_reg);
+ if (rsrc->soc_mclk)
+ acp_set_i2s_clk(chip, dai->driver->id);
+ val = readl(chip->base + reg_val);
+ val = val | BIT(0);
+ writel(val, chip->base + reg_val);
+ writel(1, chip->base + ier_val);
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ break;
+ 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;
+ }
+
+ } else {
+ switch (dai->driver->id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ break;
+ 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;
+ }
+ }
+ val = readl(chip->base + reg_val);
+ val = val & ~BIT(0);
+ writel(val, chip->base + reg_val);
+
+ if (!(readl(chip->base + ACP_BTTDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_BTTDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_BTTDM_IER);
+ if (!(readl(chip->base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_I2STDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_I2STDM_IER);
+ if (!(readl(chip->base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(readl(chip->base + ACP_HSTDM_IRER) & BIT(0)))
+ writel(0, chip->base + ACP_HSTDM_IER);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
+ u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ chip = dev_get_platdata(dev);
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_SP_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_SP_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_BT_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_BT_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip));
+ }
+ 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;
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_HS_TX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->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;
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ phy_addr = ACP7x_I2S_HS_RX_MEM_WINDOW_START;
+ else
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, chip->base + reg_dma_size);
+ writel(acp_fifo_addr, chip->base + reg_fifo_addr);
+ writel(FIFO_SIZE, chip->base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ 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(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
+
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+
+ return 0;
+}
+
+static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ struct acp_resource *rsrc = chip->rsrc;
+ unsigned int dir = substream->stream;
+ unsigned int irq_bit = 0;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(I2S_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
+ stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(I2S_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
+ stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(BT_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
+ stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(BT_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
+ 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;
+ }
+
+ /* Save runtime dai configuration in stream */
+ stream->id = dai->driver->id + dir;
+ stream->dai_id = dai->driver->id;
+ stream->irq_bit = irq_bit;
+ stream->dir = substream->stream;
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops = {
+ .startup = acp_i2s_startup,
+ .hw_params = acp_i2s_hwparams,
+ .prepare = acp_i2s_prepare,
+ .trigger = acp_i2s_trigger,
+ .set_fmt = acp_i2s_set_fmt,
+ .set_tdm_slot = acp_i2s_set_tdm_slot,
+};
+EXPORT_SYMBOL_NS_GPL(asoc_acp_cpu_dai_ops, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP Audio I2S controller");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c
new file mode 100644
index 000000000000..ba8db0851daa
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-common.c
@@ -0,0 +1,645 @@
+// 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) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba Kareem <Syed.SabaKareem@amd.com>
+//
+
+/*
+ * Common file to be used by amd platforms
+ */
+
+#include "amd.h"
+#include <linux/acpi.h>
+#include <linux/pci.h>
+#include <linux/export.h>
+
+#include "../mach-config.h"
+
+#define ACP_RENOIR_PDM_ADDR 0x02
+#define ACP_REMBRANDT_PDM_ADDR 0x03
+#define ACP63_PDM_ADDR 0x02
+#define ACP70_PDM_ADDR 0x02
+
+struct acp_resource rn_rsrc = {
+ .offset = 20,
+ .no_of_ctrls = 1,
+ .irqp_used = 0,
+ .irq_reg_offset = 0x1800,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x02052800,
+};
+EXPORT_SYMBOL_NS_GPL(rn_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource rmb_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+EXPORT_SYMBOL_NS_GPL(rmb_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource acp63_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+EXPORT_SYMBOL_NS_GPL(acp63_rsrc, "SND_SOC_ACP_COMMON");
+
+struct acp_resource acp70_rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .scratch_reg_offset = 0x10000,
+ .sram_pte_offset = 0x03800000,
+};
+EXPORT_SYMBOL_NS_GPL(acp70_rsrc, "SND_SOC_ACP_COMMON");
+
+static const struct snd_acp_hw_ops acp_common_hw_ops = {
+ /* ACP hardware initilizations */
+ .acp_init = acp_init,
+ .acp_deinit = acp_deinit,
+
+ /* ACP Interrupts*/
+ .irq = acp_irq_handler,
+ .en_interrupts = acp_enable_interrupts,
+ .dis_interrupts = acp_disable_interrupts,
+};
+
+irqreturn_t acp_irq_handler(int irq, void *data)
+{
+ struct acp_chip_info *chip = data;
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream;
+ u16 i2s_flag = 0;
+ u32 ext_intr_stat, ext_intr_stat1;
+
+ if (rsrc->no_of_ctrls == 2)
+ ext_intr_stat1 = readl(ACP_EXTERNAL_INTR_STAT(chip, (rsrc->irqp_used - 1)));
+
+ ext_intr_stat = readl(ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ if (ext_intr_stat & stream->irq_bit) {
+ writel(stream->irq_bit,
+ ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ if (chip->rsrc->no_of_ctrls == 2) {
+ if (ext_intr_stat1 & stream->irq_bit) {
+ writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(chip,
+ (rsrc->irqp_used - 1)));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ }
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ if (i2s_flag)
+ return IRQ_HANDLED;
+
+ return IRQ_NONE;
+}
+
+int acp_enable_interrupts(struct acp_chip_info *chip)
+{
+ struct acp_resource *rsrc;
+ u32 ext_intr_ctrl;
+
+ rsrc = chip->rsrc;
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(chip));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, "SND_SOC_ACP_COMMON");
+
+int acp_disable_interrupts(struct acp_chip_info *chip)
+{
+ struct acp_resource *rsrc;
+
+ rsrc = chip->rsrc;
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(chip, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(chip));
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_disable_interrupts, "SND_SOC_ACP_COMMON");
+
+static void set_acp_pdm_ring_buffer(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct acp_stream *stream = runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+
+ u32 physical_addr, pdm_size, period_bytes;
+
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ pdm_size = frames_to_bytes(runtime, runtime->buffer_size);
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init ACP PDM Ring buffer */
+ writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
+ writel(pdm_size, chip->base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void set_acp_pdm_clk(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int pdm_ctrl;
+
+ /* Enable default ACP PDM clk */
+ writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(pdm_ctrl, chip->base + ACP_WOV_MISC_CTRL);
+ set_acp_pdm_ring_buffer(substream, dai);
+}
+
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 ext_int_ctrl;
+
+ soc_runtime = snd_soc_substream_to_rtd(substream);
+ dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+
+ /* Programming channel mask and sampling rate */
+ writel(chip->ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ /* Enabling ACP Pdm interuppts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ set_acp_pdm_clk(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_pdm_params, "SND_SOC_ACP_COMMON");
+
+static int set_acp_i2s_dma_fifo(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ struct acp_resource *rsrc = chip->rsrc;
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 reg_dma_size, reg_fifo_size, reg_fifo_addr;
+ u32 phy_addr, acp_fifo_addr, ext_int_ctrl;
+ unsigned int dir = substream->stream;
+
+ switch (dai->driver->id) {
+ case I2S_SP_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE(chip);
+ phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE(chip);
+ phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_I2S_RX_RINGBUFADDR(chip));
+ }
+ break;
+ case I2S_BT_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_BT_TX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_TX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_TX_FIFOSIZE(chip);
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_TX_RINGBUFADDR(chip));
+ } else {
+ reg_dma_size = ACP_BT_RX_DMA_SIZE(chip);
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ BT_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_BT_RX_FIFOADDR(chip);
+ reg_fifo_size = ACP_BT_RX_FIFOSIZE(chip);
+ phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, chip->base + ACP_BT_RX_RINGBUFADDR(chip));
+ }
+ 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, chip->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, chip->base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
+ return -EINVAL;
+ }
+
+ writel(DMA_SIZE, chip->base + reg_dma_size);
+ writel(acp_fifo_addr, chip->base + reg_fifo_addr);
+ writel(FIFO_SIZE, chip->base + reg_fifo_size);
+
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ 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(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
+
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, rsrc->irqp_used));
+ return 0;
+}
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip,
+ struct acp_stream *stream)
+{
+ struct snd_soc_dai *dai;
+ struct snd_soc_pcm_runtime *soc_runtime;
+ u32 tdm_fmt, reg_val, fmt_reg, val;
+
+ soc_runtime = snd_soc_substream_to_rtd(substream);
+ dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ tdm_fmt = chip->tdm_tx_fmt[stream->dai_id - 1];
+ switch (stream->dai_id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_ITER;
+ fmt_reg = ACP_BTTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_ITER;
+ fmt_reg = ACP_I2STDM_TXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ fmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ default:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = chip->xfer_tx_resolution[stream->dai_id - 1] << 3;
+ } else {
+ tdm_fmt = chip->tdm_rx_fmt[stream->dai_id - 1];
+ switch (stream->dai_id) {
+ case I2S_BT_INSTANCE:
+ reg_val = ACP_BTTDM_IRER;
+ fmt_reg = ACP_BTTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ reg_val = ACP_I2STDM_IRER;
+ fmt_reg = ACP_I2STDM_RXFRMT;
+ break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ fmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ default:
+ pr_err("Invalid dai id %x\n", stream->dai_id);
+ return -EINVAL;
+ }
+ val = chip->xfer_rx_resolution[stream->dai_id - 1] << 3;
+ }
+ writel(val, chip->base + reg_val);
+ if (chip->tdm_mode == TDM_ENABLE) {
+ writel(tdm_fmt, chip->base + fmt_reg);
+ val = readl(chip->base + reg_val);
+ writel(val | 0x2, chip->base + reg_val);
+ }
+ return set_acp_i2s_dma_fifo(substream, dai);
+}
+EXPORT_SYMBOL_NS_GPL(restore_acp_i2s_params, "SND_SOC_ACP_COMMON");
+
+static int acp_power_on(struct acp_chip_info *chip)
+{
+ u32 val, acp_pgfsm_stat_reg, acp_pgfsm_ctrl_reg;
+ void __iomem *base;
+
+ base = chip->base;
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ acp_pgfsm_stat_reg = ACP_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL;
+ break;
+ case ACP_RMB_PCI_ID:
+ acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP6X_PGFSM_CONTROL;
+ break;
+ case ACP63_PCI_ID:
+ acp_pgfsm_stat_reg = ACP63_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP63_PGFSM_CONTROL;
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS;
+ acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(base + acp_pgfsm_stat_reg);
+ 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 + acp_pgfsm_ctrl_reg);
+
+ return readl_poll_timeout(base + acp_pgfsm_stat_reg, val,
+ !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp_reset(void __iomem *base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, base + ACP_SOFT_RESET);
+ ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK,
+ DELAY_US, ACP_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, base + ACP_SOFT_RESET);
+ return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
+}
+
+int acp_init(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* power on */
+ ret = acp_power_on(chip);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, chip->base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ writel(0, chip->base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_init, "SND_SOC_ACP_COMMON");
+
+int acp_deinit(struct acp_chip_info *chip)
+{
+ int ret;
+
+ /* Reset */
+ ret = acp_reset(chip->base);
+ if (ret)
+ return ret;
+
+ if (chip->acp_rev < ACP70_PCI_ID)
+ writel(0, chip->base + ACP_CONTROL);
+ else
+ writel(0x01, chip->base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_deinit, "SND_SOC_ACP_COMMON");
+int acp_machine_select(struct acp_chip_info *chip)
+{
+ struct snd_soc_acpi_mach *mach;
+ int size, platform;
+
+ if (chip->flag == FLAG_AMD_LEGACY_ONLY_DMIC && chip->is_pdm_dev) {
+ platform = chip->acp_rev;
+ chip->mach_dev = platform_device_register_data(chip->dev, "acp-pdm-mach",
+ PLATFORM_DEVID_NONE, &platform,
+ sizeof(platform));
+ } else {
+ size = sizeof(*chip->machines);
+ mach = snd_soc_acpi_find_machine(chip->machines);
+ if (!mach) {
+ dev_err(chip->dev, "warning: No matching ASoC machine driver found\n");
+ return -EINVAL;
+ }
+ mach->mach_params.subsystem_rev = chip->acp_rev;
+ chip->mach_dev = platform_device_register_data(chip->dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach, size);
+ }
+ if (IS_ERR(chip->mach_dev))
+ dev_warn(chip->dev, "Unable to register Machine device\n");
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_machine_select, "SND_SOC_ACP_COMMON");
+
+static void check_acp3x_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP3X_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ chip->is_pdm_config = true;
+ break;
+ }
+}
+
+static void check_acp6x_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void check_acp70_config(struct acp_chip_info *chip)
+{
+ u32 val;
+
+ val = readl(chip->base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ chip->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_9:
+ chip->is_i2s_config = true;
+ break;
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_19:
+ case ACP_CONFIG_20:
+ chip->is_i2s_config = true;
+ chip->is_pdm_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ u32 pdm_addr, ret;
+
+ switch (chip->acp_rev) {
+ case ACP_RN_PCI_ID:
+ pdm_addr = ACP_RENOIR_PDM_ADDR;
+ check_acp3x_config(chip);
+ break;
+ case ACP_RMB_PCI_ID:
+ pdm_addr = ACP_REMBRANDT_PDM_ADDR;
+ check_acp6x_config(chip);
+ break;
+ case ACP63_PCI_ID:
+ pdm_addr = ACP63_PDM_ADDR;
+ check_acp6x_config(chip);
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ pdm_addr = ACP70_PDM_ADDR;
+ check_acp70_config(chip);
+ break;
+ default:
+ break;
+ }
+
+ if (chip->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), pdm_addr, 0);
+ if (pdm_dev) {
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == pdm_addr)
+ chip->is_pdm_dev = true;
+ }
+
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret))
+ chip->is_pdm_dev = dmic_status;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(check_acp_config, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp31_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp31_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp31_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp31_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp31_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp31_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp6x_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp6x_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp6x_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp6x_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp6x_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp6x_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp63_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp63_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp63_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp63_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp63_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp63_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+struct snd_acp_hw_ops acp70_common_hw_ops;
+EXPORT_SYMBOL_NS_GPL(acp70_common_hw_ops, "SND_SOC_ACP_COMMON");
+int acp70_hw_ops_init(struct acp_chip_info *chip)
+{
+ memcpy(&acp70_common_hw_ops, &acp_common_hw_ops, sizeof(acp_common_hw_ops));
+ chip->acp_hw_ops = &acp70_common_hw_ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp70_hw_ops_init, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP legacy common features");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
new file mode 100644
index 000000000000..a7a551366a40
--- /dev/null
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -0,0 +1,245 @@
+// 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) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * Machine Driver Legacy Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+#include "acp3x-es83xx/acp3x-es83xx.h"
+
+static struct acp_card_drvdata rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata es83xx_rn_data = {
+ .hs_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = ES83XX,
+ .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,
+ .tdm_mode = false,
+};
+
+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,
+ .tdm_mode = false,
+};
+
+static struct acp_card_drvdata acp_dmic_data = {
+ .dmic_cpu_id = DMIC,
+ .dmic_codec_id = DMIC,
+};
+
+static bool acp_asoc_init_ops(struct acp_card_drvdata *priv)
+{
+ bool has_ops = false;
+
+ if (priv->hs_codec_id == ES83XX) {
+ has_ops = true;
+ acp3x_es83xx_init_ops(&priv->ops);
+ }
+ return has_ops;
+}
+
+static int acp_asoc_suspend_pre(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = acp_ops_suspend_pre(card);
+ if (ret == 1)
+ return 0;
+ else
+ return ret;
+}
+
+static int acp_asoc_resume_post(struct snd_soc_card *card)
+{
+ int ret;
+
+ ret = acp_ops_resume_post(card);
+ if (ret == 1)
+ return 0;
+ else
+ return ret;
+}
+
+static int acp_asoc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = NULL;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ acp_card_drvdata = card->drvdata;
+ acp_card_drvdata->acpi_mach = (struct snd_soc_acpi_mach *)pdev->dev.platform_data;
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+
+ acp_asoc_init_ops(card->drvdata);
+
+ /* If widgets and controls are not set in specific callback,
+ * they will be added per-codec in acp-mach-common.c
+ */
+ ret = acp_ops_configure_widgets(card);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Cannot configure widgets for card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+ card->suspend_pre = acp_asoc_suspend_pre;
+ card->resume_post = acp_asoc_resume_post;
+
+ ret = acp_ops_probe(card);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Cannot probe card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+ if (!strcmp(pdev->name, "acp-pdm-mach"))
+ acp_card_drvdata->acp_rev = *((int *)dev->platform_data);
+ else
+ acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
+
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ ret = acp_legacy_dai_links_create(card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Cannot create dai links for card (%s): %d\n",
+ card->name, ret);
+ goto out;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "devm_snd_soc_register_card(%s) failed: %d\n",
+ card->name, ret);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "acp3xalc56821019",
+ .driver_data = (kernel_ulong_t)&rt5682_rt1019_data,
+ },
+ {
+ .name = "acp3xalc5682sm98360",
+ .driver_data = (kernel_ulong_t)&rt5682s_max_data,
+ },
+ {
+ .name = "acp3xalc5682s1019",
+ .driver_data = (kernel_ulong_t)&rt5682s_rt1019_data,
+ },
+ {
+ .name = "acp3x-es83xx",
+ .driver_data = (kernel_ulong_t)&es83xx_rn_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,
+ },
+ {
+ .name = "acp-pdm-mach",
+ .driver_data = (kernel_ulong_t)&acp_dmic_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .pm = &snd_soc_pm_ops,
+ .name = "acp_mach",
+ },
+ .probe = acp_asoc_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS("SND_SOC_AMD_MACH");
+MODULE_DESCRIPTION("ACP chrome audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
new file mode 100644
index 000000000000..a0dab85088ec
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -0,0 +1,1796 @@
+// 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) 2021, 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Machine Driver Interface for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include "../../codecs/rt5682.h"
+#include "../../codecs/rt1019.h"
+#include "../../codecs/rt5682s.h"
+#include "../../codecs/nau8825.h"
+#include "../../codecs/nau8821.h"
+#include "acp-mach.h"
+
+#define PCO_PLAT_CLK 48000000
+#define RT5682_PLL_FREQ (48000 * 512)
+#define DUAL_CHANNEL 2
+#define FOUR_CHANNEL 4
+#define NAU8821_CODEC_DAI "nau8821-hifi"
+#define NAU8821_BCLK 1536000
+#define NAU8821_FREQ_OUT 12288000
+#define MAX98388_CODEC_DAI "max98388-aif1"
+
+#define TDM_MODE_ENABLE 1
+
+const struct dmi_system_id acp_quirk_table[] = {
+ {
+ /* Google skyrim proto-0 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "Google_Skyrim"),
+ },
+ .driver_data = (void *)TDM_MODE_ENABLE,
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(acp_quirk_table);
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int acp_clk_enable(struct acp_card_drvdata *drvdata,
+ unsigned int srate, unsigned int bclk_ratio)
+{
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * bclk_ratio);
+
+ return clk_prepare_enable(drvdata->wclk);
+}
+
+/* Declare RT5682 codec components */
+SND_SOC_DAILINK_DEF(rt5682,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
+
+static struct snd_soc_jack rt5682_jack;
+static struct snd_soc_jack_pin rt5682_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new rt5682_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget rt5682_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rt5682_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+/* Define card ops for RT5682 CODEC */
+static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682)
+ return -EINVAL;
+
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt5682_widgets,
+ ARRAY_SIZE(rt5682_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5682_controls,
+ ARRAY_SIZE(rt5682_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rt5682_jack,
+ rt5682_jack_pins,
+ ARRAY_SIZE(rt5682_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(rt5682_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &rt5682_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682_map, ARRAY_SIZE(rt5682_map));
+}
+
+static int acp_card_hs_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return ret;
+}
+
+static void acp_card_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+
+ if (!drvdata->soc_mclk)
+ clk_disable_unprepare(drvdata->wclk);
+}
+
+static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1,
+ 49152000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682_ops = {
+ .startup = acp_card_hs_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt5682_hw_params,
+};
+
+/* Define RT5682S CODEC component*/
+SND_SOC_DAILINK_DEF(rt5682s,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1")));
+
+static struct snd_soc_jack rt5682s_jack;
+static struct snd_soc_jack_pin rt5682s_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new rt5682s_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget rt5682s_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rt5682s_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682S)
+ return -EINVAL;
+
+ if (!drvdata->soc_mclk) {
+ drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
+ drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
+ }
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt5682s_widgets,
+ ARRAY_SIZE(rt5682s_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt5682s_controls,
+ ARRAY_SIZE(rt5682s_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &rt5682s_jack,
+ rt5682s_jack_pins,
+ ARRAY_SIZE(rt5682s_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(rt5682s_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &rt5682s_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682s_map, ARRAY_SIZE(rt5682s_map));
+}
+
+static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL1, RT5682S_PLL_S_BCLK1,
+ 6144000, 49152000);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL1,
+ 49152000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * ch * format);
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682s_ops = {
+ .startup = acp_card_hs_startup,
+ .hw_params = acp_card_rt5682s_hw_params,
+};
+
+static const unsigned int dmic_channels[] = {
+ DUAL_CHANNEL, FOUR_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list dmic_constraints_channels = {
+ .count = ARRAY_SIZE(dmic_channels),
+ .list = dmic_channels,
+ .mask = 0,
+};
+
+static int acp_card_dmic_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &dmic_constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_dmic_ops = {
+ .startup = acp_card_dmic_startup,
+};
+
+/* Declare RT1019 codec components */
+SND_SOC_DAILINK_DEF(rt1019,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"),
+ COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
+
+static const struct snd_kcontrol_new rt1019_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget rt1019_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route rt1019_map_lr[] = {
+ { "Left Spk", NULL, "Left SPO" },
+ { "Right Spk", NULL, "Right SPO" },
+};
+
+static struct snd_soc_codec_conf rt1019_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-10EC1019:00"),
+ .name_prefix = "Right",
+ },
+};
+
+static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, rt1019_widgets,
+ ARRAY_SIZE(rt1019_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, rt1019_controls,
+ ARRAY_SIZE(rt1019_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, rt1019_map_lr,
+ ARRAY_SIZE(rt1019_map_lr));
+}
+
+static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int i, ret = 0;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->amp_codec_id != RT1019)
+ return -EINVAL;
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ if (strcmp(codec_dai->name, "rt1019-aif"))
+ continue;
+
+ if (drvdata->tdm_mode)
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ TDM_CHANNELS * format * srate, 256 * srate);
+ else
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ ch * format * srate, 256 * srate);
+
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT1019_SCLK_S_PLL,
+ 256 * srate, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A
+ | SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ /**
+ * As codec supports slot 2 for left channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:00")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x4, 8, 16);
+ if (ret < 0)
+ break;
+ }
+
+ /**
+ * As codec supports slot 3 for right channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:01")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x8, 8, 16);
+ if (ret < 0)
+ break;
+ }
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int acp_card_amp_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt1019_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt1019_hw_params,
+};
+
+/* Declare Maxim codec components */
+SND_SOC_DAILINK_DEF(max98360a,
+ DAILINK_COMP_ARRAY(COMP_CODEC("MX98360A:00", "HiFi")));
+
+static const struct snd_kcontrol_new max98360a_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget max98360a_widgets[] = {
+ SND_SOC_DAPM_SPK("Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max98360a_map[] = {
+ {"Spk", NULL, "Speaker"},
+};
+
+static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98360A)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, max98360a_widgets,
+ ARRAY_SIZE(max98360a_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max98360a_controls,
+ ARRAY_SIZE(max98360a_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, max98360a_map,
+ ARRAY_SIZE(max98360a_map));
+}
+
+static int acp_card_maxim_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ unsigned int fmt, srate, ch, format;
+ int ret;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_maxim_ops = {
+ .startup = acp_card_amp_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_maxim_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(max98388,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", MAX98388_CODEC_DAI),
+ COMP_CODEC("i2c-ADS8388:01", MAX98388_CODEC_DAI)));
+
+static const struct snd_kcontrol_new max98388_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const struct snd_soc_dapm_widget max98388_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route max98388_map[] = {
+ { "Left Spk", NULL, "Left BE_OUT" },
+ { "Right Spk", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_codec_conf max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:00"),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF("i2c-ADS8388:01"),
+ .name_prefix = "Right",
+ },
+};
+
+static const unsigned int max98388_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits_max = {
+ .list = max98388_format,
+ .count = ARRAY_SIZE(max98388_format),
+};
+
+static int acp_card_max98388_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits_max);
+
+ return 0;
+}
+
+static int acp_card_max98388_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret;
+
+ if (drvdata->amp_codec_id != MAX98388)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, max98388_widgets,
+ ARRAY_SIZE(max98388_widgets));
+
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ /* Don't need to add routes if widget addition failed */
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, max98388_controls,
+ ARRAY_SIZE(max98388_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, max98388_map,
+ ARRAY_SIZE(max98388_map));
+}
+
+static int acp_max98388_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai =
+ snd_soc_card_get_codec_dai(card,
+ MAX98388_CODEC_DAI);
+ int ret;
+
+ ret = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_CBC_CFC | SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_max98388_ops = {
+ .startup = acp_card_max98388_startup,
+ .hw_params = acp_max98388_hw_params,
+};
+
+/* Declare nau8825 codec components */
+SND_SOC_DAILINK_DEF(nau8825,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10508825:00", "nau8825-hifi")));
+
+static struct snd_soc_jack nau8825_jack;
+static struct snd_soc_jack_pin nau8825_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget nau8825_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route nau8825_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+};
+
+static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != NAU8825)
+ return -EINVAL;
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, nau8825_widgets,
+ ARRAY_SIZE(nau8825_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8825_controls,
+ ARRAY_SIZE(nau8825_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &nau8825_jack,
+ nau8825_jack_pins,
+ ARRAY_SIZE(nau8825_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(nau8825_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, &nau8825_jack, NULL);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack call-back failed: %d\n", ret);
+ return ret;
+ }
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8825_map, ARRAY_SIZE(nau8825_map));
+}
+
+static int acp_nau8825_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS,
+ (48000 * 256), SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params),
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set FLL: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 4 and slot 5 for playback and slot 6 for capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x30, 0xC0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x40, 0x30, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+ return ret;
+}
+
+static int acp_nau8825_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_nau8825_ops = {
+ .startup = acp_nau8825_startup,
+ .hw_params = acp_nau8825_hw_params,
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *codec_dai;
+ int ret = 0;
+
+ codec_dai = snd_soc_card_get_codec_dai(card, NAU8821_CODEC_DAI);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_INTERNAL,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "set sysclk err = %d\n", ret);
+ return -EIO;
+ }
+ } else {
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, NAU8821_BCLK,
+ NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set FLL: %d\n", ret);
+ }
+ return ret;
+}
+
+static struct snd_soc_jack nau8821_jack;
+static struct snd_soc_jack_pin nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new nau8821_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static const struct snd_soc_dapm_widget nau8821_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8821_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static const unsigned int nau8821_format[] = {16};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = nau8821_format,
+ .count = ARRAY_SIZE(nau8821_format),
+};
+
+static int acp_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ ret = snd_soc_dapm_new_controls(&card->dapm, nau8821_widgets,
+ ARRAY_SIZE(nau8821_widgets));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add widget dapm controls, ret %d\n", ret);
+ // Don't need to add routes if widget addition failed
+ return ret;
+ }
+
+ ret = snd_soc_add_card_controls(card, nau8821_controls,
+ ARRAY_SIZE(nau8821_controls));
+ if (ret) {
+ dev_err(rtd->dev, "unable to add card controls, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_LINEOUT |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &nau8821_jack,
+ nau8821_jack_pins,
+ ARRAY_SIZE(nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(nau8821_jack.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ nau8821_enable_jack_detect(component, &nau8821_jack);
+
+ return snd_soc_dapm_add_routes(&rtd->card->dapm, nau8821_audio_route,
+ ARRAY_SIZE(nau8821_audio_route));
+}
+
+static int acp_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+ return 0;
+}
+
+static int acp_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret;
+ unsigned int fmt;
+
+ if (drvdata->soc_mclk)
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, NAU8821_CLK_FLL_BLK, 0,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FS clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, snd_soc_params_to_bclk(params),
+ params_rate(params) * 256);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp_8821_ops = {
+ .startup = acp_8821_startup,
+ .hw_params = acp_nau8821_hw_params,
+};
+
+SND_SOC_DAILINK_DEF(nau8821,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-NVTN2020:00",
+ NAU8821_CODEC_DAI)));
+
+/* Declare DMIC codec components */
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi")));
+
+/* Declare ACP CPU components */
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ .name = "acp_asoc_renoir.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_rmb_component[] = {
+ {
+ .name = "acp_asoc_rembrandt.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_acp63_component[] = {
+ {
+ .name = "acp_asoc_acp63.0",
+ }
+};
+
+static struct snd_soc_dai_link_component platform_acp70_component[] = {
+ {
+ .name = "acp_asoc_acp70.0",
+ }
+};
+
+static struct snd_soc_dai_link_component sof_component[] = {
+ {
+ .name = "0000:04:00.5",
+ }
+};
+
+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_sp_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp-virtual")));
+SND_SOC_DAILINK_DEF(sof_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs")));
+SND_SOC_DAILINK_DEF(sof_hs_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual")));
+SND_SOC_DAILINK_DEF(sof_bt,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-bt")));
+SND_SOC_DAILINK_DEF(sof_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
+SND_SOC_DAILINK_DEF(pdm_dmic,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-pdm-dmic")));
+
+static int acp_rtk_set_bias_level(struct snd_soc_card *card,
+ struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_component *component = dapm->component;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ int ret = 0;
+
+ if (!component)
+ return 0;
+
+ if (strncmp(component->name, "i2c-RTL5682", 11) &&
+ strncmp(component->name, "i2c-10EC1019", 12))
+ return 0;
+
+ /*
+ * For Realtek's codec and amplifier components,
+ * the lrck and bclk must be enabled brfore their all dapms be powered on,
+ * and must be disabled after their all dapms be powered down
+ * to avoid any pop.
+ */
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+
+ /* Increase bclk's enable_count */
+ ret = clk_prepare_enable(drvdata->bclk);
+ if (ret < 0)
+ dev_err(component->dev, "Failed to enable bclk %d\n", ret);
+ } else {
+ /*
+ * Decrease bclk's enable_count.
+ * While the enable_count is 0, the bclk would be closed.
+ */
+ clk_disable_unprepare(drvdata->bclk);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->bt_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_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;
+ }
+ if (drv_data->hs_codec_id == NAU8821) {
+ links[i].codecs = nau8821;
+ links[i].num_codecs = ARRAY_SIZE(nau8821);
+ links[i].init = acp_8821_init;
+ links[i].ops = &acp_8821_ops;
+ }
+ i++;
+ }
+
+ if (drv_data->hs_cpu_id == I2S_HS) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = sof_hs;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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;
+ if (drv_data->acp_rev == ACP_RN_PCI_ID) {
+ links[i].cpus = sof_sp;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ } else {
+ links[i].cpus = sof_sp_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
+ }
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].playback_only = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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);
+ }
+ 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;
+ }
+ i++;
+ }
+
+ if (drv_data->amp_cpu_id == I2S_HS) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = sof_hs_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs_virtual);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].playback_only = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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 == MAX98388) {
+ links[i].playback_only = 0;
+ links[i].codecs = max98388;
+ links[i].num_codecs = ARRAY_SIZE(max98388);
+ links[i].ops = &acp_max98388_ops;
+ links[i].init = acp_card_max98388_init;
+ card->codec_conf = max98388_conf;
+ card->num_configs = ARRAY_SIZE(max98388_conf);
+ }
+ 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->bt_cpu_id == I2S_BT) {
+ links[i].name = "acp-bt-codec";
+ links[i].id = BT_BE_ID;
+ links[i].cpus = sof_bt;
+ links[i].num_cpus = ARRAY_SIZE(sof_bt);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ if (!drv_data->bt_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ links[i].id = DMIC_BE_ID;
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ links[i].cpus = sof_dmic;
+ links[i].num_cpus = ARRAY_SIZE(sof_dmic);
+ links[i].platforms = sof_component;
+ links[i].num_platforms = ARRAY_SIZE(sof_component);
+ links[i].capture_only = 1;
+ links[i].nonatomic = true;
+ links[i].no_pcm = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+ card->set_bias_level = acp_rtk_set_bias_level;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_sofdsp_dai_links_create, "SND_SOC_AMD_MACH");
+
+int acp_legacy_dai_links_create(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *links;
+ struct device *dev = card->dev;
+ struct acp_card_drvdata *drv_data = card->drvdata;
+ int i = 0, num_links = 0;
+ int rc;
+
+ if (drv_data->hs_cpu_id)
+ num_links++;
+ if (drv_data->amp_cpu_id)
+ num_links++;
+ if (drv_data->dmic_cpu_id)
+ num_links++;
+
+ links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
+
+ if (drv_data->hs_cpu_id == I2S_SP) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ if (drv_data->hs_codec_id == RT5682) {
+ links[i].codecs = rt5682;
+ links[i].num_codecs = ARRAY_SIZE(rt5682);
+ links[i].init = acp_card_rt5682_init;
+ links[i].ops = &acp_card_rt5682_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;
+ }
+ if (drv_data->hs_codec_id == ES83XX) {
+ rc = acp_ops_configure_link(card, &links[i]);
+ if (rc != 0) {
+ dev_err(dev, "Failed to configure link for ES83XX: %d\n", rc);
+ return rc;
+ }
+ }
+ 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);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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;
+ links[i].cpus = i2s_sp;
+ links[i].num_cpus = ARRAY_SIZE(i2s_sp);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].playback_only = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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);
+ }
+ 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;
+ }
+ 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);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+
+ links[i].playback_only = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ 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].stream_name = "DMIC capture";
+ links[i].id = DMIC_BE_ID;
+ if (drv_data->dmic_codec_id == DMIC) {
+ links[i].codecs = dmic_codec;
+ links[i].num_codecs = ARRAY_SIZE(dmic_codec);
+ } else {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = &snd_soc_dummy_dlc;
+ links[i].num_codecs = 1;
+ }
+ links[i].cpus = pdm_dmic;
+ links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
+ switch (drv_data->acp_rev) {
+ case ACP_RMB_PCI_ID:
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ break;
+ case ACP63_PCI_ID:
+ links[i].platforms = platform_acp63_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp63_component);
+ break;
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ links[i].platforms = platform_acp70_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_acp70_component);
+ break;
+ default:
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ break;
+ }
+ links[i].ops = &acp_card_dmic_ops;
+ links[i].capture_only = 1;
+ }
+
+ card->dai_link = links;
+ card->num_links = num_links;
+ card->set_bias_level = acp_rtk_set_bias_level;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_legacy_dai_links_create, "SND_SOC_AMD_MACH");
+
+MODULE_DESCRIPTION("AMD ACP Common Machine driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
new file mode 100644
index 000000000000..f94c30c20f20
--- /dev/null
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -0,0 +1,139 @@
+/* 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __ACP_MACH_H
+#define __ACP_MACH_H
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "acp_common.h"
+
+#define TDM_CHANNELS 8
+
+#define ACP_OPS(priv, cb) ((priv)->ops.cb)
+
+#define acp_get_drvdata(card) ((struct acp_card_drvdata *)(card)->drvdata)
+
+enum be_id {
+ HEADSET_BE_ID = 0,
+ AMP_BE_ID,
+ DMIC_BE_ID,
+ BT_BE_ID,
+};
+
+enum cpu_endpoints {
+ NONE = 0,
+ I2S_HS,
+ I2S_SP,
+ I2S_BT,
+ DMIC,
+};
+
+enum codec_endpoints {
+ DUMMY = 0,
+ RT5682,
+ RT1019,
+ MAX98360A,
+ RT5682S,
+ NAU8825,
+ NAU8821,
+ MAX98388,
+ ES83XX,
+};
+
+struct acp_mach_ops {
+ int (*probe)(struct snd_soc_card *card);
+ int (*configure_link)(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link);
+ int (*configure_widgets)(struct snd_soc_card *card);
+ int (*suspend_pre)(struct snd_soc_card *card);
+ int (*resume_post)(struct snd_soc_card *card);
+};
+
+struct acp_card_drvdata {
+ unsigned int hs_cpu_id;
+ unsigned int amp_cpu_id;
+ unsigned int bt_cpu_id;
+ unsigned int dmic_cpu_id;
+ unsigned int hs_codec_id;
+ unsigned int amp_codec_id;
+ unsigned int bt_codec_id;
+ unsigned int dmic_codec_id;
+ unsigned int dai_fmt;
+ unsigned int acp_rev;
+ struct clk *wclk;
+ struct clk *bclk;
+ struct acp_mach_ops ops;
+ struct snd_soc_acpi_mach *acpi_mach;
+ void *mach_priv;
+ bool soc_mclk;
+ bool tdm_mode;
+};
+
+int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
+int acp_legacy_dai_links_create(struct snd_soc_card *card);
+extern const struct dmi_system_id acp_quirk_table[];
+
+static inline int acp_ops_probe(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, probe))
+ ret = ACP_OPS(priv, probe)(card);
+ return ret;
+}
+
+static inline int acp_ops_configure_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, configure_link))
+ ret = ACP_OPS(priv, configure_link)(card, dai_link);
+ return ret;
+}
+
+static inline int acp_ops_configure_widgets(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, configure_widgets))
+ ret = ACP_OPS(priv, configure_widgets)(card);
+ return ret;
+}
+
+static inline int acp_ops_suspend_pre(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, suspend_pre))
+ ret = ACP_OPS(priv, suspend_pre)(card);
+ return ret;
+}
+
+static inline int acp_ops_resume_post(struct snd_soc_card *card)
+{
+ int ret = 1;
+ struct acp_card_drvdata *priv = acp_get_drvdata(card);
+
+ if (ACP_OPS(priv, resume_post))
+ ret = ACP_OPS(priv, resume_post)(card);
+ return ret;
+}
+
+#endif
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
new file mode 100644
index 000000000000..0b2aa33cc426
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -0,0 +1,300 @@
+// 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. All rights reserved.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic PCI interface for ACP device
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_pci"
+
+#define ACP3x_REG_START 0x1240000
+#define ACP3x_REG_END 0x125C000
+
+static irqreturn_t irq_handler(int irq, void *data)
+{
+ struct acp_chip_info *chip = data;
+
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->irq)
+ return chip->acp_hw_ops->irq(irq, chip);
+
+ return IRQ_NONE;
+}
+static void acp_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp_platform_devs(struct pci_dev *pci, struct acp_chip_info *chip, u32 addr)
+{
+ struct platform_device_info pdevinfo;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (chip->is_i2s_config || chip->is_pdm_dev) {
+ chip->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!chip->res) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ chip->res->flags = IORESOURCE_MEM;
+ chip->res->start = addr;
+ chip->res->end = addr + (ACP3x_REG_END - ACP3x_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp_fill_platform_dev_info(&pdevinfo, parent, NULL, chip->name,
+ 0, chip->res, 1, chip, sizeof(*chip));
+
+ chip->acp_plat_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(chip->acp_plat_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(chip->acp_plat_dev);
+ goto err;
+ }
+ if (chip->is_pdm_dev && chip->is_pdm_config) {
+ chip->dmic_codec_dev = platform_device_register_data(&pci->dev,
+ "dmic-codec",
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+ if (IS_ERR(chip->dmic_codec_dev)) {
+ dev_err(&pci->dev, "failed to create DMIC device\n");
+ ret = PTR_ERR(chip->dmic_codec_dev);
+ goto unregister_acp_plat_dev;
+ }
+ }
+ return 0;
+unregister_acp_plat_dev:
+ platform_device_unregister(chip->acp_plat_dev);
+err:
+ return ret;
+}
+
+static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ struct device *dev = &pci->dev;
+ struct acp_chip_info *chip;
+ unsigned int flag, addr;
+ int ret;
+
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY && flag != FLAG_AMD_LEGACY_ONLY_DMIC)
+ return -ENODEV;
+
+ chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ if (pci_enable_device(pci))
+ return dev_err_probe(&pci->dev, -ENODEV,
+ "pci_enable_device failed\n");
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ ret = -ENOMEM;
+ goto disable_pci;
+ }
+
+ pci_set_master(pci);
+
+ chip->acp_rev = pci->revision;
+ switch (pci->revision) {
+ case 0x01:
+ chip->name = "acp_asoc_renoir";
+ chip->rsrc = &rn_rsrc;
+ chip->acp_hw_ops_init = acp31_hw_ops_init;
+ chip->machines = &snd_soc_acpi_amd_acp_machines;
+ break;
+ case 0x6f:
+ chip->name = "acp_asoc_rembrandt";
+ chip->rsrc = &rmb_rsrc;
+ chip->acp_hw_ops_init = acp6x_hw_ops_init;
+ chip->machines = &snd_soc_acpi_amd_rmb_acp_machines;
+ break;
+ case 0x63:
+ chip->name = "acp_asoc_acp63";
+ chip->rsrc = &acp63_rsrc;
+ chip->acp_hw_ops_init = acp63_hw_ops_init;
+ chip->machines = &snd_soc_acpi_amd_acp63_acp_machines;
+ break;
+ case 0x70:
+ case 0x71:
+ chip->name = "acp_asoc_acp70";
+ chip->rsrc = &acp70_rsrc;
+ chip->acp_hw_ops_init = acp70_hw_ops_init;
+ chip->machines = &snd_soc_acpi_amd_acp70_acp_machines;
+ break;
+ default:
+ dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
+ ret = -EINVAL;
+ goto release_regions;
+ }
+ chip->flag = flag;
+
+ addr = pci_resource_start(pci, 0);
+ chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
+ if (!chip->base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ chip->addr = addr;
+
+ chip->acp_hw_ops_init(chip);
+ ret = acp_hw_init(chip);
+ if (ret)
+ goto release_regions;
+
+ ret = devm_request_irq(dev, pci->irq, irq_handler,
+ IRQF_SHARED, "ACP_I2S_IRQ", chip);
+ if (ret) {
+ dev_err(&pci->dev, "ACP I2S IRQ request failed %d\n", ret);
+ goto de_init;
+ }
+
+ check_acp_config(pci, chip);
+ if (!chip->is_pdm_dev && !chip->is_i2s_config)
+ goto skip_pdev_creation;
+
+ ret = create_acp_platform_devs(pci, chip, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+
+ chip->chip_pdev = chip->acp_plat_dev;
+ chip->dev = &chip->acp_plat_dev->dev;
+
+ acp_machine_select(chip);
+
+ INIT_LIST_HEAD(&chip->stream_list);
+ spin_lock_init(&chip->acp_lock);
+skip_pdev_creation:
+ dev_set_drvdata(&pci->dev, chip);
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return ret;
+
+de_init:
+ acp_hw_deinit(chip);
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+};
+
+static int snd_acp_suspend(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_hw_deinit(chip);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_acp_resume(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_drvdata(dev);
+ ret = acp_hw_init(chip);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ ret = acp_hw_en_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP en-interrupts failed\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops acp_pm_ops = {
+ RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
+static void acp_pci_remove(struct pci_dev *pci)
+{
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = pci_get_drvdata(pci);
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ if (chip->dmic_codec_dev)
+ platform_device_unregister(chip->dmic_codec_dev);
+ if (chip->acp_plat_dev)
+ platform_device_unregister(chip->acp_plat_dev);
+ if (chip->mach_dev)
+ platform_device_unregister(chip->mach_dev);
+
+ ret = acp_hw_deinit(chip);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+}
+
+/* PCI IDs */
+static const struct pci_device_id acp_pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, acp_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_amd_acp_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = acp_pci_ids,
+ .probe = acp_pci_probe,
+ .remove = acp_pci_remove,
+ .driver = {
+ .pm = pm_ptr(&acp_pm_ops),
+ },
+};
+module_pci_driver(snd_amd_acp_pci_driver);
+
+MODULE_DESCRIPTION("AMD ACP common PCI support");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("SND_SOC_ACP_COMMON");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c
new file mode 100644
index 000000000000..1bfc34c2aa53
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -0,0 +1,187 @@
+// 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>
+// Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+//
+
+/*
+ * Generic Hardware interface for ACP Audio PDM controller
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp-pdm"
+
+static int acp_dmic_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip;
+ u32 physical_addr, size_dmic, period_bytes;
+ unsigned int dmic_ctrl;
+
+ chip = dev_get_platdata(dev);
+ /* Enable default DMIC clk */
+ writel(PDM_CLK_FREQ_MASK, chip->base + ACP_WOV_CLK_CTRL);
+ dmic_ctrl = readl(chip->base + ACP_WOV_MISC_CTRL);
+ dmic_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(dmic_ctrl, chip->base + ACP_WOV_MISC_CTRL);
+
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ size_dmic = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ if (chip->acp_rev >= ACP70_PCI_ID)
+ physical_addr = ACP7x_DMIC_MEM_WINDOW_START;
+ else
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init DMIC Ring buffer */
+ writel(physical_addr, chip->base + ACP_WOV_RX_RINGBUFADDR);
+ writel(size_dmic, chip->base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, chip->base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+
+ return 0;
+}
+
+static int acp_dmic_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int dma_enable;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ if (!(dma_enable & DMA_EN_MASK)) {
+ writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_ENABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ }
+
+ ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE,
+ dma_enable, (dma_enable & DMA_EN_MASK),
+ DELAY_US, PDM_TIMEOUT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dma_enable = readl(chip->base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((dma_enable & DMA_EN_MASK)) {
+ writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_DISABLE, chip->base + ACP_WOV_PDM_DMA_ENABLE);
+
+ }
+
+ ret = readl_poll_timeout_atomic(chip->base + ACP_WOV_PDM_DMA_ENABLE,
+ dma_enable, !(dma_enable & DMA_EN_MASK),
+ DELAY_US, PDM_TIMEOUT);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int acp_dmic_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hwparams, struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ unsigned int channels, ch_mask;
+
+ channels = params_channels(hwparams);
+ switch (channels) {
+ case 2:
+ ch_mask = 0;
+ break;
+ case 4:
+ ch_mask = 1;
+ break;
+ case 6:
+ ch_mask = 2;
+ break;
+ default:
+ dev_err(dev, "Invalid channels %d\n", channels);
+ return -EINVAL;
+ }
+
+ chip->ch_mask = ch_mask;
+ if (params_format(hwparams) != SNDRV_PCM_FORMAT_S32_LE) {
+ dev_err(dai->dev, "Invalid format:%d\n", params_format(hwparams));
+ return -EINVAL;
+ }
+
+ writel(ch_mask, chip->base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, chip->base + ACP_WOV_PDM_DECIMATION_FACTOR);
+
+ return 0;
+}
+
+static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct acp_stream *stream = substream->runtime->private_data;
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ u32 ext_int_ctrl;
+
+ stream->dai_id = DMIC_INSTANCE;
+ stream->irq_bit = BIT(PDM_DMA_STAT);
+ stream->pte_offset = ACP_SRAM_PDM_PTE_OFFSET;
+ stream->reg_offset = ACP_REGION2_OFFSET;
+
+ /* Enable DMIC Interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+
+ return 0;
+}
+
+static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->component->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ u32 ext_int_ctrl;
+
+ /* Disable DMIC interrupts */
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(chip, 0));
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(chip, 0));
+}
+
+const struct snd_soc_dai_ops acp_dmic_dai_ops = {
+ .prepare = acp_dmic_prepare,
+ .hw_params = acp_dmic_hwparams,
+ .trigger = acp_dmic_dai_trigger,
+ .startup = acp_dmic_dai_startup,
+ .shutdown = acp_dmic_dai_shutdown,
+};
+EXPORT_SYMBOL_NS_GPL(acp_dmic_dai_ops, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP Audio PDM controller");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
new file mode 100644
index 000000000000..b3eddf76aaa4
--- /dev/null
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -0,0 +1,358 @@
+// 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) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic interface for ACP audio blck PCM component
+ */
+
+#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"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_i2s_dma"
+
+static const struct snd_pcm_hardware acp_pcm_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .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_BATCH |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .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,
+};
+
+static const struct snd_pcm_hardware acp6x_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_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .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 acp6x_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_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .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,
+};
+
+void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream)
+{
+ struct acp_resource *rsrc = chip->rsrc;
+ u32 reg_val;
+
+ reg_val = rsrc->sram_pte_offset;
+ stream->reg_offset = 0x02000000;
+
+ writel((reg_val + GRP1_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ writel((reg_val + GRP2_OFFSET) | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+
+ writel(reg_val | BIT(31), chip->base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_5);
+ writel(PAGE_SIZE_4K_ENABLE, chip->base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5);
+
+ writel(0x01, chip->base + ACPAXI2AXI_ATU_CTRL);
+}
+EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, "SND_SOC_ACP_COMMON");
+
+void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size)
+{
+ struct snd_pcm_substream *substream = stream->substream;
+ struct acp_resource *rsrc = chip->rsrc;
+ dma_addr_t addr = substream->dma_buffer.addr;
+ int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ u32 low, high, val;
+ u16 page_idx;
+
+ switch (chip->acp_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ switch (stream->dai_id) {
+ case I2S_SP_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x0;
+ else
+ val = 0x1000;
+ break;
+ case I2S_BT_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x2000;
+ else
+ val = 0x3000;
+ break;
+ case I2S_HS_INSTANCE:
+ if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
+ val = 0x4000;
+ else
+ val = 0x5000;
+ break;
+ case DMIC_INSTANCE:
+ val = 0x6000;
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", stream->dai_id);
+ return;
+ }
+ break;
+ default:
+ val = stream->pte_offset;
+ break;
+ }
+
+ for (page_idx = 0; page_idx < num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+ writel(low, chip->base + rsrc->scratch_reg_offset + val);
+ high |= BIT(31);
+ writel(high, chip->base + rsrc->scratch_reg_offset + val + 4);
+
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(config_acp_dma, "SND_SOC_ACP_COMMON");
+
+static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip;
+ struct acp_stream *stream;
+ int ret;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->substream = substream;
+ chip = dev_get_drvdata(dev->parent);
+ switch (chip->acp_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp6x_pcm_hardware_playback;
+ else
+ runtime->hw = acp6x_pcm_hardware_capture;
+ break;
+ default:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp_pcm_hardware_playback;
+ else
+ runtime->hw = acp_pcm_hardware_capture;
+ break;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, DMA_SIZE);
+ if (ret) {
+ dev_err(component->dev, "set hw constraint HW_PARAM_PERIOD_BYTES failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, DMA_SIZE);
+ if (ret) {
+ dev_err(component->dev, "set hw constraint HW_PARAM_BUFFER_BYTES failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(stream);
+ return ret;
+ }
+ runtime->private_data = stream;
+
+ writel(1, ACP_EXTERNAL_INTR_ENB(chip));
+
+ spin_lock_irq(&chip->acp_lock);
+ list_add_tail(&stream->list, &chip->stream_list);
+ spin_unlock_irq(&chip->acp_lock);
+
+ return ret;
+}
+
+static int acp_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u64 size = params_buffer_bytes(params);
+
+ /* Configure ACP DMA block with params */
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, size);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t acp_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+
+ bytescount = acp_get_byte_count(chip, stream->dai_id, substream->stream);
+
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+
+ pos = do_div(bytescount, buffersize);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream = substream->runtime->private_data;
+
+ /* Remove entry from list */
+ spin_lock_irq(&chip->acp_lock);
+ list_del(&stream->list);
+ spin_unlock_irq(&chip->acp_lock);
+ kfree(stream);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp_pcm_component = {
+ .name = DRV_NAME,
+ .open = acp_dma_open,
+ .close = acp_dma_close,
+ .hw_params = acp_dma_hw_params,
+ .pointer = acp_dma_pointer,
+ .pcm_construct = acp_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+int acp_platform_register(struct device *dev)
+{
+ struct acp_chip_info *chip;
+ struct snd_soc_dai_driver;
+ unsigned int status;
+
+ chip = dev_get_platdata(dev);
+ if (!chip || !chip->base) {
+ dev_err(dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ status = devm_snd_soc_register_component(dev, &acp_pcm_component,
+ chip->dai_driver,
+ chip->num_dai);
+ if (status) {
+ dev_err(dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_register, "SND_SOC_ACP_COMMON");
+
+int acp_platform_unregister(struct device *dev)
+{
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(acp_platform_unregister, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP PCM Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
new file mode 100644
index 000000000000..aeffd24710e7
--- /dev/null
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -0,0 +1,247 @@
+// 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 <linux/pci.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/amd/node.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_asoc_rembrandt"
+
+#define MP1_C2PMSG_69 0x3B10A14
+#define MP1_C2PMSG_85 0x3B10A54
+#define MP1_C2PMSG_93 0x3B10A74
+
+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,
+},
+{
+ .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,
+},
+{
+ .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,
+},
+{
+ .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_master_clock_generate(struct device *dev)
+{
+ int data, rc;
+
+ rc = amd_smn_write(0, MP1_C2PMSG_93, 0);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, MP1_C2PMSG_85, 0xC4);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, MP1_C2PMSG_69, 0x4);
+ if (rc)
+ return rc;
+
+ return read_poll_timeout(smn_read_register, data, data > 0, DELAY_US,
+ ACP_TIMEOUT, false, MP1_C2PMSG_93);
+}
+
+static int rembrandt_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ u32 ret;
+
+ 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 != ACP_RMB_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp_rmb_dai;
+ chip->num_dai = ARRAY_SIZE(acp_rmb_dai);
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
+ ret = acp6x_master_clock_generate(dev);
+ if (ret)
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void rembrandt_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int rmb_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk)
+ acp6x_master_clock_generate(dev);
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rmb_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume)
+};
+
+static struct platform_driver rembrandt_driver = {
+ .probe = rembrandt_audio_probe,
+ .remove = rembrandt_audio_remove,
+ .driver = {
+ .name = "acp_asoc_rembrandt",
+ .pm = pm_ptr(&rmb_dma_pm_ops),
+ },
+};
+
+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/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
new file mode 100644
index 000000000000..04f6d70b6a92
--- /dev/null
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -0,0 +1,191 @@
+// 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) 2021 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@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 <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+
+#define DRV_NAME "acp_asoc_renoir"
+
+static struct snd_soc_dai_driver acp_renoir_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,
+},
+{
+ .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,
+},
+{
+ .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 renoir_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ 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 != ACP_RN_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp_renoir_dai;
+ chip->num_dai = ARRAY_SIZE(acp_renoir_dai);
+
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+
+ acp_platform_register(dev);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void renoir_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+}
+
+static int rn_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops rn_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume)
+};
+
+static struct platform_driver renoir_driver = {
+ .probe = renoir_audio_probe,
+ .remove = renoir_audio_remove,
+ .driver = {
+ .name = "acp_asoc_renoir",
+ .pm = pm_ptr(&rn_dma_pm_ops),
+ },
+};
+
+module_platform_driver(renoir_driver);
+
+MODULE_DESCRIPTION("AMD ACP Renoir 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/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
new file mode 100644
index 000000000000..6c24f9d8694e
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-legacy-mach - ASoC legacy Machine driver for AMD SoundWire platforms
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "soc_amd_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long soc_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_JACK_JDSRC(soc_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_JACK_JDSRC(soc_sdw_quirk));
+ if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+ if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR)
+ dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n");
+}
+
+static int soc_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ soc_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id soc_sdw_quirk_table[] = {
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {}
+};
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *soc_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf,
+ struct snd_soc_dai_link_component *sdw_platform_component)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *soc_end;
+ int cpu_pin_id;
+ int stream;
+ int ret;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (soc_end->name_prefix) {
+ (*codec_conf)->dlc.name = soc_end->codec_name;
+ (*codec_conf)->name_prefix = soc_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (soc_end->include_sidecar) {
+ ret = soc_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-PIN%d-PLAYBACK",
+ "SDW%d-PIN%d-CAPTURE",
+ "SDW%d-PIN%d-PLAYBACK-%s",
+ "SDW%d-PIN%d-CAPTURE-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ int num_cpus = hweight32(soc_dai->link_mask[stream]);
+ int num_codecs = soc_dai->num_devs[stream];
+ int playback, capture;
+ int j = 0;
+ char *name;
+
+ if (!soc_dai->num_devs[stream])
+ continue;
+
+ soc_end = list_first_entry(&soc_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = soc_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ ret = get_acp63_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(soc_end->link_mask) - 1,
+ cpu_pin_id,
+ type_strings[soc_end->dai_info->dai_type]);
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(soc_end->link_mask) - 1,
+ cpu_pin_id);
+ }
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (!soc_end->dai_info->direction[stream])
+ continue;
+
+ int link_num = ffs(soc_end->link_mask) - 1;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, cpu_pin_id);
+ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ codec_maps[j].cpu = 0;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = soc_end->codec_name;
+ codecs[j].dai_name = soc_end->dai_info->dai_name;
+ j++;
+ }
+
+ WARN_ON(j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, sdw_platform_component,
+ 1, codecs, num_codecs,
+ 0, asoc_sdw_rtd_init, &sdw_ops);
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(soc_end, &soc_dai->endpoints, list) {
+ if (soc_end->dai_info->init)
+ soc_end->dai_info->init(card, *dai_links,
+ soc_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *soc_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct snd_soc_dai_link_component *sdw_platform_component;
+ int ret;
+
+ sdw_platform_component = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!sdw_platform_component)
+ return -ENOMEM;
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ sdw_platform_component->name = "amd_ps_sdw_dma.0";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* generate DAI links by each sdw link */
+ while (soc_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, soc_dais, dai_links,
+ &current_be_id, codec_conf, sdw_platform_component);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ soc_dais++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct snd_soc_dai_link_component *pdm_cpu;
+ struct snd_soc_dai_link_component *pdm_platform;
+ int ret;
+
+ pdm_cpu = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!pdm_cpu)
+ return -ENOMEM;
+
+ pdm_platform = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL);
+ if (!pdm_platform)
+ return -ENOMEM;
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ pdm_cpu->name = "acp_ps_pdm_dma.0";
+ pdm_platform->name = "acp_ps_pdm_dma.0";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *be_id = ACP_DMIC_BE_ID;
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
+ 0, 1, // DMIC only supports capture
+ pdm_cpu->name, pdm_platform->name,
+ "dmic-codec.0", "dmic-hifi", no_pcm,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int soc_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, dmic_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct asoc_sdw_endpoint *soc_ends __free(kfree) = NULL;
+ struct asoc_sdw_dailink *soc_dais __free(kfree) = NULL;
+ struct snd_soc_codec_conf *codec_conf;
+ struct snd_soc_dai_link *dai_links;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_links;
+ int be_id = 0;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ /* One per DAI link, worst case is a DAI link for every endpoint */
+ soc_dais = kcalloc(num_ends, sizeof(*soc_dais), GFP_KERNEL);
+ if (!soc_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ soc_ends = kcalloc(num_ends, sizeof(*soc_ends), GFP_KERNEL);
+ if (!soc_ends)
+ return -ENOMEM;
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, soc_dais, soc_ends, &num_devs);
+ if (ret < 0)
+ return ret;
+
+ sdw_be_num = ret;
+
+ /* enable dmic */
+ if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
+ dmic_num = 1;
+
+ dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
+
+ codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf)
+ return -ENOMEM;
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + dmic_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links)
+ return -ENOMEM;
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_devs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ soc_dais, &codec_conf);
+ if (ret)
+ return ret;
+ }
+
+ /* dmic */
+ if (dmic_num > 0) {
+ if (ctx->ignore_internal_dmic) {
+ dev_warn(dev, "Ignoring ACP DMIC\n");
+ } else {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id, 0);
+ if (ret)
+ return ret;
+ }
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+ return ret;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct amd_mc_ctx *amd_ctx;
+ struct asoc_sdw_mc_private *ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
+ if (!amd_ctx)
+ return -ENOMEM;
+
+ amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
+ amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ ctx->private = amd_ctx;
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "amd-soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = asoc_sdw_card_late_probe;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ dmi_check_system(soc_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ soc_sdw_quirk, quirk_override);
+ soc_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = soc_sdw_quirk;
+ dev_dbg(card->dev, "legacy quirk 0x%lx\n", ctx->mc_quirk);
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = soc_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+ if (mach->mach_params.dmic_num) {
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:dmic cfg-mics:%d",
+ card->components,
+ mach->mach_params.dmic_num);
+ if (!card->components)
+ return -ENOMEM;
+ }
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "amd_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver soc_sdw_driver = {
+ .driver = {
+ .name = "amd_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(soc_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC AMD SoundWire Legacy Generic Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
+MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
diff --git a/sound/soc/amd/acp/acp-sdw-mach-common.c b/sound/soc/amd/acp/acp-sdw-mach-common.c
new file mode 100644
index 000000000000..e5f394dc2f4c
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-mach-common.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-mach-common - Common machine driver helper functions for
+ * legacy(No DSP) stack and SOF stack.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include "soc_amd_sdw_common.h"
+
+int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP63_SW0_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP63_SW1_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ case SOC_SDW_AMP_IN_DAI_ID:
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP63_SW1_AUDIO0_RX;
+ break;
+ default:
+ dev_err(dev, "invalid be_id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(dev, "Invalid link id:%d\n", sdw_link_id);
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp63_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(dev, "sdw_link_id:%d, be_id:%d, cpu_pin_id:%d\n", sdw_link_id, be_id, *cpu_pin_id);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp70_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
+MODULE_DESCRIPTION("AMD SoundWire Common Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
new file mode 100644
index 000000000000..654fe78b2e2e
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright(c) 2024 Advanced Micro Devices, Inc.
+
+/*
+ * acp-sdw-sof-mach - ASoC Machine driver for AMD SoundWire platforms
+ */
+
+#include <linux/bitmap.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "soc_amd_sdw_common.h"
+#include "../../codecs/rt711.h"
+
+static unsigned long sof_sdw_quirk = RT711_JD1;
+static int quirk_override = -1;
+module_param_named(quirk, quirk_override, int, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
+
+static void log_quirks(struct device *dev)
+{
+ if (SOC_JACK_JDSRC(sof_sdw_quirk))
+ dev_dbg(dev, "quirk realtek,jack-detect-source %ld\n",
+ SOC_JACK_JDSRC(sof_sdw_quirk));
+ if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC)
+ dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+}
+
+static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
+{
+ sof_sdw_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id sof_sdw_quirk_table[] = {
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "AMD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Birman-PHX"),
+ },
+ .driver_data = (void *)RT711_JD2,
+ },
+ {}
+};
+
+static struct snd_soc_dai_link_component platform_component[] = {
+ {
+ /* name might be overridden during probe */
+ .name = "0000:04:00.5",
+ }
+};
+
+static const struct snd_soc_ops sdw_ops = {
+ .startup = asoc_sdw_startup,
+ .prepare = asoc_sdw_prepare,
+ .trigger = asoc_sdw_trigger,
+ .hw_params = asoc_sdw_hw_params,
+ .hw_free = asoc_sdw_hw_free,
+ .shutdown = asoc_sdw_shutdown,
+};
+
+static const char * const type_strings[] = {"SimpleJack", "SmartAmp", "SmartMic"};
+
+static int create_sdw_dailink(struct snd_soc_card *card,
+ struct asoc_sdw_dailink *sof_dai,
+ struct snd_soc_dai_link **dai_links,
+ int *be_id, struct snd_soc_codec_conf **codec_conf)
+{
+ struct device *dev = card->dev;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct amd_mc_ctx *amd_ctx = (struct amd_mc_ctx *)ctx->private;
+ struct asoc_sdw_endpoint *sof_end;
+ int cpu_pin_id;
+ int stream;
+ int ret;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->name_prefix) {
+ (*codec_conf)->dlc.name = sof_end->codec_name;
+ (*codec_conf)->name_prefix = sof_end->name_prefix;
+ (*codec_conf)++;
+ }
+
+ if (sof_end->include_sidecar) {
+ ret = sof_end->codec_info->add_sidecar(card, dai_links, codec_conf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for_each_pcm_streams(stream) {
+ static const char * const sdw_stream_name[] = {
+ "SDW%d-PIN%d-PLAYBACK",
+ "SDW%d-PIN%d-CAPTURE",
+ "SDW%d-PIN%d-PLAYBACK-%s",
+ "SDW%d-PIN%d-CAPTURE-%s",
+ };
+ struct snd_soc_dai_link_ch_map *codec_maps;
+ struct snd_soc_dai_link_component *codecs;
+ struct snd_soc_dai_link_component *cpus;
+ int num_cpus = hweight32(sof_dai->link_mask[stream]);
+ int num_codecs = sof_dai->num_devs[stream];
+ int playback, capture;
+ int j = 0;
+ char *name;
+
+ if (!sof_dai->num_devs[stream])
+ continue;
+
+ sof_end = list_first_entry(&sof_dai->endpoints,
+ struct asoc_sdw_endpoint, list);
+
+ *be_id = sof_end->dai_info->dailink[stream];
+ if (*be_id < 0) {
+ dev_err(dev, "Invalid dailink id %d\n", *be_id);
+ return -EINVAL;
+ }
+
+ switch (amd_ctx->acp_rev) {
+ case ACP63_PCI_REV:
+ ret = get_acp63_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* create stream name according to first link id */
+ if (ctx->append_dai_type) {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream + 2],
+ ffs(sof_end->link_mask) - 1,
+ cpu_pin_id,
+ type_strings[sof_end->dai_info->dai_type]);
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL,
+ sdw_stream_name[stream],
+ ffs(sof_end->link_mask) - 1,
+ cpu_pin_id);
+ }
+ if (!name)
+ return -ENOMEM;
+
+ cpus = devm_kcalloc(dev, num_cpus, sizeof(*cpus), GFP_KERNEL);
+ if (!cpus)
+ return -ENOMEM;
+
+ codecs = devm_kcalloc(dev, num_codecs, sizeof(*codecs), GFP_KERNEL);
+ if (!codecs)
+ return -ENOMEM;
+
+ codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
+ if (!codec_maps)
+ return -ENOMEM;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (!sof_end->dai_info->direction[stream])
+ continue;
+
+ int link_num = ffs(sof_end->link_mask) - 1;
+
+ cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SDW%d Pin%d",
+ link_num, cpu_pin_id);
+ dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
+ if (!cpus->dai_name)
+ return -ENOMEM;
+
+ codec_maps[j].cpu = 0;
+ codec_maps[j].codec = j;
+
+ codecs[j].name = sof_end->codec_name;
+ codecs[j].dai_name = sof_end->dai_info->dai_name;
+ j++;
+ }
+
+ WARN_ON(j != num_codecs);
+
+ playback = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
+
+ asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
+ cpus, num_cpus, platform_component,
+ ARRAY_SIZE(platform_component), codecs, num_codecs,
+ 1, asoc_sdw_rtd_init, &sdw_ops);
+
+ /*
+ * SoundWire DAILINKs use 'stream' functions and Bank Switch operations
+ * based on wait_for_completion(), tag them as 'nonatomic'.
+ */
+ (*dai_links)->nonatomic = true;
+ (*dai_links)->ch_maps = codec_maps;
+
+ list_for_each_entry(sof_end, &sof_dai->endpoints, list) {
+ if (sof_end->dai_info->init)
+ sof_end->dai_info->init(card, *dai_links,
+ sof_end->codec_info,
+ playback);
+ }
+
+ (*dai_links)++;
+ }
+
+ return 0;
+}
+
+static int create_sdw_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id,
+ struct asoc_sdw_dailink *sof_dais,
+ struct snd_soc_codec_conf **codec_conf)
+{
+ int ret;
+
+ /* generate DAI links by each sdw link */
+ while (sof_dais->initialised) {
+ int current_be_id = 0;
+
+ ret = create_sdw_dailink(card, sof_dais, dai_links,
+ &current_be_id, codec_conf);
+ if (ret)
+ return ret;
+
+ /* Update the be_id to match the highest ID used for SDW link */
+ if (*be_id < current_be_id)
+ *be_id = current_be_id;
+
+ sof_dais++;
+ }
+
+ return 0;
+}
+
+static int create_dmic_dailinks(struct snd_soc_card *card,
+ struct snd_soc_dai_link **dai_links, int *be_id, int no_pcm)
+{
+ struct device *dev = card->dev;
+ int ret;
+
+ ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
+ 0, 1, // DMIC only supports capture
+ "acp-sof-dmic", platform_component->name,
+ "dmic-codec", "dmic-hifi", no_pcm,
+ asoc_sdw_dmic_init, NULL);
+ if (ret)
+ return ret;
+
+ (*dai_links)++;
+
+ return 0;
+}
+
+static int sof_card_dai_links_create(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
+ int sdw_be_num = 0, dmic_num = 0;
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params;
+ struct asoc_sdw_endpoint *sof_ends __free(kfree) = NULL;
+ struct asoc_sdw_dailink *sof_dais __free(kfree) = NULL;
+ struct snd_soc_codec_conf *codec_conf;
+ struct snd_soc_dai_link *dai_links;
+ int num_devs = 0;
+ int num_ends = 0;
+ int num_links;
+ int be_id = 0;
+ int ret;
+
+ ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends);
+ if (ret < 0) {
+ dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
+ return ret;
+ }
+
+ /* One per DAI link, worst case is a DAI link for every endpoint */
+ sof_dais = kcalloc(num_ends, sizeof(*sof_dais), GFP_KERNEL);
+ if (!sof_dais)
+ return -ENOMEM;
+
+ /* One per endpoint, ie. each DAI on each codec/amp */
+ sof_ends = kcalloc(num_ends, sizeof(*sof_ends), GFP_KERNEL);
+ if (!sof_ends)
+ return -ENOMEM;
+
+ ret = asoc_sdw_parse_sdw_endpoints(card, sof_dais, sof_ends, &num_devs);
+ if (ret < 0)
+ return ret;
+
+ sdw_be_num = ret;
+
+ /* enable dmic */
+ if (sof_sdw_quirk & ASOC_SDW_ACP_DMIC || mach_params->dmic_num)
+ dmic_num = 1;
+
+ dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
+
+ codec_conf = devm_kcalloc(dev, num_devs, sizeof(*codec_conf), GFP_KERNEL);
+ if (!codec_conf)
+ return -ENOMEM;
+
+ /* allocate BE dailinks */
+ num_links = sdw_be_num + dmic_num;
+ dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL);
+ if (!dai_links)
+ return -ENOMEM;
+
+ card->codec_conf = codec_conf;
+ card->num_configs = num_devs;
+ card->dai_link = dai_links;
+ card->num_links = num_links;
+
+ /* SDW */
+ if (sdw_be_num) {
+ ret = create_sdw_dailinks(card, &dai_links, &be_id,
+ sof_dais, &codec_conf);
+ if (ret)
+ return ret;
+ }
+
+ /* dmic */
+ if (dmic_num > 0) {
+ if (ctx->ignore_internal_dmic) {
+ dev_warn(dev, "Ignoring ACP DMIC\n");
+ } else {
+ ret = create_dmic_dailinks(card, &dai_links, &be_id, 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+ WARN_ON(dai_links != card->dai_link + card->num_links);
+
+ return ret;
+}
+
+static int mc_probe(struct platform_device *pdev)
+{
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ struct snd_soc_card *card;
+ struct amd_mc_ctx *amd_ctx;
+ struct asoc_sdw_mc_private *ctx;
+ int amp_num = 0, i;
+ int ret;
+
+ amd_ctx = devm_kzalloc(&pdev->dev, sizeof(*amd_ctx), GFP_KERNEL);
+ if (!amd_ctx)
+ return -ENOMEM;
+
+ amd_ctx->acp_rev = mach->mach_params.subsystem_rev;
+ amd_ctx->max_sdw_links = ACP63_SDW_MAX_LINKS;
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count();
+ ctx->private = amd_ctx;
+ card = &ctx->card;
+ card->dev = &pdev->dev;
+ card->name = "amd-soundwire";
+ card->owner = THIS_MODULE;
+ card->late_probe = asoc_sdw_card_late_probe;
+
+ snd_soc_card_set_drvdata(card, ctx);
+
+ dmi_check_system(sof_sdw_quirk_table);
+
+ if (quirk_override != -1) {
+ dev_info(card->dev, "Overriding quirk 0x%lx => 0x%x\n",
+ sof_sdw_quirk, quirk_override);
+ sof_sdw_quirk = quirk_override;
+ }
+
+ log_quirks(card->dev);
+
+ ctx->mc_quirk = sof_sdw_quirk;
+ /* reset amp_num to ensure amp_num++ starts from 0 in each probe */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ codec_info_list[i].amp_num = 0;
+
+ ret = sof_card_dai_links_create(card);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * the default amp_num is zero for each codec and
+ * amp_num will only be increased for active amp
+ * codecs on used platform
+ */
+ for (i = 0; i < ctx->codec_info_list_count; i++)
+ amp_num += codec_info_list[i].amp_num;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ " cfg-amp:%d", amp_num);
+ if (!card->components)
+ return -ENOMEM;
+
+ /* Register the card */
+ ret = devm_snd_soc_register_card(card->dev, card);
+ if (ret) {
+ dev_err_probe(card->dev, ret, "snd_soc_register_card failed %d\n", ret);
+ asoc_sdw_mc_dailink_exit_loop(card);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, card);
+
+ return ret;
+}
+
+static void mc_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ asoc_sdw_mc_dailink_exit_loop(card);
+}
+
+static const struct platform_device_id mc_id_table[] = {
+ { "amd_sof_sdw", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, mc_id_table);
+
+static struct platform_driver sof_sdw_driver = {
+ .driver = {
+ .name = "amd_sof_sdw",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mc_probe,
+ .remove = mc_remove,
+ .id_table = mc_id_table,
+};
+
+module_platform_driver(sof_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC AMD SoundWire Generic Machine driver");
+MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDW_UTILS");
+MODULE_IMPORT_NS("SND_SOC_AMD_SDW_MACH");
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
new file mode 100644
index 000000000000..6215e31ecedd
--- /dev/null
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -0,0 +1,174 @@
+// 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) 2021, 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+//
+
+/*
+ * SOF Machine Driver Support for ACP HW block
+ */
+
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
+#include <linux/module.h>
+
+#include "acp-mach.h"
+
+static struct acp_card_drvdata sof_rt5682_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_rt5682s_max_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_SP,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+};
+
+static struct acp_card_drvdata sof_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,
+};
+
+static struct acp_card_drvdata sof_rt5682s_hs_rt1019_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,
+};
+
+static struct acp_card_drvdata sof_nau8821_max98388_data = {
+ .hs_cpu_id = I2S_SP,
+ .amp_cpu_id = I2S_HS,
+ .bt_cpu_id = I2S_BT,
+ .hs_codec_id = NAU8821,
+ .amp_codec_id = MAX98388,
+ .soc_mclk = true,
+};
+
+static int acp_sof_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
+ int ret;
+
+ if (!pdev->id_entry)
+ return -EINVAL;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->name = pdev->id_entry->name;
+ card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ /* Widgets and controls added per-codec in acp-mach-common.c */
+
+ acp_card_drvdata = card->drvdata;
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
+ acp_card_drvdata->acp_rev = mach->mach_params.subsystem_rev;
+ ret = acp_sofdsp_dai_links_create(card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to create DAI links\n");
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register card(%s)\n", card->name);
+ return 0;
+}
+
+static const struct platform_device_id board_ids[] = {
+ {
+ .name = "rt5682-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_rt1019_data
+ },
+ {
+ .name = "rt5682-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682_max_data
+ },
+ {
+ .name = "rt5682s-max",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_max_data
+ },
+ {
+ .name = "rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data
+ },
+ {
+ .name = "nau8825-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8825_data
+ },
+ {
+ .name = "rt5682s-hs-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_hs_rt1019_data
+ },
+ {
+ .name = "nau8821-max",
+ .driver_data = (kernel_ulong_t)&sof_nau8821_max98388_data
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, board_ids);
+
+static struct platform_driver acp_asoc_audio = {
+ .driver = {
+ .name = "sof_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp_sof_probe,
+ .id_table = board_ids,
+};
+
+module_platform_driver(acp_asoc_audio);
+
+MODULE_IMPORT_NS("SND_SOC_AMD_MACH");
+MODULE_DESCRIPTION("ACP SOF Machine Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
new file mode 100644
index 000000000000..eb5d4a5baef2
--- /dev/null
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
@@ -0,0 +1,452 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Machine driver for AMD ACP Audio engine using ES8336 codec.
+//
+// Copyright 2023 Marian Postevca <posteuca@mutex.one>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc-acpi.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/string_choices.h>
+#include "../acp-mach.h"
+#include "acp3x-es83xx.h"
+
+#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv))
+
+#define DUAL_CHANNEL 2
+
+#define ES83XX_ENABLE_DMIC BIT(4)
+#define ES83XX_48_MHZ_MCLK BIT(5)
+
+struct acp3x_es83xx_private {
+ bool speaker_on;
+ bool headphone_on;
+ unsigned long quirk;
+ struct snd_soc_component *codec;
+ struct device *codec_dev;
+ struct gpio_desc *gpio_speakers, *gpio_headphone;
+ struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio;
+ struct acpi_gpio_mapping gpio_mapping[3];
+ struct snd_soc_dapm_route mic_map[2];
+};
+
+static const unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256)
+#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000)
+
+static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ struct acp3x_es83xx_private *priv;
+ unsigned int freq;
+ int ret;
+
+ runtime = substream->runtime;
+ rtd = snd_soc_substream_to_rtd(substream);
+ codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ priv = get_mach_priv(rtd->card);
+
+ if (priv->quirk & ES83XX_48_MHZ_MCLK) {
+ dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n");
+ freq = ES83xx_48_MHZ_MCLK_FREQ;
+ } else {
+ dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n");
+ freq = ES83xx_12288_KHZ_MCLK_FREQ;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ return 0;
+}
+
+static struct snd_soc_jack es83xx_jack;
+
+static struct snd_soc_jack_pin es83xx_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+
+ SND_SOC_DAPM_SUPPLY("Headphone Power", SND_SOC_NOPM, 0, 0,
+ acp3x_es83xx_headphone_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,
+ acp3x_es83xx_speaker_power_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = {
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Headphone", NULL, "Headphone Power"},
+
+ /*
+ * There is no separate speaker output instead the speakers are muxed to
+ * the HP outputs. The mux is controlled Speaker and/or headphone switch.
+ */
+ {"Speaker", NULL, "HPOL"},
+ {"Speaker", NULL, "HPOR"},
+ {"Speaker", NULL, "Speaker Power"},
+};
+
+
+static const struct snd_kcontrol_new acp3x_es83xx_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+};
+
+static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card)
+{
+ card->dapm_widgets = acp3x_es83xx_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets);
+ card->controls = acp3x_es83xx_controls;
+ card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls);
+ card->dapm_routes = acp3x_es83xx_audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map);
+
+ return 0;
+}
+
+static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card);
+
+ dev_dbg(priv->codec_dev, "headphone power event = %d\n", event);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->headphone_on = true;
+ else
+ priv->headphone_on = false;
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);
+
+ return 0;
+}
+
+static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(w->dapm->card);
+
+ dev_dbg(priv->codec_dev, "speaker power event: %d\n", event);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ priv->speaker_on = true;
+ else
+ priv->speaker_on = false;
+
+ gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);
+ gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);
+
+ return 0;
+}
+
+static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ /* We need to disable the jack in the machine driver suspend
+ * callback so that the CODEC suspend callback actually gets
+ * called. Without doing it, the CODEC suspend/resume
+ * callbacks do not get called if headphones are plugged in.
+ * This is because plugging in headphones keeps some supplies
+ * active, this in turn means that the lowest bias level
+ * that the CODEC can go to is SND_SOC_BIAS_STANDBY.
+ * If components do not set idle_bias_on to true then
+ * their suspend/resume callbacks do not get called.
+ */
+ dev_dbg(priv->codec_dev, "card suspend\n");
+ snd_soc_component_set_jack(priv->codec, NULL, NULL);
+ return 0;
+}
+
+static int acp3x_es83xx_resume_post(struct snd_soc_card *card)
+{
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+
+ /* We disabled jack detection in suspend callback,
+ * enable it back.
+ */
+ dev_dbg(priv->codec_dev, "card resume\n");
+ snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL);
+ return 0;
+}
+
+static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
+{
+
+ priv->enable_spk_gpio.crs_entry_index = 0;
+ priv->enable_hp_gpio.crs_entry_index = 1;
+
+ priv->enable_spk_gpio.active_low = false;
+ priv->enable_hp_gpio.active_low = false;
+
+ priv->gpio_mapping[0].name = "speakers-enable-gpios";
+ priv->gpio_mapping[0].data = &priv->enable_spk_gpio;
+ priv->gpio_mapping[0].size = 1;
+ priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
+
+ priv->gpio_mapping[1].name = "headphone-enable-gpios";
+ priv->gpio_mapping[1].data = &priv->enable_hp_gpio;
+ priv->gpio_mapping[1].size = 1;
+ priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;
+
+ dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n",
+ priv->enable_spk_gpio.crs_entry_index,
+ str_low_high(priv->enable_spk_gpio.active_low),
+ priv->enable_hp_gpio.crs_entry_index,
+ str_low_high(priv->enable_hp_gpio.active_low));
+ return 0;
+}
+
+static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv)
+{
+ int num_routes = 0;
+ int i;
+
+ if (!(priv->quirk & ES83XX_ENABLE_DMIC)) {
+ priv->mic_map[num_routes].sink = "MIC1";
+ priv->mic_map[num_routes].source = "Internal Mic";
+ num_routes++;
+ }
+
+ priv->mic_map[num_routes].sink = "MIC2";
+ priv->mic_map[num_routes].source = "Headset Mic";
+ num_routes++;
+
+ for (i = 0; i < num_routes; i++)
+ dev_info(priv->codec_dev, "%s is %s\n",
+ priv->mic_map[i].source, priv->mic_map[i].sink);
+
+ return num_routes;
+}
+
+static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;
+ struct snd_soc_card *card = runtime->card;
+ struct acp3x_es83xx_private *priv = get_mach_priv(card);
+ int ret = 0;
+ int num_routes;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &es83xx_jack, es83xx_jack_pins,
+ ARRAY_SIZE(es83xx_jack_pins));
+ if (ret) {
+ dev_err(card->dev, "jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+
+ snd_soc_component_set_jack(codec, &es83xx_jack, NULL);
+
+ priv->codec = codec;
+ acp3x_es83xx_configure_gpios(priv);
+
+ ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping);
+ if (ret)
+ dev_warn(priv->codec_dev, "failed to add speaker gpio\n");
+
+ priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable",
+ priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_speakers)) {
+ dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n");
+ return PTR_ERR(priv->gpio_speakers);
+ }
+
+ priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable",
+ priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpio_headphone)) {
+ dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n");
+ return PTR_ERR(priv->gpio_headphone);
+ }
+
+ num_routes = acp3x_es83xx_configure_mics(priv);
+ if (num_routes > 0) {
+ ret = snd_soc_dapm_add_routes(&card->dapm, priv->mic_map, num_routes);
+ if (ret != 0)
+ device_remove_software_node(priv->codec_dev);
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp3x_es83xx_ops = {
+ .startup = acp3x_es83xx_codec_startup,
+};
+
+
+SND_SOC_DAILINK_DEF(codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
+
+static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),
+ },
+ .driver_data = (void *)(ES83XX_ENABLE_DMIC),
+ },
+ {}
+};
+
+static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)
+{
+ link->codecs = codec;
+ link->num_codecs = ARRAY_SIZE(codec);
+ link->init = acp3x_es83xx_init;
+ link->ops = &acp3x_es83xx_ops;
+ link->dai_fmt = SND_SOC_DAIFMT_I2S
+ | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ return 0;
+}
+
+static int acp3x_es83xx_probe(struct snd_soc_card *card)
+{
+ int ret = 0;
+ struct device *dev = card->dev;
+ const struct dmi_system_id *dmi_id;
+
+ dmi_id = dmi_first_match(acp3x_es83xx_dmi_table);
+ if (dmi_id && dmi_id->driver_data) {
+ struct acp3x_es83xx_private *priv;
+ struct acp_card_drvdata *acp_drvdata;
+ struct acpi_device *adev;
+ struct device *codec_dev;
+
+ acp_drvdata = (struct acp_card_drvdata *)card->drvdata;
+
+ dev_info(dev, "matched DMI table with this system, trying to register sound card\n");
+
+ adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1);
+ if (!adev) {
+ dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id);
+ return -ENXIO;
+ }
+
+ codec_dev = acpi_get_first_physical_node(adev);
+ acpi_dev_put(adev);
+ if (!codec_dev) {
+ dev_warn(dev, "Error cannot find codec device, will defer probe\n");
+ return -EPROBE_DEFER;
+ }
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ put_device(codec_dev);
+ return -ENOMEM;
+ }
+
+ priv->codec_dev = codec_dev;
+ priv->quirk = (unsigned long)dmi_id->driver_data;
+ acp_drvdata->mach_priv = priv;
+ dev_info(dev, "successfully probed the sound card\n");
+ } else {
+ ret = -ENODEV;
+ dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n");
+ }
+ return ret;
+}
+
+
+void acp3x_es83xx_init_ops(struct acp_mach_ops *ops)
+{
+ ops->probe = acp3x_es83xx_probe;
+ ops->configure_widgets = acp3x_es83xx_configure_widgets;
+ ops->configure_link = acp3x_es83xx_configure_link;
+ ops->suspend_pre = acp3x_es83xx_suspend_pre;
+ ops->resume_post = acp3x_es83xx_resume_post;
+}
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h
new file mode 100644
index 000000000000..03551ffdd9da
--- /dev/null
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2023 Marian Postevca <posteuca@mutex.one>
+ */
+
+#ifndef __ACP3X_ES83XX_H
+#define __ACP3X_ES83XX_H
+
+void acp3x_es83xx_init_ops(struct acp_mach_ops *ops);
+
+#endif
+
diff --git a/sound/soc/amd/acp/acp63.c b/sound/soc/amd/acp/acp63.c
new file mode 100644
index 000000000000..10fb416b959d
--- /dev/null
+++ b/sound/soc/amd/acp/acp63.c
@@ -0,0 +1,298 @@
+// 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) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
+/*
+ * Hardware interface for ACP6.3 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 <linux/pm_runtime.h>
+#include <linux/pci.h>
+
+#include <asm/amd/node.h>
+
+#include "amd.h"
+#include "acp-mach.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_asoc_acp63"
+
+#define CLK_PLL_PWR_REQ_N0 0X0006C2C0
+#define CLK_SPLL_FIELD_2_N0 0X0006C114
+#define CLK_PLL_REQ_N0 0X0006C0DC
+#define CLK_DFSBYPASS_CONTR 0X0006C2C8
+#define CLK_DFS_CNTL_N0 0X0006C1A4
+
+#define PLL_AUTO_STOP_REQ BIT(4)
+#define PLL_AUTO_START_REQ BIT(0)
+#define PLL_FRANCE_EN BIT(4)
+#define EXIT_DPF_BYPASS_0 BIT(16)
+#define EXIT_DPF_BYPASS_1 BIT(17)
+#define CLK0_DIVIDER 0X30
+
+union clk_pll_req_no {
+ struct {
+ u32 fb_mult_int : 9;
+ u32 reserved : 3;
+ u32 pll_spine_div : 4;
+ u32 gb_mult_frac : 16;
+ } bitfields, bits;
+ u32 clk_pll_req_no_reg;
+};
+
+static struct snd_soc_dai_driver acp63_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,
+},
+{
+ .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,
+},
+{
+ .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,
+},
+{
+ .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 acp63_i2s_master_clock_generate(struct acp_chip_info *chip)
+{
+ int rc;
+ u32 data;
+ union clk_pll_req_no clk_pll;
+
+ /* Clk5 pll register values to get mclk as 196.6MHz*/
+ clk_pll.bits.fb_mult_int = 0x31;
+ clk_pll.bits.pll_spine_div = 0;
+ clk_pll.bits.gb_mult_frac = 0x26E9;
+
+ rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_STOP_REQ);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_SPLL_FIELD_2_N0, &data);
+ if (rc)
+ return rc;
+ if (data & PLL_FRANCE_EN) {
+ rc = amd_smn_write(0, CLK_SPLL_FIELD_2_N0, data | PLL_FRANCE_EN);
+ if (rc)
+ return rc;
+ }
+
+ rc = amd_smn_write(0, CLK_PLL_REQ_N0, clk_pll.clk_pll_req_no_reg);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_PLL_PWR_REQ_N0, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_PLL_PWR_REQ_N0, data | PLL_AUTO_START_REQ);
+ if (rc)
+ return rc;
+
+ rc = amd_smn_read(0, CLK_DFSBYPASS_CONTR, &data);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_0);
+ if (rc)
+ return rc;
+ rc = amd_smn_write(0, CLK_DFSBYPASS_CONTR, data | EXIT_DPF_BYPASS_1);
+ if (rc)
+ return rc;
+
+ return amd_smn_write(0, CLK_DFS_CNTL_N0, CLK0_DIVIDER);
+}
+
+static int acp63_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ 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 != ACP63_PCI_ID) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp63_dai;
+ chip->num_dai = ARRAY_SIZE(acp63_dai);
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk) {
+ ret = acp63_i2s_master_clock_generate(chip);
+ if (ret)
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ if (chip->is_i2s_config && chip->rsrc->soc_mclk)
+ acp63_i2s_master_clock_generate(chip);
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops acp63_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp63_pcm_resume)
+};
+
+static struct platform_driver acp63_driver = {
+ .probe = acp63_audio_probe,
+ .remove = acp63_audio_remove,
+ .driver = {
+ .name = "acp_asoc_acp63",
+ .pm = pm_ptr(&acp63_dma_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_driver);
+
+MODULE_DESCRIPTION("AMD ACP acp63 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/acp70.c b/sound/soc/amd/acp/acp70.c
new file mode 100644
index 000000000000..b95e3949e70b
--- /dev/null
+++ b/sound/soc/amd/acp/acp70.c
@@ -0,0 +1,227 @@
+// 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) 2023 Advanced Micro Devices, Inc.
+//
+// Authors: Syed Saba kareem <syed.sabakareem@amd.com>
+/*
+ * Hardware interface for ACP7.0 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 <linux/pm_runtime.h>
+#include <linux/pci.h>
+#include "amd.h"
+#include "acp-mach.h"
+
+#include <asm/amd/node.h>
+
+#define DRV_NAME "acp_asoc_acp70"
+
+#define CLK7_CLK0_DFS_CNTL_N1 0X0006C1A4
+#define CLK0_DIVIDER 0X19
+
+static struct snd_soc_dai_driver acp70_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+},
+{
+ .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 acp_acp70_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ int ret;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ switch (chip->acp_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ break;
+ default:
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ chip->dev = dev;
+ chip->dai_driver = acp70_dai;
+ chip->num_dai = ARRAY_SIZE(acp70_dai);
+
+ /* Set clk7 DFS clock divider register value to get mclk as 196.608MHz*/
+ ret = amd_smn_write(0, CLK7_CLK0_DFS_CNTL_N1, CLK0_DIVIDER);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set I2S master clock as 196.608MHz\n");
+ return ret;
+ }
+ ret = acp_hw_en_interrupts(chip);
+ if (ret) {
+ dev_err(dev, "ACP en-interrupts failed\n");
+ return ret;
+ }
+ acp_platform_register(dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp_acp70_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip = dev_get_platdata(dev);
+ int ret;
+
+ ret = acp_hw_dis_interrupts(chip);
+ if (ret)
+ dev_err(dev, "ACP dis-interrupts failed\n");
+
+ acp_platform_unregister(dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp70_pcm_resume(struct device *dev)
+{
+ struct acp_chip_info *chip = dev_get_drvdata(dev->parent);
+ struct acp_stream *stream;
+ struct snd_pcm_substream *substream;
+ snd_pcm_uframes_t buf_in_frames;
+ u64 buf_size;
+
+ spin_lock(&chip->acp_lock);
+ list_for_each_entry(stream, &chip->stream_list, list) {
+ substream = stream->substream;
+ if (substream && substream->runtime) {
+ buf_in_frames = (substream->runtime->buffer_size);
+ buf_size = frames_to_bytes(substream->runtime, buf_in_frames);
+ config_pte_for_stream(chip, stream);
+ config_acp_dma(chip, stream, buf_size);
+ if (stream->dai_id)
+ restore_acp_i2s_params(substream, chip, stream);
+ else
+ restore_acp_pdm_params(substream, chip);
+ }
+ }
+ spin_unlock(&chip->acp_lock);
+ return 0;
+}
+
+static const struct dev_pm_ops acp70_dma_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp70_pcm_resume)
+};
+
+static struct platform_driver acp70_driver = {
+ .probe = acp_acp70_audio_probe,
+ .remove = acp_acp70_audio_remove,
+ .driver = {
+ .name = "acp_asoc_acp70",
+ .pm = pm_ptr(&acp70_dma_pm_ops),
+ },
+};
+
+module_platform_driver(acp70_driver);
+
+MODULE_DESCRIPTION("AMD ACP ACP70 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/acp_common.h b/sound/soc/amd/acp/acp_common.h
new file mode 100644
index 000000000000..f1ae88013f62
--- /dev/null
+++ b/sound/soc/amd/acp/acp_common.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
+ */
+
+/*
+ * acp_common.h - acp common header file
+ */
+
+#ifndef __ACP_COMMON_H
+#define __ACP_COMMON_H
+
+#define ACP_RN_PCI_ID 0x01
+#define ACP_VANGOGH_PCI_ID 0x50
+#define ACP_RMB_PCI_ID 0x6F
+#define ACP63_PCI_ID 0x63
+#define ACP70_PCI_ID 0x70
+#define ACP71_PCI_ID 0x71
+
+#endif
diff --git a/sound/soc/amd/acp/amd-acp63-acpi-match.c b/sound/soc/amd/acp/amd-acp63-acpi-match.c
new file mode 100644
index 000000000000..9b6a49c051cd
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp63-acpi-match.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp63-acpi-match.c - tables and support for ACP 6.3 platform
+ * ACPI enumeration.
+ *
+ * Copyright 2024 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp63_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp63_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp63_4_in_1_sdca,
+ .drv_name = "amd_sof_sdw",
+ .sof_tplg_filename = "sof-acp_6_3-rt711-l0-rt1316-l0-rt714-l1.tplg",
+ .fw_filename = "sof-acp_6_3.ri",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sof_sdw_machines);
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = acp63_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp63_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp63_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP6.3 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
new file mode 100644
index 000000000000..e87ccfeee5bd
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp70-acpi-match.c - tables and support for ACP 7.0 & ACP7.1
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0
+};
+
+static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 0,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
+ .num = 0,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_single_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_single_adr),
+ .adr_d = rt1320_1_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_rt722_l0_rt1320_l1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c
new file mode 100644
index 000000000000..d95047d2ee94
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acpi-mach.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acpi-match.c - tables and support for ACP platforms
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+
+struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = {
+ {
+ .id = "10EC5682",
+ .drv_name = "acp3xalc56821019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "acp3xalc5682sm98360",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "acp3xalc5682s1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {
+ .id = "AMDI1019",
+ .drv_name = "renoir-acp",
+ },
+ {
+ .id = "ESSX8336",
+ .drv_name = "acp3x-es83xx",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp_machines, "SND_SOC_ACP_COMMON");
+
+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,
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_rmb_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[] = {
+ {
+ .id = "AMDI0052",
+ .drv_name = "acp63-acp",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp63_acp_machines, "SND_SOC_ACP_COMMON");
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[] = {
+ {
+ .id = "AMDI0029",
+ .drv_name = "acp70-acp",
+ },
+ {},
+};
+EXPORT_SYMBOL_NS_GPL(snd_soc_acpi_amd_acp70_acp_machines, "SND_SOC_ACP_COMMON");
+
+MODULE_DESCRIPTION("AMD ACP tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Venkataprasad.potturu@amd.com");
diff --git a/sound/soc/amd/acp/amd-sdw-acpi.c b/sound/soc/amd/acp/amd-sdw-acpi.c
new file mode 100644
index 000000000000..238b584887ee
--- /dev/null
+++ b/sound/soc/amd/acp/amd-sdw-acpi.c
@@ -0,0 +1,62 @@
+// 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) 2023 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+
+/*
+ * SDW AMD ACPI scan helper function
+ */
+
+#include <linux/acpi.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/fwnode.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw_amd.h>
+#include <linux/string.h>
+
+int amd_sdw_scan_controller(struct sdw_amd_acpi_info *info)
+{
+ struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle);
+ u32 sdw_bitmap = 0;
+ u8 count = 0;
+ int ret;
+
+ if (!adev)
+ return -EINVAL;
+
+ /* Found controller, find links supported */
+ ret = fwnode_property_read_u32_array(acpi_fwnode_handle(adev),
+ "mipi-sdw-manager-list", &sdw_bitmap, 1);
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-manager-list: %d\n", ret);
+ return -EINVAL;
+ }
+ count = hweight32(sdw_bitmap);
+ /* Check count is within bounds */
+ if (count > info->count) {
+ dev_err(&adev->dev, "Manager count %d exceeds max %d\n",
+ count, info->count);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ dev_dbg(&adev->dev, "No SoundWire Managers detected\n");
+ return -EINVAL;
+ }
+ dev_dbg(&adev->dev, "ACPI reports %d SoundWire Manager devices\n", count);
+ info->link_mask = sdw_bitmap;
+ return 0;
+}
+EXPORT_SYMBOL_NS(amd_sdw_scan_controller, "SND_AMD_SOUNDWIRE_ACPI");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("AMD SoundWire ACPI helpers");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
new file mode 100644
index 000000000000..863e74fcee43
--- /dev/null
+++ b/sound/soc/amd/acp/amd.h
@@ -0,0 +1,365 @@
+/* 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef __AMD_ACP_H
+#define __AMD_ACP_H
+
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+
+#include "acp_common.h"
+#include "chip_offset_byte.h"
+
+#define DMIC_INSTANCE 0x00
+#define I2S_SP_INSTANCE 0x01
+#define I2S_BT_INSTANCE 0x02
+#define I2S_HS_INSTANCE 0x03
+
+#define MEM_WINDOW_START 0x4080000
+
+#define ACP_I2S_REG_START 0x1242400
+#define ACP_I2S_REG_END 0x1242810
+#define ACP3x_I2STDM_REG_START 0x1242400
+#define ACP3x_I2STDM_REG_END 0x1242410
+#define ACP3x_BT_TDM_REG_START 0x1242800
+#define ACP3x_BT_TDM_REG_END 0x1242810
+
+#define THRESHOLD(bit, base) ((bit) + (base))
+#define I2S_RX_THRESHOLD(base) THRESHOLD(7, base)
+#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 ACP7x_I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define ACP7x_I2S_SP_RX_MEM_WINDOW_START 0x4200000
+#define ACP7x_I2S_BT_TX_MEM_WINDOW_START 0x4400000
+#define ACP7x_I2S_BT_RX_MEM_WINDOW_START 0x4600000
+#define ACP7x_I2S_HS_TX_MEM_WINDOW_START 0x4800000
+#define ACP7x_I2S_HS_RX_MEM_WINDOW_START 0x4A00000
+#define ACP7x_DMIC_MEM_WINDOW_START 0x4C00000
+
+#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
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER 65536
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+#define ACP_MAX_STREAM 8
+
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+
+#define SLOT_WIDTH_8 0x8
+#define SLOT_WIDTH_16 0x10
+#define SLOT_WIDTH_24 0x18
+#define SLOT_WIDTH_32 0x20
+
+#define ACP6X_PGFSM_CONTROL 0x1024
+#define ACP6X_PGFSM_STATUS 0x1028
+
+#define ACP63_PGFSM_CONTROL ACP6X_PGFSM_CONTROL
+#define ACP63_PGFSM_STATUS ACP6X_PGFSM_STATUS
+
+#define ACP70_PGFSM_CONTROL ACP6X_PGFSM_CONTROL
+#define ACP70_PGFSM_STATUS ACP6X_PGFSM_STATUS
+
+#define ACP_ZSC_DSP_CTRL 0x0001014
+#define ACP_ZSC_STS 0x0001018
+#define ACP_SOFT_RST_DONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0xffffffff
+#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
+
+#define ACP_TIMEOUT 500
+#define DELAY_US 5
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define PDM_DMA_STAT 0x10
+#define PDM_DMA_INTR_MASK 0x10000
+#define PDM_DEC_64 0x2
+#define PDM_CLK_FREQ_MASK 0x07
+#define PDM_MISC_CTRL_MASK 0x10
+#define PDM_ENABLE 0x01
+#define PDM_DISABLE 0x00
+#define DMA_EN_MASK 0x02
+#define DELAY_US 5
+#define PDM_TIMEOUT 1000
+#define ACP_REGION2_OFFSET 0x02000000
+
+struct acp_chip_info {
+ char *name; /* Platform name */
+ struct resource *res;
+ struct device *dev;
+ struct snd_soc_dai_driver *dai_driver;
+
+ unsigned int acp_rev; /* ACP Revision id */
+ void __iomem *base; /* ACP memory PCI base */
+ struct snd_acp_hw_ops *acp_hw_ops;
+ int (*acp_hw_ops_init)(struct acp_chip_info *chip);
+ struct platform_device *chip_pdev;
+ struct acp_resource *rsrc; /* Platform specific resources*/
+ struct list_head stream_list;
+ spinlock_t acp_lock; /* Used to protect stream_list */
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *acp_plat_dev;
+ struct platform_device *mach_dev;
+ struct snd_soc_acpi_mach *machines;
+ int num_dai;
+ u32 addr;
+ u32 bclk_div;
+ u32 lrclk_div;
+ u32 ch_mask;
+ u32 tdm_tx_fmt[3];
+ u32 tdm_rx_fmt[3];
+ u32 xfer_tx_resolution[3];
+ u32 xfer_rx_resolution[3];
+ unsigned int flag; /* Distinguish b/w Legacy or Only PDM */
+ bool is_pdm_dev; /* flag set to true when ACP PDM controller exists */
+ bool is_pdm_config; /* flag set to true when PDM configuration is selected from BIOS */
+ bool is_i2s_config; /* flag set to true when I2S configuration is selected from BIOS */
+ bool tdm_mode;
+};
+
+struct acp_stream {
+ struct list_head list;
+ struct snd_pcm_substream *substream;
+ int irq_bit;
+ int dai_id;
+ int id;
+ int dir;
+ u64 bytescount;
+ u32 reg_offset;
+ u32 pte_offset;
+ u32 fifo_offset;
+};
+
+struct acp_resource {
+ int offset;
+ int no_of_ctrls;
+ int irqp_used;
+ bool soc_mclk;
+ u32 irq_reg_offset;
+ u64 scratch_reg_offset;
+ u64 sram_pte_offset;
+};
+
+/**
+ * struct snd_acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @irq: ACP irq handler
+ * @en_interrupts: ACP enable interrupts
+ * @dis_interrupts: ACP disable interrupts
+ */
+struct snd_acp_hw_ops {
+ /* ACP hardware initilizations */
+ int (*acp_init)(struct acp_chip_info *chip);
+ int (*acp_deinit)(struct acp_chip_info *chip);
+
+ /* ACP Interrupts*/
+ irqreturn_t (*irq)(int irq, void *data);
+ int (*en_interrupts)(struct acp_chip_info *chip);
+ int (*dis_interrupts)(struct acp_chip_info *chip);
+};
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+extern struct acp_resource rn_rsrc;
+extern struct acp_resource rmb_rsrc;
+extern struct acp_resource acp63_rsrc;
+extern struct acp_resource acp70_rsrc;
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines;
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines;
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines;
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines;
+
+extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
+extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
+
+int acp_platform_register(struct device *dev);
+int acp_platform_unregister(struct device *dev);
+
+int acp_machine_select(struct acp_chip_info *chip);
+
+int acp_init(struct acp_chip_info *chip);
+int acp_deinit(struct acp_chip_info *chip);
+int acp_enable_interrupts(struct acp_chip_info *chip);
+int acp_disable_interrupts(struct acp_chip_info *chip);
+irqreturn_t acp_irq_handler(int irq, void *data);
+
+extern struct snd_acp_hw_ops acp31_common_hw_ops;
+extern struct snd_acp_hw_ops acp6x_common_hw_ops;
+extern struct snd_acp_hw_ops acp63_common_hw_ops;
+extern struct snd_acp_hw_ops acp70_common_hw_ops;
+extern int acp31_hw_ops_init(struct acp_chip_info *chip);
+extern int acp6x_hw_ops_init(struct acp_chip_info *chip);
+extern int acp63_hw_ops_init(struct acp_chip_info *chip);
+extern int acp70_hw_ops_init(struct acp_chip_info *chip);
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+void config_pte_for_stream(struct acp_chip_info *chip, struct acp_stream *stream);
+void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int size);
+void restore_acp_pdm_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip);
+
+int restore_acp_i2s_params(struct snd_pcm_substream *substream,
+ struct acp_chip_info *chip, struct acp_stream *stream);
+
+void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip);
+
+static inline int acp_hw_init(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_init)
+ return chip->acp_hw_ops->acp_init(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->acp_deinit)
+ return chip->acp_hw_ops->acp_deinit(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_en_interrupts(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->en_interrupts)
+ return chip->acp_hw_ops->en_interrupts(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_dis_interrupts(struct acp_chip_info *chip)
+{
+ if (chip && chip->acp_hw_ops && chip->acp_hw_ops->dis_interrupts)
+ chip->acp_hw_ops->dis_interrupts(chip);
+ return -EOPNOTSUPP;
+}
+
+static inline u64 acp_get_byte_count(struct acp_chip_info *chip, int dai_id, int direction)
+{
+ u64 byte_count = 0, low = 0, high = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_BT_TX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", dai_id);
+ goto POINTER_RETURN_BYTES;
+ }
+ } else {
+ switch (dai_id) {
+ case I2S_BT_INSTANCE:
+ high = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_BT_RX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_SP_INSTANCE:
+ high = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(chip));
+ low = readl(chip->base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(chip));
+ break;
+ case I2S_HS_INSTANCE:
+ high = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ case DMIC_INSTANCE:
+ high = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(chip->base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ break;
+ default:
+ dev_err(chip->dev, "Invalid dai id %x\n", dai_id);
+ goto POINTER_RETURN_BYTES;
+ }
+ }
+ /* Get 64 bit value from two 32 bit registers */
+ byte_count = (high << 32) | low;
+
+POINTER_RETURN_BYTES:
+ return byte_count;
+}
+#endif
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
new file mode 100644
index 000000000000..82275c9de53a
--- /dev/null
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -0,0 +1,146 @@
+/* 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+
+#ifndef _ACP_IP_OFFSET_HEADER
+#define _ACP_IP_OFFSET_HEADER
+
+#define ACPAXI2AXI_ATU_CTRL 0xC40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0xC00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0xC04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0xC08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0xC0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+
+#define GRP1_OFFSET 0x0
+#define GRP2_OFFSET 0x4000
+
+#define ACP_PGFSM_CONTROL 0x141C
+#define ACP_PGFSM_STATUS 0x1420
+#define ACP_SOFT_RESET 0x1000
+#define ACP_CONTROL 0x1004
+#define ACP_PIN_CONFIG 0x1440
+#define ACP3X_PIN_CONFIG 0x1400
+
+#define ACP_EXTERNAL_INTR_REG_ADDR(chip, offset, ctrl) \
+ (chip->base + chip->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
+
+#define ACP_EXTERNAL_INTR_ENB(chip) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x0, 0x0)
+#define ACP_EXTERNAL_INTR_CNTL(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, 0x4, ctrl)
+#define ACP_EXTERNAL_INTR_STAT(chip, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(chip, \
+ (0x4 + (chip->rsrc->no_of_ctrls * 0x04)), ctrl)
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+
+#define ACP_I2S_REG_ADDR(acp_adata, addr) \
+ ((addr) + (acp_adata->rsrc->irqp_used * \
+ acp_adata->rsrc->irq_reg_offset))
+
+#define ACP_I2S_RX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2000)
+#define ACP_I2S_RX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2004)
+#define ACP_I2S_RX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2008)
+#define ACP_I2S_RX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x200C)
+#define ACP_I2S_RX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2010)
+#define ACP_I2S_RX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2014)
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2018)
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x201C)
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2020)
+#define ACP_I2S_TX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2024)
+#define ACP_I2S_TX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2028)
+#define ACP_I2S_TX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x202C)
+#define ACP_I2S_TX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2030)
+#define ACP_I2S_TX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2034)
+#define ACP_I2S_TX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2038)
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x203C)
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2040)
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2044)
+#define ACP_BT_RX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2048)
+#define ACP_BT_RX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x204C)
+#define ACP_BT_RX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2050)
+#define ACP_BT_RX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2054)
+#define ACP_BT_RX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2058)
+#define ACP_BT_RX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x205C)
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2060)
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2064)
+#define ACP_BT_RX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2068)
+#define ACP_BT_TX_RINGBUFADDR(adata) ACP_I2S_REG_ADDR(adata, 0x206C)
+#define ACP_BT_TX_RINGBUFSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2070)
+#define ACP_BT_TX_LINKPOSITIONCNTR(adata) ACP_I2S_REG_ADDR(adata, 0x2074)
+#define ACP_BT_TX_FIFOADDR(adata) ACP_I2S_REG_ADDR(adata, 0x2078)
+#define ACP_BT_TX_FIFOSIZE(adata) ACP_I2S_REG_ADDR(adata, 0x207C)
+#define ACP_BT_TX_DMA_SIZE(adata) ACP_I2S_REG_ADDR(adata, 0x2080)
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH(adata) ACP_I2S_REG_ADDR(adata, 0x2084)
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW(adata) ACP_I2S_REG_ADDR(adata, 0x2088)
+#define ACP_BT_TX_INTR_WATERMARK_SIZE(adata) ACP_I2S_REG_ADDR(adata, 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
+#define ACP_I2STDM_RXFRMT 0x2408
+#define ACP_I2STDM_ITER 0x240C
+#define ACP_I2STDM_TXFRMT 0x2410
+
+/* Registers from ACP_BT_TDM block */
+
+#define ACP_BTTDM_IER 0x2800
+#define ACP_BTTDM_IRER 0x2804
+#define ACP_BTTDM_RXFRMT 0x2808
+#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
+#define ACP_WOV_PDM_DMA_ENABLE 0x2C08
+#define ACP_WOV_RX_RINGBUFADDR 0x2C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x2C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x2C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x2C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x2C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x2C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x2C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x2C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x2C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x2C30
+#define ACP_WOV_BUFFER_STATUS 0x2C58
+#define ACP_WOV_MISC_CTRL 0x2C5C
+#define ACP_WOV_CLK_CTRL 0x2C60
+#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
diff --git a/sound/soc/amd/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h
new file mode 100644
index 000000000000..1f24e0e06487
--- /dev/null
+++ b/sound/soc/amd/acp/soc_amd_sdw_common.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved
+ */
+
+/*
+ * soc_amd_sdw_common.h - prototypes for common helpers
+ */
+
+#ifndef SOC_AMD_SDW_COMMON_H
+#define SOC_AMD_SDW_COMMON_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+#include <sound/soc_sdw_utils.h>
+
+#define ACP63_SDW_MAX_CPU_DAIS 8
+#define ACP63_SDW_MAX_LINKS 2
+
+#define AMD_SDW_MAX_GROUPS 9
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
+#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
+#define ASOC_SDW_FOUR_SPK BIT(4)
+#define ASOC_SDW_ACP_DMIC BIT(5)
+#define ASOC_SDW_CODEC_SPKR BIT(15)
+
+#define AMD_SDW0 0
+#define AMD_SDW1 1
+#define ACP63_SW0_AUDIO0_TX 0
+#define ACP63_SW0_AUDIO1_TX 1
+#define ACP63_SW0_AUDIO2_TX 2
+
+#define ACP63_SW0_AUDIO0_RX 3
+#define ACP63_SW0_AUDIO1_RX 4
+#define ACP63_SW0_AUDIO2_RX 5
+
+#define ACP63_SW1_AUDIO0_TX 0
+#define ACP63_SW1_AUDIO0_RX 1
+
+#define ACP_DMIC_BE_ID 4
+
+#define ACP70_SW_AUDIO0_TX 0
+#define ACP70_SW_AUDIO1_TX 1
+#define ACP70_SW_AUDIO2_TX 2
+
+#define ACP70_SW_AUDIO0_RX 3
+#define ACP70_SW_AUDIO1_RX 4
+#define ACP70_SW_AUDIO2_RX 5
+
+struct amd_mc_ctx {
+ unsigned int acp_rev;
+ unsigned int max_sdw_links;
+};
+
+int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+
+#endif
diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c
index 406526e79af3..4ca1978020a9 100644
--- a/sound/soc/amd/acp3x-rt5682-max9836.c
+++ b/sound/soc/amd/acp3x-rt5682-max9836.c
@@ -28,6 +28,17 @@
#define DUAL_CHANNEL 2
static struct snd_soc_jack pco_jack;
+static struct snd_soc_jack_pin pco_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
static struct clk *rt5682_dai_wclk;
static struct clk *rt5682_dai_bclk;
static struct gpio_desc *dmic_sel;
@@ -43,7 +54,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
{
int ret;
struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct snd_soc_component *component = codec_dai->component;
dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
@@ -51,7 +62,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
/* set rt5682 dai fmt */
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM);
+ | SND_SOC_DAIFMT_CBP_CFP);
if (ret < 0) {
dev_err(rtd->card->dev,
"Failed to set rt5682 dai fmt: %d\n", ret);
@@ -86,11 +97,13 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
rt5682_dai_wclk = clk_get(component->dev, "rt5682-dai-wclk");
rt5682_dai_bclk = clk_get(component->dev, "rt5682-dai-bclk");
- ret = snd_soc_card_jack_new(card, "Headset Jack",
- SND_JACK_HEADSET | SND_JACK_LINEOUT |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3,
- &pco_jack, NULL, 0);
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &pco_jack,
+ pco_jack_pins,
+ ARRAY_SIZE(pco_jack_pins));
if (ret) {
dev_err(card->dev, "HP jack creation failed %d\n", ret);
return ret;
@@ -113,7 +126,7 @@ static int acp3x_5682_init(struct snd_soc_pcm_runtime *rtd)
static int rt5682_clk_enable(struct snd_pcm_substream *substream)
{
int ret = 0;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
/* RT5682 will support only 48K output with 48M mclk */
clk_set_rate(rt5682_dai_wclk, 48000);
@@ -130,7 +143,7 @@ static int rt5682_clk_enable(struct snd_pcm_substream *substream)
static int acp3x_1015_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai;
int srate, i, ret;
@@ -140,9 +153,7 @@ static int acp3x_1015_hw_params(struct snd_pcm_substream *substream,
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (strcmp(codec_dai->name, "rt1015-aif"))
continue;
- ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
- if (ret < 0)
- return ret;
+
ret = snd_soc_dai_set_pll(codec_dai, 0, RT1015_PLL_S_BCLK,
64 * srate, 256 * srate);
if (ret < 0)
@@ -183,7 +194,7 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = {
static int acp3x_5682_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -201,7 +212,7 @@ static int acp3x_5682_startup(struct snd_pcm_substream *substream)
static int acp3x_max_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
@@ -217,9 +228,9 @@ static int acp3x_max_startup(struct snd_pcm_substream *substream)
static int acp3x_ec_dmic0_startup(struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_card *card = rtd->card;
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct acp3x_platform_info *machine = snd_soc_card_get_drvdata(card);
machine->cap_i2s_instance = I2S_BT_INSTANCE;
@@ -277,6 +288,8 @@ SND_SOC_DAILINK_DEF(rt5682,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5682:00", "rt5682-aif1")));
SND_SOC_DAILINK_DEF(max,
DAILINK_COMP_ARRAY(COMP_CODEC("MX98357A:00", "HiFi")));
+SND_SOC_DAILINK_DEF(rt1015p,
+ DAILINK_COMP_ARRAY(COMP_CODEC("RTL1015:00", "HiFi")));
SND_SOC_DAILINK_DEF(rt1015,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1015:00", "rt1015-aif"),
COMP_CODEC("i2c-10EC1015:01", "rt1015-aif")));
@@ -302,10 +315,8 @@ static struct snd_soc_dai_link acp3x_dai[] = {
.name = "acp3x-5682-play",
.stream_name = "Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBM_CFM,
+ | SND_SOC_DAIFMT_CBP_CFP,
.init = acp3x_5682_init,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
.ops = &acp3x_5682_ops,
SND_SOC_DAILINK_REG(acp3x_i2s, rt5682, platform),
},
@@ -313,8 +324,8 @@ static struct snd_soc_dai_link acp3x_dai[] = {
.name = "acp3x-max98357-play",
.stream_name = "HiFi Playback",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_playback = 1,
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
.ops = &acp3x_max_play_ops,
.cpus = acp3x_bt,
.num_cpus = ARRAY_SIZE(acp3x_bt),
@@ -325,8 +336,8 @@ static struct snd_soc_dai_link acp3x_dai[] = {
.name = "acp3x-ec-dmic0-capture",
.stream_name = "Capture DMIC0",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .dpcm_capture = 1,
+ | SND_SOC_DAIFMT_CBC_CFC,
+ .capture_only = 1,
.ops = &acp3x_ec_cap0_ops,
SND_SOC_DAILINK_REG(acp3x_bt, cros_ec, platform),
},
@@ -421,6 +432,43 @@ static struct snd_soc_card acp3x_1015 = {
.num_controls = ARRAY_SIZE(acp3x_mc_1015_controls),
};
+static const struct snd_soc_dapm_widget acp3x_1015p_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MUX("Dmic Mux", SND_SOC_NOPM, 0, 0,
+ &acp3x_dmic_mux_control),
+ SND_SOC_DAPM_SPK("Speakers", NULL),
+};
+
+static const struct snd_soc_dapm_route acp3x_1015p_route[] = {
+ {"Headphone Jack", NULL, "HPOL"},
+ {"Headphone Jack", NULL, "HPOR"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"Dmic Mux", "Front Mic", "DMIC"},
+ {"Dmic Mux", "Rear Mic", "DMIC"},
+ /* speaker */
+ { "Speakers", NULL, "Speaker" },
+};
+
+static const struct snd_kcontrol_new acp3x_mc_1015p_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static struct snd_soc_card acp3x_1015p = {
+ .name = "acp3xalc56821015p",
+ .owner = THIS_MODULE,
+ .dai_link = acp3x_dai,
+ .num_links = ARRAY_SIZE(acp3x_dai),
+ .dapm_widgets = acp3x_1015p_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp3x_1015p_widgets),
+ .dapm_routes = acp3x_1015p_route,
+ .num_dapm_routes = ARRAY_SIZE(acp3x_1015p_route),
+ .controls = acp3x_mc_1015p_controls,
+ .num_controls = ARRAY_SIZE(acp3x_mc_1015p_controls),
+};
+
void *soc_is_rltk_max(struct device *dev)
{
const struct acpi_device_id *match;
@@ -437,6 +485,9 @@ static void card_spk_dai_link_present(struct snd_soc_dai_link *links,
if (!strcmp(card_name, "acp3xalc56821015")) {
links[1].codecs = rt1015;
links[1].num_codecs = ARRAY_SIZE(rt1015);
+ } else if (!strcmp(card_name, "acp3xalc56821015p")) {
+ links[1].codecs = rt1015p;
+ links[1].num_codecs = ARRAY_SIZE(rt1015p);
} else {
links[1].codecs = max;
links[1].num_codecs = ARRAY_SIZE(max);
@@ -472,10 +523,9 @@ static int acp3x_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
- dev_err(&pdev->dev,
- "devm_snd_soc_register_card(%s) failed: %d\n",
- card->name, ret);
- return ret;
+ return dev_err_probe(&pdev->dev, ret,
+ "devm_snd_soc_register_card(%s) failed\n",
+ card->name);
}
return 0;
}
@@ -483,6 +533,7 @@ static int acp3x_probe(struct platform_device *pdev)
static const struct acpi_device_id acp3x_audio_acpi_match[] = {
{ "AMDI5682", (unsigned long)&acp3x_5682},
{ "AMDI1015", (unsigned long)&acp3x_1015},
+ { "10021015", (unsigned long)&acp3x_1015p},
{},
};
MODULE_DEVICE_TABLE(acpi, acp3x_audio_acpi_match);
@@ -500,5 +551,6 @@ module_platform_driver(acp3x_audio);
MODULE_AUTHOR("akshu.agrawal@amd.com");
MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
-MODULE_DESCRIPTION("ALC5682 ALC1015 & MAX98357 audio support");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("ALC5682 ALC1015, ALC1015P & MAX98357 audio support");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
new file mode 100644
index 000000000000..fdf016a64bbf
--- /dev/null
+++ b/sound/soc/amd/mach-config.h
@@ -0,0 +1,37 @@
+/* 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) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+ */
+#ifndef __AMD_MACH_CONFIG_H
+#define __AMD_MACH_CONFIG_H
+
+#include <sound/soc-acpi.h>
+
+#define FLAG_AMD_SOF BIT(1)
+#define FLAG_AMD_SOF_ONLY_DMIC BIT(2)
+#define FLAG_AMD_LEGACY BIT(3)
+#define FLAG_AMD_LEGACY_ONLY_DMIC BIT(4)
+
+#define ACP_PCI_DEV_ID 0x15E2
+
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_vangogh_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
+
+struct config_entry {
+ u32 flags;
+ u16 device;
+ const struct dmi_system_id *dmi_table;
+};
+
+#endif
diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
new file mode 100644
index 000000000000..778ee4726389
--- /dev/null
+++ b/sound/soc/amd/ps/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Pink Sardine platform Support
+snd-pci-ps-y := pci-ps.o ps-common.o
+snd-ps-pdm-dma-y := ps-pdm-dma.o
+snd-soc-ps-mach-y := ps-mach.o
+snd-ps-sdw-dma-y := ps-sdw-dma.o
+
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-sdw-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS_MACH) += snd-soc-ps-mach.o
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
new file mode 100644
index 000000000000..85feae45c44c
--- /dev/null
+++ b/sound/soc/amd/ps/acp63.h
@@ -0,0 +1,441 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD Common ACP header file for ACP6.3, ACP7.0 & ACP7.1 platforms
+ *
+ * Copyright (C) 2022, 2023, 2025 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include <linux/soundwire/sdw_amd.h>
+#include <sound/acp63_chip_offset_byte.h>
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP63_REG_START 0x1240000
+#define ACP63_REG_END 0x125C000
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP63_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP63_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP63_PGFSM_STATUS_MASK 3
+#define ACP63_POWERED_ON 0
+#define ACP63_POWER_ON_IN_PROGRESS 1
+#define ACP63_POWERED_OFF 2
+#define ACP63_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+#define ACP_DMIC_DEV 2
+
+#define ACP63_DMIC_ADDR 2
+#define ACP63_SDW_ADDR 5
+#define AMD_SDW_MAX_MANAGERS 2
+
+/* time in ms for acp timeout */
+#define ACP63_TIMEOUT 500
+
+#define ACP_SDW0_STAT BIT(21)
+#define ACP_SDW1_STAT BIT(2)
+#define ACP_ERROR_IRQ BIT(29)
+
+#define ACP_AUDIO0_TX_THRESHOLD 0x1c
+#define ACP_AUDIO1_TX_THRESHOLD 0x1a
+#define ACP_AUDIO2_TX_THRESHOLD 0x18
+#define ACP_AUDIO0_RX_THRESHOLD 0x1b
+#define ACP_AUDIO1_RX_THRESHOLD 0x19
+#define ACP_AUDIO2_RX_THRESHOLD 0x17
+#define ACP63_P1_AUDIO1_TX_THRESHOLD BIT(6)
+#define ACP63_P1_AUDIO1_RX_THRESHOLD BIT(5)
+#define ACP63_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP63_P1_SDW_DMA_IRQ_MASK 0x60
+#define ACP63_SDW0_DMA_MAX_STREAMS 6
+#define ACP63_SDW1_DMA_MAX_STREAMS 2
+#define ACP63_P1_AUDIO_TX_THRESHOLD 6
+
+/*
+ * Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL register.
+ * Stream id IRQ Bit
+ * 0 (SDW0_AUDIO0_TX) 28
+ * 1 (SDW0_AUDIO1_TX) 26
+ * 2 (SDW0_AUDIO2_TX) 24
+ * 3 (SDW0_AUDIO0_RX) 27
+ * 4 (SDW0_AUDIO1_RX) 25
+ * 5 (SDW0_AUDIO2_RX) 23
+ */
+#define ACP63_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP63_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register.
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO1_TX) 6
+ * 1 (SDW1_AUDIO1_RX) 5
+ */
+#define ACP63_SDW1_DMA_IRQ_MASK(i) (ACP63_P1_AUDIO_TX_THRESHOLD - (i))
+
+#define ACP_DELAY_US 5
+#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
+#define SDW0_MEM_WINDOW_START 0x4800000
+#define ACP_SDW_SRAM_PTE_OFFSET 0x03800400
+#define SDW0_PTE_OFFSET 0x400
+#define SDW_FIFO_SIZE 0x100
+#define SDW_DMA_SIZE 0x40
+#define ACP_SDW0_FIFO_OFFSET 0x100
+#define ACP_SDW_PTE_OFFSET 0x100
+#define SDW_FIFO_OFFSET 0x100
+#define SDW_PTE_OFFSET(i) (SDW0_PTE_OFFSET + ((i) * 0x600))
+#define ACP_SDW_FIFO_OFFSET(i) (ACP_SDW0_FIFO_OFFSET + ((i) * 0x500))
+#define SDW_MEM_WINDOW_START(i) (SDW0_MEM_WINDOW_START + ((i) * 0xC0000))
+
+#define SDW_PLAYBACK_MIN_NUM_PERIODS 2
+#define SDW_PLAYBACK_MAX_NUM_PERIODS 8
+#define SDW_PLAYBACK_MAX_PERIOD_SIZE 8192
+#define SDW_PLAYBACK_MIN_PERIOD_SIZE 1024
+#define SDW_CAPTURE_MIN_NUM_PERIODS 2
+#define SDW_CAPTURE_MAX_NUM_PERIODS 8
+#define SDW_CAPTURE_MAX_PERIOD_SIZE 8192
+#define SDW_CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
+#define SDW_MIN_BUFFER SDW_MAX_BUFFER
+
+#define ACP_HW_OPS(acp_data, cb) ((acp_data)->hw_ops->cb)
+
+#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F
+#define ACP70_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP70_PGFSM_STATUS_MASK 0xFF
+#define ACP70_TIMEOUT 2000
+#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
+#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
+#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
+#define ACP70_SDW0_PME_STAT BIT(26)
+#define ACP70_SDW1_PME_STAT BIT(27)
+
+#define ACP70_SDW0_DMA_MAX_STREAMS 6
+#define ACP70_SDW1_DMA_MAX_STREAMS ACP70_SDW0_DMA_MAX_STREAMS
+#define ACP70_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP70_P1_SDW_DMA_IRQ_MASK 0x1F8
+
+#define ACP70_P1_AUDIO0_TX_THRESHOLD 0x8
+#define ACP70_P1_AUDIO1_TX_THRESHOLD 0x6
+#define ACP70_P1_AUDIO2_TX_THRESHOLD 0x4
+#define ACP70_P1_AUDIO0_RX_THRESHOLD 0x7
+#define ACP70_P1_AUDIO1_RX_THRESHOLD 0x5
+#define ACP70_P1_AUDIO2_RX_THRESHOLD 0x3
+
+#define ACP70_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register for ACP70/ACP71 platforms
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO0_TX) 8
+ * 1 (SDW1_AUDIO1_TX) 6
+ * 2 (SDW1_AUDIO2_TX) 4
+ * 3 (SDW1_AUDIO0_RX) 7
+ * 4 (SDW1_AUDIO1_RX) 5
+ * 5 (SDW1_AUDIO2_RX) 3
+ */
+#define ACP70_SDW1_DMA_TX_IRQ_MASK(i) (ACP70_P1_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW1_DMA_RX_IRQ_MASK(i) (ACP70_P1_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+#define ACP70_SW0_AUDIO0_TX_EN ACP_SW0_AUDIO0_TX_EN
+#define ACP70_SW0_AUDIO1_TX_EN ACP_SW0_AUDIO1_TX_EN
+#define ACP70_SW0_AUDIO2_TX_EN ACP_SW0_AUDIO2_TX_EN
+#define ACP70_SW0_AUDIO0_RX_EN ACP_SW0_AUDIO0_RX_EN
+#define ACP70_SW0_AUDIO1_RX_EN ACP_SW0_AUDIO1_RX_EN
+#define ACP70_SW0_AUDIO2_RX_EN ACP_SW0_AUDIO2_RX_EN
+
+#define ACP70_SW1_AUDIO0_TX_EN 0x0003C10
+#define ACP70_SW1_AUDIO1_TX_EN 0x0003C50
+#define ACP70_SW1_AUDIO2_TX_EN 0x0003C6C
+#define ACP70_SW1_AUDIO0_RX_EN 0x0003C88
+#define ACP70_SW1_AUDIO1_RX_EN 0x0003D28
+#define ACP70_SW1_AUDIO2_RX_EN 0x0003D44
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+enum amd_acp63_sdw0_channel {
+ ACP63_SDW0_AUDIO0_TX = 0,
+ ACP63_SDW0_AUDIO1_TX,
+ ACP63_SDW0_AUDIO2_TX,
+ ACP63_SDW0_AUDIO0_RX,
+ ACP63_SDW0_AUDIO1_RX,
+ ACP63_SDW0_AUDIO2_RX,
+};
+
+enum amd_acp63_sdw1_channel {
+ ACP63_SDW1_AUDIO1_TX,
+ ACP63_SDW1_AUDIO1_RX,
+};
+
+enum amd_acp70_sdw_channel {
+ ACP70_SDW_AUDIO0_TX = 0,
+ ACP70_SDW_AUDIO1_TX,
+ ACP70_SDW_AUDIO2_TX,
+ ACP70_SDW_AUDIO0_RX,
+ ACP70_SDW_AUDIO1_RX,
+ ACP70_SDW_AUDIO2_RX,
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp63_base;
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp63_base;
+ struct mutex *acp_lock;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct sdw_dma_dev_data {
+ void __iomem *acp_base;
+ struct mutex *acp_lock; /* used to protect acp common register access */
+ u32 acp_rev;
+ struct snd_pcm_substream *acp63_sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp63_sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw0_dma_stream[ACP70_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw1_dma_stream[ACP70_SDW1_DMA_MAX_STREAMS];
+};
+
+struct acp_sdw_dma_stream {
+ u16 num_pages;
+ u16 channels;
+ u32 stream_id;
+ u32 instance;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+};
+
+union acp_sdw_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+struct sdw_dma_ring_buf_reg {
+ u32 reg_dma_size;
+ u32 reg_fifo_addr;
+ u32 reg_fifo_size;
+ u32 reg_ring_buf_size;
+ u32 reg_ring_buf_addr;
+ u32 water_mark_size_reg;
+ u32 pos_low_reg;
+ u32 pos_high_reg;
+};
+
+struct acp63_dev_data;
+
+/**
+ * struct acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @acp_get_config: function to read the acp pin configuration
+ * @acp_sdw_dma_irq_thread: ACP SoundWire DMA interrupt thread
+ * acp_suspend: ACP system level suspend callback
+ * acp_resume: ACP system level resume callback
+ * acp_suspend_runtime: ACP runtime suspend callback
+ * acp_resume_runtime: ACP runtime resume callback
+ */
+struct acp_hw_ops {
+ int (*acp_init)(void __iomem *acp_base, struct device *dev);
+ int (*acp_deinit)(void __iomem *acp_base, struct device *dev);
+ void (*acp_get_config)(struct pci_dev *pci, struct acp63_dev_data *acp_data);
+ void (*acp_sdw_dma_irq_thread)(struct acp63_dev_data *acp_data);
+ int (*acp_suspend)(struct device *dev);
+ int (*acp_resume)(struct device *dev);
+ int (*acp_suspend_runtime)(struct device *dev);
+ int (*acp_resume_runtime)(struct device *dev);
+};
+
+/**
+ * struct acp63_dev_data - acp pci driver context
+ * @acp63_base: acp mmio base
+ * @res: resource
+ * @hw_ops: ACP pci driver platform-specific ops
+ * @pdm_dev: ACP PDM controller platform device
+ * @dmic_codec: platform device for DMIC Codec
+ * sdw_dma_dev: platform device for SoundWire DMA controller
+ * @mach_dev: platform device for machine driver to support ACP PDM/SoundWire configuration
+ * @acp_lock: used to protect acp common registers
+ * @info: SoundWire AMD information found in ACPI tables
+ * @sdw: SoundWire context for all SoundWire manager instances
+ * @machine: ACPI machines for SoundWire interface
+ * @is_sdw_dev: flag set to true when any SoundWire manager instances are available
+ * @is_pdm_dev: flag set to true when ACP PDM controller exists
+ * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
+ * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
+ * @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled
+ * @acp70_sdw0_wake_event: flag set to true when wake irq asserted for SW0 instance
+ * @acp70_sdw1_wake_event: flag set to true when wake irq asserted for SW1 instance
+ * @addr: pci ioremap address
+ * @reg_range: ACP reigister range
+ * @acp_rev: ACP PCI revision id
+ * @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW0 instance
+ * @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW1 instance
+ * @acp70_sdw0-dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW0 instance
+ * @acp70_sdw_dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW1 instance
+ */
+
+struct acp63_dev_data {
+ void __iomem *acp63_base;
+ struct resource *res;
+ struct acp_hw_ops *hw_ops;
+ struct platform_device *pdm_dev;
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *sdw_dma_dev;
+ struct platform_device *mach_dev;
+ struct mutex acp_lock; /* protect shared registers */
+ struct sdw_amd_acpi_info info;
+ /* sdw context allocated by SoundWire driver */
+ struct sdw_amd_ctx *sdw;
+ struct snd_soc_acpi_mach *machines;
+ bool is_sdw_dev;
+ bool is_pdm_dev;
+ bool is_pdm_config;
+ bool is_sdw_config;
+ bool sdw_en_stat;
+ bool acp70_sdw0_wake_event;
+ bool acp70_sdw1_wake_event;
+ u32 addr;
+ u32 reg_range;
+ u32 acp_rev;
+ u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
+ u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
+ u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS];
+ u16 acp70_sdw1_dma_intr_stat[ACP70_SDW1_DMA_MAX_STREAMS];
+};
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops);
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops);
+
+static inline int acp_hw_init(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_init)
+ return ACP_HW_OPS(adata, acp_init)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_deinit)
+ return ACP_HW_OPS(adata, acp_deinit)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_hw_get_config(struct pci_dev *pci, struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_get_config)
+ ACP_HW_OPS(adata, acp_get_config)(pci, adata);
+}
+
+static inline void acp_hw_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_sdw_dma_irq_thread)
+ ACP_HW_OPS(adata, acp_sdw_dma_irq_thread)(adata);
+}
+
+static inline int acp_hw_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend)
+ return ACP_HW_OPS(adata, acp_suspend)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume)
+ return ACP_HW_OPS(adata, acp_resume)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_suspend_runtime(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime)
+ return ACP_HW_OPS(adata, acp_suspend_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime)
+ return ACP_HW_OPS(adata, acp_resume_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
new file mode 100644
index 000000000000..7936b3173632
--- /dev/null
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
+ *
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <linux/soundwire/sdw_amd.h>
+#include "../mach-config.h"
+
+#include "acp63.h"
+
+static void handle_acp70_sdw_wake_event(struct acp63_dev_data *adata)
+{
+ struct amd_sdw_manager *amd_manager;
+
+ if (adata->acp70_sdw0_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw0_wake_event = 0;
+ }
+
+ if (adata->acp70_sdw1_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw1_wake_event = 0;
+ }
+}
+
+static short int check_and_handle_acp70_sdw_wake_irq(struct acp63_dev_data *adata)
+{
+ u32 ext_intr_stat1;
+ int irq_flag = 0;
+ bool sdw_wake_irq = false;
+
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
+ writel(ACP70_SDW0_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
+ writel(ACP70_SDW1_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW0_WAKE_EN);
+ writel(ACP70_SDW0_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW1_WAKE_EN);
+ writel(ACP70_SDW1_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (sdw_wake_irq) {
+ handle_acp70_sdw_wake_event(adata);
+ irq_flag = 1;
+ }
+ return irq_flag;
+}
+
+static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 ext_intr_stat,
+ u32 ext_intr_stat1)
+{
+ u32 stream_id = 0;
+ u16 sdw_dma_irq_flag = 0;
+ u16 index;
+
+ if (ext_intr_stat & ACP63_SDW_DMA_IRQ_MASK) {
+ for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat & BIT(index)) {
+ writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ switch (index) {
+ case ACP_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_TX;
+ break;
+ case ACP_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_TX;
+ break;
+ case ACP_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_TX;
+ break;
+ case ACP_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_RX;
+ break;
+ case ACP_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_RX;
+ break;
+ case ACP_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_RX;
+ break;
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ }
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_RX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_RX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_RX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_TX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_TX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_TX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) {
+ for (index = ACP70_P1_AUDIO2_RX_THRESHOLD;
+ index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat1 & BIT(index)) {
+ writel(BIT(index),
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ switch (index) {
+ case ACP70_P1_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_TX;
+ break;
+ case ACP70_P1_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_TX;
+ break;
+ case ACP70_P1_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_TX;
+ break;
+ case ACP70_P1_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_RX;
+ break;
+ case ACP70_P1_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_RX;
+ break;
+ case ACP70_P1_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_RX;
+ break;
+ }
+
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ break;
+ }
+ return sdw_dma_irq_flag;
+}
+
+static irqreturn_t acp63_irq_thread(int irq, void *context)
+{
+ struct acp63_dev_data *adata = context;
+
+ acp_hw_sdw_dma_irq_thread(adata);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
+{
+ struct acp63_dev_data *adata;
+ struct pdm_dev_data *ps_pdm_data;
+ struct amd_sdw_manager *amd_manager;
+ u32 ext_intr_stat, ext_intr_stat1;
+ u16 irq_flag = 0;
+ u16 wake_irq_flag = 0;
+ u16 sdw_dma_irq_flag = 0;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+ /* ACP interrupts will be cleared by reading particular bit and writing
+ * same value to the status register. writing zero's doesn't have any
+ * effect.
+ * Bit by bit checking of IRQ field is implemented.
+ */
+ ext_intr_stat = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (ext_intr_stat & ACP_SDW0_STAT) {
+ writel(ACP_SDW0_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP_SDW1_STAT) {
+ writel(ACP_SDW1_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ if (ext_intr_stat & ACP_ERROR_IRQ) {
+ writel(ACP_ERROR_IRQ, adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ /* TODO: Report SoundWire Manager instance errors */
+ writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
+ writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
+ writel(0, adata->acp63_base + ACP_ERROR_STATUS);
+ irq_flag = 1;
+ }
+
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ wake_irq_flag = check_and_handle_acp70_sdw_wake_irq(adata);
+
+ if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
+ ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev);
+ writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ if (ps_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
+ irq_flag = 1;
+ }
+
+ sdw_dma_irq_flag = check_and_handle_sdw_dma_irq(adata, ext_intr_stat, ext_intr_stat1);
+ if (sdw_dma_irq_flag)
+ return IRQ_WAKE_THREAD;
+
+ if (irq_flag | wake_irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE)
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ struct acpi_device *sdw_dev;
+ struct acp63_dev_data *acp_data;
+
+ acp_data = dev_get_drvdata(dev);
+ if (!addr)
+ return -ENODEV;
+
+ sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0);
+ if (!sdw_dev)
+ return -ENODEV;
+
+ acp_data->info.handle = sdw_dev->handle;
+ acp_data->info.count = AMD_SDW_MAX_MANAGERS;
+ return amd_sdw_scan_controller(&acp_data->info);
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ struct acp63_dev_data *acp_data;
+ struct sdw_amd_res sdw_res;
+ int ret;
+
+ acp_data = dev_get_drvdata(dev);
+ memset(&sdw_res, 0, sizeof(sdw_res));
+ sdw_res.addr = acp_data->addr;
+ sdw_res.reg_range = acp_data->reg_range;
+ sdw_res.handle = acp_data->info.handle;
+ sdw_res.parent = dev;
+ sdw_res.dev = dev;
+ sdw_res.acp_lock = &acp_data->acp_lock;
+ sdw_res.count = acp_data->info.count;
+ sdw_res.mmio_base = acp_data->acp63_base;
+ sdw_res.acp_rev = acp_data->acp_rev;
+ sdw_res.link_mask = acp_data->info.link_mask;
+ ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+ if (ret)
+ dev_err(dev, "error: SoundWire probe failed\n");
+ return ret;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+ if (acp_data->sdw)
+ sdw_amd_exit(acp_data->sdw);
+ acp_data->sdw = NULL;
+
+ return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+ struct snd_soc_acpi_mach *mach;
+ const struct snd_soc_acpi_link_adr *link;
+ struct acp63_dev_data *acp_data = dev_get_drvdata(dev);
+ int ret, i;
+
+ if (acp_data->info.count) {
+ ret = sdw_amd_get_slave_info(acp_data->sdw);
+ if (ret) {
+ dev_dbg(dev, "failed to read slave information\n");
+ return NULL;
+ }
+ for (mach = acp_data->machines; mach; mach++) {
+ if (!mach->links)
+ break;
+ link = mach->links;
+ for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
+ if (!snd_soc_acpi_sdw_link_slaves_found(dev, link,
+ acp_data->sdw->peripherals))
+ break;
+ }
+ if (i == acp_data->info.count || !link->num_adr)
+ break;
+ }
+ if (mach && mach->link_mask) {
+ mach->mach_params.links = mach->links;
+ mach->mach_params.link_mask = mach->link_mask;
+ mach->mach_params.subsystem_rev = acp_data->acp_rev;
+ return mach;
+ }
+ }
+ dev_dbg(dev, "No SoundWire machine driver found\n");
+ return NULL;
+}
+#else
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ return 0;
+}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int amd_sdw_exit(struct acp63_dev_data *acp_data)
+{
+ return 0;
+}
+
+static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static int acp63_machine_register(struct device *dev)
+{
+ struct snd_soc_acpi_mach *mach;
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+ int size;
+
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ size = sizeof(*adata->machines);
+ mach = acp63_sdw_machine_select(dev);
+ if (mach) {
+ adata->mach_dev = platform_device_register_data(dev, mach->drv_name,
+ PLATFORM_DEVID_NONE, mach,
+ size);
+ if (IS_ERR(adata->mach_dev)) {
+ dev_err(dev,
+ "cannot register Machine device for SoundWire Interface\n");
+ return PTR_ERR(adata->mach_dev);
+ }
+ }
+
+ } else if (adata->is_pdm_dev && !adata->is_sdw_dev && adata->is_pdm_config) {
+ adata->mach_dev = platform_device_register_data(dev, "acp_ps_mach",
+ PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(adata->mach_dev)) {
+ dev_err(dev, "cannot register amd_ps_mach device\n");
+ return PTR_ERR(adata->mach_dev);
+ }
+ }
+ return 0;
+}
+
+static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ bool is_dmic_dev = false;
+ bool is_sdw_dev = false;
+ bool wov_en, dmic_en;
+ int ret;
+
+ /* IF WOV entry not found, enable dmic based on acp-audio-device-type entry*/
+ wov_en = true;
+ dmic_en = false;
+
+ acp_hw_get_config(pci, acp_data);
+
+ if (acp_data->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+ if (pdm_dev) {
+ /* is_dmic_dev flag will be set when ACP PDM controller device exists */
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ dmic_en = true;
+ }
+
+ handle = ACPI_HANDLE(&pci->dev);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret))
+ wov_en = dmic_status;
+ }
+
+ if (dmic_en && wov_en)
+ is_dmic_dev = true;
+
+ if (acp_data->is_sdw_config) {
+ ret = acp_scan_sdw_devices(&pci->dev, ACP63_SDW_ADDR);
+ if (!ret && acp_data->info.link_mask)
+ is_sdw_dev = true;
+ }
+
+ acp_data->is_pdm_dev = is_dmic_dev;
+ acp_data->is_sdw_dev = is_sdw_dev;
+ if (!is_dmic_dev && !is_sdw_dev) {
+ dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
+{
+ struct platform_device_info pdevinfo;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (adata->is_sdw_dev || adata->is_pdm_dev) {
+ adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ if (adata->is_pdm_dev && adata->is_pdm_config) {
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->pdm_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->pdm_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->pdm_dev);
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ adata->dmic_codec_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->dmic_codec_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->dmic_codec_dev);
+ goto unregister_pdm_dev;
+ }
+ }
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ ret = amd_sdw_probe(&pci->dev);
+ if (ret) {
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp63_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_ps_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->sdw_dma_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->sdw_dma_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->sdw_dma_dev);
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ }
+
+ return 0;
+unregister_dmic_codec_dev:
+ platform_device_unregister(adata->dmic_codec_dev);
+unregister_pdm_dev:
+ platform_device_unregister(adata->pdm_dev);
+de_init:
+ if (acp_hw_deinit(adata, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci)
+{
+ adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops),
+ GFP_KERNEL);
+ if (!adata->hw_ops)
+ return -ENOMEM;
+
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ acp63_hw_init_ops(adata->hw_ops);
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ acp70_hw_init_ops(adata->hw_ops);
+ break;
+ default:
+ dev_err(&pci->dev, "ACP device not found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int snd_acp63_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp63_dev_data *adata;
+ u32 addr;
+ u32 irqflags, flag;
+ int ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* ACP PCI revision id check for ACP6.3, ACP7.0 & ACP7.1 platforms */
+ switch (pci->revision) {
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp63/acp70/acp71 pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP6.2 audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp63_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp63_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp63_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ adata->addr = addr;
+ adata->reg_range = ACP63_REG_END - ACP63_REG_START;
+ adata->acp_rev = pci->revision;
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ mutex_init(&adata->acp_lock);
+ ret = acp_hw_init_ops(adata, pci);
+ if (ret) {
+ dev_err(&pci->dev, "ACP hw ops init failed\n");
+ goto release_regions;
+ }
+ ret = acp_hw_init(adata, &pci->dev);
+ if (ret)
+ goto release_regions;
+ ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
+ acp63_irq_thread, irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto de_init;
+ }
+ ret = get_acp63_device_config(pci, adata);
+ /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
+ if (ret) {
+ dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
+ goto skip_pdev_creation;
+ }
+ ret = create_acp63_platform_devs(pci, adata, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ adata->machines = snd_soc_acpi_amd_acp70_sdw_machines;
+ else
+ adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
+
+ ret = acp63_machine_register(&pci->dev);
+ if (ret) {
+ dev_err(&pci->dev, "ACP machine register failed\n");
+ goto de_init;
+ }
+skip_pdev_creation:
+ device_set_wakeup_enable(&pci->dev, true);
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+de_init:
+ if (acp_hw_deinit(adata, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp_suspend(struct device *dev)
+{
+ return acp_hw_suspend(dev);
+}
+
+static int snd_acp_runtime_resume(struct device *dev)
+{
+ return acp_hw_runtime_resume(dev);
+}
+
+static int snd_acp_resume(struct device *dev)
+{
+ return acp_hw_resume(dev);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
+};
+
+static void snd_acp63_remove(struct pci_dev *pci)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->sdw) {
+ amd_sdw_exit(adata);
+ platform_device_unregister(adata->sdw_dma_dev);
+ }
+ if (adata->is_pdm_dev) {
+ platform_device_unregister(adata->pdm_dev);
+ platform_device_unregister(adata->dmic_codec_dev);
+ }
+ if (adata->mach_dev)
+ platform_device_unregister(adata->mach_dev);
+ ret = acp_hw_deinit(adata, &pci->dev);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp63_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp63_ids);
+
+static struct pci_driver ps_acp63_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp63_ids,
+ .probe = snd_acp63_probe,
+ .remove = snd_acp63_remove,
+ .driver = {
+ .pm = pm_ptr(&acp63_pm_ops),
+ }
+};
+
+module_pci_driver(ps_acp63_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
+MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
+MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c
new file mode 100644
index 000000000000..1c89fb5fe1da
--- /dev/null
+++ b/sound/soc/amd/ps/ps-common.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ACP PCI driver callback routines for ACP6.3, ACP7.0 & ACP7.1
+ * platforms.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ * Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+
+#include "acp63.h"
+
+static int acp63_power_on(void __iomem *acp_base)
+{
+ u32 val;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP63_PGFSM_STATUS_MASK) != ACP63_POWER_ON_IN_PROGRESS)
+ writel(ACP63_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static int acp63_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP63_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static void acp63_enable_interrupts(void __iomem *acp_base)
+{
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp63_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp63_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp63_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ acp63_enable_interrupts(acp_base);
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static int acp63_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp63_disable_interrupts(acp_base);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_CONTROL);
+ writel(1, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp63_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
+{
+ u32 sdw0_en, sdw1_en;
+
+ sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
+ sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
+ return (sdw0_en || sdw1_en);
+}
+
+static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
+{
+ u32 val;
+
+ val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
+ if (val && adata->sdw->pdev[0])
+ pm_request_resume(&adata->sdw->pdev[0]->dev);
+
+ val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
+ if (val && adata->sdw->pdev[1])
+ pm_request_resume(&adata->sdw->pdev[1]->dev);
+}
+
+static int __maybe_unused snd_acp63_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->is_sdw_dev) {
+ adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+ if (adata->sdw_en_stat) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+
+ if (!adata->sdw_en_stat)
+ handle_acp63_sdw_pme_event(adata);
+ return 0;
+}
+
+static int __maybe_unused snd_acp63_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ return ret;
+}
+
+static void acp63_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP63_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw0_dma_stream[stream_id]);
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP63_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw1_dma_stream[stream_id]);
+ adata->acp63_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp63_init;
+ hw_ops->acp_deinit = acp63_deinit;
+ hw_ops->acp_get_config = acp63_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp63_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp63_suspend;
+ hw_ops->acp_resume = snd_acp63_resume;
+ hw_ops->acp_suspend_runtime = snd_acp63_suspend;
+ hw_ops->acp_resume_runtime = snd_acp63_runtime_resume;
+}
+
+static int acp70_power_on(void __iomem *acp_base)
+{
+ u32 val = 0;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return 0;
+ if (val & ACP70_PGFSM_STATUS_MASK)
+ writel(ACP70_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static int acp70_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP70_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static void acp70_enable_sdw_host_wake_interrupts(void __iomem *acp_base)
+{
+ u32 ext_intr_cntl1;
+
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= ACP70_SDW_HOST_WAKE_MASK;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+}
+
+static void acp70_enable_interrupts(void __iomem *acp_base)
+{
+ u32 sdw0_wake_en, sdw1_wake_en;
+
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ sdw0_wake_en = readl(acp_base + ACP_SW0_WAKE_EN);
+ sdw1_wake_en = readl(acp_base + ACP_SW1_WAKE_EN);
+ if (sdw0_wake_en || sdw1_wake_en)
+ acp70_enable_sdw_host_wake_interrupts(acp_base);
+}
+
+static void acp70_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp70_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp70_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ acp70_enable_interrupts(acp_base);
+ writel(0x1, acp_base + ACP_PME_EN);
+ return 0;
+}
+
+static int acp70_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp70_disable_interrupts(acp_base);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp70_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_20:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_16:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ case ACP_CONFIG_19:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void acp70_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP70_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw0_dma_stream[stream_id]);
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP70_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw1_dma_stream[stream_id]);
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+static int __maybe_unused snd_acp70_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->is_sdw_dev) {
+ adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+ if (adata->sdw_en_stat) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp70_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int __maybe_unused snd_acp70_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ return ret;
+}
+
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp70_init;
+ hw_ops->acp_deinit = acp70_deinit;
+ hw_ops->acp_get_config = acp70_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp70_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp70_suspend;
+ hw_ops->acp_resume = snd_acp70_resume;
+ hw_ops->acp_suspend_runtime = snd_acp70_suspend;
+ hw_ops->acp_resume_runtime = snd_acp70_runtime_resume;
+}
diff --git a/sound/soc/amd/ps/ps-mach.c b/sound/soc/amd/ps/ps-mach.c
new file mode 100644
index 000000000000..ff8ad036b077
--- /dev/null
+++ b/sound/soc/amd/ps/ps-mach.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Machine driver for AMD Pink Sardine platform using DMIC
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+
+#include "acp63.h"
+
+#define DRV_NAME "acp_ps_mach"
+
+SND_SOC_DAILINK_DEF(acp63_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_ps_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_ps_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp63_dai_pdm[] = {
+ {
+ .name = "acp63-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp63_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp63_card = {
+ .name = "acp63",
+ .owner = THIS_MODULE,
+ .dai_link = acp63_dai_pdm,
+ .num_links = 1,
+};
+
+static int acp63_probe(struct platform_device *pdev)
+{
+ struct acp63_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ int ret;
+
+ platform_set_drvdata(pdev, &acp63_card);
+ card = platform_get_drvdata(pdev);
+ acp63_card.dev = &pdev->dev;
+
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+
+ return 0;
+}
+
+static struct platform_driver acp63_mach_driver = {
+ .driver = {
+ .name = "acp_ps_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp63_probe,
+};
+
+module_platform_driver(acp63_mach_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD Pink Sardine support for DMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c
new file mode 100644
index 000000000000..9cfbe05ad996
--- /dev/null
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
+ *
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.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/pm_runtime.h>
+
+#include "acp63.h"
+
+#define DRV_NAME "acp_ps_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp63_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .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,
+};
+
+static void acp63_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp63_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
+{
+ u32 ext_int_ctrl;
+
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
+}
+
+static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata)
+{
+ u32 ext_int_ctrl;
+
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
+}
+
+static bool acp63_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp63_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp63_enable_pdm_clock(acp_base);
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp63_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp63_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp63_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ writel(PAGE_SIZE_4K_ENABLE, rtd->acp63_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ writel(low, rtd->acp63_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ writel(high, rtd->acp63_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp63_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp63_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp63_enable_pdm_interrupts(adata);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp63_base = adata->acp63_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp63_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp63_config_dma(rtd, substream->stream);
+ acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp63_base);
+ return 0;
+}
+
+static u64 acp63_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ u32 high, low;
+ u64 byte_count;
+
+ high = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count = high;
+ low = readl(rtd->acp63_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ byte_count = (byte_count << 32) | low;
+ return byte_count;
+}
+
+static snd_pcm_uframes_t acp63_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp63_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp63_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp63_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ acp63_disable_pdm_interrupts(adata);
+ adata->capture_stream = NULL;
+ kfree(runtime->private_data);
+ return 0;
+}
+
+static int acp63_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ writel(ch_mask, rtd->acp63_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DECIMATION_FACTOR, rtd->acp63_base + ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp63_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
+ if (!pdm_status)
+ ret = acp63_start_pdm_dma(rtd->acp63_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp63_check_pdm_dma_status(rtd->acp63_base);
+ if (pdm_status)
+ ret = acp63_stop_pdm_dma(rtd->acp63_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp63_pdm_dai_ops = {
+ .trigger = acp63_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp63_pdm_dai_driver = {
+ .name = "acp_ps_pdm_dma.0",
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp63_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp63_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp63_pdm_dma_open,
+ .close = acp63_pdm_dma_close,
+ .hw_params = acp63_pdm_dma_hw_params,
+ .pointer = acp63_pdm_dma_pointer,
+ .pcm_construct = acp63_pdm_dma_new,
+};
+
+static int acp63_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ struct acp63_dev_data *acp_data;
+ struct device *parent;
+ int status;
+
+ parent = pdev->dev.parent;
+ acp_data = dev_get_drvdata(parent);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp63_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp63_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+ adata->acp_lock = &acp_data->acp_lock;
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp63_pdm_component,
+ &acp63_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp63_base);
+ }
+ acp63_enable_pdm_interrupts(adata);
+ return 0;
+}
+
+static int acp63_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp63_disable_pdm_interrupts(adata);
+ return 0;
+}
+
+static int acp63_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp63_enable_pdm_interrupts(adata);
+ return 0;
+}
+
+static const struct dev_pm_ops acp63_pdm_pm_ops = {
+ RUNTIME_PM_OPS(acp63_pdm_suspend, acp63_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp63_pdm_suspend, acp63_pdm_resume)
+};
+
+static struct platform_driver acp63_pdm_dma_driver = {
+ .probe = acp63_pdm_audio_probe,
+ .remove = acp63_pdm_audio_remove,
+ .driver = {
+ .name = "acp_ps_pdm_dma",
+ .pm = pm_ptr(&acp63_pdm_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_pdm_dma_driver);
+
+MODULE_AUTHOR("Syed.SabaKareem@amd.com");
+MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
new file mode 100644
index 000000000000..1b933a017c06
--- /dev/null
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -0,0 +1,799 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ALSA SoC common SoundWire DMA Driver for ACP6.3, ACP7.0 and ACP7.1
+ * platforms.
+ *
+ * Copyright 2023, 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_amd.h>
+#include "acp63.h"
+
+#define DRV_NAME "amd_ps_sdw_dma"
+
+static struct sdw_dma_ring_buf_reg acp63_sdw0_dma_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register
+ * set as per hardware register documentation
+ */
+static struct sdw_dma_ring_buf_reg acp63_sdw1_dma_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+};
+
+static u32 acp63_sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+ ACP_SW0_AUDIO0_TX_EN,
+ ACP_SW0_AUDIO1_TX_EN,
+ ACP_SW0_AUDIO2_TX_EN,
+ ACP_SW0_AUDIO0_RX_EN,
+ ACP_SW0_AUDIO1_RX_EN,
+ ACP_SW0_AUDIO2_RX_EN,
+};
+
+/*
+ * SDW1 instance supports one TX stream and one RX stream.
+ * For TX/RX streams DMA enable register programming for SDW1 instance,
+ * it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers
+ * as per hardware register documentation.
+ */
+static u32 acp63_sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+ ACP_SW1_AUDIO1_TX_EN,
+ ACP_SW1_AUDIO1_RX_EN,
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw0_dma_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw1_dma_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO0_TX_DMA_SIZE, ACP_P1_AUDIO0_TX_FIFOADDR, ACP_P1_AUDIO0_TX_FIFOSIZE,
+ ACP_P1_AUDIO0_TX_RINGBUFSIZE, ACP_P1_AUDIO0_TX_RINGBUFADDR,
+ ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_TX_DMA_SIZE, ACP_P1_AUDIO2_TX_FIFOADDR, ACP_P1_AUDIO2_TX_FIFOSIZE,
+ ACP_P1_AUDIO2_TX_RINGBUFSIZE, ACP_P1_AUDIO2_TX_RINGBUFADDR,
+ ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO0_RX_DMA_SIZE, ACP_P1_AUDIO0_RX_FIFOADDR, ACP_P1_AUDIO0_RX_FIFOSIZE,
+ ACP_P1_AUDIO0_RX_RINGBUFSIZE, ACP_P1_AUDIO0_RX_RINGBUFADDR,
+ ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_RX_DMA_SIZE, ACP_P1_AUDIO2_RX_FIFOADDR, ACP_P1_AUDIO2_RX_FIFOSIZE,
+ ACP_P1_AUDIO2_RX_RINGBUFSIZE, ACP_P1_AUDIO2_RX_RINGBUFADDR,
+ ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static u32 acp70_sdw0_dma_enable_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ ACP70_SW0_AUDIO0_TX_EN,
+ ACP70_SW0_AUDIO1_TX_EN,
+ ACP70_SW0_AUDIO2_TX_EN,
+ ACP70_SW0_AUDIO0_RX_EN,
+ ACP70_SW0_AUDIO1_RX_EN,
+ ACP70_SW0_AUDIO2_RX_EN,
+};
+
+static u32 acp70_sdw1_dma_enable_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ ACP70_SW1_AUDIO0_TX_EN,
+ ACP70_SW1_AUDIO1_TX_EN,
+ ACP70_SW1_AUDIO2_TX_EN,
+ ACP70_SW1_AUDIO0_RX_EN,
+ ACP70_SW1_AUDIO1_RX_EN,
+ ACP70_SW1_AUDIO2_RX_EN,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
+ .period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
+ .period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
+ .periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
+ .periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
+ .period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
+ .period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
+ .periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
+ .periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, u32 irq_mask,
+ u32 irq_mask1, bool enable)
+{
+ u32 ext_intr_cntl, ext_intr_cntl1;
+
+ if (enable) {
+ ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl |= irq_mask;
+ writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= irq_mask1;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ } else {
+ ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl &= ~irq_mask;
+ writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 &= ~irq_mask1;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ }
+}
+
+static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 stream_id)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ u32 sdw_dma_pte_offset;
+ dma_addr_t addr;
+
+ addr = stream->dma_addr;
+ sdw_dma_pte_offset = SDW_PTE_OFFSET(stream->instance);
+ val = sdw_dma_pte_offset + (stream_id * ACP_SDW_PTE_OFFSET);
+
+ /* Group Enable */
+ writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+ writel(PAGE_SIZE_4K_ENABLE, acp_base + ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+ for (page_idx = 0; page_idx < stream->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ writel(low, acp_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ writel(high, acp_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+ writel(0x1, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size,
+ u32 manager_instance, u32 acp_rev)
+{
+ u32 reg_dma_size;
+ u32 reg_fifo_addr;
+ u32 reg_fifo_size;
+ u32 reg_ring_buf_size;
+ u32 reg_ring_buf_addr;
+ u32 sdw_fifo_addr;
+ u32 sdw_fifo_offset;
+ u32 sdw_ring_buf_addr;
+ u32 sdw_ring_buf_size;
+ u32 sdw_mem_window_offset;
+
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp63_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp63_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp70_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance);
+ sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance);
+ sdw_fifo_addr = sdw_fifo_offset + (stream_id * SDW_FIFO_OFFSET);
+ sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET);
+ sdw_ring_buf_size = size;
+ writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size);
+ writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr);
+ writel(sdw_fifo_addr, acp_base + reg_fifo_addr);
+ writel(SDW_DMA_SIZE, acp_base + reg_dma_size);
+ writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size);
+ return 0;
+}
+
+static int acp63_sdw_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct acp_sdw_dma_stream *stream;
+ struct snd_soc_dai *cpu_dai;
+ struct amd_sdw_manager *amd_manager;
+ struct snd_soc_pcm_runtime *prtd = snd_soc_substream_to_rtd(substream);
+ int ret;
+
+ runtime = substream->runtime;
+ cpu_dai = snd_soc_rtd_to_cpu(prtd, 0);
+ amd_manager = snd_soc_dai_get_drvdata(cpu_dai);
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp63_sdw_hardware_playback;
+ else
+ runtime->hw = acp63_sdw_hardware_capture;
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(stream);
+ return ret;
+ }
+
+ stream->stream_id = cpu_dai->id;
+ stream->instance = amd_manager->instance;
+ runtime->private_data = stream;
+ return ret;
+}
+
+static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct sdw_dma_dev_data *sdw_data;
+ u32 period_bytes;
+ u32 water_mark_size_reg;
+ u32 irq_mask, ext_intr_ctrl;
+ u64 size;
+ u32 stream_id;
+ u32 acp_ext_intr_cntl_reg;
+ int ret;
+
+ sdw_data = dev_get_drvdata(component->dev);
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return -EINVAL;
+ stream_id = stream->stream_id;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp63_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP63_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP63_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp63_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ irq_mask = BIT(ACP63_SDW1_DMA_IRQ_MASK(stream_id));
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp70_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp70_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW1_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW1_DMA_RX_IRQ_MASK(stream_id));
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ stream->dma_addr = substream->runtime->dma_addr;
+ stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp63_config_dma(stream, sdw_data->acp_base, stream_id);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size,
+ stream->instance, sdw_data->acp_rev);
+ if (ret) {
+ dev_err(component->dev, "Invalid DMA channel\n");
+ return -EINVAL;
+ }
+ ext_intr_ctrl = readl(sdw_data->acp_base + acp_ext_intr_cntl_reg);
+ ext_intr_ctrl |= irq_mask;
+ writel(ext_intr_ctrl, sdw_data->acp_base + acp_ext_intr_cntl_reg);
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ return 0;
+}
+
+static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 acp_rev)
+{
+ union acp_sdw_dma_count byte_count;
+ u32 pos_low_reg, pos_high_reg;
+
+ byte_count.bytescount = 0;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
+ if (pos_low_reg) {
+ byte_count.bcount.high = readl(acp_base + pos_high_reg);
+ byte_count.bcount.low = readl(acp_base + pos_low_reg);
+ }
+POINTER_RETURN_BYTES:
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *substream)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp_sdw_dma_stream *stream;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ sdw_data = dev_get_drvdata(comp->dev);
+ stream = substream->runtime->private_data;
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base, sdw_data->acp_rev);
+ if (bytescount > stream->bytescount)
+ bytescount -= stream->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp63_sdw_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER);
+ return 0;
+}
+
+static int acp63_sdw_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp_sdw_dma_stream *stream;
+
+ sdw_data = dev_get_drvdata(component->dev);
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return -EINVAL;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ kfree(stream);
+ return 0;
+}
+
+static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
+ void __iomem *acp_base, u32 acp_rev, bool sdw_dma_enable)
+{
+ struct acp_sdw_dma_stream *stream;
+ u32 stream_id;
+ u32 sdw_dma_en_reg;
+ u32 sdw_dma_en_stat_reg;
+ u32 sdw_dma_stat;
+ u32 dma_enable;
+
+ stream = substream->runtime->private_data;
+ stream_id = stream->stream_id;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp63_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp63_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp70_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ sdw_dma_en_stat_reg = sdw_dma_en_reg + 4;
+ dma_enable = sdw_dma_enable;
+ writel(dma_enable, acp_base + sdw_dma_en_reg);
+ return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat,
+ (sdw_dma_stat == dma_enable), ACP_DELAY_US, ACP_COUNTER);
+}
+
+static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
+ struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ int ret;
+
+ sdw_data = dev_get_drvdata(comp->dev);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, true);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, false);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (ret)
+ dev_err(comp->dev, "trigger %d failed: %d", cmd, ret);
+ return ret;
+}
+
+static const struct snd_soc_component_driver acp63_sdw_component = {
+ .name = DRV_NAME,
+ .open = acp63_sdw_dma_open,
+ .close = acp63_sdw_dma_close,
+ .hw_params = acp63_sdw_dma_hw_params,
+ .trigger = acp63_sdw_dma_trigger,
+ .pointer = acp63_sdw_dma_pointer,
+ .pcm_construct = acp63_sdw_dma_new,
+ .use_dai_pcm_id = true,
+
+};
+
+static int acp63_sdw_platform_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct sdw_dma_dev_data *sdw_data;
+ struct acp63_dev_data *acp_data;
+ struct device *parent;
+ int status;
+
+ parent = pdev->dev.parent;
+ acp_data = dev_get_drvdata(parent);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
+ if (!sdw_data)
+ return -ENOMEM;
+
+ sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!sdw_data->acp_base)
+ return -ENOMEM;
+
+ sdw_data->acp_lock = &acp_data->acp_lock;
+ sdw_data->acp_rev = acp_data->acp_rev;
+ dev_set_drvdata(&pdev->dev, sdw_data);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp63_sdw_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register sdw dma component\n");
+ return status;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp63_sdw_platform_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp63_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP63_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP63_P1_SDW_DMA_IRQ_MASK;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ if (instance == ACP_SDW0)
+ stream_count = ACP63_SDW0_DMA_MAX_STREAMS;
+ else
+ stream_count = ACP63_SDW1_DMA_MAX_STREAMS;
+
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp63_sdw0_dma_stream[index];
+ water_mark_size_reg = acp63_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp63_sdw1_dma_stream[index];
+ water_mark_size_reg = acp63_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ ACP63_PCI_REV);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp70_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP70_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP70_P1_SDW_DMA_IRQ_MASK;
+ stream_count = ACP70_SDW0_DMA_MAX_STREAMS;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp70_sdw0_dma_stream[index];
+ water_mark_size_reg = acp70_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp70_sdw1_dma_stream[index];
+ water_mark_size_reg = acp70_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ sdw_data->acp_rev);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp63_sdw_pcm_resume(struct device *dev)
+{
+ struct sdw_dma_dev_data *sdw_data;
+
+ sdw_data = dev_get_drvdata(dev);
+ if (sdw_data->acp_rev == ACP63_PCI_REV)
+ return acp63_restore_sdw_dma_config(sdw_data);
+ else
+ return acp70_restore_sdw_dma_config(sdw_data);
+}
+
+static const struct dev_pm_ops acp63_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume)
+};
+
+static struct platform_driver acp63_sdw_dma_driver = {
+ .probe = acp63_sdw_platform_probe,
+ .remove = acp63_sdw_platform_remove,
+ .driver = {
+ .name = "amd_ps_sdw_dma",
+ .pm = pm_ptr(&acp63_pm_ops),
+ },
+};
+
+module_platform_driver(acp63_sdw_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD common SDW DMA Driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile
index 62c22b6ed95a..b2ea030cbf25 100644
--- a/sound/soc/amd/raven/Makefile
+++ b/sound/soc/amd/raven/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0+
# Raven Ridge platform Support
-snd-pci-acp3x-objs := pci-acp3x.o
-snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o
-snd-acp3x-i2s-objs := acp3x-i2s.o
+snd-pci-acp3x-y := pci-acp3x.o
+snd-acp3x-pcm-dma-y := acp3x-pcm-dma.o
+snd-acp3x-i2s-y := acp3x-i2s.o
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o
obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-i2s.o
diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c
index 5bc028692fcf..e7f2a05e802c 100644
--- a/sound/soc/amd/raven/acp3x-i2s.c
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -80,7 +80,7 @@ static int acp3x_i2s_hwparams(struct snd_pcm_substream *substream,
u32 val;
u32 reg_val, frmt_reg;
- prtd = asoc_substream_to_rtd(substream);
+ prtd = snd_soc_substream_to_rtd(substream);
rtd = substream->runtime->private_data;
card = prtd->card;
adata = snd_soc_dai_get_drvdata(dai);
@@ -249,7 +249,7 @@ static int acp3x_i2s_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
+static const struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
.hw_params = acp3x_i2s_hwparams,
.trigger = acp3x_i2s_trigger,
.set_fmt = acp3x_i2s_set_fmt,
@@ -257,15 +257,15 @@ static struct snd_soc_dai_ops acp3x_i2s_dai_ops = {
};
static const struct snd_soc_component_driver acp3x_dai_component = {
- .name = DRV_NAME,
+ .name = DRV_NAME,
+ .legacy_dai_naming = 1,
};
static struct snd_soc_dai_driver acp3x_i2s_dai = {
.playback = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rate_min = 8000,
@@ -274,8 +274,7 @@ static struct snd_soc_dai_driver acp3x_i2s_dai = {
.capture = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 2,
.rate_min = 8000,
@@ -316,16 +315,8 @@ static int acp3x_dai_probe(struct platform_device *pdev)
return 0;
}
-static int acp3x_dai_remove(struct platform_device *pdev)
-{
- /* As we use devm_ memory alloc there is nothing TBD here */
-
- return 0;
-}
-
static struct platform_driver acp3x_dai_driver = {
.probe = acp3x_dai_probe,
- .remove = acp3x_dai_remove,
.driver = {
.name = "acp3x_i2s_playcap",
},
diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c
index 417cda24030c..bb9ed52d744d 100644
--- a/sound/soc/amd/raven/acp3x-pcm-dma.c
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -24,8 +24,7 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = {
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_96000,
@@ -45,8 +44,7 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = {
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
- SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
@@ -149,7 +147,7 @@ static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction)
high |= BIT(31);
rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val
+ 4);
- /* Move to next physically contiguos page */
+ /* Move to next physically contiguous page */
val += 8;
addr += PAGE_SIZE;
}
@@ -217,7 +215,7 @@ static int acp3x_dma_open(struct snd_soc_component *component,
int ret;
runtime = substream->runtime;
- prtd = asoc_substream_to_rtd(substream);
+ prtd = snd_soc_substream_to_rtd(substream);
component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
adata = dev_get_drvdata(component->dev);
i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
@@ -237,10 +235,6 @@ static int acp3x_dma_open(struct snd_soc_component *component,
return ret;
}
- if (!adata->play_stream && !adata->capture_stream &&
- !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
- rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
-
i2s_data->acp3x_base = adata->acp3x_base;
runtime->private_data = i2s_data;
return ret;
@@ -258,7 +252,7 @@ static int acp3x_dma_hw_params(struct snd_soc_component *component,
struct i2s_dev_data *adata;
u64 size;
- prtd = asoc_substream_to_rtd(substream);
+ prtd = snd_soc_substream_to_rtd(substream);
card = prtd->card;
pinfo = snd_soc_card_get_drvdata(card);
adata = dev_get_drvdata(component->dev);
@@ -292,7 +286,7 @@ static int acp3x_dma_hw_params(struct snd_soc_component *component,
pr_err("pinfo failed\n");
}
size = params_buffer_bytes(params);
- rtd->dma_addr = substream->dma_buffer.addr;
+ rtd->dma_addr = substream->runtime->dma_addr;
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
config_acp3x_dma(rtd, substream->stream);
return 0;
@@ -326,13 +320,6 @@ static int acp3x_dma_new(struct snd_soc_component *component,
return 0;
}
-static int acp3x_dma_mmap(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return snd_pcm_lib_default_mmap(substream, vma);
-}
-
static int acp3x_dma_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -340,7 +327,7 @@ static int acp3x_dma_close(struct snd_soc_component *component,
struct i2s_dev_data *adata;
struct i2s_stream_instance *ins;
- prtd = asoc_substream_to_rtd(substream);
+ prtd = snd_soc_substream_to_rtd(substream);
component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
adata = dev_get_drvdata(component->dev);
ins = substream->runtime->private_data;
@@ -367,12 +354,6 @@ static int acp3x_dma_close(struct snd_soc_component *component,
}
}
- /* Disable ACP irq, when the current stream is being closed and
- * another stream is also not active.
- */
- if (!adata->play_stream && !adata->capture_stream &&
- !adata->i2ssp_play_stream && !adata->i2ssp_capture_stream)
- rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
return 0;
}
@@ -382,7 +363,6 @@ static const struct snd_soc_component_driver acp3x_i2s_component = {
.close = acp3x_dma_close,
.hw_params = acp3x_dma_hw_params,
.pointer = acp3x_dma_pointer,
- .mmap = acp3x_dma_mmap,
.pcm_construct = acp3x_dma_new,
};
@@ -414,13 +394,10 @@ static int acp3x_audio_probe(struct platform_device *pdev)
if (!adata->acp3x_base)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
- return -ENODEV;
- }
-
- adata->i2s_irq = res->start;
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->i2s_irq = status;
dev_set_drvdata(&pdev->dev, adata);
status = devm_snd_soc_register_component(&pdev->dev,
@@ -439,15 +416,15 @@ static int acp3x_audio_probe(struct platform_device *pdev)
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- pm_runtime_allow(&pdev->dev);
return 0;
}
-static int acp3x_audio_remove(struct platform_device *pdev)
+static void acp3x_audio_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- return 0;
}
static int acp3x_resume(struct device *dev)
diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h
index 03fe93913e12..7702f628ecd6 100644
--- a/sound/soc/amd/raven/acp3x.h
+++ b/sound/soc/amd/raven/acp3x.h
@@ -77,6 +77,7 @@
#define ACP_POWER_OFF_IN_PROGRESS 0x03
#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
struct acp3x_platform_info {
u16 play_i2s_instance;
@@ -86,7 +87,7 @@ struct acp3x_platform_info {
struct i2s_dev_data {
bool tdm_mode;
- unsigned int i2s_irq;
+ int i2s_irq;
u16 i2s_instance;
u32 tdm_fmt;
u32 substream_type;
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
index 31b797c8bfe6..a013a607b3d4 100644
--- a/sound/soc/amd/raven/pci-acp3x.c
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -76,6 +76,19 @@ static int acp3x_reset(void __iomem *acp3x_base)
return -ETIMEDOUT;
}
+static void acp3x_enable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp3x_disable_interrupts(void __iomem *acp_base)
+{
+ rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ mmACP_EXTERNAL_INTR_STAT);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);
+ rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);
+}
+
static int acp3x_init(struct acp3x_dev_data *adata)
{
void __iomem *acp3x_base = adata->acp3x_base;
@@ -93,6 +106,7 @@ static int acp3x_init(struct acp3x_dev_data *adata)
pr_err("ACP3x reset failed\n");
return ret;
}
+ acp3x_enable_interrupts(acp3x_base);
return 0;
}
@@ -100,6 +114,7 @@ static int acp3x_deinit(void __iomem *acp3x_base)
{
int ret;
+ acp3x_disable_interrupts(acp3x_base);
/* Reset */
ret = acp3x_reset(acp3x_base);
if (ret) {
@@ -118,6 +133,10 @@ static int snd_acp3x_probe(struct pci_dev *pci,
int ret, i;
u32 addr, val;
+ /* Raven device detection */
+ if (pci->revision != 0x00)
+ return -ENODEV;
+
if (pci_enable_device(pci)) {
dev_err(&pci->dev, "pci_enable_device failed\n");
return -ENODEV;
@@ -136,21 +155,14 @@ static int snd_acp3x_probe(struct pci_dev *pci,
goto release_regions;
}
- /* check for msi interrupt support */
- ret = pci_enable_msi(pci);
- if (ret)
- /* msi is not enabled */
- irqflags = IRQF_SHARED;
- else
- /* msi is enabled */
- irqflags = 0;
+ irqflags = IRQF_SHARED;
addr = pci_resource_start(pci, 0);
adata->acp3x_base = devm_ioremap(&pci->dev, addr,
pci_resource_len(pci, 0));
if (!adata->acp3x_base) {
ret = -ENOMEM;
- goto disable_msi;
+ goto release_regions;
}
pci_set_master(pci);
pci_set_drvdata(pci, adata);
@@ -158,7 +170,7 @@ static int snd_acp3x_probe(struct pci_dev *pci,
adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
ret = acp3x_init(adata);
if (ret)
- goto disable_msi;
+ goto release_regions;
val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
switch (val) {
@@ -231,9 +243,8 @@ static int snd_acp3x_probe(struct pci_dev *pci,
}
break;
default:
- dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
- ret = -ENODEV;
- goto disable_msi;
+ dev_info(&pci->dev, "ACP audio mode : %d\n", val);
+ break;
}
pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
pm_runtime_use_autosuspend(&pci->dev);
@@ -248,8 +259,6 @@ unregister_devs:
de_init:
if (acp3x_deinit(adata->acp3x_base))
dev_err(&pci->dev, "ACP de-init failed\n");
-disable_msi:
- pci_disable_msi(pci);
release_regions:
pci_release_regions(pci);
disable_pci:
@@ -308,7 +317,6 @@ static void snd_acp3x_remove(struct pci_dev *pci)
dev_err(&pci->dev, "ACP de-init failed\n");
pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev);
- pci_disable_msi(pci);
pci_release_regions(pci);
pci_disable_device(pci);
}
diff --git a/sound/soc/amd/renoir/Makefile b/sound/soc/amd/renoir/Makefile
index 4a82690aec16..76b4a9c3e24f 100644
--- a/sound/soc/amd/renoir/Makefile
+++ b/sound/soc/amd/renoir/Makefile
@@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0+
# Renoir platform Support
-snd-rn-pci-acp3x-objs := rn-pci-acp3x.o
-snd-acp3x-pdm-dma-objs := acp3x-pdm-dma.o
-snd-acp3x-rn-objs := acp3x-rn.o
+snd-rn-pci-acp3x-y := rn-pci-acp3x.o
+snd-acp3x-pdm-dma-y := acp3x-pdm-dma.o
+snd-acp3x-rn-y := acp3x-rn.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-rn-pci-acp3x.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += snd-acp3x-pdm-dma.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR_MACH) += snd-acp3x-rn.o
diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
index 7b14d9a81b97..95ac8c680037 100644
--- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -6,6 +6,7 @@
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
@@ -17,6 +18,10 @@
#define DRV_NAME "acp_rn_pdm_dma"
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -77,11 +82,11 @@ static void enable_pdm_clock(void __iomem *acp_base)
u32 pdm_clk_enable, pdm_ctrl;
pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
- pdm_ctrl = 0x00;
rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
- pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
}
@@ -129,7 +134,6 @@ static int start_pdm_dma(void __iomem *acp_base)
enable_pdm_clock(acp_base);
rn_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
- pdm_dma_enable = 0x00;
timeout = 0;
while (++timeout < ACP_COUNTER) {
pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
@@ -145,15 +149,11 @@ static int stop_pdm_dma(void __iomem *acp_base)
u32 pdm_enable, pdm_dma_enable;
int timeout;
- pdm_enable = 0x00;
- pdm_dma_enable = 0x00;
-
pdm_enable = rn_readl(acp_base + ACP_WOV_PDM_ENABLE);
pdm_dma_enable = rn_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
if (pdm_dma_enable & 0x01) {
pdm_dma_enable = 0x02;
rn_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
- pdm_dma_enable = 0x00;
timeout = 0;
while (++timeout < ACP_COUNTER) {
pdm_dma_enable = rn_readl(acp_base +
@@ -248,7 +248,7 @@ static int acp_pdm_dma_hw_params(struct snd_soc_component *component,
return -EINVAL;
size = params_buffer_bytes(params);
period_bytes = params_period_bytes(params);
- rtd->dma_addr = substream->dma_buffer.addr;
+ rtd->dma_addr = substream->runtime->dma_addr;
rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
config_acp_dma(rtd, substream->stream);
init_pdm_ring_buffer(MEM_WINDOW_START, size, period_bytes,
@@ -297,13 +297,6 @@ static int acp_pdm_dma_new(struct snd_soc_component *component,
return 0;
}
-static int acp_pdm_dma_mmap(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return snd_pcm_lib_default_mmap(substream, vma);
-}
-
static int acp_pdm_dma_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -358,7 +351,7 @@ static int acp_pdm_dai_trigger(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_dai_ops acp_pdm_dai_ops = {
+static const struct snd_soc_dai_ops acp_pdm_dai_ops = {
.trigger = acp_pdm_dai_trigger,
};
@@ -376,13 +369,13 @@ static struct snd_soc_dai_driver acp_pdm_dai_driver = {
};
static const struct snd_soc_component_driver acp_pdm_component = {
- .name = DRV_NAME,
- .open = acp_pdm_dma_open,
- .close = acp_pdm_dma_close,
- .hw_params = acp_pdm_dma_hw_params,
- .pointer = acp_pdm_dma_pointer,
- .mmap = acp_pdm_dma_mmap,
- .pcm_construct = acp_pdm_dma_new,
+ .name = DRV_NAME,
+ .open = acp_pdm_dma_open,
+ .close = acp_pdm_dma_close,
+ .hw_params = acp_pdm_dma_hw_params,
+ .pointer = acp_pdm_dma_pointer,
+ .pcm_construct = acp_pdm_dma_new,
+ .legacy_dai_naming = 1,
};
static int acp_pdm_audio_probe(struct platform_device *pdev)
@@ -413,13 +406,11 @@ static int acp_pdm_audio_probe(struct platform_device *pdev)
if (!adata->acp_base)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
- return -ENODEV;
- }
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->pdm_irq = status;
- adata->pdm_irq = res->start;
adata->capture_stream = NULL;
dev_set_drvdata(&pdev->dev, adata);
@@ -439,15 +430,15 @@ static int acp_pdm_audio_probe(struct platform_device *pdev)
}
pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- pm_runtime_allow(&pdev->dev);
return 0;
}
-static int acp_pdm_audio_remove(struct platform_device *pdev)
+static void acp_pdm_audio_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- return 0;
}
static int acp_pdm_resume(struct device *dev)
diff --git a/sound/soc/amd/renoir/acp3x-rn.c b/sound/soc/amd/renoir/acp3x-rn.c
index 306134b89a82..3249f74a0197 100644
--- a/sound/soc/amd/renoir/acp3x-rn.c
+++ b/sound/soc/amd/renoir/acp3x-rn.c
@@ -54,10 +54,9 @@ static int acp_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, machine);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
- dev_err(&pdev->dev,
- "snd_soc_register_card(%s) failed: %d\n",
- acp_card.name, ret);
- return ret;
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
}
return 0;
}
@@ -73,5 +72,6 @@ static struct platform_driver acp_mach_driver = {
module_platform_driver(acp_mach_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Renoir support for DMIC");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/renoir/rn-pci-acp3x.c b/sound/soc/amd/renoir/rn-pci-acp3x.c
index b943e59fc302..b3812b70f5f9 100644
--- a/sound/soc/amd/renoir/rn-pci-acp3x.c
+++ b/sound/soc/amd/renoir/rn-pci-acp3x.c
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -19,15 +20,14 @@ static int acp_power_gating;
module_param(acp_power_gating, int, 0644);
MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
-/**
- * dmic_acpi_check = -1 - Checks ACPI method to know DMIC hardware status runtime
- * = 0 - Skips the DMIC device creation and returns probe failure
- * = 1 - Assumes that platform has DMIC support and skips ACPI
- * method check
+/*
+ * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
+ * = 0 - Skip the DMIC device creation and return probe failure
+ * = 1 - Force DMIC support
*/
static int dmic_acpi_check = ACP_DMIC_AUTO;
module_param(dmic_acpi_check, bint, 0644);
-MODULE_PARM_DESC(dmic_acpi_check, "checks Dmic hardware runtime");
+MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
struct acp_dev_data {
void __iomem *acp_base;
@@ -163,6 +163,45 @@ static int rn_acp_deinit(void __iomem *acp_base)
return 0;
}
+static const struct dmi_system_id rn_acp_quirk_table[] = {
+ {
+ /* Lenovo IdeaPad S340-14API */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
+ }
+ },
+ {
+ /* Lenovo IdeaPad Flex 5 14ARE05 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
+ }
+ },
+ {
+ /* Lenovo IdeaPad 5 15ARE05 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
+ }
+ },
+ {
+ /* Lenovo ThinkPad E14 Gen 2 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
+ }
+ },
+ {
+ /* Lenovo ThinkPad X395 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
+ }
+ },
+ {}
+};
+
static int snd_rn_acp_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -172,10 +211,20 @@ static int snd_rn_acp_probe(struct pci_dev *pci,
acpi_handle handle;
acpi_integer dmic_status;
#endif
- unsigned int irqflags;
+ const struct dmi_system_id *dmi_id;
+ unsigned int irqflags, flag;
int ret, index;
u32 addr;
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Renoir device check */
+ if (pci->revision != 0x01)
+ return -ENODEV;
+
if (pci_enable_device(pci)) {
dev_err(&pci->dev, "pci_enable_device failed\n");
return -ENODEV;
@@ -224,7 +273,7 @@ static int snd_rn_acp_probe(struct pci_dev *pci,
handle = ACPI_HANDLE(&pci->dev);
ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
if (ACPI_FAILURE(ret)) {
- ret = -EINVAL;
+ ret = -ENODEV;
goto de_init;
}
if (!dmic_status) {
@@ -232,6 +281,12 @@ static int snd_rn_acp_probe(struct pci_dev *pci,
goto de_init;
}
#endif
+ dmi_id = dmi_first_match(rn_acp_quirk_table);
+ if (dmi_id && !dmi_id->driver_data) {
+ dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
+ ret = -ENODEV;
+ goto de_init;
+ }
}
adata->res = devm_kzalloc(&pci->dev,
@@ -332,6 +387,8 @@ static const struct dev_pm_ops rn_acp_pm = {
.runtime_resume = snd_rn_acp_resume,
.suspend = snd_rn_acp_suspend,
.resume = snd_rn_acp_resume,
+ .restore = snd_rn_acp_resume,
+ .poweroff = snd_rn_acp_suspend,
};
static void snd_rn_acp_remove(struct pci_dev *pci)
diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h
index 14620399d766..7d0f4e6a2834 100644
--- a/sound/soc/amd/renoir/rn_acp3x.h
+++ b/sound/soc/amd/renoir/rn_acp3x.h
@@ -34,7 +34,7 @@
#define ACP_ERROR_STAT 29
#define PDM_DECIMATION_FACTOR 0x2
#define ACP_PDM_CLK_FREQ_MASK 0x07
-#define ACP_WOV_MISC_CTRL_MASK 0x10
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
#define ACP_PDM_ENABLE 0x01
#define ACP_PDM_DISABLE 0x00
#define ACP_PDM_DMA_EN_STATUS 0x02
@@ -88,3 +88,6 @@ static inline void rn_writel(u32 val, void __iomem *base_addr)
{
writel(val, base_addr - ACP_PHY_BASE_ADDRESS);
}
+
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/rpl/Makefile b/sound/soc/amd/rpl/Makefile
new file mode 100644
index 000000000000..a3825c5be4e7
--- /dev/null
+++ b/sound/soc/amd/rpl/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+# RPL platform Support
+snd-rpl-pci-acp6x-y := rpl-pci-acp6x.o
+
+obj-$(CONFIG_SND_SOC_AMD_RPL_ACP6x) += snd-rpl-pci-acp6x.o
diff --git a/sound/soc/amd/rpl/rpl-pci-acp6x.c b/sound/soc/amd/rpl/rpl-pci-acp6x.c
new file mode 100644
index 000000000000..e3afe9172bdf
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl-pci-acp6x.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD RPL ACP PCI Driver
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "rpl_acp6x.h"
+
+struct rpl_dev_data {
+ void __iomem *acp6x_base;
+};
+
+static int rpl_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int rpl_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ rpl_acp_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ rpl_acp_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static int rpl_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = rpl_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = rpl_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ return 0;
+}
+
+static int rpl_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ /* Reset */
+ ret = rpl_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ rpl_acp_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static int snd_rpl_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct rpl_dev_data *adata;
+ u32 addr;
+ int ret;
+
+ /* RPL device check */
+ switch (pci->revision) {
+ case 0x62:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp6x pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP6x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = rpl_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_rpl_suspend(struct device *dev)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = rpl_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_rpl_resume(struct device *dev)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = rpl_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops rpl_pm = {
+ RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume)
+};
+
+static void snd_rpl_remove(struct pci_dev *pci)
+{
+ struct rpl_dev_data *adata;
+ int ret;
+
+ adata = pci_get_drvdata(pci);
+ ret = rpl_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_rpl_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_rpl_ids);
+
+static struct pci_driver rpl_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_rpl_ids,
+ .probe = snd_rpl_probe,
+ .remove = snd_rpl_remove,
+ .driver = {
+ .pm = pm_ptr(&rpl_pm),
+ }
+};
+
+module_pci_driver(rpl_acp6x_driver);
+
+MODULE_DESCRIPTION("AMD ACP RPL PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/rpl/rpl_acp6x.h b/sound/soc/amd/rpl/rpl_acp6x.h
new file mode 100644
index 000000000000..f5816a33632e
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl_acp6x.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "rpl_acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+static inline u32 rpl_acp_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void rpl_acp_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
diff --git a/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..456498f5396d
--- /dev/null
+++ b/sound/soc/amd/rpl/rpl_acp6x_chip_offset_byte.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.2 Register Documentation
+ *
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _rpl_acp6x_OFFSET_HEADER
+#define _rpl_acp6x_OFFSET_HEADER
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_PGFSM_CONTROL 0x124101C
+#define ACP_PGFSM_STATUS 0x1241020
+#define ACP_CLKMUX_SEL 0x1241024
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+
+#endif
diff --git a/sound/soc/amd/vangogh/Makefile b/sound/soc/amd/vangogh/Makefile
new file mode 100644
index 000000000000..7eae82faa392
--- /dev/null
+++ b/sound/soc/amd/vangogh/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Vangogh platform Support
+snd-pci-acp5x-y := pci-acp5x.o
+snd-acp5x-i2s-y := acp5x-i2s.o
+snd-acp5x-pcm-dma-y := acp5x-pcm-dma.o
+snd-soc-acp5x-mach-y := acp5x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-pci-acp5x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-i2s.o
+obj-$(CONFIG_SND_SOC_AMD_ACP5x) += snd-acp5x-pcm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_VANGOGH_MACH) += snd-soc-acp5x-mach.o
diff --git a/sound/soc/amd/vangogh/acp5x-i2s.c b/sound/soc/amd/vangogh/acp5x-i2s.c
new file mode 100644
index 000000000000..7dbe33f4b867
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-i2s.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#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 "acp5x.h"
+
+#define DRV_NAME "acp5x_i2s_playcap"
+
+static int acp5x_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct i2s_dev_data *adata;
+ int mode;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+ mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_I2S:
+ adata->tdm_mode = TDM_DISABLE;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ adata->tdm_mode = TDM_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mode = fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ switch (mode) {
+ case SND_SOC_DAIFMT_BP_FP:
+ adata->master_mode = I2S_MASTER_MODE_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ adata->master_mode = I2S_MASTER_MODE_DISABLE;
+ break;
+ }
+ return 0;
+}
+
+static int acp5x_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ u32 tx_mask, u32 rx_mask,
+ int slots, int slot_width)
+{
+ struct i2s_dev_data *adata;
+ u32 frm_len;
+ u16 slot_len;
+
+ adata = snd_soc_dai_get_drvdata(cpu_dai);
+
+ /* These values are as per Hardware Spec */
+ switch (slot_width) {
+ case SLOT_WIDTH_8:
+ slot_len = 8;
+ break;
+ case SLOT_WIDTH_16:
+ slot_len = 16;
+ break;
+ case SLOT_WIDTH_24:
+ slot_len = 24;
+ break;
+ case SLOT_WIDTH_32:
+ slot_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ frm_len = FRM_LEN | (slots << 15) | (slot_len << 18);
+ adata->tdm_fmt = frm_len;
+ return 0;
+}
+
+static int acp5x_i2s_hwparams(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp5x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+
+ u32 val;
+ u32 reg_val, frmt_reg;
+ u32 lrclk_div_val, bclk_div_val;
+
+ lrclk_div_val = 0;
+ bclk_div_val = 0;
+ prtd = snd_soc_substream_to_rtd(substream);
+ rtd = substream->runtime->private_data;
+ card = prtd->card;
+ adata = snd_soc_dai_get_drvdata(dai);
+ pinfo = snd_soc_card_get_drvdata(card);
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ else
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ }
+
+ /* These values are as per Hardware Spec */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ rtd->xfer_resolution = 0x0;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ rtd->xfer_resolution = 0x02;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ rtd->xfer_resolution = 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ rtd->xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ frmt_reg = ACP_HSTDM_TXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_ITER;
+ frmt_reg = ACP_I2STDM_TXFRMT;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ frmt_reg = ACP_HSTDM_RXFRMT;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_IRER;
+ frmt_reg = ACP_I2STDM_RXFRMT;
+ }
+ }
+ if (adata->tdm_mode) {
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ acp_writel(val | 0x2, rtd->acp5x_base + reg_val);
+ acp_writel(adata->tdm_fmt, rtd->acp5x_base + frmt_reg);
+ }
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val &= ~ACP5x_ITER_IRER_SAMP_LEN_MASK;
+ val = val | (rtd->xfer_resolution << 3);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+
+ if (adata->master_mode) {
+ 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;
+ }
+ rtd->lrclk_div = lrclk_div_val;
+ rtd->bclk_div = bclk_div_val;
+ }
+ return 0;
+}
+
+static int acp5x_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct i2s_stream_instance *rtd;
+ struct i2s_dev_data *adata;
+ u32 ret, val, period_bytes, reg_val, ier_val, water_val;
+ u32 buf_size, buf_reg;
+
+ adata = snd_soc_dai_get_drvdata(dai);
+ rtd = substream->runtime->private_data;
+ period_bytes = frames_to_bytes(substream->runtime,
+ substream->runtime->period_size);
+ buf_size = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rtd->bytescount = acp_get_byte_count(rtd,
+ substream->stream);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ 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;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ ACP_I2S_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_ITER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_TX_RINGBUFSIZE;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ 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;
+ case I2S_SP_INSTANCE:
+ default:
+ water_val =
+ ACP_I2S_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_I2STDM_IRER;
+ ier_val = ACP_I2STDM_IER;
+ buf_reg = ACP_I2S_RX_RINGBUFSIZE;
+ }
+ }
+ acp_writel(period_bytes, rtd->acp5x_base + water_val);
+ acp_writel(buf_size, rtd->acp5x_base + buf_reg);
+ if (adata->master_mode)
+ acp5x_set_i2s_clk(adata, rtd);
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val = val | BIT(0);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+ acp_writel(1, rtd->acp5x_base + ier_val);
+ ret = 0;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_ITER;
+ }
+
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ reg_val = ACP_I2STDM_IRER;
+ }
+ }
+ val = acp_readl(rtd->acp5x_base + reg_val);
+ val = val & ~BIT(0);
+ acp_writel(val, rtd->acp5x_base + reg_val);
+
+ if (!(acp_readl(rtd->acp5x_base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(acp_readl(rtd->acp5x_base + ACP_HSTDM_IRER) & BIT(0)))
+ acp_writel(0, rtd->acp5x_base + ACP_HSTDM_IER);
+ if (!(acp_readl(rtd->acp5x_base + ACP_I2STDM_ITER) & BIT(0)) &&
+ !(acp_readl(rtd->acp5x_base + ACP_I2STDM_IRER) & BIT(0)))
+ acp_writel(0, rtd->acp5x_base + ACP_I2STDM_IER);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp5x_i2s_dai_ops = {
+ .hw_params = acp5x_i2s_hwparams,
+ .trigger = acp5x_i2s_trigger,
+ .set_fmt = acp5x_i2s_set_fmt,
+ .set_tdm_slot = acp5x_i2s_set_tdm_slot,
+};
+
+static const struct snd_soc_component_driver acp5x_dai_component = {
+ .name = "acp5x-i2s",
+ .legacy_dai_naming = 1,
+};
+
+static struct snd_soc_dai_driver acp5x_i2s_dai = {
+ .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 = 2,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .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 = 2,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .ops = &acp5x_i2s_dai_ops,
+};
+
+static int acp5x_dai_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ int ret;
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data),
+ GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENOMEM;
+ }
+ adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp5x_base)
+ return -ENOMEM;
+
+ adata->master_mode = I2S_MASTER_MODE_ENABLE;
+ dev_set_drvdata(&pdev->dev, adata);
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &acp5x_dai_component,
+ &acp5x_i2s_dai, 1);
+ if (ret)
+ dev_err(&pdev->dev, "Fail to register acp i2s dai\n");
+ return ret;
+}
+
+static struct platform_driver acp5x_dai_driver = {
+ .probe = acp5x_dai_probe,
+ .driver = {
+ .name = "acp5x_i2s_playcap",
+ },
+};
+
+module_platform_driver(acp5x_dai_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP5.x CPU DAI Driver");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c
new file mode 100644
index 000000000000..2ca904db82ab
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-mach.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Vangogh platform using either
+ * NAU8821 & CS35L41 or NAU8821 & MAX98388 codecs.
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input-event-codes.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../../codecs/nau8821.h"
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_mach"
+#define DUAL_CHANNEL 2
+#define ACP5X_NAU8821_BCLK 3072000
+#define ACP5X_NAU8821_FREQ_OUT 12288000
+#define ACP5X_NAU8821_COMP_NAME "i2c-NVTN2020:00"
+#define ACP5X_NAU8821_DAI_NAME "nau8821-hifi"
+#define ACP5X_CS35L41_COMP_LNAME "spi-VLV1776:00"
+#define ACP5X_CS35L41_COMP_RNAME "spi-VLV1776:01"
+#define ACP5X_CS35L41_DAI_NAME "cs35l41-pcm"
+#define ACP5X_MAX98388_COMP_LNAME "i2c-ADS8388:00"
+#define ACP5X_MAX98388_COMP_RNAME "i2c-ADS8388:01"
+#define ACP5X_MAX98388_DAI_NAME "max98388-aif1"
+
+static struct snd_soc_jack vg_headset;
+
+SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0")));
+SND_SOC_DAILINK_DEF(acp5x_i2s, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.0")));
+SND_SOC_DAILINK_DEF(acp5x_bt, DAILINK_COMP_ARRAY(COMP_CPU("acp5x_i2s_playcap.1")));
+SND_SOC_DAILINK_DEF(nau8821, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_NAU8821_COMP_NAME,
+ ACP5X_NAU8821_DAI_NAME)));
+
+static struct snd_soc_jack_pin acp5x_nau8821_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new acp5x_8821_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *dai;
+ int ret = 0;
+
+ dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+ if (!dai) {
+ dev_err(card->dev, "Codec dai not found\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_INTERNAL, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "set sysclk err = %d\n", ret);
+ return -EIO;
+ }
+ } else {
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(dai->dev, "can't set BLK clock %d\n", ret);
+ ret = snd_soc_dai_set_pll(dai, 0, 0, ACP5X_NAU8821_BCLK, ACP5X_NAU8821_FREQ_OUT);
+ if (ret < 0)
+ dev_err(dai->dev, "can't set FLL: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int acp5x_8821_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
+ int ret;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &vg_headset, acp5x_nau8821_jack_pins,
+ ARRAY_SIZE(acp5x_nau8821_jack_pins));
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_0, KEY_MEDIA);
+ nau8821_enable_jack_detect(component, &vg_headset);
+
+ return ret;
+}
+
+static const unsigned int rates[] = {
+ 48000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static const unsigned int channels[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static const unsigned int acp5x_nau8821_format[] = {32};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_bits = {
+ .list = acp5x_nau8821_format,
+ .count = ARRAY_SIZE(acp5x_nau8821_format),
+};
+
+static int acp5x_8821_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+
+ machine->play_i2s_instance = I2S_SP_INSTANCE;
+ machine->cap_i2s_instance = I2S_SP_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &constraints_sample_bits);
+
+ return 0;
+}
+
+static int acp5x_nau8821_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dai *dai = snd_soc_card_get_codec_dai(card, ACP5X_NAU8821_DAI_NAME);
+ int ret, bclk;
+
+ if (!dai)
+ return -EINVAL;
+
+ ret = snd_soc_dai_set_sysclk(dai, NAU8821_CLK_FLL_BLK, 0, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FS clock %d\n", ret);
+
+ bclk = snd_soc_params_to_bclk(params);
+ if (bclk < 0) {
+ dev_err(dai->dev, "Fail to get BCLK rate: %d\n", bclk);
+ return bclk;
+ }
+
+ ret = snd_soc_dai_set_pll(dai, 0, 0, bclk, params_rate(params) * 256);
+ if (ret < 0)
+ dev_err(card->dev, "can't set FLL: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops acp5x_8821_ops = {
+ .startup = acp5x_8821_startup,
+ .hw_params = acp5x_nau8821_hw_params,
+};
+
+static int acp5x_cs35l41_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+
+ return 0;
+}
+
+static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ unsigned int bclk, rate = params_rate(params);
+ struct snd_soc_component *comp;
+ int ret, i;
+
+ switch (rate) {
+ case 48000:
+ bclk = 1536000;
+ break;
+ default:
+ bclk = 0;
+ break;
+ }
+
+ for_each_rtd_components(rtd, i, comp) {
+ if (!(strcmp(comp->name, ACP5X_CS35L41_COMP_LNAME)) ||
+ !(strcmp(comp->name, ACP5X_CS35L41_COMP_RNAME))) {
+ if (!bclk) {
+ dev_err(comp->dev, "Invalid sample rate: 0x%x\n", rate);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_set_sysclk(comp, 0, 0, bclk, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(comp->dev, "failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp5x_cs35l41_play_ops = {
+ .startup = acp5x_cs35l41_startup,
+ .hw_params = acp5x_cs35l41_hw_params,
+};
+
+static struct snd_soc_codec_conf acp5x_cs35l41_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_CS35L41_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(cs35l41, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_CS35L41_COMP_LNAME,
+ ACP5X_CS35L41_DAI_NAME),
+ COMP_CODEC(ACP5X_CS35L41_COMP_RNAME,
+ ACP5X_CS35L41_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = {
+ {
+ .name = "acp5x-8821-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-CS35L41-Stereo",
+ .stream_name = "CS35L41 Stereo Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .ops = &acp5x_cs35l41_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, cs35l41, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_35l41_audio_route[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+};
+
+static struct snd_soc_card acp5x_8821_35l41_card = {
+ .name = "acp5x",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_35l41_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_35l41_dai),
+ .dapm_widgets = acp5x_8821_35l41_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_35l41_widgets),
+ .dapm_routes = acp5x_8821_35l41_audio_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_35l41_audio_route),
+ .codec_conf = acp5x_cs35l41_conf,
+ .num_configs = ARRAY_SIZE(acp5x_cs35l41_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+static int acp5x_max98388_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ machine->play_i2s_instance = I2S_HS_INSTANCE;
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_rates);
+ return 0;
+}
+
+static const struct snd_soc_ops acp5x_max98388_play_ops = {
+ .startup = acp5x_max98388_startup,
+};
+
+static struct snd_soc_codec_conf acp5x_max98388_conf[] = {
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_LNAME),
+ .name_prefix = "Left",
+ },
+ {
+ .dlc = COMP_CODEC_CONF(ACP5X_MAX98388_COMP_RNAME),
+ .name_prefix = "Right",
+ },
+};
+
+SND_SOC_DAILINK_DEF(max98388, DAILINK_COMP_ARRAY(COMP_CODEC(ACP5X_MAX98388_COMP_LNAME,
+ ACP5X_MAX98388_DAI_NAME),
+ COMP_CODEC(ACP5X_MAX98388_COMP_RNAME,
+ ACP5X_MAX98388_DAI_NAME)));
+
+static struct snd_soc_dai_link acp5x_8821_98388_dai[] = {
+ {
+ .name = "acp5x-8821-play",
+ .stream_name = "Playback/Capture",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .ops = &acp5x_8821_ops,
+ .init = acp5x_8821_init,
+ SND_SOC_DAILINK_REG(acp5x_i2s, nau8821, platform),
+ },
+ {
+ .name = "acp5x-max98388-play",
+ .stream_name = "MAX98388 Playback",
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .playback_only = 1,
+ .ops = &acp5x_max98388_play_ops,
+ SND_SOC_DAILINK_REG(acp5x_bt, max98388, platform),
+ },
+};
+
+static const struct snd_soc_dapm_widget acp5x_8821_98388_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SPK("SPK", NULL),
+};
+
+static const struct snd_soc_dapm_route acp5x_8821_98388_route[] = {
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "MICL", NULL, "Headset Mic" },
+ { "MICR", NULL, "Headset Mic" },
+ { "DMIC", NULL, "Int Mic" },
+
+ { "Headphone", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
+ { "Int Mic", NULL, "Platform Clock" },
+
+ { "SPK", NULL, "Left BE_OUT" },
+ { "SPK", NULL, "Right BE_OUT" },
+};
+
+static struct snd_soc_card acp5x_8821_98388_card = {
+ .name = "acp5x-max98388",
+ .owner = THIS_MODULE,
+ .dai_link = acp5x_8821_98388_dai,
+ .num_links = ARRAY_SIZE(acp5x_8821_98388_dai),
+ .dapm_widgets = acp5x_8821_98388_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(acp5x_8821_98388_widgets),
+ .dapm_routes = acp5x_8821_98388_route,
+ .num_dapm_routes = ARRAY_SIZE(acp5x_8821_98388_route),
+ .codec_conf = acp5x_max98388_conf,
+ .num_configs = ARRAY_SIZE(acp5x_max98388_conf),
+ .controls = acp5x_8821_controls,
+ .num_controls = ARRAY_SIZE(acp5x_8821_controls),
+};
+
+static const struct dmi_system_id acp5x_vg_quirk_table[] = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
+ },
+ .driver_data = (void *)&acp5x_8821_35l41_card,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
+ },
+ .driver_data = (void *)&acp5x_8821_98388_card,
+ },
+ {}
+};
+
+static int acp5x_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ struct acp5x_platform_info *machine;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ dmi_id = dmi_first_match(acp5x_vg_quirk_table);
+ if (!dmi_id || !dmi_id->driver_data)
+ return -ENODEV;
+
+ machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL);
+ if (!machine)
+ return -ENOMEM;
+
+ card = dmi_id->driver_data;
+ card->dev = dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret)
+ return dev_err_probe(dev, ret, "Register card (%s) failed\n", card->name);
+
+ return 0;
+}
+
+static struct platform_driver acp5x_mach_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp5x_probe,
+};
+
+module_platform_driver(acp5x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 audio support");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x-pcm-dma.c b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
new file mode 100644
index 000000000000..aa4726899434
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x-pcm-dma.c
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD ALSA SoC PCM Driver
+//
+// Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "acp5x.h"
+
+#define DRV_NAME "acp5x_i2s_dma"
+
+static const struct snd_pcm_hardware acp5x_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_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .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,
+ .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 acp5x_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_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .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,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .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,
+};
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+ struct i2s_dev_data *vg_i2s_data;
+ u16 irq_flag;
+ u32 val;
+
+ vg_i2s_data = dev_id;
+ if (!vg_i2s_data)
+ return IRQ_NONE;
+
+ irq_flag = 0;
+ val = acp_readl(vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ if ((val & BIT(HS_TX_THRESHOLD)) && vg_i2s_data->play_stream) {
+ acp_writel(BIT(HS_TX_THRESHOLD), vg_i2s_data->acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->play_stream);
+ irq_flag = 1;
+ }
+ if ((val & BIT(I2S_TX_THRESHOLD)) && vg_i2s_data->i2ssp_play_stream) {
+ acp_writel(BIT(I2S_TX_THRESHOLD),
+ vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->i2ssp_play_stream);
+ irq_flag = 1;
+ }
+
+ if ((val & BIT(HS_RX_THRESHOLD)) && vg_i2s_data->capture_stream) {
+ acp_writel(BIT(HS_RX_THRESHOLD), vg_i2s_data->acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->capture_stream);
+ irq_flag = 1;
+ }
+ if ((val & BIT(I2S_RX_THRESHOLD)) && vg_i2s_data->i2ssp_capture_stream) {
+ acp_writel(BIT(I2S_RX_THRESHOLD),
+ vg_i2s_data->acp5x_base + ACP_EXTERNAL_INTR_STAT);
+ snd_pcm_period_elapsed(vg_i2s_data->i2ssp_capture_stream);
+ irq_flag = 1;
+ }
+
+ if (irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+static void config_acp5x_dma(struct i2s_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val, acp_fifo_addr, reg_fifo_addr;
+ u32 reg_dma_size, reg_fifo_size;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ val = ACP_SRAM_HS_PB_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_PB_PTE_OFFSET;
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ val = ACP_SRAM_HS_CP_PTE_OFFSET;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ val = ACP_SRAM_SP_CP_PTE_OFFSET;
+ }
+ }
+ /* Group Enable */
+ acp_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp5x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp_writel(PAGE_SIZE_4K_ENABLE, rtd->acp5x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp_writel(low, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp_writel(high, rtd->acp5x_base + ACP_SCRATCH_REG_0 + val + 4);
+ /* Move to next physically contiguous page */
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+ acp_writel(I2S_HS_TX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_HS_TX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = ACP_I2S_TX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
+ acp_writel(I2S_SP_TX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_I2S_TX_RINGBUFADDR);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+ acp_writel(I2S_HS_RX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_HS_RX_RINGBUFADDR);
+ break;
+
+ case I2S_SP_INSTANCE:
+ default:
+ reg_dma_size = ACP_I2S_RX_DMA_SIZE;
+ acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ SP_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
+ reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
+ acp_writel(I2S_SP_RX_MEM_WINDOW_START,
+ rtd->acp5x_base + ACP_I2S_RX_RINGBUFADDR);
+ }
+ }
+ acp_writel(DMA_SIZE, rtd->acp5x_base + reg_dma_size);
+ acp_writel(acp_fifo_addr, rtd->acp5x_base + reg_fifo_addr);
+ acp_writel(FIFO_SIZE, rtd->acp5x_base + reg_fifo_size);
+ acp_writel(BIT(I2S_RX_THRESHOLD) | BIT(HS_RX_THRESHOLD)
+ | BIT(I2S_TX_THRESHOLD) | BIT(HS_TX_THRESHOLD),
+ rtd->acp5x_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static int acp5x_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *i2s_data;
+ int ret;
+
+ runtime = substream->runtime;
+ prtd = snd_soc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+
+ i2s_data = kzalloc(sizeof(*i2s_data), GFP_KERNEL);
+ if (!i2s_data)
+ return -ENOMEM;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ runtime->hw = acp5x_pcm_hardware_playback;
+ else
+ runtime->hw = acp5x_pcm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(i2s_data);
+ return ret;
+ }
+ i2s_data->acp5x_base = adata->acp5x_base;
+ runtime->private_data = i2s_data;
+ return ret;
+}
+
+static int acp5x_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct i2s_stream_instance *rtd;
+ struct snd_soc_pcm_runtime *prtd;
+ struct snd_soc_card *card;
+ struct acp5x_platform_info *pinfo;
+ struct i2s_dev_data *adata;
+ u64 size;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ card = prtd->card;
+ pinfo = snd_soc_card_get_drvdata(card);
+ adata = dev_get_drvdata(component->dev);
+ rtd = substream->runtime->private_data;
+
+ if (!rtd)
+ return -EINVAL;
+
+ if (pinfo) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ rtd->i2s_instance = pinfo->play_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->play_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = substream;
+ }
+ } else {
+ rtd->i2s_instance = pinfo->cap_i2s_instance;
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->capture_stream = substream;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = substream;
+ }
+ }
+ } else {
+ dev_err(component->dev, "pinfo failed\n");
+ return -EINVAL;
+ }
+ size = params_buffer_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ config_acp5x_dma(rtd, substream->stream);
+ return 0;
+}
+
+static snd_pcm_uframes_t acp5x_dma_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_stream_instance *rtd;
+ u32 pos;
+ u32 buffersize;
+ u64 bytescount;
+
+ rtd = substream->runtime->private_data;
+ buffersize = frames_to_bytes(substream->runtime,
+ substream->runtime->buffer_size);
+ bytescount = acp_get_byte_count(rtd, substream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static int acp5x_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp5x_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *prtd;
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *ins;
+
+ prtd = snd_soc_substream_to_rtd(substream);
+ component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+ adata = dev_get_drvdata(component->dev);
+ ins = substream->runtime->private_data;
+ if (!ins)
+ return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (ins->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->play_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_play_stream = NULL;
+ }
+ } else {
+ switch (ins->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ adata->capture_stream = NULL;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ adata->i2ssp_capture_stream = NULL;
+ }
+ }
+ kfree(ins);
+ return 0;
+}
+
+static const struct snd_soc_component_driver acp5x_i2s_component = {
+ .name = DRV_NAME,
+ .open = acp5x_dma_open,
+ .close = acp5x_dma_close,
+ .hw_params = acp5x_dma_hw_params,
+ .pointer = acp5x_dma_pointer,
+ .pcm_construct = acp5x_dma_new,
+};
+
+static int acp5x_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct i2s_dev_data *adata;
+ unsigned int irqflags;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
+ irqflags = *((unsigned int *)(pdev->dev.platform_data));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp5x_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!adata->acp5x_base)
+ return -ENOMEM;
+
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ return status;
+ adata->i2s_irq = status;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp5x_i2s_component,
+ NULL, 0);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp i2s component\n");
+ return status;
+ }
+ status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler,
+ irqflags, "ACP5x_I2S_IRQ", adata);
+ if (status) {
+ dev_err(&pdev->dev, "ACP5x I2S IRQ request failed\n");
+ return status;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp5x_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp5x_pcm_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+ struct i2s_stream_instance *rtd;
+ u32 val;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->play_stream && adata->play_stream->runtime) {
+ rtd = adata->play_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_ITER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_TXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_HSTDM_ITER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_ITER);
+ }
+ }
+ if (adata->i2ssp_play_stream && adata->i2ssp_play_stream->runtime) {
+ rtd = adata->i2ssp_play_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_ITER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_TXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_I2STDM_ITER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_ITER);
+ }
+ }
+
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ rtd = adata->capture_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_HSTDM_IRER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_HSTDM_RXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_HSTDM_IRER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_HSTDM_IRER);
+ }
+ }
+ if (adata->i2ssp_capture_stream && adata->i2ssp_capture_stream->runtime) {
+ rtd = adata->i2ssp_capture_stream->runtime->private_data;
+ config_acp5x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp_writel((rtd->xfer_resolution << 3), rtd->acp5x_base + ACP_I2STDM_IRER);
+ if (adata->tdm_mode == TDM_ENABLE) {
+ acp_writel(adata->tdm_fmt, adata->acp5x_base + ACP_I2STDM_RXFRMT);
+ val = acp_readl(adata->acp5x_base + ACP_I2STDM_IRER);
+ acp_writel(val | 0x2, adata->acp5x_base + ACP_I2STDM_IRER);
+ }
+ }
+ acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int acp5x_pcm_suspend(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp_writel(0, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static int acp5x_pcm_runtime_resume(struct device *dev)
+{
+ struct i2s_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp_writel(1, adata->acp5x_base + ACP_EXTERNAL_INTR_ENB);
+ return 0;
+}
+
+static const struct dev_pm_ops acp5x_pm_ops = {
+ RUNTIME_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp5x_pcm_suspend, acp5x_pcm_resume)
+};
+
+static struct platform_driver acp5x_dma_driver = {
+ .probe = acp5x_audio_probe,
+ .remove = acp5x_audio_remove,
+ .driver = {
+ .name = "acp5x_i2s_dma",
+ .pm = &acp5x_pm_ops,
+ },
+};
+
+module_platform_driver(acp5x_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP 5.x PCM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/vangogh/acp5x.h b/sound/soc/amd/vangogh/acp5x.h
new file mode 100644
index 000000000000..ac1936a8c43f
--- /dev/null
+++ b/sound/soc/amd/vangogh/acp5x.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PCM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "vg_chip_offset_byte.h"
+#include <sound/pcm.h>
+
+#define ACP5x_PHY_BASE_ADDRESS 0x1240000
+#define ACP_DEVICE_ID 0x15E2
+#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_ERR_INTR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+#define ACP5x_DEVS 4
+#define ACP5x_REG_START 0x1240000
+#define ACP5x_REG_END 0x1250200
+#define ACP5x_I2STDM_REG_START 0x1242400
+#define ACP5x_I2STDM_REG_END 0x1242410
+#define ACP5x_HS_TDM_REG_START 0x1242814
+#define ACP5x_HS_TDM_REG_END 0x1242824
+#define I2S_MODE 0
+#define ACP5x_I2S_MODE 1
+#define ACP5x_RES 4
+#define I2S_RX_THRESHOLD 27
+#define I2S_TX_THRESHOLD 28
+#define HS_TX_THRESHOLD 24
+#define HS_RX_THRESHOLD 23
+
+#define I2S_SP_INSTANCE 1
+#define I2S_HS_INSTANCE 2
+
+#define ACP_SRAM_PTE_OFFSET 0x02050000
+#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
+#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
+#define ACP_SRAM_HS_PB_PTE_OFFSET 0x200
+#define ACP_SRAM_HS_CP_PTE_OFFSET 0x300
+#define PAGE_SIZE_4K_ENABLE 0x2
+#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
+#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
+#define I2S_HS_TX_MEM_WINDOW_START 0x4040000
+#define I2S_HS_RX_MEM_WINDOW_START 0x4060000
+
+#define SP_PB_FIFO_ADDR_OFFSET 0x500
+#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
+#define HS_PB_FIFO_ADDR_OFFSET 0x900
+#define HS_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define PLAYBACK_MIN_NUM_PERIODS 2
+#define PLAYBACK_MAX_NUM_PERIODS 8
+#define PLAYBACK_MAX_PERIOD_SIZE 8192
+#define PLAYBACK_MIN_PERIOD_SIZE 1024
+#define CAPTURE_MIN_NUM_PERIODS 2
+#define CAPTURE_MAX_NUM_PERIODS 8
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 1024
+
+#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+#define FIFO_SIZE 0x100
+#define DMA_SIZE 0x40
+#define FRM_LEN 0x100
+
+#define I2S_MASTER_MODE_ENABLE 1
+#define I2S_MASTER_MODE_DISABLE 0
+
+#define SLOT_WIDTH_8 8
+#define SLOT_WIDTH_16 16
+#define SLOT_WIDTH_24 24
+#define SLOT_WIDTH_32 32
+#define TDM_ENABLE 1
+#define TDM_DISABLE 0
+#define ACP5x_ITER_IRER_SAMP_LEN_MASK 0x38
+
+struct i2s_dev_data {
+ bool tdm_mode;
+ bool master_mode;
+ int i2s_irq;
+ u16 i2s_instance;
+ u32 tdm_fmt;
+ void __iomem *acp5x_base;
+ struct snd_pcm_substream *play_stream;
+ struct snd_pcm_substream *capture_stream;
+ struct snd_pcm_substream *i2ssp_play_stream;
+ struct snd_pcm_substream *i2ssp_capture_stream;
+};
+
+struct i2s_stream_instance {
+ u16 num_pages;
+ u16 i2s_instance;
+ u16 direction;
+ u16 channels;
+ u32 xfer_resolution;
+ u32 val;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp5x_base;
+ u32 lrclk_div;
+ u32 bclk_div;
+};
+
+union acp_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+struct acp5x_platform_info {
+ u16 play_i2s_instance;
+ u16 cap_i2s_instance;
+};
+
+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;
+};
+
+/* common header file uses exact offset rather than relative
+ * offset which requires subtraction logic from base_addr
+ * for accessing ACP5x MMIO space registers
+ */
+static inline u32 acp_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP5x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP5x_PHY_BASE_ADDRESS);
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
+static inline u64 acp_get_byte_count(struct i2s_stream_instance *rtd,
+ int direction)
+{
+ union acp_dma_count byte_count;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_TX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_TX_LINEARPOSCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_TX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_TX_LINEARPOSCNTR_LOW);
+ }
+ } else {
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_RX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_HS_RX_LINEARPOSCNTR_LOW);
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ byte_count.bcount.high =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_RX_LINEARPOSCNTR_HIGH);
+ byte_count.bcount.low =
+ acp_readl(rtd->acp5x_base +
+ ACP_I2S_RX_LINEARPOSCNTR_LOW);
+ }
+ }
+ return byte_count.bytescount;
+}
+
+static inline void acp5x_set_i2s_clk(struct i2s_dev_data *adata,
+ struct i2s_stream_instance *rtd)
+{
+ union acp_i2stdm_mstrclkgen mclkgen;
+ u32 master_reg;
+
+ switch (rtd->i2s_instance) {
+ case I2S_HS_INSTANCE:
+ master_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ case I2S_SP_INSTANCE:
+ default:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ mclkgen.bits.i2stdm_master_mode = 0x1;
+ if (adata->tdm_mode)
+ mclkgen.bits.i2stdm_format_mode = 0x01;
+ else
+ mclkgen.bits.i2stdm_format_mode = 0x00;
+
+ mclkgen.bits.i2stdm_bclk_div_val = rtd->bclk_div;
+ mclkgen.bits.i2stdm_lrclk_div_val = rtd->lrclk_div;
+ acp_writel(mclkgen.u32_all, rtd->acp5x_base + master_reg);
+}
diff --git a/sound/soc/amd/vangogh/pci-acp5x.c b/sound/soc/amd/vangogh/pci-acp5x.c
new file mode 100644
index 000000000000..af56ff09f02a
--- /dev/null
+++ b/sound/soc/amd/vangogh/pci-acp5x.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// AMD Vangogh ACP PCI Driver
+//
+// Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+
+#include "acp5x.h"
+#include "../mach-config.h"
+
+struct acp5x_dev_data {
+ void __iomem *acp5x_base;
+ bool acp5x_audio_mode;
+ struct resource *res;
+ struct platform_device *pdev[ACP5x_DEVS];
+};
+
+static int acp5x_power_on(void __iomem *acp5x_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp5x_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp5x_reset(void __iomem *acp5x_base)
+{
+ u32 val;
+ int timeout;
+
+ acp_writel(1, acp5x_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp_writel(0, acp5x_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp_readl(acp5x_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp5x_enable_interrupts(void __iomem *acp5x_base)
+{
+ acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp5x_disable_interrupts(void __iomem *acp5x_base)
+{
+ acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);
+ acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp5x_init(void __iomem *acp5x_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp5x_power_on(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x power on failed\n");
+ return ret;
+ }
+ acp_writel(0x01, acp5x_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp5x_reset(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x reset failed\n");
+ return ret;
+ }
+ acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
+ acp5x_enable_interrupts(acp5x_base);
+ return 0;
+}
+
+static int acp5x_deinit(void __iomem *acp5x_base)
+{
+ int ret;
+
+ acp5x_disable_interrupts(acp5x_base);
+ /* Reset */
+ ret = acp5x_reset(acp5x_base);
+ if (ret) {
+ pr_err("ACP5x reset failed\n");
+ return ret;
+ }
+ acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
+ acp_writel(0x00, acp5x_base + ACP_CONTROL);
+ return 0;
+}
+
+static int snd_acp5x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp5x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP5x_DEVS];
+ unsigned int irqflags, flag;
+ int ret, i;
+ u32 addr, val;
+
+ /*
+ * Return if ACP config flag is defined, except when board
+ * supports SOF while it is not being enabled in kernel config.
+ */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY &&
+ (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH)))
+ return -ENODEV;
+
+ irqflags = IRQF_SHARED;
+ if (pci->revision != 0x50)
+ return -ENODEV;
+
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP5x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ addr = pci_resource_start(pci, 0);
+ adata->acp5x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp5x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp5x_init(adata->acp5x_base);
+ if (ret)
+ goto release_regions;
+
+ val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case I2S_MODE:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource) * ACP5x_RES,
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res[0].name = "acp5x_i2s_iomem";
+ adata->res[0].flags = IORESOURCE_MEM;
+ adata->res[0].start = addr;
+ adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);
+
+ adata->res[1].name = "acp5x_i2s_sp";
+ adata->res[1].flags = IORESOURCE_MEM;
+ adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
+ adata->res[1].end = addr + ACP5x_I2STDM_REG_END;
+
+ adata->res[2].name = "acp5x_i2s_hs";
+ adata->res[2].flags = IORESOURCE_MEM;
+ adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
+ adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;
+
+ adata->res[3].name = "acp5x_i2s_irq";
+ adata->res[3].flags = IORESOURCE_IRQ;
+ adata->res[3].start = pci->irq;
+ adata->res[3].end = adata->res[3].start;
+
+ adata->acp5x_audio_mode = ACP5x_I2S_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp5x_i2s_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 4;
+ pdevinfo[0].res = &adata->res[0];
+ pdevinfo[0].data = &irqflags;
+ pdevinfo[0].size_data = sizeof(irqflags);
+
+ pdevinfo[1].name = "acp5x_i2s_playcap";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+ pdevinfo[1].num_res = 1;
+ pdevinfo[1].res = &adata->res[1];
+
+ pdevinfo[2].name = "acp5x_i2s_playcap";
+ pdevinfo[2].id = 1;
+ pdevinfo[2].parent = &pci->dev;
+ pdevinfo[2].num_res = 1;
+ pdevinfo[2].res = &adata->res[2];
+
+ pdevinfo[3].name = "acp5x_mach";
+ pdevinfo[3].id = 0;
+ pdevinfo[3].parent = &pci->dev;
+ for (i = 0; i < ACP5x_DEVS; i++) {
+ adata->pdev[i] =
+ platform_device_register_full(&pdevinfo[i]);
+ if (IS_ERR(adata->pdev[i])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[i].name);
+ ret = PTR_ERR(adata->pdev[i]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ default:
+ dev_info(&pci->dev, "ACP audio mode : %d\n", val);
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+ return 0;
+
+unregister_devs:
+ for (--i; i >= 0; i--)
+ platform_device_unregister(adata->pdev[i]);
+de_init:
+ if (acp5x_deinit(adata->acp5x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp5x_suspend(struct device *dev)
+{
+ int ret;
+ struct acp5x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp5x_deinit(adata->acp5x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return ret;
+}
+
+static int snd_acp5x_resume(struct device *dev)
+{
+ int ret;
+ struct acp5x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp5x_init(adata->acp5x_base);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops acp5x_pm = {
+ RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
+};
+
+static void snd_acp5x_remove(struct pci_dev *pci)
+{
+ struct acp5x_dev_data *adata;
+ int i, ret;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
+ for (i = 0; i < ACP5x_DEVS; i++)
+ platform_device_unregister(adata->pdev[i]);
+ }
+ ret = acp5x_deinit(adata->acp5x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp5x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);
+
+static struct pci_driver acp5x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp5x_ids,
+ .probe = snd_acp5x_probe,
+ .remove = snd_acp5x_remove,
+ .driver = {
+ .pm = pm_ptr(&acp5x_pm),
+ }
+};
+
+module_pci_driver(acp5x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/vangogh/vg_chip_offset_byte.h b/sound/soc/amd/vangogh/vg_chip_offset_byte.h
new file mode 100644
index 000000000000..b1165ae142b7
--- /dev/null
+++ b/sound/soc/amd/vangogh/vg_chip_offset_byte.h
@@ -0,0 +1,337 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 5.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp_ip_OFFSET_HEADER
+#define _acp_ip_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+
+/* Registers from ACP_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241800
+#define ACP_EXTERNAL_INTR_CNTL 0x1241804
+#define ACP_EXTERNAL_INTR_STAT 0x1241808
+#define ACP_ERROR_STATUS 0x12418C4
+#define ACP_SW_I2S_ERROR_REASON 0x12418C8
+#define ACP_MEM_PG_STS 0x12418CC
+#define ACP_PGMEM_DEEP_SLEEP_CTRL 0x12418D0
+#define ACP_PGMEM_SHUT_DOWN_CTRL 0x12418D4
+
+/* Registers from ACP_PGFSM block */
+#define ACP_PIN_CONFIG 0x1241400
+#define ACP_PAD_PULLUP_CTRL 0x1241404
+#define ACP_PAD_PULLDOWN_CTRL 0x1241408
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124140C
+#define ACP_PAD_SCHMEN_CTRL 0x1241410
+#define ACP_SW_PAD_KEEPER_EN 0x1241414
+#define ACP_SW_WAKE_EN 0x1241418
+#define ACP_I2S_WAKE_EN 0x124141C
+#define ACP_PME_EN 0x1241420
+#define ACP_PGFSM_CONTROL 0x1241424
+#define ACP_PGFSM_STATUS 0x1241428
+#define ACP_CLKMUX_SEL 0x124142C
+#define ACP_DEVICE_STATE 0x1241430
+#define AZ_DEVICE_STATE 0x1241434
+#define ACP_INTR_URGENCY_TIMER 0x1241438
+#define AZ_INTR_URGENCY_TIMER 0x124143C
+#define ACP_AON_SW_INTR_TRIG 0x1241440
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+#endif
diff --git a/sound/soc/amd/yc/Makefile b/sound/soc/amd/yc/Makefile
new file mode 100644
index 000000000000..7a0a3a410b2d
--- /dev/null
+++ b/sound/soc/amd/yc/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Yellow Carp platform Support
+snd-pci-acp6x-y := pci-acp6x.o
+snd-acp6x-pdm-dma-y := acp6x-pdm-dma.o
+snd-soc-acp6x-mach-y := acp6x-mach.o
+
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-pci-acp6x.o
+obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-acp6x-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_YC_MACH) += snd-soc-acp6x-mach.o
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
new file mode 100644
index 000000000000..98022e5fd428
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -0,0 +1,705 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Machine driver for AMD Yellow Carp platform using DMIC
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_mach"
+
+SND_SOC_DAILINK_DEF(acp6x_pdm,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp_yc_pdm_dma.0")));
+
+SND_SOC_DAILINK_DEF(dmic_codec,
+ DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
+ "dmic-hifi")));
+
+SND_SOC_DAILINK_DEF(pdm_platform,
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_yc_pdm_dma.0")));
+
+static struct snd_soc_dai_link acp6x_dai_pdm[] = {
+ {
+ .name = "acp6x-dmic-capture",
+ .stream_name = "DMIC capture",
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(acp6x_pdm, dmic_codec, pdm_platform),
+ },
+};
+
+static struct snd_soc_card acp6x_card = {
+ .name = "acp6x",
+ .owner = THIS_MODULE,
+ .dai_link = acp6x_dai_pdm,
+ .num_links = 1,
+};
+
+static const struct dmi_system_id yc_acp_quirk_table[] = {
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5525"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D1"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21EN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21HY"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J0"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M1"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M4"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M5"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21M6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21ME"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82QF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82TL"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82UU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82V2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82YM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83AS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83BS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83L3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83N6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83Q3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83J2"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M5602RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6400RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M3402RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6500RC"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1404FA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1504FA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M7600RE"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M3502RA"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VEK"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 14 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi Book Pro 15 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Xiaomi Book Pro 14 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "TIMI"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Redmi G 2022"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Razer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Blade 14 (2022) - RZ09-0427"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Swift SFA16-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "IRBIS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "15NBC1011"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "OMEN by HP Gaming Laptop 16z-n000"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb2xxx"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A42"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A43"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A44"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A22"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A3E"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A7F"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8B27"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8B2F"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8BD6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MECHREVO"),
+ DMI_MATCH(DMI_BOARD_NAME, "MRID6"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MDC"),
+ DMI_MATCH(DMI_BOARD_NAME, "Herbag_MDU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "System76"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "pang12"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "System76"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "pang13"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 C7UCX"),
+ }
+ },
+ {}
+};
+
+static int acp6x_probe(struct platform_device *pdev)
+{
+ const struct dmi_system_id *dmi_id;
+ struct acp6x_pdm *machine = NULL;
+ struct snd_soc_card *card;
+ struct acpi_device *adev;
+ acpi_handle handle;
+ acpi_integer dmic_status;
+ int ret;
+ bool is_dmic_enable, wov_en;
+
+ /* IF WOV entry not found, enable dmic based on AcpDmicConnected entry*/
+ is_dmic_enable = false;
+ wov_en = true;
+ /* check the parent device's firmware node has _DSD or not */
+ adev = ACPI_COMPANION(pdev->dev.parent);
+ if (adev) {
+ const union acpi_object *obj;
+
+ if (!acpi_dev_get_property(adev, "AcpDmicConnected", ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == 1)
+ is_dmic_enable = true;
+ }
+
+ handle = ACPI_HANDLE(pdev->dev.parent);
+ ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
+ if (!ACPI_FAILURE(ret)) {
+ wov_en = dmic_status;
+ if (!wov_en)
+ return -ENODEV;
+ } else {
+ /* Incase of ACPI method read failure then jump to check_dmi_entry */
+ goto check_dmi_entry;
+ }
+
+ if (is_dmic_enable)
+ platform_set_drvdata(pdev, &acp6x_card);
+
+check_dmi_entry:
+ /* check for any DMI overrides */
+ dmi_id = dmi_first_match(yc_acp_quirk_table);
+ if (dmi_id)
+ platform_set_drvdata(pdev, dmi_id->driver_data);
+
+ card = platform_get_drvdata(pdev);
+ if (!card)
+ return -ENODEV;
+ dev_info(&pdev->dev, "Enabling ACP DMIC support via %s", dmi_id ? "DMI" : "ACPI");
+ acp6x_card.dev = &pdev->dev;
+
+ snd_soc_card_set_drvdata(card, machine);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ return dev_err_probe(&pdev->dev, ret,
+ "snd_soc_register_card(%s) failed\n",
+ card->name);
+ }
+ return 0;
+}
+
+static struct platform_driver acp6x_mach_driver = {
+ .driver = {
+ .name = "acp_yc_mach",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = acp6x_probe,
+};
+
+module_platform_driver(acp6x_mach_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD Yellow Carp support for DMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
new file mode 100644
index 000000000000..ac758b90f441
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Yellow Carp PDM Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/bitfield.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/pm_runtime.h>
+
+#include "acp6x.h"
+
+#define DRV_NAME "acp_yc_pdm_dma"
+
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
+static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .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,
+};
+
+static void acp6x_init_pdm_ring_buffer(u32 physical_addr, u32 buffer_size,
+ u32 watermark_size, void __iomem *acp_base)
+{
+ acp6x_writel(physical_addr, acp_base + ACP_WOV_RX_RINGBUFADDR);
+ acp6x_writel(buffer_size, acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ acp6x_writel(watermark_size, acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ acp6x_writel(0x01, acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static void acp6x_enable_pdm_clock(void __iomem *acp_base)
+{
+ u32 pdm_clk_enable, pdm_ctrl;
+
+ pdm_clk_enable = ACP_PDM_CLK_FREQ_MASK;
+ pdm_ctrl = 0x00;
+
+ acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
+ pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
+ acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
+}
+
+static void acp6x_enable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp6x_disable_pdm_interrupts(void __iomem *acp_base)
+{
+ u32 ext_int_ctrl;
+
+ ext_int_ctrl = acp6x_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
+ acp6x_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static bool acp6x_check_pdm_dma_status(void __iomem *acp_base)
+{
+ bool pdm_dma_status;
+ u32 pdm_enable, pdm_dma_enable;
+
+ pdm_dma_status = false;
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_enable & ACP_PDM_ENABLE) && (pdm_dma_enable & ACP_PDM_DMA_EN_STATUS))
+ pdm_dma_status = true;
+
+ return pdm_dma_status;
+}
+
+static int acp6x_start_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable;
+ u32 pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x01;
+ pdm_dma_enable = 0x01;
+
+ acp6x_enable_pdm_clock(acp_base);
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == ACP_PDM_DMA_EN_STATUS)
+ return 0;
+ udelay(DELAY_US);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_stop_pdm_dma(void __iomem *acp_base)
+{
+ u32 pdm_enable, pdm_dma_enable;
+ int timeout;
+
+ pdm_enable = 0x00;
+ pdm_dma_enable = 0x00;
+
+ pdm_enable = acp6x_readl(acp_base + ACP_WOV_PDM_ENABLE);
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (pdm_dma_enable & 0x01) {
+ pdm_dma_enable = 0x02;
+ acp6x_writel(pdm_dma_enable, acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ timeout = 0;
+ while (++timeout < ACP_COUNTER) {
+ pdm_dma_enable = acp6x_readl(acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((pdm_dma_enable & 0x02) == 0x00)
+ break;
+ udelay(DELAY_US);
+ }
+ if (timeout == ACP_COUNTER)
+ return -ETIMEDOUT;
+ }
+ if (pdm_enable == ACP_PDM_ENABLE) {
+ pdm_enable = ACP_PDM_DISABLE;
+ acp6x_writel(pdm_enable, acp_base + ACP_WOV_PDM_ENABLE);
+ }
+ acp6x_writel(0x01, acp_base + ACP_WOV_PDM_FIFO_FLUSH);
+ return 0;
+}
+
+static void acp6x_config_dma(struct pdm_stream_instance *rtd, int direction)
+{
+ u16 page_idx;
+ u32 low, high, val;
+ dma_addr_t addr;
+
+ addr = rtd->dma_addr;
+ val = PDM_PTE_OFFSET;
+
+ /* Group Enable */
+ acp6x_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp6x_base +
+ ACPAXI2AXI_ATU_BASE_ADDR_GRP_1);
+ acp6x_writel(PAGE_SIZE_4K_ENABLE, rtd->acp6x_base +
+ ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1);
+ for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) {
+ /* Load the low address of page int ACP SRAM through SRBM */
+ low = lower_32_bits(addr);
+ high = upper_32_bits(addr);
+
+ acp6x_writel(low, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val);
+ high |= BIT(31);
+ acp6x_writel(high, rtd->acp6x_base + ACP_SCRATCH_REG_0 + val + 4);
+ val += 8;
+ addr += PAGE_SIZE;
+ }
+}
+
+static int acp6x_pdm_dma_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+ struct pdm_dev_data *adata;
+ struct pdm_stream_instance *pdm_data;
+ int ret;
+
+ runtime = substream->runtime;
+ adata = dev_get_drvdata(component->dev);
+ pdm_data = kzalloc(sizeof(*pdm_data), GFP_KERNEL);
+ if (!pdm_data)
+ return -EINVAL;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw = acp6x_pdm_hardware_capture;
+
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(component->dev, "set integer constraint failed\n");
+ kfree(pdm_data);
+ return ret;
+ }
+
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ adata->capture_stream = substream;
+
+ pdm_data->acp6x_base = adata->acp6x_base;
+ runtime->private_data = pdm_data;
+ return ret;
+}
+
+static int acp6x_pdm_dma_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct pdm_stream_instance *rtd;
+ size_t size, period_bytes;
+
+ rtd = substream->runtime->private_data;
+ if (!rtd)
+ return -EINVAL;
+ size = params_buffer_bytes(params);
+ period_bytes = params_period_bytes(params);
+ rtd->dma_addr = substream->runtime->dma_addr;
+ rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+ acp6x_config_dma(rtd, substream->stream);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, size,
+ period_bytes, rtd->acp6x_base);
+ return 0;
+}
+
+static u64 acp6x_pdm_get_byte_count(struct pdm_stream_instance *rtd,
+ int direction)
+{
+ union acp_pdm_dma_count byte_count;
+
+ byte_count.bcount.high =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ byte_count.bcount.low =
+ acp6x_readl(rtd->acp6x_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp6x_pdm_dma_pointer(struct snd_soc_component *comp,
+ struct snd_pcm_substream *stream)
+{
+ struct pdm_stream_instance *rtd;
+ u32 pos, buffersize;
+ u64 bytescount;
+
+ rtd = stream->runtime->private_data;
+ buffersize = frames_to_bytes(stream->runtime,
+ stream->runtime->buffer_size);
+ bytescount = acp6x_pdm_get_byte_count(rtd, stream->stream);
+ if (bytescount > rtd->bytescount)
+ bytescount -= rtd->bytescount;
+ pos = do_div(bytescount, buffersize);
+ return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp6x_pdm_dma_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct device *parent = component->dev->parent;
+
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ parent, MIN_BUFFER, MAX_BUFFER);
+ return 0;
+}
+
+static int acp6x_pdm_dma_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
+
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ adata->capture_stream = NULL;
+ return 0;
+}
+
+static int acp6x_pdm_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct pdm_stream_instance *rtd;
+ int ret;
+ bool pdm_status;
+ unsigned int ch_mask;
+
+ rtd = substream->runtime->private_data;
+ ret = 0;
+ switch (substream->runtime->channels) {
+ case TWO_CH:
+ ch_mask = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ acp6x_writel(ch_mask, rtd->acp6x_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ acp6x_writel(PDM_DECIMATION_FACTOR, rtd->acp6x_base +
+ ACP_WOV_PDM_DECIMATION_FACTOR);
+ rtd->bytescount = acp6x_pdm_get_byte_count(rtd, substream->stream);
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (!pdm_status)
+ ret = acp6x_start_pdm_dma(rtd->acp6x_base);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ pdm_status = acp6x_check_pdm_dma_status(rtd->acp6x_base);
+ if (pdm_status)
+ ret = acp6x_stop_pdm_dma(rtd->acp6x_base);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_soc_dai_ops acp6x_pdm_dai_ops = {
+ .trigger = acp6x_pdm_dai_trigger,
+};
+
+static struct snd_soc_dai_driver acp6x_pdm_dai_driver = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ },
+ .ops = &acp6x_pdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver acp6x_pdm_component = {
+ .name = DRV_NAME,
+ .open = acp6x_pdm_dma_open,
+ .close = acp6x_pdm_dma_close,
+ .hw_params = acp6x_pdm_dma_hw_params,
+ .pointer = acp6x_pdm_dma_pointer,
+ .pcm_construct = acp6x_pdm_dma_new,
+ .legacy_dai_naming = 1,
+};
+
+static int acp6x_pdm_audio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct pdm_dev_data *adata;
+ int status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata = devm_kzalloc(&pdev->dev, sizeof(*adata), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ adata->acp6x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp6x_base)
+ return -ENOMEM;
+
+ adata->capture_stream = NULL;
+
+ dev_set_drvdata(&pdev->dev, adata);
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &acp6x_pdm_component,
+ &acp6x_pdm_dai_driver, 1);
+ if (status) {
+ dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+ return -ENODEV;
+ }
+ pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
+static void acp6x_pdm_audio_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int acp6x_pdm_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+ struct snd_pcm_runtime *runtime;
+ struct pdm_stream_instance *rtd;
+ u32 period_bytes, buffer_len;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->capture_stream && adata->capture_stream->runtime) {
+ runtime = adata->capture_stream->runtime;
+ rtd = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buffer_len = frames_to_bytes(runtime, runtime->buffer_size);
+ acp6x_config_dma(rtd, SNDRV_PCM_STREAM_CAPTURE);
+ acp6x_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
+ period_bytes, adata->acp6x_base);
+ }
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int acp6x_pdm_suspend(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_disable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static int acp6x_pdm_runtime_resume(struct device *dev)
+{
+ struct pdm_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ acp6x_enable_pdm_interrupts(adata->acp6x_base);
+ return 0;
+}
+
+static const struct dev_pm_ops acp6x_pdm_pm_ops = {
+ RUNTIME_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(acp6x_pdm_suspend, acp6x_pdm_resume)
+};
+
+static struct platform_driver acp6x_pdm_dma_driver = {
+ .probe = acp6x_pdm_audio_probe,
+ .remove = acp6x_pdm_audio_remove,
+ .driver = {
+ .name = "acp_yc_pdm_dma",
+ .pm = pm_ptr(&acp6x_pdm_pm_ops),
+ },
+};
+
+module_platform_driver(acp6x_pdm_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6x YC PDM Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/yc/acp6x.h b/sound/soc/amd/yc/acp6x.h
new file mode 100644
index 000000000000..2de7d1edf00b
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ALSA SoC PDM Driver
+ *
+ * Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#include "acp6x_chip_offset_byte.h"
+
+#define ACP_DEVICE_ID 0x15E2
+#define ACP6x_PHY_BASE_ADDRESS 0x1240000
+#define ACP6x_REG_START 0x1240000
+#define ACP6x_REG_END 0x1250200
+#define ACP6x_DEVS 3
+#define ACP6x_PDM_MODE 1
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP_PGFSM_STATUS_MASK 3
+#define ACP_POWERED_ON 0
+#define ACP_POWER_ON_IN_PROGRESS 1
+#define ACP_POWERED_OFF 2
+#define ACP_POWER_OFF_IN_PROGRESS 3
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+#define PDM_DMA_STAT 0x10
+
+#define PDM_DMA_INTR_MASK 0x10000
+#define ACP_ERROR_STAT 29
+#define PDM_DECIMATION_FACTOR 2
+#define ACP_PDM_CLK_FREQ_MASK 7
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
+#define ACP_PDM_ENABLE 1
+#define ACP_PDM_DISABLE 0
+#define ACP_PDM_DMA_EN_STATUS 2
+#define TWO_CH 2
+#define DELAY_US 5
+#define ACP_COUNTER 20000
+
+#define ACP_SRAM_PTE_OFFSET 0x03800000
+#define PAGE_SIZE_4K_ENABLE 2
+#define PDM_PTE_OFFSET 0
+#define PDM_MEM_WINDOW_START 0x4000000
+
+#define CAPTURE_MIN_NUM_PERIODS 4
+#define CAPTURE_MAX_NUM_PERIODS 4
+#define CAPTURE_MAX_PERIOD_SIZE 8192
+#define CAPTURE_MIN_PERIOD_SIZE 4096
+
+#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
+#define MIN_BUFFER MAX_BUFFER
+
+/* time in ms for runtime suspend delay */
+#define ACP_SUSPEND_DELAY_MS 2000
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+};
+
+struct pdm_dev_data {
+ u32 pdm_irq;
+ void __iomem *acp6x_base;
+ struct snd_pcm_substream *capture_stream;
+};
+
+struct pdm_stream_instance {
+ u16 num_pages;
+ u16 channels;
+ dma_addr_t dma_addr;
+ u64 bytescount;
+ void __iomem *acp6x_base;
+};
+
+union acp_pdm_dma_count {
+ struct {
+ u32 low;
+ u32 high;
+ } bcount;
+ u64 bytescount;
+};
+
+static inline u32 acp6x_readl(void __iomem *base_addr)
+{
+ return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+static inline void acp6x_writel(u32 val, void __iomem *base_addr)
+{
+ writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
+}
+
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
diff --git a/sound/soc/amd/yc/acp6x_chip_offset_byte.h b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
new file mode 100644
index 000000000000..f05fb2dfb5da
--- /dev/null
+++ b/sound/soc/amd/yc/acp6x_chip_offset_byte.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * AMD ACP 6.x Register Documentation
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _acp6x_OFFSET_HEADER
+#define _acp6x_OFFSET_HEADER
+
+/* Registers from ACP_DMA block */
+#define ACP_DMA_CNTL_0 0x1240000
+#define ACP_DMA_CNTL_1 0x1240004
+#define ACP_DMA_CNTL_2 0x1240008
+#define ACP_DMA_CNTL_3 0x124000C
+#define ACP_DMA_CNTL_4 0x1240010
+#define ACP_DMA_CNTL_5 0x1240014
+#define ACP_DMA_CNTL_6 0x1240018
+#define ACP_DMA_CNTL_7 0x124001C
+#define ACP_DMA_DSCR_STRT_IDX_0 0x1240020
+#define ACP_DMA_DSCR_STRT_IDX_1 0x1240024
+#define ACP_DMA_DSCR_STRT_IDX_2 0x1240028
+#define ACP_DMA_DSCR_STRT_IDX_3 0x124002C
+#define ACP_DMA_DSCR_STRT_IDX_4 0x1240030
+#define ACP_DMA_DSCR_STRT_IDX_5 0x1240034
+#define ACP_DMA_DSCR_STRT_IDX_6 0x1240038
+#define ACP_DMA_DSCR_STRT_IDX_7 0x124003C
+#define ACP_DMA_DSCR_CNT_0 0x1240040
+#define ACP_DMA_DSCR_CNT_1 0x1240044
+#define ACP_DMA_DSCR_CNT_2 0x1240048
+#define ACP_DMA_DSCR_CNT_3 0x124004C
+#define ACP_DMA_DSCR_CNT_4 0x1240050
+#define ACP_DMA_DSCR_CNT_5 0x1240054
+#define ACP_DMA_DSCR_CNT_6 0x1240058
+#define ACP_DMA_DSCR_CNT_7 0x124005C
+#define ACP_DMA_PRIO_0 0x1240060
+#define ACP_DMA_PRIO_1 0x1240064
+#define ACP_DMA_PRIO_2 0x1240068
+#define ACP_DMA_PRIO_3 0x124006C
+#define ACP_DMA_PRIO_4 0x1240070
+#define ACP_DMA_PRIO_5 0x1240074
+#define ACP_DMA_PRIO_6 0x1240078
+#define ACP_DMA_PRIO_7 0x124007C
+#define ACP_DMA_CUR_DSCR_0 0x1240080
+#define ACP_DMA_CUR_DSCR_1 0x1240084
+#define ACP_DMA_CUR_DSCR_2 0x1240088
+#define ACP_DMA_CUR_DSCR_3 0x124008C
+#define ACP_DMA_CUR_DSCR_4 0x1240090
+#define ACP_DMA_CUR_DSCR_5 0x1240094
+#define ACP_DMA_CUR_DSCR_6 0x1240098
+#define ACP_DMA_CUR_DSCR_7 0x124009C
+#define ACP_DMA_CUR_TRANS_CNT_0 0x12400A0
+#define ACP_DMA_CUR_TRANS_CNT_1 0x12400A4
+#define ACP_DMA_CUR_TRANS_CNT_2 0x12400A8
+#define ACP_DMA_CUR_TRANS_CNT_3 0x12400AC
+#define ACP_DMA_CUR_TRANS_CNT_4 0x12400B0
+#define ACP_DMA_CUR_TRANS_CNT_5 0x12400B4
+#define ACP_DMA_CUR_TRANS_CNT_6 0x12400B8
+#define ACP_DMA_CUR_TRANS_CNT_7 0x12400BC
+#define ACP_DMA_ERR_STS_0 0x12400C0
+#define ACP_DMA_ERR_STS_1 0x12400C4
+#define ACP_DMA_ERR_STS_2 0x12400C8
+#define ACP_DMA_ERR_STS_3 0x12400CC
+#define ACP_DMA_ERR_STS_4 0x12400D0
+#define ACP_DMA_ERR_STS_5 0x12400D4
+#define ACP_DMA_ERR_STS_6 0x12400D8
+#define ACP_DMA_ERR_STS_7 0x12400DC
+#define ACP_DMA_DESC_BASE_ADDR 0x12400E0
+#define ACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4
+#define ACP_DMA_CH_STS 0x12400E8
+#define ACP_DMA_CH_GROUP 0x12400EC
+#define ACP_DMA_CH_RST_STS 0x12400F0
+
+/* Registers from ACP_AXI2AXIATU block */
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C
+#define ACPAXI2AXI_ATU_CTRL 0x1240C40
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_9 0x1240C44
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_9 0x1240C48
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_10 0x1240C4C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_10 0x1240C50
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_11 0x1240C54
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_11 0x1240C58
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_12 0x1240C5C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_12 0x1240C60
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_13 0x1240C64
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_13 0x1240C68
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_14 0x1240C6C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_14 0x1240C70
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_15 0x1240C74
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_15 0x1240C78
+#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_16 0x1240C7C
+#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_16 0x1240C80
+
+/* Registers from ACP_CLKRST block */
+#define ACP_SOFT_RESET 0x1241000
+#define ACP_CONTROL 0x1241004
+#define ACP_STATUS 0x1241008
+#define ACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010
+#define ACP_ZSC_DSP_CTRL 0x1241014
+#define ACP_ZSC_STS 0x1241018
+#define ACP_PGFSM_CONTROL 0x1241024
+#define ACP_PGFSM_STATUS 0x1241028
+#define ACP_CLKMUX_SEL 0x124102C
+
+/* Registers from ACP_AON block */
+#define ACP_PME_EN 0x1241400
+#define ACP_DEVICE_STATE 0x1241404
+#define AZ_DEVICE_STATE 0x1241408
+#define ACP_PIN_CONFIG 0x1241440
+#define ACP_PAD_PULLUP_CTRL 0x1241444
+#define ACP_PAD_PULLDOWN_CTRL 0x1241448
+#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x124144C
+#define ACP_PAD_SCHMEN_CTRL 0x1241450
+#define ACP_SW_PAD_KEEPER_EN 0x1241454
+#define ACP_SW_WAKE_EN 0x1241458
+#define ACP_I2S_WAKE_EN 0x124145C
+#define ACP_SW1_WAKE_EN 0x1241460
+
+/* Registers from ACP_P1_MISC block */
+#define ACP_EXTERNAL_INTR_ENB 0x1241A00
+#define ACP_EXTERNAL_INTR_CNTL 0x1241A04
+#define ACP_EXTERNAL_INTR_CNTL1 0x1241A08
+#define ACP_EXTERNAL_INTR_STAT 0x1241A0C
+#define ACP_EXTERNAL_INTR_STAT1 0x1241A10
+#define ACP_ERROR_STATUS 0x1241A4C
+#define ACP_P1_SW_I2S_ERROR_REASON 0x1241A50
+#define ACP_P1_SW_POS_TRACK_I2S_TX_CTRL 0x1241A6C
+#define ACP_P1_SW_I2S_TX_DMA_POS 0x1241A70
+#define ACP_P1_SW_POS_TRACK_I2S_RX_CTRL 0x1241A74
+#define ACP_P1_SW_I2S_RX_DMA_POS 0x1241A78
+#define ACP_P1_DMIC_I2S_GPIO_INTR_CTRL 0x1241A7C
+#define ACP_P1_DMIC_I2S_GPIO_INTR_STATUS 0x1241A80
+#define ACP_SCRATCH_REG_BASE_ADDR 0x1241A84
+#define ACP_P1_SW_POS_TRACK_BT_TX_CTRL 0x1241A88
+#define ACP_P1_SW_BT_TX_DMA_POS 0x1241A8C
+#define ACP_P1_SW_POS_TRACK_HS_TX_CTRL 0x1241A90
+#define ACP_P1_SW_HS_TX_DMA_POS 0x1241A94
+#define ACP_P1_SW_POS_TRACK_BT_RX_CTRL 0x1241A98
+#define ACP_P1_SW_BT_RX_DMA_POS 0x1241A9C
+#define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x1241AA0
+#define ACP_P1_SW_HS_RX_DMA_POS 0x1241AA4
+
+/* Registers from ACP_AUDIO_BUFFERS block */
+#define ACP_I2S_RX_RINGBUFADDR 0x1242000
+#define ACP_I2S_RX_RINGBUFSIZE 0x1242004
+#define ACP_I2S_RX_LINKPOSITIONCNTR 0x1242008
+#define ACP_I2S_RX_FIFOADDR 0x124200C
+#define ACP_I2S_RX_FIFOSIZE 0x1242010
+#define ACP_I2S_RX_DMA_SIZE 0x1242014
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018
+#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C
+#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020
+#define ACP_I2S_TX_RINGBUFADDR 0x1242024
+#define ACP_I2S_TX_RINGBUFSIZE 0x1242028
+#define ACP_I2S_TX_LINKPOSITIONCNTR 0x124202C
+#define ACP_I2S_TX_FIFOADDR 0x1242030
+#define ACP_I2S_TX_FIFOSIZE 0x1242034
+#define ACP_I2S_TX_DMA_SIZE 0x1242038
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C
+#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040
+#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044
+#define ACP_BT_RX_RINGBUFADDR 0x1242048
+#define ACP_BT_RX_RINGBUFSIZE 0x124204C
+#define ACP_BT_RX_LINKPOSITIONCNTR 0x1242050
+#define ACP_BT_RX_FIFOADDR 0x1242054
+#define ACP_BT_RX_FIFOSIZE 0x1242058
+#define ACP_BT_RX_DMA_SIZE 0x124205C
+#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060
+#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064
+#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068
+#define ACP_BT_TX_RINGBUFADDR 0x124206C
+#define ACP_BT_TX_RINGBUFSIZE 0x1242070
+#define ACP_BT_TX_LINKPOSITIONCNTR 0x1242074
+#define ACP_BT_TX_FIFOADDR 0x1242078
+#define ACP_BT_TX_FIFOSIZE 0x124207C
+#define ACP_BT_TX_DMA_SIZE 0x1242080
+#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084
+#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088
+#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C
+#define ACP_HS_RX_RINGBUFADDR 0x1242090
+#define ACP_HS_RX_RINGBUFSIZE 0x1242094
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x1242098
+#define ACP_HS_RX_FIFOADDR 0x124209C
+#define ACP_HS_RX_FIFOSIZE 0x12420A0
+#define ACP_HS_RX_DMA_SIZE 0x12420A4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0
+#define ACP_HS_TX_RINGBUFADDR 0x12420B4
+#define ACP_HS_TX_RINGBUFSIZE 0x12420B8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x12420BC
+#define ACP_HS_TX_FIFOADDR 0x12420C0
+#define ACP_HS_TX_FIFOSIZE 0x12420C4
+#define ACP_HS_TX_DMA_SIZE 0x12420C8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4
+
+/* Registers from ACP_I2S_TDM block */
+#define ACP_I2STDM_IER 0x1242400
+#define ACP_I2STDM_IRER 0x1242404
+#define ACP_I2STDM_RXFRMT 0x1242408
+#define ACP_I2STDM_ITER 0x124240C
+#define ACP_I2STDM_TXFRMT 0x1242410
+#define ACP_I2STDM0_MSTRCLKGEN 0x1242414
+#define ACP_I2STDM1_MSTRCLKGEN 0x1242418
+#define ACP_I2STDM2_MSTRCLKGEN 0x124241C
+#define ACP_I2STDM_REFCLKGEN 0x1242420
+
+/* Registers from ACP_BT_TDM block */
+#define ACP_BTTDM_IER 0x1242800
+#define ACP_BTTDM_IRER 0x1242804
+#define ACP_BTTDM_RXFRMT 0x1242808
+#define ACP_BTTDM_ITER 0x124280C
+#define ACP_BTTDM_TXFRMT 0x1242810
+#define ACP_HSTDM_IER 0x1242814
+#define ACP_HSTDM_IRER 0x1242818
+#define ACP_HSTDM_RXFRMT 0x124281C
+#define ACP_HSTDM_ITER 0x1242820
+#define ACP_HSTDM_TXFRMT 0x1242824
+
+/* Registers from ACP_WOV block */
+#define ACP_WOV_PDM_ENABLE 0x1242C04
+#define ACP_WOV_PDM_DMA_ENABLE 0x1242C08
+#define ACP_WOV_RX_RINGBUFADDR 0x1242C0C
+#define ACP_WOV_RX_RINGBUFSIZE 0x1242C10
+#define ACP_WOV_RX_LINKPOSITIONCNTR 0x1242C14
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH 0x1242C18
+#define ACP_WOV_RX_LINEARPOSITIONCNTR_LOW 0x1242C1C
+#define ACP_WOV_RX_INTR_WATERMARK_SIZE 0x1242C20
+#define ACP_WOV_PDM_FIFO_FLUSH 0x1242C24
+#define ACP_WOV_PDM_NO_OF_CHANNELS 0x1242C28
+#define ACP_WOV_PDM_DECIMATION_FACTOR 0x1242C2C
+#define ACP_WOV_PDM_VAD_CTRL 0x1242C30
+#define ACP_WOV_WAKE 0x1242C54
+#define ACP_WOV_BUFFER_STATUS 0x1242C58
+#define ACP_WOV_MISC_CTRL 0x1242C5C
+#define ACP_WOV_CLK_CTRL 0x1242C60
+#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x1242C64
+#define ACP_WOV_ERROR_STATUS_REGISTER 0x1242C68
+#define ACP_PDM_CLKDIV 0x1242C6C
+
+/* Registers from ACP_P1_AUDIO_BUFFERS block */
+#define ACP_P1_I2S_RX_RINGBUFADDR 0x1243A00
+#define ACP_P1_I2S_RX_RINGBUFSIZE 0x1243A04
+#define ACP_P1_I2S_RX_LINKPOSITIONCNTR 0x1243A08
+#define ACP_P1_I2S_RX_FIFOADDR 0x1243A0C
+#define ACP_P1_I2S_RX_FIFOSIZE 0x1243A10
+#define ACP_P1_I2S_RX_DMA_SIZE 0x1243A14
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1243A18
+#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x1243A1C
+#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x1243A20
+#define ACP_P1_I2S_TX_RINGBUFADDR 0x1243A24
+#define ACP_P1_I2S_TX_RINGBUFSIZE 0x1243A28
+#define ACP_P1_I2S_TX_LINKPOSITIONCNTR 0x1243A2C
+#define ACP_P1_I2S_TX_FIFOADDR 0x1243A30
+#define ACP_P1_I2S_TX_FIFOSIZE 0x1243A34
+#define ACP_P1_I2S_TX_DMA_SIZE 0x1243A38
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x1243A3C
+#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1243A40
+#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x1243A44
+#define ACP_P1_BT_RX_RINGBUFADDR 0x1243A48
+#define ACP_P1_BT_RX_RINGBUFSIZE 0x1243A4C
+#define ACP_P1_BT_RX_LINKPOSITIONCNTR 0x1243A50
+#define ACP_P1_BT_RX_FIFOADDR 0x1243A54
+#define ACP_P1_BT_RX_FIFOSIZE 0x1243A58
+#define ACP_P1_BT_RX_DMA_SIZE 0x1243A5C
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1243A60
+#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x1243A64
+#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x1243A68
+#define ACP_P1_BT_TX_RINGBUFADDR 0x1243A6C
+#define ACP_P1_BT_TX_RINGBUFSIZE 0x1243A70
+#define ACP_P1_BT_TX_LINKPOSITIONCNTR 0x1243A74
+#define ACP_P1_BT_TX_FIFOADDR 0x1243A78
+#define ACP_P1_BT_TX_FIFOSIZE 0x1243A7C
+#define ACP_P1_BT_TX_DMA_SIZE 0x1243A80
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1243A84
+#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x1243A88
+#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x1243A8C
+#define ACP_P1_HS_RX_RINGBUFADDR 0x1243A90
+#define ACP_P1_HS_RX_RINGBUFSIZE 0x1243A94
+#define ACP_P1_HS_RX_LINKPOSITIONCNTR 0x1243A98
+#define ACP_P1_HS_RX_FIFOADDR 0x1243A9C
+#define ACP_P1_HS_RX_FIFOSIZE 0x1243AA0
+#define ACP_P1_HS_RX_DMA_SIZE 0x1243AA4
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x1243AA8
+#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x1243AAC
+#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x1243AB0
+#define ACP_P1_HS_TX_RINGBUFADDR 0x1243AB4
+#define ACP_P1_HS_TX_RINGBUFSIZE 0x1243AB8
+#define ACP_P1_HS_TX_LINKPOSITIONCNTR 0x1243ABC
+#define ACP_P1_HS_TX_FIFOADDR 0x1243AC0
+#define ACP_P1_HS_TX_FIFOSIZE 0x1243AC4
+#define ACP_P1_HS_TX_DMA_SIZE 0x1243AC8
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x1243ACC
+#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x1243AD0
+#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x1243AD4
+
+/* Registers from ACP_SCRATCH block */
+#define ACP_SCRATCH_REG_0 0x1250000
+#define ACP_SCRATCH_REG_1 0x1250004
+#define ACP_SCRATCH_REG_2 0x1250008
+#define ACP_SCRATCH_REG_3 0x125000C
+#define ACP_SCRATCH_REG_4 0x1250010
+#define ACP_SCRATCH_REG_5 0x1250014
+#define ACP_SCRATCH_REG_6 0x1250018
+#define ACP_SCRATCH_REG_7 0x125001C
+#define ACP_SCRATCH_REG_8 0x1250020
+#define ACP_SCRATCH_REG_9 0x1250024
+#define ACP_SCRATCH_REG_10 0x1250028
+#define ACP_SCRATCH_REG_11 0x125002C
+#define ACP_SCRATCH_REG_12 0x1250030
+#define ACP_SCRATCH_REG_13 0x1250034
+#define ACP_SCRATCH_REG_14 0x1250038
+#define ACP_SCRATCH_REG_15 0x125003C
+#define ACP_SCRATCH_REG_16 0x1250040
+#define ACP_SCRATCH_REG_17 0x1250044
+#define ACP_SCRATCH_REG_18 0x1250048
+#define ACP_SCRATCH_REG_19 0x125004C
+#define ACP_SCRATCH_REG_20 0x1250050
+#define ACP_SCRATCH_REG_21 0x1250054
+#define ACP_SCRATCH_REG_22 0x1250058
+#define ACP_SCRATCH_REG_23 0x125005C
+#define ACP_SCRATCH_REG_24 0x1250060
+#define ACP_SCRATCH_REG_25 0x1250064
+#define ACP_SCRATCH_REG_26 0x1250068
+#define ACP_SCRATCH_REG_27 0x125006C
+#define ACP_SCRATCH_REG_28 0x1250070
+#define ACP_SCRATCH_REG_29 0x1250074
+#define ACP_SCRATCH_REG_30 0x1250078
+#define ACP_SCRATCH_REG_31 0x125007C
+#define ACP_SCRATCH_REG_32 0x1250080
+#define ACP_SCRATCH_REG_33 0x1250084
+#define ACP_SCRATCH_REG_34 0x1250088
+#define ACP_SCRATCH_REG_35 0x125008C
+#define ACP_SCRATCH_REG_36 0x1250090
+#define ACP_SCRATCH_REG_37 0x1250094
+#define ACP_SCRATCH_REG_38 0x1250098
+#define ACP_SCRATCH_REG_39 0x125009C
+#define ACP_SCRATCH_REG_40 0x12500A0
+#define ACP_SCRATCH_REG_41 0x12500A4
+#define ACP_SCRATCH_REG_42 0x12500A8
+#define ACP_SCRATCH_REG_43 0x12500AC
+#define ACP_SCRATCH_REG_44 0x12500B0
+#define ACP_SCRATCH_REG_45 0x12500B4
+#define ACP_SCRATCH_REG_46 0x12500B8
+#define ACP_SCRATCH_REG_47 0x12500BC
+#define ACP_SCRATCH_REG_48 0x12500C0
+#define ACP_SCRATCH_REG_49 0x12500C4
+#define ACP_SCRATCH_REG_50 0x12500C8
+#define ACP_SCRATCH_REG_51 0x12500CC
+#define ACP_SCRATCH_REG_52 0x12500D0
+#define ACP_SCRATCH_REG_53 0x12500D4
+#define ACP_SCRATCH_REG_54 0x12500D8
+#define ACP_SCRATCH_REG_55 0x12500DC
+#define ACP_SCRATCH_REG_56 0x12500E0
+#define ACP_SCRATCH_REG_57 0x12500E4
+#define ACP_SCRATCH_REG_58 0x12500E8
+#define ACP_SCRATCH_REG_59 0x12500EC
+#define ACP_SCRATCH_REG_60 0x12500F0
+#define ACP_SCRATCH_REG_61 0x12500F4
+#define ACP_SCRATCH_REG_62 0x12500F8
+#define ACP_SCRATCH_REG_63 0x12500FC
+#define ACP_SCRATCH_REG_64 0x1250100
+#define ACP_SCRATCH_REG_65 0x1250104
+#define ACP_SCRATCH_REG_66 0x1250108
+#define ACP_SCRATCH_REG_67 0x125010C
+#define ACP_SCRATCH_REG_68 0x1250110
+#define ACP_SCRATCH_REG_69 0x1250114
+#define ACP_SCRATCH_REG_70 0x1250118
+#define ACP_SCRATCH_REG_71 0x125011C
+#define ACP_SCRATCH_REG_72 0x1250120
+#define ACP_SCRATCH_REG_73 0x1250124
+#define ACP_SCRATCH_REG_74 0x1250128
+#define ACP_SCRATCH_REG_75 0x125012C
+#define ACP_SCRATCH_REG_76 0x1250130
+#define ACP_SCRATCH_REG_77 0x1250134
+#define ACP_SCRATCH_REG_78 0x1250138
+#define ACP_SCRATCH_REG_79 0x125013C
+#define ACP_SCRATCH_REG_80 0x1250140
+#define ACP_SCRATCH_REG_81 0x1250144
+#define ACP_SCRATCH_REG_82 0x1250148
+#define ACP_SCRATCH_REG_83 0x125014C
+#define ACP_SCRATCH_REG_84 0x1250150
+#define ACP_SCRATCH_REG_85 0x1250154
+#define ACP_SCRATCH_REG_86 0x1250158
+#define ACP_SCRATCH_REG_87 0x125015C
+#define ACP_SCRATCH_REG_88 0x1250160
+#define ACP_SCRATCH_REG_89 0x1250164
+#define ACP_SCRATCH_REG_90 0x1250168
+#define ACP_SCRATCH_REG_91 0x125016C
+#define ACP_SCRATCH_REG_92 0x1250170
+#define ACP_SCRATCH_REG_93 0x1250174
+#define ACP_SCRATCH_REG_94 0x1250178
+#define ACP_SCRATCH_REG_95 0x125017C
+#define ACP_SCRATCH_REG_96 0x1250180
+#define ACP_SCRATCH_REG_97 0x1250184
+#define ACP_SCRATCH_REG_98 0x1250188
+#define ACP_SCRATCH_REG_99 0x125018C
+#define ACP_SCRATCH_REG_100 0x1250190
+#define ACP_SCRATCH_REG_101 0x1250194
+#define ACP_SCRATCH_REG_102 0x1250198
+#define ACP_SCRATCH_REG_103 0x125019C
+#define ACP_SCRATCH_REG_104 0x12501A0
+#define ACP_SCRATCH_REG_105 0x12501A4
+#define ACP_SCRATCH_REG_106 0x12501A8
+#define ACP_SCRATCH_REG_107 0x12501AC
+#define ACP_SCRATCH_REG_108 0x12501B0
+#define ACP_SCRATCH_REG_109 0x12501B4
+#define ACP_SCRATCH_REG_110 0x12501B8
+#define ACP_SCRATCH_REG_111 0x12501BC
+#define ACP_SCRATCH_REG_112 0x12501C0
+#define ACP_SCRATCH_REG_113 0x12501C4
+#define ACP_SCRATCH_REG_114 0x12501C8
+#define ACP_SCRATCH_REG_115 0x12501CC
+#define ACP_SCRATCH_REG_116 0x12501D0
+#define ACP_SCRATCH_REG_117 0x12501D4
+#define ACP_SCRATCH_REG_118 0x12501D8
+#define ACP_SCRATCH_REG_119 0x12501DC
+#define ACP_SCRATCH_REG_120 0x12501E0
+#define ACP_SCRATCH_REG_121 0x12501E4
+#define ACP_SCRATCH_REG_122 0x12501E8
+#define ACP_SCRATCH_REG_123 0x12501EC
+#define ACP_SCRATCH_REG_124 0x12501F0
+#define ACP_SCRATCH_REG_125 0x12501F4
+#define ACP_SCRATCH_REG_126 0x12501F8
+#define ACP_SCRATCH_REG_127 0x12501FC
+#define ACP_SCRATCH_REG_128 0x1250200
+#endif
diff --git a/sound/soc/amd/yc/pci-acp6x.c b/sound/soc/amd/yc/pci-acp6x.c
new file mode 100644
index 000000000000..1140ed1cbb3d
--- /dev/null
+++ b/sound/soc/amd/yc/pci-acp6x.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD Yellow Carp ACP PCI Driver
+ *
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+
+#include "acp6x.h"
+
+struct acp6x_dev_data {
+ void __iomem *acp6x_base;
+ struct resource *res;
+ bool acp6x_audio_mode;
+ struct platform_device *pdev[ACP6x_DEVS];
+};
+
+static int acp6x_power_on(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
+ acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int timeout;
+
+ acp6x_writel(1, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ acp6x_writel(0, acp_base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = acp6x_readl(acp_base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static void acp6x_disable_interrupts(void __iomem *acp_base)
+{
+ acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
+ ACP_EXTERNAL_INTR_STAT);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp6x_init(void __iomem *acp_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp6x_power_on(acp_base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ acp6x_writel(0x01, acp_base + ACP_CONTROL);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);
+ acp6x_enable_interrupts(acp_base);
+ return 0;
+}
+
+static int acp6x_deinit(void __iomem *acp_base)
+{
+ int ret;
+
+ acp6x_disable_interrupts(acp_base);
+ /* Reset */
+ ret = acp6x_reset(acp_base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+ acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);
+ acp6x_writel(0x00, acp_base + ACP_CONTROL);
+ return 0;
+}
+
+static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)
+{
+ struct acp6x_dev_data *adata;
+ struct pdm_dev_data *yc_pdm_data;
+ u32 val;
+
+ adata = dev_id;
+ if (!adata)
+ return IRQ_NONE;
+
+ val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (val & BIT(PDM_DMA_STAT)) {
+ yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
+ acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
+ if (yc_pdm_data->capture_stream)
+ snd_pcm_period_elapsed(yc_pdm_data->capture_stream);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int snd_acp6x_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct acp6x_dev_data *adata;
+ struct platform_device_info pdevinfo[ACP6x_DEVS];
+ int index = 0;
+ int val = 0x00;
+ u32 addr;
+ unsigned int irqflags, flag;
+ int ret;
+
+ irqflags = IRQF_SHARED;
+
+ /* Return if acp config flag is defined */
+ flag = snd_amd_acp_find_config(pci);
+ if (flag)
+ return -ENODEV;
+
+ /* Yellow Carp device check */
+ switch (pci->revision) {
+ case 0x60:
+ case 0x6f:
+ break;
+ default:
+ dev_dbg(&pci->dev, "acp6x pci device not found\n");
+ return -ENODEV;
+ }
+ if (pci_enable_device(pci)) {
+ dev_err(&pci->dev, "pci_enable_device failed\n");
+ return -ENODEV;
+ }
+
+ ret = pci_request_regions(pci, "AMD ACP3x audio");
+ if (ret < 0) {
+ dev_err(&pci->dev, "pci_request_regions failed\n");
+ goto disable_pci;
+ }
+
+ adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),
+ GFP_KERNEL);
+ if (!adata) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+
+ addr = pci_resource_start(pci, 0);
+ adata->acp6x_base = devm_ioremap(&pci->dev, addr,
+ pci_resource_len(pci, 0));
+ if (!adata->acp6x_base) {
+ ret = -ENOMEM;
+ goto release_regions;
+ }
+ pci_set_master(pci);
+ pci_set_drvdata(pci, adata);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ goto release_regions;
+ val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);
+ switch (val) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_info(&pci->dev, "Audio Mode %d\n", val);
+ break;
+ default:
+ adata->res = devm_kzalloc(&pci->dev,
+ sizeof(struct resource),
+ GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+
+ adata->res->name = "acp_iomem";
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);
+
+ adata->acp6x_audio_mode = ACP6x_PDM_MODE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo[0].name = "acp_yc_pdm_dma";
+ pdevinfo[0].id = 0;
+ pdevinfo[0].parent = &pci->dev;
+ pdevinfo[0].num_res = 1;
+ pdevinfo[0].res = adata->res;
+
+ pdevinfo[1].name = "dmic-codec";
+ pdevinfo[1].id = 0;
+ pdevinfo[1].parent = &pci->dev;
+
+ pdevinfo[2].name = "acp_yc_mach";
+ pdevinfo[2].id = 0;
+ pdevinfo[2].parent = &pci->dev;
+
+ for (index = 0; index < ACP6x_DEVS; index++) {
+ adata->pdev[index] =
+ platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev, "cannot register %s device\n",
+ pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ break;
+ }
+ ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,
+ irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto unregister_devs;
+ }
+ pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_allow(&pci->dev);
+
+ return 0;
+unregister_devs:
+ for (--index; index >= 0; index--)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (acp6x_deinit(adata->acp6x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+release_regions:
+ pci_release_regions(pci);
+disable_pci:
+ pci_disable_device(pci);
+
+ return ret;
+}
+
+static int snd_acp6x_suspend(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ return ret;
+}
+
+static int snd_acp6x_resume(struct device *dev)
+{
+ struct acp6x_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp6x_init(adata->acp6x_base);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+}
+
+static const struct dev_pm_ops acp6x_pm = {
+ RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
+};
+
+static void snd_acp6x_remove(struct pci_dev *pci)
+{
+ struct acp6x_dev_data *adata;
+ int ret, index;
+
+ adata = pci_get_drvdata(pci);
+ if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {
+ for (index = 0; index < ACP6x_DEVS; index++)
+ platform_device_unregister(adata->pdev[index]);
+ }
+ ret = acp6x_deinit(adata->acp6x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_forbid(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
+ pci_release_regions(pci);
+ pci_disable_device(pci);
+}
+
+static const struct pci_device_id snd_acp6x_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
+ .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
+ .class_mask = 0xffffff },
+ { 0, },
+};
+MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);
+
+static struct pci_driver yc_acp6x_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = snd_acp6x_ids,
+ .probe = snd_acp6x_probe,
+ .remove = snd_acp6x_remove,
+ .driver = {
+ .pm = pm_ptr(&acp6x_pm),
+ }
+};
+
+module_pci_driver(yc_acp6x_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");
+MODULE_LICENSE("GPL v2");