Re: [PATCH v2 07/21] ASoC: ops: Introduce 'soc_set_enum_kctl'
From: Ajay Kumar Nandam
Date: Wed Oct 07 2026 - 13:38:49 EST
On 10/4/2026 11:33 AM, James Calligeros wrote:
From: Martin Povišer <povik+lin@xxxxxxxxxxx>
The new function is to be used to set enumerated controls to desired
values. It is something a machine driver may call in fixup_controls.
Signed-off-by: Martin Povišer <povik+lin@xxxxxxxxxxx>
Signed-off-by: James Calligeros <jcalligeros99@xxxxxxxxx>
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -505,6 +505,75 @@ int snd_soc_deactivate_kctl(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_deactivate_kctl);
+static int soc_set_enum_kctl(struct snd_kcontrol *kctl, const char *value)
+{
+ struct snd_ctl_elem_value evalue = { 0 };
+ struct snd_ctl_elem_info info = { 0 };
+ int sel, i, ret;
+
+ ret = kctl->info(kctl, &info);
+ if (ret < 0)
+ return ret;
+
Could this initialize the control IDs before calling the control callbacks?
The normal ALSA write path does not call `put()` with an empty ID: `snd_ctl_elem_write()` resolves the control and then calls `snd_ctl_build_ioff(&control->id, kctl, index_offset)` before invoking the control's `put()` callback. This helper builds a zeroed `struct snd_ctl_elem_value` and passes it directly to `kctl->put()`, so callbacks that inspect `ucontrol->id.name` see an empty name.
There are existing enum/mux callbacks that branch on `ucontrol->id.name`, for example `rt700_mux_put()` and similar SoundWire codec mux callbacks. So this helper should probably initialize the callback objects from the target control before calling into them, e.g. set `info.id = kctl->id` and `evalue.id = kctl->id` before the `info()` / `put()` calls.
While touching this, it may also be worth heap-allocating `struct snd_ctl_elem_value`, matching the nearby `snd_soc_clip_to_platform_max()` pattern, since the ALSA control value struct is fairly large for stack use.
+ if (info.type != SNDRV_CTL_ELEM_TYPE_ENUMERATED)
+ return -EINVAL;
+
+ for (sel = 0; sel < info.value.enumerated.items; sel++) {
+ info.value.enumerated.item = sel;
+ ret = kctl->info(kctl, &info);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(value, info.value.enumerated.name))
+ break;
+ }
+
+ if (sel == info.value.enumerated.items)
+ return -EINVAL;
+
+ for (i = 0; i < info.count; i++)
+ evalue.value.enumerated.item[i] = sel;
+
+ if (kctl->put)
+ return kctl->put(kctl, &evalue);
+ else
+ return -EINVAL;
+}
+
+/**
+ * snd_soc_set_enum_kctl - Set enumerated control matching a name
+ *
+ * @card: where to look for the controls
+ * @name: name
+ * @value: string value to set the controls to
+ *
+ * Return 0 on success, else error.
+ */
+int snd_soc_set_enum_kctl(struct snd_soc_card *card,
+ const char *name, const char *value)
+{
+ struct snd_kcontrol *kctl;
+ int ret = -EINVAL;
+
+ /* Sanity check for name */
+ if (unlikely(!name))
+ return -EINVAL;
+
+ kctl = snd_soc_card_get_kcontrol(card, name);
+
+ if (kctl) {
+ ret = soc_set_enum_kctl(kctl, value);
+ if (ret)
+ return ret;
+ } else {
+ dev_dbg(card->dev, "Tried to set unknown control '%s' to '%s'\n",
+ name, value);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_enum_kctl);
+
int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{