ALSA: hda - Fix registration race of VGA switcheroo

Delay the registration of VGA switcheroo client to the end of the
probing.  Otherwise a too quick switching may result in Oops during
probing.

Also add the check of the return value from snd_hda_lock_devices().

Reported-and-tested-by: Daniel J Blueman <daniel@quora.org>
Cc: <stable@vger.kernel.org> [v3.5+]
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2012-10-12 17:28:18 +02:00
parent e73fa21b4e
commit 128960a9ad
1 changed files with 20 additions and 11 deletions

View File

@ -501,6 +501,7 @@ struct azx {
/* VGA-switcheroo setup */ /* VGA-switcheroo setup */
unsigned int use_vga_switcheroo:1; unsigned int use_vga_switcheroo:1;
unsigned int vga_switcheroo_registered:1;
unsigned int init_failed:1; /* delayed init failed */ unsigned int init_failed:1; /* delayed init failed */
unsigned int disabled:1; /* disabled by VGA-switcher */ unsigned int disabled:1; /* disabled by VGA-switcher */
@ -2640,7 +2641,9 @@ static void azx_vs_set_state(struct pci_dev *pci,
if (disabled) { if (disabled) {
azx_suspend(&pci->dev); azx_suspend(&pci->dev);
chip->disabled = true; chip->disabled = true;
snd_hda_lock_devices(chip->bus); if (snd_hda_lock_devices(chip->bus))
snd_printk(KERN_WARNING SFX
"Cannot lock devices!\n");
} else { } else {
snd_hda_unlock_devices(chip->bus); snd_hda_unlock_devices(chip->bus);
chip->disabled = false; chip->disabled = false;
@ -2683,14 +2686,20 @@ static const struct vga_switcheroo_client_ops azx_vs_ops = {
static int __devinit register_vga_switcheroo(struct azx *chip) static int __devinit register_vga_switcheroo(struct azx *chip)
{ {
int err;
if (!chip->use_vga_switcheroo) if (!chip->use_vga_switcheroo)
return 0; return 0;
/* FIXME: currently only handling DIS controller /* FIXME: currently only handling DIS controller
* is there any machine with two switchable HDMI audio controllers? * is there any machine with two switchable HDMI audio controllers?
*/ */
return vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops,
VGA_SWITCHEROO_DIS, VGA_SWITCHEROO_DIS,
chip->bus != NULL); chip->bus != NULL);
if (err < 0)
return err;
chip->vga_switcheroo_registered = 1;
return 0;
} }
#else #else
#define init_vga_switcheroo(chip) /* NOP */ #define init_vga_switcheroo(chip) /* NOP */
@ -2712,7 +2721,8 @@ static int azx_free(struct azx *chip)
if (use_vga_switcheroo(chip)) { if (use_vga_switcheroo(chip)) {
if (chip->disabled && chip->bus) if (chip->disabled && chip->bus)
snd_hda_unlock_devices(chip->bus); snd_hda_unlock_devices(chip->bus);
vga_switcheroo_unregister_client(chip->pci); if (chip->vga_switcheroo_registered)
vga_switcheroo_unregister_client(chip->pci);
} }
if (chip->initialized) { if (chip->initialized) {
@ -3060,14 +3070,6 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
} }
ok: ok:
err = register_vga_switcheroo(chip);
if (err < 0) {
snd_printk(KERN_ERR SFX
"Error registering VGA-switcheroo client\n");
azx_free(chip);
return err;
}
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
if (err < 0) { if (err < 0) {
snd_printk(KERN_ERR SFX "Error creating device [card]!\n"); snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
@ -3338,6 +3340,13 @@ static int __devinit azx_probe(struct pci_dev *pci,
if (pci_dev_run_wake(pci)) if (pci_dev_run_wake(pci))
pm_runtime_put_noidle(&pci->dev); pm_runtime_put_noidle(&pci->dev);
err = register_vga_switcheroo(chip);
if (err < 0) {
snd_printk(KERN_ERR SFX
"Error registering VGA-switcheroo client\n");
goto out_free;
}
dev++; dev++;
return 0; return 0;