mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-25 16:11:45 +00:00
ALSA: hda - Treat stereo-to-mono mix properly
The commit [ef403edb75
: 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>
This commit is contained in:
parent
06e5801b8c
commit
cc261738ad
2 changed files with 49 additions and 10 deletions
|
@ -687,13 +687,30 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
|
||||||
return val;
|
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) */
|
/* 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)
|
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);
|
unsigned int caps = query_amp_caps(codec, nid, dir);
|
||||||
int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
|
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);
|
snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
|
||||||
else
|
else
|
||||||
snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val);
|
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)
|
||||||
static int update_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)
|
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,
|
return snd_hda_codec_amp_stereo(codec, nid, dir, idx,
|
||||||
mask, val);
|
mask, val);
|
||||||
else
|
else
|
||||||
|
|
|
@ -134,13 +134,38 @@ static void print_amp_caps(struct snd_info_buffer *buffer,
|
||||||
(caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
|
(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,
|
static void print_amp_vals(struct snd_info_buffer *buffer,
|
||||||
struct hda_codec *codec, hda_nid_t nid,
|
struct hda_codec *codec, hda_nid_t nid,
|
||||||
int dir, int stereo, int indices)
|
int dir, unsigned int wcaps, int indices)
|
||||||
{
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
bool stereo;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
|
||||||
|
|
||||||
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
|
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
|
||||||
for (i = 0; i < indices; i++) {
|
for (i = 0; i < indices; i++) {
|
||||||
snd_iprintf(buffer, " [");
|
snd_iprintf(buffer, " [");
|
||||||
|
@ -757,12 +782,10 @@ static void print_codec_info(struct snd_info_entry *entry,
|
||||||
(codec->single_adc_amp &&
|
(codec->single_adc_amp &&
|
||||||
wid_type == AC_WID_AUD_IN))
|
wid_type == AC_WID_AUD_IN))
|
||||||
print_amp_vals(buffer, codec, nid, HDA_INPUT,
|
print_amp_vals(buffer, codec, nid, HDA_INPUT,
|
||||||
wid_caps & AC_WCAP_STEREO,
|
wid_caps, 1);
|
||||||
1);
|
|
||||||
else
|
else
|
||||||
print_amp_vals(buffer, codec, nid, HDA_INPUT,
|
print_amp_vals(buffer, codec, nid, HDA_INPUT,
|
||||||
wid_caps & AC_WCAP_STEREO,
|
wid_caps, conn_len);
|
||||||
conn_len);
|
|
||||||
}
|
}
|
||||||
if (wid_caps & AC_WCAP_OUT_AMP) {
|
if (wid_caps & AC_WCAP_OUT_AMP) {
|
||||||
snd_iprintf(buffer, " Amp-Out caps: ");
|
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 &&
|
if (wid_type == AC_WID_PIN &&
|
||||||
codec->pin_amp_workaround)
|
codec->pin_amp_workaround)
|
||||||
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
|
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
|
||||||
wid_caps & AC_WCAP_STEREO,
|
wid_caps, conn_len);
|
||||||
conn_len);
|
|
||||||
else
|
else
|
||||||
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
|
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
|
||||||
wid_caps & AC_WCAP_STEREO, 1);
|
wid_caps, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (wid_type) {
|
switch (wid_type) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue