[alsa-devel] [PATCH v2] ASoC: Add FreeScale SGTL5000 codec suppor

Zeng Zhaoming zhaoming.zeng at freescale.com
Tue Jan 18 03:32:55 CET 2011


On Mon 2011-01-17 15:32:11, Mark Brown wrote:
> On Mon, Jan 17, 2011 at 02:48:54PM +0800, Zeng Zhaoming wrote:
> > v1 url:
> > http://mailman.alsa-project.org/pipermail/alsa-devel/2010-December/034615.html
> >
> > Changes since v1:
> >   - Add DAPM widges as Mark and Arnaud point out.
> >   - cleanup regulator code
> >   - new implement dai_ops functions
> >   - sort configs in Kconfig and Makefile
> 
> > From 1704fd5e7cf2540e247aa8a4f69d81ae5e6cbd4e Mon Sep 17 00:00:00 2001
> > From: Zeng Zhaoming <zengzm.kernel at gmail.com>
> > Date: Mon, 17 Jan 2011 10:34:42 +0800
> 
> Please submit patches in the form documented in SubmittingPatches.  In
> particular please don't send as an attachement, particularly not with
> extraneous stuff before the changelog.
> 
> > +struct sgtl5000_priv {
> > +     int sysclk;     /* sysclk rate */
> > +     int master;     /* i2s master or not? */
> > +     int fmt;        /* i2s data format */
> > +     int lrclk;      /* frame clock rate */
> > +     int capture_channels;   /* the num of channels for capture. */
> > +     int small_pop;  /* hw assistant to eliminate pop */
> 
> It's not clear why you're storing some of this stuff in the private data
> - for example, capture_channels is only referred to in the place where
> it is set (so you may as well use the original value) and small_pop is
> set unconditionally (it looks like the intention was to have platform
> data for it?).
>
yes, you are right. lrclk and capture_channels fields can be remove.
small_pop is a configurable option of platform data.

> > +static int mic_bias_event(struct snd_soc_dapm_widget *w,
> > +     struct snd_kcontrol *kcontrol, int event)
> > +{
> > +     /* change mic bias resistor to 4Kohm */
> > +     snd_soc_write(w->codec, SGTL5000_CHIP_MIC_CTRL, SGTL5000_BIAS_R_4k);
> > +
> > +     return 0;
> > +}
> 
> This seems really odd - this does the write unconditionally regardless
> of event and never turns anything off.  If this can't just be set once
> at startup then a comment explaining why an event is needed would be
> good.
>
the odd code is cuased by:
If this is set to zero the micbias block is powered off, and 4Kohm is common case
for enabled micbias.
0x0 = Powered off
0x1 = 2Kohm
0x2 = 4Kohm
0x3 = 8Kohm
when turn on micbias, it will set to 2Kohm, so we need to change it to 4Kohm after widget powerup.

> > +static int dac_event(struct snd_soc_dapm_widget *w,
> > +     struct snd_kcontrol *kcontrol, int event)
> > +{
> > +     switch (event) {
> > +     case SND_SOC_DAPM_PRE_PMU:
> > +             snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
> > +                     SGTL5000_VAG_POWERUP|SGTL5000_DAC_POWERUP,
> > +                     SGTL5000_VAG_POWERUP|SGTL5000_DAC_POWERUP);
> > +             break;
> > +
> > +     case SND_SOC_DAPM_POST_PMD:
> > +             snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
> > +                     SGTL5000_DAC_POWERUP, 0);
> > +             break;
> 
> Why does the powerdown not disable VAG_POWERUP?  My first thought was
> that VAG_POWERUP ought to be a supply widget...
>

My first version, VAG_POWERUP is a supply widget, but spec says:
The headphone (and/or lineout) powerup should be set BEFORE clearing this bit.

But the powerdown sequence go against it.
So I turn to powerup VAG with DAC, and powerdown it before HP and Line_out.
> > +static int adc_event(struct snd_soc_dapm_widget *w,
> > +     struct snd_kcontrol *kcontrol, int event)
> > +{
> > +     snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
> > +             SGTL5000_ADC_POWERUP,
> > +             SGTL5000_ADC_POWERUP);
> > +
> 
> Again, this is really unclear.
>

ADC powerup be controlled by both CHIP_ANA_POWER and CHIP_DIG_POWER.
It seems we need to set both of them.

> > +/* custom function to get of PCM playback volume */
> > +static int dac_get_volsw(struct snd_kcontrol *kcontrol,
> > +                      struct snd_ctl_elem_value *ucontrol)
> > +{
> > +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> > +     int reg, l, r;
> > +
> > +     reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
> > +     l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
> > +     r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT;
> > +     l = l < 0x3c ? 0x3c : l;
> > +     l = l > 0xfc ? 0xfc : l;
> > +     r = r < 0x3c ? 0x3c : r;
> > +     r = r > 0xfc ? 0xfc : r;
> > +
> > +     /* revert it */
> > +     l = 0xfc - l;
> > +     r = 0xfc - r;
> 
> This is really unclear.  The lack of any comments and use of the ternery
> operator isn't helping...  I'd suggest describing the register semantics
> in a comment.
>
Ok, I will add more useful comment for this.
spec says:
Set the Right channel DAC volume with 0.5017 dB steps from 0 to -90 dB
0x3B and less = Reserved
0x3C = 0 dB
0x3D = -0.5 dB
0xF0 = -90 dB
0xFC and greater = Muted

> It also looks like you're missing some locking.
> 
> > +     l = ucontrol->value.integer.value[0];
> > +     r = ucontrol->value.integer.value[1];
> > +
> > +     l = l < 0 ? 0 : l;
> > +     l = l > 0xfc - 0x3c ? 0xfc - 0x3c : l;
> > +     r = r < 0 ? 0 : r;
> > +     r = r > 0xfc - 0x3c ? 0xfc - 0x3c : r;
> > +     l = 0xfc - l;
> > +     r = 0xfc - r;
> 
> Likewise.
> 
> > +     /* we need SOC_DOUBLE_S8_TLV with invert */
> > +     {
> > +             .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
> > +             .name = "PCM Playback Volume",
> > +             .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
> > +                     SNDRV_CTL_ELEM_ACCESS_READWRITE,
> > +             .info = dac_info_volsw,
> > +             .get = dac_get_volsw,
> > +             .put = dac_put_volsw,
> > +     },
> 
> Perhaps implementing the abstract type would help with the clarity
> issues?
>
> > +     case SND_SOC_DAIFMT_CBM_CFS:
> > +     case SND_SOC_DAIFMT_CBS_CFM:
> > +             return -EINVAL;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> 
> Could just have the default: case.
> 
> > +     if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> > +             sgtl5000->capture_channels = channels;
> > +
> > +             reg = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
> > +             reg |= SGTL5000_ADC_POWERUP;
> > +
> > +             if (sgtl5000->capture_channels == 1)
> > +                     reg &= ~SGTL5000_ADC_STEREO;
> > +             else
> > +                     reg |= SGTL5000_ADC_STEREO;
> > +
> > +             snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, reg);
> > +     }
> 
> snd_soc_update_bits().
> 
> > +     /* get devided factor of frame clock */
> > +     switch (sys_fs / sgtl5000->lrclk) {
> > +     case 4:
> > +             clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT;
> > +             break;
> > +     case 2:
> > +             clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT;
> > +             break;
> > +     default:
> > +             break;
> > +     }
> 
> I'd expect to see this error out if it can't find an appropriate
> divider?

the default means sys_fs / lrclk = 1, not a error.





More information about the Alsa-devel mailing list