diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-02-19 12:14:54 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-03-07 12:30:01 -0500 |
commit | 5f171baaa5afb8bb26d09b63d429ccc2cafc6bf7 (patch) | |
tree | 06e0ccc0a758dbde9848f45b1d45a5829010d8b2 /sound/pci | |
parent | 967303dabc22335e83c6ee4a9e0684a7c05da976 (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.c | 177 |
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 | |||
2302 | static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin); | ||
2303 | |||
2304 | static const char * const out_jack_texts[] = { | ||
2305 | "Line Out", "Headphone Out", | ||
2306 | }; | ||
2307 | |||
2301 | static int out_jack_mode_info(struct snd_kcontrol *kcontrol, | 2308 | static 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 | ||
2310 | static int out_jack_mode_get(struct snd_kcontrol *kcontrol, | 2314 | 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, | |||
2366 | ; | 2370 | ; |
2367 | } | 2371 | } |
2368 | 2372 | ||
2373 | static 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 | |||
2369 | static int create_out_jack_modes(struct hda_codec *codec, int num_pins, | 2384 | static 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 | ||
2519 | static 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 | |||
2499 | static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) | 2528 | static 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 | */ | ||
2564 | static 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 | |||
2598 | static 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 | |||
2619 | static 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 | |||
2629 | static 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 | |||
2662 | static 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 | |||
2669 | static 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 |