Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/controllers/acpi.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* ALSA driver for ACPI-based HDA Controllers.
4
*/
5
6
#include <linux/module.h>
7
#include <linux/platform_device.h>
8
#include <linux/acpi.h>
9
10
#include <sound/hda_codec.h>
11
12
#include "hda_controller.h"
13
14
struct hda_acpi {
15
struct azx azx;
16
struct snd_card *card;
17
struct platform_device *pdev;
18
void __iomem *regs;
19
struct work_struct probe_work;
20
const struct hda_data *data;
21
};
22
23
/**
24
* struct hda_data - Optional device-specific data
25
* @short_name: Used for the ALSA card name; defaults to KBUILD_MODNAME
26
* @long_name: Used for longer description; defaults to short_name
27
* @flags: Passed to &azx->driver_caps
28
*
29
* A pointer to a record of this type may be stored in the
30
* &acpi_device_id->driver_data field of an ACPI match table entry in order to
31
* customize the naming and behavior of a particular device. All fields are
32
* optional and sensible defaults will be selected in their absence.
33
*/
34
struct hda_data {
35
const char *short_name;
36
const char *long_name;
37
unsigned long flags;
38
};
39
40
static int hda_acpi_dev_disconnect(struct snd_device *device)
41
{
42
struct azx *chip = device->device_data;
43
44
chip->bus.shutdown = 1;
45
return 0;
46
}
47
48
static int hda_acpi_dev_free(struct snd_device *device)
49
{
50
struct azx *azx = device->device_data;
51
struct hda_acpi *hda = container_of(azx, struct hda_acpi, azx);
52
53
cancel_work_sync(&hda->probe_work);
54
if (azx_bus(azx)->chip_init) {
55
azx_stop_all_streams(azx);
56
azx_stop_chip(azx);
57
}
58
59
azx_free_stream_pages(azx);
60
azx_free_streams(azx);
61
snd_hdac_bus_exit(azx_bus(azx));
62
63
return 0;
64
}
65
66
static int hda_acpi_init(struct hda_acpi *hda)
67
{
68
struct hdac_bus *bus = azx_bus(&hda->azx);
69
struct snd_card *card = hda->azx.card;
70
struct device *dev = &hda->pdev->dev;
71
struct azx *azx = &hda->azx;
72
struct resource *res;
73
unsigned short gcap;
74
const char *sname, *lname;
75
int err, irq;
76
77
/* The base address for the HDA registers and the interrupt are wrapped
78
* in an ACPI _CRS object which can be parsed by platform_get_irq() and
79
* devm_platform_get_and_ioremap_resource()
80
*/
81
82
irq = platform_get_irq(hda->pdev, 0);
83
if (irq < 0)
84
return irq;
85
86
hda->regs = devm_platform_get_and_ioremap_resource(hda->pdev, 0, &res);
87
if (IS_ERR(hda->regs))
88
return PTR_ERR(hda->regs);
89
90
bus->remap_addr = hda->regs;
91
bus->addr = res->start;
92
93
err = devm_request_irq(dev, irq, azx_interrupt,
94
IRQF_SHARED, KBUILD_MODNAME, azx);
95
if (err) {
96
dev_err(dev, "unable to request IRQ %d, disabling device\n",
97
irq);
98
return err;
99
}
100
bus->irq = irq;
101
bus->dma_stop_delay = 100;
102
card->sync_irq = bus->irq;
103
104
gcap = azx_readw(azx, GCAP);
105
dev_dbg(dev, "chipset global capabilities = 0x%x\n", gcap);
106
107
azx->align_buffer_size = 1;
108
109
azx->capture_streams = (gcap >> 8) & 0x0f;
110
azx->playback_streams = (gcap >> 12) & 0x0f;
111
112
azx->capture_index_offset = 0;
113
azx->playback_index_offset = azx->capture_streams;
114
azx->num_streams = azx->playback_streams + azx->capture_streams;
115
116
err = azx_init_streams(azx);
117
if (err < 0) {
118
dev_err(dev, "failed to initialize streams: %d\n", err);
119
return err;
120
}
121
122
err = azx_alloc_stream_pages(azx);
123
if (err < 0) {
124
dev_err(dev, "failed to allocate stream pages: %d\n", err);
125
return err;
126
}
127
128
azx_init_chip(azx, 1);
129
130
if (!bus->codec_mask) {
131
dev_err(dev, "no codecs found!\n");
132
return -ENODEV;
133
}
134
135
strscpy(card->driver, "hda-acpi");
136
137
sname = hda->data->short_name ? hda->data->short_name : KBUILD_MODNAME;
138
139
if (strlen(sname) > sizeof(card->shortname))
140
dev_info(dev, "truncating shortname for card %s\n", sname);
141
strscpy(card->shortname, sname);
142
143
lname = hda->data->long_name ? hda->data->long_name : sname;
144
145
snprintf(card->longname, sizeof(card->longname),
146
"%s at 0x%lx irq %i", lname, bus->addr, bus->irq);
147
148
return 0;
149
}
150
151
static void hda_acpi_probe_work(struct work_struct *work)
152
{
153
struct hda_acpi *hda = container_of(work, struct hda_acpi, probe_work);
154
struct azx *chip = &hda->azx;
155
int err;
156
157
err = hda_acpi_init(hda);
158
if (err < 0)
159
return;
160
161
err = azx_probe_codecs(chip, 8);
162
if (err < 0)
163
return;
164
165
err = azx_codec_configure(chip);
166
if (err < 0)
167
return;
168
169
err = snd_card_register(chip->card);
170
if (err < 0)
171
return;
172
173
chip->running = 1;
174
}
175
176
static int hda_acpi_create(struct hda_acpi *hda)
177
{
178
static const struct snd_device_ops ops = {
179
.dev_disconnect = hda_acpi_dev_disconnect,
180
.dev_free = hda_acpi_dev_free,
181
};
182
static const struct hda_controller_ops null_ops;
183
struct azx *azx = &hda->azx;
184
int err;
185
186
mutex_init(&azx->open_mutex);
187
azx->card = hda->card;
188
INIT_LIST_HEAD(&azx->pcm_list);
189
190
azx->ops = &null_ops;
191
azx->driver_caps = hda->data->flags;
192
azx->driver_type = hda->data->flags & 0xff;
193
azx->codec_probe_mask = -1;
194
195
err = azx_bus_init(azx, NULL);
196
if (err < 0)
197
return err;
198
199
err = snd_device_new(hda->card, SNDRV_DEV_LOWLEVEL, &hda->azx, &ops);
200
if (err < 0) {
201
dev_err(&hda->pdev->dev, "Error creating device\n");
202
return err;
203
}
204
205
return 0;
206
}
207
208
static int hda_acpi_probe(struct platform_device *pdev)
209
{
210
struct hda_acpi *hda;
211
int err;
212
213
hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
214
if (!hda)
215
return -ENOMEM;
216
217
hda->pdev = pdev;
218
hda->data = acpi_device_get_match_data(&pdev->dev);
219
220
/* Fall back to defaults if the table didn't have a *struct hda_data */
221
if (!hda->data)
222
hda->data = devm_kzalloc(&pdev->dev, sizeof(*hda->data),
223
GFP_KERNEL);
224
if (!hda->data)
225
return -ENOMEM;
226
227
err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
228
THIS_MODULE, 0, &hda->card);
229
if (err < 0) {
230
dev_err(&pdev->dev, "Error creating card!\n");
231
return err;
232
}
233
234
INIT_WORK(&hda->probe_work, hda_acpi_probe_work);
235
236
err = hda_acpi_create(hda);
237
if (err < 0)
238
goto out_free;
239
hda->card->private_data = &hda->azx;
240
241
dev_set_drvdata(&pdev->dev, hda->card);
242
243
schedule_work(&hda->probe_work);
244
245
return 0;
246
247
out_free:
248
snd_card_free(hda->card);
249
return err;
250
}
251
252
static void hda_acpi_remove(struct platform_device *pdev)
253
{
254
snd_card_free(dev_get_drvdata(&pdev->dev));
255
}
256
257
static void hda_acpi_shutdown(struct platform_device *pdev)
258
{
259
struct snd_card *card = dev_get_drvdata(&pdev->dev);
260
struct azx *chip;
261
262
if (!card)
263
return;
264
chip = card->private_data;
265
if (chip && chip->running)
266
azx_stop_chip(chip);
267
}
268
269
static int hda_acpi_suspend(struct device *dev)
270
{
271
struct snd_card *card = dev_get_drvdata(dev);
272
int rc;
273
274
rc = pm_runtime_force_suspend(dev);
275
if (rc < 0)
276
return rc;
277
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
278
279
return 0;
280
}
281
282
static int hda_acpi_resume(struct device *dev)
283
{
284
struct snd_card *card = dev_get_drvdata(dev);
285
int rc;
286
287
rc = pm_runtime_force_resume(dev);
288
if (rc < 0)
289
return rc;
290
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
291
292
return 0;
293
}
294
295
static const struct dev_pm_ops hda_acpi_pm = {
296
SYSTEM_SLEEP_PM_OPS(hda_acpi_suspend, hda_acpi_resume)
297
};
298
299
static const struct hda_data nvidia_hda_data = {
300
.short_name = "NVIDIA",
301
.long_name = "NVIDIA HDA Controller",
302
.flags = AZX_DCAPS_CORBRP_SELF_CLEAR,
303
};
304
305
static const struct acpi_device_id hda_acpi_match[] = {
306
{ .id = "NVDA2014", .driver_data = (uintptr_t) &nvidia_hda_data },
307
{ .id = "NVDA2015", .driver_data = (uintptr_t) &nvidia_hda_data },
308
{},
309
};
310
MODULE_DEVICE_TABLE(acpi, hda_acpi_match);
311
312
static struct platform_driver hda_acpi_platform_driver = {
313
.driver = {
314
.name = KBUILD_MODNAME,
315
.pm = &hda_acpi_pm,
316
.acpi_match_table = hda_acpi_match,
317
},
318
.probe = hda_acpi_probe,
319
.remove = hda_acpi_remove,
320
.shutdown = hda_acpi_shutdown,
321
};
322
module_platform_driver(hda_acpi_platform_driver);
323
324
MODULE_DESCRIPTION("Driver for ACPI-based HDA Controllers");
325
MODULE_LICENSE("GPL");
326
327