diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-06-21 05:48:29 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-06-21 06:20:13 -0400 |
commit | 8df2a3129d946dc91f9824958567a990329822b3 (patch) | |
tree | b2c6b7002bffa7c0cc9361bf8c7ed8babcd5919a /sound | |
parent | a00a5fad9ddbabc7cd03d143520b9a4730edc75d (diff) |
ALSA: hda - Fix re-routing of HP-independent mode in patch_via.c
Re-route the whole output path when HP-independent mode is changed.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/patch_via.c | 101 |
1 files changed, 60 insertions, 41 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 853d24411d53..7b353405e068 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c | |||
@@ -138,9 +138,7 @@ struct via_spec { | |||
138 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | 138 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; |
139 | 139 | ||
140 | /* HP mode source */ | 140 | /* HP mode source */ |
141 | const struct hda_input_mux *hp_mux; | ||
142 | unsigned int hp_independent_mode; | 141 | unsigned int hp_independent_mode; |
143 | unsigned int hp_independent_mode_index; | ||
144 | unsigned int dmic_enabled; | 142 | unsigned int dmic_enabled; |
145 | unsigned int no_pin_power_ctl; | 143 | unsigned int no_pin_power_ctl; |
146 | enum VIA_HDA_CODEC codec_type; | 144 | enum VIA_HDA_CODEC codec_type; |
@@ -406,6 +404,24 @@ static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, | |||
406 | #define get_connection_index(codec, mux, nid) \ | 404 | #define get_connection_index(codec, mux, nid) \ |
407 | __get_connection_index(codec, mux, nid, NULL) | 405 | __get_connection_index(codec, mux, nid, NULL) |
408 | 406 | ||
407 | static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, | ||
408 | unsigned int mask) | ||
409 | { | ||
410 | unsigned int caps = get_wcaps(codec, nid); | ||
411 | if (dir == HDA_INPUT) | ||
412 | caps &= AC_WCAP_IN_AMP; | ||
413 | else | ||
414 | caps &= AC_WCAP_OUT_AMP; | ||
415 | if (!caps) | ||
416 | return false; | ||
417 | if (query_amp_caps(codec, nid, dir) & mask) | ||
418 | return true; | ||
419 | return false; | ||
420 | } | ||
421 | |||
422 | #define have_vol_or_mute(codec, nid, dir) \ | ||
423 | check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE) | ||
424 | |||
409 | /* unmute input amp and select the specificed source */ | 425 | /* unmute input amp and select the specificed source */ |
410 | static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, | 426 | static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, |
411 | hda_nid_t src, hda_nid_t mix) | 427 | hda_nid_t src, hda_nid_t mix) |
@@ -423,22 +439,20 @@ static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, | |||
423 | AC_VERB_SET_CONNECT_SEL, idx); | 439 | AC_VERB_SET_CONNECT_SEL, idx); |
424 | 440 | ||
425 | /* unmute if the input amp is present */ | 441 | /* unmute if the input amp is present */ |
426 | if (query_amp_caps(codec, nid, HDA_INPUT) & | 442 | if (have_vol_or_mute(codec, nid, HDA_INPUT)) |
427 | (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) | ||
428 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, | 443 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, |
429 | AMP_IN_UNMUTE(idx)); | 444 | AMP_IN_UNMUTE(idx)); |
430 | 445 | ||
431 | /* unmute the src output */ | 446 | /* unmute the src output */ |
432 | if (query_amp_caps(codec, src, HDA_OUTPUT) & | 447 | if (have_vol_or_mute(codec, src, HDA_OUTPUT)) |
433 | (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) | ||
434 | snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, | 448 | snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, |
435 | AMP_OUT_UNMUTE); | 449 | AMP_OUT_UNMUTE); |
436 | 450 | ||
437 | /* unmute AA-path if present */ | 451 | /* unmute AA-path if present */ |
438 | if (!mix) | 452 | if (!mix || mix == src) |
439 | return; | 453 | return; |
440 | idx = __get_connection_index(codec, nid, mix, NULL); | 454 | idx = __get_connection_index(codec, nid, mix, NULL); |
441 | if (idx >= 0) | 455 | if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT)) |
442 | snd_hda_codec_write(codec, nid, 0, | 456 | snd_hda_codec_write(codec, nid, 0, |
443 | AC_VERB_SET_AMP_GAIN_MUTE, | 457 | AC_VERB_SET_AMP_GAIN_MUTE, |
444 | AMP_IN_UNMUTE(idx)); | 458 | AMP_IN_UNMUTE(idx)); |
@@ -694,9 +708,16 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, | |||
694 | static int via_independent_hp_info(struct snd_kcontrol *kcontrol, | 708 | static int via_independent_hp_info(struct snd_kcontrol *kcontrol, |
695 | struct snd_ctl_elem_info *uinfo) | 709 | struct snd_ctl_elem_info *uinfo) |
696 | { | 710 | { |
697 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | 711 | static const char * const texts[] = { "OFF", "ON" }; |
698 | struct via_spec *spec = codec->spec; | 712 | |
699 | return snd_hda_input_mux_info(spec->hp_mux, uinfo); | 713 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
714 | uinfo->count = 1; | ||
715 | uinfo->value.enumerated.items = 2; | ||
716 | if (uinfo->value.enumerated.item >= 2) | ||
717 | uinfo->value.enumerated.item = 1; | ||
718 | strcpy(uinfo->value.enumerated.name, | ||
719 | texts[uinfo->value.enumerated.item]); | ||
720 | return 0; | ||
700 | } | 721 | } |
701 | 722 | ||
702 | static int via_independent_hp_get(struct snd_kcontrol *kcontrol, | 723 | static int via_independent_hp_get(struct snd_kcontrol *kcontrol, |
@@ -714,12 +735,28 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, | |||
714 | { | 735 | { |
715 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | 736 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
716 | struct via_spec *spec = codec->spec; | 737 | struct via_spec *spec = codec->spec; |
717 | hda_nid_t nid = kcontrol->private_value; | 738 | hda_nid_t nid, src; |
718 | unsigned int pinsel = ucontrol->value.enumerated.item[0]; | 739 | int i, idx, num_conns; |
719 | /* Get Independent Mode index of headphone pin widget */ | 740 | struct nid_path *path; |
720 | spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel | 741 | |
721 | ? 1 : 0; | 742 | spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0]; |
722 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); | 743 | if (spec->hp_independent_mode) |
744 | path = &spec->hp_path; | ||
745 | else | ||
746 | path = &spec->hp_dep_path; | ||
747 | |||
748 | /* re-route the output path */ | ||
749 | for (i = path->depth - 1; i > 0; i--) { | ||
750 | nid = path->path[i]; | ||
751 | src = path->path[i - 1]; | ||
752 | idx = __get_connection_index(codec, nid, src, &num_conns); | ||
753 | if (idx < 0) | ||
754 | continue; | ||
755 | if (num_conns > 1 && | ||
756 | get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) | ||
757 | snd_hda_codec_write(codec, nid, 0, | ||
758 | AC_VERB_SET_CONNECT_SEL, idx); | ||
759 | } | ||
723 | 760 | ||
724 | /* update jack power state */ | 761 | /* update jack power state */ |
725 | set_widgets_power_state(codec); | 762 | set_widgets_power_state(codec); |
@@ -746,7 +783,6 @@ static int via_hp_build(struct hda_codec *codec) | |||
746 | return -ENOMEM; | 783 | return -ENOMEM; |
747 | 784 | ||
748 | knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; | 785 | knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; |
749 | knew->private_value = nid; | ||
750 | 786 | ||
751 | return 0; | 787 | return 0; |
752 | } | 788 | } |
@@ -1622,9 +1658,9 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, | |||
1622 | hda_nid_t nid; | 1658 | hda_nid_t nid; |
1623 | int err; | 1659 | int err; |
1624 | 1660 | ||
1625 | if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) | 1661 | if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) |
1626 | nid = dac; | 1662 | nid = dac; |
1627 | else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) | 1663 | else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) |
1628 | nid = pin; | 1664 | nid = pin; |
1629 | else | 1665 | else |
1630 | nid = 0; | 1666 | nid = 0; |
@@ -1636,9 +1672,9 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx, | |||
1636 | return err; | 1672 | return err; |
1637 | } | 1673 | } |
1638 | 1674 | ||
1639 | if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) | 1675 | if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) |
1640 | nid = dac; | 1676 | nid = dac; |
1641 | else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) | 1677 | else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) |
1642 | nid = pin; | 1678 | nid = pin; |
1643 | else | 1679 | else |
1644 | nid = 0; | 1680 | nid = 0; |
@@ -1741,19 +1777,6 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec) | |||
1741 | return 0; | 1777 | return 0; |
1742 | } | 1778 | } |
1743 | 1779 | ||
1744 | static void create_hp_imux(struct via_spec *spec) | ||
1745 | { | ||
1746 | int i; | ||
1747 | struct hda_input_mux *imux = &spec->private_imux[1]; | ||
1748 | static const char * const texts[] = { "OFF", "ON", NULL}; | ||
1749 | |||
1750 | /* for hp mode select */ | ||
1751 | for (i = 0; texts[i]; i++) | ||
1752 | snd_hda_add_imux_item(imux, texts[i], i, NULL); | ||
1753 | |||
1754 | spec->hp_mux = &spec->private_imux[1]; | ||
1755 | } | ||
1756 | |||
1757 | static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) | 1780 | static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) |
1758 | { | 1781 | { |
1759 | struct via_spec *spec = codec->spec; | 1782 | struct via_spec *spec = codec->spec; |
@@ -1762,18 +1785,14 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) | |||
1762 | if (!pin) | 1785 | if (!pin) |
1763 | return 0; | 1786 | return 0; |
1764 | 1787 | ||
1765 | if (parse_output_path(codec, pin, 0, &spec->hp_path)) { | 1788 | if (parse_output_path(codec, pin, 0, &spec->hp_path)) |
1766 | spec->hp_dac_nid = spec->hp_path.path[0]; | 1789 | spec->hp_dac_nid = spec->hp_path.path[0]; |
1767 | spec->hp_independent_mode_index = spec->hp_path.idx[0]; | ||
1768 | create_hp_imux(spec); | ||
1769 | } | ||
1770 | 1790 | ||
1771 | if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], | 1791 | if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], |
1772 | &spec->hp_dep_path) && | 1792 | &spec->hp_dep_path) && |
1773 | !spec->hp_dac_nid) | 1793 | !spec->hp_dac_nid) |
1774 | return 0; | 1794 | return 0; |
1775 | 1795 | ||
1776 | |||
1777 | err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); | 1796 | err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); |
1778 | if (err < 0) | 1797 | if (err < 0) |
1779 | return err; | 1798 | return err; |
@@ -2068,7 +2087,7 @@ static int via_parse_auto_config(struct hda_codec *codec) | |||
2068 | 2087 | ||
2069 | spec->input_mux = &spec->private_imux[0]; | 2088 | spec->input_mux = &spec->private_imux[0]; |
2070 | 2089 | ||
2071 | if (spec->hp_mux) { | 2090 | if (spec->hp_dac_nid && spec->hp_dep_path.depth) { |
2072 | err = via_hp_build(codec); | 2091 | err = via_hp_build(codec); |
2073 | if (err < 0) | 2092 | if (err < 0) |
2074 | return err; | 2093 | return err; |