Commit 792cf2fa authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec

So far, Realtek codec driver evaluates the NIDs for volume and mute
controls twice, once while parsing the DACs and evaluating the
assignment, and another while creating the mixer elements.  This is
utterly redundant and even fragile, as it's assuming that the ctl
element evaluation is identical between both parsing DACs and creating
mixer elements.

This patch simplifies the code flow by doing the volume / mute
controls evaluation only once while parsing the DACs.  The patch ended
up in larger changes than expected because of some cleanups became
mandatory.

As a gratis bonus, this patch also fixes some cases where the stereo
channels are used wrongly for mono amps.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 2f179721
...@@ -3046,17 +3046,6 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) ...@@ -3046,17 +3046,6 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
return false; return false;
} }
static void clear_vol_marks(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
int i;
for (i = 0; i < spec->out_path.used; i++) {
struct nid_path *path = snd_array_elem(&spec->out_path, i);
path->ctls[0] = path->ctls[1] = 0;
}
}
/* badness definition */ /* badness definition */
enum { enum {
/* No primary DAC is found for the main output */ /* No primary DAC is found for the main output */
...@@ -3121,8 +3110,15 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, ...@@ -3121,8 +3110,15 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin,
return NULL; return NULL;
} }
static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, /* look for widgets in the path between the given NIDs appropriate for
hda_nid_t dac) * volume and mute controls, and assign the values to ctls[].
*
* When no appropriate widget is found in the path, the badness value
* is incremented depending on the situation. The function returns the
* total badness for both volume and mute controls.
*/
static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t dac)
{ {
struct nid_path *path = get_out_path(codec, pin, dac); struct nid_path *path = get_out_path(codec, pin, dac);
hda_nid_t nid; hda_nid_t nid;
...@@ -3143,7 +3139,8 @@ static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, ...@@ -3143,7 +3139,8 @@ static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
nid = alc_look_for_out_mute_nid(codec, path); nid = alc_look_for_out_mute_nid(codec, path);
if (nid) { if (nid) {
unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
nid_has_mute(codec, nid, HDA_OUTPUT))
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
else else
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
...@@ -3237,7 +3234,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, ...@@ -3237,7 +3234,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
if (!add_new_out_path(codec, pin, dac)) if (!add_new_out_path(codec, pin, dac))
dac = dacs[i] = 0; dac = dacs[i] = 0;
if (dac) if (dac)
badness += eval_shared_vol_badness(codec, pin, dac); badness += assign_out_path_ctls(codec, pin, dac);
} }
return badness; return badness;
...@@ -3533,36 +3530,52 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) ...@@ -3533,36 +3530,52 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path);
} }
/* clear the bitmap flags for creating controls */
clear_vol_marks(codec);
kfree(best_cfg); kfree(best_cfg);
return 0; return 0;
} }
/* replace the channels in the composed amp value with the given number */
static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
{
val &= ~(0x3U << 16);
val |= chs << 16;
return val;
}
static int alc_auto_add_vol_ctl(struct hda_codec *codec, static int alc_auto_add_vol_ctl(struct hda_codec *codec,
const char *pfx, int cidx, const char *pfx, int cidx,
hda_nid_t nid, unsigned int chs, unsigned int chs,
struct nid_path *path) struct nid_path *path)
{ {
unsigned int val; unsigned int val;
if (!nid || !path) if (!path)
return 0; return 0;
val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); val = path->ctls[NID_PATH_VOL_CTL];
if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */ if (!val)
return 0; return 0;
path->ctls[NID_PATH_VOL_CTL] = val; val = amp_val_replace_channels(val, chs);
return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val);
val); }
/* return the channel bits suitable for the given path->ctls[] */
static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
int type)
{
int chs = 1; /* mono (left only) */
if (path) {
hda_nid_t nid = get_amp_nid_(path->ctls[type]);
if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
chs = 3; /* stereo */
}
return chs;
} }
static int alc_auto_add_stereo_vol(struct hda_codec *codec, static int alc_auto_add_stereo_vol(struct hda_codec *codec,
const char *pfx, int cidx, const char *pfx, int cidx,
hda_nid_t nid, struct nid_path *path) struct nid_path *path)
{ {
int chs = 1; int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
if (get_wcaps(codec, nid) & AC_WCAP_STEREO) return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path);
chs = 3;
return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path);
} }
/* create a mute-switch for the given mixer widget; /* create a mute-switch for the given mixer widget;
...@@ -3570,39 +3583,33 @@ static int alc_auto_add_stereo_vol(struct hda_codec *codec, ...@@ -3570,39 +3583,33 @@ static int alc_auto_add_stereo_vol(struct hda_codec *codec,
*/ */
static int alc_auto_add_sw_ctl(struct hda_codec *codec, static int alc_auto_add_sw_ctl(struct hda_codec *codec,
const char *pfx, int cidx, const char *pfx, int cidx,
hda_nid_t nid, unsigned int chs, unsigned int chs,
struct nid_path *path) struct nid_path *path)
{ {
int wid_type; unsigned int val;
int type; int type = ALC_CTL_WIDGET_MUTE;
unsigned long val;
if (!nid || !path) if (!path)
return 0; return 0;
wid_type = get_wcaps_type(get_wcaps(codec, nid)); val = path->ctls[NID_PATH_MUTE_CTL];
if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { if (!val)
type = ALC_CTL_WIDGET_MUTE;
val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
} else if (snd_hda_get_num_conns(codec, nid) == 1) {
type = ALC_CTL_WIDGET_MUTE;
val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
} else {
type = ALC_CTL_BIND_MUTE;
val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT);
}
if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */
return 0; return 0;
path->ctls[NID_PATH_MUTE_CTL] = val; val = amp_val_replace_channels(val, chs);
if (get_amp_direction_(val) == HDA_INPUT) {
hda_nid_t nid = get_amp_nid_(val);
if (snd_hda_get_num_conns(codec, nid) > 1) {
type = ALC_CTL_BIND_MUTE;
val |= 2 << 19; /* FIXME: fixed two widgets, so far */
}
}
return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
} }
static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
int cidx, hda_nid_t nid, int cidx, struct nid_path *path)
struct nid_path *path)
{ {
int chs = 1; int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
if (get_wcaps(codec, nid) & AC_WCAP_STEREO) return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path);
chs = 3;
return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path);
} }
static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
...@@ -3647,7 +3654,6 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, ...@@ -3647,7 +3654,6 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
const char *name; const char *name;
int index; int index;
hda_nid_t dac, pin; hda_nid_t dac, pin;
hda_nid_t sw, vol;
struct nid_path *path; struct nid_path *path;
dac = spec->multiout.dac_nids[i]; dac = spec->multiout.dac_nids[i];
...@@ -3665,33 +3671,25 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, ...@@ -3665,33 +3671,25 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
path = get_out_path(codec, pin, dac); path = get_out_path(codec, pin, dac);
if (!path) if (!path)
continue; continue;
sw = alc_look_for_out_mute_nid(codec, path);
vol = alc_look_for_out_vol_nid(codec, path);
if (!name || !strcmp(name, "CLFE")) { if (!name || !strcmp(name, "CLFE")) {
/* Center/LFE */ /* Center/LFE */
err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1, err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path);
path);
if (err < 0) if (err < 0)
return err; return err;
err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2, err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path);
path);
if (err < 0) if (err < 0)
return err; return err;
err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1, err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path);
path);
if (err < 0) if (err < 0)
return err; return err;
err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2, err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path);
path);
if (err < 0) if (err < 0)
return err; return err;
} else { } else {
err = alc_auto_add_stereo_vol(codec, name, index, vol, err = alc_auto_add_stereo_vol(codec, name, index, path);
path);
if (err < 0) if (err < 0)
return err; return err;
err = alc_auto_add_stereo_sw(codec, name, index, sw, err = alc_auto_add_stereo_sw(codec, name, index, path);
path);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -3703,34 +3701,19 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, ...@@ -3703,34 +3701,19 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
hda_nid_t dac, const char *pfx, hda_nid_t dac, const char *pfx,
int cidx) int cidx)
{ {
struct alc_spec *spec = codec->spec;
struct nid_path *path; struct nid_path *path;
hda_nid_t sw, vol;
int err; int err;
path = get_out_path(codec, pin, dac); path = get_out_path(codec, pin, dac);
if (!path) if (!path)
return 0; return 0;
/* bind volume control will be created in the case of dac = 0 */
if (!dac) { if (dac) {
unsigned int val; err = alc_auto_add_stereo_vol(codec, pfx, cidx, path);
/* the corresponding DAC is already occupied */ if (err < 0)
if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) return err;
return 0; /* no way */
/* create a switch only */
val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT);
if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
return 0; /* already created */
path->ctls[NID_PATH_MUTE_CTL] = val;
return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val);
} }
err = alc_auto_add_stereo_sw(codec, pfx, cidx, path);
sw = alc_look_for_out_mute_nid(codec, path);
vol = alc_look_for_out_vol_nid(codec, path);
err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path);
if (err < 0)
return err;
err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path);
if (err < 0) if (err < 0)
return err; return err;
return 0; return 0;
...@@ -4051,7 +4034,12 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, ...@@ -4051,7 +4034,12 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
return badness; return badness;
} }
return 0; /* assign volume and mute controls */
for (i = old_pins; i < spec->multi_ios; i++)
badness += assign_out_path_ctls(codec, spec->multi_io[i].pin,
spec->multi_io[i].dac);
return badness;
} }
static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
......
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