diff options
Diffstat (limited to 'sound/soc/amd')
-rw-r--r-- | sound/soc/amd/Kconfig | 5 | ||||
-rw-r--r-- | sound/soc/amd/acp-config.c | 33 | ||||
-rw-r--r-- | sound/soc/amd/acp/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/amd/acp/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-i2s.c | 2 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-legacy-common.c | 347 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-mach-common.c | 267 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-mach.h | 2 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-pci.c | 57 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-pdm.c | 13 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-platform.c | 6 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-rembrandt.c | 184 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-renoir.c | 115 | ||||
-rw-r--r-- | sound/soc/amd/acp/acp-sof-mach.c | 16 | ||||
-rw-r--r-- | sound/soc/amd/acp/amd.h | 51 | ||||
-rw-r--r-- | sound/soc/amd/mach-config.h | 1 | ||||
-rw-r--r-- | sound/soc/amd/ps/ps-sdw-dma.c | 5 | ||||
-rw-r--r-- | sound/soc/amd/vangogh/acp5x-mach.c | 149 |
18 files changed, 984 insertions, 279 deletions
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 1dd8579e8034..273688c05317 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -79,14 +79,15 @@ config SND_SOC_AMD_ACP5x ACP DMA driver, CPU DAI driver. config SND_SOC_AMD_VANGOGH_MACH - tristate "AMD Vangogh support for NAU8821 CS35L41" + 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 CS35L41 codecs. + using NAU8821 and either CS35L41 or MAX98388 codecs. Say m if you have such a device. If unsure select "N". diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 0932473b6394..f27c27580009 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -47,6 +47,20 @@ 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, "Valve"), + DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"), + }, + }, + {} + }, + }, }; int snd_amd_acp_find_config(struct pci_dev *pci) @@ -82,6 +96,11 @@ static struct snd_soc_acpi_codecs amp_max = { .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", @@ -130,6 +149,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_sof_machines[] = { }; 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 = &_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", diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig index ce0037810743..631cdf96d637 100644 --- a/sound/soc/amd/acp/Kconfig +++ b/sound/soc/amd/acp/Kconfig @@ -18,6 +18,9 @@ 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 @@ -27,6 +30,7 @@ config SND_SOC_AMD_ACP_PCM config SND_SOC_AMD_ACP_PCI tristate "AMD ACP PCI Driver Support" + select SND_SOC_AMD_ACP_LEGACY_COMMON depends on X86 && PCI help This options enables generic PCI driver for ACP device. @@ -36,6 +40,7 @@ config SND_AMD_ASOC_RENOIR 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 depends on X86 && PCI help This option enables Renoir I2S support on AMD platform. @@ -45,6 +50,7 @@ config SND_AMD_ASOC_REMBRANDT 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 depends on X86 && PCI help This option enables Rembrandt I2S support on AMD platform. @@ -61,6 +67,8 @@ config SND_SOC_AMD_MACH_COMMON 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. diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile index d9abb0ee5218..4e65fdbc8dca 100644 --- a/sound/soc/amd/acp/Makefile +++ b/sound/soc/amd/acp/Makefile @@ -8,6 +8,7 @@ snd-acp-pcm-objs := acp-platform.o snd-acp-i2s-objs := acp-i2s.o snd-acp-pdm-objs := acp-pdm.o +snd-acp-legacy-common-objs := acp-legacy-common.o snd-acp-pci-objs := acp-pci.o #platform specific driver @@ -22,6 +23,7 @@ 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_LEGACY_COMMON) += snd-acp-legacy-common.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-i2s.c b/sound/soc/amd/acp/acp-i2s.c index 09b6511c0a26..09dc5f2c0bfc 100644 --- a/sound/soc/amd/acp/acp-i2s.c +++ b/sound/soc/amd/acp/acp-i2s.c @@ -149,6 +149,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ dev_err(dev, "Invalid dai id %x\n", dai->driver->id); return -EINVAL; } + adata->xfer_tx_resolution[dai->driver->id - 1] = xfer_resolution; } else { switch (dai->driver->id) { case I2S_BT_INSTANCE: @@ -167,6 +168,7 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_ dev_err(dev, "Invalid dai id %x\n", dai->driver->id); return -EINVAL; } + adata->xfer_rx_resolution[dai->driver->id - 1] = xfer_resolution; } val = readl(adata->acp_base + reg_val); 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..ba58165cc6e6 --- /dev/null +++ b/sound/soc/amd/acp/acp-legacy-common.c @@ -0,0 +1,347 @@ +// 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/pci.h> +#include <linux/export.h> + +void acp_enable_interrupts(struct acp_dev_data *adata) +{ + struct acp_resource *rsrc = adata->rsrc; + u32 ext_intr_ctrl; + + writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); + ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); + ext_intr_ctrl |= ACP_ERROR_MASK; + writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); +} +EXPORT_SYMBOL_NS_GPL(acp_enable_interrupts, SND_SOC_ACP_COMMON); + +void acp_disable_interrupts(struct acp_dev_data *adata) +{ + struct acp_resource *rsrc = adata->rsrc; + + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); + writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); +} +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_dev_data *adata = dev_get_drvdata(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, adata->acp_base + ACP_WOV_RX_RINGBUFADDR); + writel(pdm_size, 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); +} + +static void set_acp_pdm_clk(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); + unsigned int pdm_ctrl; + + /* Enable default ACP PDM clk */ + writel(PDM_CLK_FREQ_MASK, adata->acp_base + ACP_WOV_CLK_CTRL); + pdm_ctrl = readl(adata->acp_base + ACP_WOV_MISC_CTRL); + pdm_ctrl |= PDM_MISC_CTRL_MASK; + writel(pdm_ctrl, adata->acp_base + ACP_WOV_MISC_CTRL); + set_acp_pdm_ring_buffer(substream, dai); +} + +void restore_acp_pdm_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata) +{ + struct snd_soc_dai *dai; + struct snd_soc_pcm_runtime *soc_runtime; + u32 ext_int_ctrl; + + soc_runtime = asoc_substream_to_rtd(substream); + dai = asoc_rtd_to_cpu(soc_runtime, 0); + /* Programming channel mask and sampling rate */ + writel(adata->ch_mask, adata->acp_base + ACP_WOV_PDM_NO_OF_CHANNELS); + writel(PDM_DEC_64, adata->acp_base + ACP_WOV_PDM_DECIMATION_FACTOR); + + /* Enabling ACP Pdm interuppts */ + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0)); + ext_int_ctrl |= PDM_DMA_INTR_MASK; + writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 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_dev_data *adata = dev_get_drvdata(dev); + struct acp_resource *rsrc = adata->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; + acp_fifo_addr = rsrc->sram_pte_offset + + SP_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_I2S_TX_FIFOADDR; + reg_fifo_size = ACP_I2S_TX_FIFOSIZE; + phy_addr = I2S_SP_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR); + } else { + reg_dma_size = ACP_I2S_RX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + SP_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_I2S_RX_FIFOADDR; + reg_fifo_size = ACP_I2S_RX_FIFOSIZE; + phy_addr = I2S_SP_RX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_I2S_RX_RINGBUFADDR); + } + break; + case I2S_BT_INSTANCE: + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + reg_dma_size = ACP_BT_TX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + BT_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_BT_TX_FIFOADDR; + reg_fifo_size = ACP_BT_TX_FIFOSIZE; + phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR); + } else { + reg_dma_size = ACP_BT_RX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + BT_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_BT_RX_FIFOADDR; + reg_fifo_size = ACP_BT_RX_FIFOSIZE; + phy_addr = I2S_BT_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR); + } + break; + case I2S_HS_INSTANCE: + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + reg_dma_size = ACP_HS_TX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + HS_PB_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_HS_TX_FIFOADDR; + reg_fifo_size = ACP_HS_TX_FIFOSIZE; + phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR); + } else { + reg_dma_size = ACP_HS_RX_DMA_SIZE; + acp_fifo_addr = rsrc->sram_pte_offset + + HS_CAPT_FIFO_ADDR_OFFSET; + reg_fifo_addr = ACP_HS_RX_FIFOADDR; + reg_fifo_size = ACP_HS_RX_FIFOSIZE; + phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset; + writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR); + } + break; + default: + dev_err(dev, "Invalid dai id %x\n", dai->driver->id); + return -EINVAL; + } + + writel(DMA_SIZE, adata->acp_base + reg_dma_size); + writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr); + writel(FIFO_SIZE, adata->acp_base + reg_fifo_size); + + ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 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(adata, rsrc->irqp_used)); + return 0; +} + +int restore_acp_i2s_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata, + 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 = asoc_substream_to_rtd(substream); + dai = asoc_rtd_to_cpu(soc_runtime, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + tdm_fmt = adata->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 = adata->xfer_tx_resolution[stream->dai_id - 1] << 3; + } else { + tdm_fmt = adata->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 = adata->xfer_rx_resolution[stream->dai_id - 1] << 3; + } + writel(val, adata->acp_base + reg_val); + if (adata->tdm_mode == TDM_ENABLE) { + writel(tdm_fmt, adata->acp_base + fmt_reg); + val = readl(adata->acp_base + reg_val); + writel(val | 0x2, adata->acp_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 ACP3X_DEV: + acp_pgfsm_stat_reg = ACP_PGFSM_STATUS; + acp_pgfsm_ctrl_reg = ACP_PGFSM_CONTROL; + break; + case ACP6X_DEV: + acp_pgfsm_stat_reg = ACP6X_PGFSM_STATUS; + acp_pgfsm_ctrl_reg = ACP6X_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; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp_init, SND_SOC_ACP_COMMON); + +int acp_deinit(void __iomem *base) +{ + int ret; + + /* Reset */ + ret = acp_reset(base); + if (ret) + return ret; + + writel(0, base + ACP_CONTROL); + return 0; +} +EXPORT_SYMBOL_NS_GPL(acp_deinit, SND_SOC_ACP_COMMON); + +int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data) +{ + pci_write_config_dword(dev, 0x60, smn_addr); + pci_write_config_dword(dev, 0x64, data); + return 0; +} +EXPORT_SYMBOL_NS_GPL(smn_write, SND_SOC_ACP_COMMON); + +int smn_read(struct pci_dev *dev, u32 smn_addr) +{ + u32 data; + + pci_write_config_dword(dev, 0x60, smn_addr); + pci_read_config_dword(dev, 0x64, &data); + return data; +} +EXPORT_SYMBOL_NS_GPL(smn_read, SND_SOC_ACP_COMMON); + +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c index 6da17140beea..ff5cbc4a6427 100644 --- a/sound/soc/amd/acp/acp-mach-common.c +++ b/sound/soc/amd/acp/acp-mach-common.c @@ -25,12 +25,18 @@ #include "../../codecs/rt1019.h" #include "../../codecs/rt5682s.h" #include "../../codecs/nau8825.h" +#include "../../codecs/nau8821.h" #include "acp-mach.h" +static struct snd_soc_jack vg_headset; #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 @@ -661,6 +667,97 @@ static const struct snd_soc_ops acp_card_maxim_ops = { .hw_params = acp_card_maxim_hw_params, }; +SND_SOC_DAILINK_DEF(max98388, + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ADS8388:00", "max98388-aif1"), + COMP_CODEC("i2c-ADS8388:01", "max98388-aif1"))); + +static const struct snd_soc_dapm_widget max98388_widgets[] = { + SND_SOC_DAPM_SPK("SPK", NULL), +}; + +static const struct snd_soc_dapm_route max98388_map[] = { + { "SPK", NULL, "Left BE_OUT" }, + { "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; + } + 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_CBS_CFS | 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"))); @@ -790,6 +887,162 @@ static const struct snd_soc_ops acp_card_nau8825_ops = { .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 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 = asoc_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_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, + &vg_headset); + 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_PLAYPAUSE); + snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(vg_headset.jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + nau8821_enable_jack_detect(component, &vg_headset); + + 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 = asoc_substream_to_rtd(substream); + struct snd_soc_card *card = rtd->card; + struct acp_card_drvdata *drvdata = card->drvdata; + struct snd_soc_dai *codec_dai = asoc_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-hifi"))); + /* Declare DMIC codec components */ SND_SOC_DAILINK_DEF(dmic_codec, DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec", "dmic-hifi"))); @@ -920,6 +1173,12 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) 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++; } @@ -1007,6 +1266,14 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card) links[i].ops = &acp_card_maxim_ops; links[i].init = acp_card_maxim_init; } + if (drv_data->amp_codec_id == MAX98388) { + 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); diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h index 165f407697c0..2b3ec6594023 100644 --- a/sound/soc/amd/acp/acp-mach.h +++ b/sound/soc/amd/acp/acp-mach.h @@ -41,6 +41,8 @@ enum codec_endpoints { MAX98360A, RT5682S, NAU8825, + NAU8821, + MAX98388, }; enum platform_end_point { diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c index 8154fbfd1229..a32c14a109b7 100644 --- a/sound/soc/amd/acp/acp-pci.c +++ b/sound/soc/amd/acp/acp-pci.c @@ -16,6 +16,7 @@ #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" @@ -106,6 +107,7 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id goto unregister_dmic_dev; } + acp_init(chip); res = devm_kcalloc(&pci->dev, num_res, sizeof(struct resource), GFP_KERNEL); if (!res) { ret = -ENOMEM; @@ -139,7 +141,12 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id ret = PTR_ERR(pdev); goto unregister_dmic_dev; } - + chip->chip_pdev = pdev; + 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; unregister_dmic_dev: @@ -152,12 +159,56 @@ disable_pci: return ret; }; +static int __maybe_unused snd_acp_suspend(struct device *dev) +{ + struct acp_chip_info *chip; + int ret; + + chip = dev_get_drvdata(dev); + ret = acp_deinit(chip->base); + if (ret) + dev_err(dev, "ACP de-init failed\n"); + return ret; +} + +static int __maybe_unused snd_acp_resume(struct device *dev) +{ + struct acp_chip_info *chip; + struct acp_dev_data *adata; + struct device child; + int ret; + + chip = dev_get_drvdata(dev); + ret = acp_init(chip); + if (ret) + dev_err(dev, "ACP init failed\n"); + child = chip->chip_pdev->dev; + adata = dev_get_drvdata(&child); + if (adata) + acp_enable_interrupts(adata); + return ret; +} + +static const struct dev_pm_ops acp_pm_ops = { + SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL) + SET_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 (dmic_dev) platform_device_unregister(dmic_dev); if (pdev) platform_device_unregister(pdev); + ret = acp_deinit(chip->base); + if (ret) + dev_err(&pci->dev, "ACP de-init failed\n"); } /* PCI IDs */ @@ -173,8 +224,12 @@ static struct pci_driver snd_amd_acp_pci_driver = { .id_table = acp_pci_ids, .probe = acp_pci_probe, .remove = acp_pci_remove, + .driver = { + .pm = &acp_pm_ops, + }, }; module_pci_driver(snd_amd_acp_pci_driver); 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 index f8030b79ac17..f754bf79b5e3 100644 --- a/sound/soc/amd/acp/acp-pdm.c +++ b/sound/soc/amd/acp/acp-pdm.c @@ -25,18 +25,6 @@ #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) { @@ -135,6 +123,7 @@ static int acp_dmic_hwparams(struct snd_pcm_substream *substream, return -EINVAL; } + adata->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; diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c index f220378ec20e..f516daf6fef4 100644 --- a/sound/soc/amd/acp/acp-platform.c +++ b/sound/soc/amd/acp/acp-platform.c @@ -127,7 +127,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *data) return IRQ_NONE; } -static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream) +void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream) { struct acp_resource *rsrc = adata->rsrc; u32 pte_reg, pte_size, reg_val; @@ -143,8 +143,9 @@ static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size); writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL); } +EXPORT_SYMBOL_NS_GPL(config_pte_for_stream, SND_SOC_ACP_COMMON); -static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size) +void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size) { struct snd_pcm_substream *substream = stream->substream; struct acp_resource *rsrc = adata->rsrc; @@ -168,6 +169,7 @@ static void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream 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) { diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c index 1b997837c7d8..cc8284f417c0 100644 --- a/sound/soc/amd/acp/acp-rembrandt.c +++ b/sound/soc/amd/acp/acp-rembrandt.c @@ -19,30 +19,17 @@ #include <sound/soc.h> #include <sound/soc-dai.h> #include <linux/dma-mapping.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> #include "amd.h" #define DRV_NAME "acp_asoc_rembrandt" -#define ACP6X_PGFSM_CONTROL 0x1024 -#define ACP6X_PGFSM_STATUS 0x1028 - -#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001 - -#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 -#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 -#define ACP_PGFSM_STATUS_MASK 0x03 -#define ACP_POWERED_ON 0x00 -#define ACP_POWER_ON_IN_PROGRESS 0x01 -#define ACP_POWERED_OFF 0x02 -#define ACP_POWER_OFF_IN_PROGRESS 0x03 - -#define ACP_ERROR_MASK 0x20000000 -#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF - - -static int rmb_acp_init(void __iomem *base); -static int rmb_acp_deinit(void __iomem *base); +#define MP1_C2PMSG_69 0x3B10A14 +#define MP1_C2PMSG_85 0x3B10A54 +#define MP1_C2PMSG_93 0x3B10A74 +#define HOST_BRIDGE_ID 0x14B5 static struct acp_resource rsrc = { .offset = 0, @@ -180,108 +167,22 @@ static struct snd_soc_dai_driver acp_rmb_dai[] = { }, }; -static int acp6x_power_on(void __iomem *base) -{ - u32 val; - int timeout; - - val = readl(base + ACP6X_PGFSM_STATUS); - - if (val == ACP_POWERED_ON) - return 0; - - if ((val & ACP_PGFSM_STATUS_MASK) != - ACP_POWER_ON_IN_PROGRESS) - writel(ACP_PGFSM_CNTL_POWER_ON_MASK, - base + ACP6X_PGFSM_CONTROL); - timeout = 0; - while (++timeout < 500) { - val = readl(base + ACP6X_PGFSM_STATUS); - if (!val) - return 0; - udelay(1); - } - return -ETIMEDOUT; -} - -static int acp6x_reset(void __iomem *base) +static int acp6x_master_clock_generate(struct device *dev) { - u32 val; - int timeout; - - writel(1, base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = readl(base + ACP_SOFT_RESET); - if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK) - break; - cpu_relax(); - } - writel(0, base + ACP_SOFT_RESET); - timeout = 0; - while (++timeout < 500) { - val = readl(base + ACP_SOFT_RESET); - if (!val) - return 0; - cpu_relax(); - } - return -ETIMEDOUT; -} - -static void acp6x_enable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - u32 ext_intr_ctrl; - - writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); - ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); - ext_intr_ctrl |= ACP_ERROR_MASK; - writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); -} - -static void acp6x_disable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - - writel(ACP_EXT_INTR_STAT_CLEAR_MASK, - ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); -} + int data = 0; + struct pci_dev *smn_dev; -static int rmb_acp_init(void __iomem *base) -{ - int ret; - - /* power on */ - ret = acp6x_power_on(base); - if (ret) { - pr_err("ACP power on failed\n"); - return ret; - } - writel(0x01, base + ACP_CONTROL); - - /* Reset */ - ret = acp6x_reset(base); - if (ret) { - pr_err("ACP reset failed\n"); - return ret; - } - - return 0; -} - -static int rmb_acp_deinit(void __iomem *base) -{ - int ret = 0; - - /* Reset */ - ret = acp6x_reset(base); - if (ret) { - pr_err("ACP reset failed\n"); - return ret; + smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, HOST_BRIDGE_ID, NULL); + if (!smn_dev) { + dev_err(dev, "Failed to get host bridge device\n"); + return -ENODEV; } - writel(0x00, base + ACP_CONTROL); + smn_write(smn_dev, MP1_C2PMSG_93, 0); + smn_write(smn_dev, MP1_C2PMSG_85, 0xC4); + smn_write(smn_dev, MP1_C2PMSG_69, 0x4); + read_poll_timeout(smn_read, data, data, DELAY_US, + ACP_TIMEOUT, false, smn_dev, MP1_C2PMSG_93); return 0; } @@ -303,8 +204,6 @@ static int rembrandt_audio_probe(struct platform_device *pdev) return -ENODEV; } - rmb_acp_init(chip->base); - adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); if (!adata) return -ENOMEM; @@ -335,9 +234,14 @@ static int rembrandt_audio_probe(struct platform_device *pdev) acp_machine_select(adata); dev_set_drvdata(dev, adata); - acp6x_enable_interrupts(adata); + acp6x_master_clock_generate(dev); + acp_enable_interrupts(adata); 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; } @@ -345,19 +249,49 @@ static void rembrandt_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_chip_info *chip = dev_get_platdata(dev); - - rmb_acp_deinit(chip->base); - acp6x_disable_interrupts(adata); + acp_disable_interrupts(adata); acp_platform_unregister(dev); + pm_runtime_disable(&pdev->dev); } +static int __maybe_unused rmb_pcm_resume(struct device *dev) +{ + struct acp_dev_data *adata = dev_get_drvdata(dev); + struct acp_stream *stream; + struct snd_pcm_substream *substream; + snd_pcm_uframes_t buf_in_frames; + u64 buf_size; + + acp6x_master_clock_generate(dev); + spin_lock(&adata->acp_lock); + list_for_each_entry(stream, &adata->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(adata, stream); + config_acp_dma(adata, stream, buf_size); + if (stream->dai_id) + restore_acp_i2s_params(substream, adata, stream); + else + restore_acp_pdm_params(substream, adata); + } + } + spin_unlock(&adata->acp_lock); + return 0; +} + +static const struct dev_pm_ops rmb_dma_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume) +}; + static struct platform_driver rembrandt_driver = { .probe = rembrandt_audio_probe, .remove_new = rembrandt_audio_remove, .driver = { .name = "acp_asoc_rembrandt", + .pm = &rmb_dma_pm_ops, }, }; diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index f188365fe214..1899658ab25d 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -25,20 +25,6 @@ #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 acp_resource rsrc = { .offset = 20, .no_of_ctrls = 1, @@ -154,89 +140,7 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { }, }; -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_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(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - u32 ext_intr_ctrl; - - writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); - ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); - ext_intr_ctrl |= ACP_ERROR_MASK; - writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); -} - -static void acp3x_disable_interrupts(struct acp_dev_data *adata) -{ - struct acp_resource *rsrc = adata->rsrc; - - writel(ACP_EXT_INTR_STAT_CLEAR_MASK, - ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); - writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); -} -static int 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; - - return 0; -} - -static int rn_acp_deinit(void __iomem *base) -{ - int ret = 0; - - /* Reset */ - ret = acp3x_reset(base); - if (ret) - return ret; - - writel(0x00, base + ACP_CONTROL); - return 0; -} static int renoir_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -256,12 +160,6 @@ static int renoir_audio_probe(struct platform_device *pdev) 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) return -ENOMEM; @@ -290,7 +188,7 @@ static int renoir_audio_probe(struct platform_device *pdev) acp_machine_select(adata); dev_set_drvdata(dev, adata); - acp3x_enable_interrupts(adata); + acp_enable_interrupts(adata); acp_platform_register(dev); return 0; @@ -300,17 +198,8 @@ static void renoir_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct acp_dev_data *adata = dev_get_drvdata(dev); - struct acp_chip_info *chip; - int ret; - - chip = dev_get_platdata(&pdev->dev); - - acp3x_disable_interrupts(adata); - - ret = rn_acp_deinit(chip->base); - if (ret) - dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret)); + acp_disable_interrupts(adata); acp_platform_unregister(dev); } diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c index 99a7d3879340..a1c893f33f74 100644 --- a/sound/soc/amd/acp/acp-sof-mach.c +++ b/sound/soc/amd/acp/acp-sof-mach.c @@ -83,6 +83,17 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = { .tdm_mode = false, }; +static struct acp_card_drvdata sof_nau8821_max98388_data = { + .hs_cpu_id = I2S_SP, + .amp_cpu_id = I2S_HS, + .dmic_cpu_id = NONE, + .hs_codec_id = NAU8821, + .amp_codec_id = MAX98388, + .dmic_codec_id = NONE, + .soc_mclk = true, + .tdm_mode = false, +}; + static const struct snd_kcontrol_new acp_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -166,6 +177,10 @@ static const struct platform_device_id board_ids[] = { .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 + }, { } }; static struct platform_driver acp_asoc_audio = { @@ -187,4 +202,5 @@ MODULE_ALIAS("platform:rt5682s-max"); MODULE_ALIAS("platform:rt5682s-rt1019"); MODULE_ALIAS("platform:nau8825-max"); MODULE_ALIAS("platform:rt5682s-hs-rt1019"); +MODULE_ALIAS("platform:nau8821-max"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h index 12a176a50fd6..2ebe2099cbb5 100644 --- a/sound/soc/amd/acp/amd.h +++ b/sound/soc/amd/acp/amd.h @@ -92,10 +92,43 @@ #define SLOT_WIDTH_24 0x18 #define SLOT_WIDTH_32 0x20 +#define ACP6X_PGFSM_CONTROL 0x1024 +#define ACP6X_PGFSM_STATUS 0x1028 + +#define ACP_SOFT_RST_DONE_MASK 0x00010001 + +#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01 +#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00 +#define ACP_PGFSM_STATUS_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_POWER_ON_IN_PROGRESS 0x01 +#define ACP_POWERED_OFF 0x02 +#define ACP_POWER_OFF_IN_PROGRESS 0x03 + +#define ACP_ERROR_MASK 0x20000000 +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xffffffff + +#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 */ unsigned int acp_rev; /* ACP Revision id */ void __iomem *base; /* ACP memory PCI base */ + struct platform_device *chip_pdev; }; struct acp_stream { @@ -144,8 +177,11 @@ struct acp_dev_data { u32 lrclk_div; struct acp_resource *rsrc; + u32 ch_mask; u32 tdm_tx_fmt[3]; u32 tdm_rx_fmt[3]; + u32 xfer_tx_resolution[3]; + u32 xfer_rx_resolution[3]; }; union acp_i2stdm_mstrclkgen { @@ -168,9 +204,24 @@ int acp_platform_unregister(struct device *dev); int acp_machine_select(struct acp_dev_data *adata); +int smn_read(struct pci_dev *dev, u32 smn_addr); +int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data); + +int acp_init(struct acp_chip_info *chip); +int acp_deinit(void __iomem *base); +void acp_enable_interrupts(struct acp_dev_data *adata); +void acp_disable_interrupts(struct acp_dev_data *adata); /* Machine configuration */ int snd_amd_acp_find_config(struct pci_dev *pci); +void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream); +void config_acp_dma(struct acp_dev_data *adata, struct acp_stream *stream, int size); +void restore_acp_pdm_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata); + +int restore_acp_i2s_params(struct snd_pcm_substream *substream, + struct acp_dev_data *adata, struct acp_stream *stream); + static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction) { u64 byte_count = 0, low = 0, high = 0; diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h index 7b4c625da40d..d392e6d6e6e1 100644 --- a/sound/soc/amd/mach-config.h +++ b/sound/soc/amd/mach-config.h @@ -20,6 +20,7 @@ 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[]; struct config_entry { u32 flags; diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 324c80fca672..6230d1b12225 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -488,10 +488,9 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev) return 0; } -static int acp63_sdw_platform_remove(struct platform_device *pdev) +static void acp63_sdw_platform_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - return 0; } static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data) @@ -552,7 +551,7 @@ static const struct dev_pm_ops acp63_pm_ops = { static struct platform_driver acp63_sdw_dma_driver = { .probe = acp63_sdw_platform_probe, - .remove = acp63_sdw_platform_remove, + .remove_new = acp63_sdw_platform_remove, .driver = { .name = "amd_ps_sdw_dma", .pm = &acp63_pm_ops, diff --git a/sound/soc/amd/vangogh/acp5x-mach.c b/sound/soc/amd/vangogh/acp5x-mach.c index e5bcd1e6eb73..125a8e93478d 100644 --- a/sound/soc/amd/vangogh/acp5x-mach.c +++ b/sound/soc/amd/vangogh/acp5x-mach.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Machine driver for AMD Vangogh platform using NAU8821 & CS35L41 - * codecs. + * Machine driver for AMD Vangogh platform using either + * NAU8821 & CS35L41 or NAU8821 & MAX98388 codecs. * * Copyright 2021 Advanced Micro Devices, Inc. */ @@ -22,7 +22,6 @@ #define DRV_NAME "acp5x_mach" #define DUAL_CHANNEL 2 -#define VG_JUPITER 1 #define ACP5X_NAU8821_BCLK 3072000 #define ACP5X_NAU8821_FREQ_OUT 12288000 #define ACP5X_NAU8821_COMP_NAME "i2c-NVTN2020:00" @@ -30,8 +29,10 @@ #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 unsigned long acp5x_machine_id; static struct snd_soc_jack vg_headset; SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("acp5x_i2s_dma.0"))); @@ -242,7 +243,6 @@ static int acp5x_cs35l41_hw_params(struct snd_pcm_substream *substream, } return 0; - } static const struct snd_soc_ops acp5x_cs35l41_play_ops = { @@ -292,8 +292,6 @@ static struct snd_soc_dai_link acp5x_8821_35l41_dai[] = { }, }; - - static const struct snd_soc_dapm_widget acp5x_8821_35l41_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), @@ -331,16 +329,110 @@ static struct snd_soc_card acp5x_8821_35l41_card = { .num_controls = ARRAY_SIZE(acp5x_8821_controls), }; -static int acp5x_vg_quirk_cb(const struct dmi_system_id *id) +static int acp5x_max98388_startup(struct snd_pcm_substream *substream) { - acp5x_machine_id = VG_JUPITER; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct acp5x_platform_info *machine = snd_soc_card_get_drvdata(rtd->card); + struct snd_pcm_runtime *runtime = substream->runtime; - return 1; + 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, + .dpcm_playback = 1, + .dpcm_capture = 1, + .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, + .dpcm_playback = 1, + .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[] = { { - .callback = acp5x_vg_quirk_cb, .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Valve"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"), @@ -351,23 +443,31 @@ static const struct dmi_system_id acp5x_vg_quirk_table[] = { 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; + card = (struct snd_soc_card *)device_get_match_data(dev); + if (!card) { + /* + * This is normally the result of directly probing the driver + * in pci-acp5x through platform_device_register_full(), which + * is necessary for the CS35L41 variant, as it doesn't support + * ACPI probing and relies on DMI quirks. + */ + dmi_id = dmi_first_match(acp5x_vg_quirk_table); + if (!dmi_id) + return -ENODEV; + + card = &acp5x_8821_35l41_card; + } + machine = devm_kzalloc(dev, sizeof(*machine), GFP_KERNEL); if (!machine) return -ENOMEM; - dmi_check_system(acp5x_vg_quirk_table); - switch (acp5x_machine_id) { - case VG_JUPITER: - card = &acp5x_8821_35l41_card; - break; - default: - return -ENODEV; - } card->dev = dev; platform_set_drvdata(pdev, card); snd_soc_card_set_drvdata(card, machine); @@ -379,10 +479,17 @@ static int acp5x_probe(struct platform_device *pdev) return 0; } +static const struct acpi_device_id acp5x_acpi_match[] = { + { "AMDI8821", (kernel_ulong_t)&acp5x_8821_98388_card }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acp5x_acpi_match); + static struct platform_driver acp5x_mach_driver = { .driver = { - .name = "acp5x_mach", + .name = DRV_NAME, .pm = &snd_soc_pm_ops, + .acpi_match_table = acp5x_acpi_match, }, .probe = acp5x_probe, }; @@ -390,6 +497,6 @@ static struct platform_driver acp5x_mach_driver = { module_platform_driver(acp5x_mach_driver); MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); -MODULE_DESCRIPTION("NAU8821 & CS35L41 audio support"); +MODULE_DESCRIPTION("NAU8821/CS35L41 & NAU8821/MAX98388 audio support"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); |