Subject: [RESEND PATCH v5 6/6] 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 <[email protected]>
---
sound/soc/amd/raven/acp3x-pcm-dma.c | 135 -----------------------------
sound/soc/amd/raven/acp3x.h | 8 ++
sound/soc/amd/raven/pci-acp3x.c | 168 +++++++++++++++++++++++++++++++++++-
3 files changed, 173 insertions(+), 138 deletions(-)

diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c
index 8a8b135..00713eb 100644
--- a/sound/soc/amd/raven/acp3x-pcm-dma.c
+++ b/sound/soc/amd/raven/acp3x-pcm-dma.c
@@ -58,106 +58,6 @@ static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = {
.periods_max = CAPTURE_MAX_NUM_PERIODS,
};

-static int acp3x_power_on(void __iomem *acp3x_base, bool on)
-{
- u16 val, mask;
- u32 timeout;
-
- if (on == true) {
- val = 1;
- mask = ACP3x_POWER_ON;
- } else {
- val = 0;
- mask = ACP3x_POWER_OFF;
- }
-
- rv_writel(val, acp3x_base + mmACP_PGFSM_CONTROL);
- timeout = 0;
- while (true) {
- val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
- if ((val & ACP3x_POWER_OFF_IN_PROGRESS) == mask)
- break;
- if (timeout > 100) {
- pr_err("ACP3x power state change failure\n");
- return -ENODEV;
- }
- timeout++;
- cpu_relax();
- }
- return 0;
-}
-
-static int acp3x_reset(void __iomem *acp3x_base)
-{
- u32 val, timeout;
-
- rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
- timeout = 0;
- while (true) {
- val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
- if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) ||
- timeout > 100) {
- if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
- break;
- return -ENODEV;
- }
- timeout++;
- cpu_relax();
- }
-
- rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
- timeout = 0;
- while (true) {
- val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
- if (!val || timeout > 100) {
- if (!val)
- break;
- return -ENODEV;
- }
- timeout++;
- cpu_relax();
- }
- return 0;
-}
-
-static int acp3x_init(void __iomem *acp3x_base)
-{
- int ret;
-
- /* power on */
- ret = acp3x_power_on(acp3x_base, true);
- 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_on(acp3x_base, false);
- if (ret) {
- pr_err("ACP3x power off failed\n");
- return ret;
- }
- return 0;
-}
-
static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
{
u16 play_flag, cap_flag;
@@ -520,10 +420,6 @@ static int acp3x_audio_probe(struct platform_device *pdev)
adata->i2s_irq = res->start;

dev_set_drvdata(&pdev->dev, adata);
- /* Initialize ACP */
- status = acp3x_init(adata->acp3x_base);
- if (status)
- goto io_irq;
status = devm_snd_soc_register_component(&pdev->dev,
&acp3x_i2s_component,
NULL, 0);
@@ -560,40 +456,20 @@ static int acp3x_audio_probe(struct platform_device *pdev)
kfree(res);
kfree(adata->acp3x_base);
kfree(adata);
- status = acp3x_deinit(adata->acp3x_base);
- if (status)
- dev_err(&pdev->dev, "ACP de-init failed\n");
- else
- dev_info(&pdev->dev, "ACP de-initialized\n");
- /*ignore device status and return driver probe error*/
return -ENODEV;
}

static int acp3x_audio_remove(struct platform_device *pdev)
{
- int ret;
- struct i2s_dev_data *adata = dev_get_drvdata(&pdev->dev);
-
- ret = acp3x_deinit(adata->acp3x_base);
- if (ret)
- dev_err(&pdev->dev, "ACP de-init failed\n");
- else
- dev_info(&pdev->dev, "ACP de-initialized\n");
-
pm_runtime_disable(&pdev->dev);
return 0;
}

static int acp3x_resume(struct device *dev)
{
- int status;
u32 val;
struct i2s_dev_data *adata = dev_get_drvdata(dev);

- status = acp3x_init(adata->acp3x_base);
- if (status)
- return -ENODEV;
-
if (adata->play_stream && adata->play_stream->runtime) {
struct i2s_stream_instance *rtd =
adata->play_stream->runtime->private_data;
@@ -638,15 +514,8 @@ static int acp3x_resume(struct device *dev)

static int acp3x_pcm_runtime_suspend(struct device *dev)
{
- int status;
struct i2s_dev_data *adata = dev_get_drvdata(dev);

- status = acp3x_deinit(adata->acp3x_base);
- if (status)
- dev_err(dev, "ACP de-init failed\n");
- else
- dev_info(dev, "ACP de-initialized\n");
-
rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);

return 0;
@@ -654,12 +523,8 @@ static int acp3x_pcm_runtime_suspend(struct device *dev)

static int acp3x_pcm_runtime_resume(struct device *dev)
{
- int status;
struct i2s_dev_data *adata = dev_get_drvdata(dev);

- status = acp3x_init(adata->acp3x_base);
- if (status)
- return -ENODEV;
rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB);
return 0;
}
diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h
index 01b283a..c40f960 100644
--- a/sound/soc/amd/raven/acp3x.h
+++ b/sound/soc/amd/raven/acp3x.h
@@ -7,6 +7,7 @@

#include "chip_offset_byte.h"

+#define DELAY 600
#define I2S_SP_INSTANCE 0x01
#define I2S_BT_INSTANCE 0x02

@@ -65,6 +66,13 @@
#define SLOT_WIDTH_16 0x10
#define SLOT_WIDTH_24 0x18
#define SLOT_WIDTH_32 0x20
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03

struct acp3x_platform_info {
u16 play_i2s_instance;
diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c
index 91ebee9..2d2c7ba 100644
--- a/sound/soc/amd/raven/pci-acp3x.c
+++ b/sound/soc/amd/raven/pci-acp3x.c
@@ -9,6 +9,9 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>

#include "acp3x.h"

@@ -19,6 +22,114 @@ struct acp3x_dev_data {
struct platform_device *pdev[ACP3x_DEVS];
};

+static int acp3x_power_on(void __iomem *acp3x_base)
+{
+ u32 val;
+ u32 timeout = 0;
+ int ret = 0;
+
+ 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);
+ while (++timeout < DELAY) {
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+ if (!val)
+ break;
+ udelay(1);
+ if (timeout > 500) {
+ pr_err("ACP is Not Powered ON\n");
+ return -ETIMEDOUT;
+ }
+ }
+}
+static int acp3x_power_off(void __iomem *acp3x_base)
+{
+ u32 val;
+ u32 timeout = 0;
+
+ rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+ acp3x_base + mmACP_PGFSM_CONTROL);
+ while (++timeout < DELAY) {
+ val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+ return 0;
+ udelay(1);
+ if (timeout > 500) {
+ pr_err("ACP is Not Powered OFF\n");
+ return -ETIMEDOUT;
+ }
+ }
+}
+static int acp3x_reset(void __iomem *acp3x_base)
+{
+ u32 val, timeout;
+
+ rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < DELAY) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) ||
+ timeout > 100) {
+ if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
+ break;
+ return -ENODEV;
+ }
+ cpu_relax();
+ }
+ rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < DELAY) {
+ val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
+ if (!val)
+ break;
+ if (timeout > 100)
+ return -ENODEV;
+ cpu_relax();
+ }
+ return 0;
+}
+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)
{
@@ -63,6 +174,10 @@ 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)
+ return -ENODEV;
+

val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
switch (val) {
@@ -133,9 +248,19 @@ static int snd_acp3x_probe(struct pci_dev *pci,
ret = -ENODEV;
goto unmap_mmio;
}
+ pm_runtime_set_autosuspend_delay(&pci->dev, 10000);
+ pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_set_active(&pci->dev);
+ pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_enable(&pci->dev);
return 0;

unmap_mmio:
+ ret = acp3x_deinit(adata->acp3x_base);
+ if (ret)
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ else
+ dev_info(&pci->dev, "ACP de-initialized\n");
pci_disable_msi(pci);
for (i = 0 ; i < ACP3x_DEVS ; i++)
platform_device_unregister(adata->pdev[i]);
@@ -148,23 +273,57 @@ static int snd_acp3x_probe(struct pci_dev *pci,

return ret;
}
+static int snd_acp3x_suspend(struct device *dev)
+{
+ int status;
+ struct acp3x_dev_data *adata = dev_get_drvdata(dev);
+
+ status = acp3x_deinit(adata->acp3x_base);
+ if (status)
+ dev_err(dev, "ACP de-init failed\n");
+ else
+ dev_info(dev, "ACP de-initialized\n");

+ return 0;
+}
+static int snd_acp3x_resume(struct device *dev)
+{
+ int status;
+ struct acp3x_dev_data *adata = dev_get_drvdata(dev);
+
+ status = acp3x_init(adata->acp3x_base);
+ if (status) {
+ dev_err(dev, "ACP init failed\n");
+ return status;
+ }
+ 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)
{
- int i;
+ int i, ret;
struct acp3x_dev_data *adata = pci_get_drvdata(pci);

if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
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");
+ else
+ dev_info(&pci->dev, "ACP de-initialized\n");
iounmap(adata->acp3x_base);
-
+ pm_runtime_disable(&pci->dev);
+ pm_runtime_get_noresume(&pci->dev);
pci_disable_msi(pci);
pci_release_regions(pci);
pci_disable_device(pci);
}
-
static const struct pci_device_id snd_acp3x_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
@@ -178,6 +337,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);
--
2.7.4


2019-11-13 17:27:22

by Pierre-Louis Bossart

[permalink] [raw]
Subject: Re: [alsa-devel] [RESEND PATCH v5 6/6] ASoC: amd: Added ACP3x system resume and runtime pm


> diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h
> index 01b283a..c40f960 100644
> --- a/sound/soc/amd/raven/acp3x.h
> +++ b/sound/soc/amd/raven/acp3x.h
> @@ -7,6 +7,7 @@
>
> #include "chip_offset_byte.h"
>
> +#define DELAY 600

you probably want a prefix here to avoid conflicts


> +static int acp3x_power_on(void __iomem *acp3x_base)
> +{
> + u32 val;
> + u32 timeout = 0;
> + int ret = 0;
> +
> + 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);
> + while (++timeout < DELAY) {

so here timeout can reach 600.

> + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
> + if (!val)
> + break;
> + udelay(1);
> + if (timeout > 500) {

but here you abort at 500.
Looks like the first test is not needed?

> + pr_err("ACP is Not Powered ON\n");
> + return -ETIMEDOUT;
> + }
> + }
> +}
> +static int acp3x_power_off(void __iomem *acp3x_base)
> +{
> + u32 val;
> + u32 timeout = 0;
> +
> + rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
> + acp3x_base + mmACP_PGFSM_CONTROL);
> + while (++timeout < DELAY) {

same here

> + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
> + if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
> + return 0;
> + udelay(1);
> + if (timeout > 500) {
> + pr_err("ACP is Not Powered OFF\n");
> + return -ETIMEDOUT;
> + }
> + }
> +}
> +static int acp3x_reset(void __iomem *acp3x_base)
> +{
> + u32 val, timeout;
> +
> + rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
> + timeout = 0;
> + while (++timeout < DELAY) {
> + val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
> + if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) ||
> + timeout > 100) {

and here

> + if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
> + break;
> + return -ENODEV;
> + }
> + cpu_relax();
> + }
> + rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
> + timeout = 0;
> + while (++timeout < DELAY) {
> + val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
> + if (!val)
> + break;
> + if (timeout > 100)

and here.


> @@ -133,9 +248,19 @@ static int snd_acp3x_probe(struct pci_dev *pci,
> ret = -ENODEV;
> goto unmap_mmio;
> }
> + pm_runtime_set_autosuspend_delay(&pci->dev, 10000);

I think it's the largest value I've ever seen for autosuspend.
It is necessary? If yes you should document why.

> + pm_runtime_use_autosuspend(&pci->dev);
> + pm_runtime_set_active(&pci->dev);
> + pm_runtime_put_noidle(&pci->dev);
> + pm_runtime_enable(&pci->dev);
> return 0;
>
> unmap_mmio:
> + ret = acp3x_deinit(adata->acp3x_base);
> + if (ret)
> + dev_err(&pci->dev, "ACP de-init failed\n");
> + else
> + dev_info(&pci->dev, "ACP de-initialized\n");

dev_dbg?

> pci_disable_msi(pci);
> for (i = 0 ; i < ACP3x_DEVS ; i++)
> platform_device_unregister(adata->pdev[i]);
> @@ -148,23 +273,57 @@ static int snd_acp3x_probe(struct pci_dev *pci,
>
> return ret;
> }
> +static int snd_acp3x_suspend(struct device *dev)
> +{
> + int status;
> + struct acp3x_dev_data *adata = dev_get_drvdata(dev);
> +
> + status = acp3x_deinit(adata->acp3x_base);
> + if (status)
> + dev_err(dev, "ACP de-init failed\n");
> + else
> + dev_info(dev, "ACP de-initialized\n");

dev_dbg?


> static void snd_acp3x_remove(struct pci_dev *pci)
> {
> - int i;
> + int i, ret;
> struct acp3x_dev_data *adata = pci_get_drvdata(pci);
>
> if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
> 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");
> + else
> + dev_info(&pci->dev, "ACP de-initialized\n");

dev_dbg?

2019-11-13 19:10:34

by kernel test robot

[permalink] [raw]
Subject: Re: [RESEND PATCH v5 6/6] ASoC: amd: Added ACP3x system resume and runtime pm

Hi Ravulapati,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on asoc/for-next]
[also build test WARNING on next-20191113]
[cannot apply to v5.4-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url: https://github.com/0day-ci/linux/commits/Ravulapati-Vishnu-vardhan-rao/ASoC-amd-Create-multiple-I2S-platform-device-Endpoint/20191113-230604
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
config: x86_64-randconfig-h001-20191113 (attached as .config)
compiler: gcc-7 (Debian 7.4.0-14) 7.4.0
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>

All warnings (new ones prefixed by >>):

sound/soc/amd/raven/pci-acp3x.c: In function 'acp3x_power_on':
sound/soc/amd/raven/pci-acp3x.c:29:6: warning: unused variable 'ret' [-Wunused-variable]
int ret = 0;
^~~
sound/soc/amd/raven/pci-acp3x.c: In function 'acp3x_power_off':
>> sound/soc/amd/raven/pci-acp3x.c:68:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
sound/soc/amd/raven/pci-acp3x.c: In function 'acp3x_power_on':
sound/soc/amd/raven/pci-acp3x.c:50:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^

vim +68 sound/soc/amd/raven/pci-acp3x.c

24
25 static int acp3x_power_on(void __iomem *acp3x_base)
26 {
27 u32 val;
28 u32 timeout = 0;
> 29 int ret = 0;
30
31 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
32
33 if (val == 0)
34 return val;
35
36 if (!((val & ACP_PGFSM_STATUS_MASK) ==
37 ACP_POWER_ON_IN_PROGRESS))
38 rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
39 acp3x_base + mmACP_PGFSM_CONTROL);
40 while (++timeout < DELAY) {
41 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
42 if (!val)
43 break;
44 udelay(1);
45 if (timeout > 500) {
46 pr_err("ACP is Not Powered ON\n");
47 return -ETIMEDOUT;
48 }
49 }
50 }
51 static int acp3x_power_off(void __iomem *acp3x_base)
52 {
53 u32 val;
54 u32 timeout = 0;
55
56 rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
57 acp3x_base + mmACP_PGFSM_CONTROL);
58 while (++timeout < DELAY) {
59 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
60 if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
61 return 0;
62 udelay(1);
63 if (timeout > 500) {
64 pr_err("ACP is Not Powered OFF\n");
65 return -ETIMEDOUT;
66 }
67 }
> 68 }
69 static int acp3x_reset(void __iomem *acp3x_base)
70 {
71 u32 val, timeout;
72
73 rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
74 timeout = 0;
75 while (++timeout < DELAY) {
76 val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
77 if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) ||
78 timeout > 100) {
79 if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
80 break;
81 return -ENODEV;
82 }
83 cpu_relax();
84 }
85 rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
86 timeout = 0;
87 while (++timeout < DELAY) {
88 val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
89 if (!val)
90 break;
91 if (timeout > 100)
92 return -ENODEV;
93 cpu_relax();
94 }
95 return 0;
96 }
97 static int acp3x_init(void __iomem *acp3x_base)
98 {
99 int ret;
100
101 /* power on */
102 ret = acp3x_power_on(acp3x_base);
103 if (ret) {
104 pr_err("ACP3x power on failed\n");
105 return ret;
106 }
107 /* Reset */
108 ret = acp3x_reset(acp3x_base);
109 if (ret) {
110 pr_err("ACP3x reset failed\n");
111 return ret;
112 }
113 return 0;
114 }
115 static int acp3x_deinit(void __iomem *acp3x_base)
116 {
117 int ret;
118
119 /* Reset */
120 ret = acp3x_reset(acp3x_base);
121 if (ret) {
122 pr_err("ACP3x reset failed\n");
123 return ret;
124 }
125 /* power off */
126 ret = acp3x_power_off(acp3x_base);
127 if (ret) {
128 pr_err("ACP3x power off failed\n");
129 return ret;
130 }
131 return 0;
132 }
133 static int snd_acp3x_probe(struct pci_dev *pci,
134 const struct pci_device_id *pci_id)
135 {
136 int ret;
137 u32 addr, val, i;
138 struct acp3x_dev_data *adata;
139 struct platform_device_info pdevinfo[ACP3x_DEVS];
140 unsigned int irqflags;
141
142 if (pci_enable_device(pci)) {
143 dev_err(&pci->dev, "pci_enable_device failed\n");
144 return -ENODEV;
145 }
146
147 ret = pci_request_regions(pci, "AMD ACP3x audio");
148 if (ret < 0) {
149 dev_err(&pci->dev, "pci_request_regions failed\n");
150 goto disable_pci;
151 }
152
153 adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
154 GFP_KERNEL);
155 if (!adata) {
156 ret = -ENOMEM;
157 goto release_regions;
158 }
159
160 /* check for msi interrupt support */
161 ret = pci_enable_msi(pci);
162 if (ret)
163 /* msi is not enabled */
164 irqflags = IRQF_SHARED;
165 else
166 /* msi is enabled */
167 irqflags = 0;
168
169 addr = pci_resource_start(pci, 0);
170 adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0));
171 if (!adata->acp3x_base) {
172 ret = -ENOMEM;
173 goto release_regions;
174 }
175 pci_set_master(pci);
176 pci_set_drvdata(pci, adata);
177 ret = acp3x_init(adata->acp3x_base);
178 if (ret)
179 return -ENODEV;
180
181
182 val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
183 switch (val) {
184 case I2S_MODE:
185 adata->res = devm_kzalloc(&pci->dev,
186 sizeof(struct resource) * 4,
187 GFP_KERNEL);
188 if (!adata->res) {
189 ret = -ENOMEM;
190 goto unmap_mmio;
191 }
192
193 adata->res[0].name = "acp3x_i2s_iomem";
194 adata->res[0].flags = IORESOURCE_MEM;
195 adata->res[0].start = addr;
196 adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
197
198 adata->res[1].name = "acp3x_i2s_sp";
199 adata->res[1].flags = IORESOURCE_MEM;
200 adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
201 adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
202
203 adata->res[2].name = "acp3x_i2s_bt";
204 adata->res[2].flags = IORESOURCE_MEM;
205 adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
206 adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
207
208 adata->res[3].name = "acp3x_i2s_irq";
209 adata->res[3].flags = IORESOURCE_IRQ;
210 adata->res[3].start = pci->irq;
211 adata->res[3].end = adata->res[3].start;
212
213 adata->acp3x_audio_mode = ACP3x_I2S_MODE;
214
215 memset(&pdevinfo, 0, sizeof(pdevinfo));
216 pdevinfo[0].name = "acp3x_rv_i2s_dma";
217 pdevinfo[0].id = 0;
218 pdevinfo[0].parent = &pci->dev;
219 pdevinfo[0].num_res = 4;
220 pdevinfo[0].res = &adata->res[0];
221 pdevinfo[0].data = &irqflags;
222 pdevinfo[0].size_data = sizeof(irqflags);
223
224 pdevinfo[1].name = "acp3x_i2s_playcap";
225 pdevinfo[1].id = 0;
226 pdevinfo[1].parent = &pci->dev;
227 pdevinfo[1].num_res = 1;
228 pdevinfo[1].res = &adata->res[1];
229
230 pdevinfo[2].name = "acp3x_i2s_playcap";
231 pdevinfo[2].id = 1;
232 pdevinfo[2].parent = &pci->dev;
233 pdevinfo[2].num_res = 1;
234 pdevinfo[2].res = &adata->res[2];
235 for (i = 0; i < ACP3x_DEVS ; i++) {
236 adata->pdev[i] =
237 platform_device_register_full(&pdevinfo[i]);
238 if (IS_ERR(adata->pdev[i])) {
239 dev_err(&pci->dev, "cannot register %s device\n",
240 pdevinfo[i].name);
241 ret = PTR_ERR(adata->pdev[i]);
242 goto unmap_mmio;
243 }
244 }
245 break;
246 default:
247 dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
248 ret = -ENODEV;
249 goto unmap_mmio;
250 }
251 pm_runtime_set_autosuspend_delay(&pci->dev, 10000);
252 pm_runtime_use_autosuspend(&pci->dev);
253 pm_runtime_set_active(&pci->dev);
254 pm_runtime_put_noidle(&pci->dev);
255 pm_runtime_enable(&pci->dev);
256 return 0;
257
258 unmap_mmio:
259 ret = acp3x_deinit(adata->acp3x_base);
260 if (ret)
261 dev_err(&pci->dev, "ACP de-init failed\n");
262 else
263 dev_info(&pci->dev, "ACP de-initialized\n");
264 pci_disable_msi(pci);
265 for (i = 0 ; i < ACP3x_DEVS ; i++)
266 platform_device_unregister(adata->pdev[i]);
267 kfree(adata->res);
268 iounmap(adata->acp3x_base);
269 release_regions:
270 pci_release_regions(pci);
271 disable_pci:
272 pci_disable_device(pci);
273
274 return ret;
275 }
276 static int snd_acp3x_suspend(struct device *dev)
277 {
278 int status;
279 struct acp3x_dev_data *adata = dev_get_drvdata(dev);
280
281 status = acp3x_deinit(adata->acp3x_base);
282 if (status)
283 dev_err(dev, "ACP de-init failed\n");
284 else
285 dev_info(dev, "ACP de-initialized\n");
286
287 return 0;
288 }
289 static int snd_acp3x_resume(struct device *dev)
290 {
291 int status;
292 struct acp3x_dev_data *adata = dev_get_drvdata(dev);
293
294 status = acp3x_init(adata->acp3x_base);
295 if (status) {
296 dev_err(dev, "ACP init failed\n");
297 return status;
298 }
299 return 0;
300 }
301 static const struct dev_pm_ops acp3x_pm = {
302 .runtime_suspend = snd_acp3x_suspend,
303 .runtime_resume = snd_acp3x_resume,
304 .resume = snd_acp3x_resume,
305 };
306 static void snd_acp3x_remove(struct pci_dev *pci)
307 {
308 int i, ret;
309 struct acp3x_dev_data *adata = pci_get_drvdata(pci);
310
311 if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
312 for (i = 0 ; i < ACP3x_DEVS ; i++)
313 platform_device_unregister(adata->pdev[i]);
314 }
315 ret = acp3x_deinit(adata->acp3x_base);
316 if (ret)
317 dev_err(&pci->dev, "ACP de-init failed\n");
318 else
319 dev_info(&pci->dev, "ACP de-initialized\n");
320 iounmap(adata->acp3x_base);
321 pm_runtime_disable(&pci->dev);
322 pm_runtime_get_noresume(&pci->dev);
323 pci_disable_msi(pci);
324 pci_release_regions(pci);
325 pci_disable_device(pci);
326 }
327 static const struct pci_device_id snd_acp3x_ids[] = {
328 { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
329 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
330 .class_mask = 0xffffff },
331 { 0, },
332 };
333 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
334

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/[email protected] Intel Corporation


Attachments:
(No filename) (11.40 kB)
.config.gz (33.94 kB)
Download all attachments