1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
2 | // |
3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
4 | // redistributing this file, you may do so under either license. |
5 | // |
6 | // Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved. |
7 | // |
8 | // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> |
9 | |
10 | /* |
11 | * Generic PCI interface for ACP device |
12 | */ |
13 | |
14 | #include <linux/delay.h> |
15 | #include <linux/interrupt.h> |
16 | #include <linux/pci.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/module.h> |
19 | #include <linux/pm_runtime.h> |
20 | |
21 | #include "amd.h" |
22 | #include "../mach-config.h" |
23 | |
24 | #define DRV_NAME "acp_pci" |
25 | |
26 | #define ACP3x_REG_START 0x1240000 |
27 | #define ACP3x_REG_END 0x125C000 |
28 | |
29 | static struct platform_device *dmic_dev; |
30 | static struct platform_device *pdev; |
31 | |
32 | static const struct resource acp_res[] = { |
33 | { |
34 | .start = 0, |
35 | .end = ACP3x_REG_END - ACP3x_REG_START, |
36 | .name = "acp_mem" , |
37 | .flags = IORESOURCE_MEM, |
38 | }, |
39 | { |
40 | .start = 0, |
41 | .end = 0, |
42 | .name = "acp_dai_irq" , |
43 | .flags = IORESOURCE_IRQ, |
44 | }, |
45 | }; |
46 | |
47 | static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
48 | { |
49 | struct platform_device_info pdevinfo; |
50 | struct device *dev = &pci->dev; |
51 | const struct resource *res_acp; |
52 | struct acp_chip_info *chip; |
53 | struct resource *res; |
54 | unsigned int flag, addr, num_res, i; |
55 | int ret; |
56 | |
57 | flag = snd_amd_acp_find_config(pci); |
58 | if (flag != FLAG_AMD_LEGACY && flag != FLAG_AMD_LEGACY_ONLY_DMIC) |
59 | return -ENODEV; |
60 | |
61 | chip = devm_kzalloc(dev: &pci->dev, size: sizeof(*chip), GFP_KERNEL); |
62 | if (!chip) |
63 | return -ENOMEM; |
64 | |
65 | if (pci_enable_device(dev: pci)) |
66 | return dev_err_probe(dev: &pci->dev, err: -ENODEV, |
67 | fmt: "pci_enable_device failed\n" ); |
68 | |
69 | ret = pci_request_regions(pci, "AMD ACP3x audio" ); |
70 | if (ret < 0) { |
71 | dev_err(&pci->dev, "pci_request_regions failed\n" ); |
72 | ret = -ENOMEM; |
73 | goto disable_pci; |
74 | } |
75 | |
76 | pci_set_master(dev: pci); |
77 | |
78 | res_acp = acp_res; |
79 | num_res = ARRAY_SIZE(acp_res); |
80 | |
81 | switch (pci->revision) { |
82 | case 0x01: |
83 | chip->name = "acp_asoc_renoir" ; |
84 | chip->acp_rev = ACP3X_DEV; |
85 | break; |
86 | case 0x6f: |
87 | chip->name = "acp_asoc_rembrandt" ; |
88 | chip->acp_rev = ACP6X_DEV; |
89 | break; |
90 | case 0x63: |
91 | chip->name = "acp_asoc_acp63" ; |
92 | chip->acp_rev = ACP63_DEV; |
93 | break; |
94 | case 0x70: |
95 | chip->name = "acp_asoc_acp70" ; |
96 | chip->acp_rev = ACP70_DEV; |
97 | break; |
98 | default: |
99 | dev_err(dev, "Unsupported device revision:0x%x\n" , pci->revision); |
100 | ret = -EINVAL; |
101 | goto release_regions; |
102 | } |
103 | |
104 | dmic_dev = platform_device_register_data(parent: dev, name: "dmic-codec" , PLATFORM_DEVID_NONE, NULL, size: 0); |
105 | if (IS_ERR(ptr: dmic_dev)) { |
106 | dev_err(dev, "failed to create DMIC device\n" ); |
107 | ret = PTR_ERR(ptr: dmic_dev); |
108 | goto release_regions; |
109 | } |
110 | |
111 | addr = pci_resource_start(pci, 0); |
112 | chip->base = devm_ioremap(dev: &pci->dev, offset: addr, pci_resource_len(pci, 0)); |
113 | if (!chip->base) { |
114 | ret = -ENOMEM; |
115 | goto unregister_dmic_dev; |
116 | } |
117 | |
118 | ret = acp_init(chip); |
119 | if (ret) |
120 | goto unregister_dmic_dev; |
121 | |
122 | res = devm_kcalloc(dev: &pci->dev, n: num_res, size: sizeof(struct resource), GFP_KERNEL); |
123 | if (!res) { |
124 | ret = -ENOMEM; |
125 | goto unregister_dmic_dev; |
126 | } |
127 | |
128 | for (i = 0; i < num_res; i++, res_acp++) { |
129 | res[i].name = res_acp->name; |
130 | res[i].flags = res_acp->flags; |
131 | res[i].start = addr + res_acp->start; |
132 | res[i].end = addr + res_acp->end; |
133 | if (res_acp->flags == IORESOURCE_IRQ) { |
134 | res[i].start = pci->irq; |
135 | res[i].end = res[i].start; |
136 | } |
137 | } |
138 | |
139 | ret = check_acp_pdm(pci, chip); |
140 | if (ret < 0) |
141 | goto skip_pdev_creation; |
142 | |
143 | chip->flag = flag; |
144 | memset(&pdevinfo, 0, sizeof(pdevinfo)); |
145 | |
146 | pdevinfo.name = chip->name; |
147 | pdevinfo.id = 0; |
148 | pdevinfo.parent = &pci->dev; |
149 | pdevinfo.num_res = num_res; |
150 | pdevinfo.res = &res[0]; |
151 | pdevinfo.data = chip; |
152 | pdevinfo.size_data = sizeof(*chip); |
153 | |
154 | pdev = platform_device_register_full(pdevinfo: &pdevinfo); |
155 | if (IS_ERR(ptr: pdev)) { |
156 | dev_err(&pci->dev, "cannot register %s device\n" , pdevinfo.name); |
157 | ret = PTR_ERR(ptr: pdev); |
158 | goto unregister_dmic_dev; |
159 | } |
160 | |
161 | skip_pdev_creation: |
162 | chip->chip_pdev = pdev; |
163 | dev_set_drvdata(dev: &pci->dev, data: chip); |
164 | pm_runtime_set_autosuspend_delay(dev: &pci->dev, delay: 2000); |
165 | pm_runtime_use_autosuspend(dev: &pci->dev); |
166 | pm_runtime_put_noidle(dev: &pci->dev); |
167 | pm_runtime_allow(dev: &pci->dev); |
168 | return ret; |
169 | |
170 | unregister_dmic_dev: |
171 | platform_device_unregister(dmic_dev); |
172 | release_regions: |
173 | pci_release_regions(pci); |
174 | disable_pci: |
175 | pci_disable_device(dev: pci); |
176 | |
177 | return ret; |
178 | }; |
179 | |
180 | static int __maybe_unused snd_acp_suspend(struct device *dev) |
181 | { |
182 | struct acp_chip_info *chip; |
183 | int ret; |
184 | |
185 | chip = dev_get_drvdata(dev); |
186 | ret = acp_deinit(chip); |
187 | if (ret) |
188 | dev_err(dev, "ACP de-init failed\n" ); |
189 | return ret; |
190 | } |
191 | |
192 | static int __maybe_unused snd_acp_resume(struct device *dev) |
193 | { |
194 | struct acp_chip_info *chip; |
195 | struct acp_dev_data *adata; |
196 | struct device child; |
197 | int ret; |
198 | |
199 | chip = dev_get_drvdata(dev); |
200 | ret = acp_init(chip); |
201 | if (ret) |
202 | dev_err(dev, "ACP init failed\n" ); |
203 | child = chip->chip_pdev->dev; |
204 | adata = dev_get_drvdata(dev: &child); |
205 | if (adata) |
206 | acp_enable_interrupts(adata); |
207 | return ret; |
208 | } |
209 | |
210 | static const struct dev_pm_ops acp_pm_ops = { |
211 | SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL) |
212 | SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume) |
213 | }; |
214 | |
215 | static void acp_pci_remove(struct pci_dev *pci) |
216 | { |
217 | struct acp_chip_info *chip; |
218 | int ret; |
219 | |
220 | chip = pci_get_drvdata(pdev: pci); |
221 | pm_runtime_forbid(dev: &pci->dev); |
222 | pm_runtime_get_noresume(dev: &pci->dev); |
223 | if (dmic_dev) |
224 | platform_device_unregister(dmic_dev); |
225 | if (pdev) |
226 | platform_device_unregister(pdev); |
227 | ret = acp_deinit(chip); |
228 | if (ret) |
229 | dev_err(&pci->dev, "ACP de-init failed\n" ); |
230 | } |
231 | |
232 | /* PCI IDs */ |
233 | static const struct pci_device_id acp_pci_ids[] = { |
234 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)}, |
235 | { 0, } |
236 | }; |
237 | MODULE_DEVICE_TABLE(pci, acp_pci_ids); |
238 | |
239 | /* pci_driver definition */ |
240 | static struct pci_driver snd_amd_acp_pci_driver = { |
241 | .name = KBUILD_MODNAME, |
242 | .id_table = acp_pci_ids, |
243 | .probe = acp_pci_probe, |
244 | .remove = acp_pci_remove, |
245 | .driver = { |
246 | .pm = &acp_pm_ops, |
247 | }, |
248 | }; |
249 | module_pci_driver(snd_amd_acp_pci_driver); |
250 | |
251 | MODULE_LICENSE("Dual BSD/GPL" ); |
252 | MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); |
253 | MODULE_ALIAS(DRV_NAME); |
254 | |