On Tue, Jun 15, 2010 at 10:27:34AM +1000, Stuart Longland wrote: > On Fri, Jun 11, 2010 at 11:18:04AM +0100, Mark Brown wrote: > > On Fri, Jun 11, 2010 at 03:55:12PM +1000, Stuart Longland wrote: > > > > > (1) When using the SOC_DOUBLE_R_SX_TLV controls, I notice there's an > > > odd interaction between the mute switch associated with the > > > control, and its corresponding gain setting. > > > > > Nothing changes in the actual registers (except the mute bit of > > > course) but the displayed gain shoots up to infinity if the mute > > > is toggled when the gain associated with that mute control is > > > set below 0dB. When the gain is at 0dB or above, toggling the > > > mute has no effect on the displayed gain setting. > > > > This is almost always due to having an overlap between the bitfield for > > the volume and the mute bit. > > I'll have a closer look, perhaps there's some misunderstanding on my > part as to how the macros work. Okay, I've had a close inspection of how the SOC_DOUBLE_R_SX_TLV widgets are implemented. I couldn't find where in the git trees the control had been added, I wound up applying this patch myself in my tree... it apparently got applied in the official trees, but I cannot find it. <http://permalink.gmane.org/gmane.linux.alsa.devel/72893> Hopefully below is telling everyone what we already know... and I'm just checking that I understand this code properly. If I'm misunderstanding something, please let me know. So the macro is defined as follows (in include/sound.soc.h): #define SOC_DOUBLE_R_SX_TLV(xname, xreg_left, xreg_right, xshift,\ xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw_2r_sx, \ .get = snd_soc_get_volsw_2r_sx, \ .put = snd_soc_put_volsw_2r_sx, \ .private_value = (unsigned long)&(struct soc_mixer_control) \ {.reg = xreg_left, \ .rreg = xreg_right, .shift = xshift, \ .min = xmin, .max = xmax} } 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); Now for my data, almost all the driver gain registers have the following format: Bit 7 6 5 4 3 2 1 0 | | \ / Resvd Mute '------Signed Gain setting in dB------' Therefore; I want the 'mask' variable in the above function to select that bottom 6 bits; a hex value of 0x3f... So working backwards to derive mc->shift... mask = (1 << mc->shift) - 1 = 0011 1111b 1 << mc->shift = 0100 0000b mc->shift = 6 Thus, the macro needs to specify xshift=6. As for the other settings... my gain range is -6dB through to 29dB, so those specify the xmin and xmax as being -6 and 29 respectively. As for the mute; we define this using the SOC_DOUBLE_R macro; bit 6 is the mute bit as we can see above. It's also inverted; turning it on, turns the output driver off... and it's a single-bit value. I therefore define the line output driver volume, and its corresponding mute switch as follows: /* Left Line Output Gain Setting Register */ #define AIC3204_LOLGAIN AIC3204_PGREG(1, 18) /* Left Line Output Mute */ #define AIC3204_LOLGAIN_MUTE_SHIFT (6) /* Left Line Output Gain Mask */ #define AIC3204_LOLGAIN_MASK (0x3f) /* Right Line Output Gain Setting Register */ #define AIC3204_LORGAIN AIC3204_PGREG(1, 19) /* Right Line Output Mute */ #define AIC3204_LORGAIN_MUTE_SHIFT (6) /* Right Line Output Gain Mask */ #define AIC3204_LORGAIN_MASK (0x3f) ... SOC_DOUBLE_R_SX_TLV("Line Output Playback Volume", AIC3204_LOLGAIN, AIC3204_LORGAIN, 6, -6, 29, output_stage_tlv), SOC_DOUBLE_R("Line Output Playback Switch", AIC3204_LOLGAIN, AIC3204_LORGAIN, AIC3204_LOLGAIN_MUTE_SHIFT, 0x01, 1), Now, that *mostly* works, except when the gain is set below 0dB, then strange things occur. Did I miss something or is the bug elsewhere? Regards, -- Stuart Longland (aka Redhatter, VK4MSL) .'''. Gentoo Linux/MIPS Cobalt and Docs Developer '.'` : . . . . . . . . . . . . . . . . . . . . . . .'.' http://dev.gentoo.org/~redhatter :.' I haven't lost my mind... ...it's backed up on a tape somewhere. _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel