Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933495AbbDUQO4 (ORCPT ); Tue, 21 Apr 2015 12:14:56 -0400 Received: from youngberry.canonical.com ([91.189.89.112]:57339 "EHLO youngberry.canonical.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755940AbbDUPdk (ORCPT ); Tue, 21 Apr 2015 11:33:40 -0400 From: Luis Henriques To: linux-kernel@vger.kernel.org, stable@vger.kernel.org, kernel-team@lists.ubuntu.com Cc: Takashi Iwai , Luis Henriques Subject: [PATCH 3.16.y-ckt 056/144] ALSA: hda - Treat stereo-to-mono mix properly Date: Tue, 21 Apr 2015 16:30:41 +0100 Message-Id: <1429630329-21748-57-git-send-email-luis.henriques@canonical.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1429630329-21748-1-git-send-email-luis.henriques@canonical.com> References: <1429630329-21748-1-git-send-email-luis.henriques@canonical.com> X-Extended-Stable: 3.16 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5517 Lines: 150 3.16.7-ckt10 -stable review patch. If anyone has any objections, please let me know. ------------------ From: Takashi Iwai commit cc261738add93947d138d2fabad9f4dbed4e5c00 upstream. 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 Signed-off-by: Takashi Iwai Signed-off-by: Luis Henriques --- sound/pci/hda/hda_generic.c | 21 +++++++++++++++++++-- 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 b756dd2906e4..0882f060696d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -653,13 +653,30 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, return val; } +/* is this a stereo widget or a stereo-to-mono mix? */ +static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir) +{ + unsigned int wcaps = get_wcaps(codec, nid); + hda_nid_t conn; + + if (wcaps & AC_WCAP_STEREO) + return true; + if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX) + return false; + if (snd_hda_get_num_conns(codec, nid) != 1) + return false; + if (snd_hda_get_connections(codec, nid, &conn, 1) < 0) + return false; + return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO); +} + /* initialize the amp value (only at the first time) */ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) { unsigned int caps = query_amp_caps(codec, nid, dir); int val = get_amp_val_to_activate(codec, nid, dir, caps, false); - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + if (is_stereo_amps(codec, nid, dir)) snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); else snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val); @@ -669,7 +686,7 @@ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, unsigned int mask, unsigned int val) { - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) + if (is_stereo_amps(codec, nid, dir)) return snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val); 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, (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT); } +/* is this a stereo widget or a stereo-to-mono mix? */ +static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int wcaps, int indices) +{ + hda_nid_t conn; + + if (wcaps & AC_WCAP_STEREO) + return true; + /* check for a stereo-to-mono mix; it must be: + * only a single connection, only for input, and only a mixer widget + */ + if (indices != 1 || dir != HDA_INPUT || + get_wcaps_type(wcaps) != AC_WID_AUD_MIX) + return false; + + if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0) + return false; + /* the connection source is a stereo? */ + wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP); + return !!(wcaps & AC_WCAP_STEREO); +} + static void print_amp_vals(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, - int dir, int stereo, int indices) + int dir, unsigned int wcaps, int indices) { unsigned int val; + bool stereo; int i; + stereo = is_stereo_amps(codec, nid, dir, wcaps, indices); + dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); @@ -757,12 +782,10 @@ static void print_codec_info(struct snd_info_entry *entry, (codec->single_adc_amp && wid_type == AC_WID_AUD_IN)) print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO, - 1); + wid_caps, 1); else print_amp_vals(buffer, codec, nid, HDA_INPUT, - wid_caps & AC_WCAP_STEREO, - conn_len); + wid_caps, conn_len); } if (wid_caps & AC_WCAP_OUT_AMP) { snd_iprintf(buffer, " Amp-Out caps: "); @@ -771,11 +794,10 @@ static void print_codec_info(struct snd_info_entry *entry, if (wid_type == AC_WID_PIN && codec->pin_amp_workaround) print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO, - conn_len); + wid_caps, conn_len); else print_amp_vals(buffer, codec, nid, HDA_OUTPUT, - wid_caps & AC_WCAP_STEREO, 1); + wid_caps, 1); } switch (wid_type) { -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/