summaryrefslogtreecommitdiff
path: root/sound/soc/amd/acp
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2022-03-21 16:19:21 +0100
committerTakashi Iwai <tiwai@suse.de>2022-03-21 16:19:21 +0100
commit646b907e1559f006c79a752ee3eebe220ceb983d (patch)
tree39e9607c0ab85af410d46d3d8edf8c8f36ea652f /sound/soc/amd/acp
parenta6d4b685026cfe9837b07532db5d1e1681b5d129 (diff)
parent49a24e9d9c740d3bd8b1200f225f67d45e3d68a5 (diff)
Merge tag 'asoc-v5.18' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v5.18 Quite a quiet release for ASoC, lots of work on drivers and platforms but nothing too groundbreaking but not much on the core itself: - Start of moving SoF to support multiple IPC mechanisms. - Use of NHLT ACPI table to reduce the amount of quirking required for Intel systems. - Some building blocks for use in forthcoming Intel AVS driver for legacy Intel DSP firmwares. - Support for AMD PDM, Atmel PDMC, Awinic AW8738, i.MX cards with TLV320AIC31xx, Intel machines with CS35L41 and ESSX8336, Mediatek MT8181 wideband bluetooth, nVidia Tegra234, Qualcomm SC7280, Renesas RZ/V2L, Texas Instruments TAS585M
Diffstat (limited to 'sound/soc/amd/acp')
-rw-r--r--sound/soc/amd/acp/Kconfig10
-rw-r--r--sound/soc/amd/acp/Makefile4
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c38
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c50
-rw-r--r--sound/soc/amd/acp/acp-pci.c160
-rw-r--r--sound/soc/amd/acp/acp-pdm.c193
-rw-r--r--sound/soc/amd/acp/acp-renoir.c189
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c14
-rw-r--r--sound/soc/amd/acp/amd.h23
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h26
10 files changed, 695 insertions, 12 deletions
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index d5838df3064b..626e4a5cb06a 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -15,6 +15,9 @@ config SND_SOC_AMD_ACP_COMMON
if SND_SOC_AMD_ACP_COMMON
+config SND_SOC_AMD_ACP_PDM
+ tristate
+
config SND_SOC_AMD_ACP_I2S
tristate
@@ -22,10 +25,17 @@ 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
+ help
+ This options enables generic PCI driver for ACP device.
+
config SND_AMD_ASOC_RENOIR
tristate "AMD ACP ASOC Renoir Support"
select SND_SOC_AMD_ACP_PCM
select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
depends on X86 && PCI
help
This option enables Renoir I2S support on AMD platform.
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 16c144c2965c..657ddfadf0bb 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -7,6 +7,8 @@
#common acp driver
snd-acp-pcm-objs := acp-platform.o
snd-acp-i2s-objs := acp-i2s.o
+snd-acp-pdm-objs := acp-pdm.o
+snd-acp-pci-objs := acp-pci.o
#platform specific driver
snd-acp-renoir-objs := acp-renoir.o
@@ -18,6 +20,8 @@ snd-acp-sof-mach-objs := acp-sof-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_PCI) += snd-acp-pci.o
obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
index 0ad1cf41b308..5d276365d644 100644
--- a/sound/soc/amd/acp/acp-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -23,13 +23,33 @@
static struct acp_card_drvdata rt5682_rt1019_data = {
.hs_cpu_id = I2S_SP,
.amp_cpu_id = I2S_SP,
- .dmic_cpu_id = NONE,
+ .dmic_cpu_id = DMIC,
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
- .dmic_codec_id = NONE,
+ .dmic_codec_id = DMIC,
.gpio_spkr_en = EN_SPKR_GPIO_GB,
};
+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,
+ .gpio_spkr_en = EN_SPKR_GPIO_NONE,
+};
+
+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,
+ .gpio_spkr_en = EN_SPKR_GPIO_NONE,
+};
+
static const struct snd_kcontrol_new acp_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -96,9 +116,17 @@ static int acp_asoc_probe(struct platform_device *pdev)
static const struct platform_device_id board_ids[] = {
{
- .name = "rn_rt5682_rt1019",
+ .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,
+ },
{ }
};
static struct platform_driver acp_asoc_audio = {
@@ -113,5 +141,7 @@ module_platform_driver(acp_asoc_audio);
MODULE_IMPORT_NS(SND_SOC_AMD_MACH);
MODULE_DESCRIPTION("ACP chrome audio support");
-MODULE_ALIAS("platform:rn_rt5682_rt1019");
+MODULE_ALIAS("platform:acp3xalc56821019");
+MODULE_ALIAS("platform:acp3xalc5682sm98360");
+MODULE_ALIAS("platform:acp3xalc5682s1019");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index cd05ee2802c9..caa202f7864e 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -291,6 +291,32 @@ static const struct snd_soc_ops acp_card_rt5682s_ops = {
.shutdown = acp_card_shutdown,
};
+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"),
@@ -438,6 +464,8 @@ SND_SOC_DAILINK_DEF(sof_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
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")));
int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
{
@@ -556,6 +584,8 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
num_links++;
links = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link) * num_links, GFP_KERNEL);
+ if (!links)
+ return -ENOMEM;
if (drv_data->hs_cpu_id == I2S_SP) {
links[i].name = "acp-headset-codec";
@@ -613,6 +643,26 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
links[i].ops = &acp_card_maxim_ops;
links[i].init = acp_card_maxim_init;
}
+ i++;
+ }
+
+ if (drv_data->dmic_cpu_id == DMIC) {
+ links[i].name = "acp-dmic-codec";
+ 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 = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ links[i].cpus = pdm_dmic;
+ links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ links[i].ops = &acp_card_dmic_ops;
+ links[i].dpcm_capture = 1;
}
card->dai_link = links;
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
new file mode 100644
index 000000000000..340e39d7f420
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -0,0 +1,160 @@
+// 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/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 struct platform_device *dmic_dev;
+static struct platform_device *pdev;
+
+static const struct resource acp3x_res[] = {
+ {
+ .start = 0,
+ .end = ACP3x_REG_END - ACP3x_REG_START,
+ .name = "acp_mem",
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0,
+ .end = 0,
+ .name = "acp_dai_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+ struct platform_device_info pdevinfo;
+ struct device *dev = &pci->dev;
+ const struct resource *res_acp;
+ struct acp_chip_info *chip;
+ struct resource *res;
+ unsigned int flag, addr, num_res, i;
+ int ret;
+
+ flag = snd_amd_acp_find_config(pci);
+ if (flag != FLAG_AMD_LEGACY)
+ return -ENODEV;
+
+ chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ 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");
+ return -ENOMEM;
+ }
+
+ pci_set_master(pci);
+
+ switch (pci->revision) {
+ case 0x01:
+ res_acp = acp3x_res;
+ num_res = ARRAY_SIZE(acp3x_res);
+ chip->name = "acp_asoc_renoir";
+ chip->acp_rev = ACP3X_DEV;
+ break;
+ default:
+ dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
+ return -EINVAL;
+ }
+
+ dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(dmic_dev)) {
+ dev_err(dev, "failed to create DMIC device\n");
+ return PTR_ERR(dmic_dev);
+ }
+
+ addr = pci_resource_start(pci, 0);
+ chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
+
+ res = devm_kzalloc(&pci->dev, sizeof(struct resource) * num_res, GFP_KERNEL);
+ if (!res) {
+ platform_device_unregister(dmic_dev);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_res; i++, res_acp++) {
+ res[i].name = res_acp->name;
+ res[i].flags = res_acp->flags;
+ res[i].start = addr + res_acp->start;
+ res[i].end = addr + res_acp->end;
+ if (res_acp->flags == IORESOURCE_IRQ) {
+ res[i].start = pci->irq;
+ res[i].end = res[i].start;
+ }
+ }
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ pdevinfo.name = chip->name;
+ pdevinfo.id = 0;
+ pdevinfo.parent = &pci->dev;
+ pdevinfo.num_res = num_res;
+ pdevinfo.res = &res[0];
+ pdevinfo.data = chip;
+ pdevinfo.size_data = sizeof(*chip);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
+ platform_device_unregister(dmic_dev);
+ ret = PTR_ERR(pdev);
+ }
+
+ return ret;
+};
+
+static void acp_pci_remove(struct pci_dev *pci)
+{
+ if (dmic_dev)
+ platform_device_unregister(dmic_dev);
+ if (pdev)
+ platform_device_unregister(pdev);
+}
+
+/* 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,
+};
+module_pci_driver(snd_amd_acp_pci_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+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..424c6e0bb9d6
--- /dev/null
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -0,0 +1,193 @@
+// 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"
+
+#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
+
+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_dev_data *adata = dev_get_drvdata(dev);
+ u32 physical_addr, size_dmic, period_bytes;
+ unsigned int dmic_ctrl;
+
+ /* Enable default DMIC clk */
+ writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL);
+ dmic_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL);
+ dmic_ctrl |= PDM_MISC_CTRL_MASK;
+ writel(dmic_ctrl, adata->acp_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);
+
+ physical_addr = stream->reg_offset + MEM_WINDOW_START;
+
+ /* Init DMIC Ring buffer */
+ writel(physical_addr, adata->acp_base + ACP_WOV_RX_RINGBUFADDR);
+ writel(size_dmic, adata->acp_base + ACP_WOV_RX_RINGBUFSIZE);
+ writel(period_bytes, adata->acp_base + ACP_WOV_RX_INTR_WATERMARK_SIZE);
+ writel(0x01, adata->acp_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_dev_data *adata = dev_get_drvdata(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(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if (!(dma_enable & DMA_EN_MASK)) {
+ writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_ENABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ }
+
+ ret = readl_poll_timeout_atomic(adata->acp_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(adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+ if ((dma_enable & DMA_EN_MASK)) {
+ writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_ENABLE);
+ writel(PDM_DISABLE, adata->acp_base + ACP_WOV_PDM_DMA_ENABLE);
+
+ }
+
+ ret = readl_poll_timeout_atomic(adata->acp_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_dev_data *adata = dev_get_drvdata(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;
+ }
+
+ 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, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS);
+ writel(PDM_DEC_64, adata->acp_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_dev_data *adata = dev_get_drvdata(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(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+
+ 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_dev_data *adata = dev_get_drvdata(dev);
+ u32 ext_int_ctrl;
+
+ /* Disable DMIC interrrupts */
+ ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
+ writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+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_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
index 9b321a055b52..75c9229ece97 100644
--- a/sound/soc/amd/acp/acp-renoir.c
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -25,15 +25,46 @@
#define DRV_NAME "acp_asoc_renoir"
+#define ACP_SOFT_RST_DONE_MASK 0x00010001
+
+#define ACP_PWR_ON_MASK 0x01
+#define ACP_PWR_OFF_MASK 0x00
+#define ACP_PGFSM_STAT_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_PWR_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define DELAY_US 5
+#define ACP_TIMEOUT 500
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
static struct snd_soc_acpi_codecs amp_rt1019 = {
.num_codecs = 1,
.codecs = {"10EC1019"}
};
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = {
{
.id = "10EC5682",
- .drv_name = "rn_rt5682_rt1019",
+ .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,
},
@@ -97,13 +128,145 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = {
.ops = &asoc_acp_cpu_dai_ops,
.probe = &asoc_acp_i2s_probe,
},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
};
+static int acp3x_power_on(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + ACP_PGFSM_STATUS);
+
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS)
+ writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp3x_power_off(void __iomem *base)
+{
+ u32 val;
+
+ writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(base + ACP_PGFSM_STATUS, val,
+ (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF,
+ DELAY_US, ACP_TIMEOUT);
+}
+
+static int acp3x_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);
+}
+
+static void acp3x_enable_interrupts(void __iomem *base)
+{
+ u32 ext_intr_ctrl;
+
+ writel(0x01, base + ACP_EXTERNAL_INTR_ENB);
+ ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL);
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp3x_disable_interrupts(void __iomem *base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT);
+ writel(0x00, base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int rn_acp_init(void __iomem *base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp3x_power_on(base);
+ if (ret)
+ return ret;
+
+ writel(0x01, base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp3x_reset(base);
+ if (ret)
+ return ret;
+
+ acp3x_enable_interrupts(base);
+
+ return 0;
+}
+
+static int rn_acp_deinit(void __iomem *base)
+{
+ int ret = 0;
+
+ acp3x_disable_interrupts(base);
+
+ /* Reset */
+ ret = acp3x_reset(base);
+ if (ret)
+ return ret;
+
+ writel(0x00, base + ACP_CONTROL);
+
+ /* power off */
+ ret = acp3x_power_off(base);
+ if (ret)
+ return ret;
+
+ return 0;
+}
static int renoir_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
struct acp_dev_data *adata;
struct resource *res;
+ 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 != ACP3X_DEV) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ ret = rn_acp_init(chip->base);
+ if (ret) {
+ dev_err(&pdev->dev, "ACP Init failed\n");
+ return -EINVAL;
+ }
adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
if (!adata)
@@ -119,13 +282,11 @@ static int renoir_audio_probe(struct platform_device *pdev)
if (!adata->acp_base)
return -ENOMEM;
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
- if (!res) {
- dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
- return -ENODEV;
- }
+ ret = platform_get_irq_byname(pdev, "acp_dai_irq");
+ if (ret < 0)
+ return ret;
+ adata->i2s_irq = ret;
- adata->i2s_irq = res->start;
adata->dev = dev;
adata->dai_driver = acp_renoir_dai;
adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
@@ -142,6 +303,20 @@ static int renoir_audio_probe(struct platform_device *pdev)
static int renoir_audio_remove(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;
+ }
+
+ ret = rn_acp_deinit(chip->base);
+ if (ret) {
+ dev_err(&pdev->dev, "ACP de-init Failed\n");
+ return -EINVAL;
+ }
acp_platform_unregister(dev);
return 0;
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index 4cc431e54fe1..3346677949e3 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -40,6 +40,15 @@ static struct acp_card_drvdata sof_rt5682_max_data = {
.gpio_spkr_en = EN_SPKR_GPIO_NONE,
};
+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,
@@ -126,6 +135,10 @@ static const struct platform_device_id board_ids[] = {
.name = "rt5682s-max",
.driver_data = (kernel_ulong_t)&sof_rt5682s_max_data
},
+ {
+ .name = "rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&sof_rt5682s_rt1019_data
+ },
{ }
};
static struct platform_driver acp_asoc_audio = {
@@ -143,4 +156,5 @@ MODULE_DESCRIPTION("ACP chrome SOF audio support");
MODULE_ALIAS("platform:rt5682-rt1019");
MODULE_ALIAS("platform:rt5682-max");
MODULE_ALIAS("platform:rt5682s-max");
+MODULE_ALIAS("platform:rt5682s-rt1019");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index 8eee3d34774b..8fd38bf4d3bd 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -12,13 +12,19 @@
#define __AMD_ACP_H
#include <sound/pcm.h>
+#include <sound/soc.h>
#include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+
#include "chip_offset_byte.h"
+#define ACP3X_DEV 3
+
#define I2S_SP_INSTANCE 0x00
#define I2S_BT_INSTANCE 0x01
+#define DMIC_INSTANCE 0x02
-#define MEM_WINDOW_START 0x4000000
+#define MEM_WINDOW_START 0x4080000
#define ACP_I2S_REG_START 0x1242400
#define ACP_I2S_REG_END 0x1242810
@@ -38,6 +44,7 @@
#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 PAGE_SIZE_4K_ENABLE 0x2
#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
@@ -68,6 +75,12 @@
#define ACP_MAX_STREAM 6
+struct acp_chip_info {
+ char *name; /* Platform name */
+ unsigned int acp_rev; /* ACP Revision id */
+ void __iomem *base; /* ACP memory PCI base */
+};
+
struct acp_stream {
struct snd_pcm_substream *substream;
int irq_bit;
@@ -96,6 +109,7 @@ struct acp_dev_data {
};
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
+extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
int asoc_acp_i2s_probe(struct snd_soc_dai *dai);
int acp_platform_register(struct device *dev);
@@ -103,6 +117,9 @@ int acp_platform_unregister(struct device *dev);
int acp_machine_select(struct acp_dev_data *adata);
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
{
u64 byte_count, low = 0, high = 0;
@@ -131,6 +148,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
break;
+ case DMIC_INSTANCE:
+ high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
+ break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
return -EINVAL;
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
index c7f77e975dc7..88f6fa597cd6 100644
--- a/sound/soc/amd/acp/chip_offset_byte.h
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -14,6 +14,12 @@
#define ACPAXI2AXI_ATU_CTRL 0xC40
#define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20
#define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24
+
+#define ACP_PGFSM_CONTROL 0x141C
+#define ACP_PGFSM_STATUS 0x1420
+#define ACP_SOFT_RESET 0x1000
+#define ACP_CONTROL 0x1004
+
#define ACP_EXTERNAL_INTR_ENB 0x1800
#define ACP_EXTERNAL_INTR_CNTL 0x1804
#define ACP_EXTERNAL_INTR_STAT 0x1808
@@ -73,4 +79,24 @@
#define ACP_BTTDM_ITER 0x280C
#define ACP_BTTDM_TXFRMT 0x2810
+/* 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
+
#endif