Merge branch 'fix/hda' into topic/hda
authorTakashi Iwai <tiwai@suse.de>
Thu, 29 Jul 2010 13:32:34 +0000 (15:32 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 29 Jul 2010 13:32:34 +0000 (15:32 +0200)
Documentation/sound/alsa/HD-Audio-Models.txt
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_via.c

index 1d38b0d..03771d7 100644 (file)
@@ -114,6 +114,11 @@ ALC662/663/272
   samsung-nc10 Samsung NC10 mini notebook
   auto         auto-config reading BIOS (default)
 
+ALC680
+======
+  base         Base model (ASUS NX90)
+  auto         auto-config reading BIOS (default)
+
 ALC882/883/885/888/889
 ======================
   3stack-dig   3-jack with SPDIF I/O
@@ -282,6 +287,7 @@ Conexant 5051
   hp           HP Spartan laptop
   hp-dv6736    HP dv6736
   hp-f700      HP Compaq Presario F700
+  ideapad      Lenovo IdeaPad laptop
   lenovo-x200  Lenovo X200 laptop
   toshiba      Toshiba Satellite M300
 
index ba2098d..e5c3484 100644 (file)
@@ -1565,6 +1565,17 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
+static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
+                            unsigned int ofs)
+{
+       u32 caps = query_amp_caps(codec, nid, dir);
+       /* get num steps */
+       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       if (ofs < caps)
+               caps -= ofs;
+       return caps;
+}
+
 /**
  * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
  *
@@ -1579,23 +1590,17 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
        u8 chs = get_amp_channels(kcontrol);
        int dir = get_amp_direction(kcontrol);
        unsigned int ofs = get_amp_offset(kcontrol);
-       u32 caps;
 
-       caps = query_amp_caps(codec, nid, dir);
-       /* num steps */
-       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
-       if (!caps) {
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = get_amp_max_value(codec, nid, dir, ofs);
+       if (!uinfo->value.integer.max) {
                printk(KERN_WARNING "hda_codec: "
                       "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
                       kcontrol->id.name);
                return -EINVAL;
        }
-       if (ofs < caps)
-               caps -= ofs;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = chs == 3 ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = caps;
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
@@ -1620,8 +1625,14 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
                 int ch, int dir, int idx, unsigned int ofs,
                 unsigned int val)
 {
+       unsigned int maxval;
+
        if (val > 0)
                val += ofs;
+       /* ofs = 0: raw max value */
+       maxval = get_amp_max_value(codec, nid, dir, 0);
+       if (val > maxval)
+               val = maxval;
        return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
                                        HDA_AMP_VOLMASK, val);
 }
index a1fc837..bf3ced5 100644 (file)
@@ -649,7 +649,9 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus,
        *codecp = NULL;
        if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
                list_for_each_entry(codec, &bus->codec_list, list) {
-                       if (codec->addr == caddr) {
+                       if (codec->vendor_id == vendorid &&
+                           codec->subsystem_id == subid &&
+                           codec->addr == caddr) {
                                *codecp = codec;
                                break;
                        }
index 2bf2cb5..c99425a 100644 (file)
@@ -131,6 +131,8 @@ struct conexant_spec {
        unsigned int dc_enable;
        unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
        unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
+
+       unsigned int beep_amp;
 };
 
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -515,6 +517,15 @@ static struct snd_kcontrol_new cxt_capture_mixers[] = {
        {}
 };
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static struct snd_kcontrol_new cxt_beep_mixer[] = {
+       HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+       { } /* end */
+};
+#endif
+
 static const char *slave_vols[] = {
        "Headphone Playback Volume",
        "Speaker Playback Volume",
@@ -580,6 +591,23 @@ static int conexant_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+       /* create beep controls if needed */
+       if (spec->beep_amp) {
+               struct snd_kcontrol_new *knew;
+               for (knew = cxt_beep_mixer; knew->name; knew++) {
+                       struct snd_kcontrol *kctl;
+                       kctl = snd_ctl_new1(knew, codec);
+                       if (!kctl)
+                               return -ENOMEM;
+                       kctl->private_value = spec->beep_amp;
+                       err = snd_hda_ctl_add(codec, 0, kctl);
+                       if (err < 0)
+                               return err;
+               }
+       }
+#endif
+
        return 0;
 }
 
@@ -590,6 +618,13 @@ static struct hda_codec_ops conexant_patch_ops = {
        .free = conexant_free,
 };
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define set_beep_amp(spec, nid, idx, dir) \
+       ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
+
 /*
  * EAPD control
  * the private value = nid | (invert << 8)
@@ -1130,9 +1165,10 @@ static int patch_cxt5045(struct hda_codec *codec)
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = cxt5045_init_verbs;
        spec->spdif_route = 0;
-       spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes),
-       spec->channel_mode = cxt5045_modes,
+       spec->num_channel_mode = ARRAY_SIZE(cxt5045_modes);
+       spec->channel_mode = cxt5045_modes;
 
+       set_beep_amp(spec, 0x16, 0, 1);
 
        codec->patch_ops = conexant_patch_ops;
 
@@ -1211,6 +1247,9 @@ static int patch_cxt5045(struct hda_codec *codec)
                break;
        }
 
+       if (spec->beep_amp)
+               snd_hda_attach_beep_device(codec, spec->beep_amp);
+
        return 0;
 }
 
@@ -1632,6 +1671,11 @@ static void cxt5051_update_speaker(struct hda_codec *codec)
        pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
        snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            pinctl);
+       /* on ideapad there is an aditional speaker (subwoofer) to mute */
+       if (spec->ideapad)
+               snd_hda_codec_write(codec, 0x1b, 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1888,6 +1932,13 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
 #endif
 }
 
+static struct hda_verb cxt5051_ideapad_init_verbs[] = {
+       /* Subwoofer */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+       { } /* end */
+};
+
 /* initialize jack-sensing, too */
 static int cxt5051_init(struct hda_codec *codec)
 {
@@ -1917,6 +1968,7 @@ enum {
        CXT5051_LENOVO_X200,    /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */
        CXT5051_F700,       /* HP Compaq Presario F700 */
        CXT5051_TOSHIBA,        /* Toshiba M300 & co */
+       CXT5051_IDEAPAD,        /* Lenovo IdeaPad Y430 */
        CXT5051_MODELS
 };
 
@@ -1927,6 +1979,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = {
        [CXT5051_LENOVO_X200]   = "lenovo-x200",
        [CXT5051_F700]          = "hp-700",
        [CXT5051_TOSHIBA]       = "toshiba",
+       [CXT5051_IDEAPAD]       = "ideapad",
 };
 
 static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
@@ -1938,6 +1991,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
                      CXT5051_LAPTOP),
        SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
+       SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD),
        {}
 };
 
@@ -1972,6 +2026,8 @@ static int patch_cxt5051(struct hda_codec *codec)
        spec->cur_adc = 0;
        spec->cur_adc_idx = 0;
 
+       set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
+
        codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
 
        board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
@@ -1999,8 +2055,16 @@ static int patch_cxt5051(struct hda_codec *codec)
                spec->mixers[0] = cxt5051_toshiba_mixers;
                spec->auto_mic = AUTO_MIC_PORTB;
                break;
+       case CXT5051_IDEAPAD:
+               spec->init_verbs[spec->num_init_verbs++] =
+                       cxt5051_ideapad_init_verbs;
+               spec->ideapad = 1;
+               break;
        }
 
+       if (spec->beep_amp)
+               snd_hda_attach_beep_device(codec, spec->beep_amp);
+
        return 0;
 }
 
@@ -2616,7 +2680,6 @@ static struct snd_kcontrol_new cxt5066_vostro_mixers[] = {
                .put = cxt5066_mic_boost_mux_enum_put,
                .private_value = 0x23 | 0x100,
        },
-       HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
        {}
 };
 
@@ -2977,8 +3040,10 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x21b4, "Thinkpad Edge", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
+       SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G series", CXT5066_IDEAPAD),
+       SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G series (AMD)", CXT5066_IDEAPAD),
        SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
-       SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD),
        {}
 };
 
@@ -3014,6 +3079,8 @@ static int patch_cxt5066(struct hda_codec *codec)
        spec->cur_adc = 0;
        spec->cur_adc_idx = 0;
 
+       set_beep_amp(spec, 0x13, 0, HDA_OUTPUT);
+
        board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
                                                  cxt5066_models, cxt5066_cfg_tbl);
        switch (board_config) {
@@ -3062,7 +3129,6 @@ static int patch_cxt5066(struct hda_codec *codec)
                spec->port_d_mode = 0;
                spec->dell_vostro = 1;
                spec->mic_boost = 3; /* default 30dB gain */
-               snd_hda_attach_beep_device(codec, 0x13);
 
                /* no S/PDIF out */
                spec->multiout.dig_out_nid = 0;
@@ -3104,6 +3170,9 @@ static int patch_cxt5066(struct hda_codec *codec)
                break;
        }
 
+       if (spec->beep_amp)
+               snd_hda_attach_beep_device(codec, spec->beep_amp);
+
        return 0;
 }
 
index 596ea2f..439d6e7 100644 (file)
@@ -256,6 +256,13 @@ enum {
        ALC882_MODEL_LAST,
 };
 
+/* ALC680 models */
+enum {
+       ALC680_BASE,
+       ALC680_AUTO,
+       ALC680_MODEL_LAST,
+};
+
 /* for GPIO Poll */
 #define GPIO_MASK      0x03
 
@@ -326,6 +333,12 @@ struct alc_spec {
        hda_nid_t *capsrc_nids;
        hda_nid_t dig_in_nid;           /* digital-in NID; optional */
 
+       /* capture setup for dynamic dual-adc switch */
+       unsigned int cur_adc_idx;
+       hda_nid_t cur_adc;
+       unsigned int cur_adc_stream_tag;
+       unsigned int cur_adc_format;
+
        /* capture source */
        unsigned int num_mux_defs;
        const struct hda_input_mux *input_mux;
@@ -367,6 +380,7 @@ struct alc_spec {
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
+       unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
        int init_amp;
 
        /* for virtual master */
@@ -1003,6 +1017,29 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
        return -1;
 }
 
+/* switch the current ADC according to the jack state */
+static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int present;
+       hda_nid_t new_adc;
+
+       present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
+       if (present)
+               spec->cur_adc_idx = 1;
+       else
+               spec->cur_adc_idx = 0;
+       new_adc = spec->adc_nids[spec->cur_adc_idx];
+       if (spec->cur_adc && spec->cur_adc != new_adc) {
+               /* stream is running, let's swap the current ADC */
+               snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+               spec->cur_adc = new_adc;
+               snd_hda_codec_setup_stream(codec, new_adc,
+                                          spec->cur_adc_stream_tag, 0,
+                                          spec->cur_adc_format);
+       }
+}
+
 static void alc_mic_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -1017,6 +1054,11 @@ static void alc_mic_automute(struct hda_codec *codec)
        if (snd_BUG_ON(!spec->adc_nids))
                return;
 
+       if (spec->dual_adc_switch) {
+               alc_dual_mic_adc_auto_switch(codec);
+               return;
+       }
+
        cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
 
        present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
@@ -3607,6 +3649,41 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
+/* analog capture with dynamic dual-adc changes */
+static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      struct snd_pcm_substream *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+       spec->cur_adc_stream_tag = stream_tag;
+       spec->cur_adc_format = format;
+       snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+       return 0;
+}
+
+static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      struct snd_pcm_substream *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+       spec->cur_adc = 0;
+       return 0;
+}
+
+static struct hda_pcm_stream dualmic_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0, /* fill later */
+       .ops = {
+               .prepare = dualmic_capture_pcm_prepare,
+               .cleanup = dualmic_capture_pcm_cleanup
+       },
+};
 
 /*
  */
@@ -5045,24 +5122,12 @@ static void fixup_automic_adc(struct hda_codec *codec)
        spec->auto_mic = 0; /* disable auto-mic to be sure */
 }
 
-/* choose the ADC/MUX containing the input pin and initialize the setup */
-static void fixup_single_adc(struct hda_codec *codec)
+/* set the default connection to that pin */
+static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t pin = 0;
        int i;
 
-       /* search for the input pin; there must be only one */
-       for (i = 0; i < AUTO_PIN_LAST; i++) {
-               if (spec->autocfg.input_pins[i]) {
-                       pin = spec->autocfg.input_pins[i];
-                       break;
-               }
-       }
-       if (!pin)
-               return;
-
-       /* set the default connection to that pin */
        for (i = 0; i < spec->num_adc_nids; i++) {
                hda_nid_t cap = spec->capsrc_nids ?
                        spec->capsrc_nids[i] : spec->adc_nids[i];
@@ -5071,11 +5136,6 @@ static void fixup_single_adc(struct hda_codec *codec)
                idx = get_connection_index(codec, cap, pin);
                if (idx < 0)
                        continue;
-               /* use only this ADC */
-               if (spec->capsrc_nids)
-                       spec->capsrc_nids += i;
-               spec->adc_nids += i;
-               spec->num_adc_nids = 1;
                /* select or unmute this route */
                if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
                        snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
@@ -5084,10 +5144,45 @@ static void fixup_single_adc(struct hda_codec *codec)
                        snd_hda_codec_write_cache(codec, cap, 0,
                                          AC_VERB_SET_CONNECT_SEL, idx);
                }
+               return i; /* return the found index */
+       }
+       return -1; /* not found */
+}
+
+/* choose the ADC/MUX containing the input pin and initialize the setup */
+static void fixup_single_adc(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t pin = 0;
+       int i;
+
+       /* search for the input pin; there must be only one */
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               if (spec->autocfg.input_pins[i]) {
+                       pin = spec->autocfg.input_pins[i];
+                       break;
+               }
+       }
+       if (!pin)
                return;
+       i = init_capsrc_for_pin(codec, pin);
+       if (i >= 0) {
+               /* use only this ADC */
+               if (spec->capsrc_nids)
+                       spec->capsrc_nids += i;
+               spec->adc_nids += i;
+               spec->num_adc_nids = 1;
        }
 }
 
+/* initialize dual adcs */
+static void fixup_dual_adc_switch(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       init_capsrc_for_pin(codec, spec->ext_mic.pin);
+       init_capsrc_for_pin(codec, spec->int_mic.pin);
+}
+
 static void set_capture_mixer(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -5101,7 +5196,10 @@ static void set_capture_mixer(struct hda_codec *codec)
        };
        if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
                int mux = 0;
-               if (spec->auto_mic)
+               int num_adcs = spec->num_adc_nids;
+               if (spec->dual_adc_switch)
+                       fixup_dual_adc_switch(codec);
+               else if (spec->auto_mic)
                        fixup_automic_adc(codec);
                else if (spec->input_mux) {
                        if (spec->input_mux->num_items > 1)
@@ -5109,7 +5207,9 @@ static void set_capture_mixer(struct hda_codec *codec)
                        else if (spec->input_mux->num_items == 1)
                                fixup_single_adc(codec);
                }
-               spec->cap_mixer = caps[mux][spec->num_adc_nids - 1];
+               if (spec->dual_adc_switch)
+                       num_adcs = 1;
+               spec->cap_mixer = caps[mux][num_adcs - 1];
        }
 }
 
@@ -14152,6 +14252,36 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+static int alc275_setup_dual_adc(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
+               return 0;
+       if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
+           (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
+               if (spec->ext_mic.pin <= 0x12) {
+                       spec->private_adc_nids[0] = 0x08;
+                       spec->private_adc_nids[1] = 0x11;
+                       spec->private_capsrc_nids[0] = 0x23;
+                       spec->private_capsrc_nids[1] = 0x22;
+               } else {
+                       spec->private_adc_nids[0] = 0x11;
+                       spec->private_adc_nids[1] = 0x08;
+                       spec->private_capsrc_nids[0] = 0x22;
+                       spec->private_capsrc_nids[1] = 0x23;
+               }
+               spec->adc_nids = spec->private_adc_nids;
+               spec->capsrc_nids = spec->private_capsrc_nids;
+               spec->num_adc_nids = 2;
+               spec->dual_adc_switch = 1;
+               snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
+                           spec->adc_nids[0], spec->adc_nids[1]);
+               return 1;
+       }
+       return 0;
+}
+
 /*
  * BIOS auto configuration
  */
@@ -14191,11 +14321,14 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
-       fillup_priv_adc_nids(codec, alc269_adc_candidates,
-                            sizeof(alc269_adc_candidates));
+
+       if (!alc275_setup_dual_adc(codec))
+               fillup_priv_adc_nids(codec, alc269_adc_candidates,
+                                    sizeof(alc269_adc_candidates));
 
        /* set default input source */
-       snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
+       if (!spec->dual_adc_switch)
+               snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
                                  0, AC_VERB_SET_CONNECT_SEL,
                                  spec->input_mux->items[0].index);
 
@@ -14493,6 +14626,10 @@ static int patch_alc269(struct hda_codec *codec)
                 */
                spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
                spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
+       } else if (spec->dual_adc_switch) {
+               spec->stream_analog_playback = &alc269_pcm_analog_playback;
+               /* switch ADC dynamically */
+               spec->stream_analog_capture = &dualmic_pcm_analog_capture;
        } else {
                spec->stream_analog_playback = &alc269_pcm_analog_playback;
                spec->stream_analog_capture = &alc269_pcm_analog_capture;
@@ -18635,7 +18772,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
 
        add_verb(spec, alc662_init_verbs);
        if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
-           codec->vendor_id == 0x10ec0665)
+           codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
                add_verb(spec, alc663_init_verbs);
 
        if (codec->vendor_id == 0x10ec0272)
@@ -18780,6 +18917,335 @@ static int patch_alc888(struct hda_codec *codec)
        return patch_alc882(codec);
 }
 
+/*
+ * ALC680 support
+ */
+#define ALC680_DIGOUT_NID      ALC880_DIGOUT_NID
+#define alc680_modes           alc260_modes
+
+static hda_nid_t alc680_dac_nids[3] = {
+       /* Lout1, Lout2, hp */
+       0x02, 0x03, 0x04
+};
+
+static hda_nid_t alc680_adc_nids[3] = {
+       /* ADC0-2 */
+       /* DMIC, MIC, Line-in*/
+       0x07, 0x08, 0x09
+};
+
+static struct snd_kcontrol_new alc680_base_mixer[] = {
+       /* output mixer control */
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x4, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+       { }
+};
+
+static struct snd_kcontrol_new alc680_capture_mixer[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc680_init_verbs[] = {
+       /* Unmute DAC0-1 and set vol = 0 */
+       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       { }
+};
+
+/* create input playback/capture controls for the given pin */
+static int alc680_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+                                   const char *ctlname, int idx)
+{
+       hda_nid_t dac;
+       int err;
+
+       switch (nid) {
+       case 0x14:
+               dac = 0x02;
+               break;
+       case 0x15:
+               dac = 0x03;
+               break;
+       case 0x16:
+               dac = 0x04;
+               break;
+       default:
+               return 0;
+       }
+       if (spec->multiout.dac_nids[0] != dac &&
+           spec->multiout.dac_nids[1] != dac) {
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+                                 HDA_COMPOSE_AMP_VAL(dac, 3, idx,
+                                                     HDA_OUTPUT));
+               if (err < 0)
+                       return err;
+
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+                         HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+
+               if (err < 0)
+                       return err;
+               spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+       }
+
+       return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec,
+                                            const struct auto_pin_cfg *cfg)
+{
+       hda_nid_t nid;
+       int err;
+
+       spec->multiout.dac_nids = spec->private_dac_nids;
+
+       nid = cfg->line_out_pins[0];
+       if (nid) {
+               const char *name;
+               if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+                       name = "Speaker";
+               else
+                       name = "Front";
+               err = alc680_new_analog_output(spec, nid, name, 0);
+               if (err < 0)
+                       return err;
+       }
+
+       nid = cfg->speaker_pins[0];
+       if (nid) {
+               err = alc680_new_analog_output(spec, nid, "Speaker", 0);
+               if (err < 0)
+                       return err;
+       }
+       nid = cfg->hp_pins[0];
+       if (nid) {
+               err = alc680_new_analog_output(spec, nid, "Headphone", 0);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void alc680_auto_set_output_and_unmute(struct hda_codec *codec,
+                                             hda_nid_t nid, int pin_type)
+{
+       alc_set_pin_output(codec, nid, pin_type);
+}
+
+static void alc680_auto_init_multi_out(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t nid = spec->autocfg.line_out_pins[0];
+       if (nid) {
+               int pin_type = get_pin_type(spec->autocfg.line_out_type);
+               alc680_auto_set_output_and_unmute(codec, nid, pin_type);
+       }
+}
+
+static void alc680_auto_init_hp_out(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t pin;
+
+       pin = spec->autocfg.hp_pins[0];
+       if (pin)
+               alc680_auto_set_output_and_unmute(codec, pin, PIN_HP);
+       pin = spec->autocfg.speaker_pins[0];
+       if (pin)
+               alc680_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+}
+
+/* pcm configuration: identical with ALC880 */
+#define alc680_pcm_analog_playback     alc880_pcm_analog_playback
+#define alc680_pcm_analog_capture      alc880_pcm_analog_capture
+#define alc680_pcm_analog_alt_capture  alc880_pcm_analog_alt_capture
+#define alc680_pcm_digital_playback    alc880_pcm_digital_playback
+
+static struct hda_input_mux alc680_capture_source = {
+       .num_items = 1,
+       .items = {
+               { "Mic", 0x0 },
+       },
+};
+
+/*
+ * BIOS auto configuration
+ */
+static int alc680_parse_auto_config(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
+       static hda_nid_t alc680_ignore[] = { 0 };
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+                                          alc680_ignore);
+       if (err < 0)
+               return err;
+       if (!spec->autocfg.line_outs) {
+               if (spec->autocfg.dig_outs || spec->autocfg.dig_in_pin) {
+                       spec->multiout.max_channels = 2;
+                       spec->no_analog = 1;
+                       goto dig_only;
+               }
+               return 0; /* can't find valid BIOS pin config */
+       }
+       err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
+       if (err < 0)
+               return err;
+
+       spec->multiout.max_channels = 2;
+
+ dig_only:
+       /* digital only support output */
+       if (spec->autocfg.dig_outs) {
+               spec->multiout.dig_out_nid = ALC680_DIGOUT_NID;
+               spec->dig_out_type = spec->autocfg.dig_out_type[0];
+       }
+       if (spec->kctls.list)
+               add_mixer(spec, spec->kctls.list);
+
+       add_verb(spec, alc680_init_verbs);
+       spec->num_mux_defs = 1;
+       spec->input_mux = &alc680_capture_source;
+
+       err = alc_auto_add_mic_boost(codec);
+       if (err < 0)
+               return err;
+
+       return 1;
+}
+
+#define alc680_auto_init_analog_input  alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc680_auto_init(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       alc680_auto_init_multi_out(codec);
+       alc680_auto_init_hp_out(codec);
+       alc680_auto_init_analog_input(codec);
+       if (spec->unsol_event)
+               alc_inithook(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc680_models[ALC680_MODEL_LAST] = {
+       [ALC680_BASE]           = "base",
+       [ALC680_AUTO]           = "auto",
+};
+
+static struct snd_pci_quirk alc680_cfg_tbl[] = {
+       SND_PCI_QUIRK(0x1043, 0x12f3, "ASUS NX90", ALC680_BASE),
+       {}
+};
+
+static struct alc_config_preset alc680_presets[] = {
+       [ALC680_BASE] = {
+               .mixers = { alc680_base_mixer },
+               .cap_mixer =  alc680_capture_mixer,
+               .init_verbs = { alc680_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc680_dac_nids),
+               .dac_nids = alc680_dac_nids,
+               .num_adc_nids = ARRAY_SIZE(alc680_adc_nids),
+               .adc_nids = alc680_adc_nids,
+               .hp_nid = 0x04,
+               .dig_out_nid = ALC680_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc680_modes),
+               .channel_mode = alc680_modes,
+               .input_mux = &alc680_capture_source,
+       },
+};
+
+static int patch_alc680(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+       int board_config;
+       int err;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
+                                                 alc680_models,
+                                                 alc680_cfg_tbl);
+
+       if (board_config < 0 || board_config >= ALC680_MODEL_LAST) {
+               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+                      codec->chip_name);
+               board_config = ALC680_AUTO;
+       }
+
+       if (board_config == ALC680_AUTO) {
+               /* automatic parse from the BIOS config */
+               err = alc680_parse_auto_config(codec);
+               if (err < 0) {
+                       alc_free(codec);
+                       return err;
+               } else if (!err) {
+                       printk(KERN_INFO
+                              "hda_codec: Cannot set up configuration "
+                              "from BIOS.  Using base mode...\n");
+                       board_config = ALC680_BASE;
+               }
+       }
+
+       if (board_config != ALC680_AUTO)
+               setup_preset(codec, &alc680_presets[board_config]);
+
+       spec->stream_analog_playback = &alc680_pcm_analog_playback;
+       spec->stream_analog_capture = &alc680_pcm_analog_capture;
+       spec->stream_analog_alt_capture = &alc680_pcm_analog_alt_capture;
+       spec->stream_digital_playback = &alc680_pcm_digital_playback;
+
+       if (!spec->adc_nids) {
+               spec->adc_nids = alc680_adc_nids;
+               spec->num_adc_nids = ARRAY_SIZE(alc680_adc_nids);
+       }
+
+       if (!spec->cap_mixer)
+               set_capture_mixer(codec);
+
+       spec->vmaster_nid = 0x02;
+
+       codec->patch_ops = alc_patch_ops;
+       if (board_config == ALC680_AUTO)
+               spec->init_hook = alc680_auto_init;
+
+       return 0;
+}
+
 /*
  * patch entries
  */
@@ -18804,6 +19270,7 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
        { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
        { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
+       { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
        { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
        { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
        { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
index 7345381..ae3acb2 100644 (file)
@@ -552,24 +552,30 @@ static void via_auto_init_hp_out(struct hda_codec *codec)
        }
 }
 
+static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+
 static void via_auto_init_analog_input(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
+       unsigned int ctl;
        int i;
 
        for (i = 0; i < AUTO_PIN_LAST; i++) {
                hda_nid_t nid = spec->autocfg.input_pins[i];
+               if (!nid)
+                       continue;
 
+               if (spec->smart51_enabled && is_smart51_pins(spec, nid))
+                       ctl = PIN_OUT;
+               else if (i <= AUTO_PIN_FRONT_MIC)
+                       ctl = PIN_VREF50;
+               else
+                       ctl = PIN_IN;
                snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   (i <= AUTO_PIN_FRONT_MIC ?
-                                    PIN_VREF50 : PIN_IN));
-
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
        }
 }
 
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
-
 static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
                                unsigned int *affected_parm)
 {
@@ -658,6 +664,8 @@ static void set_jack_power_state(struct hda_codec *codec)
                /* PW0 (19h), SW1 (18h), AOW1 (11h) */
                parm = AC_PWRST_D3;
                set_pin_power_state(codec, 0x19, &parm);
+               if (spec->smart51_enabled)
+                       parm = AC_PWRST_D0;
                snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
                                    parm);
                snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
@@ -667,6 +675,8 @@ static void set_jack_power_state(struct hda_codec *codec)
                if (is_8ch) {
                        parm = AC_PWRST_D3;
                        set_pin_power_state(codec, 0x22, &parm);
+                       if (spec->smart51_enabled)
+                               parm = AC_PWRST_D0;
                        snd_hda_codec_write(codec, 0x26, 0,
                                            AC_VERB_SET_POWER_STATE, parm);
                        snd_hda_codec_write(codec, 0x24, 0,
@@ -3915,6 +3925,13 @@ static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
                }
        }
 
+       /* for Smart 5.1, line/mic inputs double as output pins */
+       if (cfg->line_outs == 1) {
+               spec->multiout.num_dacs = 3;
+               spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
+               spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
+       }
+
        return 0;
 }
 
@@ -3932,7 +3949,8 @@ static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
        for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
                nid = cfg->line_out_pins[i];
 
-               if (!nid)
+               /* for Smart 5.1, there are always at least six channels */
+               if (!nid && i > AUTO_SEQ_CENLFE)
                        continue;
 
                nid_vol = nid_vols[i];