Commit 09a9ad69 authored by Takashi Iwai's avatar Takashi Iwai

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: default avatarTakashi Iwai <tiwai@suse.de>
parent a934d5a9
...@@ -83,10 +83,20 @@ enum VIA_HDA_CODEC { ...@@ -83,10 +83,20 @@ enum VIA_HDA_CODEC {
#define MAX_NID_PATH_DEPTH 5 #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 { struct nid_path {
int depth; int depth;
hda_nid_t path[MAX_NID_PATH_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 { struct via_spec {
...@@ -422,43 +432,39 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, ...@@ -422,43 +432,39 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
return false; return false;
} }
#define have_vol_or_mute(codec, nid, dir) \ #define have_mute(codec, nid, dir) \
check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE) check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
/* unmute input amp and select the specificed source */ /* enable/disable the output-route */
static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
hda_nid_t src, hda_nid_t mix) bool enable, bool force)
{ {
int idx, num_conns; int i;
for (i = 0; i < path->depth; i++) {
idx = __get_connection_index(codec, nid, src, &num_conns); hda_nid_t src, dst;
if (idx < 0) int idx = path->idx[i];
return; src = path->path[i];
if (i < path->depth - 1)
/* select the route explicitly when multiple connections exist */ dst = path->path[i + 1];
if (num_conns > 1 && else
get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) dst = 0;
snd_hda_codec_write(codec, nid, 0, if (enable && path->multi[i])
AC_VERB_SET_CONNECT_SEL, idx); snd_hda_codec_write(codec, dst, 0,
AC_VERB_SET_CONNECT_SEL, idx);
/* unmute if the input amp is present */ if (have_mute(codec, dst, HDA_INPUT)) {
if (have_vol_or_mute(codec, nid, HDA_INPUT)) int val = enable ? AMP_IN_UNMUTE(idx) :
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(idx);
AMP_IN_UNMUTE(idx)); snd_hda_codec_write(codec, dst, 0,
AC_VERB_SET_AMP_GAIN_MUTE, val);
/* unmute the src output */ }
if (have_vol_or_mute(codec, src, HDA_OUTPUT)) if (!force && (src == path->vol_ctl || src == path->mute_ctl))
snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, continue;
AMP_OUT_UNMUTE); if (have_mute(codec, src, HDA_OUTPUT)) {
int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
/* unmute AA-path if present */ snd_hda_codec_write(codec, src, 0,
if (!mix || mix == src) AC_VERB_SET_AMP_GAIN_MUTE, val);
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));
} }
/* set the given pin as output */ /* set the given pin as output */
...@@ -474,16 +480,18 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, ...@@ -474,16 +480,18 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
AC_VERB_SET_EAPD_BTLENABLE, 0x02); AC_VERB_SET_EAPD_BTLENABLE, 0x02);
} }
static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, static void via_auto_init_output(struct hda_codec *codec,
int pin_type, struct nid_path *path) struct nid_path *path, int pin_type,
bool force)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
unsigned int caps; unsigned int caps;
hda_nid_t nid; hda_nid_t pin, nid;
int i; int i, idx;
if (!pin) if (!path->depth)
return; return;
pin = path->path[path->depth - 1];
init_output_pin(codec, pin, pin_type); init_output_pin(codec, pin, pin_type);
caps = query_amp_caps(codec, pin, HDA_OUTPUT); 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, ...@@ -494,34 +502,48 @@ static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
AMP_OUT_MUTE | val); 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--) { for (i = path->depth - 1; i > 0; i--) {
nid = path->path[i - 1]; nid = path->path[i];
unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid); 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) static void via_auto_init_multi_out(struct hda_codec *codec)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
int i; int i;
for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
via_auto_init_output(codec, spec->autocfg.line_out_pins[i], via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
PIN_OUT, &spec->out_path[i]);
} }
static void via_auto_init_hp_out(struct hda_codec *codec) static void via_auto_init_hp_out(struct hda_codec *codec)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
if (spec->hp_dac_nid) if (!spec->hp_dac_nid) {
via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
&spec->hp_path); return;
else }
via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, if (spec->hp_independent_mode) {
&spec->hp_dep_path); 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) 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) ...@@ -529,8 +551,7 @@ static void via_auto_init_speaker_out(struct hda_codec *codec)
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
if (spec->autocfg.speaker_outs) if (spec->autocfg.speaker_outs)
via_auto_init_output(codec, spec->autocfg.speaker_pins[0], via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
PIN_OUT, &spec->speaker_path);
} }
static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 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, ...@@ -738,27 +759,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec; 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]; spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
if (spec->hp_independent_mode) if (spec->hp_independent_mode) {
path = &spec->hp_path; activate_output_path(codec, &spec->hp_dep_path, false, false);
else activate_output_path(codec, &spec->hp_path, true, false);
path = &spec->hp_dep_path; } else {
activate_output_path(codec, &spec->hp_path, false, false);
/* re-route the output path */ activate_output_path(codec, &spec->hp_dep_path, true, false);
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);
} }
/* update jack power state */ /* update jack power state */
...@@ -1577,12 +1585,8 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, ...@@ -1577,12 +1585,8 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
for (i = 0; i < nums; i++) { for (i = 0; i < nums; i++) {
if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
continue; continue;
if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
path->path[0] = conn[i]; goto found;
path->idx[0] = i;
path->depth = 1;
return true;
}
} }
if (depth >= MAX_NID_PATH_DEPTH) if (depth >= MAX_NID_PATH_DEPTH)
return false; return false;
...@@ -1593,14 +1597,18 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, ...@@ -1593,14 +1597,18 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
(wid_type != -1 && type != wid_type)) (wid_type != -1 && type != wid_type))
continue; continue;
if (__parse_output_path(codec, conn[i], target_dac, if (__parse_output_path(codec, conn[i], target_dac,
path, depth + 1, AC_WID_AUD_SEL)) { path, depth + 1, AC_WID_AUD_SEL))
path->path[path->depth] = conn[i]; goto found;
path->idx[path->depth] = i;
path->depth++;
return true;
}
} }
return false; 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, 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) ...@@ -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, 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; struct via_spec *spec = codec->spec;
char name[32]; char name[32];
hda_nid_t nid, sel, conn[8]; hda_nid_t dac, pin, sel, nid;
int nums, err; int err;
/* check selector widget connected to the pin */ dac = check_dac ? path->path[0] : 0;
sel = 0; pin = path->path[path->depth - 1];
nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn)); sel = path->depth > 1 ? path->path[1] : 0;
if (nums == 1 && conn[0] != pin)
sel = conn[0];
if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
nid = dac; nid = dac;
...@@ -1661,6 +1667,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, ...@@ -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)); HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
if (err < 0) if (err < 0)
return err; return err;
path->vol_ctl = nid;
} }
if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) 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, ...@@ -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)); HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
if (err < 0) if (err < 0)
return err; return err;
path->mute_ctl = nid;
} }
return 0; return 0;
} }
...@@ -1747,10 +1755,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) ...@@ -1747,10 +1755,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
if (!pin || !dac) if (!pin || !dac)
continue; continue;
if (i == HDA_CLFE) { 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) if (err < 0)
return err; 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) if (err < 0)
return err; return err;
} else { } else {
...@@ -1758,7 +1768,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) ...@@ -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 && if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
cfg->line_outs == 1) cfg->line_outs == 1)
pfx = "Speaker"; 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) if (err < 0)
return err; return err;
} }
...@@ -1790,6 +1801,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) ...@@ -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) static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
{ {
struct via_spec *spec = codec->spec; struct via_spec *spec = codec->spec;
struct nid_path *path;
int err; int err;
if (!pin) if (!pin)
...@@ -1803,9 +1815,17 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) ...@@ -1803,9 +1815,17 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
!spec->hp_dac_nid) !spec->hp_dac_nid)
return 0; 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) if (err < 0)
return err; 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; return 0;
} }
...@@ -1822,11 +1842,13 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec) ...@@ -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)) { if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
dac = spec->speaker_path.path[0]; dac = spec->speaker_path.path[0];
spec->multiout.extra_out_nid[0] = dac; 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], if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
&spec->speaker_path)) &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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment