[alsa-devel] Update on TLV320AIC3204 Driver

Stuart Longland redhatter at gentoo.org
Tue Jun 15 07:11:10 CEST 2010


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.


More information about the Alsa-devel mailing list