Skip to content

Commit 095d621

Browse files
Stefan Bindingbroonie
authored andcommitted
ASoC: ops: fix snd_soc_get_volsw for sx controls
SX controls are currently broken, since the clamp introduced in commit a0ce874 ("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: a0ce874 ("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>
1 parent fa43ab1 commit 095d621

1 file changed

Lines changed: 20 additions & 12 deletions

File tree

sound/soc/soc-ops.c

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
111111
EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
112112

113113
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)
115116
{
116117
int val = reg_val;
117118

@@ -141,20 +142,26 @@ static unsigned int sdca_soc_q78_ctl_to_reg(struct soc_mixer_control *mc, int va
141142
}
142143

143144
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+
unsigned int mask, unsigned int shift, int max,
146+
bool sx)
145147
{
146148
int val = (reg_val >> shift) & mask;
147149

148150
if (mc->sign_bit)
149151
val = sign_extend32(val, mc->sign_bit);
150152

151-
val = clamp(val, mc->min, mc->max);
152-
val -= mc->min;
153+
if (sx) {
154+
val -= mc->min; // SX controls intentionally can overflow here
155+
val = min_t(unsigned int, val & mask, max);
156+
} else {
157+
val = clamp(val, mc->min, mc->max);
158+
val -= mc->min;
159+
}
153160

154161
if (mc->invert)
155162
val = max - val;
156163

157-
return val & mask;
164+
return val;
158165
}
159166

160167
static unsigned int soc_mixer_ctl_to_reg(struct soc_mixer_control *mc, int val,
@@ -280,9 +287,10 @@ static int soc_put_volsw(struct snd_kcontrol *kcontrol,
280287

281288
static int soc_get_volsw(struct snd_kcontrol *kcontrol,
282289
struct snd_ctl_elem_value *ucontrol,
283-
struct soc_mixer_control *mc, int mask, int max)
290+
struct soc_mixer_control *mc, int mask, int max, bool sx)
284291
{
285-
int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int, unsigned int, int);
292+
int (*reg_to_ctl)(struct soc_mixer_control *, unsigned int, unsigned int,
293+
unsigned int, int, bool);
286294
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
287295
unsigned int reg_val;
288296
int val;
@@ -293,16 +301,16 @@ static int soc_get_volsw(struct snd_kcontrol *kcontrol,
293301
reg_to_ctl = soc_mixer_reg_to_ctl;
294302

295303
reg_val = snd_soc_component_read(component, mc->reg);
296-
val = reg_to_ctl(mc, reg_val, mask, mc->shift, max);
304+
val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx);
297305

298306
ucontrol->value.integer.value[0] = val;
299307

300308
if (snd_soc_volsw_is_stereo(mc)) {
301309
if (mc->reg == mc->rreg) {
302-
val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max);
310+
val = reg_to_ctl(mc, reg_val, mask, mc->rshift, max, sx);
303311
} else {
304312
reg_val = snd_soc_component_read(component, mc->rreg);
305-
val = reg_to_ctl(mc, reg_val, mask, mc->shift, max);
313+
val = reg_to_ctl(mc, reg_val, mask, mc->shift, max, sx);
306314
}
307315

308316
ucontrol->value.integer.value[1] = val;
@@ -371,7 +379,7 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
371379
(struct soc_mixer_control *)kcontrol->private_value;
372380
unsigned int mask = soc_mixer_mask(mc);
373381

374-
return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max - mc->min);
382+
return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max - mc->min, false);
375383
}
376384
EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
377385

@@ -413,7 +421,7 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
413421
(struct soc_mixer_control *)kcontrol->private_value;
414422
unsigned int mask = soc_mixer_sx_mask(mc);
415423

416-
return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max);
424+
return soc_get_volsw(kcontrol, ucontrol, mc, mask, mc->max, true);
417425
}
418426
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
419427

0 commit comments

Comments
 (0)