ALSA: hda - Fix possible runtime PM refcount unbalance
authorTakashi Iwai <tiwai@suse.de>
Tue, 3 Mar 2015 16:22:12 +0000 (17:22 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 23 Mar 2015 12:17:48 +0000 (13:17 +0100)
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>
sound/hda/hdac_device.c
sound/pci/hda/hda_bind.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_local.h

index aaece36247e772f126a86d7dae89c6eae4c6612b..6e8ee1d6974a2322fb9eb94b7ffe8f1bdb4625d7 100644 (file)
@@ -109,7 +109,6 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
        return 0;
 
  error:
-       pm_runtime_put_noidle(&codec->dev);
        put_device(&codec->dev);
        return err;
 }
@@ -121,7 +120,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_device_init);
  */
 void snd_hdac_device_exit(struct hdac_device *codec)
 {
-       /* pm_runtime_put_noidle(&codec->dev); */
+       pm_runtime_put_noidle(&codec->dev);
        snd_hdac_bus_remove_device(codec->bus, codec);
        kfree(codec->vendor_name);
        kfree(codec->chip_name);
index 130f672e6f37b2a913471fbc69d3ae4d06d6e228..7b269c3237e3cdc1da0cf7efd68d3ea9378ade7f 100644 (file)
@@ -95,6 +95,7 @@ static int hda_codec_driver_probe(struct device *dev)
                err = snd_card_register(codec->card);
                if (err < 0)
                        goto error_module;
+               snd_hda_codec_register(codec);
        }
 
        return 0;
index 36483f7dd3ce7ed7cbfabc05202ca344fa8aedc7..145cae7903b61956f6fc01ad03e4b1fbf202295e 100644 (file)
@@ -912,6 +912,13 @@ static void codec_release_pcms(struct hda_codec *codec)
 
 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 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
 static unsigned int hda_set_power_state(struct hda_codec *codec,
                                unsigned int power_state);
 
-static int snd_hda_codec_dev_register(struct snd_device *device)
+/* also called from hda_bind.c */
+void snd_hda_codec_register(struct hda_codec *codec)
 {
-       struct hda_codec *codec = device->device_data;
-
-       snd_hda_register_beep_device(codec);
-       if (device_is_registered(hda_codec_dev(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);
+               /* 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)
+{
+       snd_hda_codec_register(device->device_data);
        return 0;
 }
 
@@ -1094,7 +1109,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
        return 0;
 
  error:
-       pm_runtime_put_noidle(hda_codec_dev(codec));
        put_device(hda_codec_dev(codec));
        return err;
 }
index e7c47a4397628073a34d42a1e14159b1f2822ffa..76776164623de006f7c48ca66489594d1e703543 100644 (file)
@@ -314,6 +314,7 @@ struct hda_codec {
 
        /* 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)
index e0db30c66e5fcc0949724cc091a69bf0f95fa3c1..8a83775e0e273566bd131097795c1868fdc99e57 100644 (file)
@@ -150,6 +150,7 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 #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 {