Email/web address change
[linux-2.6-block.git] / sound / pci / hda / patch_realtek.c
index 5e82acf77c5ae84cbd3f77b1d3b8165246a8df22..f6316507d764d7a201453273db7ad4d53032b493 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2004 Kailang Yang <kailang@realtek.com.tw>
  *                    PeiSen Hou <pshou@realtek.com.tw>
  *                    Takashi Iwai <tiwai@suse.de>
- *                    Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+ *                    Jonathan Woithe <jwoithe@just42.net>
  *
  *  This driver is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -177,6 +177,7 @@ struct alc_spec {
        unsigned int detect_lo:1;       /* Line-out detection enabled */
        unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
        unsigned int automute_lo_possible:1;      /* there are line outs and HP */
+       unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
@@ -185,7 +186,6 @@ struct alc_spec {
        unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
        unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
        unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
-       unsigned int use_jack_tbl:1; /* 1 for model=auto */
 
        /* auto-mute control */
        int automute_mode;
@@ -496,13 +496,24 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 
        for (i = 0; i < num_pins; i++) {
                hda_nid_t nid = pins[i];
+               unsigned int val;
                if (!nid)
                        break;
                switch (spec->automute_mode) {
                case ALC_AUTOMUTE_PIN:
+                       /* don't reset VREF value in case it's controlling
+                        * the amp (see alc861_fixup_asus_amp_vref_0f())
+                        */
+                       if (spec->keep_vref_in_automute) {
+                               val = snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                               val &= ~PIN_HP;
+                       } else
+                               val = 0;
+                       val |= pin_bits;
                        snd_hda_codec_write(codec, nid, 0,
                                            AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           pin_bits);
+                                           val);
                        break;
                case ALC_AUTOMUTE_AMP:
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
@@ -621,17 +632,10 @@ static void alc_mic_automute(struct hda_codec *codec)
                alc_mux_select(codec, 0, spec->int_mic_idx, false);
 }
 
-/* unsolicited event for HP jack sensing */
-static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
+/* handle the specified unsol action (ALC_XXX_EVENT) */
+static void alc_exec_unsol_event(struct hda_codec *codec, int action)
 {
-       struct alc_spec *spec = codec->spec;
-       if (codec->vendor_id == 0x10ec0880)
-               res >>= 28;
-       else
-               res >>= 26;
-       if (spec->use_jack_tbl)
-               res = snd_hda_jack_get_action(codec, res);
-       switch (res) {
+       switch (action) {
        case ALC_HP_EVENT:
                alc_hp_automute(codec);
                break;
@@ -645,6 +649,17 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
        snd_hda_jack_report_sync(codec);
 }
 
+/* unsolicited event for HP jack sensing */
+static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       if (codec->vendor_id == 0x10ec0880)
+               res >>= 28;
+       else
+               res >>= 26;
+       res = snd_hda_jack_get_action(codec, res);
+       alc_exec_unsol_event(codec, res);
+}
+
 /* call init functions of standard auto-mute helpers */
 static void alc_inithook(struct hda_codec *codec)
 {
@@ -1883,7 +1898,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = {
 };
 #endif
 
-static int alc_build_controls(struct hda_codec *codec)
+static int __alc_build_controls(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        struct snd_kcontrol *kctl = NULL;
@@ -2029,11 +2044,16 @@ static int alc_build_controls(struct hda_codec *codec)
 
        alc_free_kctls(codec); /* no longer needed */
 
-       err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+       return 0;
+}
+
+static int alc_build_controls(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err = __alc_build_controls(codec);
        if (err < 0)
                return err;
-
-       return 0;
+       return snd_hda_jack_add_kctls(codec, &spec->autocfg);
 }
 
 
@@ -3233,7 +3253,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
        int i, err, noutputs;
 
        noutputs = cfg->line_outs;
-       if (spec->multi_ios > 0)
+       if (spec->multi_ios > 0 && cfg->line_outs < 3)
                noutputs += spec->multi_ios;
 
        for (i = 0; i < noutputs; i++) {
@@ -3904,7 +3924,6 @@ static void set_capture_mixer(struct hda_codec *codec)
 static void alc_auto_init_std(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       spec->use_jack_tbl = 1;
        alc_auto_init_multi_out(codec);
        alc_auto_init_extra_out(codec);
        alc_auto_init_analog_input(codec);
@@ -4168,6 +4187,8 @@ static int patch_alc880(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC_MODEL_AUTO)
                spec->init_hook = alc_auto_init_std;
+       else
+               codec->patch_ops.build_controls = __alc_build_controls;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc880_loopbacks;
@@ -4297,6 +4318,8 @@ static int patch_alc260(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC_MODEL_AUTO)
                spec->init_hook = alc_auto_init_std;
+       else
+               codec->patch_ops.build_controls = __alc_build_controls;
        spec->shutup = alc_eapd_shutup;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
@@ -4691,6 +4714,8 @@ static int patch_alc882(struct hda_codec *codec)
        codec->patch_ops = alc_patch_ops;
        if (board_config == ALC_MODEL_AUTO)
                spec->init_hook = alc_auto_init_std;
+       else
+               codec->patch_ops.build_controls = __alc_build_controls;
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
@@ -4722,7 +4747,6 @@ enum {
        ALC262_FIXUP_FSC_H270,
        ALC262_FIXUP_HP_Z200,
        ALC262_FIXUP_TYAN,
-       ALC262_FIXUP_TOSHIBA_RX1,
        ALC262_FIXUP_LENOVO_3000,
        ALC262_FIXUP_BENQ,
        ALC262_FIXUP_BENQ_T31,
@@ -4752,16 +4776,6 @@ static const struct alc_fixup alc262_fixups[] = {
                        { }
                }
        },
-       [ALC262_FIXUP_TOSHIBA_RX1] = {
-               .type = ALC_FIXUP_PINS,
-               .v.pins = (const struct alc_pincfg[]) {
-                       { 0x14, 0x90170110 }, /* speaker */
-                       { 0x15, 0x0421101f }, /* HP */
-                       { 0x1a, 0x40f000f0 }, /* N/A */
-                       { 0x1b, 0x40f000f0 }, /* N/A */
-                       { 0x1e, 0x40f000f0 }, /* N/A */
-               }
-       },
        [ALC262_FIXUP_LENOVO_3000] = {
                .type = ALC_FIXUP_VERBS,
                .v.verbs = (const struct hda_verb[]) {
@@ -4794,8 +4808,6 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = {
        SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ),
        SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ),
        SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN),
-       SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
-                     ALC262_FIXUP_TOSHIBA_RX1),
        SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270),
        SND_PCI_QUIRK(0x17aa, 0x384e, "Lenovo 3000", ALC262_FIXUP_LENOVO_3000),
        SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_FIXUP_BENQ),
@@ -5364,7 +5376,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
                      ALC269_FIXUP_AMIC),
        SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269_FIXUP_AMIC),
-       SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269_FIXUP_AMIC),
        SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269_FIXUP_AMIC),
        SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_FIXUP_AMIC),
        SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269_FIXUP_AMIC),
@@ -5573,8 +5584,28 @@ static const struct hda_amp_list alc861_loopbacks[] = {
 /* Pin config fixes */
 enum {
        PINFIX_FSC_AMILO_PI1505,
+       PINFIX_ASUS_A6RP,
 };
 
+/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */
+static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
+                       const struct alc_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int val;
+
+       if (action != ALC_FIXUP_ACT_INIT)
+               return;
+       val = snd_hda_codec_read(codec, 0x0f, 0,
+                                AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
+               val |= AC_PINCTL_IN_EN;
+       val |= AC_PINCTL_VREF_50;
+       snd_hda_codec_write(codec, 0x0f, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       spec->keep_vref_in_automute = 1;
+}
+
 static const struct alc_fixup alc861_fixups[] = {
        [PINFIX_FSC_AMILO_PI1505] = {
                .type = ALC_FIXUP_PINS,
@@ -5584,9 +5615,15 @@ static const struct alc_fixup alc861_fixups[] = {
                        { }
                }
        },
+       [PINFIX_ASUS_A6RP] = {
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc861_fixup_asus_amp_vref_0f,
+       },
 };
 
 static const struct snd_pci_quirk alc861_fixup_tbl[] = {
+       SND_PCI_QUIRK_VENDOR(0x1043, "ASUS laptop", PINFIX_ASUS_A6RP),
+       SND_PCI_QUIRK(0x1584, 0x2b01, "Haier W18", PINFIX_ASUS_A6RP),
        SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
        {}
 };