Just like the jack mode enum ctls for output jacks, add the support for similar enum ctls for input pins to control the bias Vref. The new controls will be added when spec->add_in_jack_modes is set either by the codec driver or by a hint string.
Note that ground and 100% vrefs are excluded from the list for simplicity, currently. We may add a new flag to allow them, too. But I guess it's easier to put a value override in the pinfix in such a case.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 140 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 141 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1ea9c2d..878556b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -26,6 +26,7 @@ #include <linux/sort.h> #include <linux/ctype.h> #include <linux/string.h> +#include <linux/bitops.h> #include <sound/core.h> #include <sound/jack.h> #include "hda_codec.h" @@ -149,6 +150,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "add_out_jack_modes"); if (val >= 0) spec->add_out_jack_modes = !!val; + val = snd_hda_get_bool_hint(codec, "add_in_jack_modes"); + if (val >= 0) + spec->add_in_jack_modes = !!val;
if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) spec->mixer_nid = val; @@ -2138,6 +2142,136 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins, return 0; }
+/* + * input jack mode + */ + +/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */ +#define NUM_VREFS 6 + +static const char * const vref_texts[NUM_VREFS] = { + "Line In", "Mic 50pc Bias", "Mic 0V Bias", + "", "Mic 80pc Bias", "Mic 100pc Bias" +}; + +static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin) +{ + unsigned int pincap; + + pincap = snd_hda_query_pin_caps(codec, pin); + pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + /* filter out unusual vrefs */ + pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100); + return pincap; +} + +/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */ +static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx) +{ + unsigned int i, n = 0; + + for (i = 0; i < NUM_VREFS; i++) { + if (vref_caps & (1 << i)) { + if (n == item_idx) + return i; + n++; + } + } + return 0; +} + +/* convert back from the vref ctl index to the enum item index */ +static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx) +{ + unsigned int i, n = 0; + + for (i = 0; i < NUM_VREFS; i++) { + if (i == idx) + return n; + if (vref_caps & (1 << i)) + n++; + } + return 0; +} + +static int in_jack_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + + snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps), + vref_texts); + /* set the right text */ + strcpy(uinfo->value.enumerated.name, + vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]); + return 0; +} + +static int in_jack_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + unsigned int idx; + + idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN; + ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx); + return 0; +} + +static int in_jack_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value; + unsigned int vref_caps = get_vref_caps(codec, nid); + unsigned int val, idx; + + val = snd_hda_codec_get_pin_target(codec, nid); + idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN); + if (idx == ucontrol->value.enumerated.item[0]) + return 0; + + val &= ~AC_PINCTL_VREFEN; + val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]); + snd_hda_set_pin_ctl_cache(codec, nid, val); + return 1; +} + +static const struct snd_kcontrol_new in_jack_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = in_jack_mode_info, + .get = in_jack_mode_get, + .put = in_jack_mode_put, +}; + +static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int defcfg; + struct snd_kcontrol_new *knew; + char name[44]; + + /* no jack mode for fixed pins */ + defcfg = snd_hda_codec_get_pincfg(codec, pin); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + return 0; + + /* no multiple vref caps? */ + if (hweight32(get_vref_caps(codec, pin)) <= 1) + return 0; + + get_jack_mode_name(codec, pin, name, sizeof(name)); + knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum); + if (!knew) + return -ENOMEM; + knew->private_value = pin; + return 0; +} +
/* * Parse input paths @@ -2392,6 +2526,12 @@ static int create_input_ctls(struct hda_codec *codec) err = parse_capture_source(codec, pin, num_adcs, label, -mixer); if (err < 0) return err; + + if (spec->add_in_jack_modes) { + err = create_in_jack_mode(codec, pin); + if (err < 0) + return err; + } }
if (mixer && spec->add_stereo_mix_input) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index dd0818b..142a571 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -202,6 +202,7 @@ struct hda_gen_spec { unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */ + unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
/* other internal flags */ unsigned int no_analog:1; /* digital I/O only */