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
17struct 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	u32 pme_en;
23};
24
25static int acp3x_power_on(struct acp3x_dev_data *adata)
26{
27	void __iomem *acp3x_base = adata->acp3x_base;
28	u32 val;
29	int timeout;
30
31	val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
32
33	if (val == 0)
34		return val;
35
36	if (!((val & ACP_PGFSM_STATUS_MASK) ==
37				ACP_POWER_ON_IN_PROGRESS))
38		rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
39			acp3x_base + mmACP_PGFSM_CONTROL);
40	timeout = 0;
41	while (++timeout < 500) {
42		val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
43		if (!val) {
44			/* ACP power On clears PME_EN.
45			 * Restore the value to its prior state
46			 */
47			rv_writel(adata->pme_en, acp3x_base + mmACP_PME_EN);
48			return 0;
49		}
50		udelay(1);
51	}
52	return -ETIMEDOUT;
53}
54
55static int acp3x_reset(void __iomem *acp3x_base)
56{
57	u32 val;
58	int timeout;
59
60	rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
61	timeout = 0;
62	while (++timeout < 500) {
63		val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
64		if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
65			break;
66		cpu_relax();
67	}
68	rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
69	timeout = 0;
70	while (++timeout < 500) {
71		val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
72		if (!val)
73			return 0;
74		cpu_relax();
75	}
76	return -ETIMEDOUT;
77}
78
79static void acp3x_enable_interrupts(void __iomem *acp_base)
80{
81	rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
82}
83
84static void acp3x_disable_interrupts(void __iomem *acp_base)
85{
86	rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
87		  mmACP_EXTERNAL_INTR_STAT);
88	rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);
89	rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);
90}
91
92static int acp3x_init(struct acp3x_dev_data *adata)
93{
94	void __iomem *acp3x_base = adata->acp3x_base;
95	int ret;
96
97	/* power on */
98	ret = acp3x_power_on(adata);
99	if (ret) {
100		pr_err("ACP3x power on failed\n");
101		return ret;
102	}
103	/* Reset */
104	ret = acp3x_reset(acp3x_base);
105	if (ret) {
106		pr_err("ACP3x reset failed\n");
107		return ret;
108	}
109	acp3x_enable_interrupts(acp3x_base);
110	return 0;
111}
112
113static int acp3x_deinit(void __iomem *acp3x_base)
114{
115	int ret;
116
117	acp3x_disable_interrupts(acp3x_base);
118	/* Reset */
119	ret = acp3x_reset(acp3x_base);
120	if (ret) {
121		pr_err("ACP3x reset failed\n");
122		return ret;
123	}
124	return 0;
125}
126
127static 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	/* Raven device detection */
137	if (pci->revision != 0x00)
138		return -ENODEV;
139
140	if (pci_enable_device(pci)) {
141		dev_err(&pci->dev, "pci_enable_device failed\n");
142		return -ENODEV;
143	}
144
145	ret = pci_request_regions(pci, "AMD ACP3x audio");
146	if (ret < 0) {
147		dev_err(&pci->dev, "pci_request_regions failed\n");
148		goto disable_pci;
149	}
150
151	adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
152			     GFP_KERNEL);
153	if (!adata) {
154		ret = -ENOMEM;
155		goto release_regions;
156	}
157
158	/* check for msi interrupt support */
159	ret = pci_enable_msi(pci);
160	if (ret)
161		/* msi is not enabled */
162		irqflags = IRQF_SHARED;
163	else
164		/* msi is enabled */
165		irqflags = 0;
166
167	addr = pci_resource_start(pci, 0);
168	adata->acp3x_base = devm_ioremap(&pci->dev, addr,
169					pci_resource_len(pci, 0));
170	if (!adata->acp3x_base) {
171		ret = -ENOMEM;
172		goto disable_msi;
173	}
174	pci_set_master(pci);
175	pci_set_drvdata(pci, adata);
176	/* Save ACP_PME_EN state */
177	adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
178	ret = acp3x_init(adata);
179	if (ret)
180		goto disable_msi;
181
182	val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
183	switch (val) {
184	case I2S_MODE:
185		adata->res = devm_kzalloc(&pci->dev,
186					  sizeof(struct resource) * 4,
187					  GFP_KERNEL);
188		if (!adata->res) {
189			ret = -ENOMEM;
190			goto de_init;
191		}
192
193		adata->res[0].name = "acp3x_i2s_iomem";
194		adata->res[0].flags = IORESOURCE_MEM;
195		adata->res[0].start = addr;
196		adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
197
198		adata->res[1].name = "acp3x_i2s_sp";
199		adata->res[1].flags = IORESOURCE_MEM;
200		adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
201		adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
202
203		adata->res[2].name = "acp3x_i2s_bt";
204		adata->res[2].flags = IORESOURCE_MEM;
205		adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
206		adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
207
208		adata->res[3].name = "acp3x_i2s_irq";
209		adata->res[3].flags = IORESOURCE_IRQ;
210		adata->res[3].start = pci->irq;
211		adata->res[3].end = adata->res[3].start;
212
213		adata->acp3x_audio_mode = ACP3x_I2S_MODE;
214
215		memset(&pdevinfo, 0, sizeof(pdevinfo));
216		pdevinfo[0].name = "acp3x_rv_i2s_dma";
217		pdevinfo[0].id = 0;
218		pdevinfo[0].parent = &pci->dev;
219		pdevinfo[0].num_res = 4;
220		pdevinfo[0].res = &adata->res[0];
221		pdevinfo[0].data = &irqflags;
222		pdevinfo[0].size_data = sizeof(irqflags);
223
224		pdevinfo[1].name = "acp3x_i2s_playcap";
225		pdevinfo[1].id = 0;
226		pdevinfo[1].parent = &pci->dev;
227		pdevinfo[1].num_res = 1;
228		pdevinfo[1].res = &adata->res[1];
229
230		pdevinfo[2].name = "acp3x_i2s_playcap";
231		pdevinfo[2].id = 1;
232		pdevinfo[2].parent = &pci->dev;
233		pdevinfo[2].num_res = 1;
234		pdevinfo[2].res = &adata->res[1];
235
236		pdevinfo[3].name = "acp3x_i2s_playcap";
237		pdevinfo[3].id = 2;
238		pdevinfo[3].parent = &pci->dev;
239		pdevinfo[3].num_res = 1;
240		pdevinfo[3].res = &adata->res[2];
241		for (i = 0; i < ACP3x_DEVS; i++) {
242			adata->pdev[i] =
243				platform_device_register_full(&pdevinfo[i]);
244			if (IS_ERR(adata->pdev[i])) {
245				dev_err(&pci->dev, "cannot register %s device\n",
246					pdevinfo[i].name);
247				ret = PTR_ERR(adata->pdev[i]);
248				goto unregister_devs;
249			}
250		}
251		break;
252	default:
253		dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
254		ret = -ENODEV;
255		goto disable_msi;
256	}
257	pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
258	pm_runtime_use_autosuspend(&pci->dev);
259	pm_runtime_put_noidle(&pci->dev);
260	pm_runtime_allow(&pci->dev);
261	return 0;
262
263unregister_devs:
264	if (val == I2S_MODE)
265		for (i = 0; i < ACP3x_DEVS; i++)
266			platform_device_unregister(adata->pdev[i]);
267de_init:
268	if (acp3x_deinit(adata->acp3x_base))
269		dev_err(&pci->dev, "ACP de-init failed\n");
270disable_msi:
271	pci_disable_msi(pci);
272release_regions:
273	pci_release_regions(pci);
274disable_pci:
275	pci_disable_device(pci);
276
277	return ret;
278}
279
280static int snd_acp3x_suspend(struct device *dev)
281{
282	int ret;
283	struct acp3x_dev_data *adata;
284
285	adata = dev_get_drvdata(dev);
286	ret = acp3x_deinit(adata->acp3x_base);
287	if (ret)
288		dev_err(dev, "ACP de-init failed\n");
289	else
290		dev_dbg(dev, "ACP de-initialized\n");
291
292	return 0;
293}
294
295static int snd_acp3x_resume(struct device *dev)
296{
297	int ret;
298	struct acp3x_dev_data *adata;
299
300	adata = dev_get_drvdata(dev);
301	ret = acp3x_init(adata);
302	if (ret) {
303		dev_err(dev, "ACP init failed\n");
304		return ret;
305	}
306	return 0;
307}
308
309static const struct dev_pm_ops acp3x_pm = {
310	.runtime_suspend = snd_acp3x_suspend,
311	.runtime_resume =  snd_acp3x_resume,
312	.resume =	snd_acp3x_resume,
313};
314
315static void snd_acp3x_remove(struct pci_dev *pci)
316{
317	struct acp3x_dev_data *adata;
318	int i, ret;
319
320	adata = pci_get_drvdata(pci);
321	if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
322		for (i = 0; i < ACP3x_DEVS; i++)
323			platform_device_unregister(adata->pdev[i]);
324	}
325	ret = acp3x_deinit(adata->acp3x_base);
326	if (ret)
327		dev_err(&pci->dev, "ACP de-init failed\n");
328	pm_runtime_forbid(&pci->dev);
329	pm_runtime_get_noresume(&pci->dev);
330	pci_disable_msi(pci);
331	pci_release_regions(pci);
332	pci_disable_device(pci);
333}
334
335static const struct pci_device_id snd_acp3x_ids[] = {
336	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
337	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
338	.class_mask = 0xffffff },
339	{ 0, },
340};
341MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
342
343static struct pci_driver acp3x_driver  = {
344	.name = KBUILD_MODNAME,
345	.id_table = snd_acp3x_ids,
346	.probe = snd_acp3x_probe,
347	.remove = snd_acp3x_remove,
348	.driver = {
349		.pm = &acp3x_pm,
350	}
351};
352
353module_pci_driver(acp3x_driver);
354
355MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
356MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
357MODULE_DESCRIPTION("AMD ACP3x PCI driver");
358MODULE_LICENSE("GPL v2");
359