dect
/
linux-2.6
Archived
13
0
Fork 0

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>
This commit is contained in:
Takashi Iwai 2011-06-21 15:57:44 +02:00
parent a934d5a983
commit 09a9ad69a5
1 changed files with 122 additions and 100 deletions

View File

@ -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;
}