ALSA: hda - Fix possible runtime PM refcount unbalance
When the driver is unloaded before the codec is bound, it still keeps
the runtime PM refcount up, and results in the unbalance. This patch
covers these cases by introducing a flag indicating the runtime PM
initialization and handling the codec registration procedure more
properly. It also fixes the missing input beep device as a gratis,
too.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c
index 130f672..7b269c3 100644
--- a/sound/pci/hda/hda_bind.c
+++ b/sound/pci/hda/hda_bind.c
@@ -95,6 +95,7 @@
err = snd_card_register(codec->card);
if (err < 0)
goto error_module;
+ snd_hda_codec_register(codec);
}
return 0;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 36483f7..145cae7 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -912,6 +912,13 @@
void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
{
+ if (codec->registered) {
+ /* pm_runtime_put() is called in snd_hdac_device_exit() */
+ pm_runtime_get_noresume(hda_codec_dev(codec));
+ pm_runtime_disable(hda_codec_dev(codec));
+ codec->registered = 0;
+ }
+
cancel_delayed_work_sync(&codec->jackpoll_work);
if (!codec->in_freeing)
snd_hda_ctls_clear(codec);
@@ -943,15 +950,23 @@
static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state);
+/* also called from hda_bind.c */
+void snd_hda_codec_register(struct hda_codec *codec)
+{
+ if (codec->registered)
+ return;
+ if (device_is_registered(hda_codec_dev(codec))) {
+ snd_hda_register_beep_device(codec);
+ pm_runtime_enable(hda_codec_dev(codec));
+ /* it was powered up in snd_hda_codec_new(), now all done */
+ snd_hda_power_down(codec);
+ codec->registered = 1;
+ }
+}
+
static int snd_hda_codec_dev_register(struct snd_device *device)
{
- struct hda_codec *codec = device->device_data;
-
- snd_hda_register_beep_device(codec);
- if (device_is_registered(hda_codec_dev(codec)))
- pm_runtime_enable(hda_codec_dev(codec));
- /* it was powered up in snd_hda_codec_new(), now all done */
- snd_hda_power_down(codec);
+ snd_hda_codec_register(device->device_data);
return 0;
}
@@ -1094,7 +1109,6 @@
return 0;
error:
- pm_runtime_put_noidle(hda_codec_dev(codec));
put_device(hda_codec_dev(codec));
return err;
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index e7c47a4..7677616 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -314,6 +314,7 @@
/* misc flags */
unsigned int in_freeing:1; /* being released */
+ unsigned int registered:1; /* codec was registered */
unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
* status change
* (e.g. Realtek codecs)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index e0db30c..8a83775 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -150,6 +150,7 @@
#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \
__snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL)
int snd_hda_codec_reset(struct hda_codec *codec);
+void snd_hda_codec_register(struct hda_codec *codec);
void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec);
enum {