On Tue, Jun 15, 2010 at 03:11:10PM +1000, Stuart Longland wrote:
When a read is performed, this structure points to this function as the means for reading the register (defined in sound/soc-core.c):
/**
- snd_soc_get_volsw_2r_sx - double with tlv and variable data size
- mixer get callback
- @kcontrol: mixer control
- @uinfo: control element information
- Returns 0 for success.
*/ int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int mask = (1<<mc->shift)-1; int min = mc->min; int val = snd_soc_read(codec, mc->reg) & mask; int valr = snd_soc_read(codec, mc->rreg) & mask;
ucontrol->value.integer.value[0] = ((val & 0xff)-min); ucontrol->value.integer.value[1] = ((valr & 0xff)-min); return 0;
} EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
I've figured it out now... and there'll be a patch to fix it.
The mute <--> gain interaction was pure coincidence as it turns out. There's no interaction between the two, just that when I toggle the mute, the gain gets re-read, and hence it *appeared* to interact.
Notice the value calculated for the channel is:
output = ( regval & 0xff ) - minimum
In my case, my minimum is -6. Suppose I set my driver gain to -1dB, in hexadecimal, 0x3f. Since no sign extension is done, (regval & 0xff) = 63. Adding 6 to this (double negation) you get 69... which is greater than the maximum gain of 29. Hence the scale shoots up to maximum.
The fix, is to change the above so that they mask the result; thus wrapping around the zero point like so:
output = ( ( regval & 0xff ) - minimum ) & mask
This fixes the problem that I observe, and shouldn't break the other user of the control. A patch to fix this issue is coming.