aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/amd/raven/pci-acp3x.c
diff options
context:
space:
mode:
authorRavulapati Vishnu vardhan rao <Vishnuvardhanrao.Ravulapati@amd.com>2019-12-28 19:10:59 +0530
committerMark Brown <broonie@kernel.org>2019-12-31 00:23:15 +0000
commit535fd141ef346a3851f6aabc3eacb0d46518eca3 (patch)
treec4851d85417d21a0b0997588315dfa6b085934c5 /sound/soc/amd/raven/pci-acp3x.c
parentcea5f40d4e7ae711622ba7ee3caa60c315f101c0 (diff)
downloadlinux-535fd141ef346a3851f6aabc3eacb0d46518eca3.tar.gz
ASoC: amd: Added ACP3x system resume and runtime pm
When system wide suspend happens, ACP will be powered off and when system resumes,for audio usecase to continue,all the runtime configuration data needs to be programmed again. Added resume pm call back to ACP pm ops and also added runtime PM operations for ACP3x PCM platform device. Device will enter into D3 state when there is no activity on audio I2S lines. Signed-off-by: Ravulapati Vishnu vardhan rao <Vishnuvardhanrao.Ravulapati@amd.com> Link: https://lore.kernel.org/r/1577540460-21438-6-git-send-email-Vishnuvardhanrao.Ravulapati@amd.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/amd/raven/pci-acp3x.c')
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c173
1 files changed, 167 insertions, 6 deletions
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
index 94f5f21d9a53..2f9f52905853 100644
--- a/sound/soc/amd/raven/pci-acp3x.c
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -9,6 +9,8 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
#include "acp3x.h"
@@ -19,6 +21,109 @@ struct acp3x_dev_data {
struct platform_device *pdev[ACP3x_DEVS];
};
+static int acp3x_power_on(void __iomem *acp3x_base)
+{
+ u32 val;
+ int timeout;
+
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+
+ if (val == 0)
+ return val;
+
+ if (!((val & ACP_PGFSM_STATUS_MASK) ==
+ ACP_POWER_ON_IN_PROGRESS))
+ rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ acp3x_base + mmACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp3x_power_off(void __iomem *acp3x_base)
+{
+ u32 val;
+ int timeout;
+
+ rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+ acp3x_base + mmACP_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp3x_reset(void __iomem *acp3x_base)
+{
+ u32 val;
+ int timeout;
+
+ rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
+ break;
+ cpu_relax();
+ }
+ rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp3x_init(void __iomem *acp3x_base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp3x_power_on(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x power on failed\n");
+ return ret;
+ }
+ /* Reset */
+ ret = acp3x_reset(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x reset failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int acp3x_deinit(void __iomem *acp3x_base)
+{
+ int ret;
+
+ /* Reset */
+ ret = acp3x_reset(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x reset failed\n");
+ return ret;
+ }
+ /* power off */
+ ret = acp3x_power_off(acp3x_base);
+ if (ret) {
+ pr_err("ACP3x power off failed\n");
+ return ret;
+ }
+ return 0;
+}
+
static int snd_acp3x_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -64,6 +169,9 @@ static int snd_acp3x_probe(struct pci_dev *pci,
}
pci_set_master(pci);
pci_set_drvdata(pci, adata);
+ ret = acp3x_init(adata->acp3x_base);
+ if (ret)
+ goto disable_msi;
val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
switch (val) {
@@ -73,7 +181,7 @@ static int snd_acp3x_probe(struct pci_dev *pci,
GFP_KERNEL);
if (!adata->res) {
ret = -ENOMEM;
- goto disable_msi;
+ goto de_init;
}
adata->res[0].name = "acp3x_i2s_iomem";
@@ -118,7 +226,7 @@ static int snd_acp3x_probe(struct pci_dev *pci,
pdevinfo[2].parent = &pci->dev;
pdevinfo[2].num_res = 1;
pdevinfo[2].res = &adata->res[2];
- for (i = 0; i < ACP3x_DEVS ; i++) {
+ for (i = 0; i < ACP3x_DEVS; i++) {
adata->pdev[i] =
platform_device_register_full(&pdevinfo[i]);
if (IS_ERR(adata->pdev[i])) {
@@ -134,12 +242,21 @@ static int snd_acp3x_probe(struct pci_dev *pci,
ret = -ENODEV;
goto disable_msi;
}
+ pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_set_active(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_enable(&pci->dev);
+ pm_runtime_allow(&pci->dev);
return 0;
unregister_devs:
if (val == I2S_MODE)
- for (i = 0 ; i < ACP3x_DEVS ; i++)
+ for (i = 0; i < ACP3x_DEVS; i++)
platform_device_unregister(adata->pdev[i]);
+de_init:
+ if (acp3x_deinit(adata->acp3x_base))
+ dev_err(&pci->dev, "ACP de-init failed\n");
disable_msi:
pci_disable_msi(pci);
release_regions:
@@ -150,15 +267,56 @@ disable_pci:
return ret;
}
+static int snd_acp3x_suspend(struct device *dev)
+{
+ int ret;
+ struct acp3x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_dbg(dev, "ACP de-initialized\n");
+
+ return 0;
+}
+
+static int snd_acp3x_resume(struct device *dev)
+{
+ int ret;
+ struct acp3x_dev_data *adata;
+
+ adata = dev_get_drvdata(dev);
+ ret = acp3x_init(adata->acp3x_base);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct dev_pm_ops acp3x_pm = {
+ .runtime_suspend = snd_acp3x_suspend,
+ .runtime_resume = snd_acp3x_resume,
+ .resume = snd_acp3x_resume,
+};
+
static void snd_acp3x_remove(struct pci_dev *pci)
{
- struct acp3x_dev_data *adata = pci_get_drvdata(pci);
- int i;
+ struct acp3x_dev_data *adata;
+ int i, ret;
+ adata = pci_get_drvdata(pci);
if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
- for (i = 0 ; i < ACP3x_DEVS ; i++)
+ for (i = 0; i < ACP3x_DEVS; i++)
platform_device_unregister(adata->pdev[i]);
}
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ pm_runtime_disable(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
pci_disable_msi(pci);
pci_release_regions(pci);
pci_disable_device(pci);
@@ -177,6 +335,9 @@ static struct pci_driver acp3x_driver = {
.id_table = snd_acp3x_ids,
.probe = snd_acp3x_probe,
.remove = snd_acp3x_remove,
+ .driver = {
+ .pm = &acp3x_pm,
+ }
};
module_pci_driver(acp3x_driver);