diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-06-21 17:57:44 +0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-06-21 18:02:32 +0400 |
commit | 09a9ad69a5467fbda3fd358d2be155c22aa416e4 (patch) | |
tree | 336c02b4a218897b40413a92cc09c26ac2599de7 /sound/pci/hda/patch_via.c | |
parent | a934d5a983528543850c90b29bedbdfd71f7097b (diff) | |
download | linux-09a9ad69a5467fbda3fd358d2be155c22aa416e4.tar.xz |
ALSA: hda - VT1708 independent HP routing fix
The codecs like VT1708 needs more complicated routing using the mixer
widget rather than the simple selector widgets.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_via.c')
-rw-r--r-- | sound/pci/hda/patch_via.c | 222 |
1 files changed, 122 insertions, 100 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5b907b356951..bceb6b2364fe 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -83,10 +83,20 @@ enum VIA_HDA_CODEC { #define MAX_NID_PATH_DEPTH 5 +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ struct nid_path { int depth; hda_nid_t path[MAX_NID_PATH_DEPTH]; - short idx[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int vol_ctl; + unsigned int mute_ctl; }; struct via_spec { @@ -422,43 +432,39 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, return false; } -#define have_vol_or_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE) +#define have_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -/* unmute input amp and select the specificed source */ -static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t src, hda_nid_t mix) +/* enable/disable the output-route */ +static void activate_output_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool force) { - int idx, num_conns; - - idx = __get_connection_index(codec, nid, src, &num_conns); - if (idx < 0) - return; - - /* select the route explicitly when multiple connections exist */ - if (num_conns > 1 && - get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, idx); - - /* unmute if the input amp is present */ - if (have_vol_or_mute(codec, nid, HDA_INPUT)) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(idx)); - - /* unmute the src output */ - if (have_vol_or_mute(codec, src, HDA_OUTPUT)) - snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - - /* unmute AA-path if present */ - if (!mix || mix == src) - return; - idx = __get_connection_index(codec, nid, mix, NULL); - if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(idx)); + int i; + for (i = 0; i < path->depth; i++) { + hda_nid_t src, dst; + int idx = path->idx[i]; + src = path->path[i]; + if (i < path->depth - 1) + dst = path->path[i + 1]; + else + dst = 0; + if (enable && path->multi[i]) + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_CONNECT_SEL, idx); + if (have_mute(codec, dst, HDA_INPUT)) { + int val = enable ? AMP_IN_UNMUTE(idx) : + AMP_IN_MUTE(idx); + snd_hda_codec_write(codec, dst, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + if (!force && (src == path->vol_ctl || src == path->mute_ctl)) + continue; + if (have_mute(codec, src, HDA_OUTPUT)) { + int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; + snd_hda_codec_write(codec, src, 0, + AC_VERB_SET_AMP_GAIN_MUTE, val); + } + } } /* set the given pin as output */ @@ -474,16 +480,18 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, AC_VERB_SET_EAPD_BTLENABLE, 0x02); } -static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, - int pin_type, struct nid_path *path) +static void via_auto_init_output(struct hda_codec *codec, + struct nid_path *path, int pin_type, + bool force) { struct via_spec *spec = codec->spec; unsigned int caps; - hda_nid_t nid; - int i; + hda_nid_t pin, nid; + int i, idx; - if (!pin) + if (!path->depth) return; + pin = path->path[path->depth - 1]; init_output_pin(codec, pin, pin_type); caps = query_amp_caps(codec, pin, HDA_OUTPUT); @@ -494,34 +502,48 @@ static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, AMP_OUT_MUTE | val); } - /* initialize the output path */ + activate_output_path(codec, path, true, force); + + /* initialize the AA-path */ + if (!spec->aa_mix_nid) + return; for (i = path->depth - 1; i > 0; i--) { - nid = path->path[i - 1]; - unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid); + nid = path->path[i]; + idx = get_connection_index(codec, nid, spec->aa_mix_nid); + if (idx >= 0) { + if (have_mute(codec, nid, HDA_INPUT)) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(idx)); + break; + } } } - static void via_auto_init_multi_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; int i; for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) - via_auto_init_output(codec, spec->autocfg.line_out_pins[i], - PIN_OUT, &spec->out_path[i]); + via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true); } static void via_auto_init_hp_out(struct hda_codec *codec) { struct via_spec *spec = codec->spec; - if (spec->hp_dac_nid) - via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, - &spec->hp_path); - else - via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, - &spec->hp_dep_path); + if (!spec->hp_dac_nid) { + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); + return; + } + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_dep_path, false, false); + via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); + } else { + activate_output_path(codec, &spec->hp_path, false, false); + via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); + } } static void via_auto_init_speaker_out(struct hda_codec *codec) @@ -529,8 +551,7 @@ static void via_auto_init_speaker_out(struct hda_codec *codec) struct via_spec *spec = codec->spec; if (spec->autocfg.speaker_outs) - via_auto_init_output(codec, spec->autocfg.speaker_pins[0], - PIN_OUT, &spec->speaker_path); + via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true); } static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); @@ -738,27 +759,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - hda_nid_t nid, src; - int i, idx, num_conns; - struct nid_path *path; spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0]; - if (spec->hp_independent_mode) - path = &spec->hp_path; - else - path = &spec->hp_dep_path; - - /* re-route the output path */ - for (i = path->depth - 1; i > 0; i--) { - nid = path->path[i]; - src = path->path[i - 1]; - idx = __get_connection_index(codec, nid, src, &num_conns); - if (idx < 0) - continue; - if (num_conns > 1 && - get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, idx); + if (spec->hp_independent_mode) { + activate_output_path(codec, &spec->hp_dep_path, false, false); + activate_output_path(codec, &spec->hp_path, true, false); + } else { + activate_output_path(codec, &spec->hp_path, false, false); + activate_output_path(codec, &spec->hp_dep_path, true, false); } /* update jack power state */ @@ -1577,12 +1585,8 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, for (i = 0; i < nums; i++) { if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) continue; - if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { - path->path[0] = conn[i]; - path->idx[0] = i; - path->depth = 1; - return true; - } + if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) + goto found; } if (depth >= MAX_NID_PATH_DEPTH) return false; @@ -1593,14 +1597,18 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, (wid_type != -1 && type != wid_type)) continue; if (__parse_output_path(codec, conn[i], target_dac, - path, depth + 1, AC_WID_AUD_SEL)) { - path->path[path->depth] = conn[i]; - path->idx[path->depth] = i; - path->depth++; - return true; - } + path, depth + 1, AC_WID_AUD_SEL)) + goto found; } return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; } static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, @@ -1634,18 +1642,16 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec) } static int create_ch_ctls(struct hda_codec *codec, const char *pfx, - hda_nid_t pin, hda_nid_t dac, int chs) + int chs, bool check_dac, struct nid_path *path) { struct via_spec *spec = codec->spec; char name[32]; - hda_nid_t nid, sel, conn[8]; - int nums, err; + hda_nid_t dac, pin, sel, nid; + int err; - /* check selector widget connected to the pin */ - sel = 0; - nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn)); - if (nums == 1 && conn[0] != pin) - sel = conn[0]; + dac = check_dac ? path->path[0] : 0; + pin = path->path[path->depth - 1]; + sel = path->depth > 1 ? path->path[1] : 0; if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) nid = dac; @@ -1661,6 +1667,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; + path->vol_ctl = nid; } if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) @@ -1677,6 +1684,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); if (err < 0) return err; + path->mute_ctl = nid; } return 0; } @@ -1747,10 +1755,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (!pin || !dac) continue; if (i == HDA_CLFE) { - err = create_ch_ctls(codec, "Center", pin, dac, 1); + err = create_ch_ctls(codec, "Center", 1, true, + &spec->out_path[i]); if (err < 0) return err; - err = create_ch_ctls(codec, "LFE", pin, dac, 2); + err = create_ch_ctls(codec, "LFE", 2, true, + &spec->out_path[i]); if (err < 0) return err; } else { @@ -1758,7 +1768,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->line_outs == 1) pfx = "Speaker"; - err = create_ch_ctls(codec, pfx, pin, dac, 3); + err = create_ch_ctls(codec, pfx, 3, true, + &spec->out_path[i]); if (err < 0) return err; } @@ -1790,6 +1801,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) { struct via_spec *spec = codec->spec; + struct nid_path *path; int err; if (!pin) @@ -1803,9 +1815,17 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) !spec->hp_dac_nid) return 0; - err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); + if (spec->hp_dac_nid) + path = &spec->hp_path; + else + path = &spec->hp_dep_path; + err = create_ch_ctls(codec, "Headphone", 3, false, path); if (err < 0) return err; + if (spec->hp_dac_nid) { + spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl; + spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl; + } return 0; } @@ -1822,11 +1842,13 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec) if (parse_output_path(codec, pin, 0, &spec->speaker_path)) { dac = spec->speaker_path.path[0]; spec->multiout.extra_out_nid[0] = dac; - return create_ch_ctls(codec, "Speaker", pin, dac, 3); + return create_ch_ctls(codec, "Speaker", 3, true, + &spec->speaker_path); } if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], &spec->speaker_path)) - return create_ch_ctls(codec, "Speaker", pin, 0, 3); + return create_ch_ctls(codec, "Speaker", 3, false, + &spec->speaker_path); return 0; } |