[alsa-devel] [RFC] Initialize volumes of HD-audio slave ctls

David Henningsson david.henningsson at canonical.com
Fri Mar 9 00:55:53 CET 2012


On 03/08/2012 04:27 PM, Takashi Iwai wrote:
> Hi,
>
> the patch below is an attempt to initialize the volume / mutes of
> slave controls (such as "Headphone", "Speaker") with vmaster in
> HD-audio, so that the sound can come out only by changing the master
> volume/mute.
>
> We have thought that such initializations could be done well in
> alsactl init, but it seems that not everyone installs the latest and
> greatest alsactl, and there is always a risk that any new controls may
> be added before alsactl is updated and released.  Since the master
> volume is set muted, the risk by this change should be low.
>
> patch_cirrus.c still doesn't support this because it's handling
> vmaster by itself, but it can be fixed later, too.
>
> If anyone has a concern by this, please let me know.

I think it is good to initialise the volume controls to a sane value in 
general. I guess the risk of causing unnecessary pops is possible, but 
not common.

I'm a little surprised by the implementation though; partly because the 
functions added to hda_codec.c does not seem to be HDA specific, partly 
by the need to mess around with set_fs. I trust you to know what you're 
doing w r t to the set_fs stuff, but maybe it would be more elegant if 
these functions could be in the core and either using, or together with, 
other functions that need to do set_fs to read/write TLV information?

Another thought is whether you need to do snd_ctl_notify or if that is 
handled automatically inside kctl->put. Or if you're just counting on 
nobody to listen at that point.

>
>
> thanks,
>
> Takashi
>
> ---
> diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
> index 0527ae1..d4736b9 100644
> --- a/sound/pci/hda/hda_codec.c
> +++ b/sound/pci/hda/hda_codec.c
> @@ -19,6 +19,7 @@
>    *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
>    */
>
> +#include<linux/mm.h>
>   #include<linux/init.h>
>   #include<linux/delay.h>
>   #include<linux/slab.h>
> @@ -2340,6 +2341,52 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
>   	return 1;
>   }
>
> +static int get_kctl_0dB_offset(struct snd_kcontrol *kctl)
> +{
> +	mm_segment_t fs;
> +	int _tlv[4];
> +	const int *tlv = NULL;
> +	int val = -1;
> +
> +	fs = get_fs();
> +	set_fs(get_ds());
> +	if (kctl->vd[0].access&  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
> +		if (!kctl->tlv.c(kctl, 0, sizeof(_tlv), _tlv))
> +			tlv = _tlv;
> +	} else if (kctl->vd[0].access&  SNDRV_CTL_ELEM_ACCESS_TLV_READ)
> +		tlv = kctl->tlv.p;
> +	if (tlv&&  tlv[0] == SNDRV_CTL_TLVT_DB_SCALE)
> +		val = -tlv[2] / tlv[3];
> +	set_fs(fs);
> +	return val;
> +}
> +
> +static int put_kctl_with_value(struct snd_kcontrol *kctl, int val)
> +{
> +	struct snd_ctl_elem_value *ucontrol;
> +	ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
> +	if (!ucontrol)
> +		return -ENOMEM;
> +	ucontrol->value.integer.value[0] = val;
> +	ucontrol->value.integer.value[1] = val;
> +	kctl->put(kctl, ucontrol);
> +	kfree(ucontrol);
> +	return 0;
> +}
> +
> +static int init_slave_0dB(void *data, struct snd_kcontrol *slave)
> +{
> +	int offset = get_kctl_0dB_offset(slave);
> +	if (offset>  0)
> +		put_kctl_with_value(slave, offset);
> +	return 0;
> +}
> +
> +static int init_slave_unmute(void *data, struct snd_kcontrol *slave)
> +{
> +	return put_kctl_with_value(slave, 1);
> +}
> +
>   /**
>    * snd_hda_add_vmaster - create a virtual master control and add slaves
>    * @codec: HD-audio codec
> @@ -2347,6 +2394,7 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
>    * @tlv: TLV data (optional)
>    * @slaves: slave control names (optional)
>    * @suffix: suffix string to each slave name (optional)
> + * @init_slave_vol: initialize slaves to unmute/0dB
>    *
>    * Create a virtual master control with the given name.  The TLV data
>    * must be either NULL or a valid data.
> @@ -2357,9 +2405,9 @@ static int check_slave_present(void *data, struct snd_kcontrol *sctl)
>    *
>    * This function returns zero if successful or a negative error code.
>    */
> -int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
> +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
>   			unsigned int *tlv, const char * const *slaves,
> -			const char *suffix)
> +			const char *suffix, bool init_slave_vol)
>   {
>   	struct snd_kcontrol *kctl;
>   	int err;
> @@ -2380,9 +2428,16 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
>   			 (map_slave_func_t)snd_ctl_add_slave, kctl);
>   	if (err<  0)
>   		return err;
> +
> +	/* init with master mute&  zero volume */
> +	put_kctl_with_value(kctl, 0);
> +	if (init_slave_vol)
> +		map_slaves(codec, slaves, suffix,
> +			   tlv ? init_slave_0dB : init_slave_unmute, kctl);
> +
>   	return 0;
>   }
> -EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
> +EXPORT_SYMBOL_HDA(__snd_hda_add_vmaster);
>
>   /**
>    * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
> diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
> index 6094dea8..caa6468 100644
> --- a/sound/pci/hda/hda_local.h
> +++ b/sound/pci/hda/hda_local.h
> @@ -139,9 +139,11 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
>   			     unsigned int *tlv);
>   struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
>   					    const char *name);
> -int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
> +int __snd_hda_add_vmaster(struct hda_codec *codec, char *name,
>   			unsigned int *tlv, const char * const *slaves,
> -			const char *suffix);
> +			const char *suffix, bool init_slave_vol);
> +#define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \
> +	__snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true)
>   int snd_hda_codec_reset(struct hda_codec *codec);
>
>   /* amp value bits */
> diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
> index 9771b07..f450f2a 100644
> --- a/sound/pci/hda/patch_analog.c
> +++ b/sound/pci/hda/patch_analog.c
> @@ -82,6 +82,7 @@ struct ad198x_spec {
>   	unsigned int inv_jack_detect: 1;/* inverted jack-detection */
>   	unsigned int inv_eapd: 1;	/* inverted EAPD implementation */
>   	unsigned int analog_beep: 1;	/* analog beep input present */
> +	unsigned int avoid_init_slave_vol:1;
>
>   #ifdef CONFIG_SND_HDA_POWER_SAVE
>   	struct hda_loopback_check loopback;
> @@ -223,11 +224,12 @@ static int ad198x_build_controls(struct hda_codec *codec)
>   		unsigned int vmaster_tlv[4];
>   		snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
>   					HDA_OUTPUT, vmaster_tlv);
> -		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
> +		err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
>   					  vmaster_tlv,
>   					  (spec->slave_vols ?
>   					   spec->slave_vols : ad_slave_pfxs),
> -					  "Playback Volume");
> +					  "Playback Volume",
> +					  !spec->avoid_init_slave_vol);
>   		if (err<  0)
>   			return err;
>   	}
> @@ -3604,6 +3606,7 @@ static int patch_ad1884(struct hda_codec *codec)
>   	spec->vmaster_nid = 0x04;
>   	/* we need to cover all playback volumes */
>   	spec->slave_vols = ad1884_slave_vols;
> +	spec->avoid_init_slave_vol = 1;
>
>   	codec->patch_ops = ad198x_patch_ops;
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
>



-- 
David Henningsson, Canonical Ltd.
http://launchpad.net/~diwic


More information about the Alsa-devel mailing list