diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-03-16 05:18:08 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-03-16 09:44:03 -0400 |
commit | cc261738add93947d138d2fabad9f4dbed4e5c00 (patch) | |
tree | dce1925148a516e3a2b015a91b2ce2663206c32e /sound | |
parent | 06e5801b8cb3fc057d88cb4dc03c0b64b2744cda (diff) |
ALSA: hda - Treat stereo-to-mono mix properly
The commit [ef403edb7558: ALSA: hda - Don't access stereo amps for
mono channel widgets] fixed the handling of mono widgets in general,
but it still misses an exceptional case: namely, a mono mixer widget
taking a single stereo input. In this case, it has stereo volumes
although it's a mono widget, and thus we have to take care of both
left and right input channels, as stated in HD-audio spec ("7.1.3
Widget Interconnection Rules").
This patch covers this missing piece by adding proper checks of stereo
amps in both the generic parser and the proc output codes.
Reported-by: Raymond Yau <superquad.vortex2@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 21 | ||||
-rw-r--r-- | sound/pci/hda/hda_proc.c | 38 |
2 files changed, 49 insertions, 10 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fe18071bf93a..8ec5289f8e05 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c | |||
@@ -687,13 +687,30 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, | |||
687 | return val; | 687 | return val; |
688 | } | 688 | } |
689 | 689 | ||
690 | /* is this a stereo widget or a stereo-to-mono mix? */ | ||
691 | static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) | ||
692 | { | ||
693 | unsigned int wcaps = get_wcaps(codec, nid); | ||
694 | hda_nid_t conn; | ||
695 | |||
696 | if (wcaps & AC_WCAP_STEREO) | ||
697 | return true; | ||
698 | if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) | ||
699 | return false; | ||
700 | if (snd_hda_get_num_conns(codec, nid) != 1) | ||
701 | return false; | ||
702 | if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) | ||
703 | return false; | ||
704 | return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); | ||
705 | } | ||
706 | |||
690 | /* initialize the amp value (only at the first time) */ | 707 | /* initialize the amp value (only at the first time) */ |
691 | static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) | 708 | static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) |
692 | { | 709 | { |
693 | unsigned int caps = query_amp_caps(codec, nid, dir); | 710 | unsigned int caps = query_amp_caps(codec, nid, dir); |
694 | int val = get_amp_val_to_activate(codec, nid, dir, caps, false); | 711 | int val = get_amp_val_to_activate(codec, nid, dir, caps, false); |
695 | 712 | ||
696 | if (get_wcaps(codec, nid) & AC_WCAP_STEREO) | 713 | if (is_stereo_amps(codec, nid, dir)) |
697 | snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); | 714 | snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); |
698 | else | 715 | else |
699 | snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); | 716 | snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); |
@@ -703,7 +720,7 @@ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) | |||
703 | static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, | 720 | static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, |
704 | unsigned int mask, unsigned int val) | 721 | unsigned int mask, unsigned int val) |
705 | { | 722 | { |
706 | if (get_wcaps(codec, nid) & AC_WCAP_STEREO) | 723 | if (is_stereo_amps(codec, nid, dir)) |
707 | return snd_hda_codec_amp_stereo(codec, nid, dir, idx, | 724 | return snd_hda_codec_amp_stereo(codec, nid, dir, idx, |
708 | mask, val); | 725 | mask, val); |
709 | else | 726 | else |
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index ce5a6da83419..05e19f78b4cb 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c | |||
@@ -134,13 +134,38 @@ static void print_amp_caps(struct snd_info_buffer *buffer, | |||
134 | (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); | 134 | (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); |
135 | } | 135 | } |
136 | 136 | ||
137 | /* is this a stereo widget or a stereo-to-mono mix? */ | ||
138 | static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, | ||
139 | int dir, unsigned int wcaps, int indices) | ||
140 | { | ||
141 | hda_nid_t conn; | ||
142 | |||
143 | if (wcaps & AC_WCAP_STEREO) | ||
144 | return true; | ||
145 | /* check for a stereo-to-mono mix; it must be: | ||
146 | * only a single connection, only for input, and only a mixer widget | ||
147 | */ | ||
148 | if (indices != 1 || dir != HDA_INPUT || | ||
149 | get_wcaps_type(wcaps) != AC_WID_AUD_MIX) | ||
150 | return false; | ||
151 | |||
152 | if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0) | ||
153 | return false; | ||
154 | /* the connection source is a stereo? */ | ||
155 | wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP); | ||
156 | return !!(wcaps & AC_WCAP_STEREO); | ||
157 | } | ||
158 | |||
137 | static void print_amp_vals(struct snd_info_buffer *buffer, | 159 | static void print_amp_vals(struct snd_info_buffer *buffer, |
138 | struct hda_codec *codec, hda_nid_t nid, | 160 | struct hda_codec *codec, hda_nid_t nid, |
139 | int dir, int stereo, int indices) | 161 | int dir, unsigned int wcaps, int indices) |
140 | { | 162 | { |
141 | unsigned int val; | 163 | unsigned int val; |
164 | bool stereo; | ||
142 | int i; | 165 | int i; |
143 | 166 | ||
167 | stereo = is_stereo_amps(codec, nid, dir, wcaps, indices); | ||
168 | |||
144 | dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; | 169 | dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; |
145 | for (i = 0; i < indices; i++) { | 170 | for (i = 0; i < indices; i++) { |
146 | snd_iprintf(buffer, " ["); | 171 | snd_iprintf(buffer, " ["); |
@@ -757,12 +782,10 @@ static void print_codec_info(struct snd_info_entry *entry, | |||
757 | (codec->single_adc_amp && | 782 | (codec->single_adc_amp && |
758 | wid_type == AC_WID_AUD_IN)) | 783 | wid_type == AC_WID_AUD_IN)) |
759 | print_amp_vals(buffer, codec, nid, HDA_INPUT, | 784 | print_amp_vals(buffer, codec, nid, HDA_INPUT, |
760 | wid_caps & AC_WCAP_STEREO, | 785 | wid_caps, 1); |
761 | 1); | ||
762 | else | 786 | else |
763 | print_amp_vals(buffer, codec, nid, HDA_INPUT, | 787 | print_amp_vals(buffer, codec, nid, HDA_INPUT, |
764 | wid_caps & AC_WCAP_STEREO, | 788 | wid_caps, conn_len); |
765 | conn_len); | ||
766 | } | 789 | } |
767 | if (wid_caps & AC_WCAP_OUT_AMP) { | 790 | if (wid_caps & AC_WCAP_OUT_AMP) { |
768 | snd_iprintf(buffer, " Amp-Out caps: "); | 791 | snd_iprintf(buffer, " Amp-Out caps: "); |
@@ -771,11 +794,10 @@ static void print_codec_info(struct snd_info_entry *entry, | |||
771 | if (wid_type == AC_WID_PIN && | 794 | if (wid_type == AC_WID_PIN && |
772 | codec->pin_amp_workaround) | 795 | codec->pin_amp_workaround) |
773 | print_amp_vals(buffer, codec, nid, HDA_OUTPUT, | 796 | print_amp_vals(buffer, codec, nid, HDA_OUTPUT, |
774 | wid_caps & AC_WCAP_STEREO, | 797 | wid_caps, conn_len); |
775 | conn_len); | ||
776 | else | 798 | else |
777 | print_amp_vals(buffer, codec, nid, HDA_OUTPUT, | 799 | print_amp_vals(buffer, codec, nid, HDA_OUTPUT, |
778 | wid_caps & AC_WCAP_STEREO, 1); | 800 | wid_caps, 1); |
779 | } | 801 | } |
780 | 802 | ||
781 | switch (wid_type) { | 803 | switch (wid_type) { |