GNU Linux-libre 5.19-rc6-gnu
[releases.git] / sound / soc / amd / vangogh / pci-acp5x.c
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
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
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
72 static void acp5x_enable_interrupts(void __iomem *acp5x_base)
73 {
74         acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
75 }
76
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
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
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
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;
129         int ret, i;
130         u32 addr, val;
131
132         irqflags = IRQF_SHARED;
133         if (pci->revision != 0x50)
134                 return -ENODEV;
135
136         if (pci_enable_device(pci)) {
137                 dev_err(&pci->dev, "pci_enable_device failed\n");
138                 return -ENODEV;
139         }
140
141         ret = pci_request_regions(pci, "AMD ACP5x audio");
142         if (ret < 0) {
143                 dev_err(&pci->dev, "pci_request_regions failed\n");
144                 goto disable_pci;
145         }
146
147         adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
148                              GFP_KERNEL);
149         if (!adata) {
150                 ret = -ENOMEM;
151                 goto release_regions;
152         }
153         addr = pci_resource_start(pci, 0);
154         adata->acp5x_base = devm_ioremap(&pci->dev, addr,
155                                          pci_resource_len(pci, 0));
156         if (!adata->acp5x_base) {
157                 ret = -ENOMEM;
158                 goto release_regions;
159         }
160         pci_set_master(pci);
161         pci_set_drvdata(pci, adata);
162         ret = acp5x_init(adata->acp5x_base);
163         if (ret)
164                 goto release_regions;
165
166         val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
167         switch (val) {
168         case I2S_MODE:
169                 adata->res = devm_kzalloc(&pci->dev,
170                                           sizeof(struct resource) * ACP5x_RES,
171                                           GFP_KERNEL);
172                 if (!adata->res) {
173                         ret = -ENOMEM;
174                         goto de_init;
175                 }
176
177                 adata->res[0].name = "acp5x_i2s_iomem";
178                 adata->res[0].flags = IORESOURCE_MEM;
179                 adata->res[0].start = addr;
180                 adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);
181
182                 adata->res[1].name = "acp5x_i2s_sp";
183                 adata->res[1].flags = IORESOURCE_MEM;
184                 adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
185                 adata->res[1].end = addr + ACP5x_I2STDM_REG_END;
186
187                 adata->res[2].name = "acp5x_i2s_hs";
188                 adata->res[2].flags = IORESOURCE_MEM;
189                 adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
190                 adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;
191
192                 adata->res[3].name = "acp5x_i2s_irq";
193                 adata->res[3].flags = IORESOURCE_IRQ;
194                 adata->res[3].start = pci->irq;
195                 adata->res[3].end = adata->res[3].start;
196
197                 adata->acp5x_audio_mode = ACP5x_I2S_MODE;
198
199                 memset(&pdevinfo, 0, sizeof(pdevinfo));
200                 pdevinfo[0].name = "acp5x_i2s_dma";
201                 pdevinfo[0].id = 0;
202                 pdevinfo[0].parent = &pci->dev;
203                 pdevinfo[0].num_res = 4;
204                 pdevinfo[0].res = &adata->res[0];
205                 pdevinfo[0].data = &irqflags;
206                 pdevinfo[0].size_data = sizeof(irqflags);
207
208                 pdevinfo[1].name = "acp5x_i2s_playcap";
209                 pdevinfo[1].id = 0;
210                 pdevinfo[1].parent = &pci->dev;
211                 pdevinfo[1].num_res = 1;
212                 pdevinfo[1].res = &adata->res[1];
213
214                 pdevinfo[2].name = "acp5x_i2s_playcap";
215                 pdevinfo[2].id = 1;
216                 pdevinfo[2].parent = &pci->dev;
217                 pdevinfo[2].num_res = 1;
218                 pdevinfo[2].res = &adata->res[2];
219
220                 pdevinfo[3].name = "acp5x_mach";
221                 pdevinfo[3].id = 0;
222                 pdevinfo[3].parent = &pci->dev;
223                 for (i = 0; i < ACP5x_DEVS; i++) {
224                         adata->pdev[i] =
225                                 platform_device_register_full(&pdevinfo[i]);
226                         if (IS_ERR(adata->pdev[i])) {
227                                 dev_err(&pci->dev, "cannot register %s device\n",
228                                         pdevinfo[i].name);
229                                 ret = PTR_ERR(adata->pdev[i]);
230                                 goto unregister_devs;
231                         }
232                 }
233                 break;
234         default:
235                 dev_info(&pci->dev, "ACP audio mode : %d\n", val);
236         }
237         pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
238         pm_runtime_use_autosuspend(&pci->dev);
239         pm_runtime_put_noidle(&pci->dev);
240         pm_runtime_allow(&pci->dev);
241         return 0;
242
243 unregister_devs:
244         for (--i; i >= 0; i--)
245                 platform_device_unregister(adata->pdev[i]);
246 de_init:
247         if (acp5x_deinit(adata->acp5x_base))
248                 dev_err(&pci->dev, "ACP de-init failed\n");
249 release_regions:
250         pci_release_regions(pci);
251 disable_pci:
252         pci_disable_device(pci);
253
254         return ret;
255 }
256
257 static int __maybe_unused snd_acp5x_suspend(struct device *dev)
258 {
259         int ret;
260         struct acp5x_dev_data *adata;
261
262         adata = dev_get_drvdata(dev);
263         ret = acp5x_deinit(adata->acp5x_base);
264         if (ret)
265                 dev_err(dev, "ACP de-init failed\n");
266         else
267                 dev_dbg(dev, "ACP de-initialized\n");
268
269         return ret;
270 }
271
272 static int __maybe_unused snd_acp5x_resume(struct device *dev)
273 {
274         int ret;
275         struct acp5x_dev_data *adata;
276
277         adata = dev_get_drvdata(dev);
278         ret = acp5x_init(adata->acp5x_base);
279         if (ret) {
280                 dev_err(dev, "ACP init failed\n");
281                 return ret;
282         }
283         return 0;
284 }
285
286 static const struct dev_pm_ops acp5x_pm = {
287         SET_RUNTIME_PM_OPS(snd_acp5x_suspend,
288                            snd_acp5x_resume, NULL)
289         SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
290 };
291
292 static void snd_acp5x_remove(struct pci_dev *pci)
293 {
294         struct acp5x_dev_data *adata;
295         int i, ret;
296
297         adata = pci_get_drvdata(pci);
298         if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
299                 for (i = 0; i < ACP5x_DEVS; i++)
300                         platform_device_unregister(adata->pdev[i]);
301         }
302         ret = acp5x_deinit(adata->acp5x_base);
303         if (ret)
304                 dev_err(&pci->dev, "ACP de-init failed\n");
305         pm_runtime_forbid(&pci->dev);
306         pm_runtime_get_noresume(&pci->dev);
307         pci_release_regions(pci);
308         pci_disable_device(pci);
309 }
310
311 static const struct pci_device_id snd_acp5x_ids[] = {
312         { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
313         .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
314         .class_mask = 0xffffff },
315         { 0, },
316 };
317 MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);
318
319 static struct pci_driver acp5x_driver  = {
320         .name = KBUILD_MODNAME,
321         .id_table = snd_acp5x_ids,
322         .probe = snd_acp5x_probe,
323         .remove = snd_acp5x_remove,
324         .driver = {
325                 .pm = &acp5x_pm,
326         }
327 };
328
329 module_pci_driver(acp5x_driver);
330
331 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
332 MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
333 MODULE_LICENSE("GPL v2");