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