ALSA: hda - Add missing slave names for Speaker Surround, etc
[pandora-kernel.git] / sound / pci / hda / hda_generic.c
index ff15aea..1a1e8e2 100644 (file)
@@ -116,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;
@@ -130,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)
 {
@@ -208,7 +245,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
                             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) {
@@ -217,7 +254,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
                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,
@@ -248,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;
@@ -290,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;
@@ -463,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);
@@ -481,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)) {
@@ -545,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);
@@ -814,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;
@@ -831,24 +898,31 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
        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;
@@ -865,14 +939,17 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs,
                                badness += bad->no_dac;
                }
                path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX);
-               if (!path && i > 0 && spec->mixer_nid) {
+               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
+               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);
        }
@@ -917,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
  *
@@ -927,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;
@@ -940,20 +1039,10 @@ 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;
@@ -971,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)
@@ -992,6 +1076,8 @@ static int fill_multi_ios(struct hda_codec *codec,
                        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;
@@ -1023,7 +1109,7 @@ 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;
@@ -1037,17 +1123,37 @@ static bool map_singles(struct hda_codec *codec, int outs,
                if (!dac)
                        continue;
                path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX);
-               if (!path && i > 0 && spec->mixer_nid)
+               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,
@@ -1073,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;
                        }
@@ -1090,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 */
@@ -1109,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 */
@@ -1118,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;
@@ -1127,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++] =
@@ -1308,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);
        }
@@ -1333,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")) {
@@ -1378,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 */
@@ -1401,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])
@@ -1415,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++) {
@@ -1425,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;
@@ -1450,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);
@@ -1473,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");
 }
 
@@ -1482,6 +1598,7 @@ 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");
 }
 
@@ -1587,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;
 
@@ -1654,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
  */
@@ -1747,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;
@@ -1764,6 +1963,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
        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)) {
@@ -1916,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;
@@ -2366,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;
 
@@ -2416,6 +2616,8 @@ static void parse_digital(struct hda_codec *codec)
                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];
@@ -2443,6 +2645,7 @@ static void parse_digital(struct hda_codec *codec)
                                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;
                        }
                }
@@ -2778,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;
        }
@@ -2791,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;
                        }
@@ -2843,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;
 }
@@ -2980,6 +3189,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *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);
@@ -3040,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,
 };
 
@@ -3540,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;
 
@@ -3569,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]);
        }
 }
 
@@ -3609,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 */
@@ -3625,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)
@@ -3661,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);
@@ -3713,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)
+       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