ASoC: amd: Add support for ACP7.0 & ACP7.1

Merge series from Vijendar Mukunda <Vijendar.Mukunda@amd.com>:

This patch series includes the below changes
	- Refactor existing ACP6.3 platform ACP PCI driver, SoundWire
	  DMA driver code.
	- Add Audio IO support for ACP7.0 and ACP7.1 platforms for
	SoundWire IO and ACP PDM controller combination.
	- Add SoundWire generic machine driver changes for legacy stack
	(No DSP enabled) for ACP7.0 & ACP7.1 platforms.
	- Add SoundWire machines for ACP7.0 & ACP7.1 platforms.
This commit is contained in:
Mark Brown
2025-02-07 17:50:59 +00:00
14 changed files with 1449 additions and 360 deletions

View File

@@ -161,15 +161,15 @@ config SND_SOC_AMD_SOUNDWIRE
If unsure select "N".
config SND_SOC_AMD_PS
tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support"
tristate "AMD Audio Coprocessor-v6.3/v7.0/v7.1 support"
select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
select SND_SOC_ACPI_AMD_MATCH
depends on X86 && PCI && ACPI
help
This option enables Audio Coprocessor i.e ACP v6.3 support on
AMD Pink sardine platform. By enabling this flag build will be
triggered for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire
DMA driver.
This option enables Audio Coprocessor i.e ACP6.3/ACP7.0/ACP7.1
variants support. By enabling this flag build will be triggered
for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire DMA
driver.
Say m if you have such a device.
If unsure select "N".

View File

@@ -156,6 +156,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH
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

View File

@@ -22,7 +22,7 @@ snd-acp70-y := acp70.o
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
snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o

View File

@@ -28,6 +28,8 @@ static void log_quirks(struct device *dev)
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)
@@ -45,6 +47,38 @@ static const struct dmi_system_id soc_sdw_quirk_table[] = {
},
.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),
},
{}
};
@@ -122,6 +156,13 @@ static int create_sdw_dailink(struct snd_soc_card *card,
if (ret)
return ret;
break;
case ACP70_PCI_REV:
case ACP71_PCI_REV:
ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
*be_id, &cpu_pin_id, dev);
if (ret)
return ret;
break;
default:
return -EINVAL;
}
@@ -221,6 +262,8 @@ static int create_sdw_dailinks(struct snd_soc_card *card,
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
case ACP70_PCI_REV:
case ACP71_PCI_REV:
sdw_platform_component->name = "amd_ps_sdw_dma.0";
break;
default:
@@ -266,6 +309,8 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
case ACP70_PCI_REV:
case ACP71_PCI_REV:
pdm_cpu->name = "acp_ps_pdm_dma.0";
pdm_platform->name = "acp_ps_pdm_dma.0";
break;

View File

@@ -59,6 +59,40 @@ int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct dev
}
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");

View File

@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* amd-acp70-acpi-match.c - tables and support for ACP 7.0 & ACP7.1
* ACPI enumeration.
*
* Copyright 2025 Advanced Micro Devices, Inc.
*/
#include <sound/soc-acpi.h>
#include "../mach-config.h"
static const struct snd_soc_acpi_endpoint single_endpoint = {
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0
};
static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 0,
.group_id = 1
};
static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 1,
.group_id = 1
};
static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
{
.adr = 0x000030025D071101ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "rt711"
},
{
.adr = 0x000030025D131601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "rt1316-1"
},
{
.adr = 0x000032025D131601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "rt1316-2"
},
};
static const struct snd_soc_acpi_adr_device rt714_adr[] = {
{
.adr = 0x130025d071401ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "rt714"
}
};
static const struct snd_soc_acpi_link_adr acp70_4_in_1_sdca[] = {
{ .mask = BIT(0),
.num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
.adr_d = rt711_rt1316_group_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt714_adr),
.adr_d = rt714_adr,
},
{}
};
static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
{
.adr = 0x000030025d072201ull,
.num_endpoints = ARRAY_SIZE(rt722_endpoints),
.endpoints = rt722_endpoints,
.name_prefix = "rt722"
}
};
static const struct snd_soc_acpi_adr_device rt1320_1_single_adr[] = {
{
.adr = 0x000130025D132001ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "rt1320-1"
}
};
static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt722_0_single_adr),
.adr_d = rt722_0_single_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(rt722_0_single_adr),
.adr_d = rt722_0_single_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt1320_1_single_adr),
.adr_d = rt1320_1_single_adr,
},
{}
};
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_rt722_l0_rt1320_l1,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0),
.links = acp70_rt722_only,
.drv_name = "amd_sdw",
},
{
.link_mask = BIT(0) | BIT(1),
.links = acp70_4_in_1_sdca,
.drv_name = "amd_sdw",
},
{},
};
EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");

View File

@@ -19,9 +19,12 @@
#define AMD_SDW_MAX_GROUPS 9
#define ACP63_PCI_REV 0x63
#define ACP70_PCI_REV 0x70
#define ACP71_PCI_REV 0x71
#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
#define ASOC_SDW_FOUR_SPK BIT(4)
#define ASOC_SDW_ACP_DMIC BIT(5)
#define ASOC_SDW_CODEC_SPKR BIT(15)
#define AMD_SDW0 0
#define AMD_SDW1 1
@@ -38,11 +41,20 @@
#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

View File

@@ -26,6 +26,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
struct config_entry {
u32 flags;

View File

@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
# Pink Sardine platform Support
snd-pci-ps-y := pci-ps.o
snd-pci-ps-y := pci-ps.o ps-common.o
snd-ps-pdm-dma-y := ps-pdm-dma.o
snd-soc-ps-mach-y := ps-mach.o
snd-ps-sdw-dma-y := ps-sdw-dma.o

View File

@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* AMD ALSA SoC PDM Driver
* AMD Common ACP header file for ACP6.3, ACP7.0 & ACP7.1 platforms
*
* Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved.
* Copyright (C) 2022, 2023, 2025 Advanced Micro Devices, Inc. All rights reserved.
*/
#include <linux/soundwire/sdw_amd.h>
@@ -11,15 +11,18 @@
#define ACP_DEVICE_ID 0x15E2
#define ACP63_REG_START 0x1240000
#define ACP63_REG_END 0x125C000
#define ACP63_PCI_REV 0x63
#define ACP70_PCI_REV 0x70
#define ACP71_PCI_REV 0x71
#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
#define ACP_PGFSM_STATUS_MASK 3
#define ACP_POWERED_ON 0
#define ACP_POWER_ON_IN_PROGRESS 1
#define ACP_POWERED_OFF 2
#define ACP_POWER_OFF_IN_PROGRESS 3
#define ACP63_PGFSM_CNTL_POWER_ON_MASK 1
#define ACP63_PGFSM_CNTL_POWER_OFF_MASK 0
#define ACP63_PGFSM_STATUS_MASK 3
#define ACP63_POWERED_ON 0
#define ACP63_POWER_ON_IN_PROGRESS 1
#define ACP63_POWERED_OFF 2
#define ACP63_POWER_OFF_IN_PROGRESS 3
#define ACP_ERROR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
@@ -60,7 +63,7 @@
#define AMD_SDW_MAX_MANAGERS 2
/* time in ms for acp timeout */
#define ACP_TIMEOUT 500
#define ACP63_TIMEOUT 500
#define ACP_SDW0_STAT BIT(21)
#define ACP_SDW1_STAT BIT(2)
@@ -72,13 +75,13 @@
#define ACP_AUDIO0_RX_THRESHOLD 0x1b
#define ACP_AUDIO1_RX_THRESHOLD 0x19
#define ACP_AUDIO2_RX_THRESHOLD 0x17
#define ACP_P1_AUDIO1_TX_THRESHOLD BIT(6)
#define ACP_P1_AUDIO1_RX_THRESHOLD BIT(5)
#define ACP_SDW_DMA_IRQ_MASK 0x1F800000
#define ACP_P1_SDW_DMA_IRQ_MASK 0x60
#define ACP63_P1_AUDIO1_TX_THRESHOLD BIT(6)
#define ACP63_P1_AUDIO1_RX_THRESHOLD BIT(5)
#define ACP63_SDW_DMA_IRQ_MASK 0x1F800000
#define ACP63_P1_SDW_DMA_IRQ_MASK 0x60
#define ACP63_SDW0_DMA_MAX_STREAMS 6
#define ACP63_SDW1_DMA_MAX_STREAMS 2
#define ACP_P1_AUDIO_TX_THRESHOLD 6
#define ACP63_P1_AUDIO_TX_THRESHOLD 6
/*
* Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping
@@ -91,8 +94,8 @@
* 4 (SDW0_AUDIO1_RX) 25
* 5 (SDW0_AUDIO2_RX) 23
*/
#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
#define ACP63_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
#define ACP63_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
/*
* Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
@@ -101,7 +104,7 @@
* 0 (SDW1_AUDIO1_TX) 6
* 1 (SDW1_AUDIO1_RX) 5
*/
#define SDW1_DMA_IRQ_MASK(i) (ACP_P1_AUDIO_TX_THRESHOLD - (i))
#define ACP63_SDW1_DMA_IRQ_MASK(i) (ACP63_P1_AUDIO_TX_THRESHOLD - (i))
#define ACP_DELAY_US 5
#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
@@ -129,6 +132,61 @@
#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
#define SDW_MIN_BUFFER SDW_MAX_BUFFER
#define ACP_HW_OPS(acp_data, cb) ((acp_data)->hw_ops->cb)
#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F
#define ACP70_PGFSM_CNTL_POWER_OFF_MASK 0
#define ACP70_PGFSM_STATUS_MASK 0xFF
#define ACP70_TIMEOUT 2000
#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
#define ACP70_SDW0_PME_STAT BIT(26)
#define ACP70_SDW1_PME_STAT BIT(27)
#define ACP70_SDW0_DMA_MAX_STREAMS 6
#define ACP70_SDW1_DMA_MAX_STREAMS ACP70_SDW0_DMA_MAX_STREAMS
#define ACP70_SDW_DMA_IRQ_MASK 0x1F800000
#define ACP70_P1_SDW_DMA_IRQ_MASK 0x1F8
#define ACP70_P1_AUDIO0_TX_THRESHOLD 0x8
#define ACP70_P1_AUDIO1_TX_THRESHOLD 0x6
#define ACP70_P1_AUDIO2_TX_THRESHOLD 0x4
#define ACP70_P1_AUDIO0_RX_THRESHOLD 0x7
#define ACP70_P1_AUDIO1_RX_THRESHOLD 0x5
#define ACP70_P1_AUDIO2_RX_THRESHOLD 0x3
#define ACP70_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
#define ACP70_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
/*
* Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
* in ACP_EXTENAL_INTR_CNTL1 register for ACP70/ACP71 platforms
* Stream id IRQ Bit
* 0 (SDW1_AUDIO0_TX) 8
* 1 (SDW1_AUDIO1_TX) 6
* 2 (SDW1_AUDIO2_TX) 4
* 3 (SDW1_AUDIO0_RX) 7
* 4 (SDW1_AUDIO1_RX) 5
* 5 (SDW1_AUDIO2_RX) 3
*/
#define ACP70_SDW1_DMA_TX_IRQ_MASK(i) (ACP70_P1_AUDIO0_TX_THRESHOLD - (2 * (i)))
#define ACP70_SDW1_DMA_RX_IRQ_MASK(i) (ACP70_P1_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
#define ACP70_SW0_AUDIO0_TX_EN ACP_SW0_AUDIO0_TX_EN
#define ACP70_SW0_AUDIO1_TX_EN ACP_SW0_AUDIO1_TX_EN
#define ACP70_SW0_AUDIO2_TX_EN ACP_SW0_AUDIO2_TX_EN
#define ACP70_SW0_AUDIO0_RX_EN ACP_SW0_AUDIO0_RX_EN
#define ACP70_SW0_AUDIO1_RX_EN ACP_SW0_AUDIO1_RX_EN
#define ACP70_SW0_AUDIO2_RX_EN ACP_SW0_AUDIO2_RX_EN
#define ACP70_SW1_AUDIO0_TX_EN 0x0003C10
#define ACP70_SW1_AUDIO1_TX_EN 0x0003C50
#define ACP70_SW1_AUDIO2_TX_EN 0x0003C6C
#define ACP70_SW1_AUDIO0_RX_EN 0x0003C88
#define ACP70_SW1_AUDIO1_RX_EN 0x0003D28
#define ACP70_SW1_AUDIO2_RX_EN 0x0003D44
enum acp_config {
ACP_CONFIG_0 = 0,
ACP_CONFIG_1,
@@ -146,20 +204,34 @@ enum acp_config {
ACP_CONFIG_13,
ACP_CONFIG_14,
ACP_CONFIG_15,
ACP_CONFIG_16,
ACP_CONFIG_17,
ACP_CONFIG_18,
ACP_CONFIG_19,
ACP_CONFIG_20,
};
enum amd_sdw0_channel {
ACP_SDW0_AUDIO0_TX = 0,
ACP_SDW0_AUDIO1_TX,
ACP_SDW0_AUDIO2_TX,
ACP_SDW0_AUDIO0_RX,
ACP_SDW0_AUDIO1_RX,
ACP_SDW0_AUDIO2_RX,
enum amd_acp63_sdw0_channel {
ACP63_SDW0_AUDIO0_TX = 0,
ACP63_SDW0_AUDIO1_TX,
ACP63_SDW0_AUDIO2_TX,
ACP63_SDW0_AUDIO0_RX,
ACP63_SDW0_AUDIO1_RX,
ACP63_SDW0_AUDIO2_RX,
};
enum amd_sdw1_channel {
ACP_SDW1_AUDIO1_TX,
ACP_SDW1_AUDIO1_RX,
enum amd_acp63_sdw1_channel {
ACP63_SDW1_AUDIO1_TX,
ACP63_SDW1_AUDIO1_RX,
};
enum amd_acp70_sdw_channel {
ACP70_SDW_AUDIO0_TX = 0,
ACP70_SDW_AUDIO1_TX,
ACP70_SDW_AUDIO2_TX,
ACP70_SDW_AUDIO0_RX,
ACP70_SDW_AUDIO1_RX,
ACP70_SDW_AUDIO2_RX,
};
struct pdm_stream_instance {
@@ -180,8 +252,11 @@ struct pdm_dev_data {
struct sdw_dma_dev_data {
void __iomem *acp_base;
struct mutex *acp_lock; /* used to protect acp common register access */
struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
u32 acp_rev;
struct snd_pcm_substream *acp63_sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
struct snd_pcm_substream *acp63_sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
struct snd_pcm_substream *acp70_sdw0_dma_stream[ACP70_SDW0_DMA_MAX_STREAMS];
struct snd_pcm_substream *acp70_sdw1_dma_stream[ACP70_SDW1_DMA_MAX_STREAMS];
};
struct acp_sdw_dma_stream {
@@ -212,10 +287,35 @@ struct sdw_dma_ring_buf_reg {
u32 pos_high_reg;
};
struct acp63_dev_data;
/**
* struct acp_hw_ops - ACP PCI driver platform specific ops
* @acp_init: ACP initialization
* @acp_deinit: ACP de-initialization
* @acp_get_config: function to read the acp pin configuration
* @acp_sdw_dma_irq_thread: ACP SoundWire DMA interrupt thread
* acp_suspend: ACP system level suspend callback
* acp_resume: ACP system level resume callback
* acp_suspend_runtime: ACP runtime suspend callback
* acp_resume_runtime: ACP runtime resume callback
*/
struct acp_hw_ops {
int (*acp_init)(void __iomem *acp_base, struct device *dev);
int (*acp_deinit)(void __iomem *acp_base, struct device *dev);
void (*acp_get_config)(struct pci_dev *pci, struct acp63_dev_data *acp_data);
void (*acp_sdw_dma_irq_thread)(struct acp63_dev_data *acp_data);
int (*acp_suspend)(struct device *dev);
int (*acp_resume)(struct device *dev);
int (*acp_suspend_runtime)(struct device *dev);
int (*acp_resume_runtime)(struct device *dev);
};
/**
* struct acp63_dev_data - acp pci driver context
* @acp63_base: acp mmio base
* @res: resource
* @hw_ops: ACP pci driver platform-specific ops
* @pdm_dev: ACP PDM controller platform device
* @dmic_codec: platform device for DMIC Codec
* sdw_dma_dev: platform device for SoundWire DMA controller
@@ -229,16 +329,25 @@ struct sdw_dma_ring_buf_reg {
* @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
* @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
* @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled
* @acp70_sdw0_wake_event: flag set to true when wake irq asserted for SW0 instance
* @acp70_sdw1_wake_event: flag set to true when wake irq asserted for SW1 instance
* @addr: pci ioremap address
* @reg_range: ACP reigister range
* @acp_rev: ACP PCI revision id
* @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
* @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
* @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
* manager-SW0 instance
* @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
* manager-SW1 instance
* @acp70_sdw0-dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
* manager-SW0 instance
* @acp70_sdw_dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
* manager-SW1 instance
*/
struct acp63_dev_data {
void __iomem *acp63_base;
struct resource *res;
struct acp_hw_ops *hw_ops;
struct platform_device *pdm_dev;
struct platform_device *dmic_codec_dev;
struct platform_device *sdw_dma_dev;
@@ -253,11 +362,80 @@ struct acp63_dev_data {
bool is_pdm_config;
bool is_sdw_config;
bool sdw_en_stat;
bool acp70_sdw0_wake_event;
bool acp70_sdw1_wake_event;
u32 addr;
u32 reg_range;
u32 acp_rev;
u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS];
u16 acp70_sdw1_dma_intr_stat[ACP70_SDW1_DMA_MAX_STREAMS];
};
void acp63_hw_init_ops(struct acp_hw_ops *hw_ops);
void acp70_hw_init_ops(struct acp_hw_ops *hw_ops);
static inline int acp_hw_init(struct acp63_dev_data *adata, struct device *dev)
{
if (adata && adata->hw_ops && adata->hw_ops->acp_init)
return ACP_HW_OPS(adata, acp_init)(adata->acp63_base, dev);
return -EOPNOTSUPP;
}
static inline int acp_hw_deinit(struct acp63_dev_data *adata, struct device *dev)
{
if (adata && adata->hw_ops && adata->hw_ops->acp_deinit)
return ACP_HW_OPS(adata, acp_deinit)(adata->acp63_base, dev);
return -EOPNOTSUPP;
}
static inline void acp_hw_get_config(struct pci_dev *pci, struct acp63_dev_data *adata)
{
if (adata && adata->hw_ops && adata->hw_ops->acp_get_config)
ACP_HW_OPS(adata, acp_get_config)(pci, adata);
}
static inline void acp_hw_sdw_dma_irq_thread(struct acp63_dev_data *adata)
{
if (adata && adata->hw_ops && adata->hw_ops->acp_sdw_dma_irq_thread)
ACP_HW_OPS(adata, acp_sdw_dma_irq_thread)(adata);
}
static inline int acp_hw_suspend(struct device *dev)
{
struct acp63_dev_data *adata = dev_get_drvdata(dev);
if (adata && adata->hw_ops && adata->hw_ops->acp_suspend)
return ACP_HW_OPS(adata, acp_suspend)(dev);
return -EOPNOTSUPP;
}
static inline int acp_hw_resume(struct device *dev)
{
struct acp63_dev_data *adata = dev_get_drvdata(dev);
if (adata && adata->hw_ops && adata->hw_ops->acp_resume)
return ACP_HW_OPS(adata, acp_resume)(dev);
return -EOPNOTSUPP;
}
static inline int acp_hw_suspend_runtime(struct device *dev)
{
struct acp63_dev_data *adata = dev_get_drvdata(dev);
if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime)
return ACP_HW_OPS(adata, acp_suspend_runtime)(dev);
return -EOPNOTSUPP;
}
static inline int acp_hw_runtime_resume(struct device *dev)
{
struct acp63_dev_data *adata = dev_get_drvdata(dev);
if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime)
return ACP_HW_OPS(adata, acp_resume_runtime)(dev);
return -EOPNOTSUPP;
}
int snd_amd_acp_find_config(struct pci_dev *pci);

View File

@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD Pink Sardine ACP PCI Driver
* AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
*
* Copyright 2022 Advanced Micro Devices, Inc.
* Copyright 2022, 2025 Advanced Micro Devices, Inc.
*/
#include <linux/pci.h>
@@ -21,109 +21,160 @@
#include "acp63.h"
static int acp63_power_on(void __iomem *acp_base)
static void handle_acp70_sdw_wake_event(struct acp63_dev_data *adata)
{
u32 val;
struct amd_sdw_manager *amd_manager;
val = readl(acp_base + ACP_PGFSM_STATUS);
if (!val)
return val;
if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
}
static int acp63_reset(void __iomem *acp_base)
{
u32 val;
int ret;
writel(1, acp_base + ACP_SOFT_RESET);
ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
DELAY_US, ACP_TIMEOUT);
if (ret)
return ret;
writel(0, acp_base + ACP_SOFT_RESET);
return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
}
static void acp63_enable_interrupts(void __iomem *acp_base)
{
writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
}
static void acp63_disable_interrupts(void __iomem *acp_base)
{
writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
}
static int acp63_init(void __iomem *acp_base, struct device *dev)
{
int ret;
ret = acp63_power_on(acp_base);
if (ret) {
dev_err(dev, "ACP power on failed\n");
return ret;
if (adata->acp70_sdw0_wake_event) {
amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
if (amd_manager)
pm_request_resume(amd_manager->dev);
adata->acp70_sdw0_wake_event = 0;
}
writel(0x01, acp_base + ACP_CONTROL);
ret = acp63_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
return ret;
if (adata->acp70_sdw1_wake_event) {
amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
if (amd_manager)
pm_request_resume(amd_manager->dev);
adata->acp70_sdw1_wake_event = 0;
}
acp63_enable_interrupts(acp_base);
writel(0, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
}
static int acp63_deinit(void __iomem *acp_base, struct device *dev)
static short int check_and_handle_acp70_sdw_wake_irq(struct acp63_dev_data *adata)
{
int ret;
u32 ext_intr_stat1;
int irq_flag = 0;
bool sdw_wake_irq = false;
acp63_disable_interrupts(acp_base);
ret = acp63_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
return ret;
ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
writel(ACP70_SDW0_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->acp70_sdw0_wake_event = true;
sdw_wake_irq = true;
}
writel(0, acp_base + ACP_CONTROL);
writel(1, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
writel(ACP70_SDW1_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->acp70_sdw1_wake_event = true;
sdw_wake_irq = true;
}
if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
writel(0, adata->acp63_base + ACP_SW0_WAKE_EN);
writel(ACP70_SDW0_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->acp70_sdw0_wake_event = true;
sdw_wake_irq = true;
}
if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
writel(0, adata->acp63_base + ACP_SW1_WAKE_EN);
writel(ACP70_SDW1_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->acp70_sdw1_wake_event = true;
sdw_wake_irq = true;
}
if (sdw_wake_irq) {
handle_acp70_sdw_wake_event(adata);
irq_flag = 1;
}
return irq_flag;
}
static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 ext_intr_stat,
u32 ext_intr_stat1)
{
u32 stream_id = 0;
u16 sdw_dma_irq_flag = 0;
u16 index;
if (ext_intr_stat & ACP63_SDW_DMA_IRQ_MASK) {
for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
if (ext_intr_stat & BIT(index)) {
writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
switch (index) {
case ACP_AUDIO0_TX_THRESHOLD:
stream_id = ACP63_SDW0_AUDIO0_TX;
break;
case ACP_AUDIO1_TX_THRESHOLD:
stream_id = ACP63_SDW0_AUDIO1_TX;
break;
case ACP_AUDIO2_TX_THRESHOLD:
stream_id = ACP63_SDW0_AUDIO2_TX;
break;
case ACP_AUDIO0_RX_THRESHOLD:
stream_id = ACP63_SDW0_AUDIO0_RX;
break;
case ACP_AUDIO1_RX_THRESHOLD:
stream_id = ACP63_SDW0_AUDIO1_RX;
break;
case ACP_AUDIO2_RX_THRESHOLD:
stream_id = ACP63_SDW0_AUDIO2_RX;
break;
}
if (adata->acp_rev >= ACP70_PCI_REV)
adata->acp70_sdw0_dma_intr_stat[stream_id] = 1;
else
adata->acp63_sdw0_dma_intr_stat[stream_id] = 1;
sdw_dma_irq_flag = 1;
}
}
}
if (adata->acp_rev == ACP63_PCI_REV) {
if (ext_intr_stat1 & ACP63_P1_AUDIO1_RX_THRESHOLD) {
writel(ACP63_P1_AUDIO1_RX_THRESHOLD,
adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_RX] = 1;
sdw_dma_irq_flag = 1;
}
if (ext_intr_stat1 & ACP63_P1_AUDIO1_TX_THRESHOLD) {
writel(ACP63_P1_AUDIO1_TX_THRESHOLD,
adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_TX] = 1;
sdw_dma_irq_flag = 1;
}
} else {
if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) {
for (index = ACP70_P1_AUDIO2_RX_THRESHOLD;
index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) {
if (ext_intr_stat1 & BIT(index)) {
writel(BIT(index),
adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
switch (index) {
case ACP70_P1_AUDIO0_TX_THRESHOLD:
stream_id = ACP70_SDW_AUDIO0_TX;
break;
case ACP70_P1_AUDIO1_TX_THRESHOLD:
stream_id = ACP70_SDW_AUDIO1_TX;
break;
case ACP70_P1_AUDIO2_TX_THRESHOLD:
stream_id = ACP70_SDW_AUDIO2_TX;
break;
case ACP70_P1_AUDIO0_RX_THRESHOLD:
stream_id = ACP70_SDW_AUDIO0_RX;
break;
case ACP70_P1_AUDIO1_RX_THRESHOLD:
stream_id = ACP70_SDW_AUDIO1_RX;
break;
case ACP70_P1_AUDIO2_RX_THRESHOLD:
stream_id = ACP70_SDW_AUDIO2_RX;
break;
}
adata->acp70_sdw1_dma_intr_stat[stream_id] = 1;
sdw_dma_irq_flag = 1;
}
}
}
}
return sdw_dma_irq_flag;
}
static irqreturn_t acp63_irq_thread(int irq, void *context)
{
struct sdw_dma_dev_data *sdw_dma_data;
struct acp63_dev_data *adata = context;
u32 stream_index;
sdw_dma_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
if (adata->sdw0_dma_intr_stat[stream_index]) {
if (sdw_dma_data->sdw0_dma_stream[stream_index])
snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
adata->sdw0_dma_intr_stat[stream_index] = 0;
}
}
for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
if (adata->sdw1_dma_intr_stat[stream_index]) {
if (sdw_dma_data->sdw1_dma_stream[stream_index])
snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
adata->sdw1_dma_intr_stat[stream_index] = 0;
}
}
acp_hw_sdw_dma_irq_thread(adata);
return IRQ_HANDLED;
}
@@ -133,10 +184,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
struct pdm_dev_data *ps_pdm_data;
struct amd_sdw_manager *amd_manager;
u32 ext_intr_stat, ext_intr_stat1;
u32 stream_id = 0;
u16 irq_flag = 0;
u16 sdw_dma_irq_flag = 0;
u16 index;
adata = dev_id;
if (!adata)
@@ -173,6 +222,9 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
irq_flag = 1;
}
if (adata->acp_rev >= ACP70_PCI_REV)
irq_flag = check_and_handle_acp70_sdw_wake_irq(adata);
if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev);
writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
@@ -180,51 +232,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
irq_flag = 1;
}
if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
if (ext_intr_stat & BIT(index)) {
writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
switch (index) {
case ACP_AUDIO0_TX_THRESHOLD:
stream_id = ACP_SDW0_AUDIO0_TX;
break;
case ACP_AUDIO1_TX_THRESHOLD:
stream_id = ACP_SDW0_AUDIO1_TX;
break;
case ACP_AUDIO2_TX_THRESHOLD:
stream_id = ACP_SDW0_AUDIO2_TX;
break;
case ACP_AUDIO0_RX_THRESHOLD:
stream_id = ACP_SDW0_AUDIO0_RX;
break;
case ACP_AUDIO1_RX_THRESHOLD:
stream_id = ACP_SDW0_AUDIO1_RX;
break;
case ACP_AUDIO2_RX_THRESHOLD:
stream_id = ACP_SDW0_AUDIO2_RX;
break;
}
adata->sdw0_dma_intr_stat[stream_id] = 1;
sdw_dma_irq_flag = 1;
}
}
}
if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
writel(ACP_P1_AUDIO1_RX_THRESHOLD,
adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
sdw_dma_irq_flag = 1;
}
if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
writel(ACP_P1_AUDIO1_TX_THRESHOLD,
adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
sdw_dma_irq_flag = 1;
}
sdw_dma_irq_flag = check_and_handle_sdw_dma_irq(adata, ext_intr_stat, ext_intr_stat1);
if (sdw_dma_irq_flag)
return IRQ_WAKE_THREAD;
@@ -380,7 +389,6 @@ static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *a
const union acpi_object *obj;
acpi_handle handle;
acpi_integer dmic_status;
u32 config;
bool is_dmic_dev = false;
bool is_sdw_dev = false;
bool wov_en, dmic_en;
@@ -390,30 +398,7 @@ static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *a
wov_en = true;
dmic_en = false;
config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
switch (config) {
case ACP_CONFIG_4:
case ACP_CONFIG_5:
case ACP_CONFIG_10:
case ACP_CONFIG_11:
acp_data->is_pdm_config = true;
break;
case ACP_CONFIG_2:
case ACP_CONFIG_3:
acp_data->is_sdw_config = true;
break;
case ACP_CONFIG_6:
case ACP_CONFIG_7:
case ACP_CONFIG_12:
case ACP_CONFIG_8:
case ACP_CONFIG_13:
case ACP_CONFIG_14:
acp_data->is_pdm_config = true;
acp_data->is_sdw_config = true;
break;
default:
break;
}
acp_hw_get_config(pci, acp_data);
if (acp_data->is_pdm_config) {
pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
@@ -540,11 +525,33 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
unregister_pdm_dev:
platform_device_unregister(adata->pdm_dev);
de_init:
if (acp63_deinit(adata->acp63_base, &pci->dev))
if (acp_hw_deinit(adata, &pci->dev))
dev_err(&pci->dev, "ACP de-init failed\n");
return ret;
}
static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci)
{
adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops),
GFP_KERNEL);
if (!adata->hw_ops)
return -ENOMEM;
switch (adata->acp_rev) {
case ACP63_PCI_REV:
acp63_hw_init_ops(adata->hw_ops);
break;
case ACP70_PCI_REV:
case ACP71_PCI_REV:
acp70_hw_init_ops(adata->hw_ops);
break;
default:
dev_err(&pci->dev, "ACP device not found\n");
return -ENODEV;
}
return 0;
}
static int snd_acp63_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -560,12 +567,14 @@ static int snd_acp63_probe(struct pci_dev *pci,
if (flag)
return -ENODEV;
/* Pink Sardine device check */
/* ACP PCI revision id check for ACP6.3, ACP7.0 & ACP7.1 platforms */
switch (pci->revision) {
case 0x63:
case ACP63_PCI_REV:
case ACP70_PCI_REV:
case ACP71_PCI_REV:
break;
default:
dev_dbg(&pci->dev, "acp63 pci device not found\n");
dev_dbg(&pci->dev, "acp63/acp70/acp71 pci device not found\n");
return -ENODEV;
}
if (pci_enable_device(pci)) {
@@ -598,7 +607,12 @@ static int snd_acp63_probe(struct pci_dev *pci,
pci_set_master(pci);
pci_set_drvdata(pci, adata);
mutex_init(&adata->acp_lock);
ret = acp63_init(adata->acp63_base, &pci->dev);
ret = acp_hw_init_ops(adata, pci);
if (ret) {
dev_err(&pci->dev, "ACP hw ops init failed\n");
goto release_regions;
}
ret = acp_hw_init(adata, &pci->dev);
if (ret)
goto release_regions;
ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
@@ -618,7 +632,11 @@ static int snd_acp63_probe(struct pci_dev *pci,
dev_err(&pci->dev, "ACP platform devices creation failed\n");
goto de_init;
}
adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
if (adata->acp_rev >= ACP70_PCI_REV)
adata->machines = snd_soc_acpi_amd_acp70_sdw_machines;
else
adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
ret = acp63_machine_register(&pci->dev);
if (ret) {
dev_err(&pci->dev, "ACP machine register failed\n");
@@ -632,7 +650,7 @@ static int snd_acp63_probe(struct pci_dev *pci,
pm_runtime_allow(&pci->dev);
return 0;
de_init:
if (acp63_deinit(adata->acp63_base, &pci->dev))
if (acp_hw_deinit(adata, &pci->dev))
dev_err(&pci->dev, "ACP de-init failed\n");
release_regions:
pci_release_regions(pci);
@@ -642,90 +660,24 @@ static int snd_acp63_probe(struct pci_dev *pci,
return ret;
}
static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
static int __maybe_unused snd_acp_suspend(struct device *dev)
{
u32 sdw0_en, sdw1_en;
sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
return (sdw0_en || sdw1_en);
return acp_hw_suspend(dev);
}
static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
static int __maybe_unused snd_acp_runtime_resume(struct device *dev)
{
u32 val;
val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
if (val && adata->sdw->pdev[0])
pm_request_resume(&adata->sdw->pdev[0]->dev);
val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
if (val && adata->sdw->pdev[1])
pm_request_resume(&adata->sdw->pdev[1]->dev);
return acp_hw_runtime_resume(dev);
}
static int __maybe_unused snd_acp63_suspend(struct device *dev)
static int __maybe_unused snd_acp_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->is_sdw_dev) {
adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
if (adata->sdw_en_stat) {
writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
}
ret = acp63_deinit(adata->acp63_base, dev);
if (ret)
dev_err(dev, "ACP de-init failed\n");
return ret;
}
static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
ret = acp63_init(adata->acp63_base, dev);
if (ret) {
dev_err(dev, "ACP init failed\n");
return ret;
}
if (!adata->sdw_en_stat)
handle_acp63_sdw_pme_event(adata);
return 0;
}
static int __maybe_unused snd_acp63_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
ret = acp63_init(adata->acp63_base, dev);
if (ret)
dev_err(dev, "ACP init failed\n");
return ret;
return acp_hw_resume(dev);
}
static const struct dev_pm_ops acp63_pm_ops = {
SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume)
SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
};
static void snd_acp63_remove(struct pci_dev *pci)
@@ -744,7 +696,7 @@ static void snd_acp63_remove(struct pci_dev *pci)
}
if (adata->mach_dev)
platform_device_unregister(adata->mach_dev);
ret = acp63_deinit(adata->acp63_base, &pci->dev);
ret = acp_hw_deinit(adata, &pci->dev);
if (ret)
dev_err(&pci->dev, "ACP de-init failed\n");
pm_runtime_forbid(&pci->dev);
@@ -775,7 +727,7 @@ module_pci_driver(ps_acp63_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_AUTHOR("Syed.SabaKareem@amd.com");
MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver");
MODULE_DESCRIPTION("AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,475 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD ACP PCI driver callback routines for ACP6.3, ACP7.0 & ACP7.1
* platforms.
*
* Copyright 2025 Advanced Micro Devices, Inc.
* Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
*/
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <sound/pcm_params.h>
#include "acp63.h"
static int acp63_power_on(void __iomem *acp_base)
{
u32 val;
val = readl(acp_base + ACP_PGFSM_STATUS);
if (!val)
return val;
if ((val & ACP63_PGFSM_STATUS_MASK) != ACP63_POWER_ON_IN_PROGRESS)
writel(ACP63_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP63_TIMEOUT);
}
static int acp63_reset(void __iomem *acp_base)
{
u32 val;
int ret;
writel(1, acp_base + ACP_SOFT_RESET);
ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
DELAY_US, ACP63_TIMEOUT);
if (ret)
return ret;
writel(0, acp_base + ACP_SOFT_RESET);
return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP63_TIMEOUT);
}
static void acp63_enable_interrupts(void __iomem *acp_base)
{
writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
}
static void acp63_disable_interrupts(void __iomem *acp_base)
{
writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
}
static int acp63_init(void __iomem *acp_base, struct device *dev)
{
int ret;
ret = acp63_power_on(acp_base);
if (ret) {
dev_err(dev, "ACP power on failed\n");
return ret;
}
writel(0x01, acp_base + ACP_CONTROL);
ret = acp63_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
return ret;
}
acp63_enable_interrupts(acp_base);
writel(0, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
}
static int acp63_deinit(void __iomem *acp_base, struct device *dev)
{
int ret;
acp63_disable_interrupts(acp_base);
ret = acp63_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
return ret;
}
writel(0, acp_base + ACP_CONTROL);
writel(1, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
}
static void acp63_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
{
u32 config;
config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
dev_dbg(&pci->dev, "ACP config value: %d\n", config);
switch (config) {
case ACP_CONFIG_4:
case ACP_CONFIG_5:
case ACP_CONFIG_10:
case ACP_CONFIG_11:
acp_data->is_pdm_config = true;
break;
case ACP_CONFIG_2:
case ACP_CONFIG_3:
acp_data->is_sdw_config = true;
break;
case ACP_CONFIG_6:
case ACP_CONFIG_7:
case ACP_CONFIG_12:
case ACP_CONFIG_8:
case ACP_CONFIG_13:
case ACP_CONFIG_14:
acp_data->is_pdm_config = true;
acp_data->is_sdw_config = true;
break;
default:
break;
}
}
static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
{
u32 sdw0_en, sdw1_en;
sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
return (sdw0_en || sdw1_en);
}
static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
{
u32 val;
val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
if (val && adata->sdw->pdev[0])
pm_request_resume(&adata->sdw->pdev[0]->dev);
val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
if (val && adata->sdw->pdev[1])
pm_request_resume(&adata->sdw->pdev[1]->dev);
}
static int __maybe_unused snd_acp63_suspend(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->is_sdw_dev) {
adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
if (adata->sdw_en_stat) {
writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
}
ret = acp_hw_deinit(adata, dev);
if (ret)
dev_err(dev, "ACP de-init failed\n");
return ret;
}
static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
ret = acp_hw_init(adata, dev);
if (ret) {
dev_err(dev, "ACP init failed\n");
return ret;
}
if (!adata->sdw_en_stat)
handle_acp63_sdw_pme_event(adata);
return 0;
}
static int __maybe_unused snd_acp63_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
ret = acp_hw_init(adata, dev);
if (ret)
dev_err(dev, "ACP init failed\n");
return ret;
}
static void acp63_sdw_dma_irq_thread(struct acp63_dev_data *adata)
{
struct sdw_dma_dev_data *sdw_data;
u32 stream_id;
sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
for (stream_id = 0; stream_id < ACP63_SDW0_DMA_MAX_STREAMS; stream_id++) {
if (adata->acp63_sdw0_dma_intr_stat[stream_id]) {
if (sdw_data->acp63_sdw0_dma_stream[stream_id])
snd_pcm_period_elapsed(sdw_data->acp63_sdw0_dma_stream[stream_id]);
adata->acp63_sdw0_dma_intr_stat[stream_id] = 0;
}
}
for (stream_id = 0; stream_id < ACP63_SDW1_DMA_MAX_STREAMS; stream_id++) {
if (adata->acp63_sdw1_dma_intr_stat[stream_id]) {
if (sdw_data->acp63_sdw1_dma_stream[stream_id])
snd_pcm_period_elapsed(sdw_data->acp63_sdw1_dma_stream[stream_id]);
adata->acp63_sdw1_dma_intr_stat[stream_id] = 0;
}
}
}
void acp63_hw_init_ops(struct acp_hw_ops *hw_ops)
{
hw_ops->acp_init = acp63_init;
hw_ops->acp_deinit = acp63_deinit;
hw_ops->acp_get_config = acp63_get_config;
hw_ops->acp_sdw_dma_irq_thread = acp63_sdw_dma_irq_thread;
hw_ops->acp_suspend = snd_acp63_suspend;
hw_ops->acp_resume = snd_acp63_resume;
hw_ops->acp_suspend_runtime = snd_acp63_suspend;
hw_ops->acp_resume_runtime = snd_acp63_runtime_resume;
}
static int acp70_power_on(void __iomem *acp_base)
{
u32 val = 0;
val = readl(acp_base + ACP_PGFSM_STATUS);
if (!val)
return 0;
if (val & ACP70_PGFSM_STATUS_MASK)
writel(ACP70_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP70_TIMEOUT);
}
static int acp70_reset(void __iomem *acp_base)
{
u32 val;
int ret;
writel(1, acp_base + ACP_SOFT_RESET);
ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
DELAY_US, ACP70_TIMEOUT);
if (ret)
return ret;
writel(0, acp_base + ACP_SOFT_RESET);
return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP70_TIMEOUT);
}
static void acp70_enable_sdw_host_wake_interrupts(void __iomem *acp_base)
{
u32 ext_intr_cntl1;
ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
ext_intr_cntl1 |= ACP70_SDW_HOST_WAKE_MASK;
writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
}
static void acp70_enable_interrupts(void __iomem *acp_base)
{
u32 sdw0_wake_en, sdw1_wake_en;
writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
sdw0_wake_en = readl(acp_base + ACP_SW0_WAKE_EN);
sdw1_wake_en = readl(acp_base + ACP_SW1_WAKE_EN);
if (sdw0_wake_en || sdw1_wake_en)
acp70_enable_sdw_host_wake_interrupts(acp_base);
}
static void acp70_disable_interrupts(void __iomem *acp_base)
{
writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
}
static int acp70_init(void __iomem *acp_base, struct device *dev)
{
int ret;
ret = acp70_power_on(acp_base);
if (ret) {
dev_err(dev, "ACP power on failed\n");
return ret;
}
writel(0x01, acp_base + ACP_CONTROL);
ret = acp70_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
return ret;
}
writel(0, acp_base + ACP_ZSC_DSP_CTRL);
acp70_enable_interrupts(acp_base);
writel(0x1, acp_base + ACP_PME_EN);
return 0;
}
static int acp70_deinit(void __iomem *acp_base, struct device *dev)
{
int ret;
acp70_disable_interrupts(acp_base);
ret = acp70_reset(acp_base);
if (ret) {
dev_err(dev, "ACP reset failed\n");
return ret;
}
writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
}
static void acp70_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
{
u32 config;
config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
dev_dbg(&pci->dev, "ACP config value: %d\n", config);
switch (config) {
case ACP_CONFIG_4:
case ACP_CONFIG_5:
case ACP_CONFIG_10:
case ACP_CONFIG_11:
case ACP_CONFIG_20:
acp_data->is_pdm_config = true;
break;
case ACP_CONFIG_2:
case ACP_CONFIG_3:
case ACP_CONFIG_16:
acp_data->is_sdw_config = true;
break;
case ACP_CONFIG_6:
case ACP_CONFIG_7:
case ACP_CONFIG_12:
case ACP_CONFIG_8:
case ACP_CONFIG_13:
case ACP_CONFIG_14:
case ACP_CONFIG_17:
case ACP_CONFIG_18:
case ACP_CONFIG_19:
acp_data->is_pdm_config = true;
acp_data->is_sdw_config = true;
break;
default:
break;
}
}
static void acp70_sdw_dma_irq_thread(struct acp63_dev_data *adata)
{
struct sdw_dma_dev_data *sdw_data;
u32 stream_id;
sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
for (stream_id = 0; stream_id < ACP70_SDW0_DMA_MAX_STREAMS; stream_id++) {
if (adata->acp70_sdw0_dma_intr_stat[stream_id]) {
if (sdw_data->acp70_sdw0_dma_stream[stream_id])
snd_pcm_period_elapsed(sdw_data->acp70_sdw0_dma_stream[stream_id]);
adata->acp70_sdw0_dma_intr_stat[stream_id] = 0;
}
}
for (stream_id = 0; stream_id < ACP70_SDW1_DMA_MAX_STREAMS; stream_id++) {
if (adata->acp70_sdw1_dma_intr_stat[stream_id]) {
if (sdw_data->acp70_sdw1_dma_stream[stream_id])
snd_pcm_period_elapsed(sdw_data->acp70_sdw1_dma_stream[stream_id]);
adata->acp70_sdw1_dma_intr_stat[stream_id] = 0;
}
}
}
static int __maybe_unused snd_acp70_suspend(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->is_sdw_dev) {
adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
if (adata->sdw_en_stat) {
writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
}
ret = acp_hw_deinit(adata, dev);
if (ret)
dev_err(dev, "ACP de-init failed\n");
return ret;
}
static int __maybe_unused snd_acp70_runtime_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
writel(1, adata->acp63_base + ACP_PME_EN);
return 0;
}
ret = acp_hw_init(adata, dev);
if (ret) {
dev_err(dev, "ACP init failed\n");
return ret;
}
return 0;
}
static int __maybe_unused snd_acp70_resume(struct device *dev)
{
struct acp63_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
writel(1, adata->acp63_base + ACP_PME_EN);
return 0;
}
ret = acp_hw_init(adata, dev);
if (ret)
dev_err(dev, "ACP init failed\n");
return ret;
}
void acp70_hw_init_ops(struct acp_hw_ops *hw_ops)
{
hw_ops->acp_init = acp70_init;
hw_ops->acp_deinit = acp70_deinit;
hw_ops->acp_get_config = acp70_get_config;
hw_ops->acp_sdw_dma_irq_thread = acp70_sdw_dma_irq_thread;
hw_ops->acp_suspend = snd_acp70_suspend;
hw_ops->acp_resume = snd_acp70_resume;
hw_ops->acp_suspend_runtime = snd_acp70_suspend;
hw_ops->acp_resume_runtime = snd_acp70_runtime_resume;
}

View File

@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD ALSA SoC Pink Sardine PDM Driver
* AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
*
* Copyright 2022 Advanced Micro Devices, Inc.
* Copyright 2022, 2025 Advanced Micro Devices, Inc.
*/
#include <linux/platform_device.h>
@@ -458,6 +458,6 @@ static struct platform_driver acp63_pdm_dma_driver = {
module_platform_driver(acp63_pdm_dma_driver);
MODULE_AUTHOR("Syed.SabaKareem@amd.com");
MODULE_DESCRIPTION("AMD PINK SARDINE PDM Driver");
MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD ALSA SoC Pink Sardine SoundWire DMA Driver
* AMD ALSA SoC common SoundWire DMA Driver for ACP6.3, ACP7.0 and ACP7.1
* platforms.
*
* Copyright 2023 Advanced Micro Devices, Inc.
* Copyright 2023, 2025 Advanced Micro Devices, Inc.
*/
#include <linux/err.h>
@@ -18,7 +19,7 @@
#define DRV_NAME "amd_ps_sdw_dma"
static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
static struct sdw_dma_ring_buf_reg acp63_sdw0_dma_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
{ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
@@ -44,7 +45,7 @@ static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STRE
* For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register
* set as per hardware register documentation
*/
static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
static struct sdw_dma_ring_buf_reg acp63_sdw1_dma_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
{ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
@@ -55,7 +56,7 @@ static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STRE
ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
};
static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
static u32 acp63_sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
ACP_SW0_AUDIO0_TX_EN,
ACP_SW0_AUDIO1_TX_EN,
ACP_SW0_AUDIO2_TX_EN,
@@ -70,11 +71,77 @@ static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
* it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers
* as per hardware register documentation.
*/
static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
static u32 acp63_sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
ACP_SW1_AUDIO1_TX_EN,
ACP_SW1_AUDIO1_RX_EN,
};
static struct sdw_dma_ring_buf_reg acp70_sdw0_dma_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
{ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
{ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
};
static struct sdw_dma_ring_buf_reg acp70_sdw1_dma_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
{ACP_P1_AUDIO0_TX_DMA_SIZE, ACP_P1_AUDIO0_TX_FIFOADDR, ACP_P1_AUDIO0_TX_FIFOSIZE,
ACP_P1_AUDIO0_TX_RINGBUFSIZE, ACP_P1_AUDIO0_TX_RINGBUFADDR,
ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE,
ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_P1_AUDIO2_TX_DMA_SIZE, ACP_P1_AUDIO2_TX_FIFOADDR, ACP_P1_AUDIO2_TX_FIFOSIZE,
ACP_P1_AUDIO2_TX_RINGBUFSIZE, ACP_P1_AUDIO2_TX_RINGBUFADDR,
ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE,
ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
{ACP_P1_AUDIO0_RX_DMA_SIZE, ACP_P1_AUDIO0_RX_FIFOADDR, ACP_P1_AUDIO0_RX_FIFOSIZE,
ACP_P1_AUDIO0_RX_RINGBUFSIZE, ACP_P1_AUDIO0_RX_RINGBUFADDR,
ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE,
ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
{ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
{ACP_P1_AUDIO2_RX_DMA_SIZE, ACP_P1_AUDIO2_RX_FIFOADDR, ACP_P1_AUDIO2_RX_FIFOSIZE,
ACP_P1_AUDIO2_RX_RINGBUFSIZE, ACP_P1_AUDIO2_RX_RINGBUFADDR,
ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE,
ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
};
static u32 acp70_sdw0_dma_enable_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
ACP70_SW0_AUDIO0_TX_EN,
ACP70_SW0_AUDIO1_TX_EN,
ACP70_SW0_AUDIO2_TX_EN,
ACP70_SW0_AUDIO0_RX_EN,
ACP70_SW0_AUDIO1_RX_EN,
ACP70_SW0_AUDIO2_RX_EN,
};
static u32 acp70_sdw1_dma_enable_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
ACP70_SW1_AUDIO0_TX_EN,
ACP70_SW1_AUDIO1_TX_EN,
ACP70_SW1_AUDIO2_TX_EN,
ACP70_SW1_AUDIO0_RX_EN,
ACP70_SW1_AUDIO1_RX_EN,
ACP70_SW1_AUDIO2_RX_EN,
};
static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -114,11 +181,10 @@ static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
};
static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable)
static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, u32 irq_mask,
u32 irq_mask1, bool enable)
{
u32 ext_intr_cntl, ext_intr_cntl1;
u32 irq_mask = ACP_SDW_DMA_IRQ_MASK;
u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK;
if (enable) {
ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
@@ -167,7 +233,7 @@ static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *ac
}
static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size,
u32 manager_instance)
u32 manager_instance, u32 acp_rev)
{
u32 reg_dma_size;
u32 reg_fifo_addr;
@@ -180,20 +246,47 @@ static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id,
u32 sdw_ring_buf_size;
u32 sdw_mem_window_offset;
switch (manager_instance) {
case ACP_SDW0:
reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size;
reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr;
reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size;
reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size;
reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr;
switch (acp_rev) {
case ACP63_PCI_REV:
switch (manager_instance) {
case ACP_SDW0:
reg_dma_size = acp63_sdw0_dma_reg[stream_id].reg_dma_size;
reg_fifo_addr = acp63_sdw0_dma_reg[stream_id].reg_fifo_addr;
reg_fifo_size = acp63_sdw0_dma_reg[stream_id].reg_fifo_size;
reg_ring_buf_size = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_size;
reg_ring_buf_addr = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
break;
case ACP_SDW1:
reg_dma_size = acp63_sdw1_dma_reg[stream_id].reg_dma_size;
reg_fifo_addr = acp63_sdw1_dma_reg[stream_id].reg_fifo_addr;
reg_fifo_size = acp63_sdw1_dma_reg[stream_id].reg_fifo_size;
reg_ring_buf_size = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_size;
reg_ring_buf_addr = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
break;
default:
return -EINVAL;
}
break;
case ACP_SDW1:
reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size;
reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr;
reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size;
reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size;
reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr;
case ACP70_PCI_REV:
case ACP71_PCI_REV:
switch (manager_instance) {
case ACP_SDW0:
reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size;
reg_fifo_addr = acp70_sdw0_dma_reg[stream_id].reg_fifo_addr;
reg_fifo_size = acp70_sdw0_dma_reg[stream_id].reg_fifo_size;
reg_ring_buf_size = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_size;
reg_ring_buf_addr = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
break;
case ACP_SDW1:
reg_dma_size = acp70_sdw1_dma_reg[stream_id].reg_dma_size;
reg_fifo_addr = acp70_sdw1_dma_reg[stream_id].reg_fifo_addr;
reg_fifo_size = acp70_sdw1_dma_reg[stream_id].reg_fifo_size;
reg_ring_buf_size = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_size;
reg_ring_buf_addr = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@@ -265,21 +358,53 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
if (!stream)
return -EINVAL;
stream_id = stream->stream_id;
switch (stream->instance) {
case ACP_SDW0:
sdw_data->sdw0_dma_stream[stream_id] = substream;
water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg;
acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id));
else
irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id));
switch (sdw_data->acp_rev) {
case ACP63_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
sdw_data->acp63_sdw0_dma_stream[stream_id] = substream;
water_mark_size_reg = acp63_sdw0_dma_reg[stream_id].water_mark_size_reg;
acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
irq_mask = BIT(ACP63_SDW0_DMA_TX_IRQ_MASK(stream_id));
else
irq_mask = BIT(ACP63_SDW0_DMA_RX_IRQ_MASK(stream_id));
break;
case ACP_SDW1:
sdw_data->acp63_sdw1_dma_stream[stream_id] = substream;
acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
water_mark_size_reg = acp63_sdw1_dma_reg[stream_id].water_mark_size_reg;
irq_mask = BIT(ACP63_SDW1_DMA_IRQ_MASK(stream_id));
break;
default:
return -EINVAL;
}
break;
case ACP_SDW1:
sdw_data->sdw1_dma_stream[stream_id] = substream;
acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg;
irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id));
case ACP70_PCI_REV:
case ACP71_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
sdw_data->acp70_sdw0_dma_stream[stream_id] = substream;
water_mark_size_reg = acp70_sdw0_dma_reg[stream_id].water_mark_size_reg;
acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
irq_mask = BIT(ACP70_SDW0_DMA_TX_IRQ_MASK(stream_id));
else
irq_mask = BIT(ACP70_SDW0_DMA_RX_IRQ_MASK(stream_id));
break;
case ACP_SDW1:
sdw_data->acp70_sdw1_dma_stream[stream_id] = substream;
acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
water_mark_size_reg = acp70_sdw1_dma_reg[stream_id].water_mark_size_reg;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
irq_mask = BIT(ACP70_SDW1_DMA_TX_IRQ_MASK(stream_id));
else
irq_mask = BIT(ACP70_SDW1_DMA_RX_IRQ_MASK(stream_id));
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@@ -290,7 +415,7 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
acp63_config_dma(stream, sdw_data->acp_base, stream_id);
ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size,
stream->instance);
stream->instance, sdw_data->acp_rev);
if (ret) {
dev_err(component->dev, "Invalid DMA channel\n");
return -EINVAL;
@@ -302,20 +427,42 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
return 0;
}
static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base)
static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
u32 acp_rev)
{
union acp_sdw_dma_count byte_count;
u32 pos_low_reg, pos_high_reg;
byte_count.bytescount = 0;
switch (stream->instance) {
case ACP_SDW0:
pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg;
pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
switch (acp_rev) {
case ACP63_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
pos_low_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_low_reg;
pos_high_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_high_reg;
break;
case ACP_SDW1:
pos_low_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_low_reg;
pos_high_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_high_reg;
break;
default:
goto POINTER_RETURN_BYTES;
}
break;
case ACP_SDW1:
pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg;
pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
case ACP70_PCI_REV:
case ACP71_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg;
pos_high_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_high_reg;
break;
case ACP_SDW1:
pos_low_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_low_reg;
pos_high_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_high_reg;
break;
default:
goto POINTER_RETURN_BYTES;
}
break;
default:
goto POINTER_RETURN_BYTES;
@@ -340,7 +487,7 @@ static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
stream = substream->runtime->private_data;
buffersize = frames_to_bytes(substream->runtime,
substream->runtime->buffer_size);
bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base);
bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base, sdw_data->acp_rev);
if (bytescount > stream->bytescount)
bytescount -= stream->bytescount;
pos = do_div(bytescount, buffersize);
@@ -367,12 +514,31 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component,
stream = substream->runtime->private_data;
if (!stream)
return -EINVAL;
switch (stream->instance) {
case ACP_SDW0:
sdw_data->sdw0_dma_stream[stream->stream_id] = NULL;
switch (sdw_data->acp_rev) {
case ACP63_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
sdw_data->acp63_sdw0_dma_stream[stream->stream_id] = NULL;
break;
case ACP_SDW1:
sdw_data->acp63_sdw1_dma_stream[stream->stream_id] = NULL;
break;
default:
return -EINVAL;
}
break;
case ACP_SDW1:
sdw_data->sdw1_dma_stream[stream->stream_id] = NULL;
case ACP70_PCI_REV:
case ACP71_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL;
break;
case ACP_SDW1:
sdw_data->acp70_sdw1_dma_stream[stream->stream_id] = NULL;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@@ -382,7 +548,7 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component,
}
static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
void __iomem *acp_base, bool sdw_dma_enable)
void __iomem *acp_base, u32 acp_rev, bool sdw_dma_enable)
{
struct acp_sdw_dma_stream *stream;
u32 stream_id;
@@ -393,12 +559,31 @@ static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
stream = substream->runtime->private_data;
stream_id = stream->stream_id;
switch (stream->instance) {
case ACP_SDW0:
sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id];
switch (acp_rev) {
case ACP63_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
sdw_dma_en_reg = acp63_sdw0_dma_enable_reg[stream_id];
break;
case ACP_SDW1:
sdw_dma_en_reg = acp63_sdw1_dma_enable_reg[stream_id];
break;
default:
return -EINVAL;
}
break;
case ACP_SDW1:
sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id];
case ACP70_PCI_REV:
case ACP71_PCI_REV:
switch (stream->instance) {
case ACP_SDW0:
sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id];
break;
case ACP_SDW1:
sdw_dma_en_reg = acp70_sdw1_dma_enable_reg[stream_id];
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
@@ -422,12 +607,12 @@ static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, true);
ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, true);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, false);
ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, false);
break;
default:
ret = -EINVAL;
@@ -474,6 +659,7 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev)
return -ENOMEM;
sdw_data->acp_lock = &acp_data->acp_lock;
sdw_data->acp_rev = acp_data->acp_rev;
dev_set_drvdata(&pdev->dev, sdw_data);
status = devm_snd_soc_register_component(&pdev->dev,
&acp63_sdw_component,
@@ -495,15 +681,17 @@ static void acp63_sdw_platform_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
static int acp63_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
{
struct acp_sdw_dma_stream *stream;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
u32 period_bytes, buf_size, water_mark_size_reg;
u32 stream_count;
u32 stream_count, irq_mask, irq_mask1;
int index, instance, ret;
irq_mask = ACP63_SDW_DMA_IRQ_MASK;
irq_mask1 = ACP63_P1_SDW_DMA_IRQ_MASK;
for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
if (instance == ACP_SDW0)
stream_count = ACP63_SDW0_DMA_MAX_STREAMS;
@@ -512,13 +700,11 @@ static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
for (index = 0; index < stream_count; index++) {
if (instance == ACP_SDW0) {
substream = sdw_data->sdw0_dma_stream[index];
water_mark_size_reg =
sdw0_dma_ring_buf_reg[index].water_mark_size_reg;
substream = sdw_data->acp63_sdw0_dma_stream[index];
water_mark_size_reg = acp63_sdw0_dma_reg[index].water_mark_size_reg;
} else {
substream = sdw_data->sdw1_dma_stream[index];
water_mark_size_reg =
sdw1_dma_ring_buf_reg[index].water_mark_size_reg;
substream = sdw_data->acp63_sdw1_dma_stream[index];
water_mark_size_reg = acp63_sdw1_dma_reg[index].water_mark_size_reg;
}
if (substream && substream->runtime) {
@@ -528,14 +714,56 @@ static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
buf_size = frames_to_bytes(runtime, runtime->buffer_size);
acp63_config_dma(stream, sdw_data->acp_base, index);
ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
buf_size, instance);
buf_size, instance,
ACP63_PCI_REV);
if (ret)
return ret;
writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
}
}
}
acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true);
acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
return 0;
}
static int acp70_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
{
struct acp_sdw_dma_stream *stream;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
u32 period_bytes, buf_size, water_mark_size_reg;
u32 stream_count, irq_mask, irq_mask1;
int index, instance, ret;
irq_mask = ACP70_SDW_DMA_IRQ_MASK;
irq_mask1 = ACP70_P1_SDW_DMA_IRQ_MASK;
stream_count = ACP70_SDW0_DMA_MAX_STREAMS;
for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
for (index = 0; index < stream_count; index++) {
if (instance == ACP_SDW0) {
substream = sdw_data->acp70_sdw0_dma_stream[index];
water_mark_size_reg = acp70_sdw0_dma_reg[index].water_mark_size_reg;
} else {
substream = sdw_data->acp70_sdw1_dma_stream[index];
water_mark_size_reg = acp70_sdw1_dma_reg[index].water_mark_size_reg;
}
if (substream && substream->runtime) {
runtime = substream->runtime;
stream = runtime->private_data;
period_bytes = frames_to_bytes(runtime, runtime->period_size);
buf_size = frames_to_bytes(runtime, runtime->buffer_size);
acp63_config_dma(stream, sdw_data->acp_base, index);
ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
buf_size, instance,
sdw_data->acp_rev);
if (ret)
return ret;
writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
}
}
}
acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
return 0;
}
@@ -544,7 +772,10 @@ static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev)
struct sdw_dma_dev_data *sdw_data;
sdw_data = dev_get_drvdata(dev);
return acp_restore_sdw_dma_config(sdw_data);
if (sdw_data->acp_rev == ACP63_PCI_REV)
return acp63_restore_sdw_dma_config(sdw_data);
else
return acp70_restore_sdw_dma_config(sdw_data);
}
static const struct dev_pm_ops acp63_pm_ops = {
@@ -563,6 +794,6 @@ static struct platform_driver acp63_sdw_dma_driver = {
module_platform_driver(acp63_sdw_dma_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
MODULE_DESCRIPTION("AMD common SDW DMA Driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);