Skip to content

Commit 4b214c9

Browse files
dadaptiwai
authored andcommitted
ALSA: hda - Add new driver for HDA controllers listed via ACPI
Some systems expose HD-Audio controllers via objects in the ACPI tables which encapsulate the controller's interrupt and the base address for the HDA registers in an ACPI _CRS object, for example, as listed in this ACPI table dump excerpt: Device (HDA0) { Name (_HID, "NVDA2014") // _HID: Hardware ID ... Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x36078000, // Address Base 0x00008000, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x0000021E, } }) } Add support for HDA controllers discovered through ACPI, including support for some platforms which expose such HDA controllers on NVIDIA SoCs. This is done with a new driver which uses existing infrastructure for extracting resource information from _CRS objects and plumbs the parsed resource information through to the existing HDA infrastructure to enable HD-Audio functionality on such devices. Although this driver is in the sound/pci/hda/ directory, it targets devices which are not actually enumerated on the PCI bus. This is because it depends upon the Intel "Azalia" infrastructure which has traditionally been usedvfor PCI-based devices. Signed-off-by: Daniel Dadap <ddadap@nvidia.com> Link: https://patch.msgid.link/aC3ksXJUM9DlKiz6@ddadap-lakeline.nvidia.com Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent 9110fad commit 4b214c9

File tree

3 files changed

+338
-0
lines changed

3 files changed

+338
-0
lines changed

sound/pci/hda/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ config SND_HDA_TEGRA
4242
To compile this driver as a module, choose M here: the module
4343
will be called snd-hda-tegra.
4444

45+
config SND_HDA_ACPI
46+
tristate "HD Audio ACPI"
47+
depends on ACPI
48+
select SND_HDA
49+
help
50+
Say Y here to include support for Azalia-compatible HDA controllers
51+
which are advertised via ACPI objects.
52+
53+
To compile this driver as a module, choose M here: the module
54+
will be called snd-hda-acpi.
55+
4556
if SND_HDA
4657

4758
config SND_HDA_HWDEP

sound/pci/hda/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22
snd-hda-intel-y := hda_intel.o
33
snd-hda-tegra-y := hda_tegra.o
4+
snd-hda-acpi-y := hda_acpi.o
45

56
snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
67
snd-hda-codec-y += hda_controller.o
@@ -80,3 +81,4 @@ obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
8081
# when built in kernel
8182
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
8283
obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
84+
obj-$(CONFIG_SND_HDA_ACPI) += snd-hda-acpi.o

sound/pci/hda/hda_acpi.c

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
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+
SET_SYSTEM_SLEEP_PM_OPS(hda_acpi_suspend, hda_acpi_resume)
297+
};
298+
299+
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");

0 commit comments

Comments
 (0)