]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: SDCA: Limit values user can write to Selected Mode
authorCharles Keepax <ckeepax@opensource.cirrus.com>
Wed, 4 Feb 2026 12:59:43 +0000 (12:59 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 4 Feb 2026 14:10:51 +0000 (14:10 +0000)
Prevent the user from both updating the Selected Mode control
whilst the jack is not present, and from writing values that don't
correspond to a valid jack type (Unknown, in progress, etc.).

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Link: https://patch.msgid.link/20260204125944.1134011-7-ckeepax@opensource.cirrus.com
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sdca/sdca_asoc.c
sound/soc/sdca/sdca_jack.c

index 9685281529e9f7b0052e3e38aeccb00148166ce7..bb6e74e80a3e81d210a5cc547dd6b52f8c593e33 100644 (file)
@@ -116,6 +116,41 @@ int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *fun
 }
 EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA");
 
+static int ge_put_enum_double(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+       struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+       struct device *dev = component->dev;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       unsigned int reg = e->reg;
+       int ret;
+
+       reg &= ~SDW_SDCA_CTL_CSEL(0x3F);
+       reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE);
+
+       ret = pm_runtime_resume_and_get(dev);
+       if (ret < 0) {
+               dev_err(dev, "failed to resume writing %s: %d\n",
+                       kcontrol->id.name, ret);
+               return ret;
+       }
+
+       ret = snd_soc_component_read(component, reg);
+       pm_runtime_put(dev);
+       if (ret < 0)
+               return ret;
+       else if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS)
+               return -EBUSY;
+
+       ret = snd_soc_enum_item_to_val(e, item[0]);
+       if (ret <= SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS)
+               return -EINVAL;
+
+       return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
 static int entity_early_parse_ge(struct device *dev,
                                 struct sdca_function_data *function,
                                 struct sdca_entity *entity)
@@ -192,7 +227,7 @@ static int entity_early_parse_ge(struct device *dev,
        kctl->name = control_name;
        kctl->info = snd_soc_info_enum_double;
        kctl->get = snd_soc_dapm_get_enum_double;
-       kctl->put = snd_soc_dapm_put_enum_double;
+       kctl->put = ge_put_enum_double;
        kctl->private_value = (unsigned long)soc_enum;
 
        entity->ge.kctl = kctl;
index bfa621b744e1a2b39494ef07c9143fdbfd58597a..605514f02045403622925ca0f2e3381f60f8e70c 100644 (file)
@@ -105,7 +105,7 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
 
                ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
 
-               ret = kctl->put(kctl, ucontrol);
+               ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
                if (ret < 0) {
                        dev_err(dev, "failed to update selected mode: %d\n", ret);
                        return ret;