diff options
author | Takashi Iwai <tiwai@suse.de> | 2021-03-16 23:01:02 -0400 |
---|---|---|
committer | Andrey Grodzovsky <andrey.grodzovsky@amd.com> | 2021-03-29 10:34:14 -0400 |
commit | e36365d9ab5bbc30bdc221ab4b3437de34492440 (patch) | |
tree | 406b696531466d2c99c20badad1c259daa309bae | |
parent | 553390b067bf77062fbc86a57246c5d37445eba5 (diff) |
ALSA: hda: Add support for BARs move on PCI rescanyadro/pcie_hotplug/movable_bars_v9.1
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/pci/hda/hda_beep.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_controller.c | 9 | ||||
-rw-r--r-- | sound/pci/hda/hda_controller.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_hwdep.c | 7 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 115 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 3 | ||||
-rw-r--r-- | sound/pci/hda/hda_sysfs.c | 5 |
7 files changed, 131 insertions, 11 deletions
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 53a2b89f8983..bf2f726e5901 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -25,6 +25,7 @@ static void generate_tone(struct hda_beep *beep, int tone) { struct hda_codec *codec = beep->codec; + snd_power_wait_and_ref(codec->card, true); if (tone && !beep->playing) { snd_hda_power_up(codec); if (beep->power_hook) @@ -39,6 +40,7 @@ static void generate_tone(struct hda_beep *beep, int tone) beep->power_hook(beep, false); snd_hda_power_down(codec); } + snd_power_unref(codec->card); } static void snd_hda_generate_beep(struct work_struct *work) diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 80016b7b6849..a9768d606da2 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1064,6 +1064,15 @@ void azx_stop_chip(struct azx *chip) } EXPORT_SYMBOL_GPL(azx_stop_chip); +void azx_suspend_streams(struct azx *chip) +{ + struct azx_pcm *apcm; + + list_for_each_entry(apcm, &chip->pcm_list, list) + snd_pcm_suspend_all(apcm->pcm); +} +EXPORT_SYMBOL_GPL(azx_suspend_streams); + /* * interrupt handler */ diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 68f9668788ea..d40a5d87d34e 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -212,5 +212,6 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots); int azx_codec_configure(struct azx *chip); int azx_init_streams(struct azx *chip); void azx_free_streams(struct azx *chip); +void azx_suspend_streams(struct azx *chip); #endif /* __SOUND_HDA_CONTROLLER_H */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 125e97fe0b1c..a86adb0acf80 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -22,14 +22,17 @@ static int verb_write_ioctl(struct hda_codec *codec, struct hda_verb_ioctl __user *arg) { u32 verb, res; + int err; if (get_user(verb, &arg->verb)) return -EFAULT; + err = snd_power_wait_and_ref(codec->card, true); res = snd_hda_codec_read(codec, verb >> 24, 0, (verb >> 8) & 0xffff, verb & 0xff); if (put_user(res, &arg->res)) - return -EFAULT; - return 0; + err = -EFAULT; + snd_power_unref(codec->card); + return err; } static int get_wcap_ioctl(struct hda_codec *codec, 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); diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 00c2eeb2c472..9e753a8ad3c9 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -782,6 +782,7 @@ static void print_codec_info(struct snd_info_entry *entry, fg = codec->core.afg; if (!fg) return; + snd_power_wait_and_ref(codec->card, true); snd_hda_power_up(codec); snd_iprintf(buffer, "Default PCM:\n"); print_pcm_caps(buffer, codec, fg); @@ -796,6 +797,7 @@ static void print_codec_info(struct snd_info_entry *entry, if (! nid || nodes < 0) { snd_iprintf(buffer, "Invalid AFG subtree\n"); snd_hda_power_down(codec); + snd_power_unref(codec->card); return; } @@ -933,6 +935,7 @@ static void print_codec_info(struct snd_info_entry *entry, kfree(conn); } snd_hda_power_down(codec); + snd_power_unref(codec->card); } /* diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index d5ffcba794e5..0a213aef4124 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -118,12 +118,15 @@ static int clear_codec(struct hda_codec *codec) { int err; + snd_power_wait_and_ref(codec->card, true); err = snd_hda_codec_reset(codec); if (err < 0) { codec_err(codec, "The codec is being used, can't free.\n"); + snd_power_unref(codec->card); return err; } snd_hda_sysfs_clear(codec); + snd_power_unref(codec->card); return 0; } @@ -131,6 +134,7 @@ static int reconfig_codec(struct hda_codec *codec) { int err; + snd_power_wait_and_ref(codec->card, true); snd_hda_power_up(codec); codec_info(codec, "hda-codec: reconfiguring\n"); err = snd_hda_codec_reset(codec); @@ -145,6 +149,7 @@ static int reconfig_codec(struct hda_codec *codec) err = snd_card_register(codec->card); error: snd_hda_power_down(codec); + snd_power_unref(codec->card); return err; } |