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