ALSA: hda - Combine snd_hda_codec_flush_*_cache() to a single function
[firefly-linux-kernel-4.4.55.git] / sound / pci / hda / hda_codec.c
index 7eab3ae5ab6d336eeb092414ec8a776f621949b1..77ddd34fffedc045115c274e8bf301d996dcfa48 100644 (file)
@@ -1086,9 +1086,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
        struct hda_pincfg *pin;
 
 #ifdef CONFIG_SND_HDA_HWDEP
-       pin = look_up_pincfg(codec, &codec->user_pins, nid);
-       if (pin)
-               return pin->cfg;
+       {
+               unsigned int cfg = 0;
+               mutex_lock(&codec->user_mutex);
+               pin = look_up_pincfg(codec, &codec->user_pins, nid);
+               if (pin)
+                       cfg = pin->cfg;
+               mutex_unlock(&codec->user_mutex);
+               if (cfg)
+                       return cfg;
+       }
 #endif
        pin = look_up_pincfg(codec, &codec->driver_pins, nid);
        if (pin)
@@ -1100,6 +1107,32 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
 
+/* remember the current pinctl target value */
+int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
+                                unsigned int val)
+{
+       struct hda_pincfg *pin;
+
+       pin = look_up_pincfg(codec, &codec->init_pins, nid);
+       if (!pin)
+               return -EINVAL;
+       pin->target = val;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_set_pin_target);
+
+/* return the current pinctl target value */
+int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hda_pincfg *pin;
+
+       pin = look_up_pincfg(codec, &codec->init_pins, nid);
+       if (!pin)
+               return 0;
+       return pin->target;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_get_pin_target);
+
 /**
  * snd_hda_shutup_pins - Shut up all pins
  * @codec: the HDA codec
@@ -1493,7 +1526,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
                    "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
                    nid, stream_tag, channel_id, format);
        p = get_hda_cvt_setup(codec, nid);
-       if (!p)
+       if (!p || p->active)
                return;
 
        if (codec->pcm_format_first)
@@ -1540,7 +1573,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
 
        snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
        p = get_hda_cvt_setup(codec, nid);
-       if (p) {
+       if (p && p->active) {
                /* here we just clear the active flag when do_now isn't set;
                 * actual clean-ups will be done later in
                 * purify_inactive_streams() called from snd_hda_codec_prpapre()
@@ -3604,6 +3637,17 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
 
+/**
+ * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
+ * @codec: HD-audio codec
+ */
+void snd_hda_codec_flush_cache(struct hda_codec *codec)
+{
+       snd_hda_codec_resume_amp(codec);
+       snd_hda_codec_resume_cache(codec);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache);
+
 void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
                                    unsigned int power_state,
                                    bool eapd_workaround)
@@ -3761,7 +3805,7 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
        }
        for (i = 0; i < codec->amp_cache.buf.used; i++) {
                struct hda_amp_info *amp;
-               amp = snd_array_elem(&codec->cmd_cache.buf, i);
+               amp = snd_array_elem(&codec->amp_cache.buf, i);
                amp->head.dirty = 1;
        }
 }
@@ -5249,23 +5293,62 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
 
-int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
-                        unsigned int val, bool cached)
+/* correct the pin ctl value for matching with the pin cap */
+unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
+                                    hda_nid_t pin, unsigned int val)
 {
-       if (val) {
-               unsigned int cap = snd_hda_query_pin_caps(codec, pin);
-               if (cap && (val & AC_PINCTL_OUT_EN)) {
-                       if (!(cap & AC_PINCAP_OUT))
-                               val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
-                       else if ((val & AC_PINCTL_HP_EN) &&
-                                !(cap & AC_PINCAP_HP_DRV))
-                               val &= ~AC_PINCTL_HP_EN;
-               }
-               if (cap && (val & AC_PINCTL_IN_EN)) {
-                       if (!(cap & AC_PINCAP_IN))
-                               val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+       static unsigned int cap_lists[][2] = {
+               { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
+               { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
+               { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
+               { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
+       };
+       unsigned int cap;
+
+       if (!val)
+               return 0;
+       cap = snd_hda_query_pin_caps(codec, pin);
+       if (!cap)
+               return val; /* don't know what to do... */
+
+       if (val & AC_PINCTL_OUT_EN) {
+               if (!(cap & AC_PINCAP_OUT))
+                       val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+               else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
+                       val &= ~AC_PINCTL_HP_EN;
+       }
+
+       if (val & AC_PINCTL_IN_EN) {
+               if (!(cap & AC_PINCAP_IN))
+                       val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+               else {
+                       unsigned int vcap, vref;
+                       int i;
+                       vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+                       vref = val & AC_PINCTL_VREFEN;
+                       for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
+                               if (vref == cap_lists[i][0] &&
+                                   !(vcap & cap_lists[i][1])) {
+                                       if (i == ARRAY_SIZE(cap_lists) - 1)
+                                               vref = AC_PINCTL_VREF_HIZ;
+                                       else
+                                               vref = cap_lists[i + 1][0];
+                               }
+                       }
+                       val &= ~AC_PINCTL_VREFEN;
+                       val |= vref;
                }
        }
+
+       return val;
+}
+EXPORT_SYMBOL_HDA(snd_hda_correct_pin_ctl);
+
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int val, bool cached)
+{
+       val = snd_hda_correct_pin_ctl(codec, pin, val);
+       snd_hda_codec_set_pin_target(codec, pin, val);
        if (cached)
                return snd_hda_codec_update_cache(codec, pin, 0,
                                AC_VERB_SET_PIN_WIDGET_CONTROL, val);