diff options
Diffstat (limited to 'sound/pci/hda/hda_intel.c')
-rw-r--r-- | sound/pci/hda/hda_intel.c | 115 |
1 files changed, 106 insertions, 9 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7e40a8c58827..6435652937af 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1866,6 +1866,26 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, return 0; } +static int azx_request_pci_regions(struct azx *chip) +{ + struct hdac_bus *bus = azx_bus(chip); + struct pci_dev *pci = chip->pci; + int err; + + err = pci_request_regions(pci, "ICH HD audio"); + if (err < 0) + return err; + chip->region_requested = 1; + + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (!bus->remap_addr) { + dev_err(&pci->dev, "ioremap error\n"); + return -ENXIO; + } + return 0; +} + static int azx_first_init(struct azx *chip) { int dev = chip->dev_index; @@ -1886,17 +1906,9 @@ static int azx_first_init(struct azx *chip) } #endif - err = pci_request_regions(pci, "ICH HD audio"); + err = azx_request_pci_regions(chip); if (err < 0) return err; - chip->region_requested = 1; - - bus->addr = pci_resource_start(pci, 0); - bus->remap_addr = pci_ioremap_bar(pci, 0); - if (bus->remap_addr == NULL) { - dev_err(card->dev, "ioremap error\n"); - return -ENXIO; - } if (chip->driver_type == AZX_DRIVER_SKL) snd_hdac_bus_parse_capabilities(bus); @@ -2420,6 +2432,86 @@ static void azx_shutdown(struct pci_dev *pci) azx_stop_chip(chip); } +#ifdef CONFIG_PM +static bool azx_bar_fixed(struct pci_dev *pdev, int resno) +{ + return false; +} + +static void azx_rescan_prepare(struct pci_dev *pdev) +{ + struct snd_card *card = pci_get_drvdata(pdev); + struct azx *chip = card->private_data; + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct hdac_bus *bus = azx_bus(chip); + struct hda_codec *codec; + + // FIXME: need unlock/lock dance as in azx_remove()? + flush_work(&hda->probe_work); + + if (hda->freed || hda->init_failed) + return; + + if (chip->running) { + pm_runtime_get_sync(&pdev->dev); + azx_prepare(&pdev->dev); + azx_suspend_streams(chip); + wait_event(card->power_ref_sleep, + !atomic_read(&card->power_ref)); + list_for_each_codec(codec, &chip->bus) { + pm_runtime_suspend(hda_codec_dev(codec)); + pm_runtime_disable(hda_codec_dev(codec)); + } + azx_suspend(&pdev->dev); + } + + /* Unmap MMIO and release BAR resource */ + iounmap(bus->remap_addr); + if (chip->region_requested) { + pci_release_regions(chip->pci); + chip->region_requested = 0; + } +} + +static void azx_rescan_done(struct pci_dev *pdev) +{ + struct snd_card *card = pci_get_drvdata(pdev); + struct azx *chip = card->private_data; + struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *azx_dev; + struct hda_codec *codec; + int err; + + if (hda->freed || hda->init_failed) + return; + + /* Reassign BAR and remap */ + err = azx_request_pci_regions(chip); + if (err < 0) { + dev_err(card->dev, "Rescan failed: disabling the device\n"); + card->shutdown = 1; + hda->init_failed = true; + // FIXME: any better handling? + return; + } + + // FIXME: should be in hdac_stream.c + list_for_each_entry(azx_dev, &bus->stream_list, list) + azx_dev->sd_addr = bus->remap_addr + (0x20 * azx_dev->index + 0x80); + + if (chip->running) { + azx_resume(&pdev->dev); + list_for_each_codec(codec, &chip->bus) { + pm_runtime_enable(hda_codec_dev(codec)); + pm_runtime_resume(hda_codec_dev(codec)); + } + azx_complete(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + } +} +#endif /* CONFIG_PM */ + /* PCI IDs */ static const struct pci_device_id azx_ids[] = { /* CPT */ @@ -2776,6 +2868,11 @@ static struct pci_driver azx_driver = { .driver = { .pm = AZX_PM_OPS, }, +#ifdef CONFIG_PM + .rescan_prepare = azx_rescan_prepare, + .rescan_done = azx_rescan_done, + .bar_fixed = azx_bar_fixed, +#endif }; module_pci_driver(azx_driver); |