ALSA: hda - VT1708 independent HP routing fix
authorTakashi Iwai <tiwai@suse.de>
Tue, 21 Jun 2011 13:57:44 +0000 (15:57 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 21 Jun 2011 14:02:32 +0000 (16:02 +0200)
The codecs like VT1708 needs more complicated routing using the mixer
widget rather than the simple selector widgets.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_via.c

index 5b907b356951941ed8907a977c89a67c5d15e8a6..bceb6b2364fe86484f03bd4af4616c2531ed6838 100644 (file)
@@ -83,10 +83,20 @@ enum VIA_HDA_CODEC {
 
 #define MAX_NID_PATH_DEPTH     5
 
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
 struct nid_path {
        int depth;
        hda_nid_t path[MAX_NID_PATH_DEPTH];
-       short idx[MAX_NID_PATH_DEPTH];
+       unsigned char idx[MAX_NID_PATH_DEPTH];
+       unsigned char multi[MAX_NID_PATH_DEPTH];
+       unsigned int vol_ctl;
+       unsigned int mute_ctl;
 };
 
 struct via_spec {
@@ -422,43 +432,39 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
        return false;
 }
 
-#define have_vol_or_mute(codec, nid, dir) \
-       check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)
+#define have_mute(codec, nid, dir) \
+       check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
 
-/* unmute input amp and select the specificed source */
-static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
-                             hda_nid_t src, hda_nid_t mix)
+/* enable/disable the output-route */
+static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
+                                bool enable, bool force)
 {
-       int idx, num_conns;
-
-       idx = __get_connection_index(codec, nid, src, &num_conns);
-       if (idx < 0)
-               return;
-
-       /* select the route explicitly when multiple connections exist */
-       if (num_conns > 1 &&
-           get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CONNECT_SEL, idx);
-
-       /* unmute if the input amp is present */
-       if (have_vol_or_mute(codec, nid, HDA_INPUT))
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                                   AMP_IN_UNMUTE(idx));
-
-       /* unmute the src output */
-       if (have_vol_or_mute(codec, src, HDA_OUTPUT))
-               snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                                   AMP_OUT_UNMUTE);
-
-       /* unmute AA-path if present */
-       if (!mix || mix == src)
-               return;
-       idx = __get_connection_index(codec, nid, mix, NULL);
-       if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT))
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_AMP_GAIN_MUTE,
-                                   AMP_IN_UNMUTE(idx));
+       int i;
+       for (i = 0; i < path->depth; i++) {
+               hda_nid_t src, dst;
+               int idx = path->idx[i];
+               src = path->path[i];                    
+               if (i < path->depth - 1)
+                       dst = path->path[i + 1];
+               else
+                       dst = 0;
+               if (enable && path->multi[i])
+                       snd_hda_codec_write(codec, dst, 0,
+                                           AC_VERB_SET_CONNECT_SEL, idx);
+               if (have_mute(codec, dst, HDA_INPUT)) {
+                       int val = enable ? AMP_IN_UNMUTE(idx) :
+                               AMP_IN_MUTE(idx);
+                       snd_hda_codec_write(codec, dst, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE, val);
+               }
+               if (!force && (src == path->vol_ctl || src == path->mute_ctl))
+                       continue;
+               if (have_mute(codec, src, HDA_OUTPUT)) {
+                       int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
+                       snd_hda_codec_write(codec, src, 0,
+                                           AC_VERB_SET_AMP_GAIN_MUTE, val);
+               }
+       }
 }
 
 /* set the given pin as output */
@@ -474,16 +480,18 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
                                    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
 }
 
-static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
-                                int pin_type, struct nid_path *path)
+static void via_auto_init_output(struct hda_codec *codec,
+                                struct nid_path *path, int pin_type,
+                                bool force)
 {
        struct via_spec *spec = codec->spec;
        unsigned int caps;
-       hda_nid_t nid;
-       int i;
+       hda_nid_t pin, nid;
+       int i, idx;
 
-       if (!pin)
+       if (!path->depth)
                return;
+       pin = path->path[path->depth - 1];
 
        init_output_pin(codec, pin, pin_type);
        caps = query_amp_caps(codec, pin, HDA_OUTPUT);
@@ -494,34 +502,48 @@ static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
                                    AMP_OUT_MUTE | val);
        }
 
-       /* initialize the output path */
+       activate_output_path(codec, path, true, force);
+
+       /* initialize the AA-path */
+       if (!spec->aa_mix_nid)
+               return;
        for (i = path->depth - 1; i > 0; i--) {
-               nid = path->path[i - 1];
-               unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
+               nid = path->path[i];
+               idx = get_connection_index(codec, nid, spec->aa_mix_nid);
+               if (idx >= 0) {
+                       if (have_mute(codec, nid, HDA_INPUT))
+                               snd_hda_codec_write(codec, nid, 0,
+                                                   AC_VERB_SET_AMP_GAIN_MUTE,
+                                                   AMP_IN_UNMUTE(idx));
+                       break;
+               }
        }
 }
 
-
 static void via_auto_init_multi_out(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        int i;
 
        for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
-               via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
-                                    PIN_OUT, &spec->out_path[i]);
+               via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
 }
 
 static void via_auto_init_hp_out(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
 
-       if (spec->hp_dac_nid)
-               via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
-                                    &spec->hp_path);
-       else
-               via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
-                                    &spec->hp_dep_path);
+       if (!spec->hp_dac_nid) {
+               via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
+               return;
+       }
+       if (spec->hp_independent_mode) {
+               activate_output_path(codec, &spec->hp_dep_path, false, false);
+               via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
+       } else {
+               activate_output_path(codec, &spec->hp_path, false, false);
+               via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
+       }
 }
 
 static void via_auto_init_speaker_out(struct hda_codec *codec)
@@ -529,8 +551,7 @@ static void via_auto_init_speaker_out(struct hda_codec *codec)
        struct via_spec *spec = codec->spec;
 
        if (spec->autocfg.speaker_outs)
-               via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
-                                    PIN_OUT, &spec->speaker_path);
+               via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
 }
 
 static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
@@ -738,27 +759,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       hda_nid_t nid, src;
-       int i, idx, num_conns;
-       struct nid_path *path;
 
        spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
-       if (spec->hp_independent_mode)
-               path = &spec->hp_path;
-       else
-               path = &spec->hp_dep_path;
-
-       /* re-route the output path */
-       for (i = path->depth - 1; i > 0; i--) {
-               nid = path->path[i];
-               src = path->path[i - 1];
-               idx = __get_connection_index(codec, nid, src, &num_conns);
-               if (idx < 0)
-                       continue;
-               if (num_conns > 1 &&
-                   get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_CONNECT_SEL, idx);
+       if (spec->hp_independent_mode) {
+               activate_output_path(codec, &spec->hp_dep_path, false, false);
+               activate_output_path(codec, &spec->hp_path, true, false);
+       } else {
+               activate_output_path(codec, &spec->hp_path, false, false);
+               activate_output_path(codec, &spec->hp_dep_path, true, false);
        }
 
        /* update jack power state */
@@ -1577,12 +1585,8 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
        for (i = 0; i < nums; i++) {
                if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
                        continue;
-               if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
-                       path->path[0] = conn[i];
-                       path->idx[0] = i;
-                       path->depth = 1;
-                       return true;
-               }
+               if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
+                       goto found;
        }
        if (depth >= MAX_NID_PATH_DEPTH)
                return false;
@@ -1593,14 +1597,18 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
                    (wid_type != -1 && type != wid_type))
                        continue;
                if (__parse_output_path(codec, conn[i], target_dac,
-                                     path, depth + 1, AC_WID_AUD_SEL)) {
-                       path->path[path->depth] = conn[i];
-                       path->idx[path->depth] = i;
-                       path->depth++;
-                       return true;
-               }
+                                     path, depth + 1, AC_WID_AUD_SEL))
+                       goto found;
        }
        return false;
+
+ found:
+       path->path[path->depth] = conn[i];
+       path->idx[path->depth] = i;
+       if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+               path->multi[path->depth] = 1;
+       path->depth++;
+       return true;
 }
 
 static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
@@ -1634,18 +1642,16 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec)
 }
 
 static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
-                         hda_nid_t pin, hda_nid_t dac, int chs)
+                         int chs, bool check_dac, struct nid_path *path)
 {
        struct via_spec *spec = codec->spec;
        char name[32];
-       hda_nid_t nid, sel, conn[8];
-       int nums, err;
+       hda_nid_t dac, pin, sel, nid;
+       int err;
 
-       /* check selector widget connected to the pin */
-       sel = 0;
-       nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn));
-       if (nums == 1 && conn[0] != pin)
-               sel = conn[0];
+       dac = check_dac ? path->path[0] : 0;
+       pin = path->path[path->depth - 1];
+       sel = path->depth > 1 ? path->path[1] : 0;
 
        if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
                nid = dac;
@@ -1661,6 +1667,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
                              HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
+               path->vol_ctl = nid;
        }
 
        if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
@@ -1677,6 +1684,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
                              HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
+               path->mute_ctl = nid;
        }
        return 0;
 }
@@ -1747,10 +1755,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
                if (!pin || !dac)
                        continue;
                if (i == HDA_CLFE) {
-                       err = create_ch_ctls(codec, "Center", pin, dac, 1);
+                       err = create_ch_ctls(codec, "Center", 1, true,
+                                            &spec->out_path[i]);
                        if (err < 0)
                                return err;
-                       err = create_ch_ctls(codec, "LFE", pin, dac, 2);
+                       err = create_ch_ctls(codec, "LFE", 2, true,
+                                            &spec->out_path[i]);
                        if (err < 0)
                                return err;
                } else {
@@ -1758,7 +1768,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
                        if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
                            cfg->line_outs == 1)
                                pfx = "Speaker";
-                       err = create_ch_ctls(codec, pfx, pin, dac, 3);
+                       err = create_ch_ctls(codec, pfx, 3, true,
+                                            &spec->out_path[i]);
                        if (err < 0)
                                return err;
                }
@@ -1790,6 +1801,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
 static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
 {
        struct via_spec *spec = codec->spec;
+       struct nid_path *path;
        int err;
 
        if (!pin)
@@ -1803,9 +1815,17 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
            !spec->hp_dac_nid)
                return 0;
 
-       err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
+       if (spec->hp_dac_nid)
+               path = &spec->hp_path;
+       else
+               path = &spec->hp_dep_path;
+       err = create_ch_ctls(codec, "Headphone", 3, false, path);
        if (err < 0)
                return err;
+       if (spec->hp_dac_nid) {
+               spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
+               spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
+       }
 
        return 0;
 }
@@ -1822,11 +1842,13 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec)
        if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
                dac = spec->speaker_path.path[0];
                spec->multiout.extra_out_nid[0] = dac;
-               return create_ch_ctls(codec, "Speaker", pin, dac, 3);
+               return create_ch_ctls(codec, "Speaker", 3, true,
+                                     &spec->speaker_path);
        }
        if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
                              &spec->speaker_path))
-               return create_ch_ctls(codec, "Speaker", pin, 0, 3);
+               return create_ch_ctls(codec, "Speaker", 3, false,
+                                     &spec->speaker_path);
 
        return 0;
 }