Merge branch 'for-linus' into for-next
[firefly-linux-kernel-4.4.55.git] / sound / pci / hda / hda_codec.c
index 822df971972c1ff54ea7886a3524356ff92fca50..e4e0501ab84a3c8bf2ffa37613191cc68541e980 100644 (file)
@@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
  again:
        snd_hda_power_up(codec);
        mutex_lock(&bus->cmd_mutex);
-       trace_hda_send_cmd(codec, cmd);
-       err = bus->ops.command(bus, cmd);
+       for (;;) {
+               trace_hda_send_cmd(codec, cmd);
+               err = bus->ops.command(bus, cmd);
+               if (err != -EAGAIN)
+                       break;
+               /* process pending verbs */
+               bus->ops.get_response(bus, codec->addr);
+       }
        if (!err && res) {
                *res = bus->ops.get_response(bus, codec->addr);
                trace_hda_get_response(codec, *res);
@@ -328,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 
+/* connection list element */
+struct hda_conn_list {
+       struct list_head list;
+       int len;
+       hda_nid_t nid;
+       hda_nid_t conns[0];
+};
+
 /* look up the cached results */
-static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
+static struct hda_conn_list *
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
 {
-       int i, len;
-       for (i = 0; i < array->used; ) {
-               hda_nid_t *p = snd_array_elem(array, i);
-               if (nid == *p)
+       struct hda_conn_list *p;
+       list_for_each_entry(p, &codec->conn_list, list) {
+               if (p->nid == nid)
                        return p;
-               len = p[1];
-               i += len + 2;
        }
        return NULL;
 }
 
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+                        const hda_nid_t *list)
+{
+       struct hda_conn_list *p;
+
+       p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+       p->len = len;
+       p->nid = nid;
+       memcpy(p->conns, list, len * sizeof(hda_nid_t));
+       list_add(&p->list, &codec->conn_list);
+       return 0;
+}
+
+static void remove_conn_list(struct hda_codec *codec)
+{
+       while (!list_empty(&codec->conn_list)) {
+               struct hda_conn_list *p;
+               p = list_first_entry(&codec->conn_list, typeof(*p), list);
+               list_del(&p->list);
+               kfree(p);
+       }
+}
+
 /* read the connection and add to the cache */
 static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
 {
@@ -354,6 +391,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
        return snd_hda_override_conn_list(codec, nid, len, list);
 }
 
+/**
+ * snd_hda_get_conn_list - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ *
+ * Note that the returned pointer isn't protected against the list
+ * modification.  If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
+ */
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+                         const hda_nid_t **listp)
+{
+       bool added = false;
+
+       for (;;) {
+               int err;
+               const struct hda_conn_list *p;
+
+               /* if the connection-list is already cached, read it */
+               p = lookup_conn_list(codec, nid);
+               if (p) {
+                       if (listp)
+                               *listp = p->conns;
+                       return p->len;
+               }
+               if (snd_BUG_ON(added))
+                       return -EINVAL;
+
+               err = read_and_add_raw_conns(codec, nid);
+               if (err < 0)
+                       return err;
+               added = true;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
+
 /**
  * snd_hda_get_connections - copy connection list
  * @codec: the HDA codec
@@ -369,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns)
 {
-       struct snd_array *array = &codec->conn_lists;
-       int len;
-       hda_nid_t *p;
-       bool added = false;
+       const hda_nid_t *list;
+       int len = snd_hda_get_conn_list(codec, nid, &list);
 
- again:
-       mutex_lock(&codec->hash_mutex);
-       len = -1;
-       /* if the connection-list is already cached, read it */
-       p = lookup_conn_list(array, nid);
-       if (p) {
-               len = p[1];
-               if (conn_list && len > max_conns) {
+       if (len > 0 && conn_list) {
+               if (len > max_conns) {
                        snd_printk(KERN_ERR "hda_codec: "
                                   "Too many connections %d for NID 0x%x\n",
                                   len, nid);
-                       mutex_unlock(&codec->hash_mutex);
                        return -EINVAL;
                }
-               if (conn_list && len)
-                       memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
+               memcpy(conn_list, list, len * sizeof(hda_nid_t));
        }
-       mutex_unlock(&codec->hash_mutex);
-       if (len >= 0)
-               return len;
-       if (snd_BUG_ON(added))
-               return -EINVAL;
 
-       len = read_and_add_raw_conns(codec, nid);
-       if (len < 0)
-               return len;
-       added = true;
-       goto again;
+       return len;
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
@@ -424,6 +485,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
        unsigned int shift, num_elems, mask;
        unsigned int wcaps;
        hda_nid_t prev_nid;
+       int null_count = 0;
 
        if (snd_BUG_ON(!conn_list || max_conns <= 0))
                return -EINVAL;
@@ -474,7 +536,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
                }
                range_val = !!(parm & (1 << (shift-1))); /* ranges */
                val = parm & mask;
-               if (val == 0) {
+               if (val == 0 && null_count++) {  /* no second chance */
                        snd_printk(KERN_WARNING "hda_codec: "
                                   "invalid CONNECT_LIST verb %x[%i]:%x\n",
                                    nid, i, parm);
@@ -512,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
        return conns;
 }
 
-static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
-{
-       hda_nid_t *p = snd_array_new(array);
-       if (!p)
-               return false;
-       *p = nid;
-       return true;
-}
-
 /**
  * snd_hda_override_conn_list - add/modify the connection-list to cache
  * @codec: the HDA codec
@@ -536,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
 int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
                               const hda_nid_t *list)
 {
-       struct snd_array *array = &codec->conn_lists;
-       hda_nid_t *p;
-       int i, old_used;
+       struct hda_conn_list *p;
 
-       mutex_lock(&codec->hash_mutex);
-       p = lookup_conn_list(array, nid);
-       if (p)
-               *p = -1; /* invalidate the old entry */
-
-       old_used = array->used;
-       if (!add_conn_list(array, nid) || !add_conn_list(array, len))
-               goto error_add;
-       for (i = 0; i < len; i++)
-               if (!add_conn_list(array, list[i]))
-                       goto error_add;
-       mutex_unlock(&codec->hash_mutex);
-       return 0;
+       p = lookup_conn_list(codec, nid);
+       if (p) {
+               list_del(&p->list);
+               kfree(p);
+       }
 
- error_add:
-       array->used = old_used;
-       mutex_unlock(&codec->hash_mutex);
-       return -ENOMEM;
+       return add_conn_list(codec, nid, len, list);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
 
@@ -575,16 +615,16 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
                           hda_nid_t nid, int recursive)
 {
-       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+       const hda_nid_t *conn;
        int i, nums;
 
-       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+       nums = snd_hda_get_conn_list(codec, mux, &conn);
        for (i = 0; i < nums; i++)
                if (conn[i] == nid)
                        return i;
        if (!recursive)
                return -1;
-       if (recursive > 5) {
+       if (recursive > 10) {
                snd_printd("hda_codec: too deep connection for 0x%x\n", nid);
                return -1;
        }
@@ -1046,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)
@@ -1060,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
@@ -1179,8 +1252,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        snd_array_free(&codec->mixers);
        snd_array_free(&codec->nids);
        snd_array_free(&codec->cvt_setups);
-       snd_array_free(&codec->conn_lists);
        snd_array_free(&codec->spdif_out);
+       remove_conn_list(codec);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
@@ -1250,9 +1323,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
        snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
-       snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
        snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
        snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
+       snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
+       INIT_LIST_HEAD(&codec->conn_list);
+
        INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
 
 #ifdef CONFIG_PM
@@ -1451,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)
@@ -1498,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()
@@ -1610,6 +1685,7 @@ static struct hda_cache_head  *get_alloc_hash(struct hda_cache_rec *cache,
                cur = snd_array_index(&cache->buf, info);
                info->key = key;
                info->val = 0;
+               info->dirty = 0;
                idx = key % (u16)ARRAY_SIZE(cache->hash);
                info->next = cache->hash[idx];
                cache->hash[idx] = cur;
@@ -1764,7 +1840,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
  */
 static struct hda_amp_info *
 update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
-               int direction, int index)
+               int direction, int index, bool init_only)
 {
        struct hda_amp_info *info;
        unsigned int parm, val = 0;
@@ -1790,14 +1866,15 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
                }
                info->vol[ch] = val;
                info->head.val |= INFO_AMP_VOL(ch);
-       }
+       } else if (init_only)
+               return NULL;
        return info;
 }
 
 /*
  * write the current volume in info to the h/w
  */
-static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
                         hda_nid_t nid, int ch, int direction, int index,
                         int val)
 {
@@ -1806,8 +1883,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
        parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
        parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
        parm |= index << AC_AMP_SET_INDEX_SHIFT;
-       if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) &&
-           (info->amp_caps & AC_AMPCAP_MIN_MUTE))
+       if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
+           (amp_caps & AC_AMPCAP_MIN_MUTE))
                ; /* set the zero value as a fake mute */
        else
                parm |= val;
@@ -1831,7 +1908,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
        unsigned int val = 0;
 
        mutex_lock(&codec->hash_mutex);
-       info = update_amp_hash(codec, nid, ch, direction, index);
+       info = update_amp_hash(codec, nid, ch, direction, index, false);
        if (info)
                val = info->vol[ch];
        mutex_unlock(&codec->hash_mutex);
@@ -1839,30 +1916,20 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
-/**
- * snd_hda_codec_amp_update - update the AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP value with a bit mask.
- * Returns 0 if the value is unchanged, 1 if changed.
- */
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
-                            int direction, int idx, int mask, int val)
+static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+                           int direction, int idx, int mask, int val,
+                           bool init_only)
 {
        struct hda_amp_info *info;
+       unsigned int caps;
+       unsigned int cache_only;
 
        if (snd_BUG_ON(mask & ~0xff))
                mask &= 0xff;
        val &= mask;
 
        mutex_lock(&codec->hash_mutex);
-       info = update_amp_hash(codec, nid, ch, direction, idx);
+       info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
        if (!info) {
                mutex_unlock(&codec->hash_mutex);
                return 0;
@@ -1873,10 +1940,32 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
                return 0;
        }
        info->vol[ch] = val;
+       cache_only = info->head.dirty = codec->cached_write;
+       caps = info->amp_caps;
        mutex_unlock(&codec->hash_mutex);
-       put_vol_mute(codec, info, nid, ch, direction, idx, val);
+       if (!cache_only)
+               put_vol_mute(codec, caps, nid, ch, direction, idx, val);
        return 1;
 }
+
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+                            int direction, int idx, int mask, int val)
+{
+       return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false);
+}
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
 /**
@@ -1905,7 +1994,31 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
-#ifdef CONFIG_PM
+/* Works like snd_hda_codec_amp_update() but it writes the value only at
+ * the first access.  If the amp was already initialized / updated beforehand,
+ * this does nothing.
+ */
+int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
+                          int dir, int idx, int mask, int val)
+{
+       return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true);
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init);
+
+int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
+                                 int dir, int idx, int mask, int val)
+{
+       int ch, ret = 0;
+
+       if (snd_BUG_ON(mask & ~0xff))
+               mask &= 0xff;
+       for (ch = 0; ch < 2; ch++)
+               ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
+                                             idx, mask, val);
+       return ret;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo);
+
 /**
  * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
  * @codec: HD-audio codec
@@ -1914,28 +2027,40 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
  */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
-       struct hda_amp_info *buffer = codec->amp_cache.buf.list;
        int i;
 
-       for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
-               u32 key = buffer->head.key;
+       mutex_lock(&codec->hash_mutex);
+       codec->cached_write = 0;
+       for (i = 0; i < codec->amp_cache.buf.used; i++) {
+               struct hda_amp_info *buffer;
+               u32 key;
                hda_nid_t nid;
                unsigned int idx, dir, ch;
+               struct hda_amp_info info;
+
+               buffer = snd_array_elem(&codec->amp_cache.buf, i);
+               if (!buffer->head.dirty)
+                       continue;
+               buffer->head.dirty = 0;
+               info = *buffer;
+               key = info.head.key;
                if (!key)
                        continue;
                nid = key & 0xff;
                idx = (key >> 16) & 0xff;
                dir = (key >> 24) & 0xff;
                for (ch = 0; ch < 2; ch++) {
-                       if (!(buffer->head.val & INFO_AMP_VOL(ch)))
+                       if (!(info.head.val & INFO_AMP_VOL(ch)))
                                continue;
-                       put_vol_mute(codec, buffer, nid, ch, dir, idx,
-                                    buffer->vol[ch]);
+                       mutex_unlock(&codec->hash_mutex);
+                       put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
+                                    info.vol[ch]);
+                       mutex_lock(&codec->hash_mutex);
                }
        }
+       mutex_unlock(&codec->hash_mutex);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* CONFIG_PM */
 
 static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
                             unsigned int ofs)
@@ -2362,6 +2487,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        snd_array_free(&codec->driver_pins);
        snd_array_free(&codec->cvt_setups);
        snd_array_free(&codec->spdif_out);
+       snd_array_free(&codec->verbs);
        codec->num_pcms = 0;
        codec->pcm_info = NULL;
        codec->preset = NULL;
@@ -3375,12 +3501,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 
-#ifdef CONFIG_PM
 /*
  * command cache
  */
 
-/* build a 32bit cache key with the widget id and the command parameter */
+/* build a 31bit cache key with the widget id and the command parameter */
 #define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
 #define get_cmd_cache_nid(key)         ((key) & 0xff)
 #define get_cmd_cache_cmd(key)         (((key) >> 8) & 0xffff)
@@ -3400,20 +3525,28 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
                              int direct, unsigned int verb, unsigned int parm)
 {
-       int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+       int err;
        struct hda_cache_head *c;
        u32 key;
+       unsigned int cache_only;
+
+       cache_only = codec->cached_write;
+       if (!cache_only) {
+               err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+               if (err < 0)
+                       return err;
+       }
 
-       if (err < 0)
-               return err;
        /* parm may contain the verb stuff for get/set amp */
        verb = verb | (parm >> 8);
        parm &= 0xff;
        key = build_cmd_cache_key(nid, verb);
        mutex_lock(&codec->bus->cmd_mutex);
        c = get_alloc_hash(&codec->cmd_cache, key);
-       if (c)
+       if (c) {
                c->val = parm;
+               c->dirty = cache_only;
+       }
        mutex_unlock(&codec->bus->cmd_mutex);
        return 0;
 }
@@ -3462,16 +3595,27 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
  */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
-       struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
        int i;
 
-       for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
-               u32 key = buffer->key;
+       mutex_lock(&codec->hash_mutex);
+       codec->cached_write = 0;
+       for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+               struct hda_cache_head *buffer;
+               u32 key;
+
+               buffer = snd_array_elem(&codec->cmd_cache.buf, i);
+               key = buffer->key;
                if (!key)
                        continue;
+               if (!buffer->dirty)
+                       continue;
+               buffer->dirty = 0;
+               mutex_unlock(&codec->hash_mutex);
                snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
                                    get_cmd_cache_cmd(key), buffer->val);
+               mutex_lock(&codec->hash_mutex);
        }
+       mutex_unlock(&codec->hash_mutex);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
 
@@ -3492,7 +3636,17 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
                                          seq->param);
 }
 EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* CONFIG_PM */
+
+/**
+ * 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,
@@ -3640,6 +3794,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
        return state;
 }
 
+/* mark all entries of cmd and amp caches dirty */
+static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
+{
+       int i;
+       for (i = 0; i < codec->cmd_cache.buf.used; i++) {
+               struct hda_cache_head *cmd;
+               cmd = snd_array_elem(&codec->cmd_cache.buf, i);
+               cmd->dirty = 1;
+       }
+       for (i = 0; i < codec->amp_cache.buf.used; i++) {
+               struct hda_amp_info *amp;
+               amp = snd_array_elem(&codec->amp_cache.buf, i);
+               amp->head.dirty = 1;
+       }
+}
+
 /*
  * kick up codec; used both from PM and power-save
  */
@@ -3647,6 +3817,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
 {
        codec->in_pm = 1;
 
+       hda_mark_cmd_cache_dirty(codec);
+
        /* set as if powered on for avoiding re-entering the resume
         * in the resume / power-save sequence
         */
@@ -5120,23 +5292,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);