14b214c9bSDaniel Dadap // SPDX-License-Identifier: GPL-2.0-only
24b214c9bSDaniel Dadap /*
34b214c9bSDaniel Dadap * ALSA driver for ACPI-based HDA Controllers.
44b214c9bSDaniel Dadap */
54b214c9bSDaniel Dadap
64b214c9bSDaniel Dadap #include <linux/module.h>
74b214c9bSDaniel Dadap #include <linux/platform_device.h>
84b214c9bSDaniel Dadap #include <linux/acpi.h>
94b214c9bSDaniel Dadap
104b214c9bSDaniel Dadap #include <sound/hda_codec.h>
114b214c9bSDaniel Dadap
124b214c9bSDaniel Dadap #include "hda_controller.h"
134b214c9bSDaniel Dadap
144b214c9bSDaniel Dadap struct hda_acpi {
154b214c9bSDaniel Dadap struct azx azx;
164b214c9bSDaniel Dadap struct snd_card *card;
174b214c9bSDaniel Dadap struct platform_device *pdev;
184b214c9bSDaniel Dadap void __iomem *regs;
194b214c9bSDaniel Dadap struct work_struct probe_work;
204b214c9bSDaniel Dadap const struct hda_data *data;
214b214c9bSDaniel Dadap };
224b214c9bSDaniel Dadap
234b214c9bSDaniel Dadap /**
244b214c9bSDaniel Dadap * struct hda_data - Optional device-specific data
254b214c9bSDaniel Dadap * @short_name: Used for the ALSA card name; defaults to KBUILD_MODNAME
264b214c9bSDaniel Dadap * @long_name: Used for longer description; defaults to short_name
274b214c9bSDaniel Dadap * @flags: Passed to &azx->driver_caps
284b214c9bSDaniel Dadap *
294b214c9bSDaniel Dadap * A pointer to a record of this type may be stored in the
304b214c9bSDaniel Dadap * &acpi_device_id->driver_data field of an ACPI match table entry in order to
314b214c9bSDaniel Dadap * customize the naming and behavior of a particular device. All fields are
324b214c9bSDaniel Dadap * optional and sensible defaults will be selected in their absence.
334b214c9bSDaniel Dadap */
344b214c9bSDaniel Dadap struct hda_data {
354b214c9bSDaniel Dadap const char *short_name;
364b214c9bSDaniel Dadap const char *long_name;
374b214c9bSDaniel Dadap unsigned long flags;
384b214c9bSDaniel Dadap };
394b214c9bSDaniel Dadap
hda_acpi_dev_disconnect(struct snd_device * device)404b214c9bSDaniel Dadap static int hda_acpi_dev_disconnect(struct snd_device *device)
414b214c9bSDaniel Dadap {
424b214c9bSDaniel Dadap struct azx *chip = device->device_data;
434b214c9bSDaniel Dadap
444b214c9bSDaniel Dadap chip->bus.shutdown = 1;
454b214c9bSDaniel Dadap return 0;
464b214c9bSDaniel Dadap }
474b214c9bSDaniel Dadap
hda_acpi_dev_free(struct snd_device * device)484b214c9bSDaniel Dadap static int hda_acpi_dev_free(struct snd_device *device)
494b214c9bSDaniel Dadap {
504b214c9bSDaniel Dadap struct azx *azx = device->device_data;
514b214c9bSDaniel Dadap struct hda_acpi *hda = container_of(azx, struct hda_acpi, azx);
524b214c9bSDaniel Dadap
534b214c9bSDaniel Dadap cancel_work_sync(&hda->probe_work);
544b214c9bSDaniel Dadap if (azx_bus(azx)->chip_init) {
554b214c9bSDaniel Dadap azx_stop_all_streams(azx);
564b214c9bSDaniel Dadap azx_stop_chip(azx);
574b214c9bSDaniel Dadap }
584b214c9bSDaniel Dadap
594b214c9bSDaniel Dadap azx_free_stream_pages(azx);
604b214c9bSDaniel Dadap azx_free_streams(azx);
614b214c9bSDaniel Dadap snd_hdac_bus_exit(azx_bus(azx));
624b214c9bSDaniel Dadap
634b214c9bSDaniel Dadap return 0;
644b214c9bSDaniel Dadap }
654b214c9bSDaniel Dadap
hda_acpi_init(struct hda_acpi * hda)664b214c9bSDaniel Dadap static int hda_acpi_init(struct hda_acpi *hda)
674b214c9bSDaniel Dadap {
684b214c9bSDaniel Dadap struct hdac_bus *bus = azx_bus(&hda->azx);
694b214c9bSDaniel Dadap struct snd_card *card = hda->azx.card;
704b214c9bSDaniel Dadap struct device *dev = &hda->pdev->dev;
714b214c9bSDaniel Dadap struct azx *azx = &hda->azx;
724b214c9bSDaniel Dadap struct resource *res;
734b214c9bSDaniel Dadap unsigned short gcap;
744b214c9bSDaniel Dadap const char *sname, *lname;
754b214c9bSDaniel Dadap int err, irq;
764b214c9bSDaniel Dadap
774b214c9bSDaniel Dadap /* The base address for the HDA registers and the interrupt are wrapped
784b214c9bSDaniel Dadap * in an ACPI _CRS object which can be parsed by platform_get_irq() and
794b214c9bSDaniel Dadap * devm_platform_get_and_ioremap_resource()
804b214c9bSDaniel Dadap */
814b214c9bSDaniel Dadap
824b214c9bSDaniel Dadap irq = platform_get_irq(hda->pdev, 0);
834b214c9bSDaniel Dadap if (irq < 0)
844b214c9bSDaniel Dadap return irq;
854b214c9bSDaniel Dadap
864b214c9bSDaniel Dadap hda->regs = devm_platform_get_and_ioremap_resource(hda->pdev, 0, &res);
874b214c9bSDaniel Dadap if (IS_ERR(hda->regs))
884b214c9bSDaniel Dadap return PTR_ERR(hda->regs);
894b214c9bSDaniel Dadap
904b214c9bSDaniel Dadap bus->remap_addr = hda->regs;
914b214c9bSDaniel Dadap bus->addr = res->start;
924b214c9bSDaniel Dadap
934b214c9bSDaniel Dadap err = devm_request_irq(dev, irq, azx_interrupt,
944b214c9bSDaniel Dadap IRQF_SHARED, KBUILD_MODNAME, azx);
954b214c9bSDaniel Dadap if (err) {
964b214c9bSDaniel Dadap dev_err(dev, "unable to request IRQ %d, disabling device\n",
974b214c9bSDaniel Dadap irq);
984b214c9bSDaniel Dadap return err;
994b214c9bSDaniel Dadap }
1004b214c9bSDaniel Dadap bus->irq = irq;
1014b214c9bSDaniel Dadap bus->dma_stop_delay = 100;
1024b214c9bSDaniel Dadap card->sync_irq = bus->irq;
1034b214c9bSDaniel Dadap
1044b214c9bSDaniel Dadap gcap = azx_readw(azx, GCAP);
1054b214c9bSDaniel Dadap dev_dbg(dev, "chipset global capabilities = 0x%x\n", gcap);
1064b214c9bSDaniel Dadap
1074b214c9bSDaniel Dadap azx->align_buffer_size = 1;
1084b214c9bSDaniel Dadap
1094b214c9bSDaniel Dadap azx->capture_streams = (gcap >> 8) & 0x0f;
1104b214c9bSDaniel Dadap azx->playback_streams = (gcap >> 12) & 0x0f;
1114b214c9bSDaniel Dadap
1124b214c9bSDaniel Dadap azx->capture_index_offset = 0;
1134b214c9bSDaniel Dadap azx->playback_index_offset = azx->capture_streams;
1144b214c9bSDaniel Dadap azx->num_streams = azx->playback_streams + azx->capture_streams;
1154b214c9bSDaniel Dadap
1164b214c9bSDaniel Dadap err = azx_init_streams(azx);
1174b214c9bSDaniel Dadap if (err < 0) {
1184b214c9bSDaniel Dadap dev_err(dev, "failed to initialize streams: %d\n", err);
1194b214c9bSDaniel Dadap return err;
1204b214c9bSDaniel Dadap }
1214b214c9bSDaniel Dadap
1224b214c9bSDaniel Dadap err = azx_alloc_stream_pages(azx);
1234b214c9bSDaniel Dadap if (err < 0) {
1244b214c9bSDaniel Dadap dev_err(dev, "failed to allocate stream pages: %d\n", err);
1254b214c9bSDaniel Dadap return err;
1264b214c9bSDaniel Dadap }
1274b214c9bSDaniel Dadap
1284b214c9bSDaniel Dadap azx_init_chip(azx, 1);
1294b214c9bSDaniel Dadap
1304b214c9bSDaniel Dadap if (!bus->codec_mask) {
1314b214c9bSDaniel Dadap dev_err(dev, "no codecs found!\n");
1324b214c9bSDaniel Dadap return -ENODEV;
1334b214c9bSDaniel Dadap }
1344b214c9bSDaniel Dadap
1354b214c9bSDaniel Dadap strscpy(card->driver, "hda-acpi");
1364b214c9bSDaniel Dadap
1374b214c9bSDaniel Dadap sname = hda->data->short_name ? hda->data->short_name : KBUILD_MODNAME;
1384b214c9bSDaniel Dadap
1394b214c9bSDaniel Dadap if (strlen(sname) > sizeof(card->shortname))
1404b214c9bSDaniel Dadap dev_info(dev, "truncating shortname for card %s\n", sname);
1414b214c9bSDaniel Dadap strscpy(card->shortname, sname);
1424b214c9bSDaniel Dadap
1434b214c9bSDaniel Dadap lname = hda->data->long_name ? hda->data->long_name : sname;
1444b214c9bSDaniel Dadap
1454b214c9bSDaniel Dadap snprintf(card->longname, sizeof(card->longname),
1464b214c9bSDaniel Dadap "%s at 0x%lx irq %i", lname, bus->addr, bus->irq);
1474b214c9bSDaniel Dadap
1484b214c9bSDaniel Dadap return 0;
1494b214c9bSDaniel Dadap }
1504b214c9bSDaniel Dadap
hda_acpi_probe_work(struct work_struct * work)1514b214c9bSDaniel Dadap static void hda_acpi_probe_work(struct work_struct *work)
1524b214c9bSDaniel Dadap {
1534b214c9bSDaniel Dadap struct hda_acpi *hda = container_of(work, struct hda_acpi, probe_work);
1544b214c9bSDaniel Dadap struct azx *chip = &hda->azx;
1554b214c9bSDaniel Dadap int err;
1564b214c9bSDaniel Dadap
1574b214c9bSDaniel Dadap err = hda_acpi_init(hda);
1584b214c9bSDaniel Dadap if (err < 0)
1594b214c9bSDaniel Dadap return;
1604b214c9bSDaniel Dadap
1614b214c9bSDaniel Dadap err = azx_probe_codecs(chip, 8);
1624b214c9bSDaniel Dadap if (err < 0)
1634b214c9bSDaniel Dadap return;
1644b214c9bSDaniel Dadap
1654b214c9bSDaniel Dadap err = azx_codec_configure(chip);
1664b214c9bSDaniel Dadap if (err < 0)
1674b214c9bSDaniel Dadap return;
1684b214c9bSDaniel Dadap
1694b214c9bSDaniel Dadap err = snd_card_register(chip->card);
1704b214c9bSDaniel Dadap if (err < 0)
1714b214c9bSDaniel Dadap return;
1724b214c9bSDaniel Dadap
1734b214c9bSDaniel Dadap chip->running = 1;
1744b214c9bSDaniel Dadap }
1754b214c9bSDaniel Dadap
hda_acpi_create(struct hda_acpi * hda)1764b214c9bSDaniel Dadap static int hda_acpi_create(struct hda_acpi *hda)
1774b214c9bSDaniel Dadap {
1784b214c9bSDaniel Dadap static const struct snd_device_ops ops = {
1794b214c9bSDaniel Dadap .dev_disconnect = hda_acpi_dev_disconnect,
1804b214c9bSDaniel Dadap .dev_free = hda_acpi_dev_free,
1814b214c9bSDaniel Dadap };
1824b214c9bSDaniel Dadap static const struct hda_controller_ops null_ops;
1834b214c9bSDaniel Dadap struct azx *azx = &hda->azx;
1844b214c9bSDaniel Dadap int err;
1854b214c9bSDaniel Dadap
1864b214c9bSDaniel Dadap mutex_init(&azx->open_mutex);
1874b214c9bSDaniel Dadap azx->card = hda->card;
1884b214c9bSDaniel Dadap INIT_LIST_HEAD(&azx->pcm_list);
1894b214c9bSDaniel Dadap
1904b214c9bSDaniel Dadap azx->ops = &null_ops;
1914b214c9bSDaniel Dadap azx->driver_caps = hda->data->flags;
1924b214c9bSDaniel Dadap azx->driver_type = hda->data->flags & 0xff;
1934b214c9bSDaniel Dadap azx->codec_probe_mask = -1;
1944b214c9bSDaniel Dadap
1954b214c9bSDaniel Dadap err = azx_bus_init(azx, NULL);
1964b214c9bSDaniel Dadap if (err < 0)
1974b214c9bSDaniel Dadap return err;
1984b214c9bSDaniel Dadap
1994b214c9bSDaniel Dadap err = snd_device_new(hda->card, SNDRV_DEV_LOWLEVEL, &hda->azx, &ops);
2004b214c9bSDaniel Dadap if (err < 0) {
2014b214c9bSDaniel Dadap dev_err(&hda->pdev->dev, "Error creating device\n");
2024b214c9bSDaniel Dadap return err;
2034b214c9bSDaniel Dadap }
2044b214c9bSDaniel Dadap
2054b214c9bSDaniel Dadap return 0;
2064b214c9bSDaniel Dadap }
2074b214c9bSDaniel Dadap
hda_acpi_probe(struct platform_device * pdev)2084b214c9bSDaniel Dadap static int hda_acpi_probe(struct platform_device *pdev)
2094b214c9bSDaniel Dadap {
2104b214c9bSDaniel Dadap struct hda_acpi *hda;
2114b214c9bSDaniel Dadap int err;
2124b214c9bSDaniel Dadap
2134b214c9bSDaniel Dadap hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
2144b214c9bSDaniel Dadap if (!hda)
2154b214c9bSDaniel Dadap return -ENOMEM;
2164b214c9bSDaniel Dadap
2174b214c9bSDaniel Dadap hda->pdev = pdev;
2184b214c9bSDaniel Dadap hda->data = acpi_device_get_match_data(&pdev->dev);
2194b214c9bSDaniel Dadap
2204b214c9bSDaniel Dadap /* Fall back to defaults if the table didn't have a *struct hda_data */
2214b214c9bSDaniel Dadap if (!hda->data)
2224b214c9bSDaniel Dadap hda->data = devm_kzalloc(&pdev->dev, sizeof(*hda->data),
2234b214c9bSDaniel Dadap GFP_KERNEL);
2244b214c9bSDaniel Dadap if (!hda->data)
2254b214c9bSDaniel Dadap return -ENOMEM;
2264b214c9bSDaniel Dadap
2274b214c9bSDaniel Dadap err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
2284b214c9bSDaniel Dadap THIS_MODULE, 0, &hda->card);
2294b214c9bSDaniel Dadap if (err < 0) {
2304b214c9bSDaniel Dadap dev_err(&pdev->dev, "Error creating card!\n");
2314b214c9bSDaniel Dadap return err;
2324b214c9bSDaniel Dadap }
2334b214c9bSDaniel Dadap
2344b214c9bSDaniel Dadap INIT_WORK(&hda->probe_work, hda_acpi_probe_work);
2354b214c9bSDaniel Dadap
2364b214c9bSDaniel Dadap err = hda_acpi_create(hda);
2374b214c9bSDaniel Dadap if (err < 0)
2384b214c9bSDaniel Dadap goto out_free;
2394b214c9bSDaniel Dadap hda->card->private_data = &hda->azx;
2404b214c9bSDaniel Dadap
2414b214c9bSDaniel Dadap dev_set_drvdata(&pdev->dev, hda->card);
2424b214c9bSDaniel Dadap
2434b214c9bSDaniel Dadap schedule_work(&hda->probe_work);
2444b214c9bSDaniel Dadap
2454b214c9bSDaniel Dadap return 0;
2464b214c9bSDaniel Dadap
2474b214c9bSDaniel Dadap out_free:
2484b214c9bSDaniel Dadap snd_card_free(hda->card);
2494b214c9bSDaniel Dadap return err;
2504b214c9bSDaniel Dadap }
2514b214c9bSDaniel Dadap
hda_acpi_remove(struct platform_device * pdev)2524b214c9bSDaniel Dadap static void hda_acpi_remove(struct platform_device *pdev)
2534b214c9bSDaniel Dadap {
2544b214c9bSDaniel Dadap snd_card_free(dev_get_drvdata(&pdev->dev));
2554b214c9bSDaniel Dadap }
2564b214c9bSDaniel Dadap
hda_acpi_shutdown(struct platform_device * pdev)2574b214c9bSDaniel Dadap static void hda_acpi_shutdown(struct platform_device *pdev)
2584b214c9bSDaniel Dadap {
2594b214c9bSDaniel Dadap struct snd_card *card = dev_get_drvdata(&pdev->dev);
2604b214c9bSDaniel Dadap struct azx *chip;
2614b214c9bSDaniel Dadap
2624b214c9bSDaniel Dadap if (!card)
2634b214c9bSDaniel Dadap return;
2644b214c9bSDaniel Dadap chip = card->private_data;
2654b214c9bSDaniel Dadap if (chip && chip->running)
2664b214c9bSDaniel Dadap azx_stop_chip(chip);
2674b214c9bSDaniel Dadap }
2684b214c9bSDaniel Dadap
hda_acpi_suspend(struct device * dev)2694b214c9bSDaniel Dadap static int hda_acpi_suspend(struct device *dev)
2704b214c9bSDaniel Dadap {
2714b214c9bSDaniel Dadap struct snd_card *card = dev_get_drvdata(dev);
2724b214c9bSDaniel Dadap int rc;
2734b214c9bSDaniel Dadap
2744b214c9bSDaniel Dadap rc = pm_runtime_force_suspend(dev);
2754b214c9bSDaniel Dadap if (rc < 0)
2764b214c9bSDaniel Dadap return rc;
2774b214c9bSDaniel Dadap snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
2784b214c9bSDaniel Dadap
2794b214c9bSDaniel Dadap return 0;
2804b214c9bSDaniel Dadap }
2814b214c9bSDaniel Dadap
hda_acpi_resume(struct device * dev)2824b214c9bSDaniel Dadap static int hda_acpi_resume(struct device *dev)
2834b214c9bSDaniel Dadap {
2844b214c9bSDaniel Dadap struct snd_card *card = dev_get_drvdata(dev);
2854b214c9bSDaniel Dadap int rc;
2864b214c9bSDaniel Dadap
2874b214c9bSDaniel Dadap rc = pm_runtime_force_resume(dev);
2884b214c9bSDaniel Dadap if (rc < 0)
2894b214c9bSDaniel Dadap return rc;
2904b214c9bSDaniel Dadap snd_power_change_state(card, SNDRV_CTL_POWER_D0);
2914b214c9bSDaniel Dadap
2924b214c9bSDaniel Dadap return 0;
2934b214c9bSDaniel Dadap }
2944b214c9bSDaniel Dadap
2954b214c9bSDaniel Dadap static const struct dev_pm_ops hda_acpi_pm = {
296ebaa3bf4STakashi Iwai SYSTEM_SLEEP_PM_OPS(hda_acpi_suspend, hda_acpi_resume)
2974b214c9bSDaniel Dadap };
2984b214c9bSDaniel Dadap
299*aca89f1bSTakashi Iwai static const struct hda_data nvidia_hda_data = {
3004b214c9bSDaniel Dadap .short_name = "NVIDIA",
3014b214c9bSDaniel Dadap .long_name = "NVIDIA HDA Controller",
3024b214c9bSDaniel Dadap .flags = AZX_DCAPS_CORBRP_SELF_CLEAR,
3034b214c9bSDaniel Dadap };
3044b214c9bSDaniel Dadap
3054b214c9bSDaniel Dadap static const struct acpi_device_id hda_acpi_match[] = {
3064b214c9bSDaniel Dadap { .id = "NVDA2014", .driver_data = (uintptr_t) &nvidia_hda_data },
3074b214c9bSDaniel Dadap { .id = "NVDA2015", .driver_data = (uintptr_t) &nvidia_hda_data },
3084b214c9bSDaniel Dadap {},
3094b214c9bSDaniel Dadap };
3104b214c9bSDaniel Dadap MODULE_DEVICE_TABLE(acpi, hda_acpi_match);
3114b214c9bSDaniel Dadap
3124b214c9bSDaniel Dadap static struct platform_driver hda_acpi_platform_driver = {
3134b214c9bSDaniel Dadap .driver = {
3144b214c9bSDaniel Dadap .name = KBUILD_MODNAME,
3154b214c9bSDaniel Dadap .pm = &hda_acpi_pm,
3164b214c9bSDaniel Dadap .acpi_match_table = hda_acpi_match,
3174b214c9bSDaniel Dadap },
3184b214c9bSDaniel Dadap .probe = hda_acpi_probe,
3194b214c9bSDaniel Dadap .remove = hda_acpi_remove,
3204b214c9bSDaniel Dadap .shutdown = hda_acpi_shutdown,
3214b214c9bSDaniel Dadap };
3224b214c9bSDaniel Dadap module_platform_driver(hda_acpi_platform_driver);
3234b214c9bSDaniel Dadap
3244b214c9bSDaniel Dadap MODULE_DESCRIPTION("Driver for ACPI-based HDA Controllers");
3254b214c9bSDaniel Dadap MODULE_LICENSE("GPL");
326