Skip to content

Commit

Permalink
ALSA: hda - Add input jack mode enum controls to generic parser
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takashi Iwai committed Jan 17, 2013
1 parent acc47aa commit 2947655
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 0 deletions.
140 changes: 140 additions & 0 deletions sound/pci/hda/hda_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions sound/pci/hda/hda_generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit 2947655

Please sign in to comment.