]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - sound/pci/hda/hda_generic.c
ALSA: hda - Improve debug prints for output paths
[karo-tx-linux.git] / sound / pci / hda / hda_generic.c
index a5c4bc05d16f4bf8c3c998c0f5902599f2fb8278..37d7ed7af2a5c7ab8c2d98e8858372e63b8cd13b 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/sort.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include "hda_codec.h"
@@ -37,8 +40,8 @@
 int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
 {
        snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
-       snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
        snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
+       mutex_init(&spec->pcm_mutex);
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
@@ -72,52 +75,147 @@ static void free_kctls(struct hda_gen_spec *spec)
        snd_array_free(&spec->kctls);
 }
 
-static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec,
-                                         unsigned int nums,
-                                         struct hda_ctl_ops *ops)
+void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
+{
+       if (!spec)
+               return;
+       free_kctls(spec);
+       snd_array_free(&spec->paths);
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
+
+/*
+ * store user hints
+ */
+static void parse_user_hints(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
-       struct hda_bind_ctls **ctlp, *ctl;
-       ctlp = snd_array_new(&spec->bind_ctls);
-       if (!ctlp)
-               return NULL;
-       ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL);
-       *ctlp = ctl;
-       if (ctl)
-               ctl->ops = ops;
-       return ctl;
+       int val;
+
+       val = snd_hda_get_bool_hint(codec, "jack_detect");
+       if (val >= 0)
+               codec->no_jack_detect = !val;
+       val = snd_hda_get_bool_hint(codec, "inv_jack_detect");
+       if (val >= 0)
+               codec->inv_jack_detect = !!val;
+       val = snd_hda_get_bool_hint(codec, "trigger_sense");
+       if (val >= 0)
+               codec->no_trigger_sense = !val;
+       val = snd_hda_get_bool_hint(codec, "inv_eapd");
+       if (val >= 0)
+               codec->inv_eapd = !!val;
+       val = snd_hda_get_bool_hint(codec, "pcm_format_first");
+       if (val >= 0)
+               codec->pcm_format_first = !!val;
+       val = snd_hda_get_bool_hint(codec, "sticky_stream");
+       if (val >= 0)
+               codec->no_sticky_stream = !val;
+       val = snd_hda_get_bool_hint(codec, "spdif_status_reset");
+       if (val >= 0)
+               codec->spdif_status_reset = !!val;
+       val = snd_hda_get_bool_hint(codec, "pin_amp_workaround");
+       if (val >= 0)
+               codec->pin_amp_workaround = !!val;
+       val = snd_hda_get_bool_hint(codec, "single_adc_amp");
+       if (val >= 0)
+               codec->single_adc_amp = !!val;
+
+       val = snd_hda_get_bool_hint(codec, "auto_mute");
+       if (val >= 0)
+               spec->suppress_auto_mute = !val;
+       val = snd_hda_get_bool_hint(codec, "auto_mic");
+       if (val >= 0)
+               spec->suppress_auto_mic = !val;
+       val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
+       if (val >= 0)
+               spec->line_in_auto_switch = !!val;
+       val = snd_hda_get_bool_hint(codec, "need_dac_fix");
+       if (val >= 0)
+               spec->need_dac_fix = !!val;
+       val = snd_hda_get_bool_hint(codec, "primary_hp");
+       if (val >= 0)
+               spec->no_primary_hp = !val;
+       val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
+       if (val >= 0)
+               spec->multi_cap_vol = !!val;
+       val = snd_hda_get_bool_hint(codec, "inv_dmic_split");
+       if (val >= 0)
+               spec->inv_dmic_split = !!val;
+       val = snd_hda_get_bool_hint(codec, "indep_hp");
+       if (val >= 0)
+               spec->indep_hp = !!val;
+       val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
+       if (val >= 0)
+               spec->add_stereo_mix_input = !!val;
+       val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
+       if (val >= 0)
+               spec->add_out_jack_modes = !!val;
+       val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
+       if (val >= 0)
+               spec->add_in_jack_modes = !!val;
+
+       if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
+               spec->mixer_nid = val;
 }
 
-static void free_bind_ctls(struct hda_gen_spec *spec)
+/*
+ * pin control value accesses
+ */
+
+#define update_pin_ctl(codec, pin, val) \
+       snd_hda_codec_update_cache(codec, pin, 0, \
+                                  AC_VERB_SET_PIN_WIDGET_CONTROL, val)
+
+/* restore the pinctl based on the cached value */
+static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
 {
-       if (spec->bind_ctls.list) {
-               struct hda_bind_ctls **ctl = spec->bind_ctls.list;
-               int i;
-               for (i = 0; i < spec->bind_ctls.used; i++)
-                       kfree(ctl[i]);
-       }
-       snd_array_free(&spec->bind_ctls);
+       update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
 }
 
-void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
+/* set the pinctl target value and write it if requested */
+static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
+                          unsigned int val, bool do_write)
 {
-       if (!spec)
+       if (!pin)
                return;
-       free_kctls(spec);
-       free_bind_ctls(spec);
-       snd_array_free(&spec->paths);
+       val = snd_hda_correct_pin_ctl(codec, pin, val);
+       snd_hda_codec_set_pin_target(codec, pin, val);
+       if (do_write)
+               update_pin_ctl(codec, pin, val);
+}
+
+/* set pinctl target values for all given pins */
+static void set_pin_targets(struct hda_codec *codec, int num_pins,
+                           hda_nid_t *pins, unsigned int val)
+{
+       int i;
+       for (i = 0; i < num_pins; i++)
+               set_pin_target(codec, pins[i], val, false);
 }
-EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
 
 /*
  * parsing paths
  */
 
-/* get the path between the given NIDs;
- * passing 0 to either @pin or @dac behaves as a wildcard
- */
-struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
-                                     hda_nid_t from_nid, hda_nid_t to_nid)
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+       int i;
+       for (i = 0; i < nums; i++)
+               if (list[i] == nid)
+                       return i;
+       return -1;
+}
+
+/* return true if the given NID is contained in the path */
+static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
+{
+       return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
+}
+
+static struct nid_path *get_nid_path(struct hda_codec *codec,
+                                    hda_nid_t from_nid, hda_nid_t to_nid,
+                                    int anchor_nid)
 {
        struct hda_gen_spec *spec = codec->spec;
        int i;
@@ -127,13 +225,53 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
                if (path->depth <= 0)
                        continue;
                if ((!from_nid || path->path[0] == from_nid) &&
-                   (!to_nid || path->path[path->depth - 1] == to_nid))
-                       return path;
+                   (!to_nid || path->path[path->depth - 1] == to_nid)) {
+                       if (!anchor_nid ||
+                           (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
+                           (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
+                               return path;
+               }
        }
        return NULL;
 }
+
+/* get the path between the given NIDs;
+ * passing 0 to either @pin or @dac behaves as a wildcard
+ */
+struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
+                                     hda_nid_t from_nid, hda_nid_t to_nid)
+{
+       return get_nid_path(codec, from_nid, to_nid, 0);
+}
 EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
 
+/* get the index number corresponding to the path instance;
+ * the index starts from 1, for easier checking the invalid value
+ */
+int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       struct nid_path *array = spec->paths.list;
+       ssize_t idx;
+
+       if (!spec->paths.used)
+               return 0;
+       idx = path - array;
+       if (idx < 0 || idx >= spec->paths.used)
+               return 0;
+       return idx + 1;
+}
+
+/* get the path instance corresponding to the given index number */
+struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
+{
+       struct hda_gen_spec *spec = codec->spec;
+
+       if (idx <= 0 || idx > spec->paths.used)
+               return NULL;
+       return snd_array_elem(&spec->paths, idx - 1);
+}
+
 /* check whether the given DAC is already found in any existing paths */
 static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
 {
@@ -177,29 +315,42 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
 
 /* check whether a control with the given (nid, dir, idx) was assigned */
 static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
-                             int dir, int idx)
+                             int dir, int idx, int type)
 {
        unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
-       return is_ctl_used(codec, val, NID_PATH_VOL_CTL) ||
-               is_ctl_used(codec, val, NID_PATH_MUTE_CTL);
+       return is_ctl_used(codec, val, type);
+}
+
+static void print_nid_path(const char *pfx, struct nid_path *path)
+{
+       char buf[40];
+       int i;
+
+
+       buf[0] = 0;
+       for (i = 0; i < path->depth; i++) {
+               char tmp[4];
+               sprintf(tmp, ":%02x", path->path[i]);
+               strlcat(buf, tmp, sizeof(buf));
+       }
+       snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf);
 }
 
 /* called recursively */
 static bool __parse_nid_path(struct hda_codec *codec,
                             hda_nid_t from_nid, hda_nid_t to_nid,
-                            int with_aa_mix, struct nid_path *path, int depth)
+                            int anchor_nid, struct nid_path *path,
+                            int depth)
 {
-       struct hda_gen_spec *spec = codec->spec;
-       hda_nid_t conn[16];
+       const hda_nid_t *conn;
        int i, nums;
 
-       if (to_nid == spec->mixer_nid) {
-               if (!with_aa_mix)
-                       return false;
-               with_aa_mix = 2; /* mark aa-mix is included */
-       }
+       if (to_nid == anchor_nid)
+               anchor_nid = 0; /* anchor passed */
+       else if (to_nid == (hda_nid_t)(-anchor_nid))
+               return false; /* hit the exclusive nid */
 
-       nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn));
+       nums = snd_hda_get_conn_list(codec, to_nid, &conn);
        for (i = 0; i < nums; i++) {
                if (conn[i] != from_nid) {
                        /* special case: when from_nid is 0,
@@ -210,8 +361,8 @@ static bool __parse_nid_path(struct hda_codec *codec,
                            is_dac_already_used(codec, conn[i]))
                                continue;
                }
-               /* aa-mix is requested but not included? */
-               if (!(spec->mixer_nid && with_aa_mix == 1))
+               /* anchor is not requested or already passed? */
+               if (anchor_nid <= 0)
                        goto found;
        }
        if (depth >= MAX_NID_PATH_DEPTH)
@@ -223,7 +374,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
                    type == AC_WID_PIN)
                        continue;
                if (__parse_nid_path(codec, from_nid, conn[i],
-                                    with_aa_mix, path, depth + 1))
+                                    anchor_nid, path, depth + 1))
                        goto found;
        }
        return false;
@@ -239,22 +390,19 @@ static bool __parse_nid_path(struct hda_codec *codec,
 
 /* parse the widget path from the given nid to the target nid;
  * when @from_nid is 0, try to find an empty DAC;
- * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded.
- * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded.
- * when @with_aa_mix is 2, no special handling about spec->mixer_nid.
+ * when @anchor_nid is set to a positive value, only paths through the widget
+ * with the given value are evaluated.
+ * when @anchor_nid is set to a negative value, paths through the widget
+ * with the negative of given value are excluded, only other paths are chosen.
+ * when @anchor_nid is zero, no special handling about path selection.
  */
 bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
-                           hda_nid_t to_nid, int with_aa_mix,
+                           hda_nid_t to_nid, int anchor_nid,
                            struct nid_path *path)
 {
-       if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) {
+       if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
                path->path[path->depth] = to_nid;
                path->depth++;
-#if 0
-               snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
-                           path->depth, path->path[0], path->path[1],
-                           path->path[2], path->path[3], path->path[4]);
-#endif
                return true;
        }
        return false;
@@ -267,7 +415,7 @@ EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path);
  */
 struct nid_path *
 snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
-                    hda_nid_t to_nid, int with_aa_mix)
+                    hda_nid_t to_nid, int anchor_nid)
 {
        struct hda_gen_spec *spec = codec->spec;
        struct nid_path *path;
@@ -275,11 +423,16 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
        if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
                return NULL;
 
+       /* check whether the path has been already added */
+       path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
+       if (path)
+               return path;
+
        path = snd_array_new(&spec->paths);
        if (!path)
                return NULL;
        memset(path, 0, sizeof(*path));
-       if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path))
+       if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
                return path;
        /* push back */
        spec->paths.used--;
@@ -287,6 +440,15 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_add_new_path);
 
+/* clear the given path as invalid so that it won't be picked up later */
+static void invalidate_nid_path(struct hda_codec *codec, int idx)
+{
+       struct nid_path *path = snd_hda_get_path_from_idx(codec, idx);
+       if (!path)
+               return;
+       memset(path, 0, sizeof(*path));
+}
+
 /* look for an empty DAC slot */
 static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
                              bool is_digital)
@@ -328,6 +490,15 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
        return false;
 }
 
+static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
+                         hda_nid_t nid2, int dir)
+{
+       if (!(get_wcaps(codec, nid1) & (1 << (dir + 1))))
+               return !(get_wcaps(codec, nid2) & (1 << (dir + 1)));
+       return (query_amp_caps(codec, nid1, dir) ==
+               query_amp_caps(codec, nid2, dir));
+}
+
 #define nid_has_mute(codec, nid, dir) \
        check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
 #define nid_has_volume(codec, nid, dir) \
@@ -418,12 +589,10 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
 
 /* get the default amp value for the target state */
 static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
-                                  int dir, bool enable)
+                                  int dir, unsigned int caps, bool enable)
 {
-       unsigned int caps;
        unsigned int val = 0;
 
-       caps = query_amp_caps(codec, nid, dir);
        if (caps & AC_AMPCAP_NUM_STEPS) {
                /* set to 0dB */
                if (enable)
@@ -439,19 +608,49 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
 /* initialize the amp value (only at the first time) */
 static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
 {
-       int val = get_amp_val_to_activate(codec, nid, dir, false);
+       unsigned int caps = query_amp_caps(codec, nid, dir);
+       int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
        snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
 }
 
+/* calculate amp value mask we can modify;
+ * if the given amp is controlled by mixers, don't touch it
+ */
+static unsigned int get_amp_mask_to_modify(struct hda_codec *codec,
+                                          hda_nid_t nid, int dir, int idx,
+                                          unsigned int caps)
+{
+       unsigned int mask = 0xff;
+
+       if (caps & AC_AMPCAP_MUTE) {
+               if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL))
+                       mask &= ~0x80;
+       }
+       if (caps & AC_AMPCAP_NUM_STEPS) {
+               if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+                   is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+                       mask &= ~0x7f;
+       }
+       return mask;
+}
+
 static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
-                        int idx, bool enable)
+                        int idx, int idx_to_check, bool enable)
 {
-       int val;
-       if (is_ctl_associated(codec, nid, dir, idx) ||
-           is_active_nid(codec, nid, dir, idx))
+       unsigned int caps;
+       unsigned int mask, val;
+
+       if (!enable && is_active_nid(codec, nid, dir, idx))
+               return;
+
+       caps = query_amp_caps(codec, nid, dir);
+       val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
+       mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
+       if (!mask)
                return;
-       val = get_amp_val_to_activate(codec, nid, dir, enable);
-       snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
+
+       val &= mask;
+       snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val);
 }
 
 static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
@@ -459,19 +658,19 @@ static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
 {
        hda_nid_t nid = path->path[i];
        init_amp(codec, nid, HDA_OUTPUT, 0);
-       activate_amp(codec, nid, HDA_OUTPUT, 0, enable);
+       activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
 }
 
 static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
                            int i, bool enable, bool add_aamix)
 {
        struct hda_gen_spec *spec = codec->spec;
-       hda_nid_t conn[16];
+       const hda_nid_t *conn;
        int n, nums, idx;
        int type;
        hda_nid_t nid = path->path[i];
 
-       nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+       nums = snd_hda_get_conn_list(codec, nid, &conn);
        type = get_wcaps_type(get_wcaps(codec, nid));
        if (type == AC_WID_PIN ||
            (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
@@ -483,16 +682,13 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
        for (n = 0; n < nums; n++)
                init_amp(codec, nid, HDA_INPUT, n);
 
-       if (is_ctl_associated(codec, nid, HDA_INPUT, idx))
-               return;
-
        /* here is a little bit tricky in comparison with activate_amp_out();
         * when aa-mixer is available, we need to enable the path as well
         */
        for (n = 0; n < nums; n++) {
                if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid))
                        continue;
-               activate_amp(codec, nid, HDA_INPUT, n, enable);
+               activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
        }
 }
 
@@ -523,6 +719,20 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
 }
 EXPORT_SYMBOL_HDA(snd_hda_activate_path);
 
+/* turn on/off EAPD on the given pin */
+static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       if (spec->own_eapd_ctl ||
+           !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
+               return;
+       if (codec->inv_eapd)
+               enable = !enable;
+       snd_hda_codec_update_cache(codec, pin, 0,
+                                  AC_VERB_SET_EAPD_BTLENABLE,
+                                  enable ? 0x02 : 0x00);
+}
+
 
 /*
  * Helper functions for creating mixer ctl elements
@@ -532,31 +742,28 @@ enum {
        HDA_CTL_WIDGET_VOL,
        HDA_CTL_WIDGET_MUTE,
        HDA_CTL_BIND_MUTE,
-       HDA_CTL_BIND_VOL,
-       HDA_CTL_BIND_SW,
 };
 static const struct snd_kcontrol_new control_templates[] = {
        HDA_CODEC_VOLUME(NULL, 0, 0, 0),
        HDA_CODEC_MUTE(NULL, 0, 0, 0),
        HDA_BIND_MUTE(NULL, 0, 0, 0),
-       HDA_BIND_VOL(NULL, 0),
-       HDA_BIND_SW(NULL, 0),
 };
 
 /* add dynamic controls from template */
-static int add_control(struct hda_gen_spec *spec, int type, const char *name,
+static struct snd_kcontrol_new *
+add_control(struct hda_gen_spec *spec, int type, const char *name,
                       int cidx, unsigned long val)
 {
        struct snd_kcontrol_new *knew;
 
        knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
        if (!knew)
-               return -ENOMEM;
+               return NULL;
        knew->index = cidx;
        if (get_amp_nid_(val))
                knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
-       return 0;
+       return knew;
 }
 
 static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
@@ -565,7 +772,9 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
 {
        char name[32];
        snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
-       return add_control(spec, type, name, cidx, val);
+       if (!add_control(spec, type, name, cidx, val))
+               return -ENOMEM;
+       return 0;
 }
 
 #define add_pb_vol_ctrl(spec, type, pfx, val)                  \
@@ -643,19 +852,27 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
        return add_sw_ctl(codec, pfx, cidx, chs, path);
 }
 
+/* any ctl assigned to the path with the given index? */
+static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
+{
+       struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
+       return path && path->ctls[ctl_type];
+}
+
 static const char * const channel_name[4] = {
        "Front", "Surround", "CLFE", "Side"
 };
 
 /* give some appropriate ctl name prefix for the given line out channel */
-static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch,
-                                   bool can_be_master, int *index)
+static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
+                                   int *index, int ctl_type)
 {
+       struct hda_gen_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
 
        *index = 0;
        if (cfg->line_outs == 1 && !spec->multi_ios &&
-           !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
+           !cfg->hp_outs && !cfg->speaker_outs)
                return spec->vmaster_mute.hook ? "PCM" : "Master";
 
        /* if there is really a single DAC used in the whole output paths,
@@ -665,24 +882,41 @@ static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch,
            !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
                return spec->vmaster_mute.hook ? "PCM" : "Master";
 
+       /* multi-io channels */
+       if (ch >= cfg->line_outs)
+               return channel_name[ch];
+
        switch (cfg->line_out_type) {
        case AUTO_PIN_SPEAKER_OUT:
+               /* if the primary channel vol/mute is shared with HP volume,
+                * don't name it as Speaker
+                */
+               if (!ch && cfg->hp_outs &&
+                   !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
+                       break;
                if (cfg->line_outs == 1)
                        return "Speaker";
                if (cfg->line_outs == 2)
                        return ch ? "Bass Speaker" : "Speaker";
                break;
        case AUTO_PIN_HP_OUT:
+               /* if the primary channel vol/mute is shared with spk volume,
+                * don't name it as Headphone
+                */
+               if (!ch && cfg->speaker_outs &&
+                   !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
+                       break;
                /* for multi-io case, only the primary out */
                if (ch && spec->multi_ios)
                        break;
                *index = ch;
                return "Headphone";
-       default:
-               if (cfg->line_outs == 1 && !spec->multi_ios)
-                       return "PCM";
-               break;
        }
+
+       /* for a single channel output, we don't have to name the channel */
+       if (cfg->line_outs == 1 && !spec->multi_ios)
+               return "PCM";
+
        if (ch >= ARRAY_SIZE(channel_name)) {
                snd_BUG();
                return "PCM";
@@ -717,23 +951,26 @@ enum {
        BAD_SHARED_VOL = 0x10,
 };
 
-/* look for widgets in the path between the given NIDs appropriate for
+/* look for widgets in the given path which are appropriate for
  * volume and mute controls, and assign the values to ctls[].
  *
  * When no appropriate widget is found in the path, the badness value
  * is incremented depending on the situation.  The function returns the
  * total badness for both volume and mute controls.
  */
-static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
-                               hda_nid_t dac)
+static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
 {
-       struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin);
        hda_nid_t nid;
        unsigned int val;
        int badness = 0;
 
        if (!path)
                return BAD_SHARED_VOL * 2;
+
+       if (path->ctls[NID_PATH_VOL_CTL] ||
+           path->ctls[NID_PATH_MUTE_CTL])
+               return 0; /* already evaluated */
+
        nid = look_for_out_vol_nid(codec, path);
        if (nid) {
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
@@ -787,13 +1024,34 @@ static struct badness_table extra_out_badness = {
        .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
 };
 
+/* get the DAC of the primary output corresponding to the given array index */
+static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+
+       if (cfg->line_outs > idx)
+               return spec->private_dac_nids[idx];
+       idx -= cfg->line_outs;
+       if (spec->multi_ios > idx)
+               return spec->multi_io[idx].dac;
+       return 0;
+}
+
+/* return the DAC if it's reachable, otherwise zero */
+static inline hda_nid_t try_dac(struct hda_codec *codec,
+                               hda_nid_t dac, hda_nid_t pin)
+{
+       return is_reachable_path(codec, dac, pin) ? dac : 0;
+}
+
 /* try to assign DACs to pins and return the resultant badness */
 static int try_assign_dacs(struct hda_codec *codec, int num_outs,
                           const hda_nid_t *pins, hda_nid_t *dacs,
+                          int *path_idx,
                           const struct badness_table *bad)
 {
        struct hda_gen_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
        int i, j;
        int badness = 0;
        hda_nid_t dac;
@@ -802,25 +1060,36 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
                return 0;
 
        for (i = 0; i < num_outs; i++) {
+               struct nid_path *path;
                hda_nid_t pin = pins[i];
-               if (!dacs[i])
-                       dacs[i] = look_for_dac(codec, pin, false);
+
+               path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+               if (path) {
+                       badness += assign_out_path_ctls(codec, path);
+                       continue;
+               }
+
+               dacs[i] = look_for_dac(codec, pin, false);
                if (!dacs[i] && !i) {
+                       /* try to steal the DAC of surrounds for the front */
                        for (j = 1; j < num_outs; j++) {
                                if (is_reachable_path(codec, dacs[j], pin)) {
                                        dacs[0] = dacs[j];
                                        dacs[j] = 0;
+                                       invalidate_nid_path(codec, path_idx[j]);
+                                       path_idx[j] = 0;
                                        break;
                                }
                        }
                }
                dac = dacs[i];
                if (!dac) {
-                       if (is_reachable_path(codec, dacs[0], pin))
-                               dac = dacs[0];
-                       else if (cfg->line_outs > i &&
-                                is_reachable_path(codec, spec->private_dac_nids[i], pin))
-                               dac = spec->private_dac_nids[i];
+                       if (num_outs > 2)
+                               dac = try_dac(codec, get_primary_out(codec, i), pin);
+                       if (!dac)
+                               dac = try_dac(codec, dacs[0], pin);
+                       if (!dac)
+                               dac = try_dac(codec, get_primary_out(codec, i), pin);
                        if (dac) {
                                if (!i)
                                        badness += bad->shared_primary;
@@ -836,10 +1105,19 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
                        else
                                badness += bad->no_dac;
                }
-               if (!snd_hda_add_new_path(codec, dac, pin, 0))
+               path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
+               if (!path && !i && spec->mixer_nid) {
+                       /* try with aamix */
+                       path = snd_hda_add_new_path(codec, dac, pin, 0);
+               }
+               if (!path)
                        dac = dacs[i] = 0;
-               if (dac)
-                       badness += assign_out_path_ctls(codec, pin, dac);
+               else {
+                       /* print_nid_path("output", path); */
+                       path->active = true;
+                       path_idx[i] = snd_hda_get_path_idx(codec, path);
+                       badness += assign_out_path_ctls(codec, path);
+               }
        }
 
        return badness;
@@ -882,6 +1160,28 @@ static bool can_be_multiio_pin(struct hda_codec *codec,
        return true;
 }
 
+/* count the number of input pins that are capable to be multi-io */
+static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->autocfg;
+       unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+       unsigned int location = get_defcfg_location(defcfg);
+       int type, i;
+       int num_pins = 0;
+
+       for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+               for (i = 0; i < cfg->num_inputs; i++) {
+                       if (cfg->inputs[i].type != type)
+                               continue;
+                       if (can_be_multiio_pin(codec, location,
+                                              cfg->inputs[i].pin))
+                               num_pins++;
+               }
+       }
+       return num_pins;
+}
+
 /*
  * multi-io helper
  *
@@ -892,33 +1192,24 @@ static bool can_be_multiio_pin(struct hda_codec *codec,
  */
 static int fill_multi_ios(struct hda_codec *codec,
                          hda_nid_t reference_pin,
-                         bool hardwired, int offset)
+                         bool hardwired)
 {
        struct hda_gen_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
-       int type, i, j, dacs, num_pins, old_pins;
+       int type, i, j, num_pins, old_pins;
        unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
        unsigned int location = get_defcfg_location(defcfg);
        int badness = 0;
+       struct nid_path *path;
 
        old_pins = spec->multi_ios;
        if (old_pins >= 2)
                goto end_fill;
 
-       num_pins = 0;
-       for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
-               for (i = 0; i < cfg->num_inputs; i++) {
-                       if (cfg->inputs[i].type != type)
-                               continue;
-                       if (can_be_multiio_pin(codec, location,
-                                              cfg->inputs[i].pin))
-                               num_pins++;
-               }
-       }
+       num_pins = count_multiio_pins(codec, reference_pin);
        if (num_pins < 2)
                goto end_fill;
 
-       dacs = spec->multiout.num_dacs;
        for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
                for (i = 0; i < cfg->num_inputs; i++) {
                        hda_nid_t nid = cfg->inputs[i].pin;
@@ -935,11 +1226,6 @@ static int fill_multi_ios(struct hda_codec *codec,
                        if (j < spec->multi_ios)
                                continue;
 
-                       if (offset && offset + spec->multi_ios < dacs) {
-                               dac = spec->private_dac_nids[offset + spec->multi_ios];
-                               if (!is_reachable_path(codec, dac, nid))
-                                       dac = 0;
-                       }
                        if (hardwired)
                                dac = get_dac_if_single(codec, nid);
                        else if (!dac)
@@ -948,12 +1234,17 @@ static int fill_multi_ios(struct hda_codec *codec,
                                badness++;
                                continue;
                        }
-                       if (!snd_hda_add_new_path(codec, dac, nid, 0)) {
+                       path = snd_hda_add_new_path(codec, dac, nid,
+                                                   -spec->mixer_nid);
+                       if (!path) {
                                badness++;
                                continue;
                        }
+                       /* print_nid_path("multiio", path); */
                        spec->multi_io[spec->multi_ios].pin = nid;
                        spec->multi_io[spec->multi_ios].dac = dac;
+                       spec->out_paths[cfg->line_outs + spec->multi_ios] =
+                               snd_hda_get_path_idx(codec, path);
                        spec->multi_ios++;
                        if (spec->multi_ios >= 2)
                                break;
@@ -976,34 +1267,83 @@ static int fill_multi_ios(struct hda_codec *codec,
        }
 
        /* assign volume and mute controls */
-       for (i = old_pins; i < spec->multi_ios; i++)
-               badness += assign_out_path_ctls(codec, spec->multi_io[i].pin,
-                                               spec->multi_io[i].dac);
+       for (i = old_pins; i < spec->multi_ios; i++) {
+               path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
+               badness += assign_out_path_ctls(codec, path);
+       }
 
        return badness;
 }
 
 /* map DACs for all pins in the list if they are single connections */
 static bool map_singles(struct hda_codec *codec, int outs,
-                       const hda_nid_t *pins, hda_nid_t *dacs)
+                       const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
 {
+       struct hda_gen_spec *spec = codec->spec;
        int i;
        bool found = false;
        for (i = 0; i < outs; i++) {
+               struct nid_path *path;
                hda_nid_t dac;
                if (dacs[i])
                        continue;
                dac = get_dac_if_single(codec, pins[i]);
                if (!dac)
                        continue;
-               if (snd_hda_add_new_path(codec, dac, pins[i], 0)) {
+               path = snd_hda_add_new_path(codec, dac, pins[i],
+                                           -spec->mixer_nid);
+               if (!path && !i && spec->mixer_nid)
+                       path = snd_hda_add_new_path(codec, dac, pins[i], 0);
+               if (path) {
                        dacs[i] = dac;
                        found = true;
+                       /* print_nid_path("output", path); */
+                       path->active = true;
+                       path_idx[i] = snd_hda_get_path_idx(codec, path);
                }
        }
        return found;
 }
 
+/* create a new path including aamix if available, and return its index */
+static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       struct nid_path *path;
+
+       path = snd_hda_get_path_from_idx(codec, path_idx);
+       if (!path || !path->depth ||
+           is_nid_contained(path, spec->mixer_nid))
+               return 0;
+       path = snd_hda_add_new_path(codec, path->path[0],
+                                   path->path[path->depth - 1],
+                                   spec->mixer_nid);
+       if (!path)
+               return 0;
+       /* print_nid_path("output-aamix", path); */
+       path->active = false; /* unused as default */
+       return snd_hda_get_path_idx(codec, path);
+}
+
+/* fill the empty entries in the dac array for speaker/hp with the
+ * shared dac pointed by the paths
+ */
+static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
+                              hda_nid_t *dacs, int *path_idx)
+{
+       struct nid_path *path;
+       int i;
+
+       for (i = 0; i < num_outs; i++) {
+               if (dacs[i])
+                       continue;
+               path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+               if (!path)
+                       continue;
+               dacs[i] = path->path[0];
+       }
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
 static int fill_and_eval_dacs(struct hda_codec *codec,
                              bool fill_hardwired,
@@ -1012,6 +1352,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
        struct hda_gen_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
        int i, err, badness;
+       unsigned int val;
 
        /* set num_dacs once to full for look_for_dac() */
        spec->multiout.num_dacs = cfg->line_outs;
@@ -1021,6 +1362,17 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
        memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
        spec->multi_ios = 0;
        snd_array_free(&spec->paths);
+
+       /* clear path indices */
+       memset(spec->out_paths, 0, sizeof(spec->out_paths));
+       memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
+       memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
+       memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
+       memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
+       memset(spec->input_paths, 0, sizeof(spec->input_paths));
+       memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
+       memset(&spec->digin_path, 0, sizeof(spec->digin_path));
+
        badness = 0;
 
        /* fill hard-wired DACs first */
@@ -1029,16 +1381,19 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                do {
                        mapped = map_singles(codec, cfg->line_outs,
                                             cfg->line_out_pins,
-                                            spec->private_dac_nids);
+                                            spec->private_dac_nids,
+                                            spec->out_paths);
                        mapped |= map_singles(codec, cfg->hp_outs,
                                              cfg->hp_pins,
-                                             spec->multiout.hp_out_nid);
+                                             spec->multiout.hp_out_nid,
+                                             spec->hp_paths);
                        mapped |= map_singles(codec, cfg->speaker_outs,
                                              cfg->speaker_pins,
-                                             spec->multiout.extra_out_nid);
+                                             spec->multiout.extra_out_nid,
+                                             spec->speaker_paths);
                        if (fill_mio_first && cfg->line_outs == 1 &&
                            cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-                               err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0);
+                               err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
                                if (!err)
                                        mapped = true;
                        }
@@ -1046,26 +1401,13 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
        }
 
        badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
-                                  spec->private_dac_nids,
+                                  spec->private_dac_nids, spec->out_paths,
                                   &main_out_badness);
 
-       /* re-count num_dacs and squash invalid entries */
-       spec->multiout.num_dacs = 0;
-       for (i = 0; i < cfg->line_outs; i++) {
-               if (spec->private_dac_nids[i])
-                       spec->multiout.num_dacs++;
-               else {
-                       memmove(spec->private_dac_nids + i,
-                               spec->private_dac_nids + i + 1,
-                               sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
-                       spec->private_dac_nids[cfg->line_outs - 1] = 0;
-               }
-       }
-
        if (fill_mio_first &&
            cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
                /* try to fill multi-io first */
-               err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+               err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
                if (err < 0)
                        return err;
                /* we don't count badness at this stage yet */
@@ -1074,6 +1416,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
        if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
                err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
                                      spec->multiout.hp_out_nid,
+                                     spec->hp_paths,
                                      &extra_out_badness);
                if (err < 0)
                        return err;
@@ -1083,38 +1426,83 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                err = try_assign_dacs(codec, cfg->speaker_outs,
                                      cfg->speaker_pins,
                                      spec->multiout.extra_out_nid,
-                                        &extra_out_badness);
+                                     spec->speaker_paths,
+                                     &extra_out_badness);
                if (err < 0)
                        return err;
                badness += err;
        }
        if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
-               err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+               err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
                if (err < 0)
                        return err;
                badness += err;
        }
-       if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
-               /* try multi-ios with HP + inputs */
-               int offset = 0;
-               if (cfg->line_outs >= 3)
-                       offset = 1;
-               err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset);
-               if (err < 0)
-                       return err;
-               badness += err;
+
+       if (spec->mixer_nid) {
+               spec->aamix_out_paths[0] =
+                       check_aamix_out_path(codec, spec->out_paths[0]);
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+                       spec->aamix_out_paths[1] =
+                               check_aamix_out_path(codec, spec->hp_paths[0]);
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+                       spec->aamix_out_paths[2] =
+                               check_aamix_out_path(codec, spec->speaker_paths[0]);
+       }
+
+       if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+               if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
+                       spec->multi_ios = 1; /* give badness */
+
+       /* re-count num_dacs and squash invalid entries */
+       spec->multiout.num_dacs = 0;
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (spec->private_dac_nids[i])
+                       spec->multiout.num_dacs++;
+               else {
+                       memmove(spec->private_dac_nids + i,
+                               spec->private_dac_nids + i + 1,
+                               sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+                       spec->private_dac_nids[cfg->line_outs - 1] = 0;
+               }
        }
 
+       spec->ext_channel_count = spec->min_channel_count =
+               spec->multiout.num_dacs * 2;
+
        if (spec->multi_ios == 2) {
                for (i = 0; i < 2; i++)
                        spec->private_dac_nids[spec->multiout.num_dacs++] =
                                spec->multi_io[i].dac;
-               spec->ext_channel_count = 2;
        } else if (spec->multi_ios) {
                spec->multi_ios = 0;
                badness += BAD_MULTI_IO;
        }
 
+       /* re-fill the shared DAC for speaker / headphone */
+       if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+               refill_shared_dacs(codec, cfg->hp_outs,
+                                  spec->multiout.hp_out_nid,
+                                  spec->hp_paths);
+       if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+               refill_shared_dacs(codec, cfg->speaker_outs,
+                                  spec->multiout.extra_out_nid,
+                                  spec->speaker_paths);
+
+       /* set initial pinctl targets */
+       if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT)
+               val = PIN_HP;
+       else
+               val = PIN_OUT;
+       set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val);
+       if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+               set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
+       if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+               val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT;
+               set_pin_targets(codec, cfg->speaker_outs,
+                               cfg->speaker_pins, val);
+       }
+
        return badness;
 }
 
@@ -1126,35 +1514,70 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
 #define debug_badness(...)
 #endif
 
-static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *cfg)
+#ifdef DEBUG_BADNESS
+static inline void print_nid_path_idx(struct hda_codec *codec,
+                                     const char *pfx, int idx)
 {
-       debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
-                     cfg->line_out_pins[0], cfg->line_out_pins[1],
-                     cfg->line_out_pins[2], cfg->line_out_pins[2],
+       struct nid_path *path;
+
+       path = snd_hda_get_path_from_idx(codec, idx);
+       if (path)
+               print_nid_path(pfx, path);
+}
+
+static void debug_show_configs(struct hda_codec *codec,
+                              struct auto_pin_cfg *cfg)
+{
+       struct hda_gen_spec *spec = codec->spec;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       static const char * const lo_type[3] = { "LO", "SP", "HP" };
+#endif
+       int i;
+
+       debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
+                     cfg->line_out_pins[0], cfg->line_out_pins[1],
+                     cfg->line_out_pins[2], cfg->line_out_pins[3],
                      spec->multiout.dac_nids[0],
                      spec->multiout.dac_nids[1],
                      spec->multiout.dac_nids[2],
-                     spec->multiout.dac_nids[3]);
+                     spec->multiout.dac_nids[3],
+                     lo_type[cfg->line_out_type]);
+       for (i = 0; i < cfg->line_outs; i++)
+               print_nid_path_idx(codec, "  out", spec->out_paths[i]);
        if (spec->multi_ios > 0)
                debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
                              spec->multi_ios,
                              spec->multi_io[0].pin, spec->multi_io[1].pin,
                              spec->multi_io[0].dac, spec->multi_io[1].dac);
-       debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+       for (i = 0; i < spec->multi_ios; i++)
+               print_nid_path_idx(codec, "  mio",
+                                  spec->out_paths[cfg->line_outs + i]);
+       if (cfg->hp_outs)
+               debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
                      cfg->hp_pins[0], cfg->hp_pins[1],
-                     cfg->hp_pins[2], cfg->hp_pins[2],
+                     cfg->hp_pins[2], cfg->hp_pins[3],
                      spec->multiout.hp_out_nid[0],
                      spec->multiout.hp_out_nid[1],
                      spec->multiout.hp_out_nid[2],
                      spec->multiout.hp_out_nid[3]);
-       debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+       for (i = 0; i < cfg->hp_outs; i++)
+               print_nid_path_idx(codec, "  hp ", spec->hp_paths[i]);
+       if (cfg->speaker_outs)
+               debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
                      cfg->speaker_pins[0], cfg->speaker_pins[1],
                      cfg->speaker_pins[2], cfg->speaker_pins[3],
                      spec->multiout.extra_out_nid[0],
                      spec->multiout.extra_out_nid[1],
                      spec->multiout.extra_out_nid[2],
                      spec->multiout.extra_out_nid[3]);
+       for (i = 0; i < cfg->speaker_outs; i++)
+               print_nid_path_idx(codec, "  spk", spec->speaker_paths[i]);
+       for (i = 0; i < 3; i++)
+               print_nid_path_idx(codec, "  mix", spec->aamix_out_paths[i]);
 }
+#else
+#define debug_show_configs(codec, cfg) /* NOP */
+#endif
 
 /* find all available DACs of the codec */
 static void fill_all_dac_nids(struct hda_codec *codec)
@@ -1187,8 +1610,6 @@ static int parse_output_paths(struct hda_codec *codec)
        bool best_wired = true, best_mio = true;
        bool hp_spk_swapped = false;
 
-       fill_all_dac_nids(codec);
-
        best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
        if (!best_cfg)
                return -ENOMEM;
@@ -1204,7 +1625,7 @@ static int parse_output_paths(struct hda_codec *codec)
                debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
                              cfg->line_out_type, fill_hardwired, fill_mio_first,
                              badness);
-               debug_show_configs(spec, cfg);
+               debug_show_configs(codec, cfg);
                if (badness < best_badness) {
                        best_badness = badness;
                        *best_cfg = *cfg;
@@ -1254,20 +1675,22 @@ static int parse_output_paths(struct hda_codec *codec)
        }
 
        if (badness) {
+               debug_badness("==> restoring best_cfg\n");
                *cfg = *best_cfg;
                fill_and_eval_dacs(codec, best_wired, best_mio);
        }
        debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
                      cfg->line_out_type, best_wired, best_mio);
-       debug_show_configs(spec, cfg);
+       debug_show_configs(codec, cfg);
 
        if (cfg->line_out_pins[0]) {
                struct nid_path *path;
-               path = snd_hda_get_nid_path(codec,
-                                           spec->multiout.dac_nids[0],
-                                           cfg->line_out_pins[0]);
+               path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
                if (path)
                        spec->vmaster_nid = look_for_out_vol_nid(codec, path);
+               if (spec->vmaster_nid)
+                       snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+                                               HDA_OUTPUT, spec->vmaster_tlv);
        }
 
        kfree(best_cfg);
@@ -1288,24 +1711,13 @@ static int create_multi_out_ctls(struct hda_codec *codec,
        for (i = 0; i < noutputs; i++) {
                const char *name;
                int index;
-               hda_nid_t dac, pin;
                struct nid_path *path;
 
-               dac = spec->multiout.dac_nids[i];
-               if (!dac)
-                       continue;
-               if (i >= cfg->line_outs) {
-                       pin = spec->multi_io[i - 1].pin;
-                       index = 0;
-                       name = channel_name[i];
-               } else {
-                       pin = cfg->line_out_pins[i];
-                       name = get_line_out_pfx(spec, i, true, &index);
-               }
-
-               path = snd_hda_get_nid_path(codec, dac, pin);
+               path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
                if (!path)
                        continue;
+
+               name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
                if (!name || !strcmp(name, "CLFE")) {
                        /* Center/LFE */
                        err = add_vol_ctl(codec, "Center", 0, 1, path);
@@ -1314,6 +1726,14 @@ static int create_multi_out_ctls(struct hda_codec *codec,
                        err = add_vol_ctl(codec, "LFE", 0, 2, path);
                        if (err < 0)
                                return err;
+               } else {
+                       err = add_stereo_vol(codec, name, index, path);
+                       if (err < 0)
+                               return err;
+               }
+
+               name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
+               if (!name || !strcmp(name, "CLFE")) {
                        err = add_sw_ctl(codec, "Center", 0, 1, path);
                        if (err < 0)
                                return err;
@@ -1321,9 +1741,6 @@ static int create_multi_out_ctls(struct hda_codec *codec,
                        if (err < 0)
                                return err;
                } else {
-                       err = add_stereo_vol(codec, name, index, path);
-                       if (err < 0)
-                               return err;
                        err = add_stereo_sw(codec, name, index, path);
                        if (err < 0)
                                return err;
@@ -1332,21 +1749,18 @@ static int create_multi_out_ctls(struct hda_codec *codec,
        return 0;
 }
 
-static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-                           hda_nid_t dac, const char *pfx, int cidx)
+static int create_extra_out(struct hda_codec *codec, int path_idx,
+                           const char *pfx, int cidx)
 {
        struct nid_path *path;
        int err;
 
-       path = snd_hda_get_nid_path(codec, dac, pin);
+       path = snd_hda_get_path_from_idx(codec, path_idx);
        if (!path)
                return 0;
-       /* bind volume control will be created in the case of dac = 0 */
-       if (dac) {
-               err = add_stereo_vol(codec, pfx, cidx, path);
-               if (err < 0)
-                       return err;
-       }
+       err = add_stereo_vol(codec, pfx, cidx, path);
+       if (err < 0)
+               return err;
        err = add_stereo_sw(codec, pfx, cidx, path);
        if (err < 0)
                return err;
@@ -1355,67 +1769,26 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
 
 /* add playback controls for speaker and HP outputs */
 static int create_extra_outs(struct hda_codec *codec, int num_pins,
-                            const hda_nid_t *pins, const hda_nid_t *dacs,
-                            const char *pfx)
+                            const int *paths, const char *pfx)
 {
-       struct hda_gen_spec *spec = codec->spec;
-       struct hda_bind_ctls *ctl;
-       char name[32];
-       int i, n, err;
-
-       if (!num_pins || !pins[0])
-               return 0;
-
-       if (num_pins == 1) {
-               hda_nid_t dac = *dacs;
-               if (!dac)
-                       dac = spec->multiout.dac_nids[0];
-               return create_extra_out(codec, *pins, dac, pfx, 0);
-       }
+       int i;
 
        for (i = 0; i < num_pins; i++) {
-               hda_nid_t dac;
-               if (dacs[num_pins - 1])
-                       dac = dacs[i]; /* with individual volumes */
-               else
-                       dac = 0;
-               if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
-                       err = create_extra_out(codec, pins[i], dac,
-                                              "Bass Speaker", 0);
-               } else if (num_pins >= 3) {
-                       snprintf(name, sizeof(name), "%s %s",
+               const char *name;
+               char tmp[44];
+               int err, idx = 0;
+
+               if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
+                       name = "Bass Speaker";
+               else if (num_pins >= 3) {
+                       snprintf(tmp, sizeof(tmp), "%s %s",
                                 pfx, channel_name[i]);
-                       err = create_extra_out(codec, pins[i], dac, name, 0);
+                       name = tmp;
                } else {
-                       err = create_extra_out(codec, pins[i], dac, pfx, i);
+                       name = pfx;
+                       idx = i;
                }
-               if (err < 0)
-                       return err;
-       }
-       if (dacs[num_pins - 1])
-               return 0;
-
-       /* Let's create a bind-controls for volumes */
-       ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
-       if (!ctl)
-               return -ENOMEM;
-       n = 0;
-       for (i = 0; i < num_pins; i++) {
-               hda_nid_t vol;
-               struct nid_path *path;
-               if (!pins[i] || !dacs[i])
-                       continue;
-               path = snd_hda_get_nid_path(codec, dacs[i], pins[i]);
-               if (!path)
-                       continue;
-               vol = look_for_out_vol_nid(codec, path);
-               if (vol)
-                       ctl->values[n++] =
-                               HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
-       }
-       if (n) {
-               snprintf(name, sizeof(name), "%s Playback Volume", pfx);
-               err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl);
+               err = create_extra_out(codec, paths[i], name, idx);
                if (err < 0)
                        return err;
        }
@@ -1426,8 +1799,7 @@ static int create_hp_out_ctls(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
        return create_extra_outs(codec, spec->autocfg.hp_outs,
-                                spec->autocfg.hp_pins,
-                                spec->multiout.hp_out_nid,
+                                spec->hp_paths,
                                 "Headphone");
 }
 
@@ -1435,11 +1807,83 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
        return create_extra_outs(codec, spec->autocfg.speaker_outs,
-                                spec->autocfg.speaker_pins,
-                                spec->multiout.extra_out_nid,
+                                spec->speaker_paths,
                                 "Speaker");
 }
 
+/*
+ * independent HP controls
+ */
+
+static int indep_hp_info(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_info *uinfo)
+{
+       return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
+
+static int indep_hp_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gen_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
+       return 0;
+}
+
+static int indep_hp_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gen_spec *spec = codec->spec;
+       unsigned int select = ucontrol->value.enumerated.item[0];
+       int ret = 0;
+
+       mutex_lock(&spec->pcm_mutex);
+       if (spec->active_streams) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       if (spec->indep_hp_enabled != select) {
+               spec->indep_hp_enabled = select;
+               if (spec->indep_hp_enabled)
+                       spec->multiout.hp_out_nid[0] = 0;
+               else
+                       spec->multiout.hp_out_nid[0] = spec->alt_dac_nid;
+               ret = 1;
+       }
+ unlock:
+       mutex_unlock(&spec->pcm_mutex);
+       return ret;
+}
+
+static const struct snd_kcontrol_new indep_hp_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Independent HP",
+       .info = indep_hp_info,
+       .get = indep_hp_get,
+       .put = indep_hp_put,
+};
+
+
+static int create_indep_hp_ctls(struct hda_codec *codec)
+{
+       struct hda_gen_spec *spec = codec->spec;
+
+       if (!spec->indep_hp)
+               return 0;
+       if (!spec->multiout.hp_out_nid[0]) {
+               spec->indep_hp = 0;
+               return 0;
+       }
+
+       spec->indep_hp_enabled = false;
+       spec->alt_dac_nid = spec->multiout.hp_out_nid[0];
+       if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
+               return -ENOMEM;
+       return 0;
+}
+
 /*
  * channel mode enum control
  */
@@ -1449,14 +1893,15 @@ static int ch_mode_info(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
+       int chs;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
        uinfo->value.enumerated.items = spec->multi_ios + 1;
        if (uinfo->value.enumerated.item > spec->multi_ios)
                uinfo->value.enumerated.item = spec->multi_ios;
-       sprintf(uinfo->value.enumerated.name, "%dch",
-               (uinfo->value.enumerated.item + 1) * 2);
+       chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
+       sprintf(uinfo->value.enumerated.name, "%dch", chs);
        return 0;
 }
 
@@ -1465,17 +1910,28 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
-       ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
+       ucontrol->value.enumerated.item[0] =
+               (spec->ext_channel_count - spec->min_channel_count) / 2;
        return 0;
 }
 
+static inline struct nid_path *
+get_multiio_path(struct hda_codec *codec, int idx)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       return snd_hda_get_path_from_idx(codec,
+               spec->out_paths[spec->autocfg.line_outs + idx]);
+}
+
+static void update_automute_all(struct hda_codec *codec);
+
 static int set_multi_io(struct hda_codec *codec, int idx, bool output)
 {
        struct hda_gen_spec *spec = codec->spec;
        hda_nid_t nid = spec->multi_io[idx].pin;
        struct nid_path *path;
 
-       path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid);
+       path = get_multiio_path(codec, idx);
        if (!path)
                return -EINVAL;
 
@@ -1483,13 +1939,18 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output)
                return 0;
 
        if (output) {
-               snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
+               set_pin_target(codec, nid, PIN_OUT, true);
                snd_hda_activate_path(codec, path, true, true);
+               set_pin_eapd(codec, nid, true);
        } else {
+               set_pin_eapd(codec, nid, false);
                snd_hda_activate_path(codec, path, false, true);
-               snd_hda_set_pin_ctl_cache(codec, nid,
-                                         spec->multi_io[idx].ctl_in);
+               set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
        }
+
+       /* update jack retasking in case it modifies any of them */
+       update_automute_all(codec);
+
        return 0;
 }
 
@@ -1503,9 +1964,9 @@ static int ch_mode_put(struct snd_kcontrol *kcontrol,
        ch = ucontrol->value.enumerated.item[0];
        if (ch < 0 || ch > spec->multi_ios)
                return -EINVAL;
-       if (ch == (spec->ext_channel_count - 1) / 2)
+       if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
                return 0;
-       spec->ext_channel_count = (ch + 1) * 2;
+       spec->ext_channel_count = ch * 2 + spec->min_channel_count;
        for (i = 0; i < spec->multi_ios; i++)
                set_multi_io(codec, i, i < ch);
        spec->multiout.max_channels = max(spec->ext_channel_count,
@@ -1534,6 +1995,80 @@ static int create_multi_channel_mode(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * aamix loopback enable/disable switch
+ */
+
+#define loopback_mixing_info   indep_hp_info
+
+static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gen_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->aamix_mode;
+       return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+                              int nomix_path_idx, int mix_path_idx)
+{
+       struct nid_path *nomix_path, *mix_path;
+
+       nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
+       mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
+       if (!nomix_path || !mix_path)
+               return;
+       if (do_mix) {
+               snd_hda_activate_path(codec, nomix_path, false, true);
+               snd_hda_activate_path(codec, mix_path, true, true);
+       } else {
+               snd_hda_activate_path(codec, mix_path, false, true);
+               snd_hda_activate_path(codec, nomix_path, true, true);
+       }
+}
+
+static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gen_spec *spec = codec->spec;
+       unsigned int val = ucontrol->value.enumerated.item[0];
+
+       if (val == spec->aamix_mode)
+               return 0;
+       spec->aamix_mode = val;
+       update_aamix_paths(codec, val, spec->out_paths[0],
+                          spec->aamix_out_paths[0]);
+       update_aamix_paths(codec, val, spec->hp_paths[0],
+                          spec->aamix_out_paths[1]);
+       update_aamix_paths(codec, val, spec->speaker_paths[0],
+                          spec->aamix_out_paths[2]);
+       return 1;
+}
+
+static const struct snd_kcontrol_new loopback_mixing_enum = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Loopback Mixing",
+       .info = loopback_mixing_info,
+       .get = loopback_mixing_get,
+       .put = loopback_mixing_put,
+};
+
+static int create_loopback_mixing_ctl(struct hda_codec *codec)
+{
+       struct hda_gen_spec *spec = codec->spec;
+
+       if (!spec->mixer_nid)
+               return 0;
+       if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+             spec->aamix_out_paths[2]))
+               return 0;
+       if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
+               return -ENOMEM;
+       return 0;
+}
+
 /*
  * shared headphone/mic handling
  */
@@ -1558,11 +2093,12 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
                const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
                unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
                if (vref_val != AC_PINCTL_VREF_HIZ)
-                       snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
+                       snd_hda_set_pin_ctl_cache(codec, vref_pin,
+                                       PIN_IN | (set_as_mic ? vref_val : 0));
        }
 
        val = set_as_mic ? val | PIN_IN : PIN_HP;
-       snd_hda_set_pin_ctl(codec, pin, val);
+       set_pin_target(codec, pin, val, true);
 
        spec->automute_speaker = !set_as_mic;
        call_update_outputs(codec);
@@ -1601,6 +2137,231 @@ static int create_shared_input(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * output jack mode
+ */
+static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *uinfo)
+{
+       static const char * const texts[] = {
+               "Line Out", "Headphone Out",
+       };
+       return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts);
+}
+
+static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP)
+               ucontrol->value.enumerated.item[0] = 1;
+       else
+               ucontrol->value.enumerated.item[0] = 0;
+       return 0;
+}
+
+static int out_jack_mode_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int val;
+
+       val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT;
+       if (snd_hda_codec_get_pin_target(codec, nid) == val)
+               return 0;
+       snd_hda_set_pin_ctl_cache(codec, nid, val);
+       return 1;
+}
+
+static const struct snd_kcontrol_new out_jack_mode_enum = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = out_jack_mode_info,
+       .get = out_jack_mode_get,
+       .put = out_jack_mode_put,
+};
+
+static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->kctls.used; i++) {
+               struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i);
+               if (!strcmp(kctl->name, name) && kctl->index == idx)
+                       return true;
+       }
+       return false;
+}
+
+static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
+                              char *name, size_t name_len)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int idx = 0;
+
+       snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx);
+       strlcat(name, " Jack Mode", name_len);
+
+       for (; find_kctl_name(codec, name, idx); idx++)
+               ;
+}
+
+static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
+                                hda_nid_t *pins)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < num_pins; i++) {
+               hda_nid_t pin = pins[i];
+               unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+               if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) {
+                       struct snd_kcontrol_new *knew;
+                       char name[44];
+                       get_jack_mode_name(codec, pin, name, sizeof(name));
+                       knew = snd_hda_gen_add_kctl(spec, name,
+                                                   &out_jack_mode_enum);
+                       if (!knew)
+                               return -ENOMEM;
+                       knew->private_value = pin;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * input jack mode
+ */
+
+/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */
+#define NUM_VREFS      6
+
+static const char * const vref_texts[NUM_VREFS] = {
+       "Line In", "Mic 50pc Bias", "Mic 0V Bias",
+       "", "Mic 80pc Bias", "Mic 100pc Bias"
+};
+
+static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin)
+{
+       unsigned int pincap;
+
+       pincap = snd_hda_query_pin_caps(codec, pin);
+       pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+       /* filter out unusual vrefs */
+       pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100);
+       return pincap;
+}
+
+/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */
+static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx)
+{
+       unsigned int i, n = 0;
+
+       for (i = 0; i < NUM_VREFS; i++) {
+               if (vref_caps & (1 << i)) {
+                       if (n == item_idx)
+                               return i;
+                       n++;
+               }
+       }
+       return 0;
+}
+
+/* convert back from the vref ctl index to the enum item index */
+static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx)
+{
+       unsigned int i, n = 0;
+
+       for (i = 0; i < NUM_VREFS; i++) {
+               if (i == idx)
+                       return n;
+               if (vref_caps & (1 << i))
+                       n++;
+       }
+       return 0;
+}
+
+static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int vref_caps = get_vref_caps(codec, nid);
+
+       snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
+                                vref_texts);
+       /* set the right text */
+       strcpy(uinfo->value.enumerated.name,
+              vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
+       return 0;
+}
+
+static int in_jack_mode_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int vref_caps = get_vref_caps(codec, nid);
+       unsigned int idx;
+
+       idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN;
+       ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx);
+       return 0;
+}
+
+static int in_jack_mode_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int vref_caps = get_vref_caps(codec, nid);
+       unsigned int val, idx;
+
+       val = snd_hda_codec_get_pin_target(codec, nid);
+       idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN);
+       if (idx == ucontrol->value.enumerated.item[0])
+               return 0;
+
+       val &= ~AC_PINCTL_VREFEN;
+       val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]);
+       snd_hda_set_pin_ctl_cache(codec, nid, val);
+       return 1;
+}
+
+static const struct snd_kcontrol_new in_jack_mode_enum = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = in_jack_mode_info,
+       .get = in_jack_mode_get,
+       .put = in_jack_mode_put,
+};
+
+static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       unsigned int defcfg;
+       struct snd_kcontrol_new *knew;
+       char name[44];
+
+       /* no jack mode for fixed pins */
+       defcfg = snd_hda_codec_get_pincfg(codec, pin);
+       if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+               return 0;
+
+       /* no multiple vref caps? */
+       if (hweight32(get_vref_caps(codec, pin)) <= 1)
+               return 0;
+
+       get_jack_mode_name(codec, pin, name, sizeof(name));
+       knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
+       if (!knew)
+               return -ENOMEM;
+       knew->private_value = pin;
+       return 0;
+}
+
 
 /*
  * Parse input paths
@@ -1626,8 +2387,8 @@ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
 #endif
 
 /* create input playback/capture controls for the given pin */
-static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
-                           const char *ctlname, int ctlidx,
+static int new_analog_input(struct hda_codec *codec, int input_idx,
+                           hda_nid_t pin, const char *ctlname, int ctlidx,
                            hda_nid_t mix_nid)
 {
        struct hda_gen_spec *spec = codec->spec;
@@ -1639,9 +2400,11 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
            !nid_has_mute(codec, mix_nid, HDA_INPUT))
                return 0; /* no need for analog loopback */
 
-       path = snd_hda_add_new_path(codec, pin, mix_nid, 2);
+       path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
        if (!path)
                return -EINVAL;
+       print_nid_path("loopback", path);
+       spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
 
        idx = path->idx[path->depth - 1];
        if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
@@ -1692,6 +2455,11 @@ static int fill_adc_nids(struct hda_codec *codec)
                        break;
        }
        spec->num_adc_nids = nums;
+
+       /* copy the detected ADCs to all_adcs[] */
+       spec->num_all_adcs = nums;
+       memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
+
        return nums;
 }
 
@@ -1702,24 +2470,24 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->input_mux;
-       hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)];
+       unsigned int ok_bits;
        int i, n, nums;
-       hda_nid_t pin, adc;
 
  again:
        nums = 0;
+       ok_bits = 0;
        for (n = 0; n < spec->num_adc_nids; n++) {
-               adc = spec->adc_nids[n];
                for (i = 0; i < imux->num_items; i++) {
-                       pin = spec->imux_pins[i];
-                       if (!is_reachable_path(codec, pin, adc))
+                       if (!spec->input_paths[i][n])
                                break;
                }
-               if (i >= imux->num_items)
-                       adc_nids[nums++] = adc;
+               if (i >= imux->num_items) {
+                       ok_bits |= (1 << n);
+                       nums++;
+               }
        }
 
-       if (!nums) {
+       if (!ok_bits) {
                if (spec->shared_mic_hp) {
                        spec->shared_mic_hp = 0;
                        imux->num_items = 1;
@@ -1728,10 +2496,8 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
 
                /* check whether ADC-switch is possible */
                for (i = 0; i < imux->num_items; i++) {
-                       pin = spec->imux_pins[i];
                        for (n = 0; n < spec->num_adc_nids; n++) {
-                               adc = spec->adc_nids[n];
-                               if (is_reachable_path(codec, pin, adc)) {
+                               if (spec->input_paths[i][n]) {
                                        spec->dyn_adc_idx[i] = n;
                                        break;
                                }
@@ -1741,7 +2507,22 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
                snd_printdd("hda-codec: enabling ADC switching\n");
                spec->dyn_adc_switch = 1;
        } else if (nums != spec->num_adc_nids) {
-               memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t));
+               /* shrink the invalid adcs and input paths */
+               nums = 0;
+               for (n = 0; n < spec->num_adc_nids; n++) {
+                       if (!(ok_bits & (1 << n)))
+                               continue;
+                       if (n != nums) {
+                               spec->adc_nids[nums] = spec->adc_nids[n];
+                               for (i = 0; i < imux->num_items; i++) {
+                                       invalidate_nid_path(codec,
+                                               spec->input_paths[i][nums]);
+                                       spec->input_paths[i][nums] =
+                                               spec->input_paths[i][n];
+                               }
+                       }
+                       nums++;
+               }
                spec->num_adc_nids = nums;
        }
 
@@ -1757,78 +2538,137 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
        return 0;
 }
 
+/* parse capture source paths from the given pin and create imux items */
+static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin,
+                               int cfg_idx, int num_adcs,
+                               const char *label, int anchor)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       struct hda_input_mux *imux = &spec->input_mux;
+       int imux_idx = imux->num_items;
+       bool imux_added = false;
+       int c;
+
+       for (c = 0; c < num_adcs; c++) {
+               struct nid_path *path;
+               hda_nid_t adc = spec->adc_nids[c];
+
+               if (!is_reachable_path(codec, pin, adc))
+                       continue;
+               path = snd_hda_add_new_path(codec, pin, adc, anchor);
+               if (!path)
+                       continue;
+               print_nid_path("input", path);
+               spec->input_paths[imux_idx][c] =
+                       snd_hda_get_path_idx(codec, path);
+
+               if (!imux_added) {
+                       spec->imux_pins[imux->num_items] = pin;
+                       snd_hda_add_imux_item(imux, label, cfg_idx, NULL);
+                       imux_added = true;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * create playback/capture controls for input pins
  */
+
+/* fill the label for each input at first */
+static int fill_input_pin_labels(struct hda_codec *codec)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       const struct auto_pin_cfg *cfg = &spec->autocfg;
+       int i;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               hda_nid_t pin = cfg->inputs[i].pin;
+               const char *label;
+               int j, idx;
+
+               if (!is_input_pin(codec, pin))
+                       continue;
+
+               label = hda_get_autocfg_input_label(codec, cfg, i);
+               idx = 0;
+               for (j = i - 1; j >= 0; j--) {
+                       if (spec->input_labels[j] &&
+                           !strcmp(spec->input_labels[j], label)) {
+                               idx = spec->input_label_idxs[j] + 1;
+                               break;
+                       }
+               }
+
+               spec->input_labels[i] = label;
+               spec->input_label_idxs[i] = idx;
+       }
+
+       return 0;
+}
+
+#define CFG_IDX_MIX    99      /* a dummy cfg->input idx for stereo mix */
+
 static int create_input_ctls(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
        const struct auto_pin_cfg *cfg = &spec->autocfg;
        hda_nid_t mixer = spec->mixer_nid;
-       struct hda_input_mux *imux = &spec->input_mux;
        int num_adcs;
-       int i, c, err, type_idx = 0;
-       const char *prev_label = NULL;
+       int i, err;
+       unsigned int val;
 
        num_adcs = fill_adc_nids(codec);
        if (num_adcs < 0)
                return 0;
 
+       err = fill_input_pin_labels(codec);
+       if (err < 0)
+               return err;
+
        for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t pin;
-               const char *label;
-               bool imux_added;
 
                pin = cfg->inputs[i].pin;
                if (!is_input_pin(codec, pin))
                        continue;
 
-               label = hda_get_autocfg_input_label(codec, cfg, i);
-               if (spec->shared_mic_hp && !strcmp(label, "Misc"))
-                       label = "Headphone Mic";
-               if (prev_label && !strcmp(label, prev_label))
-                       type_idx++;
-               else
-                       type_idx = 0;
-               prev_label = label;
+               val = PIN_IN;
+               if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                       val |= snd_hda_get_default_vref(codec, pin);
+               set_pin_target(codec, pin, val, false);
 
                if (mixer) {
                        if (is_reachable_path(codec, pin, mixer)) {
-                               err = new_analog_input(codec, pin,
-                                                      label, type_idx, mixer);
+                               err = new_analog_input(codec, i, pin,
+                                                      spec->input_labels[i],
+                                                      spec->input_label_idxs[i],
+                                                      mixer);
                                if (err < 0)
                                        return err;
                        }
                }
 
-               imux_added = false;
-               for (c = 0; c < num_adcs; c++) {
-                       struct nid_path *path;
-                       hda_nid_t adc = spec->adc_nids[c];
-
-                       if (!is_reachable_path(codec, pin, adc))
-                               continue;
-                       path = snd_array_new(&spec->paths);
-                       if (!path)
-                               return -ENOMEM;
-                       memset(path, 0, sizeof(*path));
-                       if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) {
-                               snd_printd(KERN_ERR
-                                          "invalid input path 0x%x -> 0x%x\n",
-                                          pin, adc);
-                               spec->paths.used--;
-                               continue;
-                       }
+               err = parse_capture_source(codec, pin, i, num_adcs,
+                                          spec->input_labels[i], -mixer);
+               if (err < 0)
+                       return err;
 
-                       if (!imux_added) {
-                               spec->imux_pins[imux->num_items] = pin;
-                               snd_hda_add_imux_item(imux, label,
-                                                     imux->num_items, NULL);
-                               imux_added = true;
-                       }
+               if (spec->add_in_jack_modes) {
+                       err = create_in_jack_mode(codec, pin);
+                       if (err < 0)
+                               return err;
                }
        }
 
+       if (mixer && spec->add_stereo_mix_input) {
+               err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs,
+                                          "Stereo Mix", 0);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -1837,13 +2677,21 @@ static int create_input_ctls(struct hda_codec *codec)
  * input source mux
  */
 
-/* get the ADC NID corresponding to the given index */
-static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
+/* get the input path specified by the given adc and imux indices */
+static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx)
 {
        struct hda_gen_spec *spec = codec->spec;
+       if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) {
+               snd_BUG();
+               return NULL;
+       }
        if (spec->dyn_adc_switch)
                adc_idx = spec->dyn_adc_idx[imux_idx];
-       return spec->adc_nids[adc_idx];
+       if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) {
+               snd_BUG();
+               return NULL;
+       }
+       return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]);
 }
 
 static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
@@ -1862,6 +2710,7 @@ static int mux_enum_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
+       /* the ctls are created at once with multiple counts */
        unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 
        ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
@@ -1904,7 +2753,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
        int i, adc_idx, err = 0;
 
        imux = &spec->input_mux;
-       adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       adc_idx = kcontrol->id.index;
        mutex_lock(&codec->control_mutex);
        /* we use the cache-only update at first since multiple input paths
         * may shared the same amp; by updating only caches, the redundant
@@ -1912,9 +2761,8 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
         */
        codec->cached_write = 1;
        for (i = 0; i < imux->num_items; i++) {
-               path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-                                           get_adc_nid(codec, adc_idx, i));
-               if (!path->ctls[type])
+               path = get_input_path(codec, adc_idx, i);
+               if (!path || !path->ctls[type])
                        continue;
                kcontrol->private_value = path->ctls[type];
                err = func(kcontrol, ucontrol);
@@ -1926,7 +2774,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
        mutex_unlock(&codec->control_mutex);
        snd_hda_codec_flush_amp_cache(codec); /* flush the updates */
        if (err >= 0 && spec->cap_sync_hook)
-               spec->cap_sync_hook(codec);
+               spec->cap_sync_hook(codec, ucontrol);
        return err;
 }
 
@@ -2034,6 +2882,24 @@ static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
        return false;
 }
 
+/* capture switch put callback for a single control with hook call */
+static int cap_single_sw_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gen_spec *spec = codec->spec;
+       int ret;
+
+       ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+       if (ret < 0)
+               return ret;
+
+       if (spec->cap_sync_hook)
+               spec->cap_sync_hook(codec, ucontrol);
+
+       return ret;
+}
+
 static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
                              int idx, bool is_switch, unsigned int ctl,
                              bool inv_dmic)
@@ -2043,7 +2909,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
        int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
        const char *sfx = is_switch ? "Switch" : "Volume";
        unsigned int chs = inv_dmic ? 1 : 3;
-       int err;
+       struct snd_kcontrol_new *knew;
 
        if (!ctl)
                return 0;
@@ -2054,10 +2920,14 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
        else
                snprintf(tmpname, sizeof(tmpname),
                         "Capture %s", sfx);
-       err = add_control(spec, type, tmpname, idx,
-                         amp_val_replace_channels(ctl, chs));
-       if (err < 0 || !inv_dmic)
-               return err;
+       knew = add_control(spec, type, tmpname, idx,
+                          amp_val_replace_channels(ctl, chs));
+       if (!knew)
+               return -ENOMEM;
+       if (is_switch)
+               knew->put = cap_single_sw_put;
+       if (!inv_dmic)
+               return 0;
 
        /* Make independent right kcontrol */
        if (label)
@@ -2066,8 +2936,13 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
        else
                snprintf(tmpname, sizeof(tmpname),
                         "Inverted Capture %s", sfx);
-       return add_control(spec, type, tmpname, idx,
+       knew = add_control(spec, type, tmpname, idx,
                           amp_val_replace_channels(ctl, 2));
+       if (!knew)
+               return -ENOMEM;
+       if (is_switch)
+               knew->put = cap_single_sw_put;
+       return 0;
 }
 
 /* create single (and simple) capture volume and switch controls */
@@ -2114,21 +2989,18 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
 /* return the vol ctl when used first in the imux list */
 static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
 {
-       struct hda_gen_spec *spec = codec->spec;
        struct nid_path *path;
        unsigned int ctl;
        int i;
 
-       path = snd_hda_get_nid_path(codec, spec->imux_pins[idx],
-                                   get_adc_nid(codec, 0, idx));
+       path = get_input_path(codec, 0, idx);
        if (!path)
                return 0;
        ctl = path->ctls[type];
        if (!ctl)
                return 0;
        for (i = 0; i < idx - 1; i++) {
-               path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-                                           get_adc_nid(codec, 0, i));
+               path = get_input_path(codec, 0, i);
                if (path && path->ctls[type] == ctl)
                        return 0;
        }
@@ -2140,22 +3012,22 @@ static int create_multi_cap_vol_ctl(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
        struct hda_input_mux *imux = &spec->input_mux;
-       int i, err, type, type_idx = 0;
-       const char *prev_label = NULL;
+       int i, err, type;
 
        for (i = 0; i < imux->num_items; i++) {
-               const char *label;
                bool inv_dmic;
-               label = hda_get_autocfg_input_label(codec, &spec->autocfg, i);
-               if (prev_label && !strcmp(label, prev_label))
-                       type_idx++;
-               else
-                       type_idx = 0;
-               prev_label = label;
+               int idx;
+
+               idx = imux->items[i].index;
+               if (idx >= spec->autocfg.num_inputs)
+                       continue;
                inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
 
                for (type = 0; type < 2; type++) {
-                       err = add_single_cap_ctl(codec, label, type_idx, type,
+                       err = add_single_cap_ctl(codec,
+                                                spec->input_labels[idx],
+                                                spec->input_label_idxs[idx],
+                                                type,
                                                 get_first_cap_ctl(codec, i, type),
                                                 inv_dmic);
                        if (err < 0)
@@ -2188,86 +3060,139 @@ static int create_capture_mixers(struct hda_codec *codec)
 
        for (n = 0; n < nums; n++) {
                bool multi = false;
+               bool multi_cap_vol = spec->multi_cap_vol;
                bool inv_dmic = false;
                int vol, sw;
 
                vol = sw = 0;
                for (i = 0; i < imux->num_items; i++) {
                        struct nid_path *path;
-                       path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-                                                   get_adc_nid(codec, n, i));
+                       path = get_input_path(codec, n, i);
                        if (!path)
                                continue;
                        parse_capvol_in_path(codec, path);
                        if (!vol)
                                vol = path->ctls[NID_PATH_VOL_CTL];
-                       else if (vol != path->ctls[NID_PATH_VOL_CTL])
+                       else if (vol != path->ctls[NID_PATH_VOL_CTL]) {
                                multi = true;
+                               if (!same_amp_caps(codec, vol,
+                                   path->ctls[NID_PATH_VOL_CTL], HDA_INPUT))
+                                       multi_cap_vol = true;
+                       }
                        if (!sw)
                                sw = path->ctls[NID_PATH_MUTE_CTL];
-                       else if (sw != path->ctls[NID_PATH_MUTE_CTL])
+                       else if (sw != path->ctls[NID_PATH_MUTE_CTL]) {
                                multi = true;
+                               if (!same_amp_caps(codec, sw,
+                                   path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT))
+                                       multi_cap_vol = true;
+                       }
                        if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
                                inv_dmic = true;
                }
 
-               if (!multi)
-                       err = create_single_cap_vol_ctl(codec, n, vol, sw,
-                                                       inv_dmic);
-               else if (!spec->multi_cap_vol)
-                       err = create_bind_cap_vol_ctl(codec, n, vol, sw);
-               else
-                       err = create_multi_cap_vol_ctl(codec);
-               if (err < 0)
-                       return err;
+               if (!multi)
+                       err = create_single_cap_vol_ctl(codec, n, vol, sw,
+                                                       inv_dmic);
+               else if (!multi_cap_vol)
+                       err = create_bind_cap_vol_ctl(codec, n, vol, sw);
+               else
+                       err = create_multi_cap_vol_ctl(codec);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+/*
+ * add mic boosts if needed
+ */
+
+/* check whether the given amp is feasible as a boost volume */
+static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid,
+                           int dir, int idx)
+{
+       unsigned int step;
+
+       if (!nid_has_volume(codec, nid, dir) ||
+           is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
+           is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
+               return false;
+
+       step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE)
+               >> AC_AMPCAP_STEP_SIZE_SHIFT;
+       if (step < 0x20)
+               return false;
+       return true;
+}
+
+/* look for a boost amp in a widget close to the pin */
+static unsigned int look_for_boost_amp(struct hda_codec *codec,
+                                      struct nid_path *path)
+{
+       unsigned int val = 0;
+       hda_nid_t nid;
+       int depth;
+
+       for (depth = 0; depth < 3; depth++) {
+               if (depth >= path->depth - 1)
+                       break;
+               nid = path->path[depth];
+               if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) {
+                       val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+                       break;
+               } else if (check_boost_vol(codec, nid, HDA_INPUT,
+                                          path->idx[depth])) {
+                       val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth],
+                                                 HDA_INPUT);
+                       break;
+               }
        }
 
-       return 0;
+       return val;
 }
 
-/*
- * add mic boosts if needed
- */
 static int parse_mic_boost(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
-       int i, err;
-       int type_idx = 0;
-       hda_nid_t nid;
-       const char *prev_label = NULL;
+       struct hda_input_mux *imux = &spec->input_mux;
+       int i;
 
-       for (i = 0; i < cfg->num_inputs; i++) {
-               if (cfg->inputs[i].type > AUTO_PIN_MIC)
-                       break;
-               nid = cfg->inputs[i].pin;
-               if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
-                       const char *label;
-                       char boost_label[32];
-                       struct nid_path *path;
-                       unsigned int val;
+       if (!spec->num_adc_nids)
+               return 0;
 
-                       label = hda_get_autocfg_input_label(codec, cfg, i);
-                       if (spec->shared_mic_hp && !strcmp(label, "Misc"))
-                               label = "Headphone Mic";
-                       if (prev_label && !strcmp(label, prev_label))
-                               type_idx++;
-                       else
-                               type_idx = 0;
-                       prev_label = label;
+       for (i = 0; i < imux->num_items; i++) {
+               struct nid_path *path;
+               unsigned int val;
+               int idx;
+               char boost_label[44];
 
-                       snprintf(boost_label, sizeof(boost_label),
-                                "%s Boost Volume", label);
-                       val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
-                       err = add_control(spec, HDA_CTL_WIDGET_VOL,
-                                         boost_label, type_idx, val);
-                       if (err < 0)
-                               return err;
+               idx = imux->items[i].index;
+               if (idx >= imux->num_items)
+                       continue;
 
-                       path = snd_hda_get_nid_path(codec, nid, 0);
-                       if (path)
-                               path->ctls[NID_PATH_BOOST_CTL] = val;
-               }
+               /* check only line-in and mic pins */
+               if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+                       continue;
+
+               path = get_input_path(codec, 0, i);
+               if (!path)
+                       continue;
+
+               val = look_for_boost_amp(codec, path);
+               if (!val)
+                       continue;
+
+               /* create a boost control */
+               snprintf(boost_label, sizeof(boost_label),
+                        "%s Boost Volume", spec->input_labels[idx]);
+               if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label,
+                                spec->input_label_idxs[idx], val))
+                       return -ENOMEM;
+
+               path->ctls[NID_PATH_BOOST_CTL] = val;
        }
        return 0;
 }
@@ -2278,18 +3203,24 @@ static int parse_mic_boost(struct hda_codec *codec)
 static void parse_digital(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
+       struct nid_path *path;
        int i, nums;
-       hda_nid_t dig_nid;
+       hda_nid_t dig_nid, pin;
 
        /* support multiple SPDIFs; the secondary is set up as a slave */
        nums = 0;
        for (i = 0; i < spec->autocfg.dig_outs; i++) {
-               hda_nid_t pin = spec->autocfg.dig_out_pins[i];
+               pin = spec->autocfg.dig_out_pins[i];
                dig_nid = look_for_dac(codec, pin, true);
                if (!dig_nid)
                        continue;
-               if (!snd_hda_add_new_path(codec, dig_nid, pin, 2))
+               path = snd_hda_add_new_path(codec, dig_nid, pin, 0);
+               if (!path)
                        continue;
+               print_nid_path("digout", path);
+               path->active = true;
+               spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
+               set_pin_target(codec, pin, PIN_OUT, false);
                if (!nums) {
                        spec->multiout.dig_out_nid = dig_nid;
                        spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -2303,20 +3234,21 @@ static void parse_digital(struct hda_codec *codec)
        }
 
        if (spec->autocfg.dig_in_pin) {
+               pin = spec->autocfg.dig_in_pin;
                dig_nid = codec->start_nid;
                for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
-                       struct nid_path *path;
                        unsigned int wcaps = get_wcaps(codec, dig_nid);
                        if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
                                continue;
                        if (!(wcaps & AC_WCAP_DIGITAL))
                                continue;
-                       path = snd_hda_add_new_path(codec,
-                                                   spec->autocfg.dig_in_pin,
-                                                   dig_nid, 2);
+                       path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
                        if (path) {
+                               print_nid_path("digin", path);
                                path->active = true;
                                spec->dig_in_nid = dig_nid;
+                               spec->digin_path = snd_hda_get_path_idx(codec, path);
+                               set_pin_target(codec, pin, PIN_IN, false);
                                break;
                        }
                }
@@ -2347,9 +3279,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
        if (spec->cur_mux[adc_idx] == idx)
                return 0;
 
-       path = snd_hda_get_nid_path(codec,
-                                   spec->imux_pins[spec->cur_mux[adc_idx]],
-                                   spec->adc_nids[adc_idx]);
+       path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
        if (!path)
                return 0;
        if (path->active)
@@ -2363,15 +3293,14 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
        if (spec->dyn_adc_switch)
                dyn_adc_pcm_resetup(codec, idx);
 
-       path = snd_hda_get_nid_path(codec, spec->imux_pins[idx],
-                                   get_adc_nid(codec, adc_idx, idx));
+       path = get_input_path(codec, adc_idx, idx);
        if (!path)
                return 0;
        if (path->active)
                return 0;
        snd_hda_activate_path(codec, path, true, false);
        if (spec->cap_sync_hook)
-               spec->cap_sync_hook(codec);
+               spec->cap_sync_hook(codec, NULL);
        return 1;
 }
 
@@ -2389,6 +3318,9 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
                hda_nid_t nid = pins[i];
                if (!nid)
                        break;
+               /* don't detect pins retasked as inputs */
+               if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
+                       continue;
                present |= snd_hda_jack_detect(codec, nid);
        }
        return present;
@@ -2396,10 +3328,9 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
 
 /* standard HP/line-out auto-mute helper */
 static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
-                       bool mute, bool hp_out)
+                       bool mute)
 {
        struct hda_gen_spec *spec = codec->spec;
-       unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
        int i;
 
        for (i = 0; i < num_pins; i++) {
@@ -2410,14 +3341,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
                /* don't reset VREF value in case it's controlling
                 * the amp (see alc861_fixup_asus_amp_vref_0f())
                 */
-               if (spec->keep_vref_in_automute) {
-                       val = snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-                       val &= ~PIN_HP;
-               } else
+               if (spec->keep_vref_in_automute)
+                       val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP;
+               else
                        val = 0;
-               val |= pin_bits;
-               snd_hda_set_pin_ctl(codec, nid, val);
+               if (!mute)
+                       val |= snd_hda_codec_get_pin_target(codec, nid);
+               /* here we call update_pin_ctl() so that the pinctl is changed
+                * without changing the pinctl target value;
+                * the original target value will be still referred at the
+                * init / resume again
+                */
+               update_pin_ctl(codec, nid, val);
+               set_pin_eapd(codec, nid, !mute);
        }
 }
 
@@ -2433,15 +3369,16 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
         */
        if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
                do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
-                   spec->autocfg.hp_pins, spec->master_mute, true);
+                   spec->autocfg.hp_pins, spec->master_mute);
 
        if (!spec->automute_speaker)
                on = 0;
        else
                on = spec->hp_jack_present | spec->line_jack_present;
        on |= spec->master_mute;
+       spec->speaker_muted = on;
        do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
-                   spec->autocfg.speaker_pins, on, false);
+                   spec->autocfg.speaker_pins, on);
 
        /* toggle line-out mutes if needed, too */
        /* if LO is a copy of either HP or Speaker, don't need to handle it */
@@ -2453,8 +3390,9 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
        else
                on = spec->hp_jack_present;
        on |= spec->master_mute;
+       spec->line_out_muted = on;
        do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
-                   spec->autocfg.line_out_pins, on, false);
+                   spec->autocfg.line_out_pins, on);
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
 
@@ -2511,7 +3449,11 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
                return;
 
        for (i = spec->am_num_entries - 1; i > 0; i--) {
-               if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) {
+               hda_nid_t pin = spec->am_entry[i].pin;
+               /* don't detect pins retasked as outputs */
+               if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
+                       continue;
+               if (snd_hda_jack_detect(codec, pin)) {
                        mux_select(codec, 0, spec->am_entry[i].idx);
                        return;
                }
@@ -2520,6 +3462,25 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch);
 
+/* update jack retasking */
+static void update_automute_all(struct hda_codec *codec)
+{
+       struct hda_gen_spec *spec = codec->spec;
+
+       if (spec->hp_automute_hook)
+               spec->hp_automute_hook(codec, NULL);
+       else
+               snd_hda_gen_hp_automute(codec, NULL);
+       if (spec->line_automute_hook)
+               spec->line_automute_hook(codec, NULL);
+       else
+               snd_hda_gen_line_automute(codec, NULL);
+       if (spec->mic_autoswitch_hook)
+               spec->mic_autoswitch_hook(codec, NULL);
+       else
+               snd_hda_gen_mic_autoswitch(codec, NULL);
+}
+
 /*
  * Auto-Mute mode mixer enum support
  */
@@ -2621,6 +3582,9 @@ static int check_auto_mute_availability(struct hda_codec *codec)
        int present = 0;
        int i, err;
 
+       if (spec->suppress_auto_mute)
+               return 0;
+
        if (cfg->hp_pins[0])
                present++;
        if (cfg->line_out_pins[0])
@@ -2651,6 +3615,8 @@ static int check_auto_mute_availability(struct hda_codec *codec)
                snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n",
                            nid);
                snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
+                                                   spec->hp_automute_hook ?
+                                                   spec->hp_automute_hook :
                                                    snd_hda_gen_hp_automute);
                spec->detect_hp = 1;
        }
@@ -2664,6 +3630,8 @@ static int check_auto_mute_availability(struct hda_codec *codec)
                                snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid);
                                snd_hda_jack_detect_enable_callback(codec, nid,
                                                                    HDA_GEN_FRONT_EVENT,
+                                                                   spec->line_automute_hook ?
+                                                                   spec->line_automute_hook :
                                                                    snd_hda_gen_line_automute);
                                spec->detect_lo = 1;
                        }
@@ -2685,16 +3653,6 @@ static int check_auto_mute_availability(struct hda_codec *codec)
        return 0;
 }
 
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
-       int i;
-       for (i = 0; i < nums; i++)
-               if (list[i] == nid)
-                       return i;
-       return -1;
-}
-
 /* check whether all auto-mic pins are valid; setup indices if OK */
 static bool auto_mic_check_imux(struct hda_codec *codec)
 {
@@ -2716,6 +3674,8 @@ static bool auto_mic_check_imux(struct hda_codec *codec)
                snd_hda_jack_detect_enable_callback(codec,
                                                    spec->am_entry[i].pin,
                                                    HDA_GEN_MIC_EVENT,
+                                                   spec->mic_autoswitch_hook ?
+                                                   spec->mic_autoswitch_hook :
                                                    snd_hda_gen_mic_autoswitch);
        return true;
 }
@@ -2738,6 +3698,9 @@ static int check_auto_mic_availability(struct hda_codec *codec)
        unsigned int types;
        int i, num_pins;
 
+       if (spec->suppress_auto_mic)
+               return 0;
+
        types = 0;
        num_pins = 0;
        for (i = 0; i < cfg->num_inputs; i++) {
@@ -2809,11 +3772,15 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
        struct hda_gen_spec *spec = codec->spec;
        int err;
 
+       parse_user_hints(codec);
+
        if (cfg != &spec->autocfg) {
                spec->autocfg = *cfg;
                cfg = &spec->autocfg;
        }
 
+       fill_all_dac_nids(codec);
+
        if (!cfg->line_outs) {
                if (cfg->dig_outs || cfg->dig_in_pin) {
                        spec->multiout.max_channels = 2;
@@ -2850,6 +3817,12 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
        if (err < 0)
                return err;
        err = create_speaker_out_ctls(codec);
+       if (err < 0)
+               return err;
+       err = create_indep_hp_ctls(codec);
+       if (err < 0)
+               return err;
+       err = create_loopback_mixing_ctl(codec);
        if (err < 0)
                return err;
        err = create_shared_input(codec);
@@ -2859,17 +3832,16 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
        if (err < 0)
                return err;
 
-       /* check the multiple speaker pins */
-       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-               spec->const_channel_count = cfg->line_outs * 2;
-       else
-               spec->const_channel_count = cfg->speaker_outs * 2;
-
-       if (spec->multi_ios > 0)
-               spec->multiout.max_channels = max(spec->ext_channel_count,
-                                                 spec->const_channel_count);
-       else
-               spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+       spec->const_channel_count = spec->ext_channel_count;
+       /* check the multiple speaker and headphone pins */
+       if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+               spec->const_channel_count = max(spec->const_channel_count,
+                                               cfg->speaker_outs * 2);
+       if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+               spec->const_channel_count = max(spec->const_channel_count,
+                                               cfg->hp_outs * 2);
+       spec->multiout.max_channels = max(spec->ext_channel_count,
+                                         spec->const_channel_count);
 
        err = check_auto_mute_availability(codec);
        if (err < 0)
@@ -2893,6 +3865,21 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
        if (err < 0)
                return err;
 
+       if (spec->add_out_jack_modes) {
+               if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+                       err = create_out_jack_modes(codec, cfg->line_outs,
+                                                   cfg->line_out_pins);
+                       if (err < 0)
+                               return err;
+               }
+               if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+                       err = create_out_jack_modes(codec, cfg->hp_outs,
+                                                   cfg->hp_pins);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
  dig_only:
        parse_digital(codec);
 
@@ -2910,6 +3897,9 @@ static const char * const slave_pfxs[] = {
        "Front", "Surround", "Center", "LFE", "Side",
        "Headphone", "Speaker", "Mono", "Line Out",
        "CLFE", "Bass Speaker", "PCM",
+       "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side",
+       "Headphone Front", "Headphone Surround", "Headphone CLFE",
+       "Headphone Side",
        NULL,
 };
 
@@ -2948,11 +3938,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
        /* if we have no master control, let's create it */
        if (!spec->no_analog &&
            !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
-               unsigned int vmaster_tlv[4];
-               snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
-                                       HDA_OUTPUT, vmaster_tlv);
                err = snd_hda_add_vmaster(codec, "Master Playback Volume",
-                                         vmaster_tlv, slave_pfxs,
+                                         spec->vmaster_tlv, slave_pfxs,
                                          "Playback Volume");
                if (err < 0)
                        return err;
@@ -2996,6 +3983,26 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls);
  * PCM definitions
  */
 
+static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  struct snd_pcm_substream *substream,
+                                  int action)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       if (spec->pcm_playback_hook)
+               spec->pcm_playback_hook(hinfo, codec, substream, action);
+}
+
+static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo,
+                                 struct hda_codec *codec,
+                                 struct snd_pcm_substream *substream,
+                                 int action)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       if (spec->pcm_capture_hook)
+               spec->pcm_capture_hook(hinfo, codec, substream, action);
+}
+
 /*
  * Analog playback callbacks
  */
@@ -3004,8 +4011,19 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo,
                             struct snd_pcm_substream *substream)
 {
        struct hda_gen_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+       int err;
+
+       mutex_lock(&spec->pcm_mutex);
+       err = snd_hda_multi_out_analog_open(codec,
+                                           &spec->multiout, substream,
                                             hinfo);
+       if (!err) {
+               spec->active_streams |= 1 << STREAM_MULTI_OUT;
+               call_pcm_playback_hook(hinfo, codec, substream,
+                                      HDA_GEN_PCM_ACT_OPEN);
+       }
+       mutex_unlock(&spec->pcm_mutex);
+       return err;
 }
 
 static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -3015,8 +4033,14 @@ static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                struct snd_pcm_substream *substream)
 {
        struct hda_gen_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
-                                               stream_tag, format, substream);
+       int err;
+
+       err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+                                              stream_tag, format, substream);
+       if (!err)
+               call_pcm_playback_hook(hinfo, codec, substream,
+                                      HDA_GEN_PCM_ACT_PREPARE);
+       return err;
 }
 
 static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -3024,7 +4048,117 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
                                struct snd_pcm_substream *substream)
 {
        struct hda_gen_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+       int err;
+
+       err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+       if (!err)
+               call_pcm_playback_hook(hinfo, codec, substream,
+                                      HDA_GEN_PCM_ACT_CLEANUP);
+       return err;
+}
+
+static int playback_pcm_close(struct hda_pcm_stream *hinfo,
+                             struct hda_codec *codec,
+                             struct snd_pcm_substream *substream)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       mutex_lock(&spec->pcm_mutex);
+       spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
+       call_pcm_playback_hook(hinfo, codec, substream,
+                              HDA_GEN_PCM_ACT_CLOSE);
+       mutex_unlock(&spec->pcm_mutex);
+       return 0;
+}
+
+static int capture_pcm_open(struct hda_pcm_stream *hinfo,
+                           struct hda_codec *codec,
+                           struct snd_pcm_substream *substream)
+{
+       call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN);
+       return 0;
+}
+
+static int capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                              struct hda_codec *codec,
+                              unsigned int stream_tag,
+                              unsigned int format,
+                              struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+       call_pcm_capture_hook(hinfo, codec, substream,
+                             HDA_GEN_PCM_ACT_PREPARE);
+       return 0;
+}
+
+static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                              struct hda_codec *codec,
+                              struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+       call_pcm_capture_hook(hinfo, codec, substream,
+                             HDA_GEN_PCM_ACT_CLEANUP);
+       return 0;
+}
+
+static int capture_pcm_close(struct hda_pcm_stream *hinfo,
+                            struct hda_codec *codec,
+                            struct snd_pcm_substream *substream)
+{
+       call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE);
+       return 0;
+}
+
+static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                struct hda_codec *codec,
+                                struct snd_pcm_substream *substream)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int err = 0;
+
+       mutex_lock(&spec->pcm_mutex);
+       if (!spec->indep_hp_enabled)
+               err = -EBUSY;
+       else
+               spec->active_streams |= 1 << STREAM_INDEP_HP;
+       call_pcm_playback_hook(hinfo, codec, substream,
+                              HDA_GEN_PCM_ACT_OPEN);
+       mutex_unlock(&spec->pcm_mutex);
+       return err;
+}
+
+static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                 struct hda_codec *codec,
+                                 struct snd_pcm_substream *substream)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       mutex_lock(&spec->pcm_mutex);
+       spec->active_streams &= ~(1 << STREAM_INDEP_HP);
+       call_pcm_playback_hook(hinfo, codec, substream,
+                              HDA_GEN_PCM_ACT_CLOSE);
+       mutex_unlock(&spec->pcm_mutex);
+       return 0;
+}
+
+static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   unsigned int stream_tag,
+                                   unsigned int format,
+                                   struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+       call_pcm_playback_hook(hinfo, codec, substream,
+                              HDA_GEN_PCM_ACT_PREPARE);
+       return 0;
+}
+
+static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   struct snd_pcm_substream *substream)
+{
+       snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+       call_pcm_playback_hook(hinfo, codec, substream,
+                              HDA_GEN_PCM_ACT_CLEANUP);
+       return 0;
 }
 
 /*
@@ -3068,6 +4202,9 @@ static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
 /*
  * Analog capture
  */
+#define alt_capture_pcm_open   capture_pcm_open
+#define alt_capture_pcm_close  capture_pcm_close
+
 static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                   struct hda_codec *codec,
                                   unsigned int stream_tag,
@@ -3078,6 +4215,8 @@ static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
                                   stream_tag, 0, format);
+       call_pcm_capture_hook(hinfo, codec, substream,
+                             HDA_GEN_PCM_ACT_PREPARE);
        return 0;
 }
 
@@ -3089,6 +4228,8 @@ static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 
        snd_hda_codec_cleanup_stream(codec,
                                     spec->adc_nids[substream->number + 1]);
+       call_pcm_capture_hook(hinfo, codec, substream,
+                             HDA_GEN_PCM_ACT_CLEANUP);
        return 0;
 }
 
@@ -3101,6 +4242,7 @@ static const struct hda_pcm_stream pcm_analog_playback = {
        /* NID is set in build_pcms */
        .ops = {
                .open = playback_pcm_open,
+               .close = playback_pcm_close,
                .prepare = playback_pcm_prepare,
                .cleanup = playback_pcm_cleanup
        },
@@ -3111,6 +4253,12 @@ static const struct hda_pcm_stream pcm_analog_capture = {
        .channels_min = 2,
        .channels_max = 2,
        /* NID is set in build_pcms */
+       .ops = {
+               .open = capture_pcm_open,
+               .close = capture_pcm_close,
+               .prepare = capture_pcm_prepare,
+               .cleanup = capture_pcm_cleanup
+       },
 };
 
 static const struct hda_pcm_stream pcm_analog_alt_playback = {
@@ -3118,6 +4266,12 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = {
        .channels_min = 2,
        .channels_max = 2,
        /* NID is set in build_pcms */
+       .ops = {
+               .open = alt_playback_pcm_open,
+               .close = alt_playback_pcm_close,
+               .prepare = alt_playback_pcm_prepare,
+               .cleanup = alt_playback_pcm_cleanup
+       },
 };
 
 static const struct hda_pcm_stream pcm_analog_alt_capture = {
@@ -3126,6 +4280,8 @@ static const struct hda_pcm_stream pcm_analog_alt_capture = {
        .channels_max = 2,
        /* NID is set in build_pcms */
        .ops = {
+               .open = alt_capture_pcm_open,
+               .close = alt_capture_pcm_close,
                .prepare = alt_capture_pcm_prepare,
                .cleanup = alt_capture_pcm_cleanup
        },
@@ -3214,6 +4370,25 @@ static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
        },
 };
 
+static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
+                                const char *chip_name)
+{
+       char *p;
+
+       if (*str)
+               return;
+       strlcpy(str, chip_name, len);
+
+       /* drop non-alnum chars after a space */
+       for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
+               if (!isalnum(p[1])) {
+                       *p = 0;
+                       break;
+               }
+       }
+       strlcat(str, sfx, len);
+}
+
 /* build PCM streams based on the parsed results */
 int snd_hda_gen_build_pcms(struct hda_codec *codec)
 {
@@ -3221,7 +4396,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
        struct hda_pcm *info = spec->pcm_rec;
        const struct hda_pcm_stream *p;
        bool have_multi_adcs;
-       int i;
 
        codec->num_pcms = 1;
        codec->pcm_info = info;
@@ -3229,8 +4403,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
        if (spec->no_analog)
                goto skip_analog;
 
-       snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
-                "%s Analog", codec->chip_name);
+       fill_pcm_stream_name(spec->stream_name_analog,
+                            sizeof(spec->stream_name_analog),
+                            " Analog", codec->chip_name);
        info->name = spec->stream_name_analog;
 
        if (spec->multiout.num_dacs > 0) {
@@ -3258,21 +4433,12 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
                info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
        }
 
-       if (spec->channel_mode) {
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
-               for (i = 0; i < spec->num_channel_mode; i++) {
-                       if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
-                               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
-                       }
-               }
-       }
-
  skip_analog:
        /* SPDIF for stream index #1 */
        if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
-               snprintf(spec->stream_name_digital,
-                        sizeof(spec->stream_name_digital),
-                        "%s Digital", codec->chip_name);
+               fill_pcm_stream_name(spec->stream_name_digital,
+                                    sizeof(spec->stream_name_digital),
+                                    " Digital", codec->chip_name);
                codec->num_pcms = 2;
                codec->slave_dig_outs = spec->multiout.slave_dig_outs;
                info = spec->pcm_rec + 1;
@@ -3347,85 +4513,50 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
  * Standard auto-parser initializations
  */
 
-/* configure the path from the given dac to the pin as the proper output */
-static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin,
-                                 int pin_type, hda_nid_t dac)
+/* configure the given path as a proper output */
+static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
 {
-       struct hda_gen_spec *spec = codec->spec;
        struct nid_path *path;
+       hda_nid_t pin;
 
-       snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
-       path = snd_hda_get_nid_path(codec, dac, pin);
-       if (!path)
+       path = snd_hda_get_path_from_idx(codec, path_idx);
+       if (!path || !path->depth)
                return;
-       if (path->active)
-               return;
-       snd_hda_activate_path(codec, path, true, true);
-
-       if (!spec->own_eapd_ctl &&
-           (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
-               snd_hda_codec_update_cache(codec, pin, 0,
-                                          AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+       pin = path->path[path->depth - 1];
+       restore_pin_ctl(codec, pin);
+       snd_hda_activate_path(codec, path, path->active, true);
+       set_pin_eapd(codec, pin, path->active);
 }
 
 /* initialize primary output paths */
 static void init_multi_out(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
-       int pin_type;
        int i;
 
-       if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
-               pin_type = PIN_HP;
-       else
-               pin_type = PIN_OUT;
+       for (i = 0; i < spec->autocfg.line_outs; i++)
+               set_output_and_unmute(codec, spec->out_paths[i]);
+}
 
-       for (i = 0; i <= HDA_SIDE; i++) {
-               hda_nid_t nid = spec->autocfg.line_out_pins[i];
-               if (nid)
-                       set_output_and_unmute(codec, nid, pin_type,
-                                             spec->multiout.dac_nids[i]);
 
-       }
+static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
+{
+       int i;
+
+       for (i = 0; i < num_outs; i++)
+               set_output_and_unmute(codec, paths[i]);
 }
 
 /* initialize hp and speaker paths */
 static void init_extra_out(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
-       int i;
-       hda_nid_t pin, dac;
 
-       for (i = 0; i < spec->autocfg.hp_outs; i++) {
-               if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
-                       break;
-               pin = spec->autocfg.hp_pins[i];
-               if (!pin)
-                       break;
-               dac = spec->multiout.hp_out_nid[i];
-               if (!dac) {
-                       if (i > 0 && spec->multiout.hp_out_nid[0])
-                               dac = spec->multiout.hp_out_nid[0];
-                       else
-                               dac = spec->multiout.dac_nids[0];
-               }
-               set_output_and_unmute(codec, pin, PIN_HP, dac);
-       }
-       for (i = 0; i < spec->autocfg.speaker_outs; i++) {
-               if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
-                       break;
-               pin = spec->autocfg.speaker_pins[i];
-               if (!pin)
-                       break;
-               dac = spec->multiout.extra_out_nid[i];
-               if (!dac) {
-                       if (i > 0 && spec->multiout.extra_out_nid[0])
-                               dac = spec->multiout.extra_out_nid[0];
-                       else
-                               dac = spec->multiout.dac_nids[0];
-               }
-               set_output_and_unmute(codec, pin, PIN_OUT, dac);
-       }
+       if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
+               __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
+       if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
+               __init_extra_out(codec, spec->autocfg.speaker_outs,
+                                spec->speaker_paths);
 }
 
 /* initialize multi-io paths */
@@ -3437,27 +4568,16 @@ static void init_multi_io(struct hda_codec *codec)
        for (i = 0; i < spec->multi_ios; i++) {
                hda_nid_t pin = spec->multi_io[i].pin;
                struct nid_path *path;
-               path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin);
+               path = get_multiio_path(codec, i);
                if (!path)
                        continue;
                if (!spec->multi_io[i].ctl_in)
                        spec->multi_io[i].ctl_in =
-                               snd_hda_codec_update_cache(codec, pin, 0,
-                                          AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                               snd_hda_codec_get_pin_target(codec, pin);
                snd_hda_activate_path(codec, path, path->active, true);
        }
 }
 
-/* set up the input pin config, depending on the given auto-pin type */
-static void set_input_pin(struct hda_codec *codec, hda_nid_t nid,
-                         int auto_pin_type)
-{
-       unsigned int val = PIN_IN;
-       if (auto_pin_type == AUTO_PIN_MIC)
-               val |= snd_hda_get_default_vref(codec, nid);
-       snd_hda_set_pin_ctl(codec, nid, val);
-}
-
 /* set up input pins and loopback paths */
 static void init_analog_input(struct hda_codec *codec)
 {
@@ -3468,12 +4588,12 @@ static void init_analog_input(struct hda_codec *codec)
        for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t nid = cfg->inputs[i].pin;
                if (is_input_pin(codec, nid))
-                       set_input_pin(codec, nid, cfg->inputs[i].type);
+                       restore_pin_ctl(codec, nid);
 
                /* init loopback inputs */
                if (spec->mixer_nid) {
                        struct nid_path *path;
-                       path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid);
+                       path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]);
                        if (path)
                                snd_hda_activate_path(codec, path,
                                                      path->active, false);
@@ -3496,8 +4616,7 @@ static void init_input_src(struct hda_codec *codec)
 
        for (c = 0; c < nums; c++) {
                for (i = 0; i < imux->num_items; i++) {
-                       path = snd_hda_get_nid_path(codec, spec->imux_pins[i],
-                                                   get_adc_nid(codec, c, i));
+                       path = get_input_path(codec, c, i);
                        if (path) {
                                bool active = path->active;
                                if (i == spec->cur_mux[c])
@@ -3511,7 +4630,7 @@ static void init_input_src(struct hda_codec *codec)
                update_shared_mic_hp(codec, spec->cur_mux[0]);
 
        if (spec->cap_sync_hook)
-               spec->cap_sync_hook(codec);
+               spec->cap_sync_hook(codec, NULL);
 }
 
 /* set right pin controls for digital I/O */
@@ -3521,17 +4640,39 @@ static void init_digital(struct hda_codec *codec)
        int i;
        hda_nid_t pin;
 
-       for (i = 0; i < spec->autocfg.dig_outs; i++) {
-               pin = spec->autocfg.dig_out_pins[i];
-               if (!pin)
-                       continue;
-               set_output_and_unmute(codec, pin, PIN_OUT, 0);
-       }
+       for (i = 0; i < spec->autocfg.dig_outs; i++)
+               set_output_and_unmute(codec, spec->digout_paths[i]);
        pin = spec->autocfg.dig_in_pin;
-       if (pin)
-               snd_hda_set_pin_ctl(codec, pin, PIN_IN);
+       if (pin) {
+               struct nid_path *path;
+               restore_pin_ctl(codec, pin);
+               path = snd_hda_get_path_from_idx(codec, spec->digin_path);
+               if (path)
+                       snd_hda_activate_path(codec, path, path->active, false);
+       }
+}
+
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+       int i;
+
+       for (i = 0; i < codec->init_pins.used; i++) {
+               struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+               hda_nid_t nid = pin->nid;
+               if (is_jack_detectable(codec, nid) &&
+                   !snd_hda_jack_tbl_get(codec, nid))
+                       snd_hda_codec_update_cache(codec, nid, 0,
+                                       AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+       }
 }
 
+/*
+ * initialize the generic spec;
+ * this can be put as patch_ops.init function
+ */
 int snd_hda_gen_init(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
@@ -3550,10 +4691,10 @@ int snd_hda_gen_init(struct hda_codec *codec)
        init_input_src(codec);
        init_digital(codec);
 
+       clear_unsol_on_unused_pins(codec);
+
        /* call init functions of standard auto-mute helpers */
-       snd_hda_gen_hp_automute(codec, NULL);
-       snd_hda_gen_line_automute(codec, NULL);
-       snd_hda_gen_mic_autoswitch(codec, NULL);
+       update_automute_all(codec);
 
        snd_hda_codec_flush_amp_cache(codec);
        snd_hda_codec_flush_cmd_cache(codec);
@@ -3564,36 +4705,46 @@ int snd_hda_gen_init(struct hda_codec *codec)
        hda_call_check_power_status(codec, 0x01);
        return 0;
 }
-EXPORT_SYMBOL(snd_hda_gen_init);
-
+EXPORT_SYMBOL_HDA(snd_hda_gen_init);
 
 /*
- * the generic codec support
+ * free the generic spec;
+ * this can be put as patch_ops.free function
  */
+void snd_hda_gen_free(struct hda_codec *codec)
+{
+       snd_hda_gen_spec_free(codec->spec);
+       kfree(codec->spec);
+       codec->spec = NULL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_free);
 
 #ifdef CONFIG_PM
-static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * check the loopback power save state;
+ * this can be put as patch_ops.check_power_status function
+ */
+int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
 {
        struct hda_gen_spec *spec = codec->spec;
        return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
 }
+EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status);
 #endif
 
-static void generic_free(struct hda_codec *codec)
-{
-       snd_hda_gen_spec_free(codec->spec);
-       kfree(codec->spec);
-       codec->spec = NULL;
-}
+
+/*
+ * the generic codec support
+ */
 
 static const struct hda_codec_ops generic_patch_ops = {
        .build_controls = snd_hda_gen_build_controls,
        .build_pcms = snd_hda_gen_build_pcms,
        .init = snd_hda_gen_init,
-       .free = generic_free,
+       .free = snd_hda_gen_free,
        .unsol_event = snd_hda_jack_unsol_event,
 #ifdef CONFIG_PM
-       .check_power_status = generic_check_power_status,
+       .check_power_status = snd_hda_gen_check_power_status,
 #endif
 };
 
@@ -3620,7 +4771,7 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
        return 0;
 
 error:
-       generic_free(codec);
+       snd_hda_gen_free(codec);
        return err;
 }
-EXPORT_SYMBOL(snd_hda_parse_generic_codec);
+EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec);