ALSA: hda - Handle shared hp/mic jack mode

When a headphone jack is configured as a shared hp/mic jack, the jack
mode enum needs to handle both input and output directions.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2013-02-19 18:14:54 +01:00
parent 967303dabc
commit 5f171baaa5
1 changed files with 169 additions and 8 deletions

View File

@ -2298,13 +2298,17 @@ static int create_hp_mic(struct hda_codec *codec)
/*
* output jack mode
*/
static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
static const char * const out_jack_texts[] = {
"Line Out", "Headphone Out",
};
static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static const char * const texts[] = {
"Line Out", "Headphone Out",
};
return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts);
return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
}
static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
@ -2366,6 +2370,17 @@ static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
;
}
static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
if (spec->add_out_jack_modes) {
unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
return 2;
}
return 1;
}
static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
hda_nid_t *pins)
{
@ -2374,8 +2389,13 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
for (i = 0; i < num_pins; i++) {
hda_nid_t pin = pins[i];
unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) {
if (pin == spec->hp_mic_pin) {
int ret = create_hp_mic_jack_mode(codec, pin);
if (ret < 0)
return ret;
continue;
}
if (get_out_jack_num_items(codec, pin) > 1) {
struct snd_kcontrol_new *knew;
char name[44];
get_jack_mode_name(codec, pin, name, sizeof(name));
@ -2496,12 +2516,30 @@ static const struct snd_kcontrol_new in_jack_mode_enum = {
.put = in_jack_mode_put,
};
static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
int nitems = 0;
if (spec->add_in_jack_modes)
nitems = hweight32(get_vref_caps(codec, pin));
return nitems ? nitems : 1;
}
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];
unsigned int defcfg;
if (pin == spec->hp_mic_pin) {
if (!spec->add_out_jack_modes) {
int ret = create_hp_mic_jack_mode(codec, pin);
if (ret < 0)
return ret;
}
return 0;
}
/* no jack mode for fixed pins */
defcfg = snd_hda_codec_get_pincfg(codec, pin);
@ -2509,7 +2547,7 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
return 0;
/* no multiple vref caps? */
if (hweight32(get_vref_caps(codec, pin)) <= 1)
if (get_in_jack_num_items(codec, pin) <= 1)
return 0;
get_jack_mode_name(codec, pin, name, sizeof(name));
@ -2520,6 +2558,129 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
return 0;
}
/*
* HP/mic shared jack mode
*/
static int hp_mic_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;
int out_jacks = get_out_jack_num_items(codec, nid);
int in_jacks = get_in_jack_num_items(codec, nid);
const char *text = NULL;
int idx;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = out_jacks + in_jacks;
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
idx = uinfo->value.enumerated.item;
if (idx < out_jacks) {
if (out_jacks > 1)
text = out_jack_texts[idx];
else
text = "Headphone Out";
} else {
idx -= out_jacks;
if (in_jacks > 1) {
unsigned int vref_caps = get_vref_caps(codec, nid);
text = vref_texts[get_vref_idx(vref_caps, idx)];
} else
text = "Mic In";
}
strcpy(uinfo->value.enumerated.name, text);
return 0;
}
static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
{
int out_jacks = get_out_jack_num_items(codec, nid);
int in_jacks = get_in_jack_num_items(codec, nid);
unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
int idx = 0;
if (val & PIN_OUT) {
if (out_jacks > 1 && val == PIN_HP)
idx = 1;
} else if (val & PIN_IN) {
idx = out_jacks;
if (in_jacks > 1) {
unsigned int vref_caps = get_vref_caps(codec, nid);
val &= AC_PINCTL_VREFEN;
idx += cvt_from_vref_idx(vref_caps, val);
}
}
return idx;
}
static int hp_mic_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;
ucontrol->value.enumerated.item[0] =
get_cur_hp_mic_jack_mode(codec, nid);
return 0;
}
static int hp_mic_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;
int out_jacks = get_out_jack_num_items(codec, nid);
int in_jacks = get_in_jack_num_items(codec, nid);
unsigned int val, oldval, idx;
oldval = get_cur_hp_mic_jack_mode(codec, nid);
idx = ucontrol->value.enumerated.item[0];
if (oldval == idx)
return 0;
if (idx < out_jacks) {
if (out_jacks > 1)
val = idx ? PIN_HP : PIN_OUT;
else
val = PIN_HP;
} else {
idx -= out_jacks;
if (in_jacks > 1) {
unsigned int vref_caps = get_vref_caps(codec, nid);
val = snd_hda_codec_get_pin_target(codec, nid);
val &= ~AC_PINCTL_VREFEN;
val |= get_vref_idx(vref_caps, idx);
} else
val = snd_hda_get_default_vref(codec, nid);
}
snd_hda_set_pin_ctl_cache(codec, nid, val);
return 1;
}
static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = hp_mic_jack_mode_info,
.get = hp_mic_jack_mode_get,
.put = hp_mic_jack_mode_put,
};
static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
struct snd_kcontrol_new *knew;
if (get_out_jack_num_items(codec, pin) <= 1 &&
get_in_jack_num_items(codec, pin) <= 1)
return 0; /* no need */
knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
&hp_mic_jack_mode_enum);
if (!knew)
return -ENOMEM;
knew->private_value = pin;
return 0;
}
/*
* Parse input paths