diff mbox series

[15/21] ASoC: amd: acp70: add acp driver pm ops support

Message ID 20241219054857.2070420-16-Vijendar.Mukunda@amd.com
State New
Headers show
Series [01/21] ASoC: amd: add register header file for ACP7.0 platform | expand

Commit Message

Vijendar Mukunda Dec. 19, 2024, 5:48 a.m. UTC
Add pm ops support for ACP7.0 PCI driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/acp70/acp70.h     |   4 ++
 sound/soc/amd/acp70/pci-acp70.c | 106 ++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)
diff mbox series

Patch

diff --git a/sound/soc/amd/acp70/acp70.h b/sound/soc/amd/acp70/acp70.h
index 1d8e670264fc..1b5661a86352 100644
--- a/sound/soc/amd/acp70/acp70.h
+++ b/sound/soc/amd/acp70/acp70.h
@@ -60,6 +60,8 @@ 
 
 #define ACP_SDW0_STAT			BIT(21)
 #define ACP_SDW1_STAT			BIT(2)
+#define ACP_SDW0_PME_STAT		BIT(26)
+#define ACP_SDW1_PME_STAT		BIT(27)
 #define ACP_ERROR_IRQ			BIT(29)
 
 #define ACP_AUDIO0_TX_THRESHOLD		0x1c
@@ -247,6 +249,7 @@  struct sdw_dma_dev_data {
  * @is_pdm_dev: flag set to true when ACP PDM controller exists
  * @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
  */
 
 struct acp70_dev_data {
@@ -268,6 +271,7 @@  struct acp70_dev_data {
 	bool is_pdm_dev;
 	bool is_pdm_config;
 	bool is_sdw_config;
+	bool sdw_en_stat;
 };
 
 int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/acp70/pci-acp70.c b/sound/soc/amd/acp70/pci-acp70.c
index e732a680c092..3cca18612ef1 100644
--- a/sound/soc/amd/acp70/pci-acp70.c
+++ b/sound/soc/amd/acp70/pci-acp70.c
@@ -15,6 +15,7 @@ 
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
 #include "../mach-config.h"
 
 #include "acp70.h"
@@ -554,6 +555,10 @@  static int snd_acp70_probe(struct pci_dev *pci,
 		goto de_init;
 	}
 skip_pdev_creation:
+	pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&pci->dev);
+	pm_runtime_put_noidle(&pci->dev);
+	pm_runtime_allow(&pci->dev);
 	return 0;
 de_init:
 	if (acp70_deinit(adata->acp70_base, &pci->dev))
@@ -566,6 +571,102 @@  static int snd_acp70_probe(struct pci_dev *pci,
 	return ret;
 }
 
+static bool check_acp_sdw_enable_status(struct acp70_dev_data *adata)
+{
+	u32 sdw0_en, sdw1_en;
+
+	sdw0_en = readl(adata->acp70_base + ACP_SW0_EN);
+	sdw1_en = readl(adata->acp70_base + ACP_SW1_EN);
+	return (sdw0_en || sdw1_en);
+}
+
+static void handle_acp70_sdw_pme_event(struct device *dev)
+{
+	struct amd_sdw_manager *amd_manager;
+	struct acp70_dev_data *adata;
+	u32 ext_intr_stat1;
+
+	adata = dev_get_drvdata(dev);
+	ext_intr_stat1 = readl(adata->acp70_base + ACP_EXTERNAL_INTR_STAT1);
+	dev_dbg(dev, "ext_intr_stat1: 0x%x\n", ext_intr_stat1);
+	if (ext_intr_stat1 & ACP_SDW0_PME_STAT) {
+		amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+		if (amd_manager)
+			pm_request_resume(amd_manager->dev);
+	}
+
+	if (ext_intr_stat1 & ACP_SDW1_PME_STAT) {
+		amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+		if (amd_manager)
+			pm_request_resume(amd_manager->dev);
+	}
+}
+
+static int __maybe_unused snd_acp70_suspend(struct device *dev)
+{
+	struct acp70_dev_data *adata;
+	int ret;
+
+	adata = dev_get_drvdata(dev);
+	writel(0x1, adata->acp70_base + ACP_PME_EN);
+	if (adata->is_sdw_dev) {
+		adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
+		if (adata->sdw_en_stat)
+			return 0;
+	}
+	ret = acp70_deinit(adata->acp70_base, 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 acp70_dev_data *adata;
+	int ret;
+
+	adata = dev_get_drvdata(dev);
+	writel(0x1, adata->acp70_base + ACP_PME_EN);
+
+	if (adata->sdw_en_stat)
+		return 0;
+
+	ret = acp70_init(adata->acp70_base, dev);
+	if (ret) {
+		dev_err(dev, "ACP init failed\n");
+		return ret;
+	}
+
+	if (!adata->sdw_en_stat)
+		handle_acp70_sdw_pme_event(dev);
+
+	return 0;
+}
+
+static int __maybe_unused snd_acp70_resume(struct device *dev)
+{
+	struct acp70_dev_data *adata;
+	int ret;
+
+	adata = dev_get_drvdata(dev);
+	writel(0x1, adata->acp70_base + ACP_PME_EN);
+
+	if (adata->sdw_en_stat)
+		return 0;
+
+	ret = acp70_init(adata->acp70_base, dev);
+	if (ret)
+		dev_err(dev, "ACP init failed\n");
+
+	return ret;
+}
+
+static const struct dev_pm_ops acp70_pm_ops = {
+	SET_RUNTIME_PM_OPS(snd_acp70_suspend, snd_acp70_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(snd_acp70_suspend, snd_acp70_resume)
+};
+
 static void snd_acp70_remove(struct pci_dev *pci)
 {
 	struct acp70_dev_data *adata;
@@ -583,6 +684,8 @@  static void snd_acp70_remove(struct pci_dev *pci)
 	ret = acp70_deinit(adata->acp70_base, &pci->dev);
 	if (ret)
 		dev_err(&pci->dev, "ACP de-init failed\n");
+	pm_runtime_forbid(&pci->dev);
+	pm_runtime_get_noresume(&pci->dev);
 	pci_release_regions(pci);
 	pci_disable_device(pci);
 }
@@ -600,6 +703,9 @@  static struct pci_driver ps_acp70_driver  = {
 	.id_table = snd_acp70_ids,
 	.probe = snd_acp70_probe,
 	.remove = snd_acp70_remove,
+	.driver = {
+		.pm = &acp70_pm_ops,
+	}
 };
 
 module_pci_driver(ps_acp70_driver);