[PATCH 11/11] ASoC: SOF: amd: add SoundWire PM ops for ACP7.B/7.F
From: Vijendar Mukunda
Date: Thu Sep 10 2026 - 09:34:35 EST
Add handle_amd_sof_acp7x_sdw_pme_event() to iterate over all four
SoundWire managers on runtime PM resume, clear PME status and
wake-enable bits (under acp_lock to prevent races with the IRQ path),
and request runtime resume for each active manager.
Extend amd_sof_acp7x_suspend() with a clock-stop fast path: when
SoundWire managers are in clock-stop mode, write ZSC_DSP_CTRL=1 and
call acp_dsp_reset() instead of the full acp_reset() to preserve
SoundWire bus state. Extend amd_sof_acp7x_resume() with a matching
clock-stop resume path that restores ZSC_DSP_CTRL and re-arms PME_EN.
Introduce amd_sof_acp7x_suspend_runtime() and
amd_sof_acp7x_resume_runtime() as separate runtime PM callbacks.
The runtime resume path calls handle_amd_sof_acp7x_sdw_pme_event()
after re-initialising hardware to clear any PME state that arrived
during the suspend window.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@xxxxxxx>
---
sound/soc/sof/amd/acp.c | 104 +++++++++++++++++++++++++++++++++-------
sound/soc/sof/amd/acp.h | 1 +
2 files changed, 87 insertions(+), 18 deletions(-)
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index da987a51491c..b059039c9e0d 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -1365,6 +1365,9 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
dev_err(sdev->dev, "error: SoundWire probe error\n");
goto free_ipc_irq;
}
+ if (adata->info.link_mask)
+ adata->is_sdw_dev = true;
+
skip_soundwire:
if (adev) {
/* DMIC ACPI child address is 2 on ACP7x platforms */
@@ -1434,6 +1437,44 @@ void amd_sof_acp7x_remove(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON");
+static void handle_amd_sof_acp7x_sdw_pme_event(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *adata;
+ struct amd_sdw_manager *amd_manager;
+ u32 sdw_pme_stat;
+ u32 sdw_wake_en;
+ u32 pme_reg;
+ u32 wake_mask;
+ unsigned int instance;
+
+ adata = sdev->pdata->hw_pdata;
+ if (!adata->sdw)
+ return;
+
+ for (instance = 0; instance < ACP7X_SDW_MAX_MANAGER_COUNT; instance++) {
+ pme_reg = ACP7X_SW_PME_STS + (instance * 4);
+ wake_mask = ACP7X_SW_WAKE_EN_MASK << instance;
+
+ sdw_pme_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, pme_reg);
+ if (!sdw_pme_stat)
+ continue;
+
+ mutex_lock(&adata->acp_lock);
+ sdw_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN);
+ sdw_wake_en &= ~wake_mask;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN, sdw_wake_en);
+ mutex_unlock(&adata->acp_lock);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, pme_reg, sdw_pme_stat);
+
+ if (!adata->sdw->pdev[instance])
+ continue;
+
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[instance]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ }
+}
+
int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
{
struct acp_dev_data *acp_data;
@@ -1442,6 +1483,11 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
acp_data = sdev->pdata->hw_pdata;
+ if (acp_data->is_sdw_dev && check_acp_sdw_enable_status(sdev)) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1);
+ return acp_dsp_reset(sdev);
+ }
+
ret = acp_reset(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Reset failed\n");
@@ -1463,13 +1509,25 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
}
EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON");
-int amd_sof_acp7x_resume(struct snd_sof_dev *sdev)
+int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev)
+{
+ return amd_sof_acp7x_suspend(sdev, 0);
+}
+EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON");
+
+int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data;
int ret;
acp_data = sdev->pdata->hw_pdata;
+ if (acp_data->sdw_en_stat) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
+ return acp_dsp_reset(sdev);
+ }
+
ret = acp_init(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Init failed\n");
@@ -1481,30 +1539,40 @@ int amd_sof_acp7x_resume(struct snd_sof_dev *sdev)
return ret;
}
- switch (acp_data->pci_rev) {
- case ACP7B_PCI_ID:
- case ACP7F_PCI_ID:
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
- break;
- default:
- break;
- }
+ if (acp_data->is_sdw_dev)
+ handle_amd_sof_acp7x_sdw_pme_event(sdev);
return 0;
}
-EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON");
+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON");
-int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev)
+int amd_sof_acp7x_resume(struct snd_sof_dev *sdev)
{
- return amd_sof_acp7x_suspend(sdev, 0);
-}
-EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON");
+ struct acp_dev_data *acp_data;
+ int ret;
-int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev)
-{
- return amd_sof_acp7x_resume(sdev);
+ acp_data = sdev->pdata->hw_pdata;
+
+ if (acp_data->sdw_en_stat) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
+ return acp_dsp_reset(sdev);
+ }
+
+ ret = acp_init(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "ACP Init failed\n");
+ return ret;
+ }
+ ret = acp_memory_init(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "ACP Memory init failed\n");
+ return ret;
+ }
+
+ return 0;
}
-EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON");
+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("AMD ACP sof driver");
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index e3c85b5cb20c..1bc6b572448d 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -294,6 +294,7 @@ struct acp_dev_data {
bool acp7x_sdw_wake_event[ACP7X_SDW_MAX_MANAGER_COUNT];
/* PDM controller index selected from ACPI acp-audio-ep-port; passed to machine driver */
unsigned int pdm_sel;
+ bool is_sdw_dev;
unsigned int pci_rev;
int acp_sof_signed_firmware_image;
};
--
2.48.1