Merge branch 'fix/hda' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Sat, 3 Oct 2009 16:31:33 +0000 (18:31 +0200)
committerTakashi Iwai <tiwai@suse.de>
Sat, 3 Oct 2009 16:31:33 +0000 (18:31 +0200)
Documentation/sound/alsa/HD-Audio-Models.txt
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c

index 97eebd6..a2643cf 100644 (file)
@@ -209,6 +209,7 @@ AD1884A / AD1883 / AD1984A / AD1984B
   laptop       laptop with HP jack sensing
   mobile       mobile devices with HP jack sensing
   thinkpad     Lenovo Thinkpad X300
+  touchsmart   HP Touchsmart
 
 AD1884
 ======
index 20a66f8..c9ad182 100644 (file)
@@ -2303,6 +2303,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
  * white-list for enable_msi
  */
 static struct snd_pci_quirk msi_white_list[] __devinitdata = {
+       SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
        SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
        {}
 };
index 215e72a..2d603f6 100644 (file)
@@ -4031,6 +4031,127 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec)
        return 0;
 }
 
+/*
+ * HP Touchsmart
+ * port-A (0x11)      - front hp-out
+ * port-B (0x14)      - unused
+ * port-C (0x15)      - unused
+ * port-D (0x12)      - rear line out
+ * port-E (0x1c)      - front mic-in
+ * port-F (0x16)      - Internal speakers
+ * digital-mic (0x17) - Internal mic
+ */
+
+static struct hda_verb ad1984a_touchsmart_verbs[] = {
+       /* DACs; unmute as default */
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
+       /* Port-A (HP) mixer - route only from analog mixer */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-A pin */
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       /* Port-A (HP) pin - always unmuted */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Port-E (int speaker) mixer - route only from analog mixer */
+       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
+       /* Port-E pin */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       /* Port-F (int speaker) mixer - route only from analog mixer */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Port-F pin */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Analog mixer; mute as default */
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+       /* Analog Mix output amp */
+       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* capture sources */
+       /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* unsolicited event for pin-sense */
+       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+       /* allow to touch GPIO1 (for mute control) */
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
+       /* internal mic - dmic */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       /* set magic COEFs for dmic */
+       {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+       {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+       { } /* end */
+};
+
+static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
+/*     HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = snd_hda_mixer_amp_switch_info,
+               .get = snd_hda_mixer_amp_switch_get,
+               .put = ad1884a_mobile_master_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+       },
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+/* switch to external mic if plugged */
+static void ad1984a_touchsmart_automic(struct hda_codec *codec)
+{
+       if (snd_hda_codec_read(codec, 0x1c, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
+               snd_hda_codec_write(codec, 0x0c, 0,
+                                    AC_VERB_SET_CONNECT_SEL, 0x4);
+       } else {
+               snd_hda_codec_write(codec, 0x0c, 0,
+                                    AC_VERB_SET_CONNECT_SEL, 0x5);
+       }
+}
+
+
+/* unsolicited event for HP jack sensing */
+static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
+       unsigned int res)
+{
+       switch (res >> 26) {
+       case AD1884A_HP_EVENT:
+               ad1884a_hp_automute(codec);
+               break;
+       case AD1884A_MIC_EVENT:
+               ad1984a_touchsmart_automic(codec);
+               break;
+       }
+}
+
+/* initialize jack-sensing, too */
+static int ad1984a_touchsmart_init(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       ad1884a_hp_automute(codec);
+       ad1984a_touchsmart_automic(codec);
+       return 0;
+}
+
+
 /*
  */
 
@@ -4039,6 +4160,7 @@ enum {
        AD1884A_LAPTOP,
        AD1884A_MOBILE,
        AD1884A_THINKPAD,
+       AD1984A_TOUCHSMART,
        AD1884A_MODELS
 };
 
@@ -4047,6 +4169,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
        [AD1884A_LAPTOP]        = "laptop",
        [AD1884A_MOBILE]        = "mobile",
        [AD1884A_THINKPAD]      = "thinkpad",
+       [AD1984A_TOUCHSMART]    = "touchsmart",
 };
 
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
@@ -4059,6 +4182,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
        SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
        SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
        SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
+       SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
        {}
 };
 
@@ -4142,6 +4266,21 @@ static int patch_ad1884a(struct hda_codec *codec)
                codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
                codec->patch_ops.init = ad1984a_thinkpad_init;
                break;
+       case AD1984A_TOUCHSMART:
+               spec->mixers[0] = ad1984a_touchsmart_mixers;
+               spec->init_verbs[0] = ad1984a_touchsmart_verbs;
+               spec->multiout.dig_out_nid = 0;
+               codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
+               codec->patch_ops.init = ad1984a_touchsmart_init;
+               /* set the upper-limit for mixer amp to 0dB for avoiding the
+                * possible damage by overloading
+                */
+               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
+                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
+                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+                                         (1 << AC_AMPCAP_MUTE_SHIFT));
+               break;
        }
 
        return 0;
index 9d899ed..3fbbc8c 100644 (file)
@@ -682,11 +682,13 @@ static struct hda_input_mux cxt5045_capture_source = {
 };
 
 static struct hda_input_mux cxt5045_capture_source_benq = {
-       .num_items = 3,
+       .num_items = 5,
        .items = {
                { "IntMic", 0x1 },
                { "ExtMic", 0x2 },
                { "LineIn", 0x3 },
+               { "CD",     0x4 },
+               { "Mixer",  0x0 },
        }
 };
 
@@ -811,11 +813,19 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
 };
 
 static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+       HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT),
+
        HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
        HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
        HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
        HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
 
+       HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT),
+
        {}
 };
 
index 1296058..7810d3d 100644 (file)
@@ -12660,7 +12660,7 @@ static struct alc_config_preset alc268_presets[] = {
                .init_hook = alc268_toshiba_automute,
        },
        [ALC268_ACER] = {
-               .mixers = { alc268_acer_mixer, alc268_capture_nosrc_mixer,
+               .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
                            alc268_beep_mixer },
                .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
                                alc268_acer_verbs },
@@ -16852,6 +16852,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
        SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
        SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
                      ALC662_3ST_6ch_DIG),
+       SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB200", ALC663_ASUS_MODE4),
        SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
        SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
                      ALC662_3ST_6ch_DIG),
@@ -17145,70 +17146,145 @@ static struct alc_config_preset alc662_presets[] = {
  * BIOS auto configuration
  */
 
+/* convert from MIX nid to DAC */
+static inline hda_nid_t alc662_mix_to_dac(hda_nid_t nid)
+{
+       if (nid == 0x0f)
+               return 0x02;
+       else if (nid >= 0x0c && nid <= 0x0e)
+               return nid - 0x0c + 0x02;
+       else
+               return 0;
+}
+
+/* get MIX nid connected to the given pin targeted to DAC */
+static hda_nid_t alc662_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
+                                  hda_nid_t dac)
+{
+       hda_nid_t mix[4];
+       int i, num;
+
+       num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
+       for (i = 0; i < num; i++) {
+               if (alc662_mix_to_dac(mix[i]) == dac)
+                       return mix[i];
+       }
+       return 0;
+}
+
+/* look for an empty DAC slot */
+static hda_nid_t alc662_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t srcs[5];
+       int i, j, num;
+
+       num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
+       if (num < 0)
+               return 0;
+       for (i = 0; i < num; i++) {
+               hda_nid_t nid = alc662_mix_to_dac(srcs[i]);
+               if (!nid)
+                       continue;
+               for (j = 0; j < spec->multiout.num_dacs; j++)
+                       if (spec->multiout.dac_nids[j] == nid)
+                               break;
+               if (j >= spec->multiout.num_dacs)
+                       return nid;
+       }
+       return 0;
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
+                                    const struct auto_pin_cfg *cfg)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+       hda_nid_t dac;
+
+       spec->multiout.dac_nids = spec->private_dac_nids;
+       for (i = 0; i < cfg->line_outs; i++) {
+               dac = alc662_look_for_dac(codec, cfg->line_out_pins[i]);
+               if (!dac)
+                       continue;
+               spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+       }
+       return 0;
+}
+
+static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
+                             hda_nid_t nid, unsigned int chs)
+{
+       char name[32];
+       sprintf(name, "%s Playback Volume", pfx);
+       return add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                          HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+}
+
+static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
+                            hda_nid_t nid, unsigned int chs)
+{
+       char name[32];
+       sprintf(name, "%s Playback Switch", pfx);
+       return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+                          HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
+}
+
+#define alc662_add_stereo_vol(spec, pfx, nid) \
+       alc662_add_vol_ctl(spec, pfx, nid, 3)
+#define alc662_add_stereo_sw(spec, pfx, nid) \
+       alc662_add_sw_ctl(spec, pfx, nid, 3)
+
 /* add playback controls from the parsed DAC table */
-static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
+static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec,
                                             const struct auto_pin_cfg *cfg)
 {
-       char name[32];
+       struct alc_spec *spec = codec->spec;
        static const char *chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
-       hda_nid_t nid;
+       hda_nid_t nid, mix;
        int i, err;
 
        for (i = 0; i < cfg->line_outs; i++) {
-               if (!spec->multiout.dac_nids[i])
+               nid = spec->multiout.dac_nids[i];
+               if (!nid)
+                       continue;
+               mix = alc662_dac_to_mix(codec, cfg->line_out_pins[i], nid);
+               if (!mix)
                        continue;
-               nid = alc880_idx_to_dac(i);
                if (i == 2) {
                        /* Center/LFE */
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "Center Playback Volume",
-                                         HDA_COMPOSE_AMP_VAL(nid, 1, 0,
-                                                             HDA_OUTPUT));
+                       err = alc662_add_vol_ctl(spec, "Center", nid, 1);
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "LFE Playback Volume",
-                                         HDA_COMPOSE_AMP_VAL(nid, 2, 0,
-                                                             HDA_OUTPUT));
+                       err = alc662_add_vol_ctl(spec, "LFE", nid, 2);
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-                                         "Center Playback Switch",
-                                         HDA_COMPOSE_AMP_VAL(0x0e, 1, 0,
-                                                             HDA_INPUT));
+                       err = alc662_add_sw_ctl(spec, "Center", mix, 1);
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-                                         "LFE Playback Switch",
-                                         HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
-                                                             HDA_INPUT));
+                       err = alc662_add_sw_ctl(spec, "LFE", mix, 2);
                        if (err < 0)
                                return err;
                } else {
                        const char *pfx;
                        if (cfg->line_outs == 1 &&
                            cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
-                               if (!cfg->hp_pins)
+                               if (cfg->hp_outs)
                                        pfx = "Speaker";
                                else
                                        pfx = "PCM";
                        } else
                                pfx = chname[i];
-                       sprintf(name, "%s Playback Volume", pfx);
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
-                                         HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-                                                             HDA_OUTPUT));
+                       err = alc662_add_vol_ctl(spec, pfx, nid, 3);
                        if (err < 0)
                                return err;
                        if (cfg->line_outs == 1 &&
                            cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                                pfx = "Speaker";
-                       sprintf(name, "%s Playback Switch", pfx);
-                       err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
-                               HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
-                                                   3, 0, HDA_INPUT));
+                       err = alc662_add_sw_ctl(spec, pfx, mix, 3);
                        if (err < 0)
                                return err;
                }
@@ -17217,54 +17293,38 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
 }
 
 /* add playback controls for speaker and HP outputs */
-static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
+/* return DAC nid if any new DAC is assigned */
+static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                                        const char *pfx)
 {
-       hda_nid_t nid;
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t nid, mix;
        int err;
-       char name[32];
 
        if (!pin)
                return 0;
-
-       if (pin == 0x17) {
-               /* ALC663 has a mono output pin on 0x17 */
+       nid = alc662_look_for_dac(codec, pin);
+       if (!nid) {
+               char name[32];
+               /* the corresponding DAC is already occupied */
+               if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
+                       return 0; /* no way */
+               /* create a switch only */
                sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
-                                 HDA_COMPOSE_AMP_VAL(pin, 2, 0, HDA_OUTPUT));
-               return err;
+               return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+                                  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
-       if (alc880_is_fixed_pin(pin)) {
-               nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
-               /* printk(KERN_DEBUG "DAC nid=%x\n",nid); */
-               /* specify the DAC as the extra output */
-               if (!spec->multiout.hp_nid)
-                       spec->multiout.hp_nid = nid;
-               else
-                       spec->multiout.extra_out_nid[0] = nid;
-               /* control HP volume/switch on the output mixer amp */
-               nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
-               sprintf(name, "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
-                                 HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
-               if (err < 0)
-                       return err;
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_BIND_MUTE, name,
-                                 HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
-               if (err < 0)
-                       return err;
-       } else if (alc880_is_multi_pin(pin)) {
-               /* set manual connection */
-               /* we have only a switch on HP-out PIN */
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
-                                 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
+       mix = alc662_dac_to_mix(codec, pin, nid);
+       if (!mix)
+               return 0;
+       err = alc662_add_vol_ctl(spec, pfx, nid, 3);
+       if (err < 0)
+               return err;
+       err = alc662_add_sw_ctl(spec, pfx, mix, 3);
+       if (err < 0)
+               return err;
+       return nid;
 }
 
 /* create playback/capture controls for input pins */
@@ -17273,30 +17333,35 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 
 static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
                                              hda_nid_t nid, int pin_type,
-                                             int dac_idx)
+                                             hda_nid_t dac)
 {
+       int i, num;
+       hda_nid_t srcs[4];
+
        alc_set_pin_output(codec, nid, pin_type);
        /* need the manual connection? */
-       if (alc880_is_multi_pin(nid)) {
-               struct alc_spec *spec = codec->spec;
-               int idx = alc880_multi_pin_idx(nid);
-               snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
-                                   AC_VERB_SET_CONNECT_SEL,
-                                   alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
+       num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
+       if (num <= 1)
+               return;
+       for (i = 0; i < num; i++) {
+               if (alc662_mix_to_dac(srcs[i]) != dac)
+                       continue;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
+               return;
        }
 }
 
 static void alc662_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       int pin_type = get_pin_type(spec->autocfg.line_out_type);
        int i;
 
        for (i = 0; i <= HDA_SIDE; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
-               int pin_type = get_pin_type(spec->autocfg.line_out_type);
                if (nid)
                        alc662_auto_set_output_and_unmute(codec, nid, pin_type,
-                                                         i);
+                                       spec->multiout.dac_nids[i]);
        }
 }
 
@@ -17306,12 +17371,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
        hda_nid_t pin;
 
        pin = spec->autocfg.hp_pins[0];
-       if (pin) /* connect to front */
-               /* use dac 0 */
-               alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       if (pin)
+               alc662_auto_set_output_and_unmute(codec, pin, PIN_HP,
+                                                 spec->multiout.hp_nid);
        pin = spec->autocfg.speaker_pins[0];
        if (pin)
-               alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+               alc662_auto_set_output_and_unmute(codec, pin, PIN_OUT,
+                                       spec->multiout.extra_out_nid[0]);
 }
 
 #define ALC662_PIN_CD_NID              ALC880_PIN_CD_NID
@@ -17349,21 +17415,25 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        if (!spec->autocfg.line_outs)
                return 0; /* can't find valid BIOS pin config */
 
-       err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+       err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = alc662_auto_create_multi_out_ctls(spec, &spec->autocfg);
+       err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
-       err = alc662_auto_create_extra_out(spec,
+       err = alc662_auto_create_extra_out(codec,
                                           spec->autocfg.speaker_pins[0],
                                           "Speaker");
        if (err < 0)
                return err;
-       err = alc662_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
+       if (err)
+               spec->multiout.extra_out_nid[0] = err;
+       err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
                                           "Headphone");
        if (err < 0)
                return err;
+       if (err)
+               spec->multiout.hp_nid = err;
        err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
        if (err < 0)
                return err;
index 826137e..a9b2682 100644 (file)
@@ -182,8 +182,8 @@ struct sigmatel_jack {
 
 struct sigmatel_mic_route {
        hda_nid_t pin;
-       unsigned char mux_idx;
-       unsigned char dmux_idx;
+       signed char mux_idx;
+       signed char dmux_idx;
 };
 
 struct sigmatel_spec {
@@ -3469,18 +3469,26 @@ static int set_mic_route(struct hda_codec *codec,
                        break;
        if (i <= AUTO_PIN_FRONT_MIC) {
                /* analog pin */
-               mic->dmux_idx = 0;
                i = get_connection_index(codec, spec->mux_nids[0], pin);
                if (i < 0)
                        return -1;
                mic->mux_idx = i;
+               mic->dmux_idx = -1;
+               if (spec->dmux_nids)
+                       mic->dmux_idx = get_connection_index(codec,
+                                                            spec->dmux_nids[0],
+                                                            spec->mux_nids[0]);
        }  else if (spec->dmux_nids) {
                /* digital pin */
-               mic->mux_idx = 0;
                i = get_connection_index(codec, spec->dmux_nids[0], pin);
                if (i < 0)
                        return -1;
                mic->dmux_idx = i;
+               mic->mux_idx = -1;
+               if (spec->mux_nids)
+                       mic->mux_idx = get_connection_index(codec,
+                                                           spec->mux_nids[0],
+                                                           spec->dmux_nids[0]);
        }
        return 0;
 }
@@ -4557,11 +4565,11 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
                mic = &spec->ext_mic;
        else
                mic = &spec->int_mic;
-       if (mic->dmux_idx)
+       if (mic->dmux_idx >= 0)
                snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
                                          AC_VERB_SET_CONNECT_SEL,
                                          mic->dmux_idx);
-       else
+       if (mic->mux_idx >= 0)
                snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
                                          AC_VERB_SET_CONNECT_SEL,
                                          mic->mux_idx);