Message ID | 20220113163348.434108-5-AjitKumar.Pandey@amd.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | ASOC: amd: acp: Add generic PDM and PCI driver support for ACP | expand |
On 1/13/2022 5:33 PM, Ajit Kumar Pandey wrote: > ACP hardware has PGFSM control registers that can be configured to > power On/Off the ACP IP block. Add acp init()/de_init() callbacks > in renoir platform driver probe()/remove() respectively to power > on and off ACP IP block on ACP3X device. > > Signed-off-by: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> > --- > sound/soc/amd/acp/acp-renoir.c | 170 +++++++++++++++++++++++++++ > sound/soc/amd/acp/chip_offset_byte.h | 6 + > 2 files changed, 176 insertions(+) > > diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c > index 770a57a0677b..a29f910f25d1 100644 > --- a/sound/soc/amd/acp/acp-renoir.c > +++ b/sound/soc/amd/acp/acp-renoir.c > @@ -25,6 +25,19 @@ > > #define DRV_NAME "acp_asoc_renoir" > > +#define ACP_SOFT_RST_DONE_MASK 0x00010001 > + > +#define ACP_PWR_ON_MASK 0x01 > +#define ACP_PWR_OFF_MASK 0x00 > +#define ACP_PGFSM_STAT_MASK 0x03 > +#define ACP_POWERED_ON 0x00 > +#define ACP_PWR_ON_IN_PROGRESS 0x01 > +#define ACP_POWERED_OFF 0x02 > + > + > +#define ACP_ERROR_MASK 0x20000000 > +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF > + > static struct snd_soc_acpi_codecs amp_rt1019 = { > .num_codecs = 1, > .codecs = {"10EC1019"} > @@ -112,11 +125,154 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { > }, > }; > > +static int acp3x_power_on(void __iomem *base) > +{ > + u32 val; > + int timeout = 0; > + > + val = readl(base + ACP_PGFSM_STATUS); > + > + if (val == ACP_POWERED_ON) > + return 0; > + > + if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) > + writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); > + > + while (++timeout < 500) { > + val = readl(base + ACP_PGFSM_STATUS); > + if (!val) > + return 0; > + udelay(1); > + } Can this while loop perhaps be replaced with readl_poll_timeout? Similarly for cases below? > + > + return -ETIMEDOUT; > +} > + > +static int acp3x_power_off(void __iomem *base) > +{ > + u32 val; > + int timeout = 0; > + > + writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); > + > + while (++timeout < 500) { > + val = readl(base + ACP_PGFSM_STATUS); > + if ((val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF) > + return 0; > + udelay(1); > + } > + > + return -ETIMEDOUT; > +} > + > +static int acp3x_reset(void __iomem *base) > +{ > + u32 val; > + int timeout = 0; > + > + writel(1, base + ACP_SOFT_RESET); > + > + while (++timeout < 500) { > + val = readl(base + ACP_SOFT_RESET); > + if (val & ACP_SOFT_RST_DONE_MASK) > + break; > + cpu_relax(); > + } > + > + writel(0, base + ACP_SOFT_RESET); > + > + timeout = 0; > + while (++timeout < 500) { > + val = readl(base + ACP_SOFT_RESET); > + if (!val) > + return 0; > + cpu_relax(); > + } > + > + return -ETIMEDOUT; > +} > + > +static void acp3x_enable_interrupts(void __iomem *base) > +{ > + u32 ext_intr_ctrl; > + > + writel(0x01, base + ACP_EXTERNAL_INTR_ENB); > + ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL); > + ext_intr_ctrl |= ACP_ERROR_MASK; > + writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL); > +} > + > +static void acp3x_disable_interrupts(void __iomem *base) > +{ > + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT); > + writel(0x00, base + ACP_EXTERNAL_INTR_ENB); > +} > + > +static int rn_acp_init(void __iomem *base) > +{ > + int ret; > + > + /* power on */ > + ret = acp3x_power_on(base); > + if (ret) > + return ret; > + > + writel(0x01, base + ACP_CONTROL); > + > + /* Reset */ > + ret = acp3x_reset(base); > + if (ret) > + return ret; > + > + acp3x_enable_interrupts(base); > + > + return 0; > +} > + > +static int rn_acp_deinit(void __iomem *base) > +{ > + int ret = 0; > + > + acp3x_disable_interrupts(base); > + > + /* Reset */ > + ret = acp3x_reset(base); > + if (ret) > + return ret; > + > + writel(0x00, base + ACP_CONTROL); > + > + /* power off */ > + ret = acp3x_power_off(base); > + if (ret) > + return ret; > + > + return 0; > +} > static int renoir_audio_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > + struct acp_chip_info *chip; > struct acp_dev_data *adata; > struct resource *res; > + int ret; > + > + chip = dev_get_platdata(&pdev->dev); > + if (!chip || !chip->base) { > + dev_err(&pdev->dev, "ACP chip data is NULL\n"); > + return -ENODEV; > + } > + > + if (chip->acp_rev != ACP3X_DEV) { > + dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); > + return -ENODEV; > + } > + > + ret = rn_acp_init(chip->base); > + if (ret) { > + dev_err(&pdev->dev, "ACP Init failed\n"); > + return -EINVAL; > + } > > adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); > if (!adata) > @@ -155,6 +311,20 @@ static int renoir_audio_probe(struct platform_device *pdev) > static int renoir_audio_remove(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > + struct acp_chip_info *chip; > + int ret; > + > + chip = dev_get_platdata(&pdev->dev); > + if (!chip || !chip->base) { > + dev_err(&pdev->dev, "ACP chip data is NULL\n"); > + return -ENODEV; > + } > + > + ret = rn_acp_deinit(chip->base); > + if (ret) { > + dev_err(&pdev->dev, "ACP de-init Failed\n"); > + return -EINVAL; > + } > > acp_platform_unregister(dev); > return 0; > diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h > index e38589a142e9..88f6fa597cd6 100644 > --- a/sound/soc/amd/acp/chip_offset_byte.h > +++ b/sound/soc/amd/acp/chip_offset_byte.h > @@ -14,6 +14,12 @@ > #define ACPAXI2AXI_ATU_CTRL 0xC40 > #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 > #define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 > + > +#define ACP_PGFSM_CONTROL 0x141C > +#define ACP_PGFSM_STATUS 0x1420 > +#define ACP_SOFT_RESET 0x1000 > +#define ACP_CONTROL 0x1004 > + > #define ACP_EXTERNAL_INTR_ENB 0x1800 > #define ACP_EXTERNAL_INTR_CNTL 0x1804 > #define ACP_EXTERNAL_INTR_STAT 0x1808
On 1/14/2022 2:31 PM, Amadeusz Sławiński wrote: > [CAUTION: External Email] > > On 1/13/2022 5:33 PM, Ajit Kumar Pandey wrote: >> ACP hardware has PGFSM control registers that can be configured to >> power On/Off the ACP IP block. Add acp init()/de_init() callbacks >> in renoir platform driver probe()/remove() respectively to power >> on and off ACP IP block on ACP3X device. >> >> Signed-off-by: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> >> --- >> sound/soc/amd/acp/acp-renoir.c | 170 +++++++++++++++++++++++++++ >> sound/soc/amd/acp/chip_offset_byte.h | 6 + >> 2 files changed, 176 insertions(+) >> >> diff --git a/sound/soc/amd/acp/acp-renoir.c >> b/sound/soc/amd/acp/acp-renoir.c >> index 770a57a0677b..a29f910f25d1 100644 >> --- a/sound/soc/amd/acp/acp-renoir.c >> +++ b/sound/soc/amd/acp/acp-renoir.c >> @@ -25,6 +25,19 @@ >> >> #define DRV_NAME "acp_asoc_renoir" >> >> +#define ACP_SOFT_RST_DONE_MASK 0x00010001 >> + >> +#define ACP_PWR_ON_MASK 0x01 >> +#define ACP_PWR_OFF_MASK 0x00 >> +#define ACP_PGFSM_STAT_MASK 0x03 >> +#define ACP_POWERED_ON 0x00 >> +#define ACP_PWR_ON_IN_PROGRESS 0x01 >> +#define ACP_POWERED_OFF 0x02 >> + >> + >> +#define ACP_ERROR_MASK 0x20000000 >> +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF >> + >> static struct snd_soc_acpi_codecs amp_rt1019 = { >> .num_codecs = 1, >> .codecs = {"10EC1019"} >> @@ -112,11 +125,154 @@ static struct snd_soc_dai_driver >> acp_renoir_dai[] = { >> }, >> }; >> >> +static int acp3x_power_on(void __iomem *base) >> +{ >> + u32 val; >> + int timeout = 0; >> + >> + val = readl(base + ACP_PGFSM_STATUS); >> + >> + if (val == ACP_POWERED_ON) >> + return 0; >> + >> + if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) >> + writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); >> + >> + while (++timeout < 500) { >> + val = readl(base + ACP_PGFSM_STATUS); >> + if (!val) >> + return 0; >> + udelay(1); >> + } > > Can this while loop perhaps be replaced with readl_poll_timeout? > Similarly for cases below? > >> + >> + return -ETIMEDOUT; >> +} >> + >> +static int acp3x_power_off(void __iomem *base) >> +{ >> + u32 val; >> + int timeout = 0; >> + >> + writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); >> + >> + while (++timeout < 500) { >> + val = readl(base + ACP_PGFSM_STATUS); >> + if ((val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF) >> + return 0; >> + udelay(1); >> + } >> + >> + return -ETIMEDOUT; >> +} >> + >> +static int acp3x_reset(void __iomem *base) >> +{ >> + u32 val; >> + int timeout = 0; >> + >> + writel(1, base + ACP_SOFT_RESET); >> + >> + while (++timeout < 500) { >> + val = readl(base + ACP_SOFT_RESET); >> + if (val & ACP_SOFT_RST_DONE_MASK) >> + break; >> + cpu_relax(); >> + } >> + >> + writel(0, base + ACP_SOFT_RESET); >> + >> + timeout = 0; >> + while (++timeout < 500) { >> + val = readl(base + ACP_SOFT_RESET); >> + if (!val) >> + return 0; >> + cpu_relax(); >> + } >> + >> + return -ETIMEDOUT; >> +} >> + >> +static void acp3x_enable_interrupts(void __iomem *base) >> +{ >> + u32 ext_intr_ctrl; >> + >> + writel(0x01, base + ACP_EXTERNAL_INTR_ENB); >> + ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL); >> + ext_intr_ctrl |= ACP_ERROR_MASK; >> + writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL); >> +} >> + >> +static void acp3x_disable_interrupts(void __iomem *base) >> +{ >> + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + >> ACP_EXTERNAL_INTR_STAT); >> + writel(0x00, base + ACP_EXTERNAL_INTR_ENB); >> +} >> + >> +static int rn_acp_init(void __iomem *base) >> +{ >> + int ret; >> + >> + /* power on */ >> + ret = acp3x_power_on(base); >> + if (ret) >> + return ret; >> + >> + writel(0x01, base + ACP_CONTROL); >> + >> + /* Reset */ >> + ret = acp3x_reset(base); >> + if (ret) >> + return ret; >> + >> + acp3x_enable_interrupts(base); >> + >> + return 0; >> +} >> + >> +static int rn_acp_deinit(void __iomem *base) >> +{ >> + int ret = 0; >> + >> + acp3x_disable_interrupts(base); >> + >> + /* Reset */ >> + ret = acp3x_reset(base); >> + if (ret) >> + return ret; >> + >> + writel(0x00, base + ACP_CONTROL); >> + >> + /* power off */ >> + ret = acp3x_power_off(base); >> + if (ret) >> + return ret; >> + >> + return 0; >> +} >> static int renoir_audio_probe(struct platform_device *pdev) >> { >> struct device *dev = &pdev->dev; >> + struct acp_chip_info *chip; >> struct acp_dev_data *adata; >> struct resource *res; >> + int ret; >> + >> + chip = dev_get_platdata(&pdev->dev); >> + if (!chip || !chip->base) { >> + dev_err(&pdev->dev, "ACP chip data is NULL\n"); >> + return -ENODEV; >> + } >> + >> + if (chip->acp_rev != ACP3X_DEV) { >> + dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", >> chip->acp_rev); >> + return -ENODEV; >> + } >> + >> + ret = rn_acp_init(chip->base); >> + if (ret) { >> + dev_err(&pdev->dev, "ACP Init failed\n"); >> + return -EINVAL; >> + } >> >> adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); >> if (!adata) >> @@ -155,6 +311,20 @@ static int renoir_audio_probe(struct >> platform_device *pdev) >> static int renoir_audio_remove(struct platform_device *pdev) >> { >> struct device *dev = &pdev->dev; >> + struct acp_chip_info *chip; >> + int ret; >> + >> + chip = dev_get_platdata(&pdev->dev); >> + if (!chip || !chip->base) { >> + dev_err(&pdev->dev, "ACP chip data is NULL\n"); >> + return -ENODEV; >> + } >> + >> + ret = rn_acp_deinit(chip->base); >> + if (ret) { >> + dev_err(&pdev->dev, "ACP de-init Failed\n"); >> + return -EINVAL; >> + } >> >> acp_platform_unregister(dev); >> return 0; >> diff --git a/sound/soc/amd/acp/chip_offset_byte.h >> b/sound/soc/amd/acp/chip_offset_byte.h >> index e38589a142e9..88f6fa597cd6 100644 >> --- a/sound/soc/amd/acp/chip_offset_byte.h >> +++ b/sound/soc/amd/acp/chip_offset_byte.h >> @@ -14,6 +14,12 @@ >> #define ACPAXI2AXI_ATU_CTRL 0xC40 >> #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 >> #define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 >> + >> +#define ACP_PGFSM_CONTROL 0x141C >> +#define ACP_PGFSM_STATUS 0x1420 >> +#define ACP_SOFT_RESET 0x1000 >> +#define ACP_CONTROL 0x1004 >> + >> #define ACP_EXTERNAL_INTR_ENB 0x1800 >> #define ACP_EXTERNAL_INTR_CNTL 0x1804 >> #define ACP_EXTERNAL_INTR_STAT 0x1808 > ok will improvise this in next patch chain
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c index 770a57a0677b..a29f910f25d1 100644 --- a/sound/soc/amd/acp/acp-renoir.c +++ b/sound/soc/amd/acp/acp-renoir.c @@ -25,6 +25,19 @@ #define DRV_NAME "acp_asoc_renoir" +#define ACP_SOFT_RST_DONE_MASK 0x00010001 + +#define ACP_PWR_ON_MASK 0x01 +#define ACP_PWR_OFF_MASK 0x00 +#define ACP_PGFSM_STAT_MASK 0x03 +#define ACP_POWERED_ON 0x00 +#define ACP_PWR_ON_IN_PROGRESS 0x01 +#define ACP_POWERED_OFF 0x02 + + +#define ACP_ERROR_MASK 0x20000000 +#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF + static struct snd_soc_acpi_codecs amp_rt1019 = { .num_codecs = 1, .codecs = {"10EC1019"} @@ -112,11 +125,154 @@ static struct snd_soc_dai_driver acp_renoir_dai[] = { }, }; +static int acp3x_power_on(void __iomem *base) +{ + u32 val; + int timeout = 0; + + val = readl(base + ACP_PGFSM_STATUS); + + if (val == ACP_POWERED_ON) + return 0; + + if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) + writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); + + while (++timeout < 500) { + val = readl(base + ACP_PGFSM_STATUS); + if (!val) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int acp3x_power_off(void __iomem *base) +{ + u32 val; + int timeout = 0; + + writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); + + while (++timeout < 500) { + val = readl(base + ACP_PGFSM_STATUS); + if ((val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int acp3x_reset(void __iomem *base) +{ + u32 val; + int timeout = 0; + + writel(1, base + ACP_SOFT_RESET); + + while (++timeout < 500) { + val = readl(base + ACP_SOFT_RESET); + if (val & ACP_SOFT_RST_DONE_MASK) + break; + cpu_relax(); + } + + writel(0, base + ACP_SOFT_RESET); + + timeout = 0; + while (++timeout < 500) { + val = readl(base + ACP_SOFT_RESET); + if (!val) + return 0; + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static void acp3x_enable_interrupts(void __iomem *base) +{ + u32 ext_intr_ctrl; + + writel(0x01, base + ACP_EXTERNAL_INTR_ENB); + ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_ctrl |= ACP_ERROR_MASK; + writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL); +} + +static void acp3x_disable_interrupts(void __iomem *base) +{ + writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT); + writel(0x00, base + ACP_EXTERNAL_INTR_ENB); +} + +static int rn_acp_init(void __iomem *base) +{ + int ret; + + /* power on */ + ret = acp3x_power_on(base); + if (ret) + return ret; + + writel(0x01, base + ACP_CONTROL); + + /* Reset */ + ret = acp3x_reset(base); + if (ret) + return ret; + + acp3x_enable_interrupts(base); + + return 0; +} + +static int rn_acp_deinit(void __iomem *base) +{ + int ret = 0; + + acp3x_disable_interrupts(base); + + /* Reset */ + ret = acp3x_reset(base); + if (ret) + return ret; + + writel(0x00, base + ACP_CONTROL); + + /* power off */ + ret = acp3x_power_off(base); + if (ret) + return ret; + + return 0; +} static int renoir_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct acp_chip_info *chip; struct acp_dev_data *adata; struct resource *res; + int ret; + + chip = dev_get_platdata(&pdev->dev); + if (!chip || !chip->base) { + dev_err(&pdev->dev, "ACP chip data is NULL\n"); + return -ENODEV; + } + + if (chip->acp_rev != ACP3X_DEV) { + dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); + return -ENODEV; + } + + ret = rn_acp_init(chip->base); + if (ret) { + dev_err(&pdev->dev, "ACP Init failed\n"); + return -EINVAL; + } adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); if (!adata) @@ -155,6 +311,20 @@ static int renoir_audio_probe(struct platform_device *pdev) static int renoir_audio_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct acp_chip_info *chip; + int ret; + + chip = dev_get_platdata(&pdev->dev); + if (!chip || !chip->base) { + dev_err(&pdev->dev, "ACP chip data is NULL\n"); + return -ENODEV; + } + + ret = rn_acp_deinit(chip->base); + if (ret) { + dev_err(&pdev->dev, "ACP de-init Failed\n"); + return -EINVAL; + } acp_platform_unregister(dev); return 0; diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h index e38589a142e9..88f6fa597cd6 100644 --- a/sound/soc/amd/acp/chip_offset_byte.h +++ b/sound/soc/amd/acp/chip_offset_byte.h @@ -14,6 +14,12 @@ #define ACPAXI2AXI_ATU_CTRL 0xC40 #define ACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0xC20 #define ACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0xC24 + +#define ACP_PGFSM_CONTROL 0x141C +#define ACP_PGFSM_STATUS 0x1420 +#define ACP_SOFT_RESET 0x1000 +#define ACP_CONTROL 0x1004 + #define ACP_EXTERNAL_INTR_ENB 0x1800 #define ACP_EXTERNAL_INTR_CNTL 0x1804 #define ACP_EXTERNAL_INTR_STAT 0x1808
ACP hardware has PGFSM control registers that can be configured to power On/Off the ACP IP block. Add acp init()/de_init() callbacks in renoir platform driver probe()/remove() respectively to power on and off ACP IP block on ACP3X device. Signed-off-by: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> --- sound/soc/amd/acp/acp-renoir.c | 170 +++++++++++++++++++++++++++ sound/soc/amd/acp/chip_offset_byte.h | 6 + 2 files changed, 176 insertions(+)