1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // AMD Renoir ACP PCI Driver
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun //Copyright 2020 Advanced Micro Devices, Inc.
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/pci.h>
8*4882a593Smuzhiyun #include <linux/acpi.h>
9*4882a593Smuzhiyun #include <linux/dmi.h>
10*4882a593Smuzhiyun #include <linux/module.h>
11*4882a593Smuzhiyun #include <linux/io.h>
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/platform_device.h>
14*4882a593Smuzhiyun #include <linux/interrupt.h>
15*4882a593Smuzhiyun #include <linux/pm_runtime.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include "rn_acp3x.h"
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun static int acp_power_gating;
20*4882a593Smuzhiyun module_param(acp_power_gating, int, 0644);
21*4882a593Smuzhiyun MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun /**
24*4882a593Smuzhiyun * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
25*4882a593Smuzhiyun * = 0 - Skip the DMIC device creation and return probe failure
26*4882a593Smuzhiyun * = 1 - Force DMIC support
27*4882a593Smuzhiyun */
28*4882a593Smuzhiyun static int dmic_acpi_check = ACP_DMIC_AUTO;
29*4882a593Smuzhiyun module_param(dmic_acpi_check, bint, 0644);
30*4882a593Smuzhiyun MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun struct acp_dev_data {
33*4882a593Smuzhiyun void __iomem *acp_base;
34*4882a593Smuzhiyun struct resource *res;
35*4882a593Smuzhiyun struct platform_device *pdev[ACP_DEVS];
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun
rn_acp_power_on(void __iomem * acp_base)38*4882a593Smuzhiyun static int rn_acp_power_on(void __iomem *acp_base)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun u32 val;
41*4882a593Smuzhiyun int timeout;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun val = rn_readl(acp_base + ACP_PGFSM_STATUS);
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun if (val == 0)
46*4882a593Smuzhiyun return val;
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun if ((val & ACP_PGFSM_STATUS_MASK) !=
49*4882a593Smuzhiyun ACP_POWER_ON_IN_PROGRESS)
50*4882a593Smuzhiyun rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
51*4882a593Smuzhiyun acp_base + ACP_PGFSM_CONTROL);
52*4882a593Smuzhiyun timeout = 0;
53*4882a593Smuzhiyun while (++timeout < 500) {
54*4882a593Smuzhiyun val = rn_readl(acp_base + ACP_PGFSM_STATUS);
55*4882a593Smuzhiyun if (!val)
56*4882a593Smuzhiyun return 0;
57*4882a593Smuzhiyun udelay(1);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun return -ETIMEDOUT;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
rn_acp_power_off(void __iomem * acp_base)62*4882a593Smuzhiyun static int rn_acp_power_off(void __iomem *acp_base)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun u32 val;
65*4882a593Smuzhiyun int timeout;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
68*4882a593Smuzhiyun acp_base + ACP_PGFSM_CONTROL);
69*4882a593Smuzhiyun timeout = 0;
70*4882a593Smuzhiyun while (++timeout < 500) {
71*4882a593Smuzhiyun val = rn_readl(acp_base + ACP_PGFSM_STATUS);
72*4882a593Smuzhiyun if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
73*4882a593Smuzhiyun return 0;
74*4882a593Smuzhiyun udelay(1);
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun return -ETIMEDOUT;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun
rn_acp_reset(void __iomem * acp_base)79*4882a593Smuzhiyun static int rn_acp_reset(void __iomem *acp_base)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun u32 val;
82*4882a593Smuzhiyun int timeout;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun rn_writel(1, acp_base + ACP_SOFT_RESET);
85*4882a593Smuzhiyun timeout = 0;
86*4882a593Smuzhiyun while (++timeout < 500) {
87*4882a593Smuzhiyun val = rn_readl(acp_base + ACP_SOFT_RESET);
88*4882a593Smuzhiyun if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
89*4882a593Smuzhiyun break;
90*4882a593Smuzhiyun cpu_relax();
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun rn_writel(0, acp_base + ACP_SOFT_RESET);
93*4882a593Smuzhiyun timeout = 0;
94*4882a593Smuzhiyun while (++timeout < 500) {
95*4882a593Smuzhiyun val = rn_readl(acp_base + ACP_SOFT_RESET);
96*4882a593Smuzhiyun if (!val)
97*4882a593Smuzhiyun return 0;
98*4882a593Smuzhiyun cpu_relax();
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun return -ETIMEDOUT;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
rn_acp_enable_interrupts(void __iomem * acp_base)103*4882a593Smuzhiyun static void rn_acp_enable_interrupts(void __iomem *acp_base)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun u32 ext_intr_ctrl;
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
108*4882a593Smuzhiyun ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
109*4882a593Smuzhiyun ext_intr_ctrl |= ACP_ERROR_MASK;
110*4882a593Smuzhiyun rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
rn_acp_disable_interrupts(void __iomem * acp_base)113*4882a593Smuzhiyun static void rn_acp_disable_interrupts(void __iomem *acp_base)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
116*4882a593Smuzhiyun ACP_EXTERNAL_INTR_STAT);
117*4882a593Smuzhiyun rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
rn_acp_init(void __iomem * acp_base)120*4882a593Smuzhiyun static int rn_acp_init(void __iomem *acp_base)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun int ret;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun /* power on */
125*4882a593Smuzhiyun ret = rn_acp_power_on(acp_base);
126*4882a593Smuzhiyun if (ret) {
127*4882a593Smuzhiyun pr_err("ACP power on failed\n");
128*4882a593Smuzhiyun return ret;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun rn_writel(0x01, acp_base + ACP_CONTROL);
131*4882a593Smuzhiyun /* Reset */
132*4882a593Smuzhiyun ret = rn_acp_reset(acp_base);
133*4882a593Smuzhiyun if (ret) {
134*4882a593Smuzhiyun pr_err("ACP reset failed\n");
135*4882a593Smuzhiyun return ret;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
138*4882a593Smuzhiyun rn_acp_enable_interrupts(acp_base);
139*4882a593Smuzhiyun return 0;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
rn_acp_deinit(void __iomem * acp_base)142*4882a593Smuzhiyun static int rn_acp_deinit(void __iomem *acp_base)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun int ret;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun rn_acp_disable_interrupts(acp_base);
147*4882a593Smuzhiyun /* Reset */
148*4882a593Smuzhiyun ret = rn_acp_reset(acp_base);
149*4882a593Smuzhiyun if (ret) {
150*4882a593Smuzhiyun pr_err("ACP reset failed\n");
151*4882a593Smuzhiyun return ret;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
154*4882a593Smuzhiyun rn_writel(0x00, acp_base + ACP_CONTROL);
155*4882a593Smuzhiyun /* power off */
156*4882a593Smuzhiyun if (acp_power_gating) {
157*4882a593Smuzhiyun ret = rn_acp_power_off(acp_base);
158*4882a593Smuzhiyun if (ret) {
159*4882a593Smuzhiyun pr_err("ACP power off failed\n");
160*4882a593Smuzhiyun return ret;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun return 0;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun static const struct dmi_system_id rn_acp_quirk_table[] = {
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun /* Lenovo IdeaPad S340-14API */
169*4882a593Smuzhiyun .matches = {
170*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
171*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun },
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun /* Lenovo IdeaPad Flex 5 14ARE05 */
176*4882a593Smuzhiyun .matches = {
177*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
178*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun },
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun /* Lenovo IdeaPad 5 15ARE05 */
183*4882a593Smuzhiyun .matches = {
184*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
185*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun },
188*4882a593Smuzhiyun {
189*4882a593Smuzhiyun /* Lenovo ThinkPad X395 */
190*4882a593Smuzhiyun .matches = {
191*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
192*4882a593Smuzhiyun DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun },
195*4882a593Smuzhiyun {}
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun
snd_rn_acp_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)198*4882a593Smuzhiyun static int snd_rn_acp_probe(struct pci_dev *pci,
199*4882a593Smuzhiyun const struct pci_device_id *pci_id)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun struct acp_dev_data *adata;
202*4882a593Smuzhiyun struct platform_device_info pdevinfo[ACP_DEVS];
203*4882a593Smuzhiyun #if defined(CONFIG_ACPI)
204*4882a593Smuzhiyun acpi_handle handle;
205*4882a593Smuzhiyun acpi_integer dmic_status;
206*4882a593Smuzhiyun #endif
207*4882a593Smuzhiyun const struct dmi_system_id *dmi_id;
208*4882a593Smuzhiyun unsigned int irqflags;
209*4882a593Smuzhiyun int ret, index;
210*4882a593Smuzhiyun u32 addr;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun /* Renoir device check */
213*4882a593Smuzhiyun if (pci->revision != 0x01)
214*4882a593Smuzhiyun return -ENODEV;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun if (pci_enable_device(pci)) {
217*4882a593Smuzhiyun dev_err(&pci->dev, "pci_enable_device failed\n");
218*4882a593Smuzhiyun return -ENODEV;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun ret = pci_request_regions(pci, "AMD ACP3x audio");
222*4882a593Smuzhiyun if (ret < 0) {
223*4882a593Smuzhiyun dev_err(&pci->dev, "pci_request_regions failed\n");
224*4882a593Smuzhiyun goto disable_pci;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
228*4882a593Smuzhiyun GFP_KERNEL);
229*4882a593Smuzhiyun if (!adata) {
230*4882a593Smuzhiyun ret = -ENOMEM;
231*4882a593Smuzhiyun goto release_regions;
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* check for msi interrupt support */
235*4882a593Smuzhiyun ret = pci_enable_msi(pci);
236*4882a593Smuzhiyun if (ret)
237*4882a593Smuzhiyun /* msi is not enabled */
238*4882a593Smuzhiyun irqflags = IRQF_SHARED;
239*4882a593Smuzhiyun else
240*4882a593Smuzhiyun /* msi is enabled */
241*4882a593Smuzhiyun irqflags = 0;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun addr = pci_resource_start(pci, 0);
244*4882a593Smuzhiyun adata->acp_base = devm_ioremap(&pci->dev, addr,
245*4882a593Smuzhiyun pci_resource_len(pci, 0));
246*4882a593Smuzhiyun if (!adata->acp_base) {
247*4882a593Smuzhiyun ret = -ENOMEM;
248*4882a593Smuzhiyun goto disable_msi;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun pci_set_master(pci);
251*4882a593Smuzhiyun pci_set_drvdata(pci, adata);
252*4882a593Smuzhiyun ret = rn_acp_init(adata->acp_base);
253*4882a593Smuzhiyun if (ret)
254*4882a593Smuzhiyun goto disable_msi;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun if (!dmic_acpi_check) {
257*4882a593Smuzhiyun ret = -ENODEV;
258*4882a593Smuzhiyun goto de_init;
259*4882a593Smuzhiyun } else if (dmic_acpi_check == ACP_DMIC_AUTO) {
260*4882a593Smuzhiyun #if defined(CONFIG_ACPI)
261*4882a593Smuzhiyun handle = ACPI_HANDLE(&pci->dev);
262*4882a593Smuzhiyun ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
263*4882a593Smuzhiyun if (ACPI_FAILURE(ret)) {
264*4882a593Smuzhiyun ret = -EINVAL;
265*4882a593Smuzhiyun goto de_init;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun if (!dmic_status) {
268*4882a593Smuzhiyun ret = -ENODEV;
269*4882a593Smuzhiyun goto de_init;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun #endif
272*4882a593Smuzhiyun dmi_id = dmi_first_match(rn_acp_quirk_table);
273*4882a593Smuzhiyun if (dmi_id && !dmi_id->driver_data) {
274*4882a593Smuzhiyun dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
275*4882a593Smuzhiyun ret = -ENODEV;
276*4882a593Smuzhiyun goto de_init;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun adata->res = devm_kzalloc(&pci->dev,
281*4882a593Smuzhiyun sizeof(struct resource) * 2,
282*4882a593Smuzhiyun GFP_KERNEL);
283*4882a593Smuzhiyun if (!adata->res) {
284*4882a593Smuzhiyun ret = -ENOMEM;
285*4882a593Smuzhiyun goto de_init;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun adata->res[0].name = "acp_pdm_iomem";
289*4882a593Smuzhiyun adata->res[0].flags = IORESOURCE_MEM;
290*4882a593Smuzhiyun adata->res[0].start = addr;
291*4882a593Smuzhiyun adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
292*4882a593Smuzhiyun adata->res[1].name = "acp_pdm_irq";
293*4882a593Smuzhiyun adata->res[1].flags = IORESOURCE_IRQ;
294*4882a593Smuzhiyun adata->res[1].start = pci->irq;
295*4882a593Smuzhiyun adata->res[1].end = pci->irq;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun memset(&pdevinfo, 0, sizeof(pdevinfo));
298*4882a593Smuzhiyun pdevinfo[0].name = "acp_rn_pdm_dma";
299*4882a593Smuzhiyun pdevinfo[0].id = 0;
300*4882a593Smuzhiyun pdevinfo[0].parent = &pci->dev;
301*4882a593Smuzhiyun pdevinfo[0].num_res = 2;
302*4882a593Smuzhiyun pdevinfo[0].res = adata->res;
303*4882a593Smuzhiyun pdevinfo[0].data = &irqflags;
304*4882a593Smuzhiyun pdevinfo[0].size_data = sizeof(irqflags);
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun pdevinfo[1].name = "dmic-codec";
307*4882a593Smuzhiyun pdevinfo[1].id = 0;
308*4882a593Smuzhiyun pdevinfo[1].parent = &pci->dev;
309*4882a593Smuzhiyun pdevinfo[2].name = "acp_pdm_mach";
310*4882a593Smuzhiyun pdevinfo[2].id = 0;
311*4882a593Smuzhiyun pdevinfo[2].parent = &pci->dev;
312*4882a593Smuzhiyun for (index = 0; index < ACP_DEVS; index++) {
313*4882a593Smuzhiyun adata->pdev[index] =
314*4882a593Smuzhiyun platform_device_register_full(&pdevinfo[index]);
315*4882a593Smuzhiyun if (IS_ERR(adata->pdev[index])) {
316*4882a593Smuzhiyun dev_err(&pci->dev, "cannot register %s device\n",
317*4882a593Smuzhiyun pdevinfo[index].name);
318*4882a593Smuzhiyun ret = PTR_ERR(adata->pdev[index]);
319*4882a593Smuzhiyun goto unregister_devs;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
323*4882a593Smuzhiyun pm_runtime_use_autosuspend(&pci->dev);
324*4882a593Smuzhiyun pm_runtime_put_noidle(&pci->dev);
325*4882a593Smuzhiyun pm_runtime_allow(&pci->dev);
326*4882a593Smuzhiyun return 0;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun unregister_devs:
329*4882a593Smuzhiyun for (index = 0; index < ACP_DEVS; index++)
330*4882a593Smuzhiyun platform_device_unregister(adata->pdev[index]);
331*4882a593Smuzhiyun de_init:
332*4882a593Smuzhiyun if (rn_acp_deinit(adata->acp_base))
333*4882a593Smuzhiyun dev_err(&pci->dev, "ACP de-init failed\n");
334*4882a593Smuzhiyun disable_msi:
335*4882a593Smuzhiyun pci_disable_msi(pci);
336*4882a593Smuzhiyun release_regions:
337*4882a593Smuzhiyun pci_release_regions(pci);
338*4882a593Smuzhiyun disable_pci:
339*4882a593Smuzhiyun pci_disable_device(pci);
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun return ret;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
snd_rn_acp_suspend(struct device * dev)344*4882a593Smuzhiyun static int snd_rn_acp_suspend(struct device *dev)
345*4882a593Smuzhiyun {
346*4882a593Smuzhiyun int ret;
347*4882a593Smuzhiyun struct acp_dev_data *adata;
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun adata = dev_get_drvdata(dev);
350*4882a593Smuzhiyun ret = rn_acp_deinit(adata->acp_base);
351*4882a593Smuzhiyun if (ret)
352*4882a593Smuzhiyun dev_err(dev, "ACP de-init failed\n");
353*4882a593Smuzhiyun else
354*4882a593Smuzhiyun dev_dbg(dev, "ACP de-initialized\n");
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun return ret;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun
snd_rn_acp_resume(struct device * dev)359*4882a593Smuzhiyun static int snd_rn_acp_resume(struct device *dev)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun int ret;
362*4882a593Smuzhiyun struct acp_dev_data *adata;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun adata = dev_get_drvdata(dev);
365*4882a593Smuzhiyun ret = rn_acp_init(adata->acp_base);
366*4882a593Smuzhiyun if (ret) {
367*4882a593Smuzhiyun dev_err(dev, "ACP init failed\n");
368*4882a593Smuzhiyun return ret;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun return 0;
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun
373*4882a593Smuzhiyun static const struct dev_pm_ops rn_acp_pm = {
374*4882a593Smuzhiyun .runtime_suspend = snd_rn_acp_suspend,
375*4882a593Smuzhiyun .runtime_resume = snd_rn_acp_resume,
376*4882a593Smuzhiyun .suspend = snd_rn_acp_suspend,
377*4882a593Smuzhiyun .resume = snd_rn_acp_resume,
378*4882a593Smuzhiyun };
379*4882a593Smuzhiyun
snd_rn_acp_remove(struct pci_dev * pci)380*4882a593Smuzhiyun static void snd_rn_acp_remove(struct pci_dev *pci)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun struct acp_dev_data *adata;
383*4882a593Smuzhiyun int ret, index;
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun adata = pci_get_drvdata(pci);
386*4882a593Smuzhiyun for (index = 0; index < ACP_DEVS; index++)
387*4882a593Smuzhiyun platform_device_unregister(adata->pdev[index]);
388*4882a593Smuzhiyun ret = rn_acp_deinit(adata->acp_base);
389*4882a593Smuzhiyun if (ret)
390*4882a593Smuzhiyun dev_err(&pci->dev, "ACP de-init failed\n");
391*4882a593Smuzhiyun pm_runtime_forbid(&pci->dev);
392*4882a593Smuzhiyun pm_runtime_get_noresume(&pci->dev);
393*4882a593Smuzhiyun pci_disable_msi(pci);
394*4882a593Smuzhiyun pci_release_regions(pci);
395*4882a593Smuzhiyun pci_disable_device(pci);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun static const struct pci_device_id snd_rn_acp_ids[] = {
399*4882a593Smuzhiyun { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
400*4882a593Smuzhiyun .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
401*4882a593Smuzhiyun .class_mask = 0xffffff },
402*4882a593Smuzhiyun { 0, },
403*4882a593Smuzhiyun };
404*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun static struct pci_driver rn_acp_driver = {
407*4882a593Smuzhiyun .name = KBUILD_MODNAME,
408*4882a593Smuzhiyun .id_table = snd_rn_acp_ids,
409*4882a593Smuzhiyun .probe = snd_rn_acp_probe,
410*4882a593Smuzhiyun .remove = snd_rn_acp_remove,
411*4882a593Smuzhiyun .driver = {
412*4882a593Smuzhiyun .pm = &rn_acp_pm,
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun };
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun module_pci_driver(rn_acp_driver);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
419*4882a593Smuzhiyun MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
420*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
421