lkml.org 
[lkml]   [2015]   [Apr]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3.12 052/155] ALSA: hda - Treat stereo-to-mono mix properly
    Date
    From: Takashi Iwai <tiwai@suse.de>

    3.12-stable review patch. If anyone has any objections, please let me know.

    ===============

    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 <superquad.vortex2@gmail.com>
    Signed-off-by: Takashi Iwai <tiwai@suse.de>
    Signed-off-by: Jiri Slaby <jslaby@suse.cz>
    ---
    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 91fabbadd07b..1b1243450d8e 100644
    --- a/sound/pci/hda/hda_generic.c
    +++ b/sound/pci/hda/hda_generic.c
    @@ -652,13 +652,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);
    @@ -668,7 +685,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 a8cb22eec89e..d64193c1a387 100644
    --- a/sound/pci/hda/hda_proc.c
    +++ b/sound/pci/hda/hda_proc.c
    @@ -129,13 +129,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, " [");
    @@ -727,12 +752,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: ");
    @@ -741,11 +764,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) {
    --
    2.3.4


    \
     
     \ /
      Last update: 2015-04-07 16:01    [W:2.887 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site