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