ALSA: hda/tegra - async probe for avoiding module loading deadlock
The Tegra HD-audio controller driver causes deadlocks when loaded as a module since the driver invokes request_module() at binding with the codec driver. This patch works around it by deferring the probe in a work like Intel HD-audio controller driver does. Although hovering the codec probe stuff into udev would be a better solution, it may cause other regressions, so let's try this band-aid fix until the more proper solution gets landed. Reported-by: Thierry Reding <treding@nvidia.com> Tested-by: Thierry Reding <treding@nvidia.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
1f93e4a96c
commit
83510441bc
|
@ -73,6 +73,7 @@ struct hda_tegra {
|
||||||
struct clk *hda2codec_2x_clk;
|
struct clk *hda2codec_2x_clk;
|
||||||
struct clk *hda2hdmi_clk;
|
struct clk *hda2hdmi_clk;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
|
struct work_struct probe_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
@ -294,7 +295,9 @@ static int hda_tegra_dev_disconnect(struct snd_device *device)
|
||||||
static int hda_tegra_dev_free(struct snd_device *device)
|
static int hda_tegra_dev_free(struct snd_device *device)
|
||||||
{
|
{
|
||||||
struct azx *chip = device->device_data;
|
struct azx *chip = device->device_data;
|
||||||
|
struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip);
|
||||||
|
|
||||||
|
cancel_work_sync(&hda->probe_work);
|
||||||
if (azx_bus(chip)->chip_init) {
|
if (azx_bus(chip)->chip_init) {
|
||||||
azx_stop_all_streams(chip);
|
azx_stop_all_streams(chip);
|
||||||
azx_stop_chip(chip);
|
azx_stop_chip(chip);
|
||||||
|
@ -426,6 +429,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
|
||||||
/*
|
/*
|
||||||
* constructor
|
* constructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void hda_tegra_probe_work(struct work_struct *work);
|
||||||
|
|
||||||
static int hda_tegra_create(struct snd_card *card,
|
static int hda_tegra_create(struct snd_card *card,
|
||||||
unsigned int driver_caps,
|
unsigned int driver_caps,
|
||||||
struct hda_tegra *hda)
|
struct hda_tegra *hda)
|
||||||
|
@ -452,6 +458,8 @@ static int hda_tegra_create(struct snd_card *card,
|
||||||
chip->single_cmd = false;
|
chip->single_cmd = false;
|
||||||
chip->snoop = true;
|
chip->snoop = true;
|
||||||
|
|
||||||
|
INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
|
||||||
|
|
||||||
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
|
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
@ -499,6 +507,21 @@ static int hda_tegra_probe(struct platform_device *pdev)
|
||||||
card->private_data = chip;
|
card->private_data = chip;
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, card);
|
dev_set_drvdata(&pdev->dev, card);
|
||||||
|
schedule_work(&hda->probe_work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
snd_card_free(card);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hda_tegra_probe_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work);
|
||||||
|
struct azx *chip = &hda->chip;
|
||||||
|
struct platform_device *pdev = to_platform_device(hda->dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
err = hda_tegra_first_init(chip, pdev);
|
err = hda_tegra_first_init(chip, pdev);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
@ -520,11 +543,8 @@ static int hda_tegra_probe(struct platform_device *pdev)
|
||||||
chip->running = 1;
|
chip->running = 1;
|
||||||
snd_hda_set_power_save(&chip->bus, power_save * 1000);
|
snd_hda_set_power_save(&chip->bus, power_save * 1000);
|
||||||
|
|
||||||
return 0;
|
out_free:
|
||||||
|
return; /* no error return from async probe */
|
||||||
out_free:
|
|
||||||
snd_card_free(card);
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hda_tegra_remove(struct platform_device *pdev)
|
static int hda_tegra_remove(struct platform_device *pdev)
|
||||||
|
|
Loading…
Reference in a new issue