[PATCH] ALSA: hda/realtek - Add control fixup for Lenovo Thinkpad X1 Carbon 7th

Takashi Iwai tiwai at suse.de
Tue Sep 1 17:01:55 CEST 2020


On Tue, 01 Sep 2020 15:52:09 +0200,
Jaroslav Kysela wrote:
> 
> > +}
> > +
> > +static int tpx1_dual_speaker_vol_put(struct snd_kcontrol *kcontrol,
> > +				     struct snd_ctl_elem_value *ucontrol)
> > +{
> > +	struct tpx1_dual_speaker *speaker_priv = snd_kcontrol_chip(kcontrol);
> > +	int err;
> > +
> > +	/* Control tweeter volume */
> > +	err = speaker_priv->underlying.put(&speaker_priv->underlying,
> > +					   ucontrol);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	/* Control woofer volume (shared with headphone) */
> > +	err = speaker_priv->hp_vol.put(&speaker_priv->hp_vol, ucontrol);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	snd_ctl_notify(speaker_priv->codec->card, SNDRV_CTL_EVENT_MASK_VALUE,
> > +		       &speaker_priv->hp_vol.id);
> > +	return err;
> > +}
> > +
> > +static int tpx1_dual_speaker_vol_tlv(struct snd_kcontrol *kcontrol,
> > +				     int op_flag, unsigned int size,
> > +				     unsigned int __user *tlv)
> > +{
> > +	struct tpx1_dual_speaker *speaker_priv = snd_kcontrol_chip(kcontrol);
> > +
> > +	return speaker_priv->underlying.tlv.c(&speaker_priv->underlying,
> > +					      op_flag, size, tlv);
> > +}
> > +
> > +static void tpx1_dual_speaker_vol_free(struct snd_kcontrol *kcontrol)
> > +{
> > +	struct tpx1_dual_speaker *speaker_priv = snd_kcontrol_chip(kcontrol);
> > +
> > +	if (speaker_priv->underlying.private_free)
> > +		speaker_priv->underlying.private_free(
> > +			&speaker_priv->underlying);
> > +	kfree(speaker_priv);
> > +}
> > +
> > +static int tpx1_dual_override_speaker_vol(struct hda_codec *codec,
> > +					  struct snd_kcontrol *speaker_vol,
> > +					  struct snd_kcontrol *hp_vol)
> > +{
> > +	struct tpx1_dual_speaker *speaker_priv;
> > +
> > +	speaker_priv = kmalloc(sizeof(struct tpx1_dual_speaker), GFP_KERNEL);
> > +	if (!speaker_priv)
> > +		return -ENOMEM;
> > +	speaker_priv->codec = codec;
> > +	memcpy(&speaker_priv->underlying, speaker_vol,
> > +	       sizeof(struct snd_kcontrol));
> > +	memcpy(&speaker_priv->hp_vol, hp_vol, sizeof(struct snd_kcontrol));
> 
> This is a bit clumsy part. It would be probably nice to have a helper in the
> upper control code to clone the original control safely. Takashi?

The purpose of those is to have two controls managing the same amp and
get notified with each other at other's update, right?  The missing
piece is only about notification, and that could be done in the common
code somehow, too.  For example, we can reduce the 16bit usage of NID
to 8 bit embedded in private_value, then we'll have 8 bit space for
storing the coupled kctl nid or some other tag for notification.

However, the approach by this patch has minor problems, as far as I
see:

- The notification may be issued unnecessarily for Master volume
  change;
  when you change Master volume, it'll notify Headphone and/or Speaker
  as well although those (virtual) values aren't changed.
  It's a minor issue and can be almost negligible, though.

- The volume status depends on the operation order;
  e.g. when switching the output from speaker to headphone, at first
  mute and set volume zero Speaker, then unmute/raise Headphone.
  But if we do unmute/raise Headphone at first, then mute/zero
  Speaker, the headphone output will be also zero volume out of
  sudden.
  It seems that PA does in the former way, so the current approach
  might work practically, but it can be a pitfall in some corner
  cases.

BTW, if this approach with the tied kctls sharing the same amp is
acceptable, we may apply it also for the existing case; e.g. the
generic parser already creates a bit weird kctl like "Headphone+LO" or
"Speaker+LO".  Those can be re-implemented with two tied kctls, too.


thanks,

Takashi


More information about the Alsa-devel mailing list