Merge tag 'docs-5.6-fixes' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / sound / soc / amd / raven / pci-acp3x.c
1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // AMD ACP PCI Driver
4 //
5 //Copyright 2016 Advanced Micro Devices, Inc.
6
7 #include <linux/pci.h>
8 #include <linux/module.h>
9 #include <linux/io.h>
10 #include <linux/platform_device.h>
11 #include <linux/interrupt.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/delay.h>
14
15 #include "acp3x.h"
16
17 struct acp3x_dev_data {
18         void __iomem *acp3x_base;
19         bool acp3x_audio_mode;
20         struct resource *res;
21         struct platform_device *pdev[ACP3x_DEVS];
22 };
23
24 static int acp3x_power_on(void __iomem *acp3x_base)
25 {
26         u32 val;
27         int timeout;
28
29         val = rv_readl(acp3x_base + mmACP_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                 rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
37                         acp3x_base + mmACP_PGFSM_CONTROL);
38         timeout = 0;
39         while (++timeout < 500) {
40                 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
41                 if (!val)
42                         return 0;
43                 udelay(1);
44         }
45         return -ETIMEDOUT;
46 }
47
48 static int acp3x_reset(void __iomem *acp3x_base)
49 {
50         u32 val;
51         int timeout;
52
53         rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
54         timeout = 0;
55         while (++timeout < 500) {
56                 val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
57                 if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
58                         break;
59                 cpu_relax();
60         }
61         rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
62         timeout = 0;
63         while (++timeout < 500) {
64                 val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
65                 if (!val)
66                         return 0;
67                 cpu_relax();
68         }
69         return -ETIMEDOUT;
70 }
71
72 static int acp3x_init(void __iomem *acp3x_base)
73 {
74         int ret;
75
76         /* power on */
77         ret = acp3x_power_on(acp3x_base);
78         if (ret) {
79                 pr_err("ACP3x power on failed\n");
80                 return ret;
81         }
82         /* Reset */
83         ret = acp3x_reset(acp3x_base);
84         if (ret) {
85                 pr_err("ACP3x reset failed\n");
86                 return ret;
87         }
88         return 0;
89 }
90
91 static int acp3x_deinit(void __iomem *acp3x_base)
92 {
93         int ret;
94
95         /* Reset */
96         ret = acp3x_reset(acp3x_base);
97         if (ret) {
98                 pr_err("ACP3x reset failed\n");
99                 return ret;
100         }
101         return 0;
102 }
103
104 static int snd_acp3x_probe(struct pci_dev *pci,
105                            const struct pci_device_id *pci_id)
106 {
107         struct acp3x_dev_data *adata;
108         struct platform_device_info pdevinfo[ACP3x_DEVS];
109         unsigned int irqflags;
110         int ret, i;
111         u32 addr, val;
112
113         if (pci_enable_device(pci)) {
114                 dev_err(&pci->dev, "pci_enable_device failed\n");
115                 return -ENODEV;
116         }
117
118         ret = pci_request_regions(pci, "AMD ACP3x audio");
119         if (ret < 0) {
120                 dev_err(&pci->dev, "pci_request_regions failed\n");
121                 goto disable_pci;
122         }
123
124         adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
125                              GFP_KERNEL);
126         if (!adata) {
127                 ret = -ENOMEM;
128                 goto release_regions;
129         }
130
131         /* check for msi interrupt support */
132         ret = pci_enable_msi(pci);
133         if (ret)
134                 /* msi is not enabled */
135                 irqflags = IRQF_SHARED;
136         else
137                 /* msi is enabled */
138                 irqflags = 0;
139
140         addr = pci_resource_start(pci, 0);
141         adata->acp3x_base = devm_ioremap(&pci->dev, addr,
142                                         pci_resource_len(pci, 0));
143         if (!adata->acp3x_base) {
144                 ret = -ENOMEM;
145                 goto disable_msi;
146         }
147         pci_set_master(pci);
148         pci_set_drvdata(pci, adata);
149         ret = acp3x_init(adata->acp3x_base);
150         if (ret)
151                 goto disable_msi;
152
153         val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
154         switch (val) {
155         case I2S_MODE:
156                 adata->res = devm_kzalloc(&pci->dev,
157                                           sizeof(struct resource) * 4,
158                                           GFP_KERNEL);
159                 if (!adata->res) {
160                         ret = -ENOMEM;
161                         goto de_init;
162                 }
163
164                 adata->res[0].name = "acp3x_i2s_iomem";
165                 adata->res[0].flags = IORESOURCE_MEM;
166                 adata->res[0].start = addr;
167                 adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
168
169                 adata->res[1].name = "acp3x_i2s_sp";
170                 adata->res[1].flags = IORESOURCE_MEM;
171                 adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
172                 adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
173
174                 adata->res[2].name = "acp3x_i2s_bt";
175                 adata->res[2].flags = IORESOURCE_MEM;
176                 adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
177                 adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
178
179                 adata->res[3].name = "acp3x_i2s_irq";
180                 adata->res[3].flags = IORESOURCE_IRQ;
181                 adata->res[3].start = pci->irq;
182                 adata->res[3].end = adata->res[3].start;
183
184                 adata->acp3x_audio_mode = ACP3x_I2S_MODE;
185
186                 memset(&pdevinfo, 0, sizeof(pdevinfo));
187                 pdevinfo[0].name = "acp3x_rv_i2s_dma";
188                 pdevinfo[0].id = 0;
189                 pdevinfo[0].parent = &pci->dev;
190                 pdevinfo[0].num_res = 4;
191                 pdevinfo[0].res = &adata->res[0];
192                 pdevinfo[0].data = &irqflags;
193                 pdevinfo[0].size_data = sizeof(irqflags);
194
195                 pdevinfo[1].name = "acp3x_i2s_playcap";
196                 pdevinfo[1].id = 0;
197                 pdevinfo[1].parent = &pci->dev;
198                 pdevinfo[1].num_res = 1;
199                 pdevinfo[1].res = &adata->res[1];
200
201                 pdevinfo[2].name = "acp3x_i2s_playcap";
202                 pdevinfo[2].id = 1;
203                 pdevinfo[2].parent = &pci->dev;
204                 pdevinfo[2].num_res = 1;
205                 pdevinfo[2].res = &adata->res[1];
206
207                 pdevinfo[3].name = "acp3x_i2s_playcap";
208                 pdevinfo[3].id = 2;
209                 pdevinfo[3].parent = &pci->dev;
210                 pdevinfo[3].num_res = 1;
211                 pdevinfo[3].res = &adata->res[2];
212                 for (i = 0; i < ACP3x_DEVS; i++) {
213                         adata->pdev[i] =
214                                 platform_device_register_full(&pdevinfo[i]);
215                         if (IS_ERR(adata->pdev[i])) {
216                                 dev_err(&pci->dev, "cannot register %s device\n",
217                                         pdevinfo[i].name);
218                                 ret = PTR_ERR(adata->pdev[i]);
219                                 goto unregister_devs;
220                         }
221                 }
222                 break;
223         default:
224                 dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
225                 ret = -ENODEV;
226                 goto disable_msi;
227         }
228         pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
229         pm_runtime_use_autosuspend(&pci->dev);
230         pm_runtime_set_active(&pci->dev);
231         pm_runtime_put_noidle(&pci->dev);
232         pm_runtime_enable(&pci->dev);
233         pm_runtime_allow(&pci->dev);
234         return 0;
235
236 unregister_devs:
237         if (val == I2S_MODE)
238                 for (i = 0; i < ACP3x_DEVS; i++)
239                         platform_device_unregister(adata->pdev[i]);
240 de_init:
241         if (acp3x_deinit(adata->acp3x_base))
242                 dev_err(&pci->dev, "ACP de-init failed\n");
243 disable_msi:
244         pci_disable_msi(pci);
245 release_regions:
246         pci_release_regions(pci);
247 disable_pci:
248         pci_disable_device(pci);
249
250         return ret;
251 }
252
253 static int snd_acp3x_suspend(struct device *dev)
254 {
255         int ret;
256         struct acp3x_dev_data *adata;
257
258         adata = dev_get_drvdata(dev);
259         ret = acp3x_deinit(adata->acp3x_base);
260         if (ret)
261                 dev_err(dev, "ACP de-init failed\n");
262         else
263                 dev_dbg(dev, "ACP de-initialized\n");
264
265         return 0;
266 }
267
268 static int snd_acp3x_resume(struct device *dev)
269 {
270         int ret;
271         struct acp3x_dev_data *adata;
272
273         adata = dev_get_drvdata(dev);
274         ret = acp3x_init(adata->acp3x_base);
275         if (ret) {
276                 dev_err(dev, "ACP init failed\n");
277                 return ret;
278         }
279         return 0;
280 }
281
282 static const struct dev_pm_ops acp3x_pm = {
283         .runtime_suspend = snd_acp3x_suspend,
284         .runtime_resume =  snd_acp3x_resume,
285         .resume =       snd_acp3x_resume,
286 };
287
288 static void snd_acp3x_remove(struct pci_dev *pci)
289 {
290         struct acp3x_dev_data *adata;
291         int i, ret;
292
293         adata = pci_get_drvdata(pci);
294         if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
295                 for (i = 0; i < ACP3x_DEVS; i++)
296                         platform_device_unregister(adata->pdev[i]);
297         }
298         ret = acp3x_deinit(adata->acp3x_base);
299         if (ret)
300                 dev_err(&pci->dev, "ACP de-init failed\n");
301         pm_runtime_disable(&pci->dev);
302         pm_runtime_get_noresume(&pci->dev);
303         pci_disable_msi(pci);
304         pci_release_regions(pci);
305         pci_disable_device(pci);
306 }
307
308 static const struct pci_device_id snd_acp3x_ids[] = {
309         { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
310         .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
311         .class_mask = 0xffffff },
312         { 0, },
313 };
314 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
315
316 static struct pci_driver acp3x_driver  = {
317         .name = KBUILD_MODNAME,
318         .id_table = snd_acp3x_ids,
319         .probe = snd_acp3x_probe,
320         .remove = snd_acp3x_remove,
321         .driver = {
322                 .pm = &acp3x_pm,
323         }
324 };
325
326 module_pci_driver(acp3x_driver);
327
328 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
329 MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
330 MODULE_DESCRIPTION("AMD ACP3x PCI driver");
331 MODULE_LICENSE("GPL v2");