aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-02-19 12:14:54 -0500
committerTakashi Iwai <tiwai@suse.de>2013-03-07 12:30:01 -0500
commit5f171baaa5afb8bb26d09b63d429ccc2cafc6bf7 (patch)
tree06e0ccc0a758dbde9848f45b1d45a5829010d8b2 /sound/pci
parent967303dabc22335e83c6ee4a9e0684a7c05da976 (diff)
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>
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/hda/hda_generic.c177
1 files changed, 169 insertions, 8 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 73de215da03f..dc849e489641 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2298,13 +2298,17 @@ static int create_hp_mic(struct hda_codec *codec)
2298/* 2298/*
2299 * output jack mode 2299 * output jack mode
2300 */ 2300 */
2301
2302static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
2303
2304static const char * const out_jack_texts[] = {
2305 "Line Out", "Headphone Out",
2306};
2307
2301static int out_jack_mode_info(struct snd_kcontrol *kcontrol, 2308static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
2302 struct snd_ctl_elem_info *uinfo) 2309 struct snd_ctl_elem_info *uinfo)
2303{ 2310{
2304 static const char * const texts[] = { 2311 return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
2305 "Line Out", "Headphone Out",
2306 };
2307 return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts);
2308} 2312}
2309 2313
2310static int out_jack_mode_get(struct snd_kcontrol *kcontrol, 2314static 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,
2366 ; 2370 ;
2367} 2371}
2368 2372
2373static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
2374{
2375 struct hda_gen_spec *spec = codec->spec;
2376 if (spec->add_out_jack_modes) {
2377 unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
2378 if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
2379 return 2;
2380 }
2381 return 1;
2382}
2383
2369static int create_out_jack_modes(struct hda_codec *codec, int num_pins, 2384static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
2370 hda_nid_t *pins) 2385 hda_nid_t *pins)
2371{ 2386{
@@ -2374,8 +2389,13 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
2374 2389
2375 for (i = 0; i < num_pins; i++) { 2390 for (i = 0; i < num_pins; i++) {
2376 hda_nid_t pin = pins[i]; 2391 hda_nid_t pin = pins[i];
2377 unsigned int pincap = snd_hda_query_pin_caps(codec, pin); 2392 if (pin == spec->hp_mic_pin) {
2378 if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) { 2393 int ret = create_hp_mic_jack_mode(codec, pin);
2394 if (ret < 0)
2395 return ret;
2396 continue;
2397 }
2398 if (get_out_jack_num_items(codec, pin) > 1) {
2379 struct snd_kcontrol_new *knew; 2399 struct snd_kcontrol_new *knew;
2380 char name[44]; 2400 char name[44];
2381 get_jack_mode_name(codec, pin, name, sizeof(name)); 2401 get_jack_mode_name(codec, pin, name, sizeof(name));
@@ -2496,12 +2516,30 @@ static const struct snd_kcontrol_new in_jack_mode_enum = {
2496 .put = in_jack_mode_put, 2516 .put = in_jack_mode_put,
2497}; 2517};
2498 2518
2519static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
2520{
2521 struct hda_gen_spec *spec = codec->spec;
2522 int nitems = 0;
2523 if (spec->add_in_jack_modes)
2524 nitems = hweight32(get_vref_caps(codec, pin));
2525 return nitems ? nitems : 1;
2526}
2527
2499static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) 2528static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
2500{ 2529{
2501 struct hda_gen_spec *spec = codec->spec; 2530 struct hda_gen_spec *spec = codec->spec;
2502 unsigned int defcfg;
2503 struct snd_kcontrol_new *knew; 2531 struct snd_kcontrol_new *knew;
2504 char name[44]; 2532 char name[44];
2533 unsigned int defcfg;
2534
2535 if (pin == spec->hp_mic_pin) {
2536 if (!spec->add_out_jack_modes) {
2537 int ret = create_hp_mic_jack_mode(codec, pin);
2538 if (ret < 0)
2539 return ret;
2540 }
2541 return 0;
2542 }
2505 2543
2506 /* no jack mode for fixed pins */ 2544 /* no jack mode for fixed pins */
2507 defcfg = snd_hda_codec_get_pincfg(codec, pin); 2545 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)
2509 return 0; 2547 return 0;
2510 2548
2511 /* no multiple vref caps? */ 2549 /* no multiple vref caps? */
2512 if (hweight32(get_vref_caps(codec, pin)) <= 1) 2550 if (get_in_jack_num_items(codec, pin) <= 1)
2513 return 0; 2551 return 0;
2514 2552
2515 get_jack_mode_name(codec, pin, name, sizeof(name)); 2553 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)
2520 return 0; 2558 return 0;
2521} 2559}
2522 2560
2561/*
2562 * HP/mic shared jack mode
2563 */
2564static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
2565 struct snd_ctl_elem_info *uinfo)
2566{
2567 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2568 hda_nid_t nid = kcontrol->private_value;
2569 int out_jacks = get_out_jack_num_items(codec, nid);
2570 int in_jacks = get_in_jack_num_items(codec, nid);
2571 const char *text = NULL;
2572 int idx;
2573
2574 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2575 uinfo->count = 1;
2576 uinfo->value.enumerated.items = out_jacks + in_jacks;
2577 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
2578 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
2579 idx = uinfo->value.enumerated.item;
2580 if (idx < out_jacks) {
2581 if (out_jacks > 1)
2582 text = out_jack_texts[idx];
2583 else
2584 text = "Headphone Out";
2585 } else {
2586 idx -= out_jacks;
2587 if (in_jacks > 1) {
2588 unsigned int vref_caps = get_vref_caps(codec, nid);
2589 text = vref_texts[get_vref_idx(vref_caps, idx)];
2590 } else
2591 text = "Mic In";
2592 }
2593
2594 strcpy(uinfo->value.enumerated.name, text);
2595 return 0;
2596}
2597
2598static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
2599{
2600 int out_jacks = get_out_jack_num_items(codec, nid);
2601 int in_jacks = get_in_jack_num_items(codec, nid);
2602 unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
2603 int idx = 0;
2604
2605 if (val & PIN_OUT) {
2606 if (out_jacks > 1 && val == PIN_HP)
2607 idx = 1;
2608 } else if (val & PIN_IN) {
2609 idx = out_jacks;
2610 if (in_jacks > 1) {
2611 unsigned int vref_caps = get_vref_caps(codec, nid);
2612 val &= AC_PINCTL_VREFEN;
2613 idx += cvt_from_vref_idx(vref_caps, val);
2614 }
2615 }
2616 return idx;
2617}
2618
2619static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol,
2620 struct snd_ctl_elem_value *ucontrol)
2621{
2622 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2623 hda_nid_t nid = kcontrol->private_value;
2624 ucontrol->value.enumerated.item[0] =
2625 get_cur_hp_mic_jack_mode(codec, nid);
2626 return 0;
2627}
2628
2629static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
2630 struct snd_ctl_elem_value *ucontrol)
2631{
2632 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2633 hda_nid_t nid = kcontrol->private_value;
2634 int out_jacks = get_out_jack_num_items(codec, nid);
2635 int in_jacks = get_in_jack_num_items(codec, nid);
2636 unsigned int val, oldval, idx;
2637
2638 oldval = get_cur_hp_mic_jack_mode(codec, nid);
2639 idx = ucontrol->value.enumerated.item[0];
2640 if (oldval == idx)
2641 return 0;
2642
2643 if (idx < out_jacks) {
2644 if (out_jacks > 1)
2645 val = idx ? PIN_HP : PIN_OUT;
2646 else
2647 val = PIN_HP;
2648 } else {
2649 idx -= out_jacks;
2650 if (in_jacks > 1) {
2651 unsigned int vref_caps = get_vref_caps(codec, nid);
2652 val = snd_hda_codec_get_pin_target(codec, nid);
2653 val &= ~AC_PINCTL_VREFEN;
2654 val |= get_vref_idx(vref_caps, idx);
2655 } else
2656 val = snd_hda_get_default_vref(codec, nid);
2657 }
2658 snd_hda_set_pin_ctl_cache(codec, nid, val);
2659 return 1;
2660}
2661
2662static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
2663 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2664 .info = hp_mic_jack_mode_info,
2665 .get = hp_mic_jack_mode_get,
2666 .put = hp_mic_jack_mode_put,
2667};
2668
2669static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
2670{
2671 struct hda_gen_spec *spec = codec->spec;
2672 struct snd_kcontrol_new *knew;
2673
2674 if (get_out_jack_num_items(codec, pin) <= 1 &&
2675 get_in_jack_num_items(codec, pin) <= 1)
2676 return 0; /* no need */
2677 knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
2678 &hp_mic_jack_mode_enum);
2679 if (!knew)
2680 return -ENOMEM;
2681 knew->private_value = pin;
2682 return 0;
2683}
2523 2684
2524/* 2685/*
2525 * Parse input paths 2686 * Parse input paths