ALSA: hda - Add amp initialization for realtek auto mode
authorTakashi Iwai <tiwai@suse.de>
Wed, 22 Apr 2009 14:31:35 +0000 (16:31 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 27 Apr 2009 16:00:30 +0000 (18:00 +0200)
In the realtek auto-probing mode, the initialization of amp with
some magic COEF or EAPD verbs is applied only when the codec SSID
has valid values to satisfy the realtek's definition.
However, many devices don't provide in that way, thus the device
doesn't work as is.

This patch allows the same initialization code even if the SSID
doesn't pass the bit test.  Also, alc_subsystem_id() is changed
just to check and define the type, so that it's called in the
parser, instead of the initializer.

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

index 583603f449b49a8423afb05596dd97464805669a..3a6306302c709b98f1ecaa4a6c27d1eea4ef360d 100644 (file)
@@ -253,6 +253,15 @@ enum {
 /* for GPIO Poll */
 #define GPIO_MASK      0x03
 
+/* extra amp-initialization sequence types */
+enum {
+       ALC_INIT_NONE,
+       ALC_INIT_DEFAULT,
+       ALC_INIT_GPIO1,
+       ALC_INIT_GPIO2,
+       ALC_INIT_GPIO3,
+};
+
 struct alc_spec {
        /* codec parameterization */
        struct snd_kcontrol_new *mixers[5];     /* mixer arrays */
@@ -322,6 +331,7 @@ struct alc_spec {
 
        /* other flags */
        unsigned int no_analog :1; /* digital I/O only */
+       int init_amp;
 
        /* for virtual master */
        hda_nid_t vmaster_nid;
@@ -994,74 +1004,21 @@ static void alc888_coef_init(struct hda_codec *codec)
                                    AC_VERB_SET_PROC_COEF, 0x3030);
 }
 
-/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
- *     31 ~ 16 :       Manufacture ID
- *     15 ~ 8  :       SKU ID
- *     7  ~ 0  :       Assembly ID
- *     port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
- */
-static void alc_subsystem_id(struct hda_codec *codec,
-                            unsigned int porta, unsigned int porte,
-                            unsigned int portd)
+static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
-       unsigned int ass, tmp, i;
-       unsigned nid;
-       struct alc_spec *spec = codec->spec;
-
-       ass = codec->subsystem_id & 0xffff;
-       if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
-               goto do_sku;
-
-       /*
-        * 31~30        : port conetcivity
-        * 29~21        : reserve
-        * 20           : PCBEEP input
-        * 19~16        : Check sum (15:1)
-        * 15~1         : Custom
-        * 0            : override
-       */
-       nid = 0x1d;
-       if (codec->vendor_id == 0x10ec0260)
-               nid = 0x17;
-       ass = snd_hda_codec_get_pincfg(codec, nid);
-       snd_printd("realtek: No valid SSID, "
-                  "checking pincfg 0x%08x for NID 0x%x\n",
-                  ass, nid);
-       if (!(ass & 1) && !(ass & 0x100000))
-               return;
-       if ((ass >> 30) != 1)   /* no physical connection */
-               return;
+       unsigned int tmp;
 
-       /* check sum */
-       tmp = 0;
-       for (i = 1; i < 16; i++) {
-               if ((ass >> i) & 1)
-                       tmp++;
-       }
-       if (((ass >> 16) & 0xf) != tmp)
-               return;
-do_sku:
-       snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
-                  ass & 0xffff, codec->vendor_id);
-       /*
-        * 0 : override
-        * 1 :  Swap Jack
-        * 2 : 0 --> Desktop, 1 --> Laptop
-        * 3~5 : External Amplifier control
-        * 7~6 : Reserved
-       */
-       tmp = (ass & 0x38) >> 3;        /* external Amp control */
-       switch (tmp) {
-       case 1:
+       switch (type) {
+       case ALC_INIT_GPIO1:
                snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
                break;
-       case 3:
+       case ALC_INIT_GPIO2:
                snd_hda_sequence_write(codec, alc_gpio2_init_verbs);
                break;
-       case 7:
+       case ALC_INIT_GPIO3:
                snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
                break;
-       case 5: /* set EAPD output high */
+       case ALC_INIT_DEFAULT:
                switch (codec->vendor_id) {
                case 0x10ec0260:
                        snd_hda_codec_write(codec, 0x0f, 0,
@@ -1115,7 +1072,7 @@ do_sku:
                                            tmp | 0x2010);
                        break;
                case 0x10ec0888:
-                       /*alc888_coef_init(codec);*/ /* called in alc_init() */
+                       alc888_coef_init(codec);
                        break;
                case 0x10ec0267:
                case 0x10ec0268:
@@ -1130,7 +1087,104 @@ do_sku:
                                            tmp | 0x3000);
                        break;
                }
-       default:
+               break;
+       }
+}
+
+static void alc_init_auto_hp(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (!spec->autocfg.hp_pins[0])
+               return;
+
+       if (!spec->autocfg.speaker_pins[0]) {
+               if (spec->autocfg.line_out_pins[0])
+                       spec->autocfg.speaker_pins[0] =
+                               spec->autocfg.line_out_pins[0];
+               else
+                       return;
+       }
+
+       snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+                                 AC_VERB_SET_UNSOLICITED_ENABLE,
+                                 AC_USRSP_EN | ALC880_HP_EVENT);
+       spec->unsol_event = alc_sku_unsol_event;
+}
+
+/* check subsystem ID and set up device-specific initialization;
+ * return 1 if initialized, 0 if invalid SSID
+ */
+/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
+ *     31 ~ 16 :       Manufacture ID
+ *     15 ~ 8  :       SKU ID
+ *     7  ~ 0  :       Assembly ID
+ *     port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
+ */
+static int alc_subsystem_id(struct hda_codec *codec,
+                           hda_nid_t porta, hda_nid_t porte,
+                           hda_nid_t portd)
+{
+       unsigned int ass, tmp, i;
+       unsigned nid;
+       struct alc_spec *spec = codec->spec;
+
+       ass = codec->subsystem_id & 0xffff;
+       if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
+               goto do_sku;
+
+       /* invalid SSID, check the special NID pin defcfg instead */
+       /*
+        * 31~30        : port conetcivity
+        * 29~21        : reserve
+        * 20           : PCBEEP input
+        * 19~16        : Check sum (15:1)
+        * 15~1         : Custom
+        * 0            : override
+       */
+       nid = 0x1d;
+       if (codec->vendor_id == 0x10ec0260)
+               nid = 0x17;
+       ass = snd_hda_codec_get_pincfg(codec, nid);
+       snd_printd("realtek: No valid SSID, "
+                  "checking pincfg 0x%08x for NID 0x%x\n",
+                  nid, ass);
+       if (!(ass & 1) && !(ass & 0x100000))
+               return 0;
+       if ((ass >> 30) != 1)   /* no physical connection */
+               return 0;
+
+       /* check sum */
+       tmp = 0;
+       for (i = 1; i < 16; i++) {
+               if ((ass >> i) & 1)
+                       tmp++;
+       }
+       if (((ass >> 16) & 0xf) != tmp)
+               return 0;
+do_sku:
+       snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
+                  ass & 0xffff, codec->vendor_id);
+       /*
+        * 0 : override
+        * 1 :  Swap Jack
+        * 2 : 0 --> Desktop, 1 --> Laptop
+        * 3~5 : External Amplifier control
+        * 7~6 : Reserved
+       */
+       tmp = (ass & 0x38) >> 3;        /* external Amp control */
+       switch (tmp) {
+       case 1:
+               spec->init_amp = ALC_INIT_GPIO1;
+               break;
+       case 3:
+               spec->init_amp = ALC_INIT_GPIO2;
+               break;
+       case 7:
+               spec->init_amp = ALC_INIT_GPIO3;
+               break;
+       case 5:
+               spec->init_amp = ALC_INIT_DEFAULT;
                break;
        }
 
@@ -1138,7 +1192,7 @@ do_sku:
         * when the external headphone out jack is plugged"
         */
        if (!(ass & 0x8000))
-               return;
+               return 1;
        /*
         * 10~8 : Jack location
         * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
@@ -1146,14 +1200,6 @@ do_sku:
         * 15   : 1 --> enable the function "Mute internal speaker
         *              when the external headphone out jack is plugged"
         */
-       if (!spec->autocfg.speaker_pins[0]) {
-               if (spec->autocfg.line_out_pins[0])
-                       spec->autocfg.speaker_pins[0] =
-                               spec->autocfg.line_out_pins[0];
-               else
-                       return;
-       }
-
        if (!spec->autocfg.hp_pins[0]) {
                tmp = (ass >> 11) & 0x3;        /* HP to chassis */
                if (tmp == 0)
@@ -1163,23 +1209,23 @@ do_sku:
                else if (tmp == 2)
                        spec->autocfg.hp_pins[0] = portd;
                else
-                       return;
+                       return 1;
        }
-       if (spec->autocfg.hp_pins[0])
-               snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,
-                       AC_VERB_SET_UNSOLICITED_ENABLE,
-                       AC_USRSP_EN | ALC880_HP_EVENT);
 
-#if 0 /* it's broken in some acses -- temporarily disabled */
-       if (spec->autocfg.input_pins[AUTO_PIN_MIC] &&
-               spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC])
-               snd_hda_codec_write(codec,
-                       spec->autocfg.input_pins[AUTO_PIN_MIC], 0,
-                       AC_VERB_SET_UNSOLICITED_ENABLE,
-                       AC_USRSP_EN | ALC880_MIC_EVENT);
-#endif /* disabled */
+       alc_init_auto_hp(codec);
+       return 1;
+}
 
-       spec->unsol_event = alc_sku_unsol_event;
+static void alc_ssid_check(struct hda_codec *codec,
+                          hda_nid_t porta, hda_nid_t porte, hda_nid_t portd)
+{
+       if (!alc_subsystem_id(codec, porta, porte, portd)) {
+               struct alc_spec *spec = codec->spec;
+               snd_printd("realtek: "
+                          "Enable default setup for auto mode as fallback\n");
+               spec->init_amp = ALC_INIT_DEFAULT;
+               alc_init_auto_hp(codec);
+       }
 }
 
 /*
@@ -2923,8 +2969,7 @@ static int alc_init(struct hda_codec *codec)
        unsigned int i;
 
        alc_fix_pll(codec);
-       if (codec->vendor_id == 0x10ec0888)
-               alc888_coef_init(codec);
+       alc_auto_init_amp(codec, spec->init_amp);
 
        for (i = 0; i < spec->num_init_verbs; i++)
                snd_hda_sequence_write(codec, spec->init_verbs[i]);
@@ -4198,7 +4243,6 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        for (i = 0; i < spec->autocfg.line_outs; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -4303,6 +4347,8 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
+       alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
        return 1;
 }
 
@@ -5678,7 +5724,6 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        hda_nid_t nid;
 
-       alc_subsystem_id(codec, 0x10, 0x15, 0x0f);
        nid = spec->autocfg.line_out_pins[0];
        if (nid) {
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -5788,6 +5833,8 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
        spec->num_mux_defs = 1;
        spec->input_mux = &spec->private_imux[0];
 
+       alc_ssid_check(codec, 0x10, 0x15, 0x0f);
+
        return 1;
 }
 
@@ -7013,7 +7060,6 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        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);
@@ -9154,7 +9200,6 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        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);
@@ -9317,6 +9362,7 @@ static int patch_alc883(struct hda_codec *codec)
                if (!spec->capsrc_nids)
                        spec->capsrc_nids = alc883_capsrc_nids;
                spec->capture_style = CAPT_MIX; /* matrix-style capture */
+               spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
                break;
        case 0x10ec0889:
                spec->stream_name_analog = "ALC889 Analog";
@@ -10842,6 +10888,8 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       alc_ssid_check(codec, 0x15, 0x14, 0x1b);
+
        return 1;
 }
 
@@ -13925,7 +13973,6 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x0e, 0x0f, 0x0b);
        for (i = 0; i < spec->autocfg.line_outs; i++) {
                hda_nid_t nid = spec->autocfg.line_out_pins[i];
                int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -14008,6 +14055,8 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
        spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
        set_capture_mixer(spec);
 
+       alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
+
        return 1;
 }
 
@@ -14889,7 +14938,6 @@ static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        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);
@@ -15107,6 +15155,8 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
        return 1;
 }
 
@@ -16931,7 +16981,6 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec)
        struct alc_spec *spec = codec->spec;
        int i;
 
-       alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
        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);
@@ -17028,6 +17077,8 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
        if (err < 0)
                return err;
 
+       alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
        return 1;
 }