This codec driver adds support for Asahi Kasei AK5702.
Signed-off-by: Paolo Doz <paolo.doz(a)gmail.com>
---
include/sound/ak5702.h | 27 +++
sound/soc/codecs/Kconfig | 4 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/ak5702.c | 507 +++++++++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/ak5702.h | 171 +++++++++++++++
5 files changed, 711 insertions(+)
diff --git a/include/sound/ak5702.h b/include/sound/ak5702.h
new file mode 100644
index 0000000..8c0d4e9
--- /dev/null
+++ b/include/sound/ak5702.h
@@ -0,0 +1,27 @@
+/*
+ * AK5702 ALSA SoC Codec driver
+ *
+ * Author: Paolo Doz <paolo.doz(a)gmail.com>
+ * Copyright: (C) 2012 Soft In S.r.l.
+ *
+ * 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.
+ */
+
+#ifndef __AK5702_H
+#define __AK5702_H
+
+/**
+ * enum ak5702_clock mode
+ */
+
+enum ak5702_clock_mode {
+ AK5702_CLKPLL_SLAVE,
+ AK5702_CLKPLL_SLAVE2,
+ AK5702_CLKPLL_MASTER,
+ AK5702_CLKEXT_SLAVE,
+ AK5702_CLKEXT_MASTER,
+};
+
+#endif /* __AK5702_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 3a84782..d8156c7 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -26,6 +26,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_AK4641 if I2C
select SND_SOC_AK4642 if I2C
select SND_SOC_AK4671 if I2C
+ select SND_SOC_AK5702 if I2C
select SND_SOC_ALC5623 if I2C
select SND_SOC_ALC5632 if I2C
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
@@ -202,6 +203,9 @@ config SND_SOC_AK4642
config SND_SOC_AK4671
tristate
+config SND_SOC_AK5702
+ tristate
+
config SND_SOC_ALC5623
tristate
config SND_SOC_ALC5632
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f6e8e36..1f88602 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -14,6 +14,7 @@ snd-soc-ak4535-objs := ak4535.o
snd-soc-ak4641-objs := ak4641.o
snd-soc-ak4642-objs := ak4642.o
snd-soc-ak4671-objs := ak4671.o
+snd-soc-ak5702-objs := ak5702.o
snd-soc-arizona-objs := arizona.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs42l51-objs := cs42l51.o
@@ -136,6 +137,7 @@ obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o
obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o
obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_AK5702) += snd-soc-ak5702.o
obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
diff --git a/sound/soc/codecs/ak5702.c b/sound/soc/codecs/ak5702.c
new file mode 100644
index 0000000..9d35062
--- /dev/null
+++ b/sound/soc/codecs/ak5702.c
@@ -0,0 +1,507 @@
+/*
+ * ak5702.c -- AK5702 Soc Audio driver
+ *
+ * Author: Paolo Doz <paolo.doz(a)gmail.com>
+ * Copyright: (C) 2012 Soft In S.r.l.
+ * 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 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 code comes from the original Freescale Ak5702 Audio driver
+ */
+
+/* #define DEBUG 1 */
+
+#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 <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 <linux/slab.h>
+#include <sound/tlv.h>
+#include <linux/regmap.h>
+
+#include <sound/ak5702.h>
+#include "ak5702.h"
+
+#define AK5702_VERSION "0.5"
+#define DRV_NAME "ak5702"
+
+/* codec private data */
+struct ak5702_priv {
+ struct regmap *regmap;
+ enum ak5702_clock_mode clock_mode;
+};
+
+/*
+ * ak5702 register cache
+ */
+static const struct reg_default ak5702_reg_defaults[] = {
+ { 0, 0x0000}, /* R0 - Power Management */
+ { 1, 0x0024}, /* R1 - PLL Control */
+ { 2, 0x0000}, /* R2 - Signal Select */
+ { 3, 0x0001}, /* R3 - Mic Gain Control */
+ { 4, 0x0023}, /* R4 - Audio Format Select */
+ { 5, 0x001F}, /* R5 - fs Select */
+ { 6, 0x0000}, /* R6 - Clock Output Select */
+ { 7, 0x0001}, /* R7 - Volume Control */
+ { 8, 0x0091}, /* R8 - Lch Input Volume Control */
+ { 9, 0x0091}, /* R9 - Rch Input Volume Control */
+ { 10, 0x0000}, /* R10 - Timer Select */
+ { 11, 0x00E1}, /* R11 - ALC Mode Control 1 */
+ { 12, 0x0000}, /* R12 - ALC Mode Control 2 */
+ { 13, 0x00A0}, /* R13 - Mode Control 1 */
+ { 14, 0x0000}, /* R14 - Mode Control 2 */
+ { 15, 0x0000}, /* R15 - Mode Control 3 */
+ { 16, 0x0000}, /* R16 - Power Management */
+ { 17, 0x0000}, /* R17 - PLL Control */
+ { 18, 0x0000}, /* R18 - Signal Select */
+ { 19, 0x0001}, /* R19 - Mic Gain Control */
+ { 20, 0x0020}, /* R20 - Audio Format Select */
+ { 21, 0x0000}, /* R21 - fs Select */
+ { 22, 0x0000}, /* R22 - Clock Output Select */
+ { 23, 0x0001}, /* R23 - Volume Control */
+ { 24, 0x0091}, /* R24 - Lch Input Volume Control */
+ { 25, 0x0091}, /* R25 - Rch Input Volume Control */
+ { 26, 0x0000}, /* R26 - Timer Select */
+ { 27, 0x00E1}, /* R27 - ALC Mode Control 1 */
+ { 28, 0x0000}, /* R28 - ALC Mode Control 2 */
+ { 29, 0x0000}, /* R29 - Mode Control 1 */
+ { 30, 0x0000}, /* R30 - Mode Control 2 */
+};
+
+static bool ak5702_writeable(struct device *dev, unsigned int reg)
+{
+ return reg < AK5702_MAX_REGISTER;
+}
+
+static const unsigned int mic_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(0, 1500, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(in_tlv, -5400, 37, 1);
+
+static const char *ak5702_adca_left12_input[] = { "Left1", "Left2" };
+static const char *ak5702_adca_right12_input[] = { "Right1", "Right2" };
+static const char *ak5702_adca_left5_input[] = { "Left1/2", "Left5" };
+static const char *ak5702_adca_right5_input[] = { "Right1/2", "Right5" };
+
+static const char *ak5702_adcb_left12_input[] = { "Left3", "Left4" };
+static const char *ak5702_adcb_right12_input[] = { "Right3", "Right4" };
+static const char *ak5702_adcb_left5_input[] = { "Left3-4", "Left5" };
+static const char *ak5702_adcb_right5_input[] = { "Right3-4", "Right5" };
+
+static const struct soc_enum ak5702_enum[] = {
+ SOC_ENUM_SINGLE(AK5702_SIG1, 0, 2, ak5702_adca_left12_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 1, 2, ak5702_adca_right12_input),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 0, 2, ak5702_adcb_left12_input),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 1, 2, ak5702_adcb_right12_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 5, 2, ak5702_adca_left5_input),
+ SOC_ENUM_SINGLE(AK5702_SIG1, 6, 2, ak5702_adca_right5_input),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 5, 2, ak5702_adcb_left5_input),
+ SOC_ENUM_SINGLE(AK5702_SIG2, 6, 2, ak5702_adcb_right5_input),
+};
+
+static const struct snd_kcontrol_new ak5702_snd_controls[] = {
+ SOC_ENUM("ADCA Left1/2 Capture Source", ak5702_enum[0]),
+ SOC_ENUM("ADCA Right1/2 Capture Source", ak5702_enum[1]),
+ SOC_ENUM("ADCB Left1/2 Capture Source", ak5702_enum[2]),
+ SOC_ENUM("ADCB Right1/2 Capture Source", ak5702_enum[3]),
+ SOC_ENUM("ADCA Left5 Capture Source", ak5702_enum[4]),
+ SOC_ENUM("ADCA Right5 Capture Source", ak5702_enum[5]),
+ SOC_ENUM("ADCB Left5 Capture Source", ak5702_enum[6]),
+ SOC_ENUM("ADCB Right5 Capture Source", ak5702_enum[7]),
+ SOC_SINGLE_TLV("Mic A Boost Gain", AK5702_MICG1, 0, 3, 0, mic_tlv),
+ SOC_SINGLE_TLV("Mic B Boost Gain", AK5702_MICG2, 0, 3, 0, mic_tlv),
+ SOC_DOUBLE_R_TLV("ADCA Capture Volume", AK5702_LVOL1, AK5702_RVOL1, 0,
+ 241, 0, in_tlv),
+ SOC_DOUBLE_R_TLV("ADCB Capture Volume", AK5702_LVOL2, AK5702_RVOL2, 0,
+ 241, 0, in_tlv),
+};
+
+/* ak5702 dapm widgets */
+static const struct snd_soc_dapm_widget ak5702_dapm_widgets[] = {
+ SND_SOC_DAPM_ADC("ADCA Left", "Capture", AK5702_PM1, 0, 0),
+ SND_SOC_DAPM_ADC("ADCA Right", "Capture", AK5702_PM1, 1, 0),
+ SND_SOC_DAPM_ADC("ADCB Left", "Capture", AK5702_PM2, 0, 0),
+ SND_SOC_DAPM_ADC("ADCB Right", "Capture", AK5702_PM2, 1, 0),
+
+ SND_SOC_DAPM_INPUT("ADCA Left Input"),
+ SND_SOC_DAPM_INPUT("ADCA Right Input"),
+ SND_SOC_DAPM_INPUT("ADCB Left Input"),
+ SND_SOC_DAPM_INPUT("ADCB Right Input"),
+};
+
+static const struct snd_soc_dapm_route ak5702_dapm_routes[] = {
+ {"ADCA Left", NULL, "ADCA Left Input"},
+ {"ADCA Right", NULL, "ADCA Right Input"},
+ {"ADCB Left", NULL, "ADCB Left Input"},
+ {"ADCB Right", NULL, "ADCB Right Input"},
+};
+
+static int ak5702_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct ak5702_priv *ak5702 = snd_soc_codec_get_drvdata(codec);
+ u8 value;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ /* power on ADCA */
+ value = AK5702_PM1_PMADAL |
+ AK5702_PM1_PMADAR |
+ AK5702_PM1_PMVCM;
+ snd_soc_write(codec, AK5702_PM1, value);
+ /* power up ADCB */
+ value = AK5702_PM2_PMADBL | AK5702_PM2_PMADBR;
+ snd_soc_write(codec, AK5702_PM2, value);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ regcache_sync(ak5702->regmap);
+ /* power down ADCA */
+ value = AK5702_POWERDOWN | AK5702_PM1_PMVCM;
+ snd_soc_write(codec, AK5702_PM1, value);
+ /* power down ADCB */
+ value = AK5702_POWERDOWN;
+ snd_soc_write(codec, AK5702_PM2, value);
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* power down */
+ value = AK5702_POWERDOWN;
+ snd_soc_write(codec, AK5702_PM1, value);
+ value = AK5702_POWERDOWN;
+ snd_soc_write(codec, AK5702_PM2, value);
+ regcache_mark_dirty(ak5702->regmap);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+static int ak5702_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ u8 value;
+ struct snd_soc_codec *codec = dai->codec;
+ struct ak5702_priv *ak5702 = snd_soc_codec_get_drvdata(codec);
+
+ pr_debug("Entered %s\n", __func__);
+
+ switch (params_channels(params)) {
+ case 2:
+ snd_soc_update_bits(codec, AK5702_FMT1, AK5702_FMT1_TDM_MASK,
+ AK5702_FMT1_STEREO);
+ break;
+ case 8:
+ if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+ value = AK5702_FMT1_TDM128;
+ else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+ value = AK5702_FMT1_TDM256;
+ else
+ return -EINVAL;
+ snd_soc_update_bits(codec, AK5702_FMT1, AK5702_FMT1_TDM_MASK,
+ value);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 48000:
+ if (ak5702->clock_mode == AK5702_CLKPLL_SLAVE2)
+ value = AK5702_FS1_BCLK_MODE2;
+ else if (ak5702->clock_mode == AK5702_CLKEXT_SLAVE)
+ value = AK5702_FS1_SLAVE_256FS; /* mode 3 256fs*/
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, AK5702_FS1,
+ AK5702_FS1_FS_MASK | AK5702_FS1_BCKO_MASK, value);
+ return 0;
+}
+
+static int ak5702_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;
+ struct ak5702_priv *ak5702 = snd_soc_codec_get_drvdata(codec);
+ u8 value = 0;
+
+ pr_debug("Entered %s\n", __func__);
+
+ switch (clk_id) {
+ case AK5702_CLKPLL_MASTER:
+ case AK5702_CLKEXT_MASTER:
+ if (dir != SND_SOC_CLOCK_OUT)
+ return -EINVAL;
+ break;
+ case AK5702_CLKPLL_SLAVE:
+ case AK5702_CLKPLL_SLAVE2:
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+ value = AK5702_PLL1_POWERUP |
+ AK5702_PLL1_SLAVE |
+ AK5702_PLL1_MODE2;
+ break;
+ case AK5702_CLKEXT_SLAVE:
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+ value = AK5702_PLL1_POWERDOWN | AK5702_PLL1_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_write(codec, AK5702_PLL1, value);
+ ak5702->clock_mode = clk_id;
+ return 0;
+}
+
+static int ak5702_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 value = 0;
+ pr_debug("Entered %s", __func__);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_I2S:
+ value = AK5702_FMT1_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ value = AK5702_FMT1_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, AK5702_FMT1, AK5702_FMT1_FMT_MASK, value);
+ snd_soc_write(codec, AK5702_FMT2, AK5702_FMT2_STEREO);
+ return 0;
+}
+
+static int ak5702_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 reg = 0;
+ pr_debug("Entered %s", __func__);
+ reg = snd_soc_read(codec, AK5702_PLL1);
+ switch (pll_id) {
+ case AK5702_PLL_POWERDOWN:
+ reg &= (~AK5702_PLL1_PM_MASK);
+ reg |= AK5702_PLL1_POWERDOWN;
+ break;
+ case AK5702_PLL_MASTER:
+ reg &= (~AK5702_PLL1_MODE_MASK);
+ reg |= AK5702_PLL1_MASTER | AK5702_PLL1_POWERUP;
+ break;
+ case AK5702_PLL_SLAVE:
+ reg &= (~AK5702_PLL1_MODE_MASK);
+ reg |= AK5702_PLL1_SLAVE | AK5702_PLL1_POWERUP;
+ break;
+ default:
+ return -ENODEV;
+ }
+ reg &= (~AK5702_PLL1_PLL_MASK);
+ switch (freq_in) {
+ case 11289600:
+ reg |= AK5702_PLL1_11289600;
+ break;
+ case 12000000:
+ reg |= AK5702_PLL1_12000000;
+ break;
+ case 12288000:
+ reg |= AK5702_PLL1_12288000;
+ break;
+ case 19200000:
+ reg |= AK5702_PLL1_19200000;
+ break;
+ default:
+ return -ENODEV;
+ }
+ snd_soc_write(codec, AK5702_PLL1, reg);
+ return 0;
+}
+
+static int ak5702_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+ int div_id, int div)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u8 value = 0;
+ pr_debug("Entered %s", __func__);
+ if (div_id == AK5702_BCLK_CLKDIV) {
+ switch (div) {
+ case AK5702_BCLK_DIV_32:
+ value = AK5702_FS1_BCKO_32FS;
+ break;
+ case AK5702_BCLK_DIV_64:
+ value = AK5702_FS1_BCKO_64FS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ snd_soc_update_bits(codec, AK5702_FS1, AK5702_FS1_BCKO_MASK, value);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ak5702_suspend(struct snd_soc_codec *codec)
+{
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static int ak5702_resume(struct snd_soc_codec *codec)
+{
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return 0;
+}
+#else
+#define ak5702_suspend NULL
+#define ak5702_resume NULL
+#endif
+
+static const struct snd_soc_dai_ops ak5702_ops = {
+ .set_sysclk = ak5702_set_dai_sysclk,
+ .set_pll = ak5702_set_dai_pll,
+ .set_clkdiv = ak5702_set_dai_clkdiv,
+ .set_fmt = ak5702_set_dai_fmt,
+ .hw_params = ak5702_dai_hw_params,
+};
+
+struct snd_soc_dai_driver ak5702_dai = {
+ .name = "ak5702-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &ak5702_ops,
+};
+
+static int ak5702_probe(struct snd_soc_codec *codec)
+{
+ struct ak5702_priv *ak5702 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+ dev_info(codec->dev, "AK5702 Audio Codec %s", AK5702_VERSION);
+
+ codec->control_data = ak5702->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+ if (ret < 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ return ret;
+}
+
+static int ak5702_remove(struct snd_soc_codec *codec)
+{
+ ak5702_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ return 0;
+}
+
+static const struct regmap_config ak5702_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = AK5702_MAX_REGISTER,
+ .writeable_reg = ak5702_writeable,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = ak5702_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak5702_reg_defaults),
+};
+
+static __devinit int ak5702_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct ak5702_priv *ak5702;
+ int ret;
+ pr_debug("Entered %s", __func__);
+
+ ak5702 = devm_kzalloc(&i2c->dev, sizeof(struct ak5702_priv),
+ GFP_KERNEL);
+ if (!ak5702)
+ return -ENOMEM;
+
+ ak5702->regmap = devm_regmap_init_i2c(i2c, &ak5702_regmap);
+ if (IS_ERR(ak5702->regmap)) {
+ ret = PTR_ERR(ak5702->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(i2c, ak5702);
+
+ ret = snd_soc_register_codec(&i2c->dev,
+ &soc_codec_dev_ak5702, &ak5702_dai, 1);
+ if (ret != 0)
+ dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+
+ return ret;
+}
+
+static __devexit int ak5702_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id ak5702_i2c_id[] = {
+ { "ak5702-codec", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, ak5702_i2c_id);
+
+static struct i2c_driver ak5702_i2c_driver = {
+ .driver = {
+ .name = "ak5702-codec",
+ .owner = THIS_MODULE,
+ },
+ .probe = ak5702_i2c_probe,
+ .remove = __devexit_p(ak5702_i2c_remove),
+ .id_table = ak5702_i2c_id,
+};
+
+struct snd_soc_codec_driver soc_codec_dev_ak5702 = {
+ .probe = ak5702_probe,
+ .remove = ak5702_remove,
+ .suspend = ak5702_suspend,
+ .resume = ak5702_resume,
+ .set_bias_level = ak5702_set_bias_level,
+ .controls = ak5702_snd_controls,
+ .num_controls = ARRAY_SIZE(ak5702_snd_controls),
+ .dapm_widgets = ak5702_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5702_dapm_widgets),
+ .dapm_routes = ak5702_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak5702_dapm_routes),
+};
+
+module_i2c_driver(ak5702_i2c_driver);
+
+MODULE_DESCRIPTION("Asahi Kasei AK5702 ALSA SoC driver");
+MODULE_AUTHOR("Paolo Doz <paolo.doz(a)gmail.com");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak5702.h b/sound/soc/codecs/ak5702.h
new file mode 100644
index 0000000..a492a61
--- /dev/null
+++ b/sound/soc/codecs/ak5702.h
@@ -0,0 +1,171 @@
+/*
+ * ak5702.h -- AK5702 Soc Audio driver
+ *
+ * Author: Paolo Doz <paolo.doz(a)gmail.com>
+ * Copyright: (C) 2012 Soft In S.r.l.
+ * 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * 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 code comes from the original Freescale Ak5702 Audio driver
+ */
+
+#ifndef _AK5702_H
+#define _AK5702_H
+
+/* AK5702 register space */
+
+#define AK5702_PM1 0x00
+#define AK5702_PLL1 0x01
+#define AK5702_SIG1 0x02
+#define AK5702_MICG1 0x03
+#define AK5702_FMT1 0x04
+#define AK5702_FS1 0x05
+#define AK5702_CLK1 0x06
+#define AK5702_VOLCTRL1 0x07
+#define AK5702_LVOL1 0x08
+#define AK5702_RVOL1 0x09
+#define AK5702_TIMER1 0x0A
+#define AK5702_ALC11 0x0B
+#define AK5702_ALC12 0x0C
+#define AK5702_MODE11 0x0D
+#define AK5702_MODE12 0x0E
+#define AK5702_MODE13 0x0F
+
+#define AK5702_PM2 0x10
+#define AK5702_PLL2 0x11
+#define AK5702_SIG2 0x12
+#define AK5702_MICG2 0x13
+#define AK5702_FMT2 0x14
+#define AK5702_FS2 0x15
+#define AK5702_CLK2 0x16
+#define AK5702_VOLCTRL2 0x17
+#define AK5702_LVOL2 0x18
+#define AK5702_RVOL2 0x19
+#define AK5702_TIMER2 0x1A
+#define AK5702_ALC21 0x1B
+#define AK5702_ALC22 0x1C
+#define AK5702_MODE21 0x1D
+#define AK5702_MODE22 0x1E
+
+#define AK5702_MAX_REGISTER 0x1F
+
+#define AK5702_CACHEREGNUM 0x1F
+
+#define AK5702_POWERDOWN 0x0
+#define AK5702_PM1_PMADAL 0x01
+#define AK5702_PM1_PMADAR 0x02
+#define AK5702_PM1_PMVCM 0x04
+#define AK5702_PM2_PMADBL 0x01
+#define AK5702_PM2_PMADBR 0x02
+
+#define AK5702_PLL1_POWERDOWN 0x0
+#define AK5702_PLL1_POWERUP 0x01
+#define AK5702_PLL1_MASTER 0x02
+#define AK5702_PLL1_SLAVE 0x0
+#define AK5702_PLL1_MODE0 0x0
+#define AK5702_PLL1_MODE2 0x08
+#define AK5702_PLL1_MODE3 0x0C
+#define AK5702_PLL1_MODE4 0x10
+#define AK5702_PLL1_MODE5 0x14
+#define AK5702_PLL1_MODE6 0x18
+#define AK5702_PLL1_MODE7 0x1C
+#define AK5702_PLL1_MODE8 0x20
+#define AK5702_PLL1_MODE9 0x24
+#define AK5702_PLL1_MODE12 0x30
+#define AK5702_PLL1_MODE13 0x34
+#define AK5702_PLL1_MODE14 0x38
+#define AK5702_PLL1_MODE15 0x3C
+#define AK5702_PLL1_11289600 0x10
+#define AK5702_PLL1_12000000 0x24
+#define AK5702_PLL1_12288000 0x14
+#define AK5702_PLL1_19200000 0x20
+
+#define AK5702_FS1_MCKI_MODE0 0x0
+#define AK5702_FS1_MCKI_MODE1 0x01
+#define AK5702_FS1_MCKI_MODE2 0x02
+#define AK5702_FS1_MCKI_MODE3 0x03
+#define AK5702_FS1_MCKI_MODE4 0x04
+#define AK5702_FS1_MCKI_MODE5 0x05
+#define AK5702_FS1_MCKI_MODE6 0x06
+#define AK5702_FS1_MCKI_MODE7 0x07
+#define AK5702_FS1_MCKI_MODE10 0x0A
+#define AK5702_FS1_MCKI_MODE11 0x0B
+#define AK5702_FS1_MCKI_MODE14 0x0E
+#define AK5702_FS1_MCKI_MODE15 0x0F
+
+#define AK5702_FS1_BCLK_MODE0 0x0
+#define AK5702_FS1_BCLK_MODE1 0x04
+#define AK5702_FS1_BCLK_MODE2 0x08
+
+#define AK5702_FS1_SLAVE_1024FS 0x01
+#define AK5702_FS1_SLAVE_512FS 0x02
+#define AK5702_FS1_SLAVE_256FS 0x03
+
+#define AK5702_SIG1_L_LIN1 0x0
+#define AK5702_SIG1_L_LIN2 0x01
+#define AK5702_SIG1_R_RIN1 0x0
+#define AK5702_SIG1_R_RIN2 0x02
+#define AK5702_SIG1_PMMPA 0x10
+#define AK5702_SIG1_L_LIN5 0x20
+#define AK5702_SIG1_R_RIN5 0x40
+#define AK5702_SIG2_L_LIN3 0x0
+#define AK5702_SIG2_L_LIN4 0x01
+#define AK5702_SIG2_R_RIN3 0x0
+#define AK5702_SIG2_R_RIN4 0x02
+#define AK5702_SIG2_PMMPB 0x10
+
+#define AK5702_MICGAIN_0DB 0x0
+
+#define AK5702_FMT1_I2S 0x03
+#define AK5702_FMT1_MSB 0x02
+#define AK5702_FMT1_STEREO 0x00
+#define AK5702_FMT1_TDM128 0x60
+#define AK5702_FMT1_TDM256 0xE0
+#define AK5702_FMT2_STEREO 0x20
+#define AK5702_FS1_BCKO_32FS 0x10
+#define AK5702_FS1_BCKO_64FS 0x20
+#define AK5702_CLK1_PS_256FS 0x0
+#define AK5702_CLK1_PS_128FS 0x01
+#define AK5702_CLK1_PS_64FS 0x02
+#define AK5702_CLK1_PS_32FS 0x03
+#define AK5702_VOLCTRL_INDEP 0x0
+#define AK5702_VOLCTRL_DEP 0x01
+#define AK5702_VOL_INIT 0x91
+
+#define AK5702_PM1_MASK 0x07
+#define AK5702_PM2_MASK 0x03
+#define AK5702_PLL1_PM_MASK 0x01
+#define AK5702_PLL1_MODE_MASK 0x02
+#define AK5702_PLL1_PLL_MASK 0x3C
+#define AK5702_FS1_BCKO_MASK 0x30
+#define AK5702_FS1_FS_MASK 0x0F
+#define AK5702_CLK1_PS_MASK 0x03
+#define AK5702_FMT1_FMT_MASK 0x03
+#define AK5702_FMT1_TDM_MASK 0xC0
+#define AK5702_FMT2_OUT_MASK 0x10
+/* clock divider id */
+#define AK5702_BCLK_CLKDIV 0
+#define AK5702_MCLK_CLKDIV 1
+
+/* bit clock div values */
+#define AK5702_BCLK_DIV_32 0
+#define AK5702_BCLK_DIV_64 1
+
+/* m clock div values */
+#define AK5702_MCLK_DIV_32 0
+#define AK5702_MCLK_DIV_64 1
+#define AK5702_MCLK_DIV_128 2
+#define AK5702_MCLK_DIV_256 3
+
+/* PLL master and slave modes */
+#define AK5702_PLL_POWERDOWN 0
+#define AK5702_PLL_MASTER 1
+#define AK5702_PLL_SLAVE 2
+
+extern struct snd_soc_dai_driver ak5702_dai;
+extern struct snd_soc_codec_driver soc_codec_dev_ak5702;
+
+#endif