Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/raven/pci-acp3x.c
26481 views
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
u32 pme_en;
23
};
24
25
static 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
55
static 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
79
static void acp3x_enable_interrupts(void __iomem *acp_base)
80
{
81
rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
82
}
83
84
static 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
92
static 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
113
static 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
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
/* 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
irqflags = IRQF_SHARED;
159
160
addr = pci_resource_start(pci, 0);
161
adata->acp3x_base = devm_ioremap(&pci->dev, addr,
162
pci_resource_len(pci, 0));
163
if (!adata->acp3x_base) {
164
ret = -ENOMEM;
165
goto release_regions;
166
}
167
pci_set_master(pci);
168
pci_set_drvdata(pci, adata);
169
/* Save ACP_PME_EN state */
170
adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
171
ret = acp3x_init(adata);
172
if (ret)
173
goto release_regions;
174
175
val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
176
switch (val) {
177
case I2S_MODE:
178
adata->res = devm_kzalloc(&pci->dev,
179
sizeof(struct resource) * 4,
180
GFP_KERNEL);
181
if (!adata->res) {
182
ret = -ENOMEM;
183
goto de_init;
184
}
185
186
adata->res[0].name = "acp3x_i2s_iomem";
187
adata->res[0].flags = IORESOURCE_MEM;
188
adata->res[0].start = addr;
189
adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
190
191
adata->res[1].name = "acp3x_i2s_sp";
192
adata->res[1].flags = IORESOURCE_MEM;
193
adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
194
adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
195
196
adata->res[2].name = "acp3x_i2s_bt";
197
adata->res[2].flags = IORESOURCE_MEM;
198
adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
199
adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
200
201
adata->res[3].name = "acp3x_i2s_irq";
202
adata->res[3].flags = IORESOURCE_IRQ;
203
adata->res[3].start = pci->irq;
204
adata->res[3].end = adata->res[3].start;
205
206
adata->acp3x_audio_mode = ACP3x_I2S_MODE;
207
208
memset(&pdevinfo, 0, sizeof(pdevinfo));
209
pdevinfo[0].name = "acp3x_rv_i2s_dma";
210
pdevinfo[0].id = 0;
211
pdevinfo[0].parent = &pci->dev;
212
pdevinfo[0].num_res = 4;
213
pdevinfo[0].res = &adata->res[0];
214
pdevinfo[0].data = &irqflags;
215
pdevinfo[0].size_data = sizeof(irqflags);
216
217
pdevinfo[1].name = "acp3x_i2s_playcap";
218
pdevinfo[1].id = 0;
219
pdevinfo[1].parent = &pci->dev;
220
pdevinfo[1].num_res = 1;
221
pdevinfo[1].res = &adata->res[1];
222
223
pdevinfo[2].name = "acp3x_i2s_playcap";
224
pdevinfo[2].id = 1;
225
pdevinfo[2].parent = &pci->dev;
226
pdevinfo[2].num_res = 1;
227
pdevinfo[2].res = &adata->res[1];
228
229
pdevinfo[3].name = "acp3x_i2s_playcap";
230
pdevinfo[3].id = 2;
231
pdevinfo[3].parent = &pci->dev;
232
pdevinfo[3].num_res = 1;
233
pdevinfo[3].res = &adata->res[2];
234
for (i = 0; i < ACP3x_DEVS; i++) {
235
adata->pdev[i] =
236
platform_device_register_full(&pdevinfo[i]);
237
if (IS_ERR(adata->pdev[i])) {
238
dev_err(&pci->dev, "cannot register %s device\n",
239
pdevinfo[i].name);
240
ret = PTR_ERR(adata->pdev[i]);
241
goto unregister_devs;
242
}
243
}
244
break;
245
default:
246
dev_info(&pci->dev, "ACP audio mode : %d\n", val);
247
break;
248
}
249
pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
250
pm_runtime_use_autosuspend(&pci->dev);
251
pm_runtime_put_noidle(&pci->dev);
252
pm_runtime_allow(&pci->dev);
253
return 0;
254
255
unregister_devs:
256
if (val == I2S_MODE)
257
for (i = 0; i < ACP3x_DEVS; i++)
258
platform_device_unregister(adata->pdev[i]);
259
de_init:
260
if (acp3x_deinit(adata->acp3x_base))
261
dev_err(&pci->dev, "ACP de-init failed\n");
262
release_regions:
263
pci_release_regions(pci);
264
disable_pci:
265
pci_disable_device(pci);
266
267
return ret;
268
}
269
270
static int snd_acp3x_suspend(struct device *dev)
271
{
272
int ret;
273
struct acp3x_dev_data *adata;
274
275
adata = dev_get_drvdata(dev);
276
ret = acp3x_deinit(adata->acp3x_base);
277
if (ret)
278
dev_err(dev, "ACP de-init failed\n");
279
else
280
dev_dbg(dev, "ACP de-initialized\n");
281
282
return 0;
283
}
284
285
static int snd_acp3x_resume(struct device *dev)
286
{
287
int ret;
288
struct acp3x_dev_data *adata;
289
290
adata = dev_get_drvdata(dev);
291
ret = acp3x_init(adata);
292
if (ret) {
293
dev_err(dev, "ACP init failed\n");
294
return ret;
295
}
296
return 0;
297
}
298
299
static const struct dev_pm_ops acp3x_pm = {
300
.runtime_suspend = snd_acp3x_suspend,
301
.runtime_resume = snd_acp3x_resume,
302
.resume = snd_acp3x_resume,
303
};
304
305
static void snd_acp3x_remove(struct pci_dev *pci)
306
{
307
struct acp3x_dev_data *adata;
308
int i, ret;
309
310
adata = pci_get_drvdata(pci);
311
if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
312
for (i = 0; i < ACP3x_DEVS; i++)
313
platform_device_unregister(adata->pdev[i]);
314
}
315
ret = acp3x_deinit(adata->acp3x_base);
316
if (ret)
317
dev_err(&pci->dev, "ACP de-init failed\n");
318
pm_runtime_forbid(&pci->dev);
319
pm_runtime_get_noresume(&pci->dev);
320
pci_release_regions(pci);
321
pci_disable_device(pci);
322
}
323
324
static const struct pci_device_id snd_acp3x_ids[] = {
325
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
326
.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
327
.class_mask = 0xffffff },
328
{ 0, },
329
};
330
MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
331
332
static struct pci_driver acp3x_driver = {
333
.name = KBUILD_MODNAME,
334
.id_table = snd_acp3x_ids,
335
.probe = snd_acp3x_probe,
336
.remove = snd_acp3x_remove,
337
.driver = {
338
.pm = &acp3x_pm,
339
}
340
};
341
342
module_pci_driver(acp3x_driver);
343
344
MODULE_AUTHOR("[email protected]");
345
MODULE_AUTHOR("[email protected]");
346
MODULE_DESCRIPTION("AMD ACP3x PCI driver");
347
MODULE_LICENSE("GPL v2");
348
349