summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2021-03-16 23:01:02 -0400
committerAndrey Grodzovsky <andrey.grodzovsky@amd.com>2021-03-29 10:34:14 -0400
commite36365d9ab5bbc30bdc221ab4b3437de34492440 (patch)
tree406b696531466d2c99c20badad1c259daa309bae
parent553390b067bf77062fbc86a57246c5d37445eba5 (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.c2
-rw-r--r--sound/pci/hda/hda_controller.c9
-rw-r--r--sound/pci/hda/hda_controller.h1
-rw-r--r--sound/pci/hda/hda_hwdep.c7
-rw-r--r--sound/pci/hda/hda_intel.c115
-rw-r--r--sound/pci/hda/hda_proc.c3
-rw-r--r--sound/pci/hda/hda_sysfs.c5
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;
}