All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver
@ 2009-09-14 17:00 Lopez Cruz, Misael
  2009-09-14 17:21 ` Mark Brown
  0 siblings, 1 reply; 6+ messages in thread
From: Lopez Cruz, Misael @ 2009-09-14 17:00 UTC (permalink / raw)
  To: alsa-devel, linux-omap; +Cc: Mark Brown, Lopez Cruz, Misael, Nagalla, Hari

Initial version of TWL6030 codec driver.

The TWL6030 codec uses a propietary digital audio interface
called McPDM. TWL6030 codec has two power modes: low-power
and high-performance, this initial version only supports
high-performance mode.

Signed-off-by: Misael Lopez Cruz <x0052729@ti.com>
---
 include/sound/soc-dai.h    |    3 +
 sound/soc/codecs/Kconfig   |    4 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/twl6030.c |  670 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/twl6030.h |   95 +++++++
 5 files changed, 774 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/codecs/twl6030.c
 create mode 100644 sound/soc/codecs/twl6030.h

diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 352d7ee..8b0d04b 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -35,6 +35,9 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_MSB             SND_SOC_DAIFMT_LEFT_J
 #define SND_SOC_DAIFMT_LSB             SND_SOC_DAIFMT_RIGHT_J

+/* propietary formats */
+#define SND_SOC_DAIFMT_MCPDM           0x10 /* Texas Instruments McPDM */
+
 /*
  * DAI Clock gating.
  *
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index bbc97fd..0effb52 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -25,6 +25,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_TLV320AIC26 if SPI_MASTER
        select SND_SOC_TLV320AIC3X if I2C
        select SND_SOC_TWL4030 if TWL4030_CORE
+       select SND_SOC_TWL6030 if TWL6030_CORE
        select SND_SOC_UDA134X
        select SND_SOC_UDA1380 if I2C
        select SND_SOC_WM8350 if MFD_WM8350
@@ -114,6 +115,9 @@ config SND_SOC_TLV320AIC3X
 config SND_SOC_TWL4030
        tristate

+config SND_SOC_TWL6030
+       tristate
+
 config SND_SOC_UDA134X
        tristate

diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 8b75305..b70c8a1 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -13,6 +13,7 @@ snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.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
@@ -50,6 +51,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC23)     += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.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..c5e76fa
--- /dev/null
+++ b/sound/soc/codecs/twl6030.c
@@ -0,0 +1,670 @@
+/*
+ * ALSA SoC TWL6030 codec driver
+ *
+ * Author:      Misael Lopez Cruz <x0052729@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/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_44100 | SNDRV_PCM_RATE_48000)
+#define TWL6030_FORMATS         (SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * 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    */
+       0x41, /* 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    */
+};
+
+/*
+ * 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;
+}
+
+/*
+ * write to the twl6030 register space
+ */
+static int twl6030_write(struct snd_soc_codec *codec,
+                       unsigned int reg, unsigned int value)
+{
+       if ((reg < TWL6030_REG_INTID) || (reg > TWL6030_REG_ACCCTL))
+               /* read-only registers */
+               return -EIO;
+
+       twl6030_write_reg_cache(codec, reg, value);
+       return twl_i2c_write_u8(TWL6030_MODULE_AUDIO, value, reg);
+}
+
+static void twl6030_init_chip(struct snd_soc_codec *codec)
+{
+       u8 *cache = codec->reg_cache;
+       int i;
+
+       /* allow registers to be accessed by i2c */
+       twl6030_write(codec, TWL6030_REG_ACCCTL, cache[TWL6030_REG_ACCCTL]);
+
+       /* set all audio section registers to reasonable defaults */
+       /* avoid read-only (ASICID, ASICREV, STATUS) and non-used registers */
+       for (i = TWL6030_REG_INTMR; i <= TWL6030_REG_DLB; i++)
+               twl6030_write(codec, i, cache[i]);
+       for (i = TWL6030_REG_TRIM1; i < TWL6030_REG_ACCCTL; i++)
+               twl6030_write(codec, i, cache[i]);
+}
+
+static void twl6030_power_up(struct snd_soc_codec *codec)
+{
+       struct snd_soc_device *socdev = codec->socdev;
+       struct twl6030_setup_data *setup = socdev->codec_data;
+
+       setup->codec_enable(1);
+
+       /* sync registers updated during power-up sequence */
+       twl6030_write_reg_cache(codec, TWL6030_REG_NCPCTL, 0x81);
+       twl6030_write_reg_cache(codec, TWL6030_REG_LDOCTL, 0x45);
+       twl6030_write_reg_cache(codec, TWL6030_REG_LPPLLCTL, 0x01);
+}
+
+static void twl6030_power_down(struct snd_soc_codec *codec)
+{
+       struct snd_soc_device *socdev = codec->socdev;
+       struct twl6030_setup_data *setup = socdev->codec_data;
+
+       setup->codec_enable(0);
+
+       /* sync registers updated during power-down sequence */
+       twl6030_write_reg_cache(codec, TWL6030_REG_NCPCTL, 0x00);
+       twl6030_write_reg_cache(codec, TWL6030_REG_LDOCTL, 0x00);
+       twl6030_write_reg_cache(codec, TWL6030_REG_LPPLLCTL, 0x00);
+}
+
+/*
+ * 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 (Attenuator) Volume",
+               TWL6030_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),
+       SOC_DOUBLE_TLV("Capture Amplifier 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 muxers 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)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               twl6030_power_up(codec);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               twl6030_power_up(codec);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               twl6030_power_up(codec);
+               break;
+       case SND_SOC_BIAS_OFF:
+               twl6030_power_down(codec);
+               break;
+       }
+       codec->bias_level = level;
+
+       return 0;
+}
+
+static int twl6030_hw_params(struct snd_pcm_substream *substream,
+                          struct snd_pcm_hw_params *params,
+                          struct snd_soc_dai *dai)
+{
+       int rate, format;
+
+       /* hardware dai (McPDM) requires bit stream of twice
+        * the actual rate, application processor (audio backend)
+        * should upsample accordingly
+        */
+       rate = params_rate(params);
+       switch (rate) {
+       case 44100:
+       case 48000:
+               break;
+       default:
+               dev_err(codec->dev, "unknown rate %d\n", rate);
+               return -EINVAL;
+       }
+
+       /* the sample lenght handled by codec at A-D and D-A
+        * conversion is 16-bits, but the dai requires 32-bits
+        */
+       format = params_format(params);
+       if (format != SNDRV_PCM_FORMAT_S32_LE) {
+               dev_err(codec->dev, "unknown format %d\n", format);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int twl6030_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 hppll, lppll;
+
+       hppll = twl6030_read_reg_cache(codec, TWL6030_REG_HPPLLCTL);
+       hppll &= TWL6030_HPLLRST;
+
+       switch (freq) {
+       case 12000000:
+               /* MCLK input, PLL enabled */
+               hppll = TWL6030_MCLK_12000KHZ
+                       | TWL6030_HPLLSQRBP
+                       | TWL6030_HPLLENA;
+               break;
+       case 19200000:
+               /* MCLK input, PLL disabled */
+               hppll = TWL6030_MCLK_19200KHZ
+                       | TWL6030_HPLLSQRBP
+                       | TWL6030_HPLLBP;
+               break;
+       case 26000000:
+               /* MCLK input, PLL enabled */
+               hppll = TWL6030_MCLK_26000KHZ
+                       | TWL6030_HPLLSQRBP
+                       | TWL6030_HPLLENA;
+               break;
+       case 38400000:
+               /* clk slicer input, PLL disabled */
+               hppll = TWL6030_MCLK_38400KHZ
+                       | TWL6030_HPLLSQRENA
+                       | TWL6030_HPLLBP;
+               break;
+       default:
+               dev_err(codec->dev, "unknown sysclk rate %d\n", freq);
+               return -EINVAL;
+       }
+
+       twl6030_write(codec, TWL6030_REG_HPPLLCTL, hppll);
+
+       /* Disable LPPLL and select HPPLL */
+       lppll = TWL6030_HPLLSEL;
+       twl6030_write(codec, TWL6030_REG_LPPLLCTL, lppll);
+
+       return 0;
+}
+
+static int twl6030_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                               unsigned int fmt)
+{
+       int format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+       /* propietary dai format */
+       if (format != SND_SOC_DAIFMT_MCPDM)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct snd_soc_dai_ops twl6030_dai_ops = {
+       .hw_params      = twl6030_hw_params,
+       .set_sysclk     = twl6030_set_dai_sysclk,
+       .set_fmt        = twl6030_set_dai_fmt,
+};
+
+struct snd_soc_dai twl6030_dai = {
+       .name = "twl6030",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 4,
+               .rates = TWL6030_RATES,
+               .formats = TWL6030_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = TWL6030_RATES,
+               .formats = TWL6030_FORMATS,
+       },
+       .ops = &twl6030_dai_ops,
+};
+EXPORT_SYMBOL_GPL(twl6030_dai);
+
+static int twl6030_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       twl6030_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int twl6030_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       twl6030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       twl6030_set_bias_level(codec, codec->suspend_bias_level);
+       return 0;
+}
+
+/*
+ * initialize the driver
+ */
+static int twl6030_init(struct snd_soc_device *socdev)
+{
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int ret = 0;
+
+       dev_info(codec->dev, "TWL6030 Audio Codec init\n");
+
+       codec->name = "twl6030";
+       codec->owner = THIS_MODULE;
+       codec->read = twl6030_read_reg_cache;
+       codec->write = twl6030_write;
+       codec->set_bias_level = twl6030_set_bias_level;
+       codec->dai = &twl6030_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = ARRAY_SIZE(twl6030_reg);
+       codec->reg_cache = kmemdup(twl6030_reg, sizeof(twl6030_reg),
+                                       GFP_KERNEL);
+       if (codec->reg_cache == NULL)
+               return -ENOMEM;
+
+       /* register pcms */
+       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to create pcms\n");
+               goto pcm_err;
+       }
+
+       /* power on device */
+       twl6030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       twl6030_init_chip(codec);
+
+       snd_soc_add_controls(codec, twl6030_snd_controls,
+                               ARRAY_SIZE(twl6030_snd_controls));
+       twl6030_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card\n");
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       kfree(codec->reg_cache);
+       return ret;
+}
+
+static struct snd_soc_device *twl6030_socdev;
+
+static int twl6030_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+
+       codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+       if (codec == NULL)
+               return -ENOMEM;
+
+       codec->dev = &pdev->dev;
+       socdev->card->codec = codec;
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       twl6030_socdev = socdev;
+       twl6030_init(socdev);
+
+       return 0;
+}
+
+static int twl6030_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       dev_info(codec->dev, "TWL6030 Audio Codec remove\n");
+       twl6030_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+       kfree(codec);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_twl6030 = {
+       .probe = twl6030_probe,
+       .remove = twl6030_remove,
+       .suspend = twl6030_suspend,
+       .resume = twl6030_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl6030);
+
+static int __init twl6030_modinit(void)
+{
+       return snd_soc_register_dai(&twl6030_dai);
+}
+module_init(twl6030_modinit);
+
+static void __exit twl6030_exit(void)
+{
+       snd_soc_unregister_dai(&twl6030_dai);
+}
+module_exit(twl6030_exit);
+
+MODULE_DESCRIPTION("ASoC TWL6030 codec driver");
+MODULE_AUTHOR("Misael Lopez Cruz");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl6030.h b/sound/soc/codecs/twl6030.h
new file mode 100644
index 0000000..a83f9e7
--- /dev/null
+++ b/sound/soc/codecs/twl6030.h
@@ -0,0 +1,95 @@
+/*
+ * ALSA SoC TWL6030 codec driver
+ *
+ * Author:      Misael Lopez Cruz <x0052729@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
+ *
+ */
+
+#ifndef __TWL6030_H__
+#define __TWL6030_H__
+
+#define TWL6030_REG_ASICID             0x01
+#define TWL6030_REG_ASICREV            0x02
+#define TWL6030_REG_INTID              0x03
+#define TWL6030_REG_INTMR              0x04
+#define TWL6030_REG_NCPCTL             0x05
+#define TWL6030_REG_LDOCTL             0x06
+#define TWL6030_REG_HPPLLCTL           0x07
+#define TWL6030_REG_LPPLLCTL           0x08
+#define TWL6030_REG_LPPLLDIV           0x09
+#define TWL6030_REG_AMICBCTL           0x0A
+#define TWL6030_REG_DMICBCTL           0x0B
+#define TWL6030_REG_MICLCTL            0x0C
+#define TWL6030_REG_MICRCTL            0x0D
+#define TWL6030_REG_MICGAIN            0x0E
+#define TWL6030_REG_LINEGAIN           0x0F
+#define TWL6030_REG_HSLCTL             0x10
+#define TWL6030_REG_HSRCTL             0x11
+#define TWL6030_REG_HSGAIN             0x12
+#define TWL6030_REG_EARCTL             0x13
+#define TWL6030_REG_HFLCTL             0x14
+#define TWL6030_REG_HFLGAIN            0x15
+#define TWL6030_REG_HFRCTL             0x16
+#define TWL6030_REG_HFRGAIN            0x17
+#define TWL6030_REG_VIBCTLL            0x18
+#define TWL6030_REG_VIBDATL            0x19
+#define TWL6030_REG_VIBCTLR            0x1A
+#define TWL6030_REG_VIBDATR            0x1B
+#define TWL6030_REG_HKCTL1             0x1C
+#define TWL6030_REG_HKCTL2             0x1D
+#define TWL6030_REG_GPOCTL             0x1E
+#define TWL6030_REG_ALB                        0x1F
+#define TWL6030_REG_DLB                        0x20
+#define TWL6030_REG_TRIM1              0x28
+#define TWL6030_REG_TRIM2              0x29
+#define TWL6030_REG_TRIM3              0x2A
+#define TWL6030_REG_HSOTRIM            0x2B
+#define TWL6030_REG_HFOTRIM            0x2C
+#define TWL6030_REG_ACCCTL             0x2D
+#define TWL6030_REG_STATUS             0x2E
+
+#define TWL6030_CACHEREGNUM            (TWL6030_REG_STATUS + 1)
+
+/* HPPLLCTL (0x07) fields */
+
+#define TWL6030_HPLLENA                        0x01
+#define TWL6030_HPLLRST                        0x02
+#define TWL6030_HPLLBP                 0x04
+#define TWL6030_HPLLSQRENA             0x08
+#define TWL6030_HPLLSQRBP              0x10
+#define TWL6030_MCLK_12000KHZ          (0 << 5)
+#define TWL6030_MCLK_19200KHZ          (1 << 5)
+#define TWL6030_MCLK_26000KHZ          (2 << 5)
+#define TWL6030_MCLK_38400KHZ          (3 << 5)
+#define TWL6030_MCLK_MSK               0x60
+
+/* LPPLLCTL (0x08) fields */
+
+#define TWL6030_LPLLENA                        0x01
+#define TWL6030_LPLLRST                        0x02
+#define TWL6030_LPLLSEL                        0x04
+#define TWL6030_FIN                    0x08
+#define TWL6030_HPLLSEL                        0x10
+
+extern struct snd_soc_dai twl6030_dai;
+extern struct snd_soc_codec_device soc_codec_dev_twl6030;
+
+struct twl6030_setup_data {
+       void (*codec_enable)(int enable);
+};
+
+#endif /* End of __TWL6030_H__ */
--
1.5.4.3

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver
  2009-09-14 17:00 [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver Lopez Cruz, Misael
@ 2009-09-14 17:21 ` Mark Brown
  2009-09-15 17:59   ` Lopez Cruz, Misael
  0 siblings, 1 reply; 6+ messages in thread
From: Mark Brown @ 2009-09-14 17:21 UTC (permalink / raw)
  To: Lopez Cruz, Misael; +Cc: alsa-devel, linux-omap, Nagalla, Hari

On Mon, Sep 14, 2009 at 12:00:25PM -0500, Lopez Cruz, Misael wrote:

> 
> +/* propietary formats */
> +#define SND_SOC_DAIFMT_MCPDM           0x10 /* Texas Instruments McPDM */

This should really be split out into a separate patch.  Are you
absolutely positive that this is a proprietary interface that won't
interoperate with standard PDM?

Also note that your format doesn't match up with the existing numbering
scheme - they're all just 0, 1, 2, 3, ...

> +#define TWL6030_RATES   (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
> +#define TWL6030_FORMATS         (SNDRV_PCM_FMTBIT_S32_LE)

> +static void twl6030_power_up(struct snd_soc_codec *codec)
> +{
> +       struct snd_soc_device *socdev = codec->socdev;
> +       struct twl6030_setup_data *setup = socdev->codec_data;
> +
> +       setup->codec_enable(1);

That's interesting...?

> +       /* Capture gains */
> +       SOC_DOUBLE_TLV("Capture Preamplifier (Attenuator) Volume",
> +               TWL6030_REG_MICGAIN, 6, 7, 1, 1, mic_preamp_tlv),

No need to mention that it's an attenuator.

> +       SOC_DOUBLE_TLV("Capture Amplifier Volume",
> +               TWL6030_REG_MICGAIN, 0, 3, 4, 0, mic_amp_tlv),

Just "Capture Volume", probably.

> +static int twl6030_set_bias_level(struct snd_soc_codec *codec,
> +                               enum snd_soc_bias_level level)
> +{
> +       switch (level) {
> +       case SND_SOC_BIAS_ON:
> +               twl6030_power_up(codec);
> +               break;
> +       case SND_SOC_BIAS_PREPARE:
> +               twl6030_power_up(codec);
> +               break;
> +       case SND_SOC_BIAS_STANDBY:
> +               twl6030_power_up(codec);
> +               break;
> +       case SND_SOC_BIAS_OFF:
> +               twl6030_power_down(codec);
> +               break;

Is there any reason not to just fold these functions into the bias
management?  It looks like the only caller and it'd save jumping around
the file to find stuff.

> +static int twl6030_init(struct snd_soc_device *socdev)
> +{
> +       struct snd_soc_codec *codec = socdev->card->codec;
> +       int ret = 0;
> +
> +       dev_info(codec->dev, "TWL6030 Audio Codec init\n");

The driver should be probing as a platform device rather than using old
style registration - see wm8350 and wm8400 for examples.

> +       /* power on device */
> +       twl6030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
> +
> +       twl6030_init_chip(codec);

Is the the right ordering?  I'd have expected to see the one time init
stuff done prior to bringing up the power for the first time.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RE: [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver
  2009-09-14 17:21 ` Mark Brown
@ 2009-09-15 17:59   ` Lopez Cruz, Misael
  2009-09-15 18:30     ` Mark Brown
  0 siblings, 1 reply; 6+ messages in thread
From: Lopez Cruz, Misael @ 2009-09-15 17:59 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, linux-omap, Nagalla, Hari

Mark Brown wrote:
> On Mon, Sep 14, 2009 at 12:00:25PM -0500, Lopez Cruz, Misael wrote:
> 
>> 
>> +/* propietary formats */
>> +#define SND_SOC_DAIFMT_MCPDM           0x10 /* Texas Instruments
>> McPDM */ 
> 
> This should really be split out into a separate patch.  Are you
> absolutely positive that this is a proprietary interface that won't
> interoperate with standard PDM?

I think channel slot definition won't make it able to interoperate with
other PDM interfaces. But I may be wrong.

>> +static void twl6030_power_up(struct snd_soc_codec *codec) +{
>> +       struct snd_soc_device *socdev = codec->socdev;
>> +       struct twl6030_setup_data *setup = socdev->codec_data; +
>> +       setup->codec_enable(1);
> 
> That's interesting...?

The codec is turned on/off through an external line (i.e. with a gpio).
Then, codec enable is board-dependent.

>> +static int twl6030_set_bias_level(struct snd_soc_codec *codec,
>> +                               enum snd_soc_bias_level level) +{
>> +       switch (level) {
>> +       case SND_SOC_BIAS_ON:
>> +               twl6030_power_up(codec);
>> +               break;
>> +       case SND_SOC_BIAS_PREPARE:
>> +               twl6030_power_up(codec);
>> +               break;
>> +       case SND_SOC_BIAS_STANDBY:
>> +               twl6030_power_up(codec);
>> +               break;
>> +       case SND_SOC_BIAS_OFF:
>> +               twl6030_power_down(codec);
>> +               break;
> 
> Is there any reason not to just fold these functions into the bias
> management?  It looks like the only caller and it'd save
> jumping around the file to find stuff.

For the moment, there is no reason. I thought it was more clear to have
separate power_up/power_down functions, but I can merge them in bias
management function.

>> +       /* power on device */
>> +       twl6030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +
>> +       twl6030_init_chip(codec);
> 
> Is the the right ordering?  I'd have expected to see the one time init
> stuff done prior to bringing up the power for the first time.

Yes, it's the right order. codec chip cannot be initialized if the codec
is not already power-up, registers are not accesible before that.


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver
  2009-09-15 17:59   ` Lopez Cruz, Misael
@ 2009-09-15 18:30     ` Mark Brown
  2009-09-15 22:39       ` Lopez Cruz, Misael
  0 siblings, 1 reply; 6+ messages in thread
From: Mark Brown @ 2009-09-15 18:30 UTC (permalink / raw)
  To: Lopez Cruz, Misael; +Cc: alsa-devel, linux-omap, Nagalla, Hari

On Tue, Sep 15, 2009 at 12:59:44PM -0500, Lopez Cruz, Misael wrote:
> Mark Brown wrote:
> > On Mon, Sep 14, 2009 at 12:00:25PM -0500, Lopez Cruz, Misael wrote:

> >> +/* propietary formats */
> >> +#define SND_SOC_DAIFMT_MCPDM           0x10 /* Texas Instruments
> >> McPDM */ 

> > This should really be split out into a separate patch.  Are you
> > absolutely positive that this is a proprietary interface that won't
> > interoperate with standard PDM?

> I think channel slot definition won't make it able to interoperate with
> other PDM interfaces. But I may be wrong.

I'd not expect full interoperability but I'd expect that at least the
basic PDM support would interoperate happily.  It wouldn't surprise me
if more than one manufacturer came up with the same extension for multi
channel PDM.

> >> +static void twl6030_power_up(struct snd_soc_codec *codec) +{
> >> +       struct snd_soc_device *socdev = codec->socdev;
> >> +       struct twl6030_setup_data *setup = socdev->codec_data; +
> >> +       setup->codec_enable(1);

> > That's interesting...?

> The codec is turned on/off through an external line (i.e. with a gpio).
> Then, codec enable is board-dependent.

Might it make more sense to specify a GPIO line instead, at least by
default?

> >> +static int twl6030_set_bias_level(struct snd_soc_codec *codec,
> >> +                               enum snd_soc_bias_level level) +{
> >> +       switch (level) {
> >> +       case SND_SOC_BIAS_ON:
> >> +               twl6030_power_up(codec);

> > Is there any reason not to just fold these functions into the bias
> > management?  It looks like the only caller and it'd save
> > jumping around the file to find stuff.

> For the moment, there is no reason. I thought it was more clear to have
> separate power_up/power_down functions, but I can merge them in bias
> management function.

It would be helpful since twl6030_power_up() will be called repeatedly
during normal operation as we move STANDBY <-> PREPARE <-> ON so needs
to be checked to ensure that it can safely be called repeatedly.

> >> +       /* power on device */
> >> +       twl6030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +
> >> +       twl6030_init_chip(codec);

> > Is the the right ordering?  I'd have expected to see the one time init
> > stuff done prior to bringing up the power for the first time.

> Yes, it's the right order. codec chip cannot be initialized if the codec
> is not already power-up, registers are not accesible before that.

OK.  Looking at this from another angle, shouldn't the chip init be
rolled into the bias level function to ensure that there aren't any
cases where it is omitted.

It's possible that the core may get facilities to allow more use of
SND_SOC_BIAS_OFF at runtime which would make this more important.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver
  2009-09-15 18:30     ` Mark Brown
@ 2009-09-15 22:39       ` Lopez Cruz, Misael
  2009-09-16 10:17         ` [alsa-devel] " Mark Brown
  0 siblings, 1 reply; 6+ messages in thread
From: Lopez Cruz, Misael @ 2009-09-15 22:39 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, linux-omap, Nagalla, Hari

Mark Brown wrote:
> On Tue, Sep 15, 2009 at 12:59:44PM -0500, Lopez Cruz, Misael wrote:
>> Mark Brown wrote:
>>> On Mon, Sep 14, 2009 at 12:00:25PM -0500, Lopez Cruz, Misael wrote:
> 
>>>> +/* propietary formats */
>>>> +#define SND_SOC_DAIFMT_MCPDM           0x10 /* Texas Instruments
>>>> McPDM */
> 
>>> This should really be split out into a separate patch.  Are you
>>> absolutely positive that this is a proprietary interface that won't
>>> interoperate with standard PDM?
> 
>> I think channel slot definition won't make it able to interoperate
>> with other PDM interfaces. But I may be wrong.
> 
> I'd not expect full interoperability but I'd expect that at least the
> basic PDM support would interoperate happily.  It wouldn't surprise me
> if more than one manufacturer came up with the same extension
> for multi channel PDM.

If that's the case, then a more appropriate name should be chosen.
Or is it fine for you _MCPDM?

I was thinking in adding _OMAP4_MCPDM, but if you think someone 
else can use the same extension, then _OMAP4 should not go in the
name.

>>>> +static void twl6030_power_up(struct snd_soc_codec *codec) +{
>>>> +       struct snd_soc_device *socdev = codec->socdev;
>>>> +       struct twl6030_setup_data *setup = socdev->codec_data; +
>>>> +       setup->codec_enable(1);
> 
>>> That's interesting...?
> 
>> The codec is turned on/off through an external line (i.e. with a
>> gpio). Then, codec enable is board-dependent.
> 
> Might it make more sense to specify a GPIO line instead, at least by
> default? 

Not sure, if the GPIO line is in TWL6030 (mfd) as well then probably
it's fine, which may be the case for now. But isn't it violating
CODEC independency anyway?

If you mean to sustitute the codec_enable function by the GPIO
line, then it opens the possibility to make the CODEC to request and
operate a GPIO line belonging to a different chip, for example
to the application processor.

On the other hand, if a default GPIO line is provided and if it's
not the correct one, the driver will be waiting forever for
power-up sequence to finish (wait_for_completion). Anyway the
wait_for_completion seems too agresive since codec power-up
sequence might fail and boot process will hang.

>>>> +       /* power on device */
>>>> +       twl6030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +
>>>> +       twl6030_init_chip(codec);
> 
>>> Is the the right ordering?  I'd have expected to see the one time
>>> init stuff done prior to bringing up the power for the first time.
> 
>> Yes, it's the right order. codec chip cannot be initialized if the
>> codec is not already power-up, registers are not accesible before
>> that. 
> 
> OK.  Looking at this from another angle, shouldn't the chip init be
> rolled into the bias level function to ensure that there aren't any
> cases where it is omitted. 
> 
> It's possible that the core may get facilities to allow more use of
> SND_SOC_BIAS_OFF at runtime which would make this more important.

Yes, that's true. Some register may get unconfigured when codec goes
off. I'll check for those scenarios.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [alsa-devel] [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver
  2009-09-15 22:39       ` Lopez Cruz, Misael
@ 2009-09-16 10:17         ` Mark Brown
  0 siblings, 0 replies; 6+ messages in thread
From: Mark Brown @ 2009-09-16 10:17 UTC (permalink / raw)
  To: Lopez Cruz, Misael; +Cc: alsa-devel, linux-omap, Nagalla, Hari

On Tue, Sep 15, 2009 at 05:39:09PM -0500, Lopez Cruz, Misael wrote:
> Mark Brown wrote:

> > I'd not expect full interoperability but I'd expect that at least the
> > basic PDM support would interoperate happily.  It wouldn't surprise me
> > if more than one manufacturer came up with the same extension
> > for multi channel PDM.

> If that's the case, then a more appropriate name should be chosen.
> Or is it fine for you _MCPDM?

I'd suggest just plain _PDM.

> >> The codec is turned on/off through an external line (i.e. with a
> >> gpio). Then, codec enable is board-dependent.

> > Might it make more sense to specify a GPIO line instead, at least by
> > default? 

> Not sure, if the GPIO line is in TWL6030 (mfd) as well then probably
> it's fine, which may be the case for now. But isn't it violating
> CODEC independency anyway?

I don't see an abstraction problem - gpiolib can handle this well
enough.

> If you mean to sustitute the codec_enable function by the GPIO
> line, then it opens the possibility to make the CODEC to request and
> operate a GPIO line belonging to a different chip, for example
> to the application processor.

If the CODEC GPIOs are supported via gpiolib that shouldn't be a
problem.  If you specify the GPIO as an int then it'll also be possible
to have invalid values so that cases with the CODEC hard wired to power
up will also be handled.

It just seems easier for boards to have the CODEC do the gpio request
and so on so that it's a simple data value that needs to be set.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2009-09-16 10:17 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-14 17:00 [PATCH 1/3] ASoC: TWL6030: Add twl6030 codec driver Lopez Cruz, Misael
2009-09-14 17:21 ` Mark Brown
2009-09-15 17:59   ` Lopez Cruz, Misael
2009-09-15 18:30     ` Mark Brown
2009-09-15 22:39       ` Lopez Cruz, Misael
2009-09-16 10:17         ` [alsa-devel] " Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.