ALSA: hda/realtek - Allow multiple individual capture volume/switch controls

So far we create only "Capture Volume" and "Capture Switch" controls
for binding all possible amps, but we'd prefer creating individual
capture volume and switch controls per input in some cases
(e.g. conexant parser does it).

Add a new flag, spec->multi_cap_vol, to follow that policy.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2012-12-18 17:18:21 +01:00
parent bc54976721
commit 9bf387b612

View File

@ -221,6 +221,7 @@ struct alc_spec {
unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
@ -2474,6 +2475,10 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
spec->num_adc_nids = 1; /* reduce to a single ADC */ spec->num_adc_nids = 1; /* reduce to a single ADC */
} }
/* single index for individual volumes ctls */
if (!spec->dyn_adc_switch && spec->multi_cap_vol)
spec->num_adc_nids = 1;
return 0; return 0;
} }
@ -2545,12 +2550,122 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
return 0; return 0;
} }
static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
int idx, bool is_switch, unsigned int ctl)
{
struct alc_spec *spec = codec->spec;
char tmpname[44];
int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL;
const char *sfx = is_switch ? "Switch" : "Volume";
if (!ctl)
return 0;
if (label)
snprintf(tmpname, sizeof(tmpname),
"%s Capture %s", label, sfx);
else
snprintf(tmpname, sizeof(tmpname),
"Capture %s", sfx);
return add_control(spec, type, tmpname, idx, ctl);
}
/* create single (and simple) capture volume and switch controls */
static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
unsigned int vol_ctl, unsigned int sw_ctl)
{
int err;
err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl);
if (err < 0)
return err;
err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl);
if (err < 0)
return err;
return 0;
}
/* create bound capture volume and switch controls */
static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
unsigned int vol_ctl, unsigned int sw_ctl)
{
struct alc_spec *spec = codec->spec;
struct snd_kcontrol_new *knew;
if (vol_ctl) {
knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp);
if (!knew)
return -ENOMEM;
knew->index = idx;
knew->private_value = vol_ctl;
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
}
if (sw_ctl) {
knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp);
if (!knew)
return -ENOMEM;
knew->index = idx;
knew->private_value = sw_ctl;
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
}
return 0;
}
/* return the vol ctl when used first in the imux list */
static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
{
struct alc_spec *spec = codec->spec;
struct nid_path *path;
unsigned int ctl;
int i;
path = get_nid_path(codec, spec->imux_pins[idx],
get_adc_nid(codec, 0, idx));
if (!path)
return 0;
ctl = path->ctls[type];
if (!ctl)
return 0;
for (i = 0; i < idx - 1; i++) {
path = get_nid_path(codec, spec->imux_pins[i],
get_adc_nid(codec, 0, i));
if (path && path->ctls[type] == ctl)
return 0;
}
return ctl;
}
/* create individual capture volume and switch controls per input */
static int create_multi_cap_vol_ctl(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->input_mux;
int i, err, type, type_idx = 0;
const char *prev_label = NULL;
for (i = 0; i < imux->num_items; i++) {
const char *label;
label = hda_get_autocfg_input_label(codec, &spec->autocfg, i);
if (prev_label && !strcmp(label, prev_label))
type_idx++;
else
type_idx = 0;
prev_label = label;
for (type = 0; type < 2; type++) {
err = add_single_cap_ctl(codec, label, type_idx, type,
get_first_cap_ctl(codec, i, type));
if (err < 0)
return err;
}
}
return 0;
}
static int create_capture_mixers(struct hda_codec *codec) static int create_capture_mixers(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->input_mux; struct hda_input_mux *imux = &spec->input_mux;
struct snd_kcontrol_new *knew; int i, n, nums, err;
int i, n, nums;
if (spec->dyn_adc_switch) if (spec->dyn_adc_switch)
nums = 1; nums = 1;
@ -2558,6 +2673,7 @@ static int create_capture_mixers(struct hda_codec *codec)
nums = spec->num_adc_nids; nums = spec->num_adc_nids;
if (!spec->auto_mic && imux->num_items > 1) { if (!spec->auto_mic && imux->num_items > 1) {
struct snd_kcontrol_new *knew;
knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); knew = alc_kcontrol_new(spec, NULL, &cap_src_temp);
if (!knew) if (!knew)
return -ENOMEM; return -ENOMEM;
@ -2565,6 +2681,7 @@ static int create_capture_mixers(struct hda_codec *codec)
} }
for (n = 0; n < nums; n++) { for (n = 0; n < nums; n++) {
bool multi = false;
int vol, sw; int vol, sw;
vol = sw = 0; vol = sw = 0;
@ -2577,26 +2694,22 @@ static int create_capture_mixers(struct hda_codec *codec)
parse_capvol_in_path(codec, path); parse_capvol_in_path(codec, path);
if (!vol) if (!vol)
vol = path->ctls[NID_PATH_VOL_CTL]; vol = path->ctls[NID_PATH_VOL_CTL];
else if (vol != path->ctls[NID_PATH_VOL_CTL])
multi = true;
if (!sw) if (!sw)
sw = path->ctls[NID_PATH_MUTE_CTL]; sw = path->ctls[NID_PATH_MUTE_CTL];
else if (sw != path->ctls[NID_PATH_MUTE_CTL])
multi = true;
} }
if (vol) { if (!multi)
knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); err = create_single_cap_vol_ctl(codec, n, vol, sw);
if (!knew) else if (!spec->multi_cap_vol)
return -ENOMEM; err = create_bind_cap_vol_ctl(codec, n, vol, sw);
knew->index = n; else
knew->private_value = vol; err = create_multi_cap_vol_ctl(codec);
knew->subdevice = HDA_SUBDEV_AMP_FLAG; if (err < 0)
} return err;
if (sw) {
knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp);
if (!knew)
return -ENOMEM;
knew->index = n;
knew->private_value = sw;
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
}
} }
return 0; return 0;