1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // AMD Vangogh ACP PCI Driver
4 //
5 // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
6
7 #include <linux/pci.h>
8 #include <linux/module.h>
9 #include <linux/io.h>
10 #include <linux/delay.h>
11 #include <linux/platform_device.h>
12 #include <linux/interrupt.h>
13 #include <linux/pm_runtime.h>
14
15 #include "acp5x.h"
16
17 struct acp5x_dev_data {
18 void __iomem *acp5x_base;
19 bool acp5x_audio_mode;
20 struct resource *res;
21 struct platform_device *pdev[ACP5x_DEVS];
22 };
23
acp5x_power_on(void __iomem * acp5x_base)24 static int acp5x_power_on(void __iomem *acp5x_base)
25 {
26 u32 val;
27 int timeout;
28
29 val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
30
31 if (val == 0)
32 return val;
33
34 if ((val & ACP_PGFSM_STATUS_MASK) !=
35 ACP_POWER_ON_IN_PROGRESS)
36 acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
37 acp5x_base + ACP_PGFSM_CONTROL);
38 timeout = 0;
39 while (++timeout < 500) {
40 val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
41 if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)
42 return 0;
43 udelay(1);
44 }
45 return -ETIMEDOUT;
46 }
47
acp5x_reset(void __iomem * acp5x_base)48 static int acp5x_reset(void __iomem *acp5x_base)
49 {
50 u32 val;
51 int timeout;
52
53 acp_writel(1, acp5x_base + ACP_SOFT_RESET);
54 timeout = 0;
55 while (++timeout < 500) {
56 val = acp_readl(acp5x_base + ACP_SOFT_RESET);
57 if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
58 break;
59 cpu_relax();
60 }
61 acp_writel(0, acp5x_base + ACP_SOFT_RESET);
62 timeout = 0;
63 while (++timeout < 500) {
64 val = acp_readl(acp5x_base + ACP_SOFT_RESET);
65 if (!val)
66 return 0;
67 cpu_relax();
68 }
69 return -ETIMEDOUT;
70 }
71
acp5x_enable_interrupts(void __iomem * acp5x_base)72 static void acp5x_enable_interrupts(void __iomem *acp5x_base)
73 {
74 acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
75 }
76
acp5x_disable_interrupts(void __iomem * acp5x_base)77 static void acp5x_disable_interrupts(void __iomem *acp5x_base)
78 {
79 acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +
80 ACP_EXTERNAL_INTR_STAT);
81 acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);
82 acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);
83 }
84
acp5x_init(void __iomem * acp5x_base)85 static int acp5x_init(void __iomem *acp5x_base)
86 {
87 int ret;
88
89 /* power on */
90 ret = acp5x_power_on(acp5x_base);
91 if (ret) {
92 pr_err("ACP5x power on failed\n");
93 return ret;
94 }
95 acp_writel(0x01, acp5x_base + ACP_CONTROL);
96 /* Reset */
97 ret = acp5x_reset(acp5x_base);
98 if (ret) {
99 pr_err("ACP5x reset failed\n");
100 return ret;
101 }
102 acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
103 acp5x_enable_interrupts(acp5x_base);
104 return 0;
105 }
106
acp5x_deinit(void __iomem * acp5x_base)107 static int acp5x_deinit(void __iomem *acp5x_base)
108 {
109 int ret;
110
111 acp5x_disable_interrupts(acp5x_base);
112 /* Reset */
113 ret = acp5x_reset(acp5x_base);
114 if (ret) {
115 pr_err("ACP5x reset failed\n");
116 return ret;
117 }
118 acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
119 acp_writel(0x00, acp5x_base + ACP_CONTROL);
120 return 0;
121 }
122
snd_acp5x_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)123 static int snd_acp5x_probe(struct pci_dev *pci,
124 const struct pci_device_id *pci_id)
125 {
126 struct acp5x_dev_data *adata;
127 struct platform_device_info pdevinfo[ACP5x_DEVS];
128 unsigned int irqflags, flag;
129 int ret, i;
130 u32 addr, val;
131
132 /* Return if acp config flag is defined */
133 flag = snd_amd_acp_find_config(pci);
134 if (flag)
135 return -ENODEV;
136
137 irqflags = IRQF_SHARED;
138 if (pci->revision != 0x50)
139 return -ENODEV;
140
141 if (pci_enable_device(pci)) {
142 dev_err(&pci->dev, "pci_enable_device failed\n");
143 return -ENODEV;
144 }
145
146 ret = pci_request_regions(pci, "AMD ACP5x audio");
147 if (ret < 0) {
148 dev_err(&pci->dev, "pci_request_regions failed\n");
149 goto disable_pci;
150 }
151
152 adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
153 GFP_KERNEL);
154 if (!adata) {
155 ret = -ENOMEM;
156 goto release_regions;
157 }
158 addr = pci_resource_start(pci, 0);
159 adata->acp5x_base = devm_ioremap(&pci->dev, addr,
160 pci_resource_len(pci, 0));
161 if (!adata->acp5x_base) {
162 ret = -ENOMEM;
163 goto release_regions;
164 }
165 pci_set_master(pci);
166 pci_set_drvdata(pci, adata);
167 ret = acp5x_init(adata->acp5x_base);
168 if (ret)
169 goto release_regions;
170
171 val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
172 switch (val) {
173 case I2S_MODE:
174 adata->res = devm_kzalloc(&pci->dev,
175 sizeof(struct resource) * ACP5x_RES,
176 GFP_KERNEL);
177 if (!adata->res) {
178 ret = -ENOMEM;
179 goto de_init;
180 }
181
182 adata->res[0].name = "acp5x_i2s_iomem";
183 adata->res[0].flags = IORESOURCE_MEM;
184 adata->res[0].start = addr;
185 adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);
186
187 adata->res[1].name = "acp5x_i2s_sp";
188 adata->res[1].flags = IORESOURCE_MEM;
189 adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
190 adata->res[1].end = addr + ACP5x_I2STDM_REG_END;
191
192 adata->res[2].name = "acp5x_i2s_hs";
193 adata->res[2].flags = IORESOURCE_MEM;
194 adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
195 adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;
196
197 adata->res[3].name = "acp5x_i2s_irq";
198 adata->res[3].flags = IORESOURCE_IRQ;
199 adata->res[3].start = pci->irq;
200 adata->res[3].end = adata->res[3].start;
201
202 adata->acp5x_audio_mode = ACP5x_I2S_MODE;
203
204 memset(&pdevinfo, 0, sizeof(pdevinfo));
205 pdevinfo[0].name = "acp5x_i2s_dma";
206 pdevinfo[0].id = 0;
207 pdevinfo[0].parent = &pci->dev;
208 pdevinfo[0].num_res = 4;
209 pdevinfo[0].res = &adata->res[0];
210 pdevinfo[0].data = &irqflags;
211 pdevinfo[0].size_data = sizeof(irqflags);
212
213 pdevinfo[1].name = "acp5x_i2s_playcap";
214 pdevinfo[1].id = 0;
215 pdevinfo[1].parent = &pci->dev;
216 pdevinfo[1].num_res = 1;
217 pdevinfo[1].res = &adata->res[1];
218
219 pdevinfo[2].name = "acp5x_i2s_playcap";
220 pdevinfo[2].id = 1;
221 pdevinfo[2].parent = &pci->dev;
222 pdevinfo[2].num_res = 1;
223 pdevinfo[2].res = &adata->res[2];
224
225 pdevinfo[3].name = "acp5x_mach";
226 pdevinfo[3].id = 0;
227 pdevinfo[3].parent = &pci->dev;
228 for (i = 0; i < ACP5x_DEVS; i++) {
229 adata->pdev[i] =
230 platform_device_register_full(&pdevinfo[i]);
231 if (IS_ERR(adata->pdev[i])) {
232 dev_err(&pci->dev, "cannot register %s device\n",
233 pdevinfo[i].name);
234 ret = PTR_ERR(adata->pdev[i]);
235 goto unregister_devs;
236 }
237 }
238 break;
239 default:
240 dev_info(&pci->dev, "ACP audio mode : %d\n", val);
241 }
242 pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
243 pm_runtime_use_autosuspend(&pci->dev);
244 pm_runtime_put_noidle(&pci->dev);
245 pm_runtime_allow(&pci->dev);
246 return 0;
247
248 unregister_devs:
249 for (--i; i >= 0; i--)
250 platform_device_unregister(adata->pdev[i]);
251 de_init:
252 if (acp5x_deinit(adata->acp5x_base))
253 dev_err(&pci->dev, "ACP de-init failed\n");
254 release_regions:
255 pci_release_regions(pci);
256 disable_pci:
257 pci_disable_device(pci);
258
259 return ret;
260 }
261
snd_acp5x_suspend(struct device * dev)262 static int __maybe_unused snd_acp5x_suspend(struct device *dev)
263 {
264 int ret;
265 struct acp5x_dev_data *adata;
266
267 adata = dev_get_drvdata(dev);
268 ret = acp5x_deinit(adata->acp5x_base);
269 if (ret)
270 dev_err(dev, "ACP de-init failed\n");
271 else
272 dev_dbg(dev, "ACP de-initialized\n");
273
274 return ret;
275 }
276
snd_acp5x_resume(struct device * dev)277 static int __maybe_unused snd_acp5x_resume(struct device *dev)
278 {
279 int ret;
280 struct acp5x_dev_data *adata;
281
282 adata = dev_get_drvdata(dev);
283 ret = acp5x_init(adata->acp5x_base);
284 if (ret) {
285 dev_err(dev, "ACP init failed\n");
286 return ret;
287 }
288 return 0;
289 }
290
291 static const struct dev_pm_ops acp5x_pm = {
292 SET_RUNTIME_PM_OPS(snd_acp5x_suspend,
293 snd_acp5x_resume, NULL)
294 SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
295 };
296
snd_acp5x_remove(struct pci_dev * pci)297 static void snd_acp5x_remove(struct pci_dev *pci)
298 {
299 struct acp5x_dev_data *adata;
300 int i, ret;
301
302 adata = pci_get_drvdata(pci);
303 if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
304 for (i = 0; i < ACP5x_DEVS; i++)
305 platform_device_unregister(adata->pdev[i]);
306 }
307 ret = acp5x_deinit(adata->acp5x_base);
308 if (ret)
309 dev_err(&pci->dev, "ACP de-init failed\n");
310 pm_runtime_forbid(&pci->dev);
311 pm_runtime_get_noresume(&pci->dev);
312 pci_release_regions(pci);
313 pci_disable_device(pci);
314 }
315
316 static const struct pci_device_id snd_acp5x_ids[] = {
317 { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
318 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
319 .class_mask = 0xffffff },
320 { 0, },
321 };
322 MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);
323
324 static struct pci_driver acp5x_driver = {
325 .name = KBUILD_MODNAME,
326 .id_table = snd_acp5x_ids,
327 .probe = snd_acp5x_probe,
328 .remove = snd_acp5x_remove,
329 .driver = {
330 .pm = &acp5x_pm,
331 }
332 };
333
334 module_pci_driver(acp5x_driver);
335
336 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
337 MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
338 MODULE_LICENSE("GPL v2");
339