1  // SPDX-License-Identifier: GPL-2.0+
2  /*
3   * AMD RPL ACP PCI Driver
4   *
5   * Copyright 2022 Advanced Micro Devices, Inc.
6   */
7  
8  #include <linux/pci.h>
9  #include <linux/module.h>
10  #include <linux/io.h>
11  #include <linux/delay.h>
12  #include <linux/platform_device.h>
13  #include <linux/pm_runtime.h>
14  
15  #include "rpl_acp6x.h"
16  
17  struct rpl_dev_data {
18  	void __iomem *acp6x_base;
19  };
20  
rpl_power_on(void __iomem * acp_base)21  static int rpl_power_on(void __iomem *acp_base)
22  {
23  	u32 val;
24  	int timeout;
25  
26  	val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
27  
28  	if (!val)
29  		return val;
30  
31  	if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
32  		rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
33  	timeout = 0;
34  	while (++timeout < 500) {
35  		val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
36  		if (!val)
37  			return 0;
38  		udelay(1);
39  	}
40  	return -ETIMEDOUT;
41  }
42  
rpl_reset(void __iomem * acp_base)43  static int rpl_reset(void __iomem *acp_base)
44  {
45  	u32 val;
46  	int timeout;
47  
48  	rpl_acp_writel(1, acp_base + ACP_SOFT_RESET);
49  	timeout = 0;
50  	while (++timeout < 500) {
51  		val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
52  		if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
53  			break;
54  		cpu_relax();
55  	}
56  	rpl_acp_writel(0, acp_base + ACP_SOFT_RESET);
57  	timeout = 0;
58  	while (++timeout < 500) {
59  		val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
60  		if (!val)
61  			return 0;
62  		cpu_relax();
63  	}
64  	return -ETIMEDOUT;
65  }
66  
rpl_init(void __iomem * acp_base)67  static int rpl_init(void __iomem *acp_base)
68  {
69  	int ret;
70  
71  	/* power on */
72  	ret = rpl_power_on(acp_base);
73  	if (ret) {
74  		pr_err("ACP power on failed\n");
75  		return ret;
76  	}
77  	rpl_acp_writel(0x01, acp_base + ACP_CONTROL);
78  	/* Reset */
79  	ret = rpl_reset(acp_base);
80  	if (ret) {
81  		pr_err("ACP reset failed\n");
82  		return ret;
83  	}
84  	rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL);
85  	return 0;
86  }
87  
rpl_deinit(void __iomem * acp_base)88  static int rpl_deinit(void __iomem *acp_base)
89  {
90  	int ret;
91  
92  	/* Reset */
93  	ret = rpl_reset(acp_base);
94  	if (ret) {
95  		pr_err("ACP reset failed\n");
96  		return ret;
97  	}
98  	rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL);
99  	rpl_acp_writel(0x00, acp_base + ACP_CONTROL);
100  	return 0;
101  }
102  
snd_rpl_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)103  static int snd_rpl_probe(struct pci_dev *pci,
104  			 const struct pci_device_id *pci_id)
105  {
106  	struct rpl_dev_data *adata;
107  	u32 addr;
108  	int ret;
109  
110  	/* RPL device check */
111  	switch (pci->revision) {
112  	case 0x62:
113  		break;
114  	default:
115  		dev_dbg(&pci->dev, "acp6x pci device not found\n");
116  		return -ENODEV;
117  	}
118  	if (pci_enable_device(pci)) {
119  		dev_err(&pci->dev, "pci_enable_device failed\n");
120  		return -ENODEV;
121  	}
122  
123  	ret = pci_request_regions(pci, "AMD ACP6x audio");
124  	if (ret < 0) {
125  		dev_err(&pci->dev, "pci_request_regions failed\n");
126  		goto disable_pci;
127  	}
128  
129  	adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data),
130  			     GFP_KERNEL);
131  	if (!adata) {
132  		ret = -ENOMEM;
133  		goto release_regions;
134  	}
135  
136  	addr = pci_resource_start(pci, 0);
137  	adata->acp6x_base = devm_ioremap(&pci->dev, addr,
138  					 pci_resource_len(pci, 0));
139  	if (!adata->acp6x_base) {
140  		ret = -ENOMEM;
141  		goto release_regions;
142  	}
143  	pci_set_master(pci);
144  	pci_set_drvdata(pci, adata);
145  	ret = rpl_init(adata->acp6x_base);
146  	if (ret)
147  		goto release_regions;
148  	pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
149  	pm_runtime_use_autosuspend(&pci->dev);
150  	pm_runtime_put_noidle(&pci->dev);
151  	pm_runtime_allow(&pci->dev);
152  
153  	return 0;
154  release_regions:
155  	pci_release_regions(pci);
156  disable_pci:
157  	pci_disable_device(pci);
158  
159  	return ret;
160  }
161  
snd_rpl_suspend(struct device * dev)162  static int __maybe_unused snd_rpl_suspend(struct device *dev)
163  {
164  	struct rpl_dev_data *adata;
165  	int ret;
166  
167  	adata = dev_get_drvdata(dev);
168  	ret = rpl_deinit(adata->acp6x_base);
169  	if (ret)
170  		dev_err(dev, "ACP de-init failed\n");
171  	return ret;
172  }
173  
snd_rpl_resume(struct device * dev)174  static int __maybe_unused snd_rpl_resume(struct device *dev)
175  {
176  	struct rpl_dev_data *adata;
177  	int ret;
178  
179  	adata = dev_get_drvdata(dev);
180  	ret = rpl_init(adata->acp6x_base);
181  	if (ret)
182  		dev_err(dev, "ACP init failed\n");
183  	return ret;
184  }
185  
186  static const struct dev_pm_ops rpl_pm = {
187  	SET_RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)
188  	SET_SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume)
189  };
190  
snd_rpl_remove(struct pci_dev * pci)191  static void snd_rpl_remove(struct pci_dev *pci)
192  {
193  	struct rpl_dev_data *adata;
194  	int ret;
195  
196  	adata = pci_get_drvdata(pci);
197  	ret = rpl_deinit(adata->acp6x_base);
198  	if (ret)
199  		dev_err(&pci->dev, "ACP de-init failed\n");
200  	pm_runtime_forbid(&pci->dev);
201  	pm_runtime_get_noresume(&pci->dev);
202  	pci_release_regions(pci);
203  	pci_disable_device(pci);
204  }
205  
206  static const struct pci_device_id snd_rpl_ids[] = {
207  	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
208  	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
209  	.class_mask = 0xffffff },
210  	{ 0, },
211  };
212  MODULE_DEVICE_TABLE(pci, snd_rpl_ids);
213  
214  static struct pci_driver rpl_acp6x_driver  = {
215  	.name = KBUILD_MODNAME,
216  	.id_table = snd_rpl_ids,
217  	.probe = snd_rpl_probe,
218  	.remove = snd_rpl_remove,
219  	.driver = {
220  		.pm = &rpl_pm,
221  	}
222  };
223  
224  module_pci_driver(rpl_acp6x_driver);
225  
226  MODULE_DESCRIPTION("AMD ACP RPL PCI driver");
227  MODULE_LICENSE("GPL v2");
228