Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/amd/yc/pci-acp6x.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
* AMD Yellow Carp ACP PCI Driver
4
*
5
* Copyright 2021 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/interrupt.h>
14
#include <sound/pcm_params.h>
15
#include <linux/pm_runtime.h>
16
17
#include "acp6x.h"
18
19
struct acp6x_dev_data {
20
void __iomem *acp6x_base;
21
struct resource *res;
22
bool acp6x_audio_mode;
23
struct platform_device *pdev[ACP6x_DEVS];
24
};
25
26
static int acp6x_power_on(void __iomem *acp_base)
27
{
28
u32 val;
29
int timeout;
30
31
val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
32
33
if (!val)
34
return val;
35
36
if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
37
acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
38
timeout = 0;
39
while (++timeout < 500) {
40
val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
41
if (!val)
42
return 0;
43
udelay(1);
44
}
45
return -ETIMEDOUT;
46
}
47
48
static int acp6x_reset(void __iomem *acp_base)
49
{
50
u32 val;
51
int timeout;
52
53
acp6x_writel(1, acp_base + ACP_SOFT_RESET);
54
timeout = 0;
55
while (++timeout < 500) {
56
val = acp6x_readl(acp_base + ACP_SOFT_RESET);
57
if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
58
break;
59
cpu_relax();
60
}
61
acp6x_writel(0, acp_base + ACP_SOFT_RESET);
62
timeout = 0;
63
while (++timeout < 500) {
64
val = acp6x_readl(acp_base + ACP_SOFT_RESET);
65
if (!val)
66
return 0;
67
cpu_relax();
68
}
69
return -ETIMEDOUT;
70
}
71
72
static void acp6x_enable_interrupts(void __iomem *acp_base)
73
{
74
acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
75
}
76
77
static void acp6x_disable_interrupts(void __iomem *acp_base)
78
{
79
acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
80
ACP_EXTERNAL_INTR_STAT);
81
acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);
82
acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
83
}
84
85
static int acp6x_init(void __iomem *acp_base)
86
{
87
int ret;
88
89
/* power on */
90
ret = acp6x_power_on(acp_base);
91
if (ret) {
92
pr_err("ACP power on failed\n");
93
return ret;
94
}
95
acp6x_writel(0x01, acp_base + ACP_CONTROL);
96
/* Reset */
97
ret = acp6x_reset(acp_base);
98
if (ret) {
99
pr_err("ACP reset failed\n");
100
return ret;
101
}
102
acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);
103
acp6x_enable_interrupts(acp_base);
104
return 0;
105
}
106
107
static int acp6x_deinit(void __iomem *acp_base)
108
{
109
int ret;
110
111
acp6x_disable_interrupts(acp_base);
112
/* Reset */
113
ret = acp6x_reset(acp_base);
114
if (ret) {
115
pr_err("ACP reset failed\n");
116
return ret;
117
}
118
acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);
119
acp6x_writel(0x00, acp_base + ACP_CONTROL);
120
return 0;
121
}
122
123
static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)
124
{
125
struct acp6x_dev_data *adata;
126
struct pdm_dev_data *yc_pdm_data;
127
u32 val;
128
129
adata = dev_id;
130
if (!adata)
131
return IRQ_NONE;
132
133
val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
134
if (val & BIT(PDM_DMA_STAT)) {
135
yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
136
acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
137
if (yc_pdm_data->capture_stream)
138
snd_pcm_period_elapsed(yc_pdm_data->capture_stream);
139
return IRQ_HANDLED;
140
}
141
return IRQ_NONE;
142
}
143
144
static int snd_acp6x_probe(struct pci_dev *pci,
145
const struct pci_device_id *pci_id)
146
{
147
struct acp6x_dev_data *adata;
148
struct platform_device_info pdevinfo[ACP6x_DEVS];
149
int index = 0;
150
int val = 0x00;
151
u32 addr;
152
unsigned int irqflags, flag;
153
int ret;
154
155
irqflags = IRQF_SHARED;
156
157
/* Return if acp config flag is defined */
158
flag = snd_amd_acp_find_config(pci);
159
if (flag)
160
return -ENODEV;
161
162
/* Yellow Carp device check */
163
switch (pci->revision) {
164
case 0x60:
165
case 0x6f:
166
break;
167
default:
168
dev_dbg(&pci->dev, "acp6x pci device not found\n");
169
return -ENODEV;
170
}
171
if (pci_enable_device(pci)) {
172
dev_err(&pci->dev, "pci_enable_device failed\n");
173
return -ENODEV;
174
}
175
176
ret = pci_request_regions(pci, "AMD ACP3x audio");
177
if (ret < 0) {
178
dev_err(&pci->dev, "pci_request_regions failed\n");
179
goto disable_pci;
180
}
181
182
adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),
183
GFP_KERNEL);
184
if (!adata) {
185
ret = -ENOMEM;
186
goto release_regions;
187
}
188
189
addr = pci_resource_start(pci, 0);
190
adata->acp6x_base = devm_ioremap(&pci->dev, addr,
191
pci_resource_len(pci, 0));
192
if (!adata->acp6x_base) {
193
ret = -ENOMEM;
194
goto release_regions;
195
}
196
pci_set_master(pci);
197
pci_set_drvdata(pci, adata);
198
ret = acp6x_init(adata->acp6x_base);
199
if (ret)
200
goto release_regions;
201
val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);
202
switch (val) {
203
case ACP_CONFIG_0:
204
case ACP_CONFIG_1:
205
case ACP_CONFIG_2:
206
case ACP_CONFIG_3:
207
case ACP_CONFIG_9:
208
case ACP_CONFIG_15:
209
dev_info(&pci->dev, "Audio Mode %d\n", val);
210
break;
211
default:
212
adata->res = devm_kzalloc(&pci->dev,
213
sizeof(struct resource),
214
GFP_KERNEL);
215
if (!adata->res) {
216
ret = -ENOMEM;
217
goto de_init;
218
}
219
220
adata->res->name = "acp_iomem";
221
adata->res->flags = IORESOURCE_MEM;
222
adata->res->start = addr;
223
adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);
224
225
adata->acp6x_audio_mode = ACP6x_PDM_MODE;
226
227
memset(&pdevinfo, 0, sizeof(pdevinfo));
228
pdevinfo[0].name = "acp_yc_pdm_dma";
229
pdevinfo[0].id = 0;
230
pdevinfo[0].parent = &pci->dev;
231
pdevinfo[0].num_res = 1;
232
pdevinfo[0].res = adata->res;
233
234
pdevinfo[1].name = "dmic-codec";
235
pdevinfo[1].id = 0;
236
pdevinfo[1].parent = &pci->dev;
237
238
pdevinfo[2].name = "acp_yc_mach";
239
pdevinfo[2].id = 0;
240
pdevinfo[2].parent = &pci->dev;
241
242
for (index = 0; index < ACP6x_DEVS; index++) {
243
adata->pdev[index] =
244
platform_device_register_full(&pdevinfo[index]);
245
if (IS_ERR(adata->pdev[index])) {
246
dev_err(&pci->dev, "cannot register %s device\n",
247
pdevinfo[index].name);
248
ret = PTR_ERR(adata->pdev[index]);
249
goto unregister_devs;
250
}
251
}
252
break;
253
}
254
ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,
255
irqflags, "ACP_PCI_IRQ", adata);
256
if (ret) {
257
dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
258
goto unregister_devs;
259
}
260
pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
261
pm_runtime_use_autosuspend(&pci->dev);
262
pm_runtime_put_noidle(&pci->dev);
263
pm_runtime_allow(&pci->dev);
264
265
return 0;
266
unregister_devs:
267
for (--index; index >= 0; index--)
268
platform_device_unregister(adata->pdev[index]);
269
de_init:
270
if (acp6x_deinit(adata->acp6x_base))
271
dev_err(&pci->dev, "ACP de-init failed\n");
272
release_regions:
273
pci_release_regions(pci);
274
disable_pci:
275
pci_disable_device(pci);
276
277
return ret;
278
}
279
280
static int snd_acp6x_suspend(struct device *dev)
281
{
282
struct acp6x_dev_data *adata;
283
int ret;
284
285
adata = dev_get_drvdata(dev);
286
ret = acp6x_deinit(adata->acp6x_base);
287
if (ret)
288
dev_err(dev, "ACP de-init failed\n");
289
return ret;
290
}
291
292
static int snd_acp6x_resume(struct device *dev)
293
{
294
struct acp6x_dev_data *adata;
295
int ret;
296
297
adata = dev_get_drvdata(dev);
298
ret = acp6x_init(adata->acp6x_base);
299
if (ret)
300
dev_err(dev, "ACP init failed\n");
301
return ret;
302
}
303
304
static const struct dev_pm_ops acp6x_pm = {
305
RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
306
SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
307
};
308
309
static void snd_acp6x_remove(struct pci_dev *pci)
310
{
311
struct acp6x_dev_data *adata;
312
int ret, index;
313
314
adata = pci_get_drvdata(pci);
315
if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {
316
for (index = 0; index < ACP6x_DEVS; index++)
317
platform_device_unregister(adata->pdev[index]);
318
}
319
ret = acp6x_deinit(adata->acp6x_base);
320
if (ret)
321
dev_err(&pci->dev, "ACP de-init failed\n");
322
pm_runtime_forbid(&pci->dev);
323
pm_runtime_get_noresume(&pci->dev);
324
pci_release_regions(pci);
325
pci_disable_device(pci);
326
}
327
328
static const struct pci_device_id snd_acp6x_ids[] = {
329
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
330
.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
331
.class_mask = 0xffffff },
332
{ 0, },
333
};
334
MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);
335
336
static struct pci_driver yc_acp6x_driver = {
337
.name = KBUILD_MODNAME,
338
.id_table = snd_acp6x_ids,
339
.probe = snd_acp6x_probe,
340
.remove = snd_acp6x_remove,
341
.driver = {
342
.pm = pm_ptr(&acp6x_pm),
343
}
344
};
345
346
module_pci_driver(yc_acp6x_driver);
347
348
MODULE_AUTHOR("[email protected]");
349
MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");
350
MODULE_LICENSE("GPL v2");
351
352