summaryrefslogtreecommitdiffstats
path: root/sound/soc/sof
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/sof')
-rw-r--r--sound/soc/sof/amd/Kconfig18
-rw-r--r--sound/soc/sof/amd/acp-common.c65
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h10
-rw-r--r--sound/soc/sof/amd/acp.c202
-rw-r--r--sound/soc/sof/amd/acp.h26
-rw-r--r--sound/soc/sof/amd/pci-acp63.c7
-rw-r--r--sound/soc/sof/core.c28
-rw-r--r--sound/soc/sof/debug.c26
-rw-r--r--sound/soc/sof/imx/imx8.c16
-rw-r--r--sound/soc/sof/imx/imx8m.c10
-rw-r--r--sound/soc/sof/imx/imx8ulp.c10
-rw-r--r--sound/soc/sof/intel/hda-common-ops.c1
-rw-r--r--sound/soc/sof/intel/hda-dai-ops.c51
-rw-r--r--sound/soc/sof/intel/hda-dai.c48
-rw-r--r--sound/soc/sof/intel/hda-dsp.c5
-rw-r--r--sound/soc/sof/intel/hda-stream.c9
-rw-r--r--sound/soc/sof/intel/hda.c80
-rw-r--r--sound/soc/sof/intel/hda.h5
-rw-r--r--sound/soc/sof/intel/lnl.c57
-rw-r--r--sound/soc/sof/intel/lnl.h15
-rw-r--r--sound/soc/sof/intel/mtl.c42
-rw-r--r--sound/soc/sof/intel/mtl.h4
-rw-r--r--sound/soc/sof/ipc3-pcm.c25
-rw-r--r--sound/soc/sof/ipc3-topology.c40
-rw-r--r--sound/soc/sof/ipc4-mtrace.c11
-rw-r--r--sound/soc/sof/ipc4-pcm.c39
-rw-r--r--sound/soc/sof/ipc4-priv.h4
-rw-r--r--sound/soc/sof/ipc4-topology.c47
-rw-r--r--sound/soc/sof/ops.h9
-rw-r--r--sound/soc/sof/sof-audio.c8
-rw-r--r--sound/soc/sof/sof-audio.h2
-rw-r--r--sound/soc/sof/sof-priv.h10
-rw-r--r--sound/soc/sof/topology.c30
33 files changed, 785 insertions, 175 deletions
diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig
index 19c5e27a19..2729c6eb3f 100644
--- a/sound/soc/sof/amd/Kconfig
+++ b/sound/soc/sof/amd/Kconfig
@@ -6,6 +6,7 @@
config SND_SOC_SOF_AMD_TOPLEVEL
tristate "SOF support for AMD audio DSPs"
+ depends on SOUNDWIRE_AMD || !SOUNDWIRE_AMD
depends on X86 || COMPILE_TEST
help
This adds support for Sound Open Firmware for AMD platforms.
@@ -60,10 +61,27 @@ config SND_SOC_SOF_ACP_PROBES
This option is not user-selectable but automatically handled by
'select' statements at a higher level
+config SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
+ tristate
+ select SND_AMD_SOUNDWIRE_ACPI if ACPI
+
+config SND_SOC_SOF_AMD_SOUNDWIRE
+ tristate "SOF support for SoundWire based AMD platforms"
+ default SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
+ depends on ACPI
+ depends on SOUNDWIRE_AMD
+ help
+ This adds support for SoundWire with Sound Open Firmware
+ for AMD platforms.
+ Say Y if you want to enable SoundWire links with SOF.
+ If unsure select "N".
+
config SND_SOC_SOF_AMD_ACP63
tristate "SOF support for ACP6.3 platform"
depends on SND_SOC_SOF_PCI
select SND_SOC_SOF_AMD_COMMON
+ select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
help
Select this option for SOF support on
AMD ACP6.3 version based platforms.
diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c
index 2d72c6d55d..0fc4e20ec6 100644
--- a/sound/soc/sof/amd/acp-common.c
+++ b/sound/soc/sof/amd/acp-common.c
@@ -118,16 +118,72 @@ void amd_sof_dump(struct snd_sof_dev *sdev, u32 flags)
&panic_info, stack, AMD_STACK_DUMP_SIZE);
}
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
+static int amd_sof_sdw_get_slave_info(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
+
+ return sdw_amd_get_slave_info(acp_data->sdw);
+}
+
+static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev)
+{
+ struct snd_soc_acpi_mach *mach;
+ const struct snd_soc_acpi_link_adr *link;
+ struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
+ int ret, i;
+
+ if (acp_data->info.count) {
+ ret = amd_sof_sdw_get_slave_info(sdev);
+ if (ret) {
+ dev_info(sdev->dev, "failed to read slave information\n");
+ return NULL;
+ }
+ for (mach = sdev->pdata->desc->alt_machines; mach; mach++) {
+ if (!mach->links)
+ break;
+ link = mach->links;
+ for (i = 0; i < acp_data->info.count && link->num_adr; link++, i++) {
+ if (!snd_soc_acpi_sdw_link_slaves_found(sdev->dev, link,
+ acp_data->sdw->ids,
+ acp_data->sdw->num_slaves))
+ break;
+ }
+ if (i == acp_data->info.count || !link->num_adr)
+ break;
+ }
+ if (mach && mach->link_mask) {
+ mach->mach_params.links = mach->links;
+ mach->mach_params.link_mask = mach->link_mask;
+ mach->mach_params.platform = dev_name(sdev->dev);
+ return mach;
+ }
+ }
+ dev_info(sdev->dev, "No SoundWire machine driver found\n");
+ return NULL;
+}
+
+#else
+static struct snd_soc_acpi_mach *amd_sof_sdw_machine_select(struct snd_sof_dev *sdev)
+{
+ return NULL;
+}
+#endif
+
struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
- struct snd_soc_acpi_mach *mach;
+ struct snd_soc_acpi_mach *mach = NULL;
- mach = snd_soc_acpi_find_machine(desc->machines);
+ if (desc->machines)
+ mach = snd_soc_acpi_find_machine(desc->machines);
if (!mach) {
- dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
- return NULL;
+ mach = amd_sof_sdw_machine_select(sdev);
+ if (!mach) {
+ dev_warn(sdev->dev, "No matching ASoC machine driver found\n");
+ return NULL;
+ }
}
sof_pdata->tplg_filename = mach->sof_tplg_filename;
@@ -204,5 +260,6 @@ EXPORT_SYMBOL_NS(sof_acp_common_ops, SND_SOC_SOF_AMD_COMMON);
MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);
+MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT);
MODULE_DESCRIPTION("ACP SOF COMMON Driver");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index a913f1cc4c..59afbe2e0f 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -65,7 +65,10 @@
/* Registers from ACP_INTR block */
#define ACP3X_EXT_INTR_STAT 0x1808
#define ACP5X_EXT_INTR_STAT 0x1808
+#define ACP6X_EXTERNAL_INTR_ENB 0x1A00
+#define ACP6X_EXTERNAL_INTR_CNTL 0x1A04
#define ACP6X_EXT_INTR_STAT 0x1A0C
+#define ACP6X_EXT_INTR_STAT1 0x1A10
#define ACP3X_DSP_SW_INTR_BASE 0x1814
#define ACP5X_DSP_SW_INTR_BASE 0x1814
@@ -78,6 +81,10 @@
#define ACP5X_AXI2DAGB_SEM_0 0x1884
#define ACP6X_AXI2DAGB_SEM_0 0x1874
+/* ACP common registers to report errors related to I2S & SoundWire interfaces */
+#define ACP_SW0_I2S_ERROR_REASON 0x18B4
+#define ACP_SW1_I2S_ERROR_REASON 0x1A50
+
/* Registers from ACP_SHA block */
#define ACP_SHA_DSP_FW_QUALIFIER 0x1C70
#define ACP_SHA_DMA_CMD 0x1CB0
@@ -96,4 +103,7 @@
/* Cache window registers */
#define ACP_DSP0_CACHE_OFFSET0 0x0420
#define ACP_DSP0_CACHE_SIZE0 0x0424
+
+#define ACP_SW0_EN 0x3000
+#define ACP_SW1_EN 0x3C00
#endif
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 1b6f5724c8..c12c7f8205 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -376,10 +376,13 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
static irqreturn_t acp_irq_handler(int irq, void *dev_id)
{
+ struct amd_sdw_manager *amd_manager;
struct snd_sof_dev *sdev = dev_id;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int base = desc->dsp_intr_base;
unsigned int val;
+ int irq_flag = 0;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
if (val & ACP_DSP_TO_HOST_IRQ) {
@@ -388,7 +391,38 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
- return IRQ_NONE;
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat);
+ if (val & ACP_SDW0_IRQ_MASK) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_SDW0_IRQ_MASK);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+
+ if (val & ACP_ERROR_IRQ_MASK) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW0_I2S_ERROR_REASON, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_SW1_I2S_ERROR_REASON, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + ACP_ERROR_STATUS, 0);
+ irq_flag = 1;
+ }
+
+ if (desc->ext_intr_stat1) {
+ val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
+ if (val & ACP_SDW1_IRQ_MASK) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
+ ACP_SDW1_IRQ_MASK);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ irq_flag = 1;
+ }
+ }
+ if (irq_flag)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
}
static int acp_power_on(struct snd_sof_dev *sdev)
@@ -444,6 +478,33 @@ static int acp_reset(struct snd_sof_dev *sdev)
if (desc->ext_intr_enb)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01);
+ if (desc->ext_intr_cntl)
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK);
+ return ret;
+}
+
+static int acp_dsp_reset(struct snd_sof_dev *sdev)
+{
+ unsigned int val;
+ int ret;
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_ASSERT_RESET);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val,
+ val & ACP_DSP_SOFT_RESET_DONE_MASK,
+ ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(sdev->dev, "timeout asserting reset\n");
+ return ret;
+ }
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_DSP_RELEASE_RESET);
+
+ ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val,
+ ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(sdev->dev, "timeout in releasing reset\n");
+
return ret;
}
@@ -463,10 +524,34 @@ static int acp_init(struct snd_sof_dev *sdev)
return acp_reset(sdev);
}
+static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *acp_data;
+ u32 sdw0_en, sdw1_en;
+
+ acp_data = sdev->pdata->hw_pdata;
+ if (!acp_data->sdw)
+ return false;
+
+ sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN);
+ sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN);
+ acp_data->sdw_en_stat = sdw0_en || sdw1_en;
+ return acp_data->sdw_en_stat;
+}
+
int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state)
{
int ret;
+ /* When acp_reset() function is invoked, it will apply ACP SOFT reset and
+ * DSP reset. ACP Soft reset sequence will cause all ACP IP registers will
+ * be reset to default values which will break the ClockStop Mode functionality.
+ * Add a condition check to apply DSP reset when SoundWire ClockStop mode
+ * is selected. For the rest of the scenarios, apply acp reset sequence.
+ */
+ if (check_acp_sdw_enable_status(sdev))
+ return acp_dsp_reset(sdev);
+
ret = acp_reset(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Reset failed\n");
@@ -482,16 +567,97 @@ EXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON);
int amd_sof_acp_resume(struct snd_sof_dev *sdev)
{
int ret;
+ struct acp_dev_data *acp_data;
- ret = acp_init(sdev);
- if (ret) {
- dev_err(sdev->dev, "ACP Init failed\n");
- return ret;
+ acp_data = sdev->pdata->hw_pdata;
+ if (!acp_data->sdw_en_stat) {
+ ret = acp_init(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "ACP Init failed\n");
+ return ret;
+ }
+ return acp_memory_init(sdev);
+ } else {
+ return acp_dsp_reset(sdev);
}
- return acp_memory_init(sdev);
}
EXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON);
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
+static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
+{
+ struct acpi_device *sdw_dev;
+ struct acp_dev_data *acp_data;
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+
+ if (!addr)
+ return -ENODEV;
+
+ acp_data = sdev->pdata->hw_pdata;
+ sdw_dev = acpi_find_child_device(ACPI_COMPANION(sdev->dev), addr, 0);
+ if (!sdw_dev)
+ return -ENODEV;
+
+ acp_data->info.handle = sdw_dev->handle;
+ acp_data->info.count = desc->sdw_max_link_count;
+
+ return amd_sdw_scan_controller(&acp_data->info);
+}
+
+static int amd_sof_sdw_probe(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *acp_data;
+ struct sdw_amd_res sdw_res;
+ int ret;
+
+ acp_data = sdev->pdata->hw_pdata;
+
+ memset(&sdw_res, 0, sizeof(sdw_res));
+ sdw_res.addr = acp_data->addr;
+ sdw_res.reg_range = acp_data->reg_range;
+ sdw_res.handle = acp_data->info.handle;
+ sdw_res.parent = sdev->dev;
+ sdw_res.dev = sdev->dev;
+ sdw_res.acp_lock = &acp_data->acp_lock;
+ sdw_res.count = acp_data->info.count;
+ sdw_res.link_mask = acp_data->info.link_mask;
+ sdw_res.mmio_base = sdev->bar[ACP_DSP_BAR];
+
+ ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+ if (ret)
+ dev_err(sdev->dev, "SoundWire probe failed\n");
+ return ret;
+}
+
+static int amd_sof_sdw_exit(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *acp_data;
+
+ acp_data = sdev->pdata->hw_pdata;
+ if (acp_data->sdw)
+ sdw_amd_exit(acp_data->sdw);
+ acp_data->sdw = NULL;
+
+ return 0;
+}
+
+#else
+static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
+{
+ return 0;
+}
+
+static int amd_sof_sdw_probe(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static int amd_sof_sdw_exit(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+#endif
+
int amd_sof_acp_probe(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
@@ -527,7 +693,9 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
}
pci_set_master(pci);
-
+ adata->addr = addr;
+ adata->reg_range = chip->reg_end_addr - chip->reg_start_addr;
+ mutex_init(&adata->acp_lock);
sdev->pdata->hw_pdata = adata;
adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
if (!adata->smn_dev) {
@@ -549,6 +717,21 @@ int amd_sof_acp_probe(struct snd_sof_dev *sdev)
goto free_smn_dev;
}
+ /* scan SoundWire capabilities exposed by DSDT */
+ ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr);
+ if (ret < 0) {
+ dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n");
+ goto skip_soundwire;
+ }
+ ret = amd_sof_sdw_probe(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: SoundWire probe error\n");
+ free_irq(sdev->ipc_irq, sdev);
+ pci_dev_put(adata->smn_dev);
+ return ret;
+ }
+
+skip_soundwire:
sdev->dsp_box.offset = 0;
sdev->dsp_box.size = BOX_SIZE_512;
@@ -605,6 +788,9 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev)
if (adata->smn_dev)
pci_dev_put(adata->smn_dev);
+ if (adata->sdw)
+ amd_sof_sdw_exit(sdev);
+
if (sdev->ipc_irq)
free_irq(sdev->ipc_irq, sdev);
@@ -616,4 +802,6 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev)
EXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
MODULE_DESCRIPTION("AMD ACP sof driver");
+MODULE_IMPORT_NS(SOUNDWIRE_AMD_INIT);
+MODULE_IMPORT_NS(SND_AMD_SOUNDWIRE_ACPI);
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 7d4a8b6b3c..e229bb6b84 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -12,7 +12,7 @@
#define __SOF_AMD_ACP_H
#include <linux/dmi.h>
-
+#include <linux/soundwire/sdw_amd.h>
#include "../sof-priv.h"
#include "../sof-audio.h"
@@ -31,6 +31,9 @@
#define ACP_ASSERT_RESET 0x01
#define ACP_RELEASE_RESET 0x00
#define ACP_SOFT_RESET_DONE_MASK 0x00010001
+#define ACP_DSP_ASSERT_RESET 0x04
+#define ACP_DSP_RELEASE_RESET 0x00
+#define ACP_DSP_SOFT_RESET_DONE_MASK 0x00050004
#define ACP_DSP_INTR_EN_MASK 0x00000001
#define ACP3X_SRAM_PTE_OFFSET 0x02050000
@@ -93,8 +96,13 @@
#define PROBE_STATUS_BIT BIT(31)
#define ACP_FIRMWARE_SIGNATURE 0x100
+#define ACP_ERROR_IRQ_MASK BIT(29)
+#define ACP_SDW0_IRQ_MASK BIT(21)
+#define ACP_SDW1_IRQ_MASK BIT(2)
+#define SDW_ACPI_ADDR_ACP63 5
#define ACP_DEFAULT_SRAM_LENGTH 0x00080000
#define ACP_SRAM_PAGE_COUNT 128
+#define ACP6X_SDW_MAX_MANAGER_COUNT 2
enum clock_source {
ACP_CLOCK_96M = 0,
@@ -184,13 +192,19 @@ struct sof_amd_acp_desc {
unsigned int host_bridge_id;
u32 pgfsm_base;
u32 ext_intr_enb;
+ u32 ext_intr_cntl;
u32 ext_intr_stat;
+ u32 ext_intr_stat1;
u32 dsp_intr_base;
u32 sram_pte_offset;
u32 hw_semaphore_offset;
u32 acp_clkmux_sel;
u32 fusion_dsp_offset;
u32 probe_reg_offset;
+ u32 reg_start_addr;
+ u32 reg_end_addr;
+ u32 sdw_max_link_count;
+ u64 sdw_acpi_dev_addr;
};
struct acp_quirk_entry {
@@ -204,6 +218,12 @@ struct acp_dev_data {
const struct firmware *fw_dbin;
/* DMIC device */
struct platform_device *dmic_dev;
+ /* mutex lock to protect ACP common registers access */
+ struct mutex acp_lock;
+ /* ACPI information stored between scan and probe steps */
+ struct sdw_amd_acpi_info info;
+ /* sdw context allocated by SoundWire driver */
+ struct sdw_amd_ctx *sdw;
unsigned int fw_bin_size;
unsigned int fw_data_bin_size;
unsigned int fw_sram_data_bin_size;
@@ -212,6 +232,9 @@ struct acp_dev_data {
const char *fw_sram_data_bin;
u32 fw_bin_page_count;
u32 fw_data_bin_page_count;
+ u32 addr;
+ u32 reg_range;
+ u32 blk_type;
dma_addr_t sha_dma_addr;
u8 *bin_buf;
dma_addr_t dma_addr;
@@ -227,6 +250,7 @@ struct acp_dev_data {
bool enable_fw_debug;
bool is_dram_in_use;
bool is_sram_in_use;
+ bool sdw_en_stat;
};
void memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes);
diff --git a/sound/soc/sof/amd/pci-acp63.c b/sound/soc/sof/amd/pci-acp63.c
index bceb94ac80..eeaa12cceb 100644
--- a/sound/soc/sof/amd/pci-acp63.c
+++ b/sound/soc/sof/amd/pci-acp63.c
@@ -31,12 +31,19 @@ static const struct sof_amd_acp_desc acp63_chip_info = {
.rev = 6,
.host_bridge_id = HOST_BRIDGE_ACP63,
.pgfsm_base = ACP6X_PGFSM_BASE,
+ .ext_intr_enb = ACP6X_EXTERNAL_INTR_ENB,
+ .ext_intr_cntl = ACP6X_EXTERNAL_INTR_CNTL,
.ext_intr_stat = ACP6X_EXT_INTR_STAT,
+ .ext_intr_stat1 = ACP6X_EXT_INTR_STAT1,
.dsp_intr_base = ACP6X_DSP_SW_INTR_BASE,
.sram_pte_offset = ACP6X_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0,
.fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL,
.probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0,
+ .sdw_max_link_count = ACP6X_SDW_MAX_MANAGER_COUNT,
+ .sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP63,
+ .reg_start_addr = ACP6x_REG_START,
+ .reg_end_addr = ACP6x_REG_END,
};
static const struct sof_dev_desc acp63_desc = {
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 425b023b03..238bda5f6b 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -339,8 +339,7 @@ static int sof_init_environment(struct snd_sof_dev *sdev)
ret = snd_sof_probe(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to probe DSP %d\n", ret);
- sof_ops_free(sdev);
- return ret;
+ goto err_sof_probe;
}
/* check machine info */
@@ -351,22 +350,27 @@ static int sof_init_environment(struct snd_sof_dev *sdev)
}
ret = sof_select_ipc_and_paths(sdev);
- if (!ret && plat_data->ipc_type != base_profile->ipc_type) {
+ if (ret) {
+ goto err_machine_check;
+ } else if (plat_data->ipc_type != base_profile->ipc_type) {
/* IPC type changed, re-initialize the ops */
sof_ops_free(sdev);
ret = validate_sof_ops(sdev);
if (ret < 0) {
snd_sof_remove(sdev);
+ snd_sof_remove_late(sdev);
return ret;
}
}
+ return 0;
+
err_machine_check:
- if (ret) {
- snd_sof_remove(sdev);
- sof_ops_free(sdev);
- }
+ snd_sof_remove(sdev);
+err_sof_probe:
+ snd_sof_remove_late(sdev);
+ sof_ops_free(sdev);
return ret;
}
@@ -679,6 +683,16 @@ int snd_sof_device_remove(struct device *dev)
*/
snd_sof_machine_unregister(sdev, pdata);
+ /*
+ * Balance the runtime pm usage count in case we are faced with an
+ * exception and we forcably prevented D3 power state to preserve
+ * context
+ */
+ if (sdev->d3_prevented) {
+ sdev->d3_prevented = false;
+ pm_runtime_put_noidle(sdev->dev);
+ }
+
if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
sof_fw_trace_free(sdev);
ret = snd_sof_dsp_power_down_notify(sdev);
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index d547318e0d..7275437ea8 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -330,14 +330,32 @@ EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init);
int snd_sof_dbg_init(struct snd_sof_dev *sdev)
{
+ struct snd_sof_pdata *plat_data = sdev->pdata;
struct snd_sof_dsp_ops *ops = sof_ops(sdev);
const struct snd_sof_debugfs_map *map;
+ struct dentry *fw_profile;
int i;
int err;
/* use "sof" as top level debugFS dir */
sdev->debugfs_root = debugfs_create_dir("sof", NULL);
+ /* expose firmware/topology prefix/names for test purposes */
+ fw_profile = debugfs_create_dir("fw_profile", sdev->debugfs_root);
+
+ debugfs_create_str("fw_path", 0444, fw_profile,
+ (char **)&plat_data->fw_filename_prefix);
+ debugfs_create_str("fw_lib_path", 0444, fw_profile,
+ (char **)&plat_data->fw_lib_prefix);
+ debugfs_create_str("tplg_path", 0444, fw_profile,
+ (char **)&plat_data->tplg_filename_prefix);
+ debugfs_create_str("fw_name", 0444, fw_profile,
+ (char **)&plat_data->fw_filename);
+ debugfs_create_str("tplg_name", 0444, fw_profile,
+ (char **)&plat_data->tplg_filename);
+ debugfs_create_u32("ipc_type", 0444, fw_profile,
+ (u32 *)&plat_data->ipc_type);
+
/* init dfsentry list */
INIT_LIST_HEAD(&sdev->dfsentry_list);
@@ -433,13 +451,15 @@ static void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg)
{
- if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
- sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) {
+ if ((IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
+ sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) && !sdev->d3_prevented) {
/* should we prevent DSP entering D3 ? */
if (!sdev->ipc_dump_printed)
dev_info(sdev->dev,
"Attempting to prevent DSP from entering D3 state to preserve context\n");
- pm_runtime_get_if_in_use(sdev->dev);
+
+ if (pm_runtime_get_if_in_use(sdev->dev) == 1)
+ sdev->d3_prevented = true;
}
/* dump vital information to the logs */
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index d777e70250..07f51489d6 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -607,7 +607,22 @@ static struct snd_sof_dsp_ops sof_imx8x_ops = {
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
};
+static struct snd_sof_of_mach sof_imx8_machs[] = {
+ {
+ .compatible = "fsl,imx8qxp-mek",
+ .sof_tplg_filename = "sof-imx8-wm8960.tplg",
+ .drv_name = "asoc-audio-graph-card2",
+ },
+ {
+ .compatible = "fsl,imx8qm-mek",
+ .sof_tplg_filename = "sof-imx8-wm8960.tplg",
+ .drv_name = "asoc-audio-graph-card2",
+ },
+ {}
+};
+
static struct sof_dev_desc sof_of_imx8qxp_desc = {
+ .of_machines = sof_imx8_machs,
.ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
.ipc_default = SOF_IPC_TYPE_3,
.default_fw_path = {
@@ -624,6 +639,7 @@ static struct sof_dev_desc sof_of_imx8qxp_desc = {
};
static struct sof_dev_desc sof_of_imx8qm_desc = {
+ .of_machines = sof_imx8_machs,
.ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
.ipc_default = SOF_IPC_TYPE_3,
.default_fw_path = {
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
index 1b976fa500..222cd1467d 100644
--- a/sound/soc/sof/imx/imx8m.c
+++ b/sound/soc/sof/imx/imx8m.c
@@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8m_ops = {
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
};
+static struct snd_sof_of_mach sof_imx8mp_machs[] = {
+ {
+ .compatible = "fsl,imx8mp-evk",
+ .sof_tplg_filename = "sof-imx8mp-wm8960.tplg",
+ .drv_name = "asoc-audio-graph-card2",
+ },
+ {}
+};
+
static struct sof_dev_desc sof_of_imx8mp_desc = {
+ .of_machines = sof_imx8mp_machs,
.ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
.ipc_default = SOF_IPC_TYPE_3,
.default_fw_path = {
diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c
index 2badca7578..7b527ffde4 100644
--- a/sound/soc/sof/imx/imx8ulp.c
+++ b/sound/soc/sof/imx/imx8ulp.c
@@ -476,7 +476,17 @@ static struct snd_sof_dsp_ops sof_imx8ulp_ops = {
.set_power_state = imx8ulp_dsp_set_power_state,
};
+static struct snd_sof_of_mach sof_imx8ulp_machs[] = {
+ {
+ .compatible = "fsl,imx8ulp-evk",
+ .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
+ .drv_name = "asoc-audio-graph-card2",
+ },
+ {}
+};
+
static struct sof_dev_desc sof_of_imx8ulp_desc = {
+ .of_machines = sof_imx8ulp_machs,
.ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
.ipc_default = SOF_IPC_TYPE_3,
.default_fw_path = {
diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c
index 88c236b9a0..d71bb66b99 100644
--- a/sound/soc/sof/intel/hda-common-ops.c
+++ b/sound/soc/sof/intel/hda-common-ops.c
@@ -86,6 +86,7 @@ struct snd_sof_dsp_ops sof_hda_common_ops = {
/* DAI drivers */
.drv = skl_dai,
.num_drv = SOF_SKL_NUM_DAIS,
+ .is_chain_dma_supported = hda_is_chain_dma_supported,
/* PM */
.suspend = hda_dsp_suspend,
diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c
index 92ec5db467..b073720b4c 100644
--- a/sound/soc/sof/intel/hda-dai-ops.c
+++ b/sound/soc/sof/intel/hda-dai-ops.c
@@ -533,6 +533,17 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
.get_hlink = hda_get_hlink,
};
+static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
+ .get_hext_stream = hda_get_hext_stream,
+ .assign_hext_stream = hda_assign_hext_stream,
+ .release_hext_stream = hda_release_hext_stream,
+ .setup_hext_stream = hda_setup_hext_stream,
+ .reset_hext_stream = hda_reset_hext_stream,
+ .trigger = hda_trigger,
+ .calc_stream_format = generic_calc_stream_format,
+ .get_hlink = sdw_get_hlink,
+};
+
static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
struct snd_pcm_substream *substream, int cmd)
{
@@ -607,6 +618,13 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
.get_hlink = hda_get_hlink,
};
+static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {
+ .get_hext_stream = hda_dspless_get_hext_stream,
+ .setup_hext_stream = hda_dspless_setup_hext_stream,
+ .calc_stream_format = generic_calc_stream_format,
+ .get_hlink = sdw_get_hlink,
+};
+
#endif
const struct hda_dai_widget_dma_ops *
@@ -614,12 +632,24 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
{
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
struct snd_sof_dai *sdai;
+ const struct sof_intel_dsp_desc *chip;
- if (sdev->dspless_mode_selected)
- return &hda_dspless_dma_ops;
-
+ chip = get_chip_info(sdev->pdata);
sdai = swidget->private;
+ if (sdev->dspless_mode_selected) {
+ switch (sdai->type) {
+ case SOF_DAI_INTEL_HDA:
+ return &hda_dspless_dma_ops;
+ case SOF_DAI_INTEL_ALH:
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return NULL;
+ return &sdw_dspless_dma_ops;
+ default:
+ return NULL;
+ }
+ }
+
switch (sdev->pdata->ipc_type) {
case SOF_IPC_TYPE_3:
{
@@ -631,22 +661,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
}
case SOF_IPC_TYPE_4:
{
- struct sof_ipc4_copier *ipc4_copier = sdai->private;
- const struct sof_intel_dsp_desc *chip;
-
- chip = get_chip_info(sdev->pdata);
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
- switch (ipc4_copier->dai_type) {
+ switch (sdai->type) {
case SOF_DAI_INTEL_HDA:
- {
- struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
- struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
if (pipeline->use_chain_dma)
return &hda_ipc4_chain_dma_ops;
return &hda_ipc4_dma_ops;
- }
case SOF_DAI_INTEL_SSP:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL;
@@ -658,6 +681,8 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg
case SOF_DAI_INTEL_ALH:
if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
return NULL;
+ if (pipeline->use_chain_dma)
+ return &sdw_ipc4_chain_dma_ops;
return &sdw_ipc4_dma_ops;
default:
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index 0e665c0840..c1682bcdb5 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -83,12 +83,13 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai
sdev = widget_to_sdev(w);
- /*
- * The swidget parameter of hda_select_dai_widget_ops() is ignored in
- * case of DSPless mode
- */
+ if (!swidget) {
+ dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
+ return NULL;
+ }
+
if (sdev->dspless_mode_selected)
- return hda_select_dai_widget_ops(sdev, NULL);
+ return hda_select_dai_widget_ops(sdev, swidget);
sdai = swidget->private;
@@ -368,8 +369,11 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- /* get stream_id */
sdev = widget_to_sdev(w);
+ if (sdev->dspless_mode_selected)
+ goto skip_tlv;
+
+ /* get stream_id */
hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
if (!hext_stream) {
@@ -402,6 +406,7 @@ static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
dma_config->dma_priv_config_size = 0;
+skip_tlv:
return 0;
}
@@ -434,17 +439,10 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
int link_id)
{
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
const struct hda_dai_widget_dma_ops *ops;
- struct snd_soc_dai_link_ch_map *ch_maps;
struct hdac_ext_stream *hext_stream;
- struct snd_soc_dai *dai;
struct snd_sof_dev *sdev;
- bool cpu_dai_found = false;
- int cpu_dai_id;
- int ch_mask;
int ret;
- int j;
ret = non_hda_dai_hw_params(substream, params, cpu_dai);
if (ret < 0) {
@@ -459,29 +457,9 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
if (!hext_stream)
return -ENODEV;
- /*
- * in the case of SoundWire we need to program the PCMSyCM registers. In case
- * of aggregated devices, we need to define the channel mask for each sublink
- * by reconstructing the split done in soc-pcm.c
- */
- for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) {
- if (dai == cpu_dai) {
- cpu_dai_found = true;
- break;
- }
- }
-
- if (!cpu_dai_found)
- return -ENODEV;
-
- ch_mask = 0;
- for_each_link_ch_maps(rtd->dai_link, j, ch_maps) {
- if (ch_maps->cpu == cpu_dai_id)
- ch_mask |= ch_maps->ch_mask;
- }
-
+ /* in the case of SoundWire we need to program the PCMSyCM registers */
ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
- ch_mask,
+ GENMASK(params_channels(params) - 1, 0),
hdac_stream(hext_stream)->stream_tag,
substream->stream);
if (ret < 0) {
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index 1506982a56..ef5c915db8 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -758,6 +758,7 @@ skip_dsp:
static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
{
+ const struct sof_intel_dsp_desc *chip;
int ret;
/* display codec must be powered before link reset */
@@ -790,6 +791,10 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
hda_dsp_ctrl_ppcap_int_enable(sdev, true);
}
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0)
+ hda_sdw_int_enable(sdev, true);
+
cleanup:
/* display codec can powered off after controller init */
hda_codec_i915_display_power(sdev, false);
diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c
index c603aaf0ca..0c189d3b19 100644
--- a/sound/soc/sof/intel/hda-stream.c
+++ b/sound/soc/sof/intel/hda-stream.c
@@ -21,6 +21,7 @@
#include <trace/events/sof_intel.h>
#include "../ops.h"
#include "../sof-audio.h"
+#include "../ipc4-priv.h"
#include "hda.h"
#define HDA_LTRP_GB_VALUE_US 95
@@ -937,6 +938,14 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
/* store total stream count (playback + capture) from GCAP */
sof_hda->stream_max = num_total;
+ /* store stream count from GCAP required for CHAIN_DMA */
+ if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ ipc4_data->num_playback_streams = num_playback;
+ ipc4_data->num_capture_streams = num_capture;
+ }
+
return 0;
}
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index fe4ae349da..7fe72b0654 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -46,44 +46,83 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8
-static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
+static void hda_get_interfaces(struct snd_sof_dev *sdev, u32 *interface_mask)
{
const struct sof_intel_dsp_desc *chip;
- u32 interface_mask[2] = { 0 };
chip = get_chip_info(sdev->pdata);
switch (chip->hw_ip_version) {
case SOF_INTEL_TANGIER:
case SOF_INTEL_BAYTRAIL:
case SOF_INTEL_BROADWELL:
- interface_mask[0] = BIT(SOF_DAI_INTEL_SSP);
+ interface_mask[SOF_DAI_DSP_ACCESS] = BIT(SOF_DAI_INTEL_SSP);
break;
case SOF_INTEL_CAVS_1_5:
case SOF_INTEL_CAVS_1_5_PLUS:
- interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
- BIT(SOF_DAI_INTEL_HDA);
- interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+ interface_mask[SOF_DAI_DSP_ACCESS] =
+ BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) | BIT(SOF_DAI_INTEL_HDA);
+ interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
break;
case SOF_INTEL_CAVS_1_8:
case SOF_INTEL_CAVS_2_0:
case SOF_INTEL_CAVS_2_5:
case SOF_INTEL_ACE_1_0:
- interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
- BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
- interface_mask[1] = BIT(SOF_DAI_INTEL_HDA);
+ interface_mask[SOF_DAI_DSP_ACCESS] =
+ BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+ interface_mask[SOF_DAI_HOST_ACCESS] = BIT(SOF_DAI_INTEL_HDA);
break;
case SOF_INTEL_ACE_2_0:
- interface_mask[0] = BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
- BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
- interface_mask[1] = interface_mask[0]; /* all interfaces accessible without DSP */
+ interface_mask[SOF_DAI_DSP_ACCESS] =
+ BIT(SOF_DAI_INTEL_SSP) | BIT(SOF_DAI_INTEL_DMIC) |
+ BIT(SOF_DAI_INTEL_HDA) | BIT(SOF_DAI_INTEL_ALH);
+ /* all interfaces accessible without DSP */
+ interface_mask[SOF_DAI_HOST_ACCESS] =
+ interface_mask[SOF_DAI_DSP_ACCESS];
break;
default:
break;
}
+}
+
+static u32 hda_get_interface_mask(struct snd_sof_dev *sdev)
+{
+ u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
+
+ hda_get_interfaces(sdev, interface_mask);
return interface_mask[sdev->dspless_mode_selected];
}
+bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
+{
+ u32 interface_mask[SOF_DAI_ACCESS_NUM] = { 0 };
+ const struct sof_intel_dsp_desc *chip;
+
+ if (sdev->dspless_mode_selected)
+ return false;
+
+ hda_get_interfaces(sdev, interface_mask);
+
+ if (!(interface_mask[SOF_DAI_DSP_ACCESS] & BIT(dai_type)))
+ return false;
+
+ if (dai_type == SOF_DAI_INTEL_HDA)
+ return true;
+
+ switch (dai_type) {
+ case SOF_DAI_INTEL_SSP:
+ case SOF_DAI_INTEL_DMIC:
+ case SOF_DAI_INTEL_ALH:
+ chip = get_chip_info(sdev->pdata);
+ if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
+ return false;
+ return true;
+ default:
+ return false;
+ }
+}
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)
/*
@@ -1192,6 +1231,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
+ const struct sof_intel_dsp_desc *chip;
int ret = 0;
hdev->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
@@ -1305,12 +1345,28 @@ skip_dsp_setup:
INIT_DELAYED_WORK(&hdev->d0i3_work, hda_dsp_d0i3_work);
}
+ chip = get_chip_info(sdev->pdata);
+ if (chip && chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
+ ret = hda_sdw_startup(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "could not startup SoundWire links\n");
+ goto disable_pp_cap;
+ }
+
+ hda_sdw_int_enable(sdev, true);
+ }
+
init_waitqueue_head(&hdev->waitq);
hdev->nhlt = intel_nhlt_init(sdev->dev);
return 0;
+disable_pp_cap:
+ if (!sdev->dspless_mode_selected) {
+ hda_dsp_ctrl_ppcap_int_enable(sdev, false);
+ hda_dsp_ctrl_ppcap_enable(sdev, false);
+ }
free_ipc_irq:
free_irq(sdev->ipc_irq, sdev);
free_irq_vector:
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index efb42117df..81a1d4606d 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -574,6 +574,11 @@ struct sof_intel_hda_stream {
#define SOF_STREAM_SD_OFFSET_CRST 0x1
/*
+ * DAI support
+ */
+bool hda_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type);
+
+/*
* DSP Core services.
*/
int hda_dsp_probe_early(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sof/intel/lnl.c b/sound/soc/sof/intel/lnl.c
index d6c4d6cd20..aeb4350cce 100644
--- a/sound/soc/sof/intel/lnl.c
+++ b/sound/soc/sof/intel/lnl.c
@@ -16,7 +16,6 @@
#include "hda-ipc.h"
#include "../sof-audio.h"
#include "mtl.h"
-#include "lnl.h"
#include <sound/hda-mlink.h>
/* LunarLake ops */
@@ -30,15 +29,17 @@ static const struct snd_sof_debugfs_map lnl_dsp_debugfs[] = {
};
/* this helps allows the DSP to setup DMIC/SSP */
-static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus)
+static int hdac_bus_offload_dmic_ssp(struct hdac_bus *bus, bool enable)
{
int ret;
- ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP, true);
+ ret = hdac_bus_eml_enable_offload(bus, true,
+ AZX_REG_ML_LEPTR_ID_INTEL_SSP, enable);
if (ret < 0)
return ret;
- ret = hdac_bus_eml_enable_offload(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC, true);
+ ret = hdac_bus_eml_enable_offload(bus, true,
+ AZX_REG_ML_LEPTR_ID_INTEL_DMIC, enable);
if (ret < 0)
return ret;
@@ -53,7 +54,19 @@ static int lnl_hda_dsp_probe(struct snd_sof_dev *sdev)
if (ret < 0)
return ret;
- return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev));
+ return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+}
+
+static void lnl_hda_dsp_remove(struct snd_sof_dev *sdev)
+{
+ int ret;
+
+ ret = hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), false);
+ if (ret < 0)
+ dev_warn(sdev->dev,
+ "Failed to disable offload for DMIC/SSP: %d\n", ret);
+
+ hda_dsp_remove(sdev);
}
static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev)
@@ -64,7 +77,7 @@ static int lnl_hda_dsp_resume(struct snd_sof_dev *sdev)
if (ret < 0)
return ret;
- return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev));
+ return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
}
static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
@@ -75,7 +88,20 @@ static int lnl_hda_dsp_runtime_resume(struct snd_sof_dev *sdev)
if (ret < 0)
return ret;
- return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev));
+ return hdac_bus_offload_dmic_ssp(sof_to_bus(sdev), true);
+}
+
+static int lnl_dsp_post_fw_run(struct snd_sof_dev *sdev)
+{
+ if (sdev->first_boot) {
+ struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
+
+ /* Check if IMR boot is usable */
+ if (!sof_debug_check_flag(SOF_DBG_IGNORE_D3_PERSISTENT))
+ hda->imrboot_supported = true;
+ }
+
+ return 0;
}
int sof_lnl_ops_init(struct snd_sof_dev *sdev)
@@ -85,8 +111,11 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
/* common defaults */
memcpy(&sof_lnl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops));
- /* probe */
- sof_lnl_ops.probe = lnl_hda_dsp_probe;
+ /* probe/remove */
+ if (!sdev->dspless_mode_selected) {
+ sof_lnl_ops.probe = lnl_hda_dsp_probe;
+ sof_lnl_ops.remove = lnl_hda_dsp_remove;
+ }
/* shutdown */
sof_lnl_ops.shutdown = hda_dsp_shutdown;
@@ -107,7 +136,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
/* pre/post fw run */
sof_lnl_ops.pre_fw_run = mtl_dsp_pre_fw_run;
- sof_lnl_ops.post_fw_run = mtl_dsp_post_fw_run;
+ sof_lnl_ops.post_fw_run = lnl_dsp_post_fw_run;
/* parse platform specific extended manifest */
sof_lnl_ops.parse_platform_ext_manifest = NULL;
@@ -116,8 +145,10 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
/* TODO: add core_get and core_put */
/* PM */
- sof_lnl_ops.resume = lnl_hda_dsp_resume;
- sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume;
+ if (!sdev->dspless_mode_selected) {
+ sof_lnl_ops.resume = lnl_hda_dsp_resume;
+ sof_lnl_ops.runtime_resume = lnl_hda_dsp_runtime_resume;
+ }
/* dsp core get/put */
sof_lnl_ops.core_get = mtl_dsp_core_get;
@@ -177,7 +208,7 @@ const struct sof_intel_dsp_desc lnl_chip_info = {
.ipc_ack = MTL_DSP_REG_HFIPCXIDA,
.ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
.ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
- .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_status_reg = MTL_DSP_ROM_STS,
.rom_init_timeout = 300,
.ssp_count = MTL_SSP_COUNT,
.d0i3_offset = MTL_HDA_VS_D0I3C,
diff --git a/sound/soc/sof/intel/lnl.h b/sound/soc/sof/intel/lnl.h
deleted file mode 100644
index 4f4734fe7e..0000000000
--- a/sound/soc/sof/intel/lnl.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* 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) 2024 Intel Corporation. All rights reserved.
- */
-
-#ifndef __SOF_INTEL_LNL_H
-#define __SOF_INTEL_LNL_H
-
-#define LNL_DSP_REG_HFDSC 0x160200 /* DSP core0 status */
-#define LNL_DSP_REG_HFDEC 0x160204 /* DSP core0 error */
-
-#endif /* __SOF_INTEL_LNL_H */
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 0502376308..060c34988e 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -439,7 +439,7 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
{
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc;
- unsigned int status, target_status;
+ unsigned int status;
u32 ipc_hdr, flags;
char *dump_msg;
int ret;
@@ -485,40 +485,13 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot)
mtl_enable_ipc_interrupts(sdev);
- if (chip->rom_status_reg == MTL_DSP_ROM_STS) {
- /*
- * Workaround: when the ROM status register is pointing to
- * the SRAM window (MTL_DSP_ROM_STS) the platform cannot catch
- * ROM_INIT_DONE because of a very short timing window.
- * Follow the recommendations and skip target state waiting.
- */
- return 0;
- }
-
/*
- * step 7:
- * - Cold/Full boot: wait for ROM init to proceed to download the firmware
- * - IMR boot: wait for ROM firmware entered (firmware booted up from IMR)
+ * ACE workaround: don't wait for ROM INIT.
+ * The platform cannot catch ROM_INIT_DONE because of a very short
+ * timing window. Follow the recommendations and skip this part.
*/
- if (imr_boot)
- target_status = FSR_STATE_FW_ENTERED;
- else
- target_status = FSR_STATE_INIT_DONE;
- ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
- chip->rom_status_reg, status,
- (FSR_TO_STATE_CODE(status) == target_status),
- HDA_DSP_REG_POLL_INTERVAL_US,
- chip->rom_init_timeout *
- USEC_PER_MSEC);
-
- if (!ret)
- return 0;
-
- if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS)
- dev_err(sdev->dev,
- "%s: timeout with rom_status_reg (%#x) read\n",
- __func__, chip->rom_status_reg);
+ return 0;
err:
flags = SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_OPTIONAL;
@@ -530,7 +503,6 @@ err:
dump_msg = kasprintf(GFP_KERNEL, "Boot iteration failed: %d/%d",
hda->boot_iteration, HDA_FW_BOOT_ATTEMPTS);
snd_sof_dsp_dbg_dump(sdev, dump_msg, flags);
- mtl_enable_interrupts(sdev, false);
mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE);
kfree(dump_msg);
@@ -755,7 +727,7 @@ const struct sof_intel_dsp_desc mtl_chip_info = {
.ipc_ack = MTL_DSP_REG_HFIPCXIDA,
.ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
.ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
- .rom_status_reg = MTL_DSP_REG_HFFLGPXQWY,
+ .rom_status_reg = MTL_DSP_ROM_STS,
.rom_init_timeout = 300,
.ssp_count = MTL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
@@ -783,7 +755,7 @@ const struct sof_intel_dsp_desc arl_s_chip_info = {
.ipc_ack = MTL_DSP_REG_HFIPCXIDA,
.ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
.ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
- .rom_status_reg = MTL_DSP_REG_HFFLGPXQWY,
+ .rom_status_reg = MTL_DSP_ROM_STS,
.rom_init_timeout = 300,
.ssp_count = MTL_SSP_COUNT,
.ssp_base_offset = CNL_SSP_BASE_OFFSET,
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
index 3c56427a96..ea8c1b83f7 100644
--- a/sound/soc/sof/intel/mtl.h
+++ b/sound/soc/sof/intel/mtl.h
@@ -70,8 +70,8 @@
#define MTL_DSP_ROM_STS MTL_SRAM_WINDOW_OFFSET(0) /* ROM status */
#define MTL_DSP_ROM_ERROR (MTL_SRAM_WINDOW_OFFSET(0) + 0x4) /* ROM error code */
-#define MTL_DSP_REG_HFFLGPXQWY 0x163200 /* DSP core0 status */
-#define MTL_DSP_REG_HFFLGPXQWY_ERROR 0x163204 /* DSP core0 error */
+#define MTL_DSP_REG_HFFLGPXQWY 0x163200 /* ROM debug status */
+#define MTL_DSP_REG_HFFLGPXQWY_ERROR 0x163204 /* ROM debug error code */
#define MTL_DSP_REG_HfIMRIS1 0x162088
#define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0)
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index a7cf52fd76..af0bf354cb 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -395,6 +395,31 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
dev_dbg(component->dev, "MICFIL PDM channels_min: %d channels_max: %d\n",
channels->min, channels->max);
break;
+ case SOF_DAI_AMD_SDW:
+ /* change the default trigger sequence as per HW implementation */
+ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
+ struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+ fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
+ SND_SOC_DPCM_TRIGGER_POST;
+ }
+
+ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
+ struct snd_soc_pcm_runtime *fe = dpcm->fe;
+
+ fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
+ SND_SOC_DPCM_TRIGGER_POST;
+ }
+ rate->min = private->dai_config->acp_sdw.rate;
+ rate->max = private->dai_config->acp_sdw.rate;
+ channels->min = private->dai_config->acp_sdw.channels;
+ channels->max = private->dai_config->acp_sdw.channels;
+
+ dev_dbg(component->dev,
+ "AMD_SDW rate_min: %d rate_max: %d\n", rate->min, rate->max);
+ dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n",
+ channels->min, channels->max);
+ break;
default:
dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type);
break;
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index d47698f4be..ab7f46a162 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -298,6 +298,14 @@ static const struct sof_topology_token micfil_pdm_tokens[] = {
offsetof(struct sof_ipc_dai_micfil_params, pdm_ch)},
};
+/* ACP_SDW */
+static const struct sof_topology_token acp_sdw_tokens[] = {
+ {SOF_TKN_AMD_ACP_SDW_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_acp_sdw_params, rate)},
+ {SOF_TKN_AMD_ACP_SDW_CH, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc_dai_acp_sdw_params, channels)},
+};
+
/* Core tokens */
static const struct sof_topology_token core_tokens[] = {
{SOF_TKN_COMP_CORE_ID, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
@@ -336,6 +344,7 @@ static const struct sof_token_info ipc3_token_list[SOF_TOKEN_COUNT] = {
[SOF_ACPI2S_TOKENS] = {"ACPI2S tokens", acpi2s_tokens, ARRAY_SIZE(acpi2s_tokens)},
[SOF_MICFIL_TOKENS] = {"MICFIL PDM tokens",
micfil_pdm_tokens, ARRAY_SIZE(micfil_pdm_tokens)},
+ [SOF_ACP_SDW_TOKENS] = {"ACP_SDW tokens", acp_sdw_tokens, ARRAY_SIZE(acp_sdw_tokens)},
};
/**
@@ -1315,6 +1324,34 @@ static int sof_link_acp_hs_load(struct snd_soc_component *scomp, struct snd_sof_
return 0;
}
+static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
+ struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
+{
+ struct sof_dai_private_data *private = dai->private;
+ u32 size = sizeof(*config);
+ int ret;
+
+ /* parse the required set of ACP_SDW tokens based on num_hw_cfgs */
+ ret = sof_update_ipc_object(scomp, &config->acp_sdw, SOF_ACP_SDW_TOKENS, slink->tuples,
+ slink->num_tuples, size, slink->num_hw_configs);
+ if (ret < 0)
+ return ret;
+
+ /* init IPC */
+ config->hdr.size = size;
+ dev_dbg(scomp->dev, "ACP SDW config rate %d channels %d\n",
+ config->acp_sdw.rate, config->acp_sdw.channels);
+
+ /* set config for all DAI's with name matching the link name */
+ dai->number_configs = 1;
+ dai->current_config = 0;
+ private->dai_config = kmemdup(config, size, GFP_KERNEL);
+ if (!private->dai_config)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink,
struct sof_ipc_dai_config *config, struct snd_sof_dai *dai)
{
@@ -1629,6 +1666,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget)
case SOF_DAI_MEDIATEK_AFE:
ret = sof_link_afe_load(scomp, slink, config, dai);
break;
+ case SOF_DAI_AMD_SDW:
+ ret = sof_link_acp_sdw_load(scomp, slink, config, dai);
+ break;
default:
break;
}
diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c
index 9f1e33ee88..0e04bea943 100644
--- a/sound/soc/sof/ipc4-mtrace.c
+++ b/sound/soc/sof/ipc4-mtrace.c
@@ -4,6 +4,7 @@
#include <linux/debugfs.h>
#include <linux/sched/signal.h>
+#include <linux/sched/clock.h>
#include <sound/sof/ipc4/header.h>
#include "sof-priv.h"
#include "ipc4-priv.h"
@@ -412,7 +413,6 @@ static int ipc4_mtrace_enable(struct snd_sof_dev *sdev)
const struct sof_ipc_ops *iops = sdev->ipc->ops;
struct sof_ipc4_msg msg;
u64 system_time;
- ktime_t kt;
int ret;
if (priv->mtrace_state != SOF_MTRACE_DISABLED)
@@ -424,9 +424,12 @@ static int ipc4_mtrace_enable(struct snd_sof_dev *sdev)
msg.primary |= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID);
msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_SYSTEM_TIME);
- /* The system time is in usec, UTC, epoch is 1601-01-01 00:00:00 */
- kt = ktime_add_us(ktime_get_real(), FW_EPOCH_DELTA * USEC_PER_SEC);
- system_time = ktime_to_us(kt);
+ /*
+ * local_clock() is used to align with dmesg, so both kernel and firmware logs have
+ * the same base and a minor delta due to the IPC. system time is in us format but
+ * local_clock() returns the time in ns, so convert to ns.
+ */
+ system_time = div64_u64(local_clock(), NSEC_PER_USEC);
msg.data_size = sizeof(system_time);
msg.data_ptr = &system_time;
ret = iops->set_get_data(sdev, &msg, msg.data_size, true);
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index d07c1b0620..4594470ed0 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -40,9 +40,12 @@ struct sof_ipc4_timestamp_info {
/**
* struct sof_ipc4_pcm_stream_priv - IPC4 specific private data
* @time_info: pointer to time info struct if it is supported, otherwise NULL
+ * @chain_dma_allocated: indicates the ChainDMA allocation state
*/
struct sof_ipc4_pcm_stream_priv {
struct sof_ipc4_timestamp_info *time_info;
+
+ bool chain_dma_allocated;
};
static inline struct sof_ipc4_timestamp_info *
@@ -269,12 +272,17 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
*/
static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
+ struct snd_sof_pcm *spcm, int direction,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
int state, int cmd)
{
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_pcm_stream_priv *stream_priv;
bool allocate, enable, set_fifo_size;
struct sof_ipc4_msg msg = {{ 0 }};
- int i;
+ int ret, i;
+
+ stream_priv = spcm->stream[direction].private;
switch (state) {
case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */
@@ -295,6 +303,11 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
set_fifo_size = false;
break;
case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */
+
+ /* ChainDMA can only be reset if it has been allocated */
+ if (!stream_priv->chain_dma_allocated)
+ return 0;
+
allocate = false;
enable = false;
set_fifo_size = false;
@@ -332,13 +345,32 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
msg.extension |= pipeline->msg.extension;
}
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ /*
+ * For ChainDMA the DMA ids are unique with the following mapping:
+ * playback: 0 - (num_playback_streams - 1)
+ * capture: num_playback_streams - (num_playback_streams +
+ * num_capture_streams - 1)
+ *
+ * Add the num_playback_streams offset to the DMA ids stored in
+ * msg.primary in case capture
+ */
+ msg.primary += SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(ipc4_data->num_playback_streams);
+ msg.primary += SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(ipc4_data->num_playback_streams);
+ }
+
if (allocate)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
if (enable)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
- return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+ ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0);
+ /* Update the ChainDMA allocation state */
+ if (!ret)
+ stream_priv->chain_dma_allocated = allocate;
+
+ return ret;
}
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
@@ -378,7 +410,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
* trigger function that handles the rest for the substream.
*/
if (pipeline->use_chain_dma)
- return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd);
+ return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream,
+ pipeline_list, state, cmd);
/* allocate memory for the pipeline data */
trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids,
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 9db9002b44..afed618a15 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -66,6 +66,8 @@ struct sof_ipc4_fw_library {
* @nhlt: NHLT table either from the BIOS or the topology manifest
* @mtrace_type: mtrace type supported on the booted platform
* @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply
+ * @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset
+ * @num_capture_streams: max number of capture DMAs
* @max_num_pipelines: max number of pipelines
* @max_libs_count: Maximum number of libraries support by the FW including the
* base firmware
@@ -79,6 +81,8 @@ struct sof_ipc4_fw_data {
void *nhlt;
enum sof_ipc4_mtrace_type mtrace_type;
u32 mtrace_log_bytes;
+ int num_playback_streams;
+ int num_capture_streams;
int max_num_pipelines;
u32 max_libs_count;
bool fw_context_save;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 4d3d415179..5cca058421 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -529,6 +529,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
{
struct sof_ipc4_available_audio_format *available_fmt;
struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_copier *ipc4_copier;
struct snd_sof_widget *pipe_widget;
@@ -568,14 +569,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
dev_dbg(scomp->dev, "dai %s node_type %u dai_type %u dai_index %d\n", swidget->widget->name,
node_type, ipc4_copier->dai_type, ipc4_copier->dai_index);
+ dai->type = ipc4_copier->dai_type;
ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
- if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
- dev_err(scomp->dev,
- "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n",
- ipc4_copier->dai_type, SOF_DAI_INTEL_HDA);
+
+ if (pipeline->use_chain_dma &&
+ !snd_sof_is_chain_dma_supported(sdev, ipc4_copier->dai_type)) {
+ dev_err(scomp->dev, "Bad DAI type '%d', Chain DMA is not supported\n",
+ ipc4_copier->dai_type);
ret = -ENODEV;
goto free_available_fmt;
}
@@ -618,7 +621,11 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
}
ipc4_copier->copier_config = (uint32_t *)blob;
- ipc4_copier->data.gtw_cfg.config_length = sizeof(*blob) >> 2;
+ /* set data.gtw_cfg.config_length based on device_count */
+ ipc4_copier->data.gtw_cfg.config_length = (sizeof(blob->gw_attr) +
+ sizeof(blob->alh_cfg.device_count) +
+ sizeof(*blob->alh_cfg.mapping) *
+ blob->alh_cfg.device_count) >> 2;
break;
}
case SOF_DAI_INTEL_SSP:
@@ -1369,6 +1376,7 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
int sample_rate, channel_count;
int bit_depth, ret;
u32 nhlt_type;
+ int dev_type = 0;
/* convert to NHLT type */
switch (linktype) {
@@ -1384,18 +1392,30 @@ static int snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_s
&bit_depth);
if (ret < 0)
return ret;
+
+ /*
+ * We need to know the type of the external device attached to a SSP
+ * port to retrieve the blob from NHLT. However, device type is not
+ * specified in topology.
+ * Query the type for the port and then pass that information back
+ * to the blob lookup function.
+ */
+ dev_type = intel_nhlt_ssp_device_type(sdev->dev, ipc4_data->nhlt,
+ dai_index);
+ if (dev_type < 0)
+ return dev_type;
break;
default:
return 0;
}
- dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d\n",
- dai_index, nhlt_type, dir);
+ dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d dev type %d\n",
+ dai_index, nhlt_type, dir, dev_type);
/* find NHLT blob with matching params */
cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,
bit_depth, bit_depth, channel_count, sample_rate,
- dir, 0);
+ dir, dev_type);
if (!cfg) {
dev_err(sdev->dev,
@@ -2816,13 +2836,14 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
if (!data)
return 0;
+ if (pipeline->use_chain_dma) {
+ pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
+ pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
+ return 0;
+ }
+
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_HDA:
- if (pipeline->use_chain_dma) {
- pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
- pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
- break;
- }
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
fallthrough;
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 2505a1e1ce..3cd748e134 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -581,6 +581,15 @@ snd_sof_set_mach_params(struct snd_soc_acpi_mach *mach,
sof_ops(sdev)->set_mach_params(mach, sdev);
}
+static inline bool
+snd_sof_is_chain_dma_supported(struct snd_sof_dev *sdev, u32 dai_type)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->is_chain_dma_supported)
+ return sof_ops(sdev)->is_chain_dma_supported(sdev, dai_type);
+
+ return false;
+}
+
/**
* snd_sof_dsp_register_poll_timeout - Periodically poll an address
* until a condition is met or a timeout occurs
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 9163975c9c..e693dcb475 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -46,7 +46,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
{
const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_pipeline *spipe = swidget->spipe;
- struct snd_sof_widget *pipe_widget;
int err = 0;
int ret;
@@ -59,8 +58,6 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
if (--swidget->use_count)
return 0;
- pipe_widget = swidget->spipe->pipe_widget;
-
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
@@ -109,8 +106,9 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
* free the scheduler widget (same as pipe_widget) associated with the current swidget.
* skip for static pipelines
*/
- if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
- ret = sof_widget_free_unlocked(sdev, pipe_widget);
+ if (swidget->spipe && swidget->dynamic_pipeline_widget &&
+ swidget->id != snd_soc_dapm_scheduler) {
+ ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
if (ret < 0 && !err)
err = ret;
}
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 05e2a899d7..499b6084b5 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -284,6 +284,7 @@ enum sof_tokens {
SOF_ACPDMIC_TOKENS,
SOF_ACPI2S_TOKENS,
SOF_MICFIL_TOKENS,
+ SOF_ACP_SDW_TOKENS,
/* this should be the last */
SOF_TOKEN_COUNT,
@@ -522,6 +523,7 @@ struct snd_sof_route {
struct snd_sof_dai {
struct snd_soc_component *scomp;
const char *name;
+ u32 type;
int number_configs;
int current_config;
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 09eb2de445..d3c436f826 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -157,6 +157,13 @@ struct sof_firmware {
u32 payload_offset;
};
+enum sof_dai_access {
+ SOF_DAI_DSP_ACCESS, /* access from DSP only */
+ SOF_DAI_HOST_ACCESS, /* access from host only */
+
+ SOF_DAI_ACCESS_NUM
+};
+
/*
* SOF DSP HW abstraction operations.
* Used to abstract DSP HW architecture and any IO busses between host CPU
@@ -350,6 +357,8 @@ struct snd_sof_dsp_ops {
struct snd_soc_dai_driver *drv;
int num_drv;
+ bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */
+
/* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */
u32 hw_info;
@@ -607,6 +616,7 @@ struct snd_sof_dev {
struct list_head dfsentry_list;
bool dbg_dump_printed;
bool ipc_dump_printed;
+ bool d3_prevented; /* runtime pm use count incremented to prevent context lost */
/* firmware loader */
struct sof_ipc_fw_ready fw_ready;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 617a225fff..bcdb499c96 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -297,6 +297,7 @@ static const struct sof_dai_types sof_dais[] = {
{"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL},
{"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL},
{"MICFIL", SOF_DAI_IMX_MICFIL},
+ {"ACP_SDW", SOF_DAI_AMD_SDW},
};
@@ -1968,6 +1969,10 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
token_id = SOF_MICFIL_TOKENS;
num_tuples += token_list[SOF_MICFIL_TOKENS].count;
break;
+ case SOF_DAI_AMD_SDW:
+ token_id = SOF_ACP_SDW_TOKENS;
+ num_tuples += token_list[SOF_ACP_SDW_TOKENS].count;
+ break;
default:
break;
}
@@ -2349,25 +2354,43 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_dapm_widget *tw)
{
if (WIDGET_IS_DAI(w->id)) {
+ static const struct sof_topology_token dai_tokens[] = {
+ {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}};
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ struct snd_soc_tplg_private *priv = &tw->priv;
struct snd_sof_widget *swidget;
- struct snd_sof_dai dai;
+ struct snd_sof_dai *sdai;
int ret;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
if (!swidget)
return -ENOMEM;
- memset(&dai, 0, sizeof(dai));
+ sdai = kzalloc(sizeof(*sdai), GFP_KERNEL);
+ if (!sdai) {
+ kfree(swidget);
+ return -ENOMEM;
+ }
+
+ ret = sof_parse_tokens(scomp, &sdai->type, dai_tokens, ARRAY_SIZE(dai_tokens),
+ priv->array, le32_to_cpu(priv->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "Failed to parse DAI tokens for %s\n", tw->name);
+ kfree(swidget);
+ kfree(sdai);
+ return ret;
+ }
- ret = sof_connect_dai_widget(scomp, w, tw, &dai);
+ ret = sof_connect_dai_widget(scomp, w, tw, sdai);
if (ret) {
kfree(swidget);
+ kfree(sdai);
return ret;
}
swidget->scomp = scomp;
swidget->widget = w;
+ swidget->private = sdai;
mutex_init(&swidget->setup_mutex);
w->dobj.private = swidget;
list_add(&swidget->list, &sdev->widget_list);
@@ -2391,6 +2414,7 @@ static int sof_dspless_widget_unload(struct snd_soc_component *scomp,
/* remove and free swidget object */
list_del(&swidget->list);
+ kfree(swidget->private);
kfree(swidget);
}