[ALSA] hda-intel - Improve HD-audio codec probing robustness
[firefly-linux-kernel-4.4.55.git] / sound / pci / hda / hda_codec.c
index fc1ddf408b9ba7de6d42c56dfa0ffbc74afaeaf8..535bcb7601b5bc3dd580ad7ca1ff4a422f10bf06 100644 (file)
@@ -155,6 +155,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
        unsigned int parm;
 
        parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
+       if (parm == -1)
+               return 0;
        *start_id = (parm >> 16) & 0x7fff;
        return (int)(parm & 0x7fff);
 }
@@ -514,7 +516,7 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node)
 
 static void init_hda_cache(struct hda_cache_rec *cache,
                           unsigned int record_size);
-static inline void free_hda_cache(struct hda_cache_rec *cache);
+static void free_hda_cache(struct hda_cache_rec *cache);
 
 /*
  * codec destructor
@@ -525,6 +527,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
                return;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
+       flush_scheduled_work();
 #endif
        list_del(&codec->list);
        codec->bus->caddr_tbl[codec->addr] = NULL;
@@ -706,7 +709,7 @@ static void __devinit init_hda_cache(struct hda_cache_rec *cache,
        cache->record_size = record_size;
 }
 
-static inline void free_hda_cache(struct hda_cache_rec *cache)
+static void free_hda_cache(struct hda_cache_rec *cache)
 {
        kfree(cache->buffer);
 }
@@ -1663,6 +1666,7 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        cancel_delayed_work(&codec->power_work);
        codec->power_on = 0;
+       codec->power_transition = 0;
 #endif
 }
 
@@ -2193,8 +2197,10 @@ static void hda_power_work(struct work_struct *work)
        struct hda_codec *codec =
                container_of(work, struct hda_codec, power_work.work);
 
-       if (!codec->power_on || codec->power_count)
+       if (!codec->power_on || codec->power_count) {
+               codec->power_transition = 0;
                return;
+       }
 
        hda_call_codec_suspend(codec);
        if (codec->bus->ops.pm_notify)
@@ -2210,7 +2216,7 @@ static void hda_keep_power_on(struct hda_codec *codec)
 void snd_hda_power_up(struct hda_codec *codec)
 {
        codec->power_count++;
-       if (codec->power_on)
+       if (codec->power_on || codec->power_transition)
                return;
 
        codec->power_on = 1;
@@ -2218,16 +2224,19 @@ void snd_hda_power_up(struct hda_codec *codec)
                codec->bus->ops.pm_notify(codec);
        hda_call_codec_resume(codec);
        cancel_delayed_work(&codec->power_work);
+       codec->power_transition = 0;
 }
 
 void snd_hda_power_down(struct hda_codec *codec)
 {
        --codec->power_count;
-       if (!codec->power_on)
+       if (!codec->power_on || codec->power_count || codec->power_transition)
                return;
-       if (power_save)
+       if (power_save) {
+               codec->power_transition = 1; /* avoid reentrance */
                schedule_delayed_work(&codec->power_work,
                                      msecs_to_jiffies(power_save * 1000));
+       }
 }
 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
@@ -2771,12 +2780,15 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               if (!codec->power_on)
+                       continue;
+#endif
                hda_call_codec_suspend(codec);
        }
        return 0;
 }
 
-#ifndef CONFIG_SND_HDA_POWER_SAVE
 /**
  * snd_hda_resume - resume the codecs
  * @bus: the HDA bus
@@ -2792,10 +2804,21 @@ int snd_hda_resume(struct hda_bus *bus)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               hda_call_codec_resume(codec);
+               if (snd_hda_codec_needs_resume(codec))
+                       hda_call_codec_resume(codec);
        }
        return 0;
 }
-#endif /* !CONFIG_SND_HDA_POWER_SAVE */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+int snd_hda_codecs_inuse(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
 
+       list_for_each_entry(codec, &bus->codec_list, list) {
+               if (snd_hda_codec_needs_resume(codec))
+                       return 1;
+       }
+       return 0;
+}
+#endif
 #endif