diff options
Diffstat (limited to 'sound/soc/sof/amd')
-rw-r--r-- | sound/soc/sof/amd/Kconfig | 18 | ||||
-rw-r--r-- | sound/soc/sof/amd/acp-common.c | 65 | ||||
-rw-r--r-- | sound/soc/sof/amd/acp-dsp-offset.h | 10 | ||||
-rw-r--r-- | sound/soc/sof/amd/acp.c | 202 | ||||
-rw-r--r-- | sound/soc/sof/amd/acp.h | 26 | ||||
-rw-r--r-- | sound/soc/sof/amd/pci-acp63.c | 7 |
6 files changed, 316 insertions, 12 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 = { |