ALSA: hda - Add missing slave names for Speaker Surround, etc
[pandora-kernel.git] / sound / pci / hda / hda_generic.c
index 46b00e0..1a1e8e2 100644 (file)
@@ -24,6 +24,8 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/sort.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include "hda_codec.h"
@@ -39,6 +41,7 @@ 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);
@@ -113,11 +116,9 @@ 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)
+static struct nid_path *get_nid_path(struct hda_codec *codec,
+                                    hda_nid_t from_nid, hda_nid_t to_nid,
+                                    int with_aa_mix)
 {
        struct hda_gen_spec *spec = codec->spec;
        int i;
@@ -127,13 +128,52 @@ 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 (with_aa_mix == HDA_PARSE_ALL ||
+                           path->with_aa_mix == with_aa_mix)
+                               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, HDA_PARSE_ALL);
+}
 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)
 {
@@ -184,22 +224,37 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
                is_ctl_used(codec, val, NID_PATH_MUTE_CTL);
 }
 
+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)
 {
        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)
+               if (with_aa_mix == HDA_PARSE_NO_AAMIX)
                        return false;
-               with_aa_mix = 2; /* mark aa-mix is included */
+               with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */
        }
 
-       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,
@@ -211,7 +266,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
                                continue;
                }
                /* aa-mix is requested but not included? */
-               if (!(spec->mixer_nid && with_aa_mix == 1))
+               if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX))
                        goto found;
        }
        if (depth >= MAX_NID_PATH_DEPTH)
@@ -230,6 +285,8 @@ static bool __parse_nid_path(struct hda_codec *codec,
 
  found:
        path->path[path->depth] = conn[i];
+       if (conn[i] == spec->mixer_nid)
+               path->with_aa_mix = true;
        path->idx[path->depth + 1] = i;
        if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
                path->multi[path->depth + 1] = 1;
@@ -239,9 +296,11 @@ 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 @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are
+ * excluded, only the paths that don't go through the mixer will be chosen.
+ * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through
+ * spec->mixer_nid will be chosen.
+ * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget.
  */
 bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
                            hda_nid_t to_nid, int with_aa_mix,
@@ -250,11 +309,6 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
        if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, 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;
@@ -275,6 +329,11 @@ 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, with_aa_mix);
+       if (path)
+               return path;
+
        path = snd_array_new(&spec->paths);
        if (!path)
                return NULL;
@@ -448,7 +507,7 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
 {
        int val;
        if (is_ctl_associated(codec, nid, dir, idx) ||
-           is_active_nid(codec, nid, dir, idx))
+           (!enable && is_active_nid(codec, nid, dir, idx)))
                return;
        val = get_amp_val_to_activate(codec, nid, dir, enable);
        snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
@@ -466,12 +525,12 @@ 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)) {
@@ -530,6 +589,8 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
        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);
@@ -799,13 +860,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;
@@ -814,25 +896,33 @@ 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);
+
+               if (dacs[i]) {
+                       badness += assign_out_path_ctls(codec, pin, dacs[i]);
+                       continue;
+               }
+
+               dacs[i] = look_for_dac(codec, pin, false);
                if (!dacs[i] && !i) {
                        for (j = 1; j < num_outs; j++) {
                                if (is_reachable_path(codec, dacs[j], pin)) {
                                        dacs[0] = dacs[j];
                                        dacs[j] = 0;
+                                       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;
@@ -848,8 +938,18 @@ 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, HDA_PARSE_NO_AAMIX);
+               if (!path && !i && spec->mixer_nid) {
+                       /* try with aamix */
+                       path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL);
+               }
+               if (!path)
                        dac = dacs[i] = 0;
+               else {
+                       print_nid_path("output", path);
+                       path->active = true;
+                       path_idx[i] = snd_hda_get_path_idx(codec, path);
+               }
                if (dac)
                        badness += assign_out_path_ctls(codec, pin, dac);
        }
@@ -894,6 +994,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
  *
@@ -904,11 +1026,11 @@ 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;
@@ -917,22 +1039,13 @@ static int fill_multi_ios(struct hda_codec *codec,
        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++) {
+                       struct nid_path *path;
                        hda_nid_t nid = cfg->inputs[i].pin;
                        hda_nid_t dac = 0;
 
@@ -947,11 +1060,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)
@@ -960,12 +1068,16 @@ 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, HDA_PARSE_NO_AAMIX);
+                       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;
@@ -997,25 +1109,51 @@ static int fill_multi_ios(struct hda_codec *codec,
 
 /* 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], HDA_PARSE_NO_AAMIX);
+               if (!path && !i && spec->mixer_nid)
+                       path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL);
+               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 nid_path *path;
+
+       path = snd_hda_get_path_from_idx(codec, path_idx);
+       if (!path || !path->depth || path->with_aa_mix)
+               return 0;
+       path = snd_hda_add_new_path(codec, path->path[0],
+                                   path->path[path->depth - 1],
+                                   HDA_PARSE_ONLY_AAMIX);
+       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 in the dac_nids table from the parsed pin configuration */
 static int fill_and_eval_dacs(struct hda_codec *codec,
                              bool fill_hardwired,
@@ -1041,16 +1179,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;
                        }
@@ -1058,7 +1199,7 @@ 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 */
@@ -1077,7 +1218,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
        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 */
@@ -1086,6 +1227,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;
@@ -1095,28 +1237,34 @@ 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 */
+
        if (spec->multi_ios == 2) {
                for (i = 0; i < 2; i++)
                        spec->private_dac_nids[spec->multiout.num_dacs++] =
@@ -1142,7 +1290,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c
 {
        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],
+                     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],
@@ -1154,7 +1302,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c
                              spec->multi_io[0].dac, spec->multi_io[1].dac);
        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],
@@ -1266,6 +1414,7 @@ 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);
        }
@@ -1275,9 +1424,7 @@ static int parse_output_paths(struct hda_codec *codec)
 
        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);
        }
@@ -1300,22 +1447,20 @@ 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;
+               hda_nid_t dac;
                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;
                if (!name || !strcmp(name, "CLFE")) {
@@ -1345,12 +1490,13 @@ static int create_multi_out_ctls(struct hda_codec *codec,
 }
 
 static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
-                           hda_nid_t dac, const char *pfx, int cidx)
+                           hda_nid_t dac, 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 */
@@ -1368,11 +1514,11 @@ 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];
+       char name[44];
        int i, n, err;
 
        if (!num_pins || !pins[0])
@@ -1382,7 +1528,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
                hda_nid_t dac = *dacs;
                if (!dac)
                        dac = spec->multiout.dac_nids[0];
-               return create_extra_out(codec, *pins, dac, pfx, 0);
+               return create_extra_out(codec, *pins, dac, paths[0], pfx, 0);
        }
 
        for (i = 0; i < num_pins; i++) {
@@ -1392,14 +1538,16 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
                else
                        dac = 0;
                if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
-                       err = create_extra_out(codec, pins[i], dac,
+                       err = create_extra_out(codec, pins[i], dac, paths[i],
                                               "Bass Speaker", 0);
                } else if (num_pins >= 3) {
                        snprintf(name, sizeof(name), "%s %s",
                                 pfx, channel_name[i]);
-                       err = create_extra_out(codec, pins[i], dac, name, 0);
+                       err = create_extra_out(codec, pins[i], dac, paths[i],
+                                              name, 0);
                } else {
-                       err = create_extra_out(codec, pins[i], dac, pfx, i);
+                       err = create_extra_out(codec, pins[i], dac, paths[i],
+                                              pfx, i);
                }
                if (err < 0)
                        return err;
@@ -1417,7 +1565,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins,
                struct nid_path *path;
                if (!pins[i] || !dacs[i])
                        continue;
-               path = snd_hda_get_nid_path(codec, dacs[i], pins[i]);
+               path = snd_hda_get_path_from_idx(codec, paths[i]);
                if (!path)
                        continue;
                vol = look_for_out_vol_nid(codec, path);
@@ -1440,6 +1588,7 @@ static int create_hp_out_ctls(struct hda_codec *codec)
        return create_extra_outs(codec, spec->autocfg.hp_outs,
                                 spec->autocfg.hp_pins,
                                 spec->multiout.hp_out_nid,
+                                spec->hp_paths,
                                 "Headphone");
 }
 
@@ -1449,9 +1598,83 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
        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
  */
@@ -1481,13 +1704,21 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol,
        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 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;
 
@@ -1548,6 +1779,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
  */
@@ -1572,11 +1877,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);
+       snd_hda_set_pin_ctl_cache(codec, pin, val);
 
        spec->automute_speaker = !set_as_mic;
        call_update_outputs(codec);
@@ -1640,8 +1946,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;
@@ -1653,9 +1959,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, HDA_PARSE_ALL);
        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)) {
@@ -1808,7 +2116,7 @@ static int create_input_ctls(struct hda_codec *codec)
 
                if (mixer) {
                        if (is_reachable_path(codec, pin, mixer)) {
-                               err = new_analog_input(codec, pin,
+                               err = new_analog_input(codec, i, pin,
                                                       label, type_idx, mixer);
                                if (err < 0)
                                        return err;
@@ -1826,13 +2134,14 @@ static int create_input_ctls(struct hda_codec *codec)
                        if (!path)
                                return -ENOMEM;
                        memset(path, 0, sizeof(*path));
-                       if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) {
+                       if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) {
                                snd_printd(KERN_ERR
                                           "invalid input path 0x%x -> 0x%x\n",
                                           pin, adc);
                                spec->paths.used--;
                                continue;
                        }
+                       print_nid_path("input", path);
 
                        if (!imux_added) {
                                spec->imux_pins[imux->num_items] = pin;
@@ -2257,7 +2566,7 @@ static int parse_mic_boost(struct hda_codec *codec)
                nid = cfg->inputs[i].pin;
                if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
                        const char *label;
-                       char boost_label[32];
+                       char boost_label[44];
                        struct nid_path *path;
                        unsigned int val;
 
@@ -2292,6 +2601,7 @@ 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;
 
@@ -2302,8 +2612,12 @@ static void parse_digital(struct hda_codec *codec)
                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, HDA_PARSE_ALL);
+               if (!path)
                        continue;
+               print_nid_path("digout", path);
+               path->active = true;
+               spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
                if (!nums) {
                        spec->multiout.dig_out_nid = dig_nid;
                        spec->dig_out_type = spec->autocfg.dig_out_type[0];
@@ -2319,7 +2633,6 @@ static void parse_digital(struct hda_codec *codec)
        if (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;
@@ -2327,10 +2640,12 @@ static void parse_digital(struct hda_codec *codec)
                                continue;
                        path = snd_hda_add_new_path(codec,
                                                    spec->autocfg.dig_in_pin,
-                                                   dig_nid, 2);
+                                                   dig_nid, HDA_PARSE_ALL);
                        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);
                                break;
                        }
                }
@@ -2431,7 +2746,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
                } else
                        val = 0;
                val |= pin_bits;
-               snd_hda_set_pin_ctl(codec, nid, val);
+               snd_hda_set_pin_ctl_cache(codec, nid, val);
                set_pin_eapd(codec, nid, !mute);
        }
 }
@@ -2666,6 +2981,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;
        }
@@ -2679,6 +2996,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;
                        }
@@ -2731,6 +3050,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;
 }
@@ -2865,6 +3186,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);
@@ -2925,6 +3252,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,
 };
 
@@ -3019,8 +3349,16 @@ 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;
+       mutex_unlock(&spec->pcm_mutex);
+       return err;
 }
 
 static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -3042,6 +3380,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
 }
 
+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);
+       mutex_unlock(&spec->pcm_mutex);
+       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;
+       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);
+       mutex_unlock(&spec->pcm_mutex);
+       return 0;
+}
+
 /*
  * Digital out
  */
@@ -3116,6 +3492,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
        },
@@ -3133,6 +3510,10 @@ 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
+       },
 };
 
 static const struct hda_pcm_stream pcm_analog_alt_capture = {
@@ -3229,6 +3610,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)
 {
@@ -3236,7 +3636,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;
@@ -3244,8 +3643,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) {
@@ -3273,21 +3673,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;
@@ -3364,25 +3755,23 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
 
 /* 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)
+                                 int pin_type, int path_idx)
 {
        struct nid_path *path;
 
        snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
-       path = snd_hda_get_nid_path(codec, dac, pin);
+       path = snd_hda_get_path_from_idx(codec, path_idx);
        if (!path)
                return;
-       if (path->active)
-               return;
-       snd_hda_activate_path(codec, path, true, true);
-       set_pin_eapd(codec, pin, true);
+       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;
-       hda_nid_t nid, dac;
+       hda_nid_t nid;
        int pin_type;
        int i;
 
@@ -3393,35 +3782,24 @@ static void init_multi_out(struct hda_codec *codec)
 
        for (i = 0; i < spec->autocfg.line_outs; i++) {
                nid = spec->autocfg.line_out_pins[i];
-               if (nid) {
-                       dac = spec->multiout.dac_nids[i];
-                       if (!dac)
-                               dac = spec->multiout.dac_nids[0];
-                       set_output_and_unmute(codec, nid, pin_type, dac);
-               }
+               if (nid)
+                       set_output_and_unmute(codec, nid, pin_type,
+                                             spec->out_paths[i]);
        }
 }
 
 
 static void __init_extra_out(struct hda_codec *codec, int num_outs,
-                            hda_nid_t *pins, hda_nid_t *dacs, int type)
+                            hda_nid_t *pins, int *paths, int type)
 {
-       struct hda_gen_spec *spec = codec->spec;
        int i;
-       hda_nid_t pin, dac;
+       hda_nid_t pin;
 
        for (i = 0; i < num_outs; i++) {
                pin = pins[i];
                if (!pin)
                        break;
-               dac = dacs[i];
-               if (!dac) {
-                       if (i > 0 && dacs[0])
-                               dac = dacs[0];
-                       else
-                               dac = spec->multiout.dac_nids[0];
-               }
-               set_output_and_unmute(codec, pin, type, dac);
+               set_output_and_unmute(codec, pin, type, paths[i]);
        }
 }
 
@@ -3433,11 +3811,11 @@ static void init_extra_out(struct hda_codec *codec)
        if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
                __init_extra_out(codec, spec->autocfg.hp_outs,
                                 spec->autocfg.hp_pins,
-                                spec->multiout.hp_out_nid, PIN_HP);
+                                spec->hp_paths, PIN_HP);
        if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
                __init_extra_out(codec, spec->autocfg.speaker_outs,
                                 spec->autocfg.speaker_pins,
-                                spec->multiout.extra_out_nid, PIN_OUT);
+                                spec->speaker_paths, PIN_OUT);
 }
 
 /* initialize multi-io paths */
@@ -3449,7 +3827,7 @@ 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)
@@ -3467,7 +3845,7 @@ static void set_input_pin(struct hda_codec *codec, hda_nid_t nid,
        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);
+       snd_hda_set_pin_ctl_cache(codec, nid, val);
 }
 
 /* set up input pins and loopback paths */
@@ -3485,7 +3863,7 @@ static void init_analog_input(struct hda_codec *codec)
                /* 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);
@@ -3537,11 +3915,17 @@ static void init_digital(struct hda_codec *codec)
                pin = spec->autocfg.dig_out_pins[i];
                if (!pin)
                        continue;
-               set_output_and_unmute(codec, pin, PIN_OUT, 0);
+               set_output_and_unmute(codec, pin, PIN_OUT,
+                                     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;
+               snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN);
+               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