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