1 // SPDX-License-Identifier: GPL-2.0+
5 //Copyright 2016 Advanced Micro Devices, Inc.
8 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/interrupt.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/delay.h>
17 struct acp3x_dev_data {
18 void __iomem *acp3x_base;
19 bool acp3x_audio_mode;
21 struct platform_device *pdev[ACP3x_DEVS];
24 static int acp3x_power_on(void __iomem *acp3x_base)
29 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
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);
39 while (++timeout < 500) {
40 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
42 /* Set PME_EN as after ACP power On,
45 rv_writel(0x1, acp3x_base + mmACP_PME_EN);
53 static int acp3x_reset(void __iomem *acp3x_base)
58 rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
60 while (++timeout < 500) {
61 val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
62 if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
66 rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
68 while (++timeout < 500) {
69 val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
77 static int acp3x_init(void __iomem *acp3x_base)
82 ret = acp3x_power_on(acp3x_base);
84 pr_err("ACP3x power on failed\n");
88 ret = acp3x_reset(acp3x_base);
90 pr_err("ACP3x reset failed\n");
96 static int acp3x_deinit(void __iomem *acp3x_base)
101 ret = acp3x_reset(acp3x_base);
103 pr_err("ACP3x reset failed\n");
109 static int snd_acp3x_probe(struct pci_dev *pci,
110 const struct pci_device_id *pci_id)
112 struct acp3x_dev_data *adata;
113 struct platform_device_info pdevinfo[ACP3x_DEVS];
114 unsigned int irqflags;
118 if (pci_enable_device(pci)) {
119 dev_err(&pci->dev, "pci_enable_device failed\n");
123 ret = pci_request_regions(pci, "AMD ACP3x audio");
125 dev_err(&pci->dev, "pci_request_regions failed\n");
129 adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
133 goto release_regions;
136 /* check for msi interrupt support */
137 ret = pci_enable_msi(pci);
139 /* msi is not enabled */
140 irqflags = IRQF_SHARED;
145 addr = pci_resource_start(pci, 0);
146 adata->acp3x_base = devm_ioremap(&pci->dev, addr,
147 pci_resource_len(pci, 0));
148 if (!adata->acp3x_base) {
153 pci_set_drvdata(pci, adata);
154 ret = acp3x_init(adata->acp3x_base);
158 val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
161 adata->res = devm_kzalloc(&pci->dev,
162 sizeof(struct resource) * 4,
169 adata->res[0].name = "acp3x_i2s_iomem";
170 adata->res[0].flags = IORESOURCE_MEM;
171 adata->res[0].start = addr;
172 adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
174 adata->res[1].name = "acp3x_i2s_sp";
175 adata->res[1].flags = IORESOURCE_MEM;
176 adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
177 adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
179 adata->res[2].name = "acp3x_i2s_bt";
180 adata->res[2].flags = IORESOURCE_MEM;
181 adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
182 adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
184 adata->res[3].name = "acp3x_i2s_irq";
185 adata->res[3].flags = IORESOURCE_IRQ;
186 adata->res[3].start = pci->irq;
187 adata->res[3].end = adata->res[3].start;
189 adata->acp3x_audio_mode = ACP3x_I2S_MODE;
191 memset(&pdevinfo, 0, sizeof(pdevinfo));
192 pdevinfo[0].name = "acp3x_rv_i2s_dma";
194 pdevinfo[0].parent = &pci->dev;
195 pdevinfo[0].num_res = 4;
196 pdevinfo[0].res = &adata->res[0];
197 pdevinfo[0].data = &irqflags;
198 pdevinfo[0].size_data = sizeof(irqflags);
200 pdevinfo[1].name = "acp3x_i2s_playcap";
202 pdevinfo[1].parent = &pci->dev;
203 pdevinfo[1].num_res = 1;
204 pdevinfo[1].res = &adata->res[1];
206 pdevinfo[2].name = "acp3x_i2s_playcap";
208 pdevinfo[2].parent = &pci->dev;
209 pdevinfo[2].num_res = 1;
210 pdevinfo[2].res = &adata->res[1];
212 pdevinfo[3].name = "acp3x_i2s_playcap";
214 pdevinfo[3].parent = &pci->dev;
215 pdevinfo[3].num_res = 1;
216 pdevinfo[3].res = &adata->res[2];
217 for (i = 0; i < ACP3x_DEVS; i++) {
219 platform_device_register_full(&pdevinfo[i]);
220 if (IS_ERR(adata->pdev[i])) {
221 dev_err(&pci->dev, "cannot register %s device\n",
223 ret = PTR_ERR(adata->pdev[i]);
224 goto unregister_devs;
229 dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
233 pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
234 pm_runtime_use_autosuspend(&pci->dev);
235 pm_runtime_put_noidle(&pci->dev);
236 pm_runtime_allow(&pci->dev);
241 for (i = 0; i < ACP3x_DEVS; i++)
242 platform_device_unregister(adata->pdev[i]);
244 if (acp3x_deinit(adata->acp3x_base))
245 dev_err(&pci->dev, "ACP de-init failed\n");
247 pci_disable_msi(pci);
249 pci_release_regions(pci);
251 pci_disable_device(pci);
256 static int snd_acp3x_suspend(struct device *dev)
259 struct acp3x_dev_data *adata;
261 adata = dev_get_drvdata(dev);
262 ret = acp3x_deinit(adata->acp3x_base);
264 dev_err(dev, "ACP de-init failed\n");
266 dev_dbg(dev, "ACP de-initialized\n");
271 static int snd_acp3x_resume(struct device *dev)
274 struct acp3x_dev_data *adata;
276 adata = dev_get_drvdata(dev);
277 ret = acp3x_init(adata->acp3x_base);
279 dev_err(dev, "ACP init failed\n");
285 static const struct dev_pm_ops acp3x_pm = {
286 .runtime_suspend = snd_acp3x_suspend,
287 .runtime_resume = snd_acp3x_resume,
288 .resume = snd_acp3x_resume,
291 static void snd_acp3x_remove(struct pci_dev *pci)
293 struct acp3x_dev_data *adata;
296 adata = pci_get_drvdata(pci);
297 if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
298 for (i = 0; i < ACP3x_DEVS; i++)
299 platform_device_unregister(adata->pdev[i]);
301 ret = acp3x_deinit(adata->acp3x_base);
303 dev_err(&pci->dev, "ACP de-init failed\n");
304 pm_runtime_forbid(&pci->dev);
305 pm_runtime_get_noresume(&pci->dev);
306 pci_disable_msi(pci);
307 pci_release_regions(pci);
308 pci_disable_device(pci);
311 static const struct pci_device_id snd_acp3x_ids[] = {
312 { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
313 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
314 .class_mask = 0xffffff },
317 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
319 static struct pci_driver acp3x_driver = {
320 .name = KBUILD_MODNAME,
321 .id_table = snd_acp3x_ids,
322 .probe = snd_acp3x_probe,
323 .remove = snd_acp3x_remove,
329 module_pci_driver(acp3x_driver);
331 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
332 MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
333 MODULE_DESCRIPTION("AMD ACP3x PCI driver");
334 MODULE_LICENSE("GPL v2");