[alsa-devel] [PATCHv4 2/7] ASoC: TWL6030: Add twl6030 codec driver

Olaya, Margarita magi.olaya at ti.com
Thu Feb 25 00:37:44 CET 2010



> -----Original Message-----
> From: Liam Girdwood [mailto:lrg at slimlogic.co.uk]
> Sent: Wednesday, February 24, 2010 4:38 AM
> To: Olaya, Margarita
> Cc: alsa-devel at alsa-project.org; linux-omap at vger.kernel.org; broonie at opensource.wolfsonmicro.com
> Subject: Re: [PATCHv4 2/7] ASoC: TWL6030: Add twl6030 codec driver
>
> On Tue, 2010-02-23 at 18:10 -0600, Olaya, Margarita wrote:
> > From: Misael Lopez Cruz <x0052729 at ti.com>
> >
> > Initial version of TWL6030 codec driver.
> >
> > The TWL6030 codec uses a propietary PDM-based digital audio interface.
> > Audio paths supported are:
> >
> > - Input: Main Mic, Sub Mic, Headset Mic, Auxiliary-FM Left/Right
> > - Output: Headset Left/Right, Handsfree Left/Right
> >
> > Signed-off-by: Misael Lopez Cruz <x0052729 at ti.com>
> > Signed-off-by: Jorge Eduardo Candelaria <jorge.candelaria at ti.com>
> > Signed-off-by: Margarita Olaya Cabrera <magi.olaya at ti.com>
> > ---
> >  sound/soc/codecs/Kconfig   |    4 +
> >  sound/soc/codecs/Makefile  |    2 +
> >  sound/soc/codecs/twl6030.c |  823 ++++++++++++++++++++++++++++++++++++++++++++
> >  sound/soc/codecs/twl6030.h |   94 +++++
> >  4 files changed, 923 insertions(+), 0 deletions(-)
> >  create mode 100644 sound/soc/codecs/twl6030.c
> >  create mode 100644 sound/soc/codecs/twl6030.h
> >
> > diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> > index 52b005f..3b3d739 100644
> > --- a/sound/soc/codecs/Kconfig
> > +++ b/sound/soc/codecs/Kconfig
> > @@ -33,6 +33,7 @@ config SND_SOC_ALL_CODECS
> >         select SND_SOC_TPA6130A2 if I2C
> >         select SND_SOC_TLV320DAC33 if I2C
> >         select SND_SOC_TWL4030 if TWL4030_CORE
> > +       select SND_SOC_TWL6030 if TWL4030_CORE
> >         select SND_SOC_UDA134X
> >         select SND_SOC_UDA1380 if I2C
> >         select SND_SOC_WM8350 if MFD_WM8350
> > @@ -155,6 +156,9 @@ config SND_SOC_TWL4030
> >         select TWL4030_CODEC
> >         tristate
> >
> > +config SND_SOC_TWL6030
> > +       tristate
> > +
> >  config SND_SOC_UDA134X
> >         tristate
> >
> > diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> > index dbaecb1..e11a193 100644
> > --- a/sound/soc/codecs/Makefile
> > +++ b/sound/soc/codecs/Makefile
> > @@ -20,6 +20,7 @@ snd-soc-tlv320aic26-objs := tlv320aic26.o
> >  snd-soc-tlv320aic3x-objs := tlv320aic3x.o
> >  snd-soc-tlv320dac33-objs := tlv320dac33.o
> >  snd-soc-twl4030-objs := twl4030.o
> > +snd-soc-twl6030-objs := twl6030.o
> >  snd-soc-uda134x-objs := uda134x.o
> >  snd-soc-uda1380-objs := uda1380.o
> >  snd-soc-wm8350-objs := wm8350.o
> > @@ -76,6 +77,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC26)     += snd-soc-tlv320aic26.o
> >  obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
> >  obj-$(CONFIG_SND_SOC_TLV320DAC33)      += snd-soc-tlv320dac33.o
> >  obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
> > +obj-$(CONFIG_SND_SOC_TWL6030)  += snd-soc-twl6030.o
> >  obj-$(CONFIG_SND_SOC_UDA134X)  += snd-soc-uda134x.o
> >  obj-$(CONFIG_SND_SOC_UDA1380)  += snd-soc-uda1380.o
> >  obj-$(CONFIG_SND_SOC_WM8350)   += snd-soc-wm8350.o
> > diff --git a/sound/soc/codecs/twl6030.c b/sound/soc/codecs/twl6030.c
> > new file mode 100644
> > index 0000000..8b52aa1
> > --- /dev/null
> > +++ b/sound/soc/codecs/twl6030.c
> > @@ -0,0 +1,823 @@
> > +/*
> > + * ALSA SoC TWL6030 codec driver
> > + *
> > + * Author:      Misael Lopez Cruz <x0052729 at ti.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU General Public License
> > + * version 2 as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful, but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> > + * General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> > + * 02110-1301 USA
> > + *
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/moduleparam.h>
> > +#include <linux/init.h>
> > +#include <linux/delay.h>
> > +#include <linux/pm.h>
> > +#include <linux/i2c.h>
> > +#include <linux/gpio.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/i2c/twl.h>
> > +
> > +#include <sound/core.h>
> > +#include <sound/pcm.h>
> > +#include <sound/pcm_params.h>
> > +#include <sound/soc.h>
> > +#include <sound/soc-dapm.h>
> > +#include <sound/initval.h>
> > +#include <sound/tlv.h>
> > +
> > +#include "twl6030.h"
> > +
> > +#define TWL6030_RATES   (SNDRV_PCM_RATE_96000)
> > +#define TWL6030_FORMATS         (SNDRV_PCM_FMTBIT_S32_LE)
> > +
> > +/* codec private data */
> > +struct twl6030_data {
> > +       struct snd_soc_codec codec;
> > +       int audpwron;
> > +       int codec_powered;
> > +};
> > +
> > +/*
> > + * twl6030 register cache & default register settings
> > + */
> > +static const u8 twl6030_reg[TWL6030_CACHEREGNUM] = {
> > +       0x00, /* not used               0x00    */
> > +       0x4B, /* TWL6030_ASICID (ro)    0x01    */
> > +       0x00, /* TWL6030_ASICREV (ro)   0x02    */
> > +       0x00, /* TWL6030_INTID          0x03    */
> > +       0x7B, /* TWL6030_INTMR          0x04    */
> > +       0x00, /* TWL6030_NCPCTRL        0x05    */
> > +       0x00, /* TWL6030_LDOCTL         0x06    */
> > +       0x00, /* TWL6030_HPPLLCTL       0x07    */
> > +       0x00, /* TWL6030_LPPLLCTL       0x08    */
> > +       0x00, /* TWL6030_LPPLLDIV       0x09    */
> > +       0x00, /* TWL6030_AMICBCTL       0x0A    */
> > +       0x00, /* TWL6030_DMICBCTL       0x0B    */
> > +       0x18, /* TWL6030_MICLCTL        0x0C    */
> > +       0x18, /* TWL6030_MICRCTL        0x0D    */
> > +       0x00, /* TWL6030_MICGAIN        0x0E    */
> > +       0x1B, /* TWL6030_LINEGAIN       0x0F    */
> > +       0x00, /* TWL6030_HSLCTL         0x10    */
> > +       0x00, /* TWL6030_HSRCTL         0x11    */
> > +       0x00, /* TWL6030_HSGAIN         0x12    */
> > +       0x06, /* TWL6030_EARCTL         0x13    */
> > +       0x00, /* TWL6030_HFLCTL         0x14    */
> > +       0x03, /* TWL6030_HFLGAIN        0x15    */
> > +       0x00, /* TWL6030_HFRCTL         0x16    */
> > +       0x03, /* TWL6030_HFRGAIN        0x17    */
> > +       0x00, /* TWL6030_VIBCTLL        0x18    */
> > +       0x00, /* TWL6030_VIBDATL        0x19    */
> > +       0x00, /* TWL6030_VIBCTLR        0x1A    */
> > +       0x00, /* TWL6030_VIBDATR        0x1B    */
> > +       0x00, /* TWL6030_HKCTL1         0x1C    */
> > +       0x00, /* TWL6030_HKCTL2         0x1D    */
> > +       0x00, /* TWL6030_GPOCTL         0x1E    */
> > +       0x00, /* TWL6030_ALB            0x1F    */
> > +       0x00, /* TWL6030_DLB            0x20    */
> > +       0x00, /* not used               0x21    */
> > +       0x00, /* not used               0x22    */
> > +       0x00, /* not used               0x23    */
> > +       0x00, /* not used               0x24    */
> > +       0x00, /* not used               0x25    */
> > +       0x00, /* not used               0x26    */
> > +       0x00, /* not used               0x27    */
> > +       0x00, /* TWL6030_TRIM1          0x28    */
> > +       0x00, /* TWL6030_TRIM2          0x29    */
> > +       0x00, /* TWL6030_TRIM3          0x2A    */
> > +       0x00, /* TWL6030_HSOTRIM        0x2B    */
> > +       0x00, /* TWL6030_HFOTRIM        0x2C    */
> > +       0x09, /* TWL6030_ACCCTL         0x2D    */
> > +       0x00, /* TWL6030_STATUS (ro)    0x2E    */
> > +};
> > +
> > +/*
> > + * twl6030 vio/gnd registers:
> > + * registers under vio/gnd supply can be accessed
> > + * before the power-up sequence, after NRESPWRON goes high
> > + */
> > +static const int twl6030_vio_reg[TWL6030_VIOREGNUM] = {
> > +       TWL6030_REG_ASICID,
> > +       TWL6030_REG_ASICREV,
> > +       TWL6030_REG_INTID,
> > +       TWL6030_REG_INTMR,
> > +       TWL6030_REG_NCPCTL,
> > +       TWL6030_REG_LDOCTL,
> > +       TWL6030_REG_AMICBCTL,
> > +       TWL6030_REG_DMICBCTL,
> > +       TWL6030_REG_HKCTL1,
> > +       TWL6030_REG_HKCTL2,
> > +       TWL6030_REG_GPOCTL,
> > +       TWL6030_REG_TRIM1,
> > +       TWL6030_REG_TRIM2,
> > +       TWL6030_REG_TRIM3,
> > +       TWL6030_REG_HSOTRIM,
> > +       TWL6030_REG_HFOTRIM,
> > +       TWL6030_REG_ACCCTL,
> > +       TWL6030_REG_STATUS,
> > +};
> > +
> > +/*
> > + * twl6030 vdd/vss registers:
> > + * registers under vdd/vss supplies can only be accessed
> > + * after the power-up sequence
> > + */
> > +static const int twl6030_vdd_reg[TWL6030_VDDREGNUM] = {
> > +       TWL6030_REG_HPPLLCTL,
> > +       TWL6030_REG_LPPLLCTL,
> > +       TWL6030_REG_LPPLLDIV,
> > +       TWL6030_REG_MICLCTL,
> > +       TWL6030_REG_MICRCTL,
> > +       TWL6030_REG_MICGAIN,
> > +       TWL6030_REG_LINEGAIN,
> > +       TWL6030_REG_HSLCTL,
> > +       TWL6030_REG_HSRCTL,
> > +       TWL6030_REG_HSGAIN,
> > +       TWL6030_REG_EARCTL,
> > +       TWL6030_REG_HFLCTL,
> > +       TWL6030_REG_HFLGAIN,
> > +       TWL6030_REG_HFRCTL,
> > +       TWL6030_REG_HFRGAIN,
> > +       TWL6030_REG_VIBCTLL,
> > +       TWL6030_REG_VIBDATL,
> > +       TWL6030_REG_VIBCTLR,
> > +       TWL6030_REG_VIBDATR,
> > +       TWL6030_REG_ALB,
> > +       TWL6030_REG_DLB,
> > +};
> > +
> > +/*
> > + * read twl6030 register cache
> > + */
> > +static inline unsigned int twl6030_read_reg_cache(struct snd_soc_codec *codec,
> > +                                               unsigned int reg)
> > +{
> > +       u8 *cache = codec->reg_cache;
> > +
> > +       if (reg >= TWL6030_CACHEREGNUM)
> > +               return -EIO;
> > +
> > +       return cache[reg];
> > +}
> > +
> > +/*
> > + * write twl6030 register cache
> > + */
> > +static inline void twl6030_write_reg_cache(struct snd_soc_codec *codec,
> > +                                               u8 reg, u8 value)
> > +{
> > +       u8 *cache = codec->reg_cache;
> > +
> > +       if (reg >= TWL6030_CACHEREGNUM)
> > +               return;
> > +       cache[reg] = value;
> > +}
> > +
> > +/*
> > + * read from twl6030 hardware register
> > + */
> > +static int twl6030_read(struct snd_soc_codec *codec,
> > +                       unsigned int reg)
> > +{
> > +       u8 value;
> > +
> > +       if (reg >= TWL6030_CACHEREGNUM)
> > +               return -EIO;
> > +
> > +       twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &value, reg);
> > +       twl6030_write_reg_cache(codec, reg, value);
>
>
> You probably want to read from the cache if the register is not
> volatile. This saves a slow I2C read.
>

This function is called for volatile registers; if the register is not volatile we call twl6030_read_reg_cache. I could merge those two functions with a case for those.

>
> > +
> > +       return value;
> > +}
> > +
> > +/*
> > + * write to the twl6030 register space
> > + */
> > +static int twl6030_write(struct snd_soc_codec *codec,
> > +                       unsigned int reg, unsigned int value)
> > +{
> > +       if (reg >= TWL6030_CACHEREGNUM)
> > +               return -EIO;
> > +
> > +       twl6030_write_reg_cache(codec, reg, value);
> > +       return twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
> > +}
> > +
> > +static void twl6030_init_vio_regs(struct snd_soc_codec *codec)
> > +{
> > +       u8 *cache = codec->reg_cache;
> > +       int reg, i;
> > +
> > +       /* allow registers to be accessed by i2c */
> > +       twl6030_write(codec, TWL6030_REG_ACCCTL, cache[TWL6030_REG_ACCCTL]);
> > +
> > +       for (i = 0; i < TWL6030_VIOREGNUM; i++) {
> > +               reg = twl6030_vio_reg[i];
> > +               /* skip read-only registers (ASICID, ASICREV, STATUS) */
> > +               if ((reg == TWL6030_REG_ASICID) ||
> > +                   (reg == TWL6030_REG_ASICREV) ||
> > +                   (reg == TWL6030_REG_STATUS))
> > +                       continue;
> > +               twl6030_write(codec, reg, cache[reg]);
> > +       }
> > +}
> > +
> > +static void twl6030_init_vdd_regs(struct snd_soc_codec *codec)
> > +{
> > +       u8 *cache = codec->reg_cache;
> > +       int reg, i;
> > +
> > +       for (i = 0; i < TWL6030_VDDREGNUM; i++) {
> > +               reg = twl6030_vdd_reg[i];
> > +               twl6030_write(codec, reg, cache[reg]);
> > +       }
> > +}
> > +
>
> Are these two functions writing the default codec register values to the
> CODEC or are these non default (i.e. reset) values. If they are reset
> values is it not better to just issue the reset (and save the slow I2C
> writes) ?

Yeap, but they write non default values to most of the registers.

>
> > +/*
> > + * MICATT volume control:
> > + * from -6 to 0 dB in 6 dB steps
> > + */
> > +static DECLARE_TLV_DB_SCALE(mic_preamp_tlv, -600, 600, 0);
> > +
> > +/*
> > + * MICGAIN volume control:
> > + * from 6 to 30 dB in 6 dB steps
> > + */
> > +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
> > +
> > +/*
> > + * HSGAIN volume control:
> > + * from -30 to 0 dB in 2 dB steps
> > + */
> > +static DECLARE_TLV_DB_SCALE(hs_tlv, -3000, 200, 0);
> > +
> > +/*
> > + * HFGAIN volume control:
> > + * from -52 to 6 dB in 2 dB steps
> > + */
> > +static DECLARE_TLV_DB_SCALE(hf_tlv, -5200, 200, 0);
> > +
> > +/* Left analog microphone selection */
> > +static const char *twl6030_amicl_texts[] =
> > +       {"Headset Mic", "Main Mic", "Aux/FM Left", "Off"};
> > +
> > +/* Right analog microphone selection */
> > +static const char *twl6030_amicr_texts[] =
> > +       {"Headset Mic", "Sub Mic", "Aux/FM Right", "Off"};
> > +
> > +static const struct soc_enum twl6030_enum[] = {
> > +       SOC_ENUM_SINGLE(TWL6030_REG_MICLCTL, 3, 3, twl6030_amicl_texts),
> > +       SOC_ENUM_SINGLE(TWL6030_REG_MICRCTL, 3, 3, twl6030_amicr_texts),
> > +};
> > +
> > +static const struct snd_kcontrol_new amicl_control =
> > +       SOC_DAPM_ENUM("Route", twl6030_enum[0]);
> > +
> > +static const struct snd_kcontrol_new amicr_control =
> > +       SOC_DAPM_ENUM("Route", twl6030_enum[1]);
> > +
> > +/* Headset DAC playback switches */
> > +static const struct snd_kcontrol_new hsdacl_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HSLCTL, 5, 1, 0);
> > +
> > +static const struct snd_kcontrol_new hsdacr_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HSRCTL, 5, 1, 0);
> > +
> > +/* Handsfree DAC playback switches */
> > +static const struct snd_kcontrol_new hfdacl_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HFLCTL, 2, 1, 0);
> > +
> > +static const struct snd_kcontrol_new hfdacr_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HFRCTL, 2, 1, 0);
> > +
> > +/* Headset driver switches */
> > +static const struct snd_kcontrol_new hsl_driver_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HSLCTL, 2, 1, 0);
> > +
> > +static const struct snd_kcontrol_new hsr_driver_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HSRCTL, 2, 1, 0);
> > +
> > +/* Handsfree driver switches */
> > +static const struct snd_kcontrol_new hfl_driver_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HFLCTL, 4, 1, 0);
> > +
> > +static const struct snd_kcontrol_new hfr_driver_switch_controls =
> > +       SOC_DAPM_SINGLE("Switch", TWL6030_REG_HFRCTL, 4, 1, 0);
> > +
> > +static const struct snd_kcontrol_new twl6030_snd_controls[] = {
> > +       /* Capture gains */
> > +       SOC_DOUBLE_TLV("Capture Preamplifier Volume",
> > +               TWL6030_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),
> > +       SOC_DOUBLE_TLV("Capture Volume",
> > +               TWL6030_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv),
> > +
> > +       /* Playback gains */
> > +       SOC_DOUBLE_TLV("Headset Playback Volume",
> > +               TWL6030_REG_HSGAIN, 0, 4, 0xF, 1, hs_tlv),
> > +       SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
> > +               TWL6030_REG_HFLGAIN, TWL6030_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
> > +
> > +};
> > +
> > +static const struct snd_soc_dapm_widget twl6030_dapm_widgets[] = {
> > +       /* Inputs */
> > +       SND_SOC_DAPM_INPUT("MAINMIC"),
> > +       SND_SOC_DAPM_INPUT("HSMIC"),
> > +       SND_SOC_DAPM_INPUT("SUBMIC"),
> > +       SND_SOC_DAPM_INPUT("AFML"),
> > +       SND_SOC_DAPM_INPUT("AFMR"),
> > +
> > +       /* Outputs */
> > +       SND_SOC_DAPM_OUTPUT("HSOL"),
> > +       SND_SOC_DAPM_OUTPUT("HSOR"),
> > +       SND_SOC_DAPM_OUTPUT("HFL"),
> > +       SND_SOC_DAPM_OUTPUT("HFR"),
> > +
> > +       /* Analog input muxes for the capture amplifiers */
> > +       SND_SOC_DAPM_MUX("Analog Left Capture Route",
> > +                       SND_SOC_NOPM, 0, 0, &amicl_control),
> > +       SND_SOC_DAPM_MUX("Analog Right Capture Route",
> > +                       SND_SOC_NOPM, 0, 0, &amicr_control),
> > +
> > +       /* Analog capture PGAs */
> > +       SND_SOC_DAPM_PGA("MicAmpL",
> > +                       TWL6030_REG_MICLCTL, 0, 0, NULL, 0),
> > +       SND_SOC_DAPM_PGA("MicAmpR",
> > +                       TWL6030_REG_MICRCTL, 0, 0, NULL, 0),
> > +
> > +       /* ADCs */
> > +       SND_SOC_DAPM_ADC("ADC Left", "Left Front Capture",
> > +                       TWL6030_REG_MICLCTL, 2, 0),
> > +       SND_SOC_DAPM_ADC("ADC Right", "Right Front Capture",
> > +                       TWL6030_REG_MICRCTL, 2, 0),
> > +
> > +       /* Microphone bias */
> > +       SND_SOC_DAPM_MICBIAS("Headset Mic Bias",
> > +                       TWL6030_REG_AMICBCTL, 0, 0),
> > +       SND_SOC_DAPM_MICBIAS("Main Mic Bias",
> > +                       TWL6030_REG_AMICBCTL, 4, 0),
> > +       SND_SOC_DAPM_MICBIAS("Digital Mic1 Bias",
> > +                       TWL6030_REG_DMICBCTL, 0, 0),
> > +       SND_SOC_DAPM_MICBIAS("Digital Mic2 Bias",
> > +                       TWL6030_REG_DMICBCTL, 4, 0),
> > +
> > +       /* DACs */
> > +       SND_SOC_DAPM_DAC("HSDAC Left", "Headset Playback",
> > +                       TWL6030_REG_HSLCTL, 0, 0),
> > +       SND_SOC_DAPM_DAC("HSDAC Right", "Headset Playback",
> > +                       TWL6030_REG_HSRCTL, 0, 0),
> > +       SND_SOC_DAPM_DAC("HFDAC Left", "Handsfree Playback",
> > +                       TWL6030_REG_HFLCTL, 0, 0),
> > +       SND_SOC_DAPM_DAC("HFDAC Right", "Handsfree Playback",
> > +                       TWL6030_REG_HFRCTL, 0, 0),
> > +
> > +       /* Analog playback switches */
> > +       SND_SOC_DAPM_SWITCH("HSDAC Left Playback",
> > +                       SND_SOC_NOPM, 0, 0, &hsdacl_switch_controls),
> > +       SND_SOC_DAPM_SWITCH("HSDAC Right Playback",
> > +                       SND_SOC_NOPM, 0, 0, &hsdacr_switch_controls),
> > +       SND_SOC_DAPM_SWITCH("HFDAC Left Playback",
> > +                       SND_SOC_NOPM, 0, 0, &hfdacl_switch_controls),
> > +       SND_SOC_DAPM_SWITCH("HFDAC Right Playback",
> > +                       SND_SOC_NOPM, 0, 0, &hfdacr_switch_controls),
> > +
> > +       SND_SOC_DAPM_SWITCH("Headset Left Driver",
> > +                       SND_SOC_NOPM, 0, 0, &hsl_driver_switch_controls),
> > +       SND_SOC_DAPM_SWITCH("Headset Right Driver",
> > +                       SND_SOC_NOPM, 0, 0, &hsr_driver_switch_controls),
> > +       SND_SOC_DAPM_SWITCH("Handsfree Left Driver",
> > +                       SND_SOC_NOPM, 0, 0, &hfl_driver_switch_controls),
> > +       SND_SOC_DAPM_SWITCH("Handsfree Right Driver",
> > +                       SND_SOC_NOPM, 0, 0, &hfr_driver_switch_controls),
> > +
> > +       /* Analog playback PGAs */
> > +       SND_SOC_DAPM_PGA("HFDAC Left PGA",
> > +                       TWL6030_REG_HFLCTL, 1, 0, NULL, 0),
> > +       SND_SOC_DAPM_PGA("HFDAC Right PGA",
> > +                       TWL6030_REG_HFRCTL, 1, 0, NULL, 0),
> > +
> > +};
> > +
> > +static const struct snd_soc_dapm_route intercon[] = {
> > +       /* Capture path */
> > +       {"Analog Left Capture Route", "Headset Mic", "HSMIC"},
> > +       {"Analog Left Capture Route", "Main Mic", "MAINMIC"},
> > +       {"Analog Left Capture Route", "Aux/FM Left", "AFML"},
> > +
> > +       {"Analog Right Capture Route", "Headset Mic", "HSMIC"},
> > +       {"Analog Right Capture Route", "Sub Mic", "SUBMIC"},
> > +       {"Analog Right Capture Route", "Aux/FM Right", "AFMR"},
> > +
> > +       {"MicAmpL", NULL, "Analog Left Capture Route"},
> > +       {"MicAmpR", NULL, "Analog Right Capture Route"},
> > +
> > +       {"ADC Left", NULL, "MicAmpL"},
> > +       {"ADC Right", NULL, "MicAmpR"},
> > +
> > +       /* Headset playback path */
> > +       {"HSDAC Left Playback", "Switch", "HSDAC Left"},
> > +       {"HSDAC Right Playback", "Switch", "HSDAC Right"},
> > +
> > +       {"Headset Left Driver", "Switch", "HSDAC Left Playback"},
> > +       {"Headset Right Driver", "Switch", "HSDAC Right Playback"},
> > +
> > +       {"HSOL", NULL, "Headset Left Driver"},
> > +       {"HSOR", NULL, "Headset Right Driver"},
> > +
> > +       /* Handsfree playback path */
> > +       {"HFDAC Left Playback", "Switch", "HFDAC Left"},
> > +       {"HFDAC Right Playback", "Switch", "HFDAC Right"},
> > +
> > +       {"HFDAC Left PGA", NULL, "HFDAC Left Playback"},
> > +       {"HFDAC Right PGA", NULL, "HFDAC Right Playback"},
> > +
> > +       {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"},
> > +       {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"},
> > +
> > +       {"HFL", NULL, "Handsfree Left Driver"},
> > +       {"HFR", NULL, "Handsfree Right Driver"},
> > +};
> > +
> > +static int twl6030_add_widgets(struct snd_soc_codec *codec)
> > +{
> > +       snd_soc_dapm_new_controls(codec, twl6030_dapm_widgets,
> > +                                ARRAY_SIZE(twl6030_dapm_widgets));
> > +
> > +       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
> > +
> > +       snd_soc_dapm_new_widgets(codec);
> > +
> > +       return 0;
> > +}
> > +
> > +static int twl6030_set_bias_level(struct snd_soc_codec *codec,
> > +                               enum snd_soc_bias_level level)
> > +{
> > +       struct twl6030_data *priv = codec->private_data;
> > +       int audpwron = priv->audpwron;
> > +
> > +       switch (level) {
> > +       case SND_SOC_BIAS_ON:
> > +       case SND_SOC_BIAS_PREPARE:
> > +       case SND_SOC_BIAS_STANDBY:
> > +               if (priv->codec_powered)
> > +                       break;
> > +
> > +               if (gpio_is_valid(audpwron)) {
> > +                       /* use AUDPWRON line */
> > +                       gpio_set_value(audpwron, 1);
> > +
> > +                       /* power-up sequence latency */
> > +                       mdelay(16);
>
> This is perhaps too long for mdelay. Probably want to sleep here.

Ok, I'll change it.

Regards,
Margarita
>
> Thanks
>
> Liam
>
> --
> Freelance Developer, SlimLogic Ltd
> ASoC and Voltage Regulator Maintainer.
> http://www.slimlogic.co.uk



More information about the Alsa-devel mailing list