diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 4cbbb1744740..285f7c4d262d 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -97,6 +97,7 @@ struct hdac_device { bool caps_overwriting:1; /* caps overwrite being in process */ bool cache_coef:1; /* cache COEF read/write too */ unsigned int registered:1; /* codec was registered */ + bool unsol_disabled; /* unsol events blocked; protected by bus->reg_lock */ }; /* device/driver type used for matching */ @@ -123,6 +124,7 @@ int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, const char *name, unsigned int addr); void snd_hdac_device_exit(struct hdac_device *dev); int snd_hdac_device_register(struct hdac_device *codec); +void snd_hdac_device_disable_unsol(struct hdac_device *codec); void snd_hdac_device_unregister(struct hdac_device *codec); int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name); int snd_hdac_codec_modalias(const struct hdac_device *hdac, char *buf, size_t size); diff --git a/sound/hda/common/bind.c b/sound/hda/common/bind.c index 6a728a773556..4772ca154a29 100644 --- a/sound/hda/common/bind.c +++ b/sound/hda/common/bind.c @@ -100,6 +100,9 @@ static int hda_codec_driver_probe(struct device *dev) if (WARN_ON(!codec->preset)) return -EINVAL; + /* unsol events are still blocked until registered */ + codec->core.unsol_disabled = false; + err = snd_hda_codec_set_name(codec, codec->preset->name); if (err < 0) goto error; @@ -160,6 +163,10 @@ static int hda_codec_driver_remove(struct device *dev) return codec->bus->core.ext_ops->hdev_detach(&codec->core); } + /* stop asynchronous jack handling before freeing driver resources */ + snd_hdac_device_disable_unsol(&codec->core); + cancel_delayed_work_sync(&codec->jackpoll_work); + snd_hda_codec_disconnect_pcms(codec); snd_hda_jack_tbl_disconnect(codec); snd_refcount_sync(&codec->pcm_ref); diff --git a/sound/hda/core/bus.c b/sound/hda/core/bus.c index 20fe1c4a2977..8d4ed9834aaa 100644 --- a/sound/hda/core/bus.c +++ b/sound/hda/core/bus.c @@ -180,7 +180,7 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work) if (!(caddr & (1 << 4))) /* no unsolicited event? */ continue; codec = bus->caddr_tbl[caddr & 0x0f]; - if (!codec || !codec->registered) + if (!codec || !codec->registered || codec->unsol_disabled) continue; spin_unlock_irq(&bus->reg_lock); drv = drv_to_hdac_driver(codec->dev.driver); @@ -191,6 +191,25 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work) spin_unlock_irq(&bus->reg_lock); } +/** + * snd_hdac_device_disable_unsol - block and flush unsol events for the codec + * @codec: the HDA core device + * + * Stop dispatching the unsolicited events to the given codec, and wait for + * the pending unsol event handler to finish. Called at unbinding the codec + * driver before releasing the driver resources. + */ +void snd_hdac_device_disable_unsol(struct hdac_device *codec) +{ + struct hdac_bus *bus = codec->bus; + + spin_lock_irq(&bus->reg_lock); + codec->unsol_disabled = true; + spin_unlock_irq(&bus->reg_lock); + flush_work(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_device_disable_unsol); + /** * snd_hdac_bus_add_device - Add a codec to bus * @bus: HDA core bus