ASoC: ops: fix snd_soc_get_volsw for sx controls

SX controls are currently broken, since the clamp introduced in
commit a0ce874cfaaa ("ASoC: ops: improve snd_soc_get_volsw") does not
handle SX controls, for example where the min value in the clamp is
greater than the max value in the clamp.

Add clamp parameter to prevent clamping in SX controls.
The nature of SX controls mean that it wraps around 0, with a variable
number of bits, therefore clamping the value becomes complicated and
prone to error.

Fixes 35 kunit tests for soc_ops_test_access.

Fixes: a0ce874cfaaa ("ASoC: ops: improve snd_soc_get_volsw")

Co-developed-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Tested-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://patch.msgid.link/20251216134938.788625-1-sbinding@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by Stefan Binding and committed by Mark Brown 095d6211 fa43ab13

+20 -12
+20 -12
sound/soc/soc-ops.c
··· 111 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); 112 113 static int sdca_soc_q78_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, 114 - unsigned int mask, unsigned int shift, int max) 115 { 116 int val = reg_val; 117 ··· 142 } 143 144 static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, 145 - unsigned int mask, unsigned int shift, int max) 146 { 147 int val = (reg_val >> shift) & mask; 148 149 if (mc->sign_bit) 150 val = sign_extend32(val, mc->sign_bit); 151 152 - val = clamp(val, mc->min, mc->max); 153 - val -= mc->min; 154 155 if (mc->invert) 156 val = max - val; 157 158 - return val & mask; 159 } 160 161 static unsigned int soc_mixer_ctl_to_reg(struct soc_mixer_control *mc, int val, ··· 287 288 static int soc_get_volsw(struct snd_kcontrol *kcontrol, 289 struct snd_ctl_elem_value *ucontrol, 290 - struct soc_mixer_control *mc, int mask, int max) 291 { 292 - int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int, unsigned int, int); 293 struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 294 unsigned int reg_val; 295 int val; ··· 301 reg_to_ctl = soc_mixer_reg_to_ctl; 302 303 reg_val = snd_soc_component_read(component, mc->reg); 304 - val = reg_to_ctl(mc, reg_val, mask, mc->shift, max); 305 306 ucontrol->value.integer.value[0] = val; 307 308 if (snd_soc_volsw_is_stereo(mc)) { 309 if (mc->reg == mc->rreg) { 310 - val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max); 311 } else { 312 reg_val = snd_soc_component_read(component, mc->rreg); 313 - val = reg_to_ctl(mc, reg_val, mask, mc->shift, max); 314 } 315 316 ucontrol->value.integer.value[1] = val; ··· 379 (struct soc_mixer_control *)kcontrol->private_value; 380 unsigned int mask = soc_mixer_mask(mc); 381 382 - return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max - mc->min); 383 } 384 EXPORT_SYMBOL_GPL(snd_soc_get_volsw); 385 ··· 421 (struct soc_mixer_control *)kcontrol->private_value; 422 unsigned int mask = soc_mixer_sx_mask(mc); 423 424 - return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max); 425 } 426 EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); 427
··· 111 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); 112 113 static int sdca_soc_q78_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, 114 + unsigned int mask, unsigned int shift, int max, 115 + bool sx) 116 { 117 int val = reg_val; 118 ··· 141 } 142 143 static int soc_mixer_reg_to_ctl(struct soc_mixer_control *mc, unsigned int reg_val, 144 + unsigned int mask, unsigned int shift, int max, 145 + bool sx) 146 { 147 int val = (reg_val >> shift) & mask; 148 149 if (mc->sign_bit) 150 val = sign_extend32(val, mc->sign_bit); 151 152 + if (sx) { 153 + val -= mc->min; // SX controls intentionally can overflow here 154 + val = min_t(unsigned int, val & mask, max); 155 + } else { 156 + val = clamp(val, mc->min, mc->max); 157 + val -= mc->min; 158 + } 159 160 if (mc->invert) 161 val = max - val; 162 163 + return val; 164 } 165 166 static unsigned int soc_mixer_ctl_to_reg(struct soc_mixer_control *mc, int val, ··· 280 281 static int soc_get_volsw(struct snd_kcontrol *kcontrol, 282 struct snd_ctl_elem_value *ucontrol, 283 + struct soc_mixer_control *mc, int mask, int max, bool sx) 284 { 285 + int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int, 286 + unsigned int, int, bool); 287 struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); 288 unsigned int reg_val; 289 int val; ··· 293 reg_to_ctl = soc_mixer_reg_to_ctl; 294 295 reg_val = snd_soc_component_read(component, mc->reg); 296 + val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); 297 298 ucontrol->value.integer.value[0] = val; 299 300 if (snd_soc_volsw_is_stereo(mc)) { 301 if (mc->reg == mc->rreg) { 302 + val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx); 303 } else { 304 reg_val = snd_soc_component_read(component, mc->rreg); 305 + val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx); 306 } 307 308 ucontrol->value.integer.value[1] = val; ··· 371 (struct soc_mixer_control *)kcontrol->private_value; 372 unsigned int mask = soc_mixer_mask(mc); 373 374 + return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max - mc->min, false); 375 } 376 EXPORT_SYMBOL_GPL(snd_soc_get_volsw); 377 ··· 413 (struct soc_mixer_control *)kcontrol->private_value; 414 unsigned int mask = soc_mixer_sx_mask(mc); 415 416 + return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max, true); 417 } 418 EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx); 419