Add pm ops support for ACP7.0 PCI driver.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@xxxxxxx>
---
sound/soc/amd/acp70/acp70.h | 4 ++
sound/soc/amd/acp70/pci-acp70.c | 106 ++++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+)
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);
--
2.34.1
[Index of Archives]
[Pulseaudio]
[Linux Audio Users]
[ALSA Devel]
[Fedora Desktop]
[Fedora SELinux]
[Big List of Linux Books]
[Yosemite News]
[KDE Users]