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