Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
# 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
config SND_SOC_ACPI_AMD_SDCA_QUIRKS
bool "AMD ACPI SDCA quirks"
depends on SND_SOC_ACPI_AMD_MATCH
depends on SND_SOC_SDCA
depends on SND_SOC_ACPI_AMD_MATCH = m || SND_SOC_SDCA = y
default y
help
Enable SDCA quirk support for AMD ACPI match tables.
This is compiled into the snd-soc-acpi-amd-match module.
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 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
select SND_SOC_RT722_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.
+51
View File
@@ -0,0 +1,51 @@
# 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-soc-acpi-amd-match-$(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS) += soc-acpi-amd-sdca-quirks.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
+701
View File
@@ -0,0 +1,701 @@
// 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:
case ACP72_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_drvdata(dev->parent);
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:
case ACP72_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) {
if (dai->id != stream->dai_id)
continue;
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:
case ACP72_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_drvdata(dev->parent);
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_drvdata(dev->parent);
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_drvdata(dev->parent);
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;
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_drvdata(dev->parent);
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);
@@ -0,0 +1,665 @@
// 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);
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_RX_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;
}
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:
case ACP72_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:
case ACP72_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");
@@ -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 == (void *)QUIRK_TDM_MODE_ENABLE)
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");
File diff suppressed because it is too large Load Diff
+143
View File
@@ -0,0 +1,143 @@
/* 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)
/* List of DMI quirks - check acp-mach-common.c for usage. */
#define QUIRK_TDM_MODE_ENABLE 1
#define QUIRK_REMAP_DMIC_BT 2
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
+301
View File
@@ -0,0 +1,301 @@
// 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:
case 0x72:
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);
+187
View File
@@ -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);
@@ -0,0 +1,360 @@
// 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:
case ACP72_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_obj(*stream);
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:
case ACP72_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_new = 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);
@@ -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;
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_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);
+191
View File
@@ -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);
@@ -0,0 +1,604 @@
// 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),
},
{
.callback = soc_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0DD3"),
},
.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, "0DD4"),
},
.driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
},
{
.callback = soc_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_SKU, "21YW"),
},
.driver_data = (void *)((ASOC_SDW_CODEC_SPKR) | (ASOC_SDW_ACP_DMIC)),
},
{
.callback = soc_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_SKU, "21YX"),
},
.driver_data = (void *)((ASOC_SDW_CODEC_SPKR) | (ASOC_SDW_ACP_DMIC)),
},
{
.callback = soc_sdw_quirk_cb,
.matches = { /* Lenovo P16s G5 AMD */
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_SKU, "21XG"),
},
.driver_data = (void *)(ASOC_SDW_ACP_DMIC),
},
{
.callback = soc_sdw_quirk_cb,
.matches = { /* Lenovo P16s G5 AMD */
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_SKU, "21XH"),
},
.driver_data = (void *)(ASOC_SDW_ACP_DMIC),
},
{
.callback = soc_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "HN7306EA"),
},
.driver_data = (void *)(ASOC_SDW_ACP_DMIC),
},
{}
};
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:
case ACP72_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);
if (!cpus->dai_name)
return -ENOMEM;
dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
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, int num_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 i;
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:
case ACP72_PCI_REV:
sdw_platform_component->name = "amd_ps_sdw_dma.0";
break;
default:
return -EINVAL;
}
/* generate DAI links by each sdw link */
for (i = 0; i < num_dais && soc_dais->initialised; i++) {
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:
case ACP72_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;
int sdw_be_num = 0, dmic_num = 0;
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
struct snd_soc_aux_dev *soc_aux;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
int num_devs = 0;
int num_ends = 0;
int num_aux = 0;
int num_confs;
int num_links;
int be_id = 0;
int ret;
ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux);
if (ret < 0) {
dev_err(dev, "failed to count devices/endpoints: %d\n", ret);
return ret;
}
num_confs = num_ends;
/* One per DAI link, worst case is a DAI link for every endpoint */
struct asoc_sdw_dailink *soc_dais __free(kfree) =
kzalloc_objs(*soc_dais, num_ends);
if (!soc_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
struct asoc_sdw_endpoint *soc_ends __free(kfree) =
kzalloc_objs(*soc_ends, num_ends);
if (!soc_ends)
return -ENOMEM;
soc_aux = devm_kcalloc(dev, num_aux, sizeof(*soc_aux), GFP_KERNEL);
if (!soc_aux)
return -ENOMEM;
ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs);
if (ret < 0)
return ret;
sdw_be_num = ret;
/* enable dmic */
if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)
dmic_num = 1;
dev_dbg(dev, "sdw %d, dmic %d", sdw_be_num, dmic_num);
codec_conf = devm_kcalloc(dev, num_confs, 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_confs;
card->dai_link = dai_links;
card->num_links = num_links;
card->aux_dev = soc_aux;
card->num_aux_devs = num_aux;
/* SDW */
if (sdw_be_num) {
ret = create_sdw_dailinks(card, &dai_links, &be_id,
soc_dais, num_ends, &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);
if (mach->mach_params.subsystem_id_set)
snd_soc_card_set_pci_ssid(card,
mach->mach_params.subsystem_vendor,
mach->mach_params.subsystem_device);
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 (soc_sdw_quirk & ASOC_SDW_ACP_DMIC) {
card->components = devm_kasprintf(card->dev, GFP_KERNEL,
"%s mic:acp-dmic cfg-mics:%d",
card->components,
1);
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");
@@ -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");
@@ -0,0 +1,462 @@
// 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:
case ACP72_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);
if (!cpus->dai_name)
return -ENOMEM;
dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name);
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, int num_dais,
struct snd_soc_codec_conf **codec_conf)
{
int i;
int ret;
/* generate DAI links by each sdw link */
for (i = 0; i < num_dais && sof_dais->initialised; i++) {
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 snd_soc_aux_dev *sof_aux;
struct snd_soc_codec_conf *codec_conf;
struct snd_soc_dai_link *dai_links;
int num_devs = 0;
int num_ends = 0;
int num_aux = 0;
int num_links;
int be_id = 0;
int ret;
ret = asoc_sdw_count_sdw_endpoints(card, &num_devs, &num_ends, &num_aux);
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 */
struct asoc_sdw_dailink *sof_dais __free(kfree) =
kzalloc_objs(*sof_dais, num_ends);
if (!sof_dais)
return -ENOMEM;
/* One per endpoint, ie. each DAI on each codec/amp */
struct asoc_sdw_endpoint *sof_ends __free(kfree) =
kzalloc_objs(*sof_ends, num_ends);
if (!sof_ends)
return -ENOMEM;
sof_aux = devm_kcalloc(dev, num_aux, sizeof(*sof_aux), GFP_KERNEL);
if (!sof_aux)
return -ENOMEM;
ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, 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;
card->aux_dev = sof_aux;
card->num_aux_devs = num_aux;
/* SDW */
if (sdw_be_num) {
ret = create_sdw_dailinks(card, &dai_links, &be_id,
sof_dais, num_ends, &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");
@@ -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 == (void *)QUIRK_TDM_MODE_ENABLE)
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");
@@ -0,0 +1,455 @@
// 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/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 snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct acp3x_es83xx_private *priv = get_mach_priv(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 snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct acp3x_es83xx_private *priv = get_mach_priv(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) {
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
ret = snd_soc_dapm_add_routes(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;
}
@@ -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
+298
View File
@@ -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);
+228
View File
@@ -0,0 +1,228 @@
// 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:
case ACP72_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);
@@ -0,0 +1,20 @@
/* 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
#define ACP72_PCI_ID 0x72
#endif
@@ -0,0 +1,557 @@
// 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_endpoint spk_2_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 2,
.group_id = 1
};
static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 3,
.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_endpoint cs42l43_endpoints[] = {
{ /* Jack Playback Endpoint */
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* DMIC Capture Endpoint */
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Jack Capture Endpoint */
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Speaker Playback Endpoint */
.num = 3,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = {
{
.adr = 0x00013301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
{
.adr = 0x00013101FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP3"
},
{
.adr = 0x00013001FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x2_l0u01_adr[] = {
{
.adr = 0x00003001FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00003101FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x2_l1u01_adr[] = {
{
.adr = 0x00013001FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013101FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x2_l1u13_adr[] = {
{
.adr = 0x00013101FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013301FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x4_l0u0246_adr[] = {
{
.adr = 0x00003001FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00003201FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
{
.adr = 0x00003401FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP3"
},
{
.adr = 0x00003601FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = {
{
.adr = 0x00003001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_adr_device cs42l43_l0u1_adr[] = {
{
.adr = 0x00003101FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_adr_device cs42l43b_l0u1_adr[] = {
{
.adr = 0x00003101FA2A3B01ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] = {
{
.adr = 0x00013001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
},
{
.adr = 0x00013001FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013101FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
{
.adr = 0x00013201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP3"
},
{
.adr = 0x00013301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs42l45_l0u0_adr[] = {
{
.adr = 0x00003001FA424501ull,
/* Re-use endpoints, but cs42l45 has no speaker */
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l45"
}
};
static const struct snd_soc_acpi_adr_device cs42l45_l1u0_adr[] = {
{
.adr = 0x00013001FA424501ull,
/* Re-use endpoints, but cs42l45 has no speaker */
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l45"
}
};
static const struct snd_soc_acpi_link_adr acp63_cs35l56x4_l1u3210[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
.adr_d = cs35l56x4_l1u3210_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs35l63x4_l0u0246[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
.adr_d = cs35l63x4_l0u0246_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l43_l0u1[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43_l0u1_adr),
.adr_d = cs42l43_l0u1_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l43b_l0u1[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43b_l0u1_adr),
.adr_d = cs42l43b_l0u1_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l43_l0u0_cs35l56x4_l1u3210[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43_l0u0_adr),
.adr_d = cs42l43_l0u0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
.adr_d = cs35l56x4_l1u3210_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l43_l1u0_cs35l56x4_l1u0123[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr),
.adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l45_l0u0[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
.adr_d = cs42l45_l0u0_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l45_l0u0_cs35l63x2_l1u01[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
.adr_d = cs42l45_l0u0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l63x2_l1u01_adr),
.adr_d = cs35l63x2_l1u01_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l45_l0u0_cs35l63x2_l1u13[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
.adr_d = cs42l45_l0u0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l63x2_l1u13_adr),
.adr_d = cs35l63x2_l1u13_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l45_l1u0[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
.adr_d = cs42l45_l1u0_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l45_l1u0_cs35l63x2_l0u01[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
.adr_d = cs42l45_l1u0_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l63x2_l0u01_adr),
.adr_d = cs35l63x2_l0u01_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp63_cs42l45_l1u0_cs35l63x4_l0u0246[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
.adr_d = cs42l45_l1u0_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
.adr_d = cs35l63x4_l0u0246_adr,
},
{}
};
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",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp63_cs42l43_l0u0_cs35l56x4_l1u3210,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp63_cs42l45_l1u0_cs35l63x4_l0u0246,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp63_cs42l45_l0u0_cs35l63x2_l1u01,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp63_cs42l45_l0u0_cs35l63x2_l1u13,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp63_cs42l45_l1u0_cs35l63x2_l0u01,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp63_cs42l43_l1u0_cs35l56x4_l1u0123,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp63_cs35l56x4_l1u3210,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp63_cs35l63x4_l0u0246,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp63_cs42l43_l0u1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp63_cs42l43b_l0u1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp63_cs42l45_l0u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp63_cs42l45_l1u0,
.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");
@@ -0,0 +1,824 @@
// 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 "soc-acpi-amd-sdca-quirks.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_endpoint spk_2_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 2,
.group_id = 1
};
static const struct snd_soc_acpi_endpoint spk_3_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 3,
.group_id = 1
};
static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
/* Amp Endpoint, work as spk_l_endpoint */
{
.num = 1,
.aggregated = 1,
.group_position = 0,
.group_id = 1,
},
/* DMIC Endpoint */
{
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
/* DMIC Endpoint */
{
/*
* rt721 endpoint #2 maps to AIF3 (internal DMIC capture).
* Endpoint #1 is AIF2 amp path and is handled by external amps
* on this platform.
*/
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device rt712_vb_1_group1_adr[] = {
{
.adr = 0x000130025D071201ull,
.num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
.endpoints = jack_amp_g1_dmic_endpoints,
.name_prefix = "rt712"
}
};
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_endpoint cs42l43_endpoints[] = {
{ /* Jack Playback Endpoint */
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* DMIC Capture Endpoint */
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Jack Capture Endpoint */
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Speaker Playback Endpoint */
.num = 3,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device cs35l56x4_l1u3210_adr[] = {
{
.adr = 0x00013301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
{
.adr = 0x00013101FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP3"
},
{
.adr = 0x00013001FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x2_l0u01_adr[] = {
{
.adr = 0x00003001FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00003101FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x2_l1u01_adr[] = {
{
.adr = 0x00013001FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013101FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x2_l1u13_adr[] = {
{
.adr = 0x00013101FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013301FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l63x4_l0u0246_adr[] = {
{
.adr = 0x00003001FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00003201FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
{
.adr = 0x00003401FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP3"
},
{
.adr = 0x00003601FA356301ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs42l43_l0u0_adr[] = {
{
.adr = 0x00003001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_adr_device cs42l43_l0u1_adr[] = {
{
.adr = 0x00003101FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_adr_device cs42l43b_l0u1_adr[] = {
{
.adr = 0x00003101FA2A3B01ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_adr_device cs42l43_l1u0_cs35l56x4_l1u0123_adr[] = {
{
.adr = 0x00013001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints),
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l43"
},
{
.adr = 0x00013001FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00013101FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
{
.adr = 0x00013201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP3"
},
{
.adr = 0x00013301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs42l45_l0u0_adr[] = {
{
.adr = 0x00003001FA424501ull,
/* Re-use endpoints, but cs42l45 has no speaker */
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l45"
}
};
static const struct snd_soc_acpi_adr_device cs42l45_l1u0_adr[] = {
{
.adr = 0x00013001FA424501ull,
/* Re-use endpoints, but cs42l45 has no speaker */
.num_endpoints = ARRAY_SIZE(cs42l43_endpoints) - 1,
.endpoints = cs42l43_endpoints,
.name_prefix = "cs42l45"
}
};
static const struct snd_soc_acpi_link_adr acp70_cs35l56x4_l1u3210[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
.adr_d = cs35l56x4_l1u3210_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs35l63x4_l0u0246[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
.adr_d = cs35l63x4_l0u0246_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u1[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43_l0u1_adr),
.adr_d = cs42l43_l0u1_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l43b_l0u1[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43b_l0u1_adr),
.adr_d = cs42l43b_l0u1_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l43_l0u0_cs35l56x4_l1u3210[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43_l0u0_adr),
.adr_d = cs42l43_l0u0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56x4_l1u3210_adr),
.adr_d = cs35l56x4_l1u3210_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l43_l1u0_cs35l56x4_l1u0123[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l43_l1u0_cs35l56x4_l1u0123_adr),
.adr_d = cs42l43_l1u0_cs35l56x4_l1u0123_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
.adr_d = cs42l45_l0u0_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u01[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
.adr_d = cs42l45_l0u0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l63x2_l1u01_adr),
.adr_d = cs35l63x2_l1u01_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l45_l0u0_cs35l63x2_l1u13[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l45_l0u0_adr),
.adr_d = cs42l45_l0u0_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l63x2_l1u13_adr),
.adr_d = cs35l63x2_l1u13_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
.adr_d = cs42l45_l1u0_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x2_l0u01[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
.adr_d = cs42l45_l1u0_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l63x2_l0u01_adr),
.adr_d = cs35l63x2_l0u01_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_cs42l45_l1u0_cs35l63x4_l0u0246[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs42l45_l1u0_adr),
.adr_d = cs42l45_l1u0_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l63x4_l0u0246_adr),
.adr_d = cs35l63x4_l0u0246_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_alc712_vb_l1[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr),
.adr_d = rt712_vb_1_group1_adr,
},
{}
};
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,
},
{}
};
static const struct snd_soc_acpi_adr_device tas2783_2_adr[] = {
{
/* left */
.adr = 0x00003c0102000001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "tas2783-1",
},
{
/* right */
.adr = 0x00003d0102000001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "tas2783-2",
},
{
/* left */
.adr = 0x0000390102000001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "tas2783-3",
},
{
/* right */
.adr = 0x00003a0102000001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "tas2783-4",
},
};
static const struct snd_soc_acpi_link_adr acp70_tas2783_2[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(tas2783_2_adr),
.adr_d = tas2783_2_adr,
},
{}
};
static const struct snd_soc_acpi_adr_device rt1320_0_single_adr[] = {
{
.adr = 0x000030025D132001ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "rt1320-1"
}
};
static const struct snd_soc_acpi_adr_device rt722_1_single_adr[] = {
{
.adr = 0x000130025d072201ull,
.num_endpoints = ARRAY_SIZE(rt722_endpoints),
.endpoints = rt722_endpoints,
.name_prefix = "rt722"
}
};
static const struct snd_soc_acpi_link_adr acp70_rt1320_l0_rt722_l1[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt1320_0_single_adr),
.adr_d = rt1320_0_single_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt722_1_single_adr),
.adr_d = rt722_1_single_adr,
},
{}
};
static const struct snd_soc_acpi_adr_device rt721_l1u0_tas2783x2_l1u8b_adr[] = {
{
.adr = 0x000130025D072101ull,
/*
* On this platform speakers are provided by two TAS2783 amps.
* Keep rt721 as UAJ + DMIC only.
*/
.num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
.endpoints = jack_dmic_endpoints,
.name_prefix = "rt721",
},
{
.adr = 0x0001380102000001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "tas2783-1",
},
{
.adr = 0x00013B0102000001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "tas2783-2",
},
};
static const struct snd_soc_acpi_link_adr acp70_rt721_l1u0_tas2783x2_l1u8b[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt721_l1u0_tas2783x2_l1u8b_adr),
.adr_d = rt721_l1u0_tas2783x2_l1u8b_adr,
},
{}
};
static const struct snd_soc_acpi_endpoint rt721_endpoints[] = {
{ /* Jack Playback/Capture Endpoint (AIF1) */
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Speaker Amplifier Endpoint (AIF2, internal amp) */
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* DMIC Capture Endpoint (AIF3) */
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device rt721_1_single_adr[] = {
{
.adr = 0x000130025D072101ull,
.num_endpoints = ARRAY_SIZE(rt721_endpoints),
.endpoints = rt721_endpoints,
.name_prefix = "rt721"
}
};
static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt721_1_single_adr),
.adr_d = rt721_1_single_adr,
},
{}
};
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
{
.link_mask = BIT(0),
.links = acp70_tas2783_2,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_rt1320_l0_rt722_l1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_rt722_l0_rt1320_l1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_4_in_1_sdca,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_cs42l43_l0u0_cs35l56x4_l1u3210,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_cs42l45_l1u0_cs35l63x4_l0u0246,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_cs42l45_l0u0_cs35l63x2_l1u01,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_cs42l45_l0u0_cs35l63x2_l1u13,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_cs42l45_l1u0_cs35l63x2_l0u01,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp70_cs42l43_l1u0_cs35l56x4_l1u0123,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp70_cs35l56x4_l1u3210,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp70_cs35l63x4_l0u0246,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp70_rt722_only,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp70_cs42l43_l0u1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp70_cs42l43b_l0u1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp70_cs42l45_l0u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp70_cs42l45_l1u0,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp70_alc712_vb_l1,
.machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp70_rt721_l1u0_tas2783x2_l1u8b,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(1),
.links = acp70_rt721_only,
.drv_name = "amd_sdw",
},
{},
};
EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[] = {
{
.link_mask = BIT(0),
.links = acp70_rt722_only,
.drv_name = "amd_sof_sdw",
.sof_tplg_filename = "sof-acp_7_0-rt722-l0.tplg",
.fw_filename = "sof-acp_7_0.ri",
},
{},
};
EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines);
MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
MODULE_IMPORT_NS("SND_SOC_SDCA");
#endif
@@ -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>
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"}
};
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");
@@ -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/module.h>
#include <linux/property.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");
+365
View File
@@ -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 0x18
#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
@@ -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
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* soc-acpi-amd-sdca-quirks.c - tables and support for SDCA quirks
*
* Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved.
*
*/
#include <linux/soundwire/sdw_amd.h>
#include <sound/sdca.h>
#include <sound/soc-acpi.h>
#include "soc-acpi-amd-sdca-quirks.h"
/*
* Pretend machine quirk. The argument type is not the traditional
* 'struct snd_soc_acpi_mach' pointer but instead the sdw_amd_ctx
* which contains the peripheral information required for the
* SoundWire/SDCA filter on the SMART_MIC setup and interface
* revision. When the return value is false, the entry in the
* 'snd_soc_acpi_mach' table needs to be skipped.
*/
bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
{
struct sdw_amd_ctx *ctx = arg;
int i;
if (!ctx)
return false;
for (i = 0; i < ctx->peripherals->num_peripherals; i++) {
if (sdca_device_quirk_match(ctx->peripherals->array[i],
SDCA_QUIRKS_RT712_VB))
return true;
}
return false;
}
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* soc-acpi-amd-sdca-quirks.h - tables and support for SDCA quirks
*
* Copyright(c) 2025 Advanced Micro Devices, Inc. All rights reserved.
*
*/
#ifndef _SND_SOC_ACPI_AMD_SDCA_QUIRKS
#define _SND_SOC_ACPI_AMD_SDCA_QUIRKS
#if IS_ENABLED(CONFIG_SND_SOC_ACPI_AMD_SDCA_QUIRKS)
bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg);
#else
static inline bool snd_soc_acpi_amd_sdca_is_device_rt712_vb(void *arg)
{
return false;
}
#endif
#endif
@@ -0,0 +1,62 @@
/* 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 ACP72_PCI_REV 0x72
#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