xref: /openbmc/linux/sound/soc/amd/renoir/rn-pci-acp3x.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11eb2852eSVijendar Mukunda // SPDX-License-Identifier: GPL-2.0+
21eb2852eSVijendar Mukunda //
31eb2852eSVijendar Mukunda // AMD Renoir ACP PCI Driver
41eb2852eSVijendar Mukunda //
51eb2852eSVijendar Mukunda //Copyright 2020 Advanced Micro Devices, Inc.
61eb2852eSVijendar Mukunda 
71eb2852eSVijendar Mukunda #include <linux/pci.h>
89e0d21e1SVijendar Mukunda #include <linux/acpi.h>
9718c406eSJaroslav Kysela #include <linux/dmi.h>
101eb2852eSVijendar Mukunda #include <linux/module.h>
111eb2852eSVijendar Mukunda #include <linux/io.h>
1285ded495SVijendar Mukunda #include <linux/delay.h>
1366c4f558SVijendar Mukunda #include <linux/platform_device.h>
1466c4f558SVijendar Mukunda #include <linux/interrupt.h>
15c346e768SVijendar Mukunda #include <linux/pm_runtime.h>
161eb2852eSVijendar Mukunda 
171eb2852eSVijendar Mukunda #include "rn_acp3x.h"
181eb2852eSVijendar Mukunda 
1985ded495SVijendar Mukunda static int acp_power_gating;
2085ded495SVijendar Mukunda module_param(acp_power_gating, int, 0644);
2185ded495SVijendar Mukunda MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
2285ded495SVijendar Mukunda 
237655e326SPierre-Louis Bossart /*
24718c406eSJaroslav Kysela  * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
25718c406eSJaroslav Kysela  *                 =  0 - Skip the DMIC device creation and return probe failure
26718c406eSJaroslav Kysela  *                 =  1 - Force DMIC support
279e0d21e1SVijendar Mukunda  */
289e0d21e1SVijendar Mukunda static int dmic_acpi_check = ACP_DMIC_AUTO;
299e0d21e1SVijendar Mukunda module_param(dmic_acpi_check, bint, 0644);
30718c406eSJaroslav Kysela MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
319e0d21e1SVijendar Mukunda 
321eb2852eSVijendar Mukunda struct acp_dev_data {
331eb2852eSVijendar Mukunda 	void __iomem *acp_base;
3466c4f558SVijendar Mukunda 	struct resource *res;
35b208c3bcSVijendar Mukunda 	struct platform_device *pdev[ACP_DEVS];
361eb2852eSVijendar Mukunda };
371eb2852eSVijendar Mukunda 
rn_acp_power_on(void __iomem * acp_base)3885ded495SVijendar Mukunda static int rn_acp_power_on(void __iomem *acp_base)
3985ded495SVijendar Mukunda {
4085ded495SVijendar Mukunda 	u32 val;
4185ded495SVijendar Mukunda 	int timeout;
4285ded495SVijendar Mukunda 
4385ded495SVijendar Mukunda 	val = rn_readl(acp_base + ACP_PGFSM_STATUS);
4485ded495SVijendar Mukunda 
4585ded495SVijendar Mukunda 	if (val == 0)
4685ded495SVijendar Mukunda 		return val;
4785ded495SVijendar Mukunda 
4885ded495SVijendar Mukunda 	if ((val & ACP_PGFSM_STATUS_MASK) !=
4985ded495SVijendar Mukunda 				ACP_POWER_ON_IN_PROGRESS)
5085ded495SVijendar Mukunda 		rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
5185ded495SVijendar Mukunda 			  acp_base + ACP_PGFSM_CONTROL);
5285ded495SVijendar Mukunda 	timeout = 0;
5385ded495SVijendar Mukunda 	while (++timeout < 500) {
5485ded495SVijendar Mukunda 		val = rn_readl(acp_base + ACP_PGFSM_STATUS);
5585ded495SVijendar Mukunda 		if (!val)
5685ded495SVijendar Mukunda 			return 0;
5785ded495SVijendar Mukunda 		udelay(1);
5885ded495SVijendar Mukunda 	}
5985ded495SVijendar Mukunda 	return -ETIMEDOUT;
6085ded495SVijendar Mukunda }
6185ded495SVijendar Mukunda 
rn_acp_power_off(void __iomem * acp_base)6285ded495SVijendar Mukunda static int rn_acp_power_off(void __iomem *acp_base)
6385ded495SVijendar Mukunda {
6485ded495SVijendar Mukunda 	u32 val;
6585ded495SVijendar Mukunda 	int timeout;
6685ded495SVijendar Mukunda 
6785ded495SVijendar Mukunda 	rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
6885ded495SVijendar Mukunda 		  acp_base + ACP_PGFSM_CONTROL);
6985ded495SVijendar Mukunda 	timeout = 0;
7085ded495SVijendar Mukunda 	while (++timeout < 500) {
7185ded495SVijendar Mukunda 		val = rn_readl(acp_base + ACP_PGFSM_STATUS);
7285ded495SVijendar Mukunda 		if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
7385ded495SVijendar Mukunda 			return 0;
7485ded495SVijendar Mukunda 		udelay(1);
7585ded495SVijendar Mukunda 	}
7685ded495SVijendar Mukunda 	return -ETIMEDOUT;
7785ded495SVijendar Mukunda }
7885ded495SVijendar Mukunda 
rn_acp_reset(void __iomem * acp_base)7985ded495SVijendar Mukunda static int rn_acp_reset(void __iomem *acp_base)
8085ded495SVijendar Mukunda {
8185ded495SVijendar Mukunda 	u32 val;
8285ded495SVijendar Mukunda 	int timeout;
8385ded495SVijendar Mukunda 
8485ded495SVijendar Mukunda 	rn_writel(1, acp_base + ACP_SOFT_RESET);
8585ded495SVijendar Mukunda 	timeout = 0;
8685ded495SVijendar Mukunda 	while (++timeout < 500) {
8785ded495SVijendar Mukunda 		val = rn_readl(acp_base + ACP_SOFT_RESET);
8885ded495SVijendar Mukunda 		if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
8985ded495SVijendar Mukunda 			break;
9085ded495SVijendar Mukunda 		cpu_relax();
9185ded495SVijendar Mukunda 	}
9285ded495SVijendar Mukunda 	rn_writel(0, acp_base + ACP_SOFT_RESET);
9385ded495SVijendar Mukunda 	timeout = 0;
9485ded495SVijendar Mukunda 	while (++timeout < 500) {
9585ded495SVijendar Mukunda 		val = rn_readl(acp_base + ACP_SOFT_RESET);
9685ded495SVijendar Mukunda 		if (!val)
9785ded495SVijendar Mukunda 			return 0;
9885ded495SVijendar Mukunda 		cpu_relax();
9985ded495SVijendar Mukunda 	}
10085ded495SVijendar Mukunda 	return -ETIMEDOUT;
10185ded495SVijendar Mukunda }
10285ded495SVijendar Mukunda 
rn_acp_enable_interrupts(void __iomem * acp_base)10385ded495SVijendar Mukunda static void rn_acp_enable_interrupts(void __iomem *acp_base)
10485ded495SVijendar Mukunda {
10585ded495SVijendar Mukunda 	u32 ext_intr_ctrl;
10685ded495SVijendar Mukunda 
10785ded495SVijendar Mukunda 	rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
10885ded495SVijendar Mukunda 	ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
10985ded495SVijendar Mukunda 	ext_intr_ctrl |= ACP_ERROR_MASK;
11085ded495SVijendar Mukunda 	rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
11185ded495SVijendar Mukunda }
11285ded495SVijendar Mukunda 
rn_acp_disable_interrupts(void __iomem * acp_base)11385ded495SVijendar Mukunda static void rn_acp_disable_interrupts(void __iomem *acp_base)
11485ded495SVijendar Mukunda {
11585ded495SVijendar Mukunda 	rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
11685ded495SVijendar Mukunda 		  ACP_EXTERNAL_INTR_STAT);
11785ded495SVijendar Mukunda 	rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
11885ded495SVijendar Mukunda }
11985ded495SVijendar Mukunda 
rn_acp_init(void __iomem * acp_base)12085ded495SVijendar Mukunda static int rn_acp_init(void __iomem *acp_base)
12185ded495SVijendar Mukunda {
12285ded495SVijendar Mukunda 	int ret;
12385ded495SVijendar Mukunda 
12485ded495SVijendar Mukunda 	/* power on */
12585ded495SVijendar Mukunda 	ret = rn_acp_power_on(acp_base);
12685ded495SVijendar Mukunda 	if (ret) {
12785ded495SVijendar Mukunda 		pr_err("ACP power on failed\n");
12885ded495SVijendar Mukunda 		return ret;
12985ded495SVijendar Mukunda 	}
13085ded495SVijendar Mukunda 	rn_writel(0x01, acp_base + ACP_CONTROL);
13185ded495SVijendar Mukunda 	/* Reset */
13285ded495SVijendar Mukunda 	ret = rn_acp_reset(acp_base);
13385ded495SVijendar Mukunda 	if (ret) {
13485ded495SVijendar Mukunda 		pr_err("ACP reset failed\n");
13585ded495SVijendar Mukunda 		return ret;
13685ded495SVijendar Mukunda 	}
13785ded495SVijendar Mukunda 	rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
13885ded495SVijendar Mukunda 	rn_acp_enable_interrupts(acp_base);
13985ded495SVijendar Mukunda 	return 0;
14085ded495SVijendar Mukunda }
14185ded495SVijendar Mukunda 
rn_acp_deinit(void __iomem * acp_base)14285ded495SVijendar Mukunda static int rn_acp_deinit(void __iomem *acp_base)
14385ded495SVijendar Mukunda {
14485ded495SVijendar Mukunda 	int ret;
14585ded495SVijendar Mukunda 
14685ded495SVijendar Mukunda 	rn_acp_disable_interrupts(acp_base);
14785ded495SVijendar Mukunda 	/* Reset */
14885ded495SVijendar Mukunda 	ret = rn_acp_reset(acp_base);
14985ded495SVijendar Mukunda 	if (ret) {
15085ded495SVijendar Mukunda 		pr_err("ACP reset failed\n");
15185ded495SVijendar Mukunda 		return ret;
15285ded495SVijendar Mukunda 	}
15385ded495SVijendar Mukunda 	rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
15485ded495SVijendar Mukunda 	rn_writel(0x00, acp_base + ACP_CONTROL);
15585ded495SVijendar Mukunda 	/* power off */
15685ded495SVijendar Mukunda 	if (acp_power_gating) {
15785ded495SVijendar Mukunda 		ret = rn_acp_power_off(acp_base);
15885ded495SVijendar Mukunda 		if (ret) {
15985ded495SVijendar Mukunda 			pr_err("ACP power off failed\n");
16085ded495SVijendar Mukunda 			return ret;
16185ded495SVijendar Mukunda 		}
16285ded495SVijendar Mukunda 	}
16385ded495SVijendar Mukunda 	return 0;
16485ded495SVijendar Mukunda }
16585ded495SVijendar Mukunda 
166718c406eSJaroslav Kysela static const struct dmi_system_id rn_acp_quirk_table[] = {
167718c406eSJaroslav Kysela 	{
16840caffd6SJaroslav Kysela 		/* Lenovo IdeaPad S340-14API */
169718c406eSJaroslav Kysela 		.matches = {
170718c406eSJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
17140caffd6SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
17240caffd6SJaroslav Kysela 		}
17340caffd6SJaroslav Kysela 	},
17440caffd6SJaroslav Kysela 	{
17540caffd6SJaroslav Kysela 		/* Lenovo IdeaPad Flex 5 14ARE05 */
17640caffd6SJaroslav Kysela 		.matches = {
17740caffd6SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
17840caffd6SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
17940caffd6SJaroslav Kysela 		}
18040caffd6SJaroslav Kysela 	},
18140caffd6SJaroslav Kysela 	{
18240caffd6SJaroslav Kysela 		/* Lenovo IdeaPad 5 15ARE05 */
18340caffd6SJaroslav Kysela 		.matches = {
18440caffd6SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
18540caffd6SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
186718c406eSJaroslav Kysela 		}
187718c406eSJaroslav Kysela 	},
18827556599SJaroslav Kysela 	{
18927556599SJaroslav Kysela 		/* Lenovo ThinkPad E14 Gen 2 */
19027556599SJaroslav Kysela 		.matches = {
19127556599SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
19227556599SJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
19327556599SJaroslav Kysela 		}
19427556599SJaroslav Kysela 	},
1951f092d1cSJaroslav Kysela 	{
1961f092d1cSJaroslav Kysela 		/* Lenovo ThinkPad X395 */
1971f092d1cSJaroslav Kysela 		.matches = {
1981f092d1cSJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
1991f092d1cSJaroslav Kysela 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
2001f092d1cSJaroslav Kysela 		}
2011f092d1cSJaroslav Kysela 	},
202718c406eSJaroslav Kysela 	{}
203718c406eSJaroslav Kysela };
204718c406eSJaroslav Kysela 
snd_rn_acp_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)2051eb2852eSVijendar Mukunda static int snd_rn_acp_probe(struct pci_dev *pci,
2061eb2852eSVijendar Mukunda 			    const struct pci_device_id *pci_id)
2071eb2852eSVijendar Mukunda {
2081eb2852eSVijendar Mukunda 	struct acp_dev_data *adata;
209b208c3bcSVijendar Mukunda 	struct platform_device_info pdevinfo[ACP_DEVS];
210ee3d1339SVijendar Mukunda #if defined(CONFIG_ACPI)
2119e0d21e1SVijendar Mukunda 	acpi_handle handle;
2129e0d21e1SVijendar Mukunda 	acpi_integer dmic_status;
213ee3d1339SVijendar Mukunda #endif
214718c406eSJaroslav Kysela 	const struct dmi_system_id *dmi_id;
215*2d7d9f36SAjit Kumar Pandey 	unsigned int irqflags, flag;
216b208c3bcSVijendar Mukunda 	int ret, index;
2171eb2852eSVijendar Mukunda 	u32 addr;
2181eb2852eSVijendar Mukunda 
219*2d7d9f36SAjit Kumar Pandey 	/* Return if acp config flag is defined */
220*2d7d9f36SAjit Kumar Pandey 	flag = snd_amd_acp_find_config(pci);
221*2d7d9f36SAjit Kumar Pandey 	if (flag)
222*2d7d9f36SAjit Kumar Pandey 		return -ENODEV;
223*2d7d9f36SAjit Kumar Pandey 
22455d8e6a8SJaroslav Kysela 	/* Renoir device check */
22555d8e6a8SJaroslav Kysela 	if (pci->revision != 0x01)
22655d8e6a8SJaroslav Kysela 		return -ENODEV;
22755d8e6a8SJaroslav Kysela 
2281eb2852eSVijendar Mukunda 	if (pci_enable_device(pci)) {
2291eb2852eSVijendar Mukunda 		dev_err(&pci->dev, "pci_enable_device failed\n");
2301eb2852eSVijendar Mukunda 		return -ENODEV;
2311eb2852eSVijendar Mukunda 	}
2321eb2852eSVijendar Mukunda 
2331eb2852eSVijendar Mukunda 	ret = pci_request_regions(pci, "AMD ACP3x audio");
2341eb2852eSVijendar Mukunda 	if (ret < 0) {
2351eb2852eSVijendar Mukunda 		dev_err(&pci->dev, "pci_request_regions failed\n");
2361eb2852eSVijendar Mukunda 		goto disable_pci;
2371eb2852eSVijendar Mukunda 	}
2381eb2852eSVijendar Mukunda 
2391eb2852eSVijendar Mukunda 	adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
2401eb2852eSVijendar Mukunda 			     GFP_KERNEL);
2411eb2852eSVijendar Mukunda 	if (!adata) {
2421eb2852eSVijendar Mukunda 		ret = -ENOMEM;
2431eb2852eSVijendar Mukunda 		goto release_regions;
2441eb2852eSVijendar Mukunda 	}
2451eb2852eSVijendar Mukunda 
24666c4f558SVijendar Mukunda 	/* check for msi interrupt support */
24766c4f558SVijendar Mukunda 	ret = pci_enable_msi(pci);
24866c4f558SVijendar Mukunda 	if (ret)
24966c4f558SVijendar Mukunda 		/* msi is not enabled */
25066c4f558SVijendar Mukunda 		irqflags = IRQF_SHARED;
25166c4f558SVijendar Mukunda 	else
25266c4f558SVijendar Mukunda 		/* msi is enabled */
25366c4f558SVijendar Mukunda 		irqflags = 0;
25466c4f558SVijendar Mukunda 
2551eb2852eSVijendar Mukunda 	addr = pci_resource_start(pci, 0);
2561eb2852eSVijendar Mukunda 	adata->acp_base = devm_ioremap(&pci->dev, addr,
2571eb2852eSVijendar Mukunda 				       pci_resource_len(pci, 0));
2581eb2852eSVijendar Mukunda 	if (!adata->acp_base) {
2591eb2852eSVijendar Mukunda 		ret = -ENOMEM;
26066c4f558SVijendar Mukunda 		goto disable_msi;
2611eb2852eSVijendar Mukunda 	}
2621eb2852eSVijendar Mukunda 	pci_set_master(pci);
2631eb2852eSVijendar Mukunda 	pci_set_drvdata(pci, adata);
26485ded495SVijendar Mukunda 	ret = rn_acp_init(adata->acp_base);
26585ded495SVijendar Mukunda 	if (ret)
26666c4f558SVijendar Mukunda 		goto disable_msi;
26766c4f558SVijendar Mukunda 
2689e0d21e1SVijendar Mukunda 	if (!dmic_acpi_check) {
2699e0d21e1SVijendar Mukunda 		ret = -ENODEV;
2709e0d21e1SVijendar Mukunda 		goto de_init;
2719e0d21e1SVijendar Mukunda 	} else if (dmic_acpi_check == ACP_DMIC_AUTO) {
27268d1abe1SVijendar Mukunda #if defined(CONFIG_ACPI)
2739e0d21e1SVijendar Mukunda 		handle = ACPI_HANDLE(&pci->dev);
2749e0d21e1SVijendar Mukunda 		ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
2759e0d21e1SVijendar Mukunda 		if (ACPI_FAILURE(ret)) {
276ab5893fdSTakashi Iwai 			ret = -ENODEV;
2779e0d21e1SVijendar Mukunda 			goto de_init;
2789e0d21e1SVijendar Mukunda 		}
2799e0d21e1SVijendar Mukunda 		if (!dmic_status) {
2809e0d21e1SVijendar Mukunda 			ret = -ENODEV;
2819e0d21e1SVijendar Mukunda 			goto de_init;
2829e0d21e1SVijendar Mukunda 		}
28368d1abe1SVijendar Mukunda #endif
284718c406eSJaroslav Kysela 		dmi_id = dmi_first_match(rn_acp_quirk_table);
285718c406eSJaroslav Kysela 		if (dmi_id && !dmi_id->driver_data) {
286718c406eSJaroslav Kysela 			dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
287718c406eSJaroslav Kysela 			ret = -ENODEV;
288718c406eSJaroslav Kysela 			goto de_init;
289718c406eSJaroslav Kysela 		}
2909e0d21e1SVijendar Mukunda 	}
2919e0d21e1SVijendar Mukunda 
29266c4f558SVijendar Mukunda 	adata->res = devm_kzalloc(&pci->dev,
29366c4f558SVijendar Mukunda 				  sizeof(struct resource) * 2,
29466c4f558SVijendar Mukunda 				  GFP_KERNEL);
29566c4f558SVijendar Mukunda 	if (!adata->res) {
29666c4f558SVijendar Mukunda 		ret = -ENOMEM;
29766c4f558SVijendar Mukunda 		goto de_init;
29866c4f558SVijendar Mukunda 	}
29966c4f558SVijendar Mukunda 
30066c4f558SVijendar Mukunda 	adata->res[0].name = "acp_pdm_iomem";
30166c4f558SVijendar Mukunda 	adata->res[0].flags = IORESOURCE_MEM;
30266c4f558SVijendar Mukunda 	adata->res[0].start = addr;
30366c4f558SVijendar Mukunda 	adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
30466c4f558SVijendar Mukunda 	adata->res[1].name = "acp_pdm_irq";
30566c4f558SVijendar Mukunda 	adata->res[1].flags = IORESOURCE_IRQ;
30666c4f558SVijendar Mukunda 	adata->res[1].start = pci->irq;
30766c4f558SVijendar Mukunda 	adata->res[1].end = pci->irq;
30866c4f558SVijendar Mukunda 
30966c4f558SVijendar Mukunda 	memset(&pdevinfo, 0, sizeof(pdevinfo));
310b208c3bcSVijendar Mukunda 	pdevinfo[0].name = "acp_rn_pdm_dma";
311b208c3bcSVijendar Mukunda 	pdevinfo[0].id = 0;
312b208c3bcSVijendar Mukunda 	pdevinfo[0].parent = &pci->dev;
313b208c3bcSVijendar Mukunda 	pdevinfo[0].num_res = 2;
314b208c3bcSVijendar Mukunda 	pdevinfo[0].res = adata->res;
315b208c3bcSVijendar Mukunda 	pdevinfo[0].data = &irqflags;
316b208c3bcSVijendar Mukunda 	pdevinfo[0].size_data = sizeof(irqflags);
31766c4f558SVijendar Mukunda 
318b208c3bcSVijendar Mukunda 	pdevinfo[1].name = "dmic-codec";
319b208c3bcSVijendar Mukunda 	pdevinfo[1].id = 0;
320b208c3bcSVijendar Mukunda 	pdevinfo[1].parent = &pci->dev;
321b208c3bcSVijendar Mukunda 	pdevinfo[2].name = "acp_pdm_mach";
322b208c3bcSVijendar Mukunda 	pdevinfo[2].id = 0;
323b208c3bcSVijendar Mukunda 	pdevinfo[2].parent = &pci->dev;
324b208c3bcSVijendar Mukunda 	for (index = 0; index < ACP_DEVS; index++) {
325b208c3bcSVijendar Mukunda 		adata->pdev[index] =
326b208c3bcSVijendar Mukunda 				platform_device_register_full(&pdevinfo[index]);
327b208c3bcSVijendar Mukunda 		if (IS_ERR(adata->pdev[index])) {
32866c4f558SVijendar Mukunda 			dev_err(&pci->dev, "cannot register %s device\n",
329b208c3bcSVijendar Mukunda 				pdevinfo[index].name);
330b208c3bcSVijendar Mukunda 			ret = PTR_ERR(adata->pdev[index]);
33166c4f558SVijendar Mukunda 			goto unregister_devs;
33266c4f558SVijendar Mukunda 		}
333b208c3bcSVijendar Mukunda 	}
334c346e768SVijendar Mukunda 	pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
335c346e768SVijendar Mukunda 	pm_runtime_use_autosuspend(&pci->dev);
336c346e768SVijendar Mukunda 	pm_runtime_put_noidle(&pci->dev);
337c346e768SVijendar Mukunda 	pm_runtime_allow(&pci->dev);
3381eb2852eSVijendar Mukunda 	return 0;
3391eb2852eSVijendar Mukunda 
34066c4f558SVijendar Mukunda unregister_devs:
341b208c3bcSVijendar Mukunda 	for (index = 0; index < ACP_DEVS; index++)
342b208c3bcSVijendar Mukunda 		platform_device_unregister(adata->pdev[index]);
34366c4f558SVijendar Mukunda de_init:
34466c4f558SVijendar Mukunda 	if (rn_acp_deinit(adata->acp_base))
34566c4f558SVijendar Mukunda 		dev_err(&pci->dev, "ACP de-init failed\n");
34666c4f558SVijendar Mukunda disable_msi:
34766c4f558SVijendar Mukunda 	pci_disable_msi(pci);
3481eb2852eSVijendar Mukunda release_regions:
3491eb2852eSVijendar Mukunda 	pci_release_regions(pci);
3501eb2852eSVijendar Mukunda disable_pci:
3511eb2852eSVijendar Mukunda 	pci_disable_device(pci);
3521eb2852eSVijendar Mukunda 
3531eb2852eSVijendar Mukunda 	return ret;
3541eb2852eSVijendar Mukunda }
3551eb2852eSVijendar Mukunda 
snd_rn_acp_suspend(struct device * dev)356c346e768SVijendar Mukunda static int snd_rn_acp_suspend(struct device *dev)
357c346e768SVijendar Mukunda {
358c346e768SVijendar Mukunda 	int ret;
359c346e768SVijendar Mukunda 	struct acp_dev_data *adata;
360c346e768SVijendar Mukunda 
361c346e768SVijendar Mukunda 	adata = dev_get_drvdata(dev);
362c346e768SVijendar Mukunda 	ret = rn_acp_deinit(adata->acp_base);
363c346e768SVijendar Mukunda 	if (ret)
364c346e768SVijendar Mukunda 		dev_err(dev, "ACP de-init failed\n");
365c346e768SVijendar Mukunda 	else
366c346e768SVijendar Mukunda 		dev_dbg(dev, "ACP de-initialized\n");
367c346e768SVijendar Mukunda 
368ce595586SVijendar Mukunda 	return ret;
369c346e768SVijendar Mukunda }
370c346e768SVijendar Mukunda 
snd_rn_acp_resume(struct device * dev)371c346e768SVijendar Mukunda static int snd_rn_acp_resume(struct device *dev)
372c346e768SVijendar Mukunda {
373c346e768SVijendar Mukunda 	int ret;
374c346e768SVijendar Mukunda 	struct acp_dev_data *adata;
375c346e768SVijendar Mukunda 
376c346e768SVijendar Mukunda 	adata = dev_get_drvdata(dev);
377c346e768SVijendar Mukunda 	ret = rn_acp_init(adata->acp_base);
378c346e768SVijendar Mukunda 	if (ret) {
379c346e768SVijendar Mukunda 		dev_err(dev, "ACP init failed\n");
380c346e768SVijendar Mukunda 		return ret;
381c346e768SVijendar Mukunda 	}
382c346e768SVijendar Mukunda 	return 0;
383c346e768SVijendar Mukunda }
384c346e768SVijendar Mukunda 
385c346e768SVijendar Mukunda static const struct dev_pm_ops rn_acp_pm = {
386c346e768SVijendar Mukunda 	.runtime_suspend = snd_rn_acp_suspend,
387c346e768SVijendar Mukunda 	.runtime_resume =  snd_rn_acp_resume,
388c346e768SVijendar Mukunda 	.suspend = snd_rn_acp_suspend,
389c346e768SVijendar Mukunda 	.resume =	snd_rn_acp_resume,
390d00f541aSMario Limonciello 	.restore =	snd_rn_acp_resume,
391d00f541aSMario Limonciello 	.poweroff =	snd_rn_acp_suspend,
392c346e768SVijendar Mukunda };
393c346e768SVijendar Mukunda 
snd_rn_acp_remove(struct pci_dev * pci)3941eb2852eSVijendar Mukunda static void snd_rn_acp_remove(struct pci_dev *pci)
3951eb2852eSVijendar Mukunda {
39685ded495SVijendar Mukunda 	struct acp_dev_data *adata;
397b208c3bcSVijendar Mukunda 	int ret, index;
39885ded495SVijendar Mukunda 
39985ded495SVijendar Mukunda 	adata = pci_get_drvdata(pci);
400b208c3bcSVijendar Mukunda 	for (index = 0; index < ACP_DEVS; index++)
401b208c3bcSVijendar Mukunda 		platform_device_unregister(adata->pdev[index]);
40285ded495SVijendar Mukunda 	ret = rn_acp_deinit(adata->acp_base);
40385ded495SVijendar Mukunda 	if (ret)
40485ded495SVijendar Mukunda 		dev_err(&pci->dev, "ACP de-init failed\n");
405c346e768SVijendar Mukunda 	pm_runtime_forbid(&pci->dev);
406c346e768SVijendar Mukunda 	pm_runtime_get_noresume(&pci->dev);
4071eb2852eSVijendar Mukunda 	pci_disable_msi(pci);
4081eb2852eSVijendar Mukunda 	pci_release_regions(pci);
4091eb2852eSVijendar Mukunda 	pci_disable_device(pci);
4101eb2852eSVijendar Mukunda }
4111eb2852eSVijendar Mukunda 
4121eb2852eSVijendar Mukunda static const struct pci_device_id snd_rn_acp_ids[] = {
4131eb2852eSVijendar Mukunda 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
4141eb2852eSVijendar Mukunda 	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
4151eb2852eSVijendar Mukunda 	.class_mask = 0xffffff },
4161eb2852eSVijendar Mukunda 	{ 0, },
4171eb2852eSVijendar Mukunda };
4181eb2852eSVijendar Mukunda MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
4191eb2852eSVijendar Mukunda 
4201eb2852eSVijendar Mukunda static struct pci_driver rn_acp_driver  = {
4211eb2852eSVijendar Mukunda 	.name = KBUILD_MODNAME,
4221eb2852eSVijendar Mukunda 	.id_table = snd_rn_acp_ids,
4231eb2852eSVijendar Mukunda 	.probe = snd_rn_acp_probe,
4241eb2852eSVijendar Mukunda 	.remove = snd_rn_acp_remove,
425c346e768SVijendar Mukunda 	.driver = {
426c346e768SVijendar Mukunda 		.pm = &rn_acp_pm,
427c346e768SVijendar Mukunda 	}
4281eb2852eSVijendar Mukunda };
4291eb2852eSVijendar Mukunda 
4301eb2852eSVijendar Mukunda module_pci_driver(rn_acp_driver);
4311eb2852eSVijendar Mukunda 
4321eb2852eSVijendar Mukunda MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
4331eb2852eSVijendar Mukunda MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
4341eb2852eSVijendar Mukunda MODULE_LICENSE("GPL v2");
435