unsigned int ctls[2]; /* 0 = volume, 1 = mute */
 };
 
+enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 };
+
 struct alc_spec {
        struct hda_gen_spec gen;
 
        const hda_nid_t *capsrc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
        hda_nid_t mixer_nid;            /* analog-mixer NID */
-       DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1);
-       DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1);
 
        /* capture setup for dynamic dual-adc switch */
        hda_nid_t cur_adc;
        return srcs[0];
 }
 
-/* get MIX nid connected to the given pin targeted to DAC */
-static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
-                                  hda_nid_t dac)
-{
-       hda_nid_t mix[5];
-       int i, num;
-
-       pin = alc_go_down_to_selector(codec, pin);
-       num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
-       for (i = 0; i < num; i++) {
-               if (alc_auto_mix_to_dac(codec, mix[i]) == dac)
-                       return mix[i];
-       }
-       return 0;
-}
-
 /* select the connection from pin to DAC if needed */
 static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin,
                               hda_nid_t dac)
        return nid_found;
 }
 
-/* mark up volume and mute control NIDs: used during badness parsing and
- * at creating actual controls
- */
-static inline unsigned int get_ctl_pos(unsigned int data)
+static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
 {
-       hda_nid_t nid = get_amp_nid_(data);
-       unsigned int dir;
-       if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
-               return 0;
-       dir = get_amp_direction_(data);
-       return (nid << 1) | dir;
-}
+       struct alc_spec *spec = codec->spec;
+       int i;
 
-#define is_ctl_used(bits, data) \
-       test_bit(get_ctl_pos(data), bits)
-#define mark_ctl_usage(bits, data) \
-       set_bit(get_ctl_pos(data), bits)
+       for (i = 0; i < spec->out_path.used; i++) {
+               struct nid_path *path = snd_array_elem(&spec->out_path, i);
+               if (path->ctls[type] == val)
+                       return true;
+       }
+       return false;
+}
 
 static void clear_vol_marks(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls));
-       memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls));
+       int i;
+
+       for (i = 0; i < spec->out_path.used; i++) {
+               struct nid_path *path = snd_array_elem(&spec->out_path, i);
+               path->ctls[0] = path->ctls[1] = 0;
+       }
 }
 
 /* badness definition */
 };
 
 static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
-                                          hda_nid_t pin, hda_nid_t dac);
+                                          struct nid_path *path);
 static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
-                                         hda_nid_t pin, hda_nid_t dac);
+                                         struct nid_path *path);
 
 static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin,
                             hda_nid_t dac)
        return false;
 }
 
+/* get the path pointing from the given dac to pin;
+ * passing 0 to either @pin or @dac behaves as a wildcard
+ */
+static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
+                                    hda_nid_t dac)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->out_path.used; i++) {
+               struct nid_path *path = snd_array_elem(&spec->out_path, i);
+               if (path->depth <= 0)
+                       continue;
+               if ((!dac || path->path[0] == dac) &&
+                   (!pin || path->path[path->depth - 1] == pin))
+                       return path;
+       }
+       return NULL;
+}
+
 static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
                                   hda_nid_t dac)
 {
-       struct alc_spec *spec = codec->spec;
+       struct nid_path *path = get_out_path(codec, pin, dac);
        hda_nid_t nid;
        unsigned int val;
        int badness = 0;
 
-       nid = alc_look_for_out_vol_nid(codec, pin, dac);
+       if (!path)
+               return BAD_SHARED_VOL * 2;
+       nid = alc_look_for_out_vol_nid(codec, path);
        if (nid) {
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-               if (is_ctl_used(spec->vol_ctls, nid))
+               if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
                        badness += BAD_SHARED_VOL;
                else
-                       mark_ctl_usage(spec->vol_ctls, val);
+                       path->ctls[NID_PATH_VOL_CTL] = val;
        } else
                badness += BAD_SHARED_VOL;
-       nid = alc_look_for_out_mute_nid(codec, pin, dac);
+       nid = alc_look_for_out_mute_nid(codec, path);
        if (nid) {
                unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
                if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT)
                        val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
                else
                        val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
-               if (is_ctl_used(spec->sw_ctls, val))
+               if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
                        badness += BAD_SHARED_VOL;
                else
-                       mark_ctl_usage(spec->sw_ctls, val);
+                       path->ctls[NID_PATH_MUTE_CTL] = val;
        } else
                badness += BAD_SHARED_VOL;
        return badness;
        memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
        spec->multi_ios = 0;
        snd_array_free(&spec->out_path);
-       clear_vol_marks(codec);
        badness = 0;
 
        /* fill hard-wired DACs first */
                      cfg->line_out_type, best_wired, best_mio);
        debug_show_configs(spec, cfg);
 
-       if (cfg->line_out_pins[0])
-               spec->vmaster_nid =
-                       alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
-                                                spec->multiout.dac_nids[0]);
+       if (cfg->line_out_pins[0]) {
+               struct nid_path *path = get_out_path(codec,
+                                                    cfg->line_out_pins[0],
+                                                    spec->multiout.dac_nids[0]);
+               if (path)
+                       spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path);
+       }
 
        /* clear the bitmap flags for creating controls */
        clear_vol_marks(codec);
 }
 
 static int alc_auto_add_vol_ctl(struct hda_codec *codec,
-                             const char *pfx, int cidx,
-                             hda_nid_t nid, unsigned int chs)
+                               const char *pfx, int cidx,
+                               hda_nid_t nid, unsigned int chs,
+                               struct nid_path *path)
 {
-       struct alc_spec *spec = codec->spec;
        unsigned int val;
-       if (!nid)
+       if (!nid || !path)
                return 0;
        val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-       if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */
+       if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */
                return 0;
-       mark_ctl_usage(spec->vol_ctls, val);
+       path->ctls[NID_PATH_VOL_CTL] = val;
        return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx,
                                 val);
 }
 
 static int alc_auto_add_stereo_vol(struct hda_codec *codec,
                                   const char *pfx, int cidx,
-                                  hda_nid_t nid)
+                                  hda_nid_t nid, struct nid_path *path)
 {
        int chs = 1;
        if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
                chs = 3;
-       return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs);
+       return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path);
 }
 
 /* create a mute-switch for the given mixer widget;
  * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
  */
 static int alc_auto_add_sw_ctl(struct hda_codec *codec,
-                            const char *pfx, int cidx,
-                            hda_nid_t nid, unsigned int chs)
+                              const char *pfx, int cidx,
+                              hda_nid_t nid, unsigned int chs,
+                              struct nid_path *path)
 {
-       struct alc_spec *spec = codec->spec;
        int wid_type;
        int type;
        unsigned long val;
-       if (!nid)
+       if (!nid || !path)
                return 0;
        wid_type = get_wcaps_type(get_wcaps(codec, nid));
        if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
                type = ALC_CTL_BIND_MUTE;
                val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT);
        }
-       if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */
+       if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */
                return 0;
-       mark_ctl_usage(spec->sw_ctls, val);
+       path->ctls[NID_PATH_MUTE_CTL] = val;
        return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
 }
 
 static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
-                                 int cidx, hda_nid_t nid)
+                                 int cidx, hda_nid_t nid,
+                                 struct nid_path *path)
 {
        int chs = 1;
        if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
                chs = 3;
-       return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs);
+       return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path);
 }
 
 static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
-                                          hda_nid_t pin, hda_nid_t dac)
+                                          struct nid_path *path)
 {
-       hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
-       if (nid_has_mute(codec, pin, HDA_OUTPUT))
-               return pin;
-       else if (mix && nid_has_mute(codec, mix, HDA_INPUT))
-               return mix;
-       else if (nid_has_mute(codec, dac, HDA_OUTPUT))
-               return dac;
+       int i;
+
+       for (i = path->depth - 1; i >= 0; i--) {
+               if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
+                       return path->path[i];
+               if (i != path->depth - 1 && i != 0 &&
+                   nid_has_mute(codec, path->path[i], HDA_INPUT))
+                       return path->path[i];
+       }
        return 0;
 }
 
 static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
-                                         hda_nid_t pin, hda_nid_t dac)
-{
-       hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
-       if (nid_has_volume(codec, dac, HDA_OUTPUT))
-               return dac;
-       else if (nid_has_volume(codec, mix, HDA_OUTPUT))
-               return mix;
-       else if (nid_has_volume(codec, pin, HDA_OUTPUT))
-               return pin;
+                                         struct nid_path *path)
+{
+       int i;
+
+       for (i = path->depth - 1; i >= 0; i--) {
+               if (nid_has_volume(codec, path->path[i], HDA_OUTPUT))
+                       return path->path[i];
+       }
        return 0;
 }
 
                int index;
                hda_nid_t dac, pin;
                hda_nid_t sw, vol;
+               struct nid_path *path;
 
                dac = spec->multiout.dac_nids[i];
                if (!dac)
                        name = alc_get_line_out_pfx(spec, i, true, &index);
                }
 
-               sw = alc_look_for_out_mute_nid(codec, pin, dac);
-               vol = alc_look_for_out_vol_nid(codec, pin, dac);
+               path = get_out_path(codec, pin, dac);
+               if (!path)
+                       continue;
+               sw = alc_look_for_out_mute_nid(codec, path);
+               vol = alc_look_for_out_vol_nid(codec, path);
                if (!name || !strcmp(name, "CLFE")) {
                        /* Center/LFE */
-                       err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
+                       err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1,
+                                                  path);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2);
+                       err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2,
+                                                  path);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1);
+                       err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1,
+                                                 path);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2);
+                       err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2,
+                                                 path);
                        if (err < 0)
                                return err;
                } else {
-                       err = alc_auto_add_stereo_vol(codec, name, index, vol);
+                       err = alc_auto_add_stereo_vol(codec, name, index, vol,
+                                                     path);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_stereo_sw(codec, name, index, sw);
+                       err = alc_auto_add_stereo_sw(codec, name, index, sw,
+                                                    path);
                        if (err < 0)
                                return err;
                }
                                     int cidx)
 {
        struct alc_spec *spec = codec->spec;
+       struct nid_path *path;
        hda_nid_t sw, vol;
        int err;
 
+       path = get_out_path(codec, pin, dac);
+       if (!path)
+               return 0;
+
        if (!dac) {
                unsigned int val;
                /* the corresponding DAC is already occupied */
                        return 0; /* no way */
                /* create a switch only */
                val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT);
-               if (is_ctl_used(spec->sw_ctls, val))
+               if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
                        return 0; /* already created */
-               mark_ctl_usage(spec->sw_ctls, val);
+               path->ctls[NID_PATH_MUTE_CTL] = val;
                return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val);
        }
 
-       sw = alc_look_for_out_mute_nid(codec, pin, dac);
-       vol = alc_look_for_out_vol_nid(codec, pin, dac);
-       err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol);
+       sw = alc_look_for_out_mute_nid(codec, path);
+       vol = alc_look_for_out_vol_nid(codec, path);
+       err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path);
        if (err < 0)
                return err;
-       err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw);
+       err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path);
        if (err < 0)
                return err;
        return 0;
        n = 0;
        for (i = 0; i < num_pins; i++) {
                hda_nid_t vol;
+               struct nid_path *path;
                if (!pins[i] || !dacs[i])
                        continue;
-               vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]);
+               path = get_out_path(codec, pins[i], dacs[i]);
+               if (!path)
+                       continue;
+               vol = alc_look_for_out_vol_nid(codec, path);
                if (vol)
                        ctl->values[n++] =
                                HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
        int i, num;
        hda_nid_t nid, mix = 0;
        hda_nid_t srcs[HDA_MAX_CONNECTIONS];
+       struct nid_path *path;
 
        alc_set_pin_output(codec, pin, pin_type);
        nid = alc_go_down_to_selector(codec, pin);
                            AMP_IN_UNMUTE(1));
        }
        /* initialize volume */
-       nid = alc_look_for_out_vol_nid(codec, pin, dac);
+       path = get_out_path(codec, pin, dac);
+       if (!path)
+               return;
+       nid = alc_look_for_out_vol_nid(codec, path);
        if (nid)
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_OUT_ZERO);
 
        /* unmute DAC if it's not assigned to a mixer */
-       nid = alc_look_for_out_mute_nid(codec, pin, dac);
+       nid = alc_look_for_out_mute_nid(codec, path);
        if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT))
                snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_OUT_ZERO);