Commit de8c85f7 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai

ALSA: HDA: Sigmatel: work around incorrect master muting

The HDA specification does not allow for a codec to mute itself just
because the volume is reduced, so _of course_ somebody had to go and do
it.  This wouldn'\''t hurt too much when the volume is adjusted by hand,
but programs like PA that try to set the volume automatically could
inadvertently mute the output.

To work around this, change the TLV dB information for the Master volume
on all Sigmatel HDA codecs to indicate the the minimal volume setting
actually mutes.
Reported-by: default avatarColin Guthrie <gmane@colin.guthr.ie>
Reported-by: default avatar"Alexander E. Patrakov" <patrakov@gmail.com>
Tested-by: default avatarColin Guthrie <cguthrie@mandriva.org>
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent c3d226ab
...@@ -1831,6 +1831,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, ...@@ -1831,6 +1831,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
hda_nid_t nid = get_amp_nid(kcontrol); hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol); int dir = get_amp_direction(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol); unsigned int ofs = get_amp_offset(kcontrol);
bool min_mute = get_amp_min_mute(kcontrol);
u32 caps, val1, val2; u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int)) if (size < 4 * sizeof(unsigned int))
...@@ -1841,6 +1842,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, ...@@ -1841,6 +1842,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT); val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs; val1 += ofs;
val1 = ((int)val1) * ((int)val2); val1 = ((int)val1) * ((int)val2);
if (min_mute)
val2 |= 0x10000;
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv)) if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT; return -EFAULT;
if (put_user(2 * sizeof(unsigned int), _tlv + 1)) if (put_user(2 * sizeof(unsigned int), _tlv + 1))
......
...@@ -38,10 +38,11 @@ ...@@ -38,10 +38,11 @@
*/ */
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \ #define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23)) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
#define HDA_AMP_VAL_MIN_MUTE (1<<29)
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ #define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0) HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */ /* mono volume with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \ .subdevice = HDA_SUBDEV_AMP_FLAG, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
...@@ -51,16 +52,20 @@ ...@@ -51,16 +52,20 @@
.get = snd_hda_mixer_amp_volume_get, \ .get = snd_hda_mixer_amp_volume_get, \
.put = snd_hda_mixer_amp_volume_put, \ .put = snd_hda_mixer_amp_volume_put, \
.tlv = { .c = snd_hda_mixer_amp_tlv }, \ .tlv = { .c = snd_hda_mixer_amp_tlv }, \
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) } .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
/* stereo volume with index */ /* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \ #define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction) HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
/* mono volume */ /* mono volume */
#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \ #define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction) HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
/* stereo volume */ /* stereo volume */
#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \ #define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction) HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
/* stereo volume with min=mute */
#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
HDA_AMP_VAL_MIN_MUTE)
/* mono mute switch with index (index=0,1,...) (channel=1,2) */ /* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
...@@ -581,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, ...@@ -581,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1) #define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) #define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/* /*
* CEA Short Audio Descriptor data * CEA Short Audio Descriptor data
......
...@@ -992,7 +992,7 @@ static struct hda_verb stac9205_core_init[] = { ...@@ -992,7 +992,7 @@ static struct hda_verb stac9205_core_init[] = {
} }
static struct snd_kcontrol_new stac9200_mixer[] = { static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
...@@ -1020,7 +1020,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = { ...@@ -1020,7 +1020,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
}; };
static struct snd_kcontrol_new stac925x_mixer[] = { static struct snd_kcontrol_new stac925x_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT), HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
{ } /* end */ { } /* end */
}; };
...@@ -1144,6 +1144,8 @@ static int stac92xx_build_controls(struct hda_codec *codec) ...@@ -1144,6 +1144,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
HDA_OUTPUT, vmaster_tlv); HDA_OUTPUT, vmaster_tlv);
/* correct volume offset */ /* correct volume offset */
vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset; vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
/* minimum value is actually mute */
vmaster_tlv[3] |= 0x1000;
err = snd_hda_add_vmaster(codec, "Master Playback Volume", err = snd_hda_add_vmaster(codec, "Master Playback Volume",
vmaster_tlv, slave_vols); vmaster_tlv, slave_vols);
if (err < 0) if (err < 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment