xref: /openbmc/linux/sound/soc/amd/raven/pci-acp3x.c (revision dc6a81c3)
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_power_off(void __iomem *acp3x_base)
49 {
50 	u32 val;
51 	int timeout;
52 
53 	rv_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
54 			acp3x_base + mmACP_PGFSM_CONTROL);
55 	timeout = 0;
56 	while (++timeout < 500) {
57 		val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
58 		if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
59 			return 0;
60 		udelay(1);
61 	}
62 	return -ETIMEDOUT;
63 }
64 
65 static int acp3x_reset(void __iomem *acp3x_base)
66 {
67 	u32 val;
68 	int timeout;
69 
70 	rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
71 	timeout = 0;
72 	while (++timeout < 500) {
73 		val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
74 		if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
75 			break;
76 		cpu_relax();
77 	}
78 	rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
79 	timeout = 0;
80 	while (++timeout < 500) {
81 		val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
82 		if (!val)
83 			return 0;
84 		cpu_relax();
85 	}
86 	return -ETIMEDOUT;
87 }
88 
89 static int acp3x_init(void __iomem *acp3x_base)
90 {
91 	int ret;
92 
93 	/* power on */
94 	ret = acp3x_power_on(acp3x_base);
95 	if (ret) {
96 		pr_err("ACP3x power on failed\n");
97 		return ret;
98 	}
99 	/* Reset */
100 	ret = acp3x_reset(acp3x_base);
101 	if (ret) {
102 		pr_err("ACP3x reset failed\n");
103 		return ret;
104 	}
105 	return 0;
106 }
107 
108 static int acp3x_deinit(void __iomem *acp3x_base)
109 {
110 	int ret;
111 
112 	/* Reset */
113 	ret = acp3x_reset(acp3x_base);
114 	if (ret) {
115 		pr_err("ACP3x reset failed\n");
116 		return ret;
117 	}
118 	/* power off */
119 	ret = acp3x_power_off(acp3x_base);
120 	if (ret) {
121 		pr_err("ACP3x power off failed\n");
122 		return ret;
123 	}
124 	return 0;
125 }
126 
127 static int snd_acp3x_probe(struct pci_dev *pci,
128 			   const struct pci_device_id *pci_id)
129 {
130 	struct acp3x_dev_data *adata;
131 	struct platform_device_info pdevinfo[ACP3x_DEVS];
132 	unsigned int irqflags;
133 	int ret, i;
134 	u32 addr, val;
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 ACP3x 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 acp3x_dev_data),
148 			     GFP_KERNEL);
149 	if (!adata) {
150 		ret = -ENOMEM;
151 		goto release_regions;
152 	}
153 
154 	/* check for msi interrupt support */
155 	ret = pci_enable_msi(pci);
156 	if (ret)
157 		/* msi is not enabled */
158 		irqflags = IRQF_SHARED;
159 	else
160 		/* msi is enabled */
161 		irqflags = 0;
162 
163 	addr = pci_resource_start(pci, 0);
164 	adata->acp3x_base = devm_ioremap(&pci->dev, addr,
165 					pci_resource_len(pci, 0));
166 	if (!adata->acp3x_base) {
167 		ret = -ENOMEM;
168 		goto disable_msi;
169 	}
170 	pci_set_master(pci);
171 	pci_set_drvdata(pci, adata);
172 	ret = acp3x_init(adata->acp3x_base);
173 	if (ret)
174 		goto disable_msi;
175 
176 	val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
177 	switch (val) {
178 	case I2S_MODE:
179 		adata->res = devm_kzalloc(&pci->dev,
180 					  sizeof(struct resource) * 4,
181 					  GFP_KERNEL);
182 		if (!adata->res) {
183 			ret = -ENOMEM;
184 			goto de_init;
185 		}
186 
187 		adata->res[0].name = "acp3x_i2s_iomem";
188 		adata->res[0].flags = IORESOURCE_MEM;
189 		adata->res[0].start = addr;
190 		adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
191 
192 		adata->res[1].name = "acp3x_i2s_sp";
193 		adata->res[1].flags = IORESOURCE_MEM;
194 		adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
195 		adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
196 
197 		adata->res[2].name = "acp3x_i2s_bt";
198 		adata->res[2].flags = IORESOURCE_MEM;
199 		adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
200 		adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
201 
202 		adata->res[3].name = "acp3x_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->acp3x_audio_mode = ACP3x_I2S_MODE;
208 
209 		memset(&pdevinfo, 0, sizeof(pdevinfo));
210 		pdevinfo[0].name = "acp3x_rv_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 = "acp3x_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 = "acp3x_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[1];
229 
230 		pdevinfo[3].name = "acp3x_i2s_playcap";
231 		pdevinfo[3].id = 2;
232 		pdevinfo[3].parent = &pci->dev;
233 		pdevinfo[3].num_res = 1;
234 		pdevinfo[3].res = &adata->res[2];
235 		for (i = 0; i < ACP3x_DEVS; i++) {
236 			adata->pdev[i] =
237 				platform_device_register_full(&pdevinfo[i]);
238 			if (IS_ERR(adata->pdev[i])) {
239 				dev_err(&pci->dev, "cannot register %s device\n",
240 					pdevinfo[i].name);
241 				ret = PTR_ERR(adata->pdev[i]);
242 				goto unregister_devs;
243 			}
244 		}
245 		break;
246 	default:
247 		dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
248 		ret = -ENODEV;
249 		goto disable_msi;
250 	}
251 	pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
252 	pm_runtime_use_autosuspend(&pci->dev);
253 	pm_runtime_set_active(&pci->dev);
254 	pm_runtime_put_noidle(&pci->dev);
255 	pm_runtime_enable(&pci->dev);
256 	pm_runtime_allow(&pci->dev);
257 	return 0;
258 
259 unregister_devs:
260 	if (val == I2S_MODE)
261 		for (i = 0; i < ACP3x_DEVS; i++)
262 			platform_device_unregister(adata->pdev[i]);
263 de_init:
264 	if (acp3x_deinit(adata->acp3x_base))
265 		dev_err(&pci->dev, "ACP de-init failed\n");
266 disable_msi:
267 	pci_disable_msi(pci);
268 release_regions:
269 	pci_release_regions(pci);
270 disable_pci:
271 	pci_disable_device(pci);
272 
273 	return ret;
274 }
275 
276 static int snd_acp3x_suspend(struct device *dev)
277 {
278 	int ret;
279 	struct acp3x_dev_data *adata;
280 
281 	adata = dev_get_drvdata(dev);
282 	ret = acp3x_deinit(adata->acp3x_base);
283 	if (ret)
284 		dev_err(dev, "ACP de-init failed\n");
285 	else
286 		dev_dbg(dev, "ACP de-initialized\n");
287 
288 	return 0;
289 }
290 
291 static int snd_acp3x_resume(struct device *dev)
292 {
293 	int ret;
294 	struct acp3x_dev_data *adata;
295 
296 	adata = dev_get_drvdata(dev);
297 	ret = acp3x_init(adata->acp3x_base);
298 	if (ret) {
299 		dev_err(dev, "ACP init failed\n");
300 		return ret;
301 	}
302 	return 0;
303 }
304 
305 static const struct dev_pm_ops acp3x_pm = {
306 	.runtime_suspend = snd_acp3x_suspend,
307 	.runtime_resume =  snd_acp3x_resume,
308 	.resume =	snd_acp3x_resume,
309 };
310 
311 static void snd_acp3x_remove(struct pci_dev *pci)
312 {
313 	struct acp3x_dev_data *adata;
314 	int i, ret;
315 
316 	adata = pci_get_drvdata(pci);
317 	if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
318 		for (i = 0; i < ACP3x_DEVS; i++)
319 			platform_device_unregister(adata->pdev[i]);
320 	}
321 	ret = acp3x_deinit(adata->acp3x_base);
322 	if (ret)
323 		dev_err(&pci->dev, "ACP de-init failed\n");
324 	pm_runtime_disable(&pci->dev);
325 	pm_runtime_get_noresume(&pci->dev);
326 	pci_disable_msi(pci);
327 	pci_release_regions(pci);
328 	pci_disable_device(pci);
329 }
330 
331 static const struct pci_device_id snd_acp3x_ids[] = {
332 	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
333 	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
334 	.class_mask = 0xffffff },
335 	{ 0, },
336 };
337 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
338 
339 static struct pci_driver acp3x_driver  = {
340 	.name = KBUILD_MODNAME,
341 	.id_table = snd_acp3x_ids,
342 	.probe = snd_acp3x_probe,
343 	.remove = snd_acp3x_remove,
344 	.driver = {
345 		.pm = &acp3x_pm,
346 	}
347 };
348 
349 module_pci_driver(acp3x_driver);
350 
351 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
352 MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
353 MODULE_DESCRIPTION("AMD ACP3x PCI driver");
354 MODULE_LICENSE("GPL v2");
355