summaryrefslogtreecommitdiff
path: root/packages/linux/linux-omap/001-sakoman-twl4030-asoc.diff
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-omap/001-sakoman-twl4030-asoc.diff')
-rw-r--r--packages/linux/linux-omap/001-sakoman-twl4030-asoc.diff875
1 files changed, 875 insertions, 0 deletions
diff --git a/packages/linux/linux-omap/001-sakoman-twl4030-asoc.diff b/packages/linux/linux-omap/001-sakoman-twl4030-asoc.diff
new file mode 100644
index 0000000000..d9f028a90c
--- /dev/null
+++ b/packages/linux/linux-omap/001-sakoman-twl4030-asoc.diff
@@ -0,0 +1,875 @@
+From: Steve Sakoman <steve@sakoman.com>
+Date: Thu, 4 Sep 2008 04:32:11 +0000 (-0700)
+Subject: SOUND: SOC: CODECS: Add support for the TWL4030 audio codec
+X-Git-Url: http://www.sakoman.net/cgi-bin/gitweb.cgi?p=linux-omap-2.6.git;a=commitdiff_plain;h=16fc70d77268043edb76946e86a3d776bad26a45
+
+SOUND: SOC: CODECS: Add support for the TWL4030 audio codec
+---
+
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index 1db04a2..2f00e1e 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -50,3 +50,8 @@ config SND_SOC_CS4270_VD33_ERRATA
+ config SND_SOC_TLV320AIC3X
+ tristate
+ depends on I2C
++
++config SND_SOC_TWL4030
++ tristate
++ depends on SND_SOC && I2C
++
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index d7b97ab..a519ced 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -10,6 +10,7 @@ snd-soc-wm9712-objs := wm9712.o
+ snd-soc-wm9713-objs := wm9713.o
+ snd-soc-cs4270-objs := cs4270.o
+ snd-soc-tlv320aic3x-objs := tlv320aic3x.o
++snd-soc-twl4030-objs := twl4030.o
+
+ obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
+ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
+@@ -23,3 +24,4 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
+ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+ obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
+ obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o
++obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
+diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
+new file mode 100644
+index 0000000..683b8a1
+--- /dev/null
++++ b/sound/soc/codecs/twl4030.c
+@@ -0,0 +1,628 @@
++/*
++ * ALSA SoC TWL4030 codec driver
++ *
++ * Author: Steve Sakoman, <steve@sakoman.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/twl4030.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 "twl4030.h"
++
++/*
++ * twl4030 register cache & default register settings
++ */
++static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
++ 0x00, /* this register not used */
++ 0x93, /* REG_CODEC_MODE (0x1) */
++ 0xc3, /* REG_OPTION (0x2) */
++ 0x00, /* REG_UNKNOWN (0x3) */
++ 0x00, /* REG_MICBIAS_CTL (0x4) */
++ 0x24, /* REG_ANAMICL (0x5) */
++ 0x04, /* REG_ANAMICR (0x6) */
++ 0x0a, /* REG_AVADC_CTL (0x7) */
++ 0x00, /* REG_ADCMICSEL (0x8) */
++ 0x00, /* REG_DIGMIXING (0x9) */
++ 0x0c, /* REG_ATXL1PGA (0xA) */
++ 0x0c, /* REG_ATXR1PGA (0xB) */
++ 0x00, /* REG_AVTXL2PGA (0xC) */
++ 0x00, /* REG_AVTXR2PGA (0xD) */
++ 0x01, /* REG_AUDIO_IF (0xE) */
++ 0x00, /* REG_VOICE_IF (0xF) */
++ 0x00, /* REG_ARXR1PGA (0x10) */
++ 0x00, /* REG_ARXL1PGA (0x11) */
++ 0x6c, /* REG_ARXR2PGA (0x12) */
++ 0x6c, /* REG_ARXL2PGA (0x13) */
++ 0x00, /* REG_VRXPGA (0x14) */
++ 0x00, /* REG_VSTPGA (0x15) */
++ 0x00, /* REG_VRX2ARXPGA (0x16) */
++ 0x0c, /* REG_AVDAC_CTL (0x17) */
++ 0x00, /* REG_ARX2VTXPGA (0x18) */
++ 0x00, /* REG_ARXL1_APGA_CTL (0x19) */
++ 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */
++ 0x4b, /* REG_ARXL2_APGA_CTL (0x1B) */
++ 0x4b, /* REG_ARXR2_APGA_CTL (0x1C) */
++ 0x00, /* REG_ATX2ARXPGA (0x1D) */
++ 0x00, /* REG_BT_IF (0x1E) */
++ 0x00, /* REG_BTPGA (0x1F) */
++ 0x00, /* REG_BTSTPGA (0x20) */
++ 0x00, /* REG_EAR_CTL (0x21) */
++ 0x24, /* REG_HS_SEL (0x22) */
++ 0x0a, /* REG_HS_GAIN_SET (0x23) */
++ 0x00, /* REG_HS_POPN_SET (0x24) */
++ 0x00, /* REG_PREDL_CTL (0x25) */
++ 0x00, /* REG_PREDR_CTL (0x26) */
++ 0x00, /* REG_PRECKL_CTL (0x27) */
++ 0x00, /* REG_PRECKR_CTL (0x28) */
++ 0x00, /* REG_HFL_CTL (0x29) */
++ 0x00, /* REG_HFR_CTL (0x2A) */
++ 0x00, /* REG_ALC_CTL (0x2B) */
++ 0x00, /* REG_ALC_SET1 (0x2C) */
++ 0x00, /* REG_ALC_SET2 (0x2D) */
++ 0x00, /* REG_BOOST_CTL (0x2E) */
++ 0x01, /* REG_SOFTVOL_CTL (0x2F) */
++ 0x00, /* REG_DTMF_FREQSEL (0x30) */
++ 0x00, /* REG_DTMF_TONEXT1H (0x31) */
++ 0x00, /* REG_DTMF_TONEXT1L (0x32) */
++ 0x00, /* REG_DTMF_TONEXT2H (0x33) */
++ 0x00, /* REG_DTMF_TONEXT2L (0x34) */
++ 0x00, /* REG_DTMF_TONOFF (0x35) */
++ 0x00, /* REG_DTMF_WANONOFF (0x36) */
++ 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */
++ 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */
++ 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */
++ 0x16, /* REG_APLL_CTL (0x3A) */
++ 0x00, /* REG_DTMF_CTL (0x3B) */
++ 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */
++ 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */
++ 0x00, /* REG_MISC_SET_1 (0x3E) */
++ 0x00, /* REG_PCMBTMUX (0x3F) */
++ 0x00, /* not used (0x40) */
++ 0x00, /* not used (0x41) */
++ 0x00, /* not used (0x42) */
++ 0x00, /* REG_RX_PATH_SEL (0x43) */
++ 0x00, /* REG_VDL_APGA_CTL (0x44) */
++ 0x00, /* REG_VIBRA_CTL (0x45) */
++ 0x00, /* REG_VIBRA_SET (0x46) */
++ 0x00, /* REG_VIBRA_PWM_SET (0x47) */
++ 0x00, /* REG_ANAMIC_GAIN (0x48) */
++ 0x00, /* REG_MISC_SET_2 (0x49) */
++};
++
++static void twl4030_dump_registers(void)
++{
++ int i = 0;
++ u8 data;
++
++ printk(KERN_INFO "TWL 4030 Register dump for Audio Module\n");
++
++ for (i = REG_CODEC_MODE; i <= REG_MISC_SET_2; i++) {
++ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &data, i);
++ printk(KERN_INFO "Register[0x%02x]=0x%02x\n", i, data);
++ }
++}
++
++/*
++ * read twl4030 register cache
++ */
++static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u8 *cache = codec->reg_cache;
++
++ return cache[reg];
++}
++
++/*
++ * write twl4030 register cache
++ */
++static inline void twl4030_write_reg_cache(struct snd_soc_codec *codec,
++ u8 reg, u8 value)
++{
++ u8 *cache = codec->reg_cache;
++
++ if (reg >= TWL4030_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the twl4030 register space
++ */
++static int twl4030_write(struct snd_soc_codec *codec,
++ unsigned int reg, unsigned int value)
++{
++ twl4030_write_reg_cache(codec, reg, value);
++ return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
++}
++
++static void twl4030_init_chip(struct snd_soc_codec *codec)
++{
++ int i;
++ u8 byte;
++
++ /* clear CODECPDZ prior to setting register defaults */
++ twl4030_write(codec, REG_CODEC_MODE,
++ twl4030_reg[REG_CODEC_MODE] & ~CODECPDZ);
++
++ udelay(10);
++
++ /* set all audio section registers to reasonable defaults */
++ for (i = REG_OPTION; i <= REG_MISC_SET_2; i++)
++ twl4030_write(codec, i, twl4030_reg[i]);
++
++}
++
++static const struct snd_kcontrol_new twl4030_snd_controls[] = {
++ SOC_DOUBLE_R("Master Playback Volume",
++ REG_ARXL2PGA, REG_ARXR2PGA,
++ 0, 127, 0),
++ SOC_DOUBLE_R("Capture Volume",
++ REG_ATXL1PGA, REG_ATXR1PGA,
++ 0, 127, 0),
++};
++
++/* add non dapm controls */
++static int twl4030_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&twl4030_snd_controls[i],
++ codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
++ SND_SOC_DAPM_INPUT("INL"),
++ SND_SOC_DAPM_INPUT("INR"),
++
++ SND_SOC_DAPM_OUTPUT("OUTL"),
++ SND_SOC_DAPM_OUTPUT("OUTR"),
++
++ SND_SOC_DAPM_DAC("DACL", "Left Playback", SND_SOC_NOPM, 0, 0),
++ SND_SOC_DAPM_DAC("DACR", "Right Playback", SND_SOC_NOPM, 0, 0),
++
++ SND_SOC_DAPM_ADC("ADCL", "Left Capture", SND_SOC_NOPM, 0, 0),
++ SND_SOC_DAPM_ADC("ADCR", "Right Capture", SND_SOC_NOPM, 0, 0),
++};
++
++static const struct snd_soc_dapm_route intercon[] = {
++ /* outputs */
++ {"OUTL", NULL, "DACL"},
++ {"OUTR", NULL, "DACR"},
++
++ /* inputs */
++ {"ADCL", NULL, "INL"},
++ {"ADCR", NULL, "INR"},
++};
++
++static int twl4030_add_widgets(struct snd_soc_codec *codec)
++{
++ snd_soc_dapm_new_controls(codec, twl4030_dapm_widgets,
++ ARRAY_SIZE(twl4030_dapm_widgets));
++
++ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++static void twl4030_power_up(struct snd_soc_codec *codec)
++{
++ u8 mode, byte, popn, hsgain;
++
++ /* set CODECPDZ to turn on codec */
++ mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE);
++ twl4030_write(codec, REG_CODEC_MODE, mode | CODECPDZ);
++ udelay(10);
++
++ /* initiate offset cancellation */
++ twl4030_write(codec, REG_ANAMICL,
++ twl4030_reg[REG_ANAMICL] | CNCL_OFFSET_START);
++
++ /* wait for offset cancellation to complete */
++ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, REG_ANAMICL);
++ while ((byte & CNCL_OFFSET_START) == CNCL_OFFSET_START)
++ twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
++ &byte, REG_ANAMICL);
++
++ /* anti-pop when changing analog gain */
++ twl4030_write(codec, REG_MISC_SET_1,
++ twl4030_reg[REG_MISC_SET_1] | SMOOTH_ANAVOL_EN);
++
++ /* toggle CODECPDZ as per TRM */
++ twl4030_write(codec, REG_CODEC_MODE, mode & ~CODECPDZ);
++ udelay(10);
++
++ twl4030_write(codec, REG_CODEC_MODE, mode | CODECPDZ);
++ udelay(10);
++
++ /* program anti-pop with bias ramp delay */
++ popn = twl4030_read_reg_cache(codec, REG_HS_POPN_SET);
++ popn &= RAMP_DELAY;
++ popn |= RAMP_DELAY_645MS;
++ twl4030_write(codec, REG_HS_POPN_SET, popn);
++ popn |= VMID_EN;
++ twl4030_write(codec, REG_HS_POPN_SET, popn);
++
++ /* enable output stage and gain setting */
++ hsgain = HSR_GAIN_0DB | HSL_GAIN_0DB;
++ twl4030_write(codec, REG_HS_GAIN_SET, hsgain);
++
++ /* enable anti-pop ramp */
++ popn |= RAMP_EN;
++ twl4030_write(codec, REG_HS_POPN_SET, popn);
++}
++
++static void twl4030_power_down(struct snd_soc_codec *codec)
++{
++ u8 popn, hsgain, mode;
++
++ /* disable anti-pop ramp */
++ popn = twl4030_read_reg_cache(codec, REG_HS_POPN_SET);
++ popn &= ~RAMP_EN;
++ twl4030_write(codec, REG_HS_POPN_SET, popn);
++
++ /* disable output stage and gain setting */
++ hsgain = HSR_GAIN_PWR_DOWN | HSL_GAIN_PWR_DOWN;
++ twl4030_write(codec, REG_HS_GAIN_SET, hsgain);
++
++ /* disable bias out */
++ popn &= ~VMID_EN;
++ twl4030_write(codec, REG_HS_POPN_SET, popn);
++
++ /* power down */
++ mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE);
++ twl4030_write(codec, REG_CODEC_MODE, mode & ~CODECPDZ);
++
++ udelay(10);
++}
++
++static int twl4030_set_bias_level(struct snd_soc_codec *codec,
++ enum snd_soc_bias_level level)
++{
++ switch (level) {
++ case SND_SOC_BIAS_ON:
++ twl4030_power_up(codec);
++ break;
++ case SND_SOC_BIAS_PREPARE:
++ break;
++ case SND_SOC_BIAS_STANDBY:
++ twl4030_power_down(codec);
++ break;
++ case SND_SOC_BIAS_OFF:
++ twl4030_power_down(codec);
++ break;
++ }
++ codec->bias_level = level;
++
++ return 0;
++}
++
++static int twl4030_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u8 mode, old_mode, format, old_format;
++
++
++ /* bit rate */
++ old_mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE) & ~CODECPDZ;
++ mode = old_mode;
++ mode &= ~APLL_RATE;
++ switch (params_rate(params)) {
++ case 44100:
++ mode |= APLL_RATE_44100;
++ break;
++ case 48000:
++ mode |= APLL_RATE_48000;
++ break;
++ default:
++ printk(KERN_ERR "TWL4030 hw params: unknown rate %d\n",
++ params_rate(params));
++ return -EINVAL;
++ }
++
++ if (mode != old_mode) {
++ /* change rate and turn codec back on */
++ twl4030_write(codec, REG_CODEC_MODE, mode);
++ mode |= CODECPDZ;
++ twl4030_write(codec, REG_CODEC_MODE, mode);
++ }
++
++ /* sample size */
++ old_format = twl4030_read_reg_cache(codec, REG_AUDIO_IF);
++ format = old_format;
++ format &= ~DATA_WIDTH;
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ format |= DATA_WIDTH_16S_16W;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ format |= DATA_WIDTH_32S_24W;
++ break;
++ default:
++ printk(KERN_ERR "TWL4030 hw params: unknown format %d\n",
++ params_format(params));
++ return -EINVAL;
++ }
++
++ if (format != old_format) {
++
++ /* clear CODECPDZ before changing format (codec requirement) */
++ mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE);
++ mode &= ~CODECPDZ;
++ twl4030_write(codec, REG_CODEC_MODE, mode);
++
++ /* change format */
++ twl4030_write(codec, REG_AUDIO_IF, format);
++
++ /* set CODECPDZ afterwards */
++ mode |= CODECPDZ;
++ twl4030_write(codec, REG_CODEC_MODE, mode);
++ }
++ return 0;
++}
++
++static int twl4030_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 infreq;
++
++ switch (freq) {
++ case 19200000:
++ infreq = APLL_INFREQ_19200KHZ;
++ break;
++ case 26000000:
++ infreq = APLL_INFREQ_26000KHZ;
++ break;
++ case 38400000:
++ infreq = APLL_INFREQ_38400KHZ;
++ break;
++ default:
++ printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
++ freq);
++ return -EINVAL;
++ }
++
++ infreq |= APLL_EN;
++ twl4030_write(codec, REG_APLL_CTL, infreq);
++
++ return 0;
++}
++
++static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u8 mode, old_format, format;
++
++ /* get format */
++ old_format = twl4030_read_reg_cache(codec, REG_AUDIO_IF);
++ format = old_format;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ format &= ~(AIF_SLAVE_EN);
++ format |= CLK256FS_EN;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ format &= ~(CLK256FS_EN);
++ format |= AIF_SLAVE_EN;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ format &= ~AIF_FORMAT;
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ format |= AIF_FORMAT_CODEC;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (format != old_format) {
++
++ /* clear CODECPDZ before changing format (codec requirement) */
++ mode = twl4030_read_reg_cache(codec, REG_CODEC_MODE);
++ mode &= ~CODECPDZ;
++ twl4030_write(codec, REG_CODEC_MODE, mode);
++
++ /* change format */
++ twl4030_write(codec, REG_AUDIO_IF, format);
++
++ /* set CODECPDZ afterwards */
++ mode |= CODECPDZ;
++ twl4030_write(codec, REG_CODEC_MODE, mode);
++ }
++
++ return 0;
++}
++
++#define TWL4030_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
++#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
++
++struct snd_soc_dai twl4030_dai = {
++ .name = "twl4030",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = TWL4030_RATES,
++ .formats = TWL4030_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = TWL4030_RATES,
++ .formats = TWL4030_FORMATS,},
++ .ops = {
++ .hw_params = twl4030_hw_params,
++ },
++ .dai_ops = {
++ .set_sysclk = twl4030_set_dai_sysclk,
++ .set_fmt = twl4030_set_dai_fmt,
++ }
++};
++EXPORT_SYMBOL_GPL(twl4030_dai);
++
++static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
++
++ return 0;
++}
++
++static int twl4030_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
++ twl4030_set_bias_level(codec, codec->suspend_bias_level);
++ return 0;
++}
++
++/*
++ * initialize the driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++
++static int twl4030_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ printk(KERN_INFO "TWL4030 Audio Codec init \n");
++
++ codec->name = "twl4030";
++ codec->owner = THIS_MODULE;
++ codec->read = twl4030_read_reg_cache;
++ codec->write = twl4030_write;
++ codec->set_bias_level = twl4030_set_bias_level;
++ codec->dai = &twl4030_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = sizeof(twl4030_reg);
++ codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_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) {
++ printk(KERN_ERR "twl4030: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ twl4030_init_chip(codec);
++
++ /* power on device */
++ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
++
++ twl4030_add_controls(codec);
++ twl4030_add_widgets(codec);
++
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "twl4030: 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 *twl4030_socdev;
++
++static int twl4030_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;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ twl4030_socdev = socdev;
++ twl4030_init(socdev);
++
++ return 0;
++}
++
++static int twl4030_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ printk(KERN_INFO "TWL4030 Audio Codec remove\n");
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_twl4030 = {
++ .probe = twl4030_probe,
++ .remove = twl4030_remove,
++ .suspend = twl4030_suspend,
++ .resume = twl4030_resume,
++};
++EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
++
++MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
++MODULE_AUTHOR("Steve Sakoman");
++MODULE_LICENSE("GPL");
+diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
+new file mode 100644
+index 0000000..013dabc
+--- /dev/null
++++ b/sound/soc/codecs/twl4030.h
+@@ -0,0 +1,197 @@
++/*
++ * ALSA SoC TWL4030 codec driver
++ *
++ * Author: Steve Sakoman <steve@sakoman.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 __TWL4030_AUDIO_H__
++#define __TWL4030_AUDIO_H__
++
++#define REG_CODEC_MODE 0x1
++#define REG_OPTION 0x2
++#define REG_UNKNOWN 0x3
++#define REG_MICBIAS_CTL 0x4
++#define REG_ANAMICL 0x5
++#define REG_ANAMICR 0x6
++#define REG_AVADC_CTL 0x7
++#define REG_ADCMICSEL 0x8
++#define REG_DIGMIXING 0x9
++#define REG_ATXL1PGA 0xA
++#define REG_ATXR1PGA 0xB
++#define REG_AVTXL2PGA 0xC
++#define REG_AVTXR2PGA 0xD
++#define REG_AUDIO_IF 0xE
++#define REG_VOICE_IF 0xF
++#define REG_ARXR1PGA 0x10
++#define REG_ARXL1PGA 0x11
++#define REG_ARXR2PGA 0x12
++#define REG_ARXL2PGA 0x13
++#define REG_VRXPGA 0x14
++#define REG_VSTPGA 0x15
++#define REG_VRX2ARXPGA 0x16
++#define REG_AVDAC_CTL 0x17
++#define REG_ARX2VTXPGA 0x18
++#define REG_ARXL1_APGA_CTL 0x19
++#define REG_ARXR1_APGA_CTL 0x1A
++#define REG_ARXL2_APGA_CTL 0x1B
++#define REG_ARXR2_APGA_CTL 0x1C
++#define REG_ATX2ARXPGA 0x1D
++#define REG_BT_IF 0x1E
++#define REG_BTPGA 0x1F
++#define REG_BTSTPGA 0x20
++#define REG_EAR_CTL 0x21
++#define REG_HS_SEL 0x22
++#define REG_HS_GAIN_SET 0x23
++#define REG_HS_POPN_SET 0x24
++#define REG_PREDL_CTL 0x25
++#define REG_PREDR_CTL 0x26
++#define REG_PRECKL_CTL 0x27
++#define REG_PRECKR_CTL 0x28
++#define REG_HFL_CTL 0x29
++#define REG_HFR_CTL 0x2A
++#define REG_ALC_CTL 0x2B
++#define REG_ALC_SET1 0x2C
++#define REG_ALC_SET2 0x2D
++#define REG_BOOST_CTL 0x2E
++#define REG_SOFTVOL_CTL 0x2F
++#define REG_DTMF_FREQSEL 0x30
++#define REG_DTMF_TONEXT1H 0x31
++#define REG_DTMF_TONEXT1L 0x32
++#define REG_DTMF_TONEXT2H 0x33
++#define REG_DTMF_TONEXT2L 0x34
++#define REG_DTMF_TONOFF 0x35
++#define REG_DTMF_WANONOFF 0x36
++#define REG_I2S_RX_SCRAMBLE_H 0x37
++#define REG_I2S_RX_SCRAMBLE_M 0x38
++#define REG_I2S_RX_SCRAMBLE_L 0x39
++#define REG_APLL_CTL 0x3A
++#define REG_DTMF_CTL 0x3B
++#define REG_DTMF_PGA_CTL2 0x3C
++#define REG_DTMF_PGA_CTL1 0x3D
++#define REG_MISC_SET_1 0x3E
++#define REG_PCMBTMUX 0x3F
++#define REG_RX_PATH_SEL 0x43
++#define REG_VDL_APGA_CTL 0x44
++#define REG_VIBRA_CTL 0x45
++#define REG_VIBRA_SET 0x46
++#define REG_VIBRA_PWM_SET 0x47
++#define REG_ANAMIC_GAIN 0x48
++#define REG_MISC_SET_2 0x49
++
++#define TWL4030_CACHEREGNUM (REG_MISC_SET_2 + 1)
++
++/* Bitfield Definitions */
++
++/* CODEC_MODE (0x01) Fields */
++
++#define APLL_RATE 0xF0
++#define APLL_RATE_8000 0x00
++#define APLL_RATE_11025 0x10
++#define APLL_RATE_12000 0x20
++#define APLL_RATE_16000 0x40
++#define APLL_RATE_22050 0x50
++#define APLL_RATE_24000 0x60
++#define APLL_RATE_32000 0x80
++#define APLL_RATE_44100 0x90
++#define APLL_RATE_48000 0xA0
++#define SEL_16K 0x04
++#define CODECPDZ 0x02
++#define OPT_MODE 0x01
++
++/* ANAMICL (0x05) Fields */
++#define CNCL_OFFSET_START 0x80
++#define OFFSET_CNCL_SEL 0x60
++#define OFFSET_CNCL_SEL_ARX1 0x00
++#define OFFSET_CNCL_SEL_ARX2 0x20
++#define OFFSET_CNCL_SEL_VRX 0x40
++#define OFFSET_CNCL_SEL_ALL 0x60
++#define MICAMPL_EN 0x10
++#define CKMIC_EN 0x08
++#define AUXL_EN 0x04
++#define HSMIC_EN 0x02
++#define MAINMIC_EN 0x01
++
++/* ANAMICR (0x06) Fields */
++#define MICAMPR_EN 0x10
++#define AUXR_EN 0x04
++#define SUBMIC_EN 0x01
++
++/* AUDIO_IF (0x0E) Fields */
++
++#define AIF_SLAVE_EN 0x80
++#define DATA_WIDTH 0x60
++#define DATA_WIDTH_16S_16W 0x00
++#define DATA_WIDTH_32S_16W 0x40
++#define DATA_WIDTH_32S_24W 0x60
++#define AIF_FORMAT 0x18
++#define AIF_FORMAT_CODEC 0x00
++#define AIF_FORMAT_LEFT 0x08
++#define AIF_FORMAT_RIGHT 0x10
++#define AIF_FORMAT_TDM 0x18
++#define AIF_TRI_EN 0x04
++#define CLK256FS_EN 0x02
++#define AIF_EN 0x01
++
++/* HS_GAIN_SET (0x23) Fields */
++
++#define HSR_GAIN 0x0C
++#define HSR_GAIN_PWR_DOWN 0x00
++#define HSR_GAIN_PLUS_6DB 0x04
++#define HSR_GAIN_0DB 0x08
++#define HSR_GAIN_MINUS_6DB 0x0C
++#define HSL_GAIN 0x03
++#define HSL_GAIN_PWR_DOWN 0x00
++#define HSL_GAIN_PLUS_6DB 0x01
++#define HSL_GAIN_0DB 0x02
++#define HSL_GAIN_MINUS_6DB 0x03
++
++/* HS_POPN_SET (0x24) Fields */
++
++#define VMID_EN 0x40
++#define EXTMUTE 0x20
++#define RAMP_DELAY 0x1C
++#define RAMP_DELAY_20MS 0x00
++#define RAMP_DELAY_40MS 0x04
++#define RAMP_DELAY_81MS 0x08
++#define RAMP_DELAY_161MS 0x0C
++#define RAMP_DELAY_323MS 0x10
++#define RAMP_DELAY_645MS 0x14
++#define RAMP_DELAY_1291MS 0x18
++#define RAMP_DELAY_2581MS 0x1C
++#define RAMP_EN 0x02
++
++/* APLL_CTL (0x3A) Fields */
++
++#define APLL_EN 0x10
++#define APLL_INFREQ 0x0F
++#define APLL_INFREQ_19200KHZ 0x05
++#define APLL_INFREQ_26000KHZ 0x06
++#define APLL_INFREQ_38400KHZ 0x0F
++
++/* REG_MISC_SET_1 (0x3E) Fields */
++
++#define CLK64_EN 0x80
++#define SCRAMBLE_EN 0x40
++#define FMLOOP_EN 0x20
++#define SMOOTH_ANAVOL_EN 0x02
++#define DIGMIC_LR_SWAP_EN 0x01
++
++extern struct snd_soc_dai twl4030_dai;
++extern struct snd_soc_codec_device soc_codec_dev_twl4030;
++
++#endif /* End of __TWL4030_AUDIO_H__ */