ALSA: hda - Merge ALC680 auto-parser to the standard parser
authorTakashi Iwai <tiwai@suse.de>
Fri, 8 Jul 2011 09:07:59 +0000 (11:07 +0200)
committerTakashi Iwai <tiwai@suse.de>
Fri, 8 Jul 2011 09:07:59 +0000 (11:07 +0200)
Improved the standard Realtek auto-parser to support the codec topology
like ALC680.

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

index a29e6b3c6a70457f705b6a521cc3ee89e51d6219..f82333b11e417aa19485a3403723cc943b53bb9f 100644 (file)
@@ -2682,6 +2682,8 @@ static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid)
        hda_nid_t list[5];
        int i, num;
 
+       if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT)
+               return nid;
        num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list));
        for (i = 0; i < num; i++) {
                if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT)
@@ -2838,6 +2840,8 @@ static int alc_auto_add_vol_ctl(struct hda_codec *codec,
                              const char *pfx, int cidx,
                              hda_nid_t nid, unsigned int chs)
 {
+       if (!nid)
+               return 0;
        return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx,
                                 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
 }
@@ -2852,9 +2856,16 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
                             const char *pfx, int cidx,
                             hda_nid_t nid, unsigned int chs)
 {
+       int wid_type;
        int type;
        unsigned long val;
-       if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
+       if (!nid)
+               return 0;
+       wid_type = get_wcaps_type(get_wcaps(codec, nid));
+       if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
+               type = ALC_CTL_WIDGET_MUTE;
+               val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
+       } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
                type = ALC_CTL_WIDGET_MUTE;
                val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
        } else {
@@ -2867,12 +2878,42 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
 #define alc_auto_add_stereo_sw(codec, pfx, cidx, nid)  \
        alc_auto_add_sw_ctl(codec, pfx, cidx, nid, 3)
 
+#define nid_has_mute(codec, nid, dir) \
+       (query_amp_caps(codec, nid, dir) & AC_AMPCAP_MUTE)
+#define nid_has_volume(codec, nid, dir) \
+       (query_amp_caps(codec, nid, dir) & AC_AMPCAP_NUM_STEPS)
+
+static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
+                                          hda_nid_t pin, hda_nid_t dac)
+{
+       hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
+       if (nid_has_mute(codec, pin, HDA_OUTPUT))
+               return pin;
+       else if (mix && nid_has_mute(codec, mix, HDA_INPUT))
+               return mix;
+       else if (nid_has_mute(codec, dac, HDA_OUTPUT))
+               return dac;
+       return 0;
+}
+
+static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
+                                         hda_nid_t pin, hda_nid_t dac)
+{
+       hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
+       if (nid_has_volume(codec, dac, HDA_OUTPUT))
+               return dac;
+       else if (nid_has_volume(codec, mix, HDA_OUTPUT))
+               return mix;
+       else if (nid_has_volume(codec, pin, HDA_OUTPUT))
+               return pin;
+       return 0;
+}
+
 /* add playback controls from the parsed DAC table */
 static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
                                             const struct auto_pin_cfg *cfg)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t nid, mix, pin;
        int i, err, noutputs;
 
        noutputs = cfg->line_outs;
@@ -2882,36 +2923,39 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
        for (i = 0; i < noutputs; i++) {
                const char *name;
                int index;
-               nid = spec->multiout.dac_nids[i];
-               if (!nid)
+               hda_nid_t dac, pin;
+               hda_nid_t sw, vol;
+
+               dac = spec->multiout.dac_nids[i];
+               if (!dac)
                        continue;
                if (i >= cfg->line_outs)
                        pin = spec->multi_io[i - 1].pin;
                else
                        pin = cfg->line_out_pins[i];
-               mix = alc_auto_dac_to_mix(codec, pin, nid);
-               if (!mix)
-                       continue;
+
+               sw = alc_look_for_out_mute_nid(codec, pin, dac);
+               vol = alc_look_for_out_vol_nid(codec, pin, dac);
                name = alc_get_line_out_pfx(spec, i, true, &index);
                if (!name) {
                        /* Center/LFE */
-                       err = alc_auto_add_vol_ctl(codec, "Center", 0, nid, 1);
+                       err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_vol_ctl(codec, "LFE", 0, nid, 2);
+                       err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_sw_ctl(codec, "Center", 0, mix, 1);
+                       err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_sw_ctl(codec, "LFE", 0, mix, 2);
+                       err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2);
                        if (err < 0)
                                return err;
                } else {
-                       err = alc_auto_add_stereo_vol(codec, name, index, nid);
+                       err = alc_auto_add_stereo_vol(codec, name, index, vol);
                        if (err < 0)
                                return err;
-                       err = alc_auto_add_stereo_sw(codec, name, index, mix);
+                       err = alc_auto_add_stereo_sw(codec, name, index, sw);
                        if (err < 0)
                                return err;
                }
@@ -2924,7 +2968,7 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                                        hda_nid_t dac, const char *pfx)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t mix;
+       hda_nid_t sw, vol;
        int err;
 
        if (!pin)
@@ -2938,13 +2982,12 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                                   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
-       mix = alc_auto_dac_to_mix(codec, pin, dac);
-       if (!mix)
-               return 0;
-       err = alc_auto_add_stereo_vol(codec, pfx, 0, dac);
+       sw = alc_look_for_out_mute_nid(codec, pin, dac);
+       vol = alc_look_for_out_vol_nid(codec, pin, dac);
+       err = alc_auto_add_stereo_vol(codec, pfx, 0, vol);
        if (err < 0)
                return err;
-       err = alc_auto_add_stereo_sw(codec, pfx, 0, mix);
+       err = alc_auto_add_stereo_sw(codec, pfx, 0, sw);
        if (err < 0)
                return err;
        return 0;
@@ -2967,15 +3010,15 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec)
 }
 
 static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
-                                             hda_nid_t nid, int pin_type,
+                                             hda_nid_t pin, int pin_type,
                                              hda_nid_t dac)
 {
        int i, num;
-       hda_nid_t mix = 0;
+       hda_nid_t nid, mix = 0;
        hda_nid_t srcs[HDA_MAX_CONNECTIONS];
 
-       alc_set_pin_output(codec, nid, pin_type);
-       nid = alc_go_down_to_selector(codec, nid);
+       alc_set_pin_output(codec, pin, pin_type);
+       nid = alc_go_down_to_selector(codec, pin);
        num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
        for (i = 0; i < num; i++) {
                if (alc_auto_mix_to_dac(codec, srcs[i]) != dac)
@@ -2990,19 +3033,17 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
        if (num > 1)
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
        /* unmute mixer widget inputs */
-       snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+       if (nid_has_mute(codec, mix, HDA_INPUT)) {
+               snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                            AMP_IN_UNMUTE(0));
-       snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+               snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                            AMP_IN_UNMUTE(1));
+       }
        /* initialize volume */
-       if (query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
-               nid = dac;
-       else if (query_amp_caps(codec, mix, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
-               nid = mix;
-       else
-               return;
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-                           AMP_OUT_ZERO);
+       nid = alc_look_for_out_vol_nid(codec, pin, dac);
+       if (nid)
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AMP_OUT_ZERO);
 }
 
 static void alc_auto_init_multi_out(struct hda_codec *codec)
@@ -6333,112 +6374,7 @@ static int patch_alc899(struct hda_codec *codec)
 /*
  * ALC680 support
  */
-/* 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->private_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;
-               int index;
-               name = alc_get_line_out_pfx(spec, 0, true, &index);
-               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);
-}
-
-/*
- * BIOS auto configuration
- */
 static int alc680_parse_auto_config(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -6458,7 +6394,20 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
                }
                return 0; /* can't find valid BIOS pin config */
        }
-       err = alc680_auto_create_multi_out_ctls(spec, &spec->autocfg);
+
+       err = alc_auto_fill_dac_nids(codec);
+       if (err < 0)
+               return err;
+
+       err = alc_auto_create_multi_out_ctls(codec, &spec->autocfg);
+       if (err < 0)
+               return err;
+
+       err = alc_auto_create_hp_out(codec);
+       if (err < 0)
+               return err;
+
+       err = alc_auto_create_speaker_out(codec);
        if (err < 0)
                return err;
 
@@ -6489,8 +6438,8 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
 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);
+       alc_auto_init_multi_out(codec);
+       alc_auto_init_extra_out(codec);
        alc_auto_init_analog_input(codec);
        alc_auto_init_input_src(codec);
        alc_auto_init_digital(codec);