Merge branch 'for-2.6.32' into for-2.6.33
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 6 Oct 2009 15:01:27 +0000 (16:01 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 6 Oct 2009 15:01:27 +0000 (16:01 +0100)
52 files changed:
arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
sound/soc/atmel/playpaq_wm8510.c
sound/soc/blackfin/bf5xx-ad1836.c
sound/soc/blackfin/bf5xx-ad1938.c
sound/soc/blackfin/bf5xx-i2s.c
sound/soc/blackfin/bf5xx-tdm-pcm.c
sound/soc/blackfin/bf5xx-tdm.c
sound/soc/blackfin/bf5xx-tdm.h
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4671.c [new file with mode: 0644]
sound/soc/codecs/ak4671.h [new file with mode: 0644]
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8711.c [new file with mode: 0644]
sound/soc/codecs/wm8711.h [new file with mode: 0644]
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_hubs.c
sound/soc/codecs/wm_hubs.h
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-pcm.h
sound/soc/imx/mx27vis_wm8974.c
sound/soc/pxa/magician.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/zylonite.c
sound/soc/s3c24xx/Kconfig
sound/soc/s3c24xx/Makefile
sound/soc/s3c24xx/neo1973_gta02_wm8753.c
sound/soc/s3c24xx/neo1973_wm8753.c
sound/soc/s3c24xx/s3c-i2s-v2.c
sound/soc/s3c24xx/s3c64xx-i2s.c
sound/soc/s3c24xx/s3c64xx-i2s.h
sound/soc/s3c24xx/smdk64xx_wm8580.c [new file with mode: 0644]
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 07659da..abf2fbc 100644 (file)
@@ -67,6 +67,8 @@
 #define S3C2412_IISMOD_BCLK_MASK       (3 << 1)
 #define S3C2412_IISMOD_8BIT            (1 << 0)
 
+#define S3C64XX_IISMOD_CDCLKCON                (1 << 12)
+
 #define S3C2412_IISPSR_PSREN           (1 << 15)
 
 #define S3C2412_IISFIC_TXFLUSH         (1 << 15)
index 97ca9af..ca24e7f 100644 (file)
@@ -30,6 +30,7 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_DSP_A           3 /* L data MSB after FRM LRC */
 #define SND_SOC_DAIFMT_DSP_B           4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_AC97            5 /* AC97 */
+#define SND_SOC_DAIFMT_PDM             6 /* Pulse density modulation */
 
 /* left and right justified also known as MSB and LSB respectively */
 #define SND_SOC_DAIFMT_MSB             SND_SOC_DAIFMT_LEFT_J
@@ -106,7 +107,7 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div);
 
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out);
+       int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
 
 /* Digital Audio interface formatting */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +115,10 @@ int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+       unsigned int tx_num, unsigned int *tx_slot,
+       unsigned int rx_num, unsigned int *rx_slot);
+
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 /* Digital Audio Interface mute */
@@ -136,8 +141,8 @@ struct snd_soc_dai_ops {
         */
        int (*set_sysclk)(struct snd_soc_dai *dai,
                int clk_id, unsigned int freq, int dir);
-       int (*set_pll)(struct snd_soc_dai *dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out);
+       int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+               unsigned int freq_in, unsigned int freq_out);
        int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
        /*
@@ -148,6 +153,9 @@ struct snd_soc_dai_ops {
        int (*set_tdm_slot)(struct snd_soc_dai *dai,
                unsigned int tx_mask, unsigned int rx_mask,
                int slots, int slot_width);
+       int (*set_channel_map)(struct snd_soc_dai *dai,
+               unsigned int tx_num, unsigned int *tx_slot,
+               unsigned int rx_num, unsigned int *rx_slot);
        int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
        /*
index c1410e3..c5c95e1 100644 (file)
        .get = snd_soc_dapm_get_enum_double, \
        .put = snd_soc_dapm_put_enum_double, \
        .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum)                   \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = snd_soc_dapm_get_enum_virt, \
+       .put = snd_soc_dapm_put_enum_virt, \
+       .private_value = (unsigned long)&xenum }
 #define SOC_DAPM_VALUE_ENUM(xname, xenum) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
@@ -260,6 +266,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@@ -333,6 +343,10 @@ struct snd_soc_dapm_route {
        const char *sink;
        const char *control;
        const char *source;
+
+       /* Note: currently only supported for links where source is a supply */
+       int (*connected)(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink);
 };
 
 /* dapm audio path between two widgets */
@@ -349,6 +363,9 @@ struct snd_soc_dapm_path {
        u32 connect:1;  /* source and sink widgets are connected */
        u32 walked:1;   /* path has been walked */
 
+       int (*connected)(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink);
+
        struct list_head list_source;
        struct list_head list_sink;
        struct list_head list;
index 475cb7e..0b1f917 100644 (file)
@@ -413,6 +413,7 @@ struct snd_soc_codec {
        unsigned int num_dai;
 
 #ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs_codec_root;
        struct dentry *debugfs_reg;
        struct dentry *debugfs_pop_time;
        struct dentry *debugfs_dapm;
index 9eb610c..9df4c68 100644 (file)
@@ -268,7 +268,7 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
 
 
-       ret = snd_soc_dai_set_pll(codec_dai, 0,
+       ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
                                         clk_get_rate(CODEC_CLK), pll_out);
        if (ret < 0) {
                pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
index cd361e3..0f45a3f 100644 (file)
@@ -52,6 +52,7 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
        int ret = 0;
        /* set cpu DAI configuration */
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -65,6 +66,12 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
+       /* set cpu DAI channel mapping */
+       ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+               channel_map, ARRAY_SIZE(channel_map), channel_map);
+       if (ret < 0)
+               return ret;
+
        return 0;
 }
 
index 08269e9..2ef1e50 100644 (file)
@@ -61,6 +61,7 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
        int ret = 0;
        /* set cpu DAI configuration */
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -75,7 +76,13 @@ static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* set codec DAI slots, 8 channels, all channels are enabled */
-       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
+       if (ret < 0)
+               return ret;
+
+       /* set cpu DAI channel mapping */
+       ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+               channel_map, ARRAY_SIZE(channel_map), channel_map);
        if (ret < 0)
                return ret;
 
index 084b688..3e6ada0 100644 (file)
@@ -49,7 +49,6 @@ struct bf5xx_i2s_port {
        u16 rcr1;
        u16 tcr2;
        u16 rcr2;
-       int counter;
        int configured;
 };
 
@@ -133,16 +132,6 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        return ret;
 }
 
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
-                            struct snd_soc_dai *dai)
-{
-       pr_debug("%s enter\n", __func__);
-
-       /*this counter is used for counting how many pcm streams are opened*/
-       bf5xx_i2s.counter++;
-       return 0;
-}
-
 static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
@@ -201,9 +190,8 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
        pr_debug("%s enter\n", __func__);
-       bf5xx_i2s.counter--;
        /* No active stream, SPORT is allowed to be configured again. */
-       if (!bf5xx_i2s.counter)
+       if (!dai->active)
                bf5xx_i2s.configured = 0;
 }
 
@@ -284,7 +272,6 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
        SNDRV_PCM_FMTBIT_S32_LE)
 
 static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
-       .startup        = bf5xx_i2s_startup,
        .shutdown       = bf5xx_i2s_shutdown,
        .hw_params      = bf5xx_i2s_hw_params,
        .set_fmt        = bf5xx_i2s_set_dai_fmt,
index ccb5e82..a8c73cb 100644 (file)
@@ -43,7 +43,7 @@
 #include "bf5xx-tdm.h"
 #include "bf5xx-sport.h"
 
-#define PCM_BUFFER_MAX  0x10000
+#define PCM_BUFFER_MAX  0x8000
 #define FRAGMENT_SIZE_MIN  (4*1024)
 #define FRAGMENTS_MIN  2
 #define FRAGMENTS_MAX  32
@@ -177,6 +177,9 @@ out:
 static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
        snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct sport_device *sport = runtime->private_data;
+       struct bf5xx_tdm_port *tdm_port = sport->private_data;
        unsigned int *src;
        unsigned int *dst;
        int i;
@@ -188,7 +191,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
                dst += pos * 8;
                while (count--) {
                        for (i = 0; i < substream->runtime->channels; i++)
-                               *(dst + i) = *src++;
+                               *(dst + tdm_port->tx_map[i]) = *src++;
                        dst += 8;
                }
        } else {
@@ -198,7 +201,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
                src += pos * 8;
                while (count--) {
                        for (i = 0; i < substream->runtime->channels; i++)
-                               *dst++ = *(src+i);
+                               *dst++ = *(src + tdm_port->rx_map[i]);
                        src += 8;
                }
        }
index ff546e9..4b36012 100644 (file)
 #include "bf5xx-sport.h"
 #include "bf5xx-tdm.h"
 
-struct bf5xx_tdm_port {
-       u16 tcr1;
-       u16 rcr1;
-       u16 tcr2;
-       u16 rcr2;
-       int configured;
-};
-
 static struct bf5xx_tdm_port bf5xx_tdm;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
@@ -181,6 +173,40 @@ static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
                bf5xx_tdm.configured = 0;
 }
 
+static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
+               unsigned int tx_num, unsigned int *tx_slot,
+               unsigned int rx_num, unsigned int *rx_slot)
+{
+       int i;
+       unsigned int slot;
+       unsigned int tx_mapped = 0, rx_mapped = 0;
+
+       if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+                       (rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+               return -EINVAL;
+
+       for (i = 0; i < tx_num; i++) {
+               slot = tx_slot[i];
+               if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+                               (!(tx_mapped & (1 << slot)))) {
+                       bf5xx_tdm.tx_map[i] = slot;
+                       tx_mapped |= 1 << slot;
+               } else
+                       return -EINVAL;
+       }
+       for (i = 0; i < rx_num; i++) {
+               slot = rx_slot[i];
+               if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+                               (!(rx_mapped & (1 << slot)))) {
+                       bf5xx_tdm.rx_map[i] = slot;
+                       rx_mapped |= 1 << slot;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_PM
 static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
 {
@@ -235,6 +261,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
        .hw_params      = bf5xx_tdm_hw_params,
        .set_fmt        = bf5xx_tdm_set_dai_fmt,
        .shutdown       = bf5xx_tdm_shutdown,
+       .set_channel_map   = bf5xx_tdm_set_channel_map,
 };
 
 struct snd_soc_dai bf5xx_tdm_dai = {
@@ -300,6 +327,8 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev)
                pr_err("Failed to register DAI: %d\n", ret);
                goto sport_config_err;
        }
+
+       sport_handle->private_data = &bf5xx_tdm;
        return 0;
 
 sport_config_err:
index 618ec3d..04189a1 100644 (file)
@@ -9,6 +9,17 @@
 #ifndef _BF5XX_TDM_H
 #define _BF5XX_TDM_H
 
+#define BFIN_TDM_DAI_MAX_SLOTS 8
+struct bf5xx_tdm_port {
+       u16 tcr1;
+       u16 rcr1;
+       u16 tcr2;
+       u16 rcr2;
+       unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
+       unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
+       int configured;
+};
+
 extern struct snd_soc_dai bf5xx_tdm_dai;
 
 #endif
index 0edca93..3c46f34 100644 (file)
@@ -19,6 +19,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
        select SND_SOC_AK4642 if I2C
+       select SND_SOC_AK4671 if I2C
        select SND_SOC_CS4270 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
@@ -36,6 +37,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8523 if I2C
        select SND_SOC_WM8580 if I2C
+       select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
@@ -96,6 +98,9 @@ config SND_SOC_AK4535
 config SND_SOC_AK4642
        tristate
 
+config SND_SOC_AK4671
+       tristate
+
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
        tristate
@@ -160,6 +165,9 @@ config SND_SOC_WM8523
 config SND_SOC_WM8580
        tristate
 
+config SND_SOC_WM8711
+       tristate
+
 config SND_SOC_WM8728
        tristate
 
index fb4af28..fc1c458 100644 (file)
@@ -6,6 +6,7 @@ snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4642-objs := ak4642.o
+snd-soc-ak4671-objs := ak4671.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-l3-objs := l3.o
@@ -24,6 +25,7 @@ snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8523-objs := wm8523.o
 snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8711-objs := wm8711.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
@@ -56,6 +58,7 @@ obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4104)   += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
+obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
@@ -74,6 +77,7 @@ obj-$(CONFIG_SND_SOC_WM8400)  += snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8510)   += snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8523)   += snd-soc-wm8523.o
 obj-$(CONFIG_SND_SOC_WM8580)   += snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8711)   += snd-soc-wm8711.o
 obj-$(CONFIG_SND_SOC_WM8728)   += snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)   += snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)   += snd-soc-wm8750.o
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644 (file)
index 0000000..b61214d
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * ak4671.c  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+static struct snd_soc_codec *ak4671_codec;
+
+/* codec private data */
+struct ak4671_priv {
+       struct snd_soc_codec codec;
+       u8 reg_cache[AK4671_CACHEREGNUM];
+};
+
+/* ak4671 register cache & default register settings */
+static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
+       0x00,   /* AK4671_AD_DA_POWER_MANAGEMENT        (0x00)  */
+       0xf6,   /* AK4671_PLL_MODE_SELECT0              (0x01)  */
+       0x00,   /* AK4671_PLL_MODE_SELECT1              (0x02)  */
+       0x02,   /* AK4671_FORMAT_SELECT                 (0x03)  */
+       0x00,   /* AK4671_MIC_SIGNAL_SELECT             (0x04)  */
+       0x55,   /* AK4671_MIC_AMP_GAIN                  (0x05)  */
+       0x00,   /* AK4671_MIXING_POWER_MANAGEMENT0      (0x06)  */
+       0x00,   /* AK4671_MIXING_POWER_MANAGEMENT1      (0x07)  */
+       0xb5,   /* AK4671_OUTPUT_VOLUME_CONTROL         (0x08)  */
+       0x00,   /* AK4671_LOUT1_SIGNAL_SELECT           (0x09)  */
+       0x00,   /* AK4671_ROUT1_SIGNAL_SELECT           (0x0a)  */
+       0x00,   /* AK4671_LOUT2_SIGNAL_SELECT           (0x0b)  */
+       0x00,   /* AK4671_ROUT2_SIGNAL_SELECT           (0x0c)  */
+       0x00,   /* AK4671_LOUT3_SIGNAL_SELECT           (0x0d)  */
+       0x00,   /* AK4671_ROUT3_SIGNAL_SELECT           (0x0e)  */
+       0x00,   /* AK4671_LOUT1_POWER_MANAGERMENT       (0x0f)  */
+       0x00,   /* AK4671_LOUT2_POWER_MANAGERMENT       (0x10)  */
+       0x80,   /* AK4671_LOUT3_POWER_MANAGERMENT       (0x11)  */
+       0x91,   /* AK4671_LCH_INPUT_VOLUME_CONTROL      (0x12)  */
+       0x91,   /* AK4671_RCH_INPUT_VOLUME_CONTROL      (0x13)  */
+       0xe1,   /* AK4671_ALC_REFERENCE_SELECT          (0x14)  */
+       0x00,   /* AK4671_DIGITAL_MIXING_CONTROL        (0x15)  */
+       0x00,   /* AK4671_ALC_TIMER_SELECT              (0x16)  */
+       0x00,   /* AK4671_ALC_MODE_CONTROL              (0x17)  */
+       0x02,   /* AK4671_MODE_CONTROL1                 (0x18)  */
+       0x01,   /* AK4671_MODE_CONTROL2                 (0x19)  */
+       0x18,   /* AK4671_LCH_OUTPUT_VOLUME_CONTROL     (0x1a)  */
+       0x18,   /* AK4671_RCH_OUTPUT_VOLUME_CONTROL     (0x1b)  */
+       0x00,   /* AK4671_SIDETONE_A_CONTROL            (0x1c)  */
+       0x02,   /* AK4671_DIGITAL_FILTER_SELECT         (0x1d)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT0             (0x1e)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT1             (0x1f)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT2             (0x20)  */
+       0x00,   /* AK4671_FIL3_COEFFICIENT3             (0x21)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT0               (0x22)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT1               (0x23)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT2               (0x24)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT3               (0x25)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT4               (0x26)  */
+       0x00,   /* AK4671_EQ_COEFFICIENT5               (0x27)  */
+       0xa9,   /* AK4671_FIL1_COEFFICIENT0             (0x28)  */
+       0x1f,   /* AK4671_FIL1_COEFFICIENT1             (0x29)  */
+       0xad,   /* AK4671_FIL1_COEFFICIENT2             (0x2a)  */
+       0x20,   /* AK4671_FIL1_COEFFICIENT3             (0x2b)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT0             (0x2c)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT1             (0x2d)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT2             (0x2e)  */
+       0x00,   /* AK4671_FIL2_COEFFICIENT3             (0x2f)  */
+       0x00,   /* AK4671_DIGITAL_FILTER_SELECT2        (0x30)  */
+       0x00,   /* this register not used                       */
+       0x00,   /* AK4671_E1_COEFFICIENT0               (0x32)  */
+       0x00,   /* AK4671_E1_COEFFICIENT1               (0x33)  */
+       0x00,   /* AK4671_E1_COEFFICIENT2               (0x34)  */
+       0x00,   /* AK4671_E1_COEFFICIENT3               (0x35)  */
+       0x00,   /* AK4671_E1_COEFFICIENT4               (0x36)  */
+       0x00,   /* AK4671_E1_COEFFICIENT5               (0x37)  */
+       0x00,   /* AK4671_E2_COEFFICIENT0               (0x38)  */
+       0x00,   /* AK4671_E2_COEFFICIENT1               (0x39)  */
+       0x00,   /* AK4671_E2_COEFFICIENT2               (0x3a)  */
+       0x00,   /* AK4671_E2_COEFFICIENT3               (0x3b)  */
+       0x00,   /* AK4671_E2_COEFFICIENT4               (0x3c)  */
+       0x00,   /* AK4671_E2_COEFFICIENT5               (0x3d)  */
+       0x00,   /* AK4671_E3_COEFFICIENT0               (0x3e)  */
+       0x00,   /* AK4671_E3_COEFFICIENT1               (0x3f)  */
+       0x00,   /* AK4671_E3_COEFFICIENT2               (0x40)  */
+       0x00,   /* AK4671_E3_COEFFICIENT3               (0x41)  */
+       0x00,   /* AK4671_E3_COEFFICIENT4               (0x42)  */
+       0x00,   /* AK4671_E3_COEFFICIENT5               (0x43)  */
+       0x00,   /* AK4671_E4_COEFFICIENT0               (0x44)  */
+       0x00,   /* AK4671_E4_COEFFICIENT1               (0x45)  */
+       0x00,   /* AK4671_E4_COEFFICIENT2               (0x46)  */
+       0x00,   /* AK4671_E4_COEFFICIENT3               (0x47)  */
+       0x00,   /* AK4671_E4_COEFFICIENT4               (0x48)  */
+       0x00,   /* AK4671_E4_COEFFICIENT5               (0x49)  */
+       0x00,   /* AK4671_E5_COEFFICIENT0               (0x4a)  */
+       0x00,   /* AK4671_E5_COEFFICIENT1               (0x4b)  */
+       0x00,   /* AK4671_E5_COEFFICIENT2               (0x4c)  */
+       0x00,   /* AK4671_E5_COEFFICIENT3               (0x4d)  */
+       0x00,   /* AK4671_E5_COEFFICIENT4               (0x4e)  */
+       0x00,   /* AK4671_E5_COEFFICIENT5               (0x4f)  */
+       0x88,   /* AK4671_EQ_CONTROL_250HZ_100HZ        (0x50)  */
+       0x88,   /* AK4671_EQ_CONTROL_3500HZ_1KHZ        (0x51)  */
+       0x08,   /* AK4671_EQ_CONTRO_10KHZ               (0x52)  */
+       0x00,   /* AK4671_PCM_IF_CONTROL0               (0x53)  */
+       0x00,   /* AK4671_PCM_IF_CONTROL1               (0x54)  */
+       0x00,   /* AK4671_PCM_IF_CONTROL2               (0x55)  */
+       0x18,   /* AK4671_DIGITAL_VOLUME_B_CONTROL      (0x56)  */
+       0x18,   /* AK4671_DIGITAL_VOLUME_C_CONTROL      (0x57)  */
+       0x00,   /* AK4671_SIDETONE_VOLUME_CONTROL       (0x58)  */
+       0x00,   /* AK4671_DIGITAL_MIXING_CONTROL2       (0x59)  */
+       0x00,   /* AK4671_SAR_ADC_CONTROL               (0x5a)  */
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+       /* Common playback gain controls */
+       SOC_SINGLE_TLV("Line Output1 Playback Volume",
+                       AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+       SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+                       AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+       SOC_SINGLE_TLV("Line Output3 Playback Volume",
+                       AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+       /* Common capture gain controls */
+       SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+                       AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       u8 reg;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+               reg |= AK4671_MUTEN;
+               snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+               reg &= ~AK4671_MUTEN;
+               snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+               break;
+       }
+
+       return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+       SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+       SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+       SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+       SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+       SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+               {"LIN1", "LIN2", "LIN3", "LIN4"};
+static const struct soc_enum ak4671_lin_mux_enum =
+       SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
+                       ARRAY_SIZE(ak4671_lin_mux_texts),
+                       ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+       SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+               {"RIN1", "RIN2", "RIN3", "RIN4"};
+static const struct soc_enum ak4671_rin_mux_enum =
+       SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
+                       ARRAY_SIZE(ak4671_rin_mux_texts),
+                       ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+       SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+       /* Inputs */
+       SND_SOC_DAPM_INPUT("LIN1"),
+       SND_SOC_DAPM_INPUT("RIN1"),
+       SND_SOC_DAPM_INPUT("LIN2"),
+       SND_SOC_DAPM_INPUT("RIN2"),
+       SND_SOC_DAPM_INPUT("LIN3"),
+       SND_SOC_DAPM_INPUT("RIN3"),
+       SND_SOC_DAPM_INPUT("LIN4"),
+       SND_SOC_DAPM_INPUT("RIN4"),
+
+       /* Outputs */
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+       SND_SOC_DAPM_OUTPUT("LOUT3"),
+       SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+       /* DAC */
+       SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+       SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+       /* ADC */
+       SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+       SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+                       AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+       /* PGA */
+       SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+                       AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+                       AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+                       AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+       /* Output Mixers */
+       SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+                       &ak4671_lout1_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+       SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+                       &ak4671_rout1_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+       SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+                       0, 0, &ak4671_lout2_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_lout2_mixer_controls),
+                       ak4671_out2_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+                       1, 0, &ak4671_rout2_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_rout2_mixer_controls),
+                       ak4671_out2_event,
+                       SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+                       &ak4671_lout3_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+       SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+                       &ak4671_rout3_mixer_controls[0],
+                       ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+       /* Input MUXs */
+       SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+                       &ak4671_lin_mux_control),
+       SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+                       &ak4671_rin_mux_control),
+
+       /* Mic Power */
+       SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+       /* Supply */
+       SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"DAC Left", "NULL", "PMPLL"},
+       {"DAC Right", "NULL", "PMPLL"},
+       {"ADC Left", "NULL", "PMPLL"},
+       {"ADC Right", "NULL", "PMPLL"},
+
+       /* Outputs */
+       {"LOUT1", "NULL", "LOUT1 Mixer"},
+       {"ROUT1", "NULL", "ROUT1 Mixer"},
+       {"LOUT2", "NULL", "LOUT2 Mix Amp"},
+       {"ROUT2", "NULL", "ROUT2 Mix Amp"},
+       {"LOUT3", "NULL", "LOUT3 Mixer"},
+       {"ROUT3", "NULL", "ROUT3 Mixer"},
+
+       {"LOUT1 Mixer", "DACL", "DAC Left"},
+       {"ROUT1 Mixer", "DACR", "DAC Right"},
+       {"LOUT2 Mixer", "DACHL", "DAC Left"},
+       {"ROUT2 Mixer", "DACHR", "DAC Right"},
+       {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
+       {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+       {"LOUT3 Mixer", "DACSL", "DAC Left"},
+       {"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+       /* Inputs */
+       {"LIN MUX", "LIN1", "LIN1"},
+       {"LIN MUX", "LIN2", "LIN2"},
+       {"LIN MUX", "LIN3", "LIN3"},
+       {"LIN MUX", "LIN4", "LIN4"},
+
+       {"RIN MUX", "RIN1", "RIN1"},
+       {"RIN MUX", "RIN2", "RIN2"},
+       {"RIN MUX", "RIN3", "RIN3"},
+       {"RIN MUX", "RIN4", "RIN4"},
+
+       {"LIN1", NULL, "Mic Bias"},
+       {"RIN1", NULL, "Mic Bias"},
+       {"LIN2", NULL, "Mic Bias"},
+       {"RIN2", NULL, "Mic Bias"},
+
+       {"ADC Left", "NULL", "LIN MUX"},
+       {"ADC Right", "NULL", "RIN MUX"},
+
+       /* Analog Loops */
+       {"LIN1 Mixing Circuit", "NULL", "LIN1"},
+       {"RIN1 Mixing Circuit", "NULL", "RIN1"},
+       {"LIN2 Mixing Circuit", "NULL", "LIN2"},
+       {"RIN2 Mixing Circuit", "NULL", "RIN2"},
+       {"LIN3 Mixing Circuit", "NULL", "LIN3"},
+       {"RIN3 Mixing Circuit", "NULL", "RIN3"},
+       {"LIN4 Mixing Circuit", "NULL", "LIN4"},
+       {"RIN4 Mixing Circuit", "NULL", "RIN4"},
+
+       {"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+       {"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+       {"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+       {"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+       {"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+       {"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+       {"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+       {"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+       {"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
+                                 ARRAY_SIZE(ak4671_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+static int ak4671_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params,
+               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 fs;
+
+       fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+       fs &= ~AK4671_FS;
+
+       switch (params_rate(params)) {
+       case 8000:
+               fs |= AK4671_FS_8KHZ;
+               break;
+       case 12000:
+               fs |= AK4671_FS_12KHZ;
+               break;
+       case 16000:
+               fs |= AK4671_FS_16KHZ;
+               break;
+       case 24000:
+               fs |= AK4671_FS_24KHZ;
+               break;
+       case 11025:
+               fs |= AK4671_FS_11_025KHZ;
+               break;
+       case 22050:
+               fs |= AK4671_FS_22_05KHZ;
+               break;
+       case 32000:
+               fs |= AK4671_FS_32KHZ;
+               break;
+       case 44100:
+               fs |= AK4671_FS_44_1KHZ;
+               break;
+       case 48000:
+               fs |= AK4671_FS_48KHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+       return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+               unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 pll;
+
+       pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+       pll &= ~AK4671_PLL;
+
+       switch (freq) {
+       case 11289600:
+               pll |= AK4671_PLL_11_2896MHZ;
+               break;
+       case 12000000:
+               pll |= AK4671_PLL_12MHZ;
+               break;
+       case 12288000:
+               pll |= AK4671_PLL_12_288MHZ;
+               break;
+       case 13000000:
+               pll |= AK4671_PLL_13MHZ;
+               break;
+       case 13500000:
+               pll |= AK4671_PLL_13_5MHZ;
+               break;
+       case 19200000:
+               pll |= AK4671_PLL_19_2MHZ;
+               break;
+       case 24000000:
+               pll |= AK4671_PLL_24MHZ;
+               break;
+       case 26000000:
+               pll |= AK4671_PLL_26MHZ;
+               break;
+       case 27000000:
+               pll |= AK4671_PLL_27MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+
+       return 0;
+}
+
+static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 mode;
+       u8 format;
+
+       /* set master/slave audio interface */
+       mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               mode |= AK4671_M_S;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               mode &= ~(AK4671_M_S);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+       format &= ~AK4671_DIF;
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               format |= AK4671_DIF_I2S_MODE;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               format |= AK4671_DIF_MSB_MODE;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               format |= AK4671_DIF_DSP_MODE;
+               format |= AK4671_BCKP;
+               format |= AK4671_MSBS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set mode and format */
+       snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+       snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+       return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+               enum snd_soc_bias_level level)
+{
+       u8 reg;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+       case SND_SOC_BIAS_STANDBY:
+               reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
+               snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+                               reg | AK4671_PMVCM);
+               break;
+       case SND_SOC_BIAS_OFF:
+               snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define AK4671_RATES           (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+                               SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+                               SNDRV_PCM_RATE_48000)
+
+#define AK4671_FORMATS         SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops ak4671_dai_ops = {
+       .hw_params      = ak4671_hw_params,
+       .set_sysclk     = ak4671_set_dai_sysclk,
+       .set_fmt        = ak4671_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4671_dai = {
+       .name = "AK4671",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = AK4671_RATES,
+               .formats = AK4671_FORMATS,},
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = AK4671_RATES,
+               .formats = AK4671_FORMATS,},
+       .ops = &ak4671_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4671_dai);
+
+static int ak4671_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (ak4671_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = ak4671_codec;
+       codec = ak4671_codec;
+
+       /* 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: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, ak4671_snd_controls,
+                            ARRAY_SIZE(ak4671_snd_controls));
+       ak4671_add_widgets(codec);
+
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+static int ak4671_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4671 = {
+       .probe = ak4671_probe,
+       .remove = ak4671_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
+
+static int ak4671_register(struct ak4671_priv *ak4671,
+               enum snd_soc_control_type control)
+{
+       int ret;
+       struct snd_soc_codec *codec = &ak4671->codec;
+
+       if (ak4671_codec) {
+               dev_err(codec->dev, "Another AK4671 is registered\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = ak4671;
+       codec->name = "AK4671";
+       codec->owner = THIS_MODULE;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = ak4671_set_bias_level;
+       codec->dai = &ak4671_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = AK4671_CACHEREGNUM;
+       codec->reg_cache = &ak4671->reg_cache;
+
+       memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       ak4671_dai.dev = codec->dev;
+       ak4671_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dai(&ak4671_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               goto err_codec;
+       }
+
+       return 0;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(ak4671);
+       return ret;
+}
+
+static void ak4671_unregister(struct ak4671_priv *ak4671)
+{
+       ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&ak4671_dai);
+       snd_soc_unregister_codec(&ak4671->codec);
+       kfree(ak4671);
+       ak4671_codec = NULL;
+}
+
+static int __devinit ak4671_i2c_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct ak4671_priv *ak4671;
+       struct snd_soc_codec *codec;
+
+       ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
+       if (ak4671 == NULL)
+               return -ENOMEM;
+
+       codec = &ak4671->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(client, ak4671);
+       codec->control_data = client;
+
+       codec->dev = &client->dev;
+
+       return ak4671_register(ak4671, SND_SOC_I2C);
+}
+
+static __devexit int ak4671_i2c_remove(struct i2c_client *client)
+{
+       struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
+
+       ak4671_unregister(ak4671);
+
+       return 0;
+}
+
+static const struct i2c_device_id ak4671_i2c_id[] = {
+       { "ak4671", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
+
+static struct i2c_driver ak4671_i2c_driver = {
+       .driver = {
+               .name = "ak4671",
+               .owner = THIS_MODULE,
+       },
+       .probe = ak4671_i2c_probe,
+       .remove = __devexit_p(ak4671_i2c_remove),
+       .id_table = ak4671_i2c_id,
+};
+
+static int __init ak4671_modinit(void)
+{
+       return i2c_add_driver(&ak4671_i2c_driver);
+}
+module_init(ak4671_modinit);
+
+static void __exit ak4671_exit(void)
+{
+       i2c_del_driver(&ak4671_i2c_driver);
+}
+module_exit(ak4671_exit);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
new file mode 100644 (file)
index 0000000..e2fad96
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * ak4671.h  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _AK4671_H
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT          0x00
+#define AK4671_PLL_MODE_SELECT0                        0x01
+#define AK4671_PLL_MODE_SELECT1                        0x02
+#define AK4671_FORMAT_SELECT                   0x03
+#define AK4671_MIC_SIGNAL_SELECT               0x04
+#define AK4671_MIC_AMP_GAIN                    0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0                0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1                0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL           0x08
+#define AK4671_LOUT1_SIGNAL_SELECT             0x09
+#define AK4671_ROUT1_SIGNAL_SELECT             0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT             0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT             0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT             0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT             0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT         0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT         0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT         0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL                0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL                0x13
+#define AK4671_ALC_REFERENCE_SELECT            0x14
+#define AK4671_DIGITAL_MIXING_CONTROL          0x15
+#define AK4671_ALC_TIMER_SELECT                        0x16
+#define AK4671_ALC_MODE_CONTROL                        0x17
+#define AK4671_MODE_CONTROL1                   0x18
+#define AK4671_MODE_CONTROL2                   0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL       0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL       0x1b
+#define AK4671_SIDETONE_A_CONTROL              0x1c
+#define AK4671_DIGITAL_FILTER_SELECT           0x1d
+#define AK4671_FIL3_COEFFICIENT0               0x1e
+#define AK4671_FIL3_COEFFICIENT1               0x1f
+#define AK4671_FIL3_COEFFICIENT2               0x20
+#define AK4671_FIL3_COEFFICIENT3               0x21
+#define AK4671_EQ_COEFFICIENT0                 0x22
+#define AK4671_EQ_COEFFICIENT1                 0x23
+#define AK4671_EQ_COEFFICIENT2                 0x24
+#define AK4671_EQ_COEFFICIENT3                 0x25
+#define AK4671_EQ_COEFFICIENT4                 0x26
+#define AK4671_EQ_COEFFICIENT5                 0x27
+#define AK4671_FIL1_COEFFICIENT0               0x28
+#define AK4671_FIL1_COEFFICIENT1               0x29
+#define AK4671_FIL1_COEFFICIENT2               0x2a
+#define AK4671_FIL1_COEFFICIENT3               0x2b
+#define AK4671_FIL2_COEFFICIENT0               0x2c
+#define AK4671_FIL2_COEFFICIENT1               0x2d
+#define AK4671_FIL2_COEFFICIENT2               0x2e
+#define AK4671_FIL2_COEFFICIENT3               0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2          0x30
+#define AK4671_E1_COEFFICIENT0                 0x32
+#define AK4671_E1_COEFFICIENT1                 0x33
+#define AK4671_E1_COEFFICIENT2                 0x34
+#define AK4671_E1_COEFFICIENT3                 0x35
+#define AK4671_E1_COEFFICIENT4                 0x36
+#define AK4671_E1_COEFFICIENT5                 0x37
+#define AK4671_E2_COEFFICIENT0                 0x38
+#define AK4671_E2_COEFFICIENT1                 0x39
+#define AK4671_E2_COEFFICIENT2                 0x3a
+#define AK4671_E2_COEFFICIENT3                 0x3b
+#define AK4671_E2_COEFFICIENT4                 0x3c
+#define AK4671_E2_COEFFICIENT5                 0x3d
+#define AK4671_E3_COEFFICIENT0                 0x3e
+#define AK4671_E3_COEFFICIENT1                 0x3f
+#define AK4671_E3_COEFFICIENT2                 0x40
+#define AK4671_E3_COEFFICIENT3                 0x41
+#define AK4671_E3_COEFFICIENT4                 0x42
+#define AK4671_E3_COEFFICIENT5                 0x43
+#define AK4671_E4_COEFFICIENT0                 0x44
+#define AK4671_E4_COEFFICIENT1                 0x45
+#define AK4671_E4_COEFFICIENT2                 0x46
+#define AK4671_E4_COEFFICIENT3                 0x47
+#define AK4671_E4_COEFFICIENT4                 0x48
+#define AK4671_E4_COEFFICIENT5                 0x49
+#define AK4671_E5_COEFFICIENT0                 0x4a
+#define AK4671_E5_COEFFICIENT1                 0x4b
+#define AK4671_E5_COEFFICIENT2                 0x4c
+#define AK4671_E5_COEFFICIENT3                 0x4d
+#define AK4671_E5_COEFFICIENT4                 0x4e
+#define AK4671_E5_COEFFICIENT5                 0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ          0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ          0x51
+#define AK4671_EQ_CONTRO_10KHZ                 0x52
+#define AK4671_PCM_IF_CONTROL0                 0x53
+#define AK4671_PCM_IF_CONTROL1                 0x54
+#define AK4671_PCM_IF_CONTROL2                 0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL                0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL                0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL         0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2         0x59
+#define AK4671_SAR_ADC_CONTROL                 0x5a
+
+#define AK4671_CACHEREGNUM                     (AK4671_SAR_ADC_CONTROL + 1)
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM                           0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL                             0x0f
+#define AK4671_PLL_11_2896MHZ                  (4 << 0)
+#define AK4671_PLL_12_288MHZ                   (5 << 0)
+#define AK4671_PLL_12MHZ                       (6 << 0)
+#define AK4671_PLL_24MHZ                       (7 << 0)
+#define AK4671_PLL_19_2MHZ                     (8 << 0)
+#define AK4671_PLL_13_5MHZ                     (12 << 0)
+#define AK4671_PLL_27MHZ                       (13 << 0)
+#define AK4671_PLL_13MHZ                       (14 << 0)
+#define AK4671_PLL_26MHZ                       (15 << 0)
+#define AK4671_FS                              0xf0
+#define AK4671_FS_8KHZ                         (0 << 4)
+#define AK4671_FS_12KHZ                                (1 << 4)
+#define AK4671_FS_16KHZ                                (2 << 4)
+#define AK4671_FS_24KHZ                                (3 << 4)
+#define AK4671_FS_11_025KHZ                    (5 << 4)
+#define AK4671_FS_22_05KHZ                     (7 << 4)
+#define AK4671_FS_32KHZ                                (10 << 4)
+#define AK4671_FS_48KHZ                                (11 << 4)
+#define AK4671_FS_44_1KHZ                      (15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL                           0x01
+#define AK4671_M_S                             0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF                             0x03
+#define AK4671_DIF_DSP_MODE                    (0 << 0)
+#define AK4671_DIF_MSB_MODE                    (2 << 0)
+#define AK4671_DIF_I2S_MODE                    (3 << 0)
+#define AK4671_BCKP                            0x04
+#define AK4671_MSBS                            0x08
+#define AK4671_SDOD                            0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN                           0x04
+
+extern struct snd_soc_dai ak4671_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4671;
+
+#endif
index 3ff0373..3f7e8a8 100644 (file)
@@ -1101,7 +1101,7 @@ static inline int fll_factors(struct _fll_div *fll_div, unsigned int input,
 }
 
 static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
-                         int pll_id, unsigned int freq_in,
+                         int pll_id, int source, unsigned int freq_in,
                          unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
index b9ef4d9..9cb8e50 100644 (file)
@@ -1011,7 +1011,8 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors,
 }
 
 static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
-                             unsigned int freq_in, unsigned int freq_out)
+                             int source, unsigned int freq_in,
+                             unsigned int freq_out)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct wm8400_priv *wm8400 = codec->private_data;
index 060d5d0..5702435 100644 (file)
@@ -271,8 +271,8 @@ static void pll_factors(unsigned int target, unsigned int source)
        pll_div.k = K;
 }
 
-static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8510_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;
        u16 reg;
index 6bded8c..3be5c0b 100644 (file)
@@ -407,8 +407,8 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target,
        return 0;
 }
 
-static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        int offset;
        struct snd_soc_codec *codec = codec_dai->codec;
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
new file mode 100644 (file)
index 0000000..90ec8c5
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * wm8711.c  --  WM8711 ALSA SoC Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ *
+ * 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.
+ */
+
+#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/spi/spi.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/tlv.h>
+#include <sound/initval.h>
+
+#include "wm8711.h"
+
+static struct snd_soc_codec *wm8711_codec;
+
+/* codec private data */
+struct wm8711_priv {
+       struct snd_soc_codec codec;
+       u16 reg_cache[WM8711_CACHEREGNUM];
+       unsigned int sysclk;
+};
+
+/*
+ * wm8711 register cache
+ * We can't read the WM8711 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
+       0x0079, 0x0079, 0x000a, 0x0008,
+       0x009f, 0x000a, 0x0000, 0x0000
+};
+
+#define wm8711_reset(c)        snd_soc_write(c, WM8711_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
+                0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
+       7, 1, 0),
+
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
+       &wm8711_output_mixer_controls[0],
+       ARRAY_SIZE(wm8711_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       /* output mixer */
+       {"Output Mixer", "Line Bypass Switch", "Line Input"},
+       {"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+       /* outputs */
+       {"RHPOUT", NULL, "Output Mixer"},
+       {"ROUT", NULL, "Output Mixer"},
+       {"LHPOUT", NULL, "Output Mixer"},
+       {"LOUT", NULL, "Output Mixer"},
+};
+
+static int wm8711_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets,
+                                 ARRAY_SIZE(wm8711_dapm_widgets));
+
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_dapm_new_widgets(codec);
+       return 0;
+}
+
+struct _coeff_div {
+       u32 mclk;
+       u32 rate;
+       u16 fs;
+       u8 sr:4;
+       u8 bosr:1;
+       u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+       /* 48k */
+       {12288000, 48000, 256, 0x0, 0x0, 0x0},
+       {18432000, 48000, 384, 0x0, 0x1, 0x0},
+       {12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+       /* 32k */
+       {12288000, 32000, 384, 0x6, 0x0, 0x0},
+       {18432000, 32000, 576, 0x6, 0x1, 0x0},
+       {12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+       /* 8k */
+       {12288000, 8000, 1536, 0x3, 0x0, 0x0},
+       {18432000, 8000, 2304, 0x3, 0x1, 0x0},
+       {11289600, 8000, 1408, 0xb, 0x0, 0x0},
+       {16934400, 8000, 2112, 0xb, 0x1, 0x0},
+       {12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+       /* 96k */
+       {12288000, 96000, 128, 0x7, 0x0, 0x0},
+       {18432000, 96000, 192, 0x7, 0x1, 0x0},
+       {12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+       /* 44.1k */
+       {11289600, 44100, 256, 0x8, 0x0, 0x0},
+       {16934400, 44100, 384, 0x8, 0x1, 0x0},
+       {12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+       /* 88.2k */
+       {11289600, 88200, 128, 0xf, 0x0, 0x0},
+       {16934400, 88200, 192, 0xf, 0x1, 0x0},
+       {12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+               if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+                       return i;
+       }
+       return 0;
+}
+
+static int wm8711_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8711_priv *wm8711 = codec->private_data;
+       u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
+       int i = get_coeff(wm8711->sysclk, params_rate(params));
+       u16 srate = (coeff_div[i].sr << 2) |
+               (coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+       snd_soc_write(codec, WM8711_SRATE, srate);
+
+       /* bit size */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               iface |= 0x0004;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               iface |= 0x0008;
+               break;
+       }
+
+       snd_soc_write(codec, WM8711_IFACE, iface);
+       return 0;
+}
+
+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       /* set active */
+       snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+
+       return 0;
+}
+
+static void wm8711_shutdown(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       /* deactivate */
+       if (!codec->active) {
+               udelay(50);
+               snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+       }
+}
+
+static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+
+       if (mute)
+               snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+       else
+               snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+
+       return 0;
+}
+
+static int wm8711_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 wm8711_priv *wm8711 = codec->private_data;
+
+       switch (freq) {
+       case 11289600:
+       case 12000000:
+       case 12288000:
+       case 16934400:
+       case 18432000:
+               wm8711->sysclk = freq;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 iface = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               iface |= 0x0040;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               iface |= 0x0002;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface |= 0x0001;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface |= 0x0003;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface |= 0x0013;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface |= 0x0090;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface |= 0x0080;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface |= 0x0010;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set iface */
+       snd_soc_write(codec, WM8711_IFACE, iface);
+       return 0;
+}
+
+
+static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+       enum snd_soc_bias_level level)
+{
+       u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               snd_soc_write(codec, WM8711_PWR, reg);
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+               break;
+       case SND_SOC_BIAS_OFF:
+               snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+               snd_soc_write(codec, WM8711_PWR, 0xffff);
+               break;
+       }
+       codec->bias_level = level;
+       return 0;
+}
+
+#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8711_ops = {
+       .prepare = wm8711_pcm_prepare,
+       .hw_params = wm8711_hw_params,
+       .shutdown = wm8711_shutdown,
+       .digital_mute = wm8711_mute,
+       .set_sysclk = wm8711_set_dai_sysclk,
+       .set_fmt = wm8711_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8711_dai = {
+       .name = "WM8711",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = WM8711_RATES,
+               .formats = WM8711_FORMATS,
+       },
+       .ops = &wm8711_ops,
+};
+EXPORT_SYMBOL_GPL(wm8711_dai);
+
+static int wm8711_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;
+
+       snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+       wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int wm8711_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       int i;
+       u8 data[2];
+       u16 *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
+               data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+               data[1] = cache[i] & 0x00ff;
+               codec->hw_write(codec->control_data, data, 2);
+       }
+       wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       wm8711_set_bias_level(codec, codec->suspend_bias_level);
+       return 0;
+}
+
+static int wm8711_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       if (wm8711_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = wm8711_codec;
+       codec = wm8711_codec;
+
+       /* 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: %d\n", ret);
+               goto pcm_err;
+       }
+
+       snd_soc_add_controls(codec, wm8711_snd_controls,
+                            ARRAY_SIZE(wm8711_snd_controls));
+       wm8711_add_widgets(codec);
+       ret = snd_soc_init_card(socdev);
+       if (ret < 0) {
+               dev_err(codec->dev, "failed to register card: %d\n", ret);
+               goto card_err;
+       }
+
+       return ret;
+
+card_err:
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+pcm_err:
+       return ret;
+}
+
+/* power down chip */
+static int wm8711_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+       snd_soc_free_pcms(socdev);
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8711 = {
+       .probe =        wm8711_probe,
+       .remove =       wm8711_remove,
+       .suspend =      wm8711_suspend,
+       .resume =       wm8711_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
+
+static int wm8711_register(struct wm8711_priv *wm8711,
+                          enum snd_soc_control_type control)
+{
+       int ret;
+       struct snd_soc_codec *codec = &wm8711->codec;
+       u16 reg;
+
+       if (wm8711_codec) {
+               dev_err(codec->dev, "Another WM8711 is registered\n");
+               return -EINVAL;
+       }
+
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       codec->private_data = wm8711;
+       codec->name = "WM8711";
+       codec->owner = THIS_MODULE;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = wm8711_set_bias_level;
+       codec->dai = &wm8711_dai;
+       codec->num_dai = 1;
+       codec->reg_cache_size = WM8711_CACHEREGNUM;
+       codec->reg_cache = &wm8711->reg_cache;
+
+       memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_reg));
+
+       ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               goto err;
+       }
+
+       ret = wm8711_reset(codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to issue reset\n");
+               goto err;
+       }
+
+       wm8711_dai.dev = codec->dev;
+
+       wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Latch the update bits */
+       reg = snd_soc_read(codec, WM8711_LOUT1V);
+       snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100);
+       reg = snd_soc_read(codec, WM8711_ROUT1V);
+       snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100);
+
+       wm8711_codec = codec;
+
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err;
+       }
+
+       ret = snd_soc_register_dai(&wm8711_dai);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+               goto err_codec;
+       }
+
+       return 0;
+
+err_codec:
+       snd_soc_unregister_codec(codec);
+err:
+       kfree(wm8711);
+       return ret;
+}
+
+static void wm8711_unregister(struct wm8711_priv *wm8711)
+{
+       wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF);
+       snd_soc_unregister_dai(&wm8711_dai);
+       snd_soc_unregister_codec(&wm8711->codec);
+       kfree(wm8711);
+       wm8711_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8711_spi_probe(struct spi_device *spi)
+{
+       struct snd_soc_codec *codec;
+       struct wm8711_priv *wm8711;
+
+       wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+       if (wm8711 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8711->codec;
+       codec->control_data = spi;
+       codec->dev = &spi->dev;
+
+       dev_set_drvdata(&spi->dev, wm8711);
+
+       return wm8711_register(wm8711, SND_SOC_SPI);
+}
+
+static int __devexit wm8711_spi_remove(struct spi_device *spi)
+{
+       struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev);
+
+       wm8711_unregister(wm8711);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8711_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+       return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8711_spi_resume(struct spi_device *spi)
+{
+       return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8711_spi_suspend NULL
+#define wm8711_spi_resume NULL
+#endif
+
+static struct spi_driver wm8711_spi_driver = {
+       .driver = {
+               .name   = "wm8711",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = wm8711_spi_probe,
+       .suspend        = wm8711_spi_suspend,
+       .resume         = wm8711_spi_resume,
+       .remove         = __devexit_p(wm8711_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8711_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct wm8711_priv *wm8711;
+       struct snd_soc_codec *codec;
+
+       wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+       if (wm8711 == NULL)
+               return -ENOMEM;
+
+       codec = &wm8711->codec;
+       codec->hw_write = (hw_write_t)i2c_master_send;
+
+       i2c_set_clientdata(i2c, wm8711);
+       codec->control_data = i2c;
+
+       codec->dev = &i2c->dev;
+
+       return wm8711_register(wm8711, SND_SOC_I2C);
+}
+
+static __devexit int wm8711_i2c_remove(struct i2c_client *client)
+{
+       struct wm8711_priv *wm8711 = i2c_get_clientdata(client);
+       wm8711_unregister(wm8711);
+       return 0;
+}
+
+static const struct i2c_device_id wm8711_i2c_id[] = {
+       { "wm8711", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
+
+static struct i2c_driver wm8711_i2c_driver = {
+       .driver = {
+               .name = "WM8711 I2C Codec",
+               .owner = THIS_MODULE,
+       },
+       .probe =    wm8711_i2c_probe,
+       .remove =   __devexit_p(wm8711_i2c_remove),
+       .id_table = wm8711_i2c_id,
+};
+#endif
+
+static int __init wm8711_modinit(void)
+{
+       int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&wm8711_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
+                      ret);
+       }
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&wm8711_spi_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
+                      ret);
+       }
+#endif
+       return 0;
+}
+module_init(wm8711_modinit);
+
+static void __exit wm8711_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&wm8711_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&wm8711_spi_driver);
+#endif
+}
+module_exit(wm8711_exit);
+
+MODULE_DESCRIPTION("ASoC WM8711 driver");
+MODULE_AUTHOR("Mike Arthur");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h
new file mode 100644 (file)
index 0000000..381e84a
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * wm8711.h  --  WM8711 Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.h
+ *
+ * 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 _WM8711_H
+#define _WM8711_H
+
+/* WM8711 register space */
+
+#define WM8711_LOUT1V   0x02
+#define WM8711_ROUT1V   0x03
+#define WM8711_APANA    0x04
+#define WM8711_APDIGI   0x05
+#define WM8711_PWR      0x06
+#define WM8711_IFACE    0x07
+#define WM8711_SRATE    0x08
+#define WM8711_ACTIVE   0x09
+#define WM8711_RESET   0x0f
+
+#define WM8711_CACHEREGNUM     8
+
+#define WM8711_SYSCLK  0
+#define WM8711_DAI             0
+
+struct wm8711_setup_data {
+       unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8711_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
+
+#endif
index 5ad677c..9b27efb 100644 (file)
@@ -724,8 +724,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
        pll_div->k = K;
 }
 
-static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        u16 reg, enable;
        int offset;
index 5e9c855..882604e 100644 (file)
@@ -814,8 +814,8 @@ reenable:
        return 0;
 }
 
-static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
 }
index 1ef2454..1685cfb 100644 (file)
@@ -536,8 +536,8 @@ static void pll_factors(unsigned int target, unsigned int source)
 }
 
 /* Untested at the moment */
-static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8940_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;
        u16 reg;
index f59703b..416fb3c 100644 (file)
@@ -540,8 +540,8 @@ static int pll_factors(unsigned int source, unsigned int target,
        return 0;
 }
 
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_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;
        u16 reg;
index 98d663a..eff2933 100644 (file)
@@ -281,36 +281,38 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)
 }
 
 struct pll_ {
-       unsigned int pre_div:4; /* prescale - 1 */
+       unsigned int pre_div:1;
        unsigned int n:4;
        unsigned int k;
 };
 
-static struct pll_ pll_div;
-
 /* The size in bits of the pll divide multiplied by 10
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 24) * 10)
 
-static void pll_factors(unsigned int target, unsigned int source)
+static void pll_factors(struct pll_ *pll_div,
+                       unsigned int target, unsigned int source)
 {
        unsigned long long Kpart;
        unsigned int K, Ndiv, Nmod;
 
+       /* There is a fixed divide by 4 in the output path */
+       target *= 4;
+
        Ndiv = target / source;
        if (Ndiv < 6) {
-               source >>= 1;
-               pll_div.pre_div = 1;
+               source /= 2;
+               pll_div->pre_div = 1;
                Ndiv = target / source;
        } else
-               pll_div.pre_div = 0;
+               pll_div->pre_div = 0;
 
        if ((Ndiv < 6) || (Ndiv > 12))
                printk(KERN_WARNING
                        "WM8974 N value %u outwith recommended range!\n",
                        Ndiv);
 
-       pll_div.n = Ndiv;
+       pll_div->n = Ndiv;
        Nmod = target % source;
        Kpart = FIXED_PLL_SIZE * (long long)Nmod;
 
@@ -325,13 +327,14 @@ static void pll_factors(unsigned int target, unsigned int source)
        /* Move down to proper range now rounding is done */
        K /= 10;
 
-       pll_div.k = K;
+       pll_div->k = K;
 }
 
-static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8974_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;
+       struct pll_ pll_div;
        u16 reg;
 
        if (freq_in == 0 || freq_out == 0) {
@@ -345,7 +348,7 @@ static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
                return 0;
        }
 
-       pll_factors(freq_out*4, freq_in);
+       pll_factors(&pll_div, freq_out, freq_in);
 
        snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
        snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
index 2d702db..f657e9a 100644 (file)
@@ -972,8 +972,8 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int target,
        pll_div->k = K;
 }
 
-static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+               int source, unsigned int freq_in, unsigned int freq_out)
 {
        u16 reg;
        struct snd_soc_codec *codec = codec_dai->codec;
index d998799..dac3977 100644 (file)
@@ -422,7 +422,7 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
        return 0;
 }
 
-static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
                          unsigned int Fref, unsigned int Fout)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -1572,33 +1572,15 @@ static int wm8993_i2c_probe(struct i2c_client *i2c,
        /* Use automatic clock configuration */
        snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
 
-       if (!wm8993->pdata.lineout1_diff)
-               snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
-                                   WM8993_LINEOUT1_MODE,
-                                   WM8993_LINEOUT1_MODE);
-       if (!wm8993->pdata.lineout2_diff)
-               snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
-                                   WM8993_LINEOUT2_MODE,
-                                   WM8993_LINEOUT2_MODE);
-
-       if (wm8993->pdata.lineout1fb)
-               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
-                                   WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
-
-       if (wm8993->pdata.lineout2fb)
-               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
-                                   WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
-
-       /* Apply the microphone bias/detection configuration - the
-        * platform data is directly applicable to the register. */
-       snd_soc_update_bits(codec, WM8993_MICBIAS,
-                           WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
-                           WM8993_MICB1_LVL | WM8993_MICB2_LVL,
-                           wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
-                           wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
-                           wm8993->pdata.micbias1_lvl |
-                           wm8993->pdata.micbias1_lvl << 1);
-
+       wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+                                     wm8993->pdata.lineout2_diff,
+                                     wm8993->pdata.lineout1fb,
+                                     wm8993->pdata.lineout2fb,
+                                     wm8993->pdata.jd_scthr,
+                                     wm8993->pdata.jd_thr,
+                                     wm8993->pdata.micbias1_lvl,
+                                     wm8993->pdata.micbias2_lvl);
+                            
        ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        if (ret != 0)
                goto err;
index abed37a..ca3d449 100644 (file)
@@ -800,8 +800,8 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
-               int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm9713_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;
        return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
index e542027..810a563 100644 (file)
@@ -738,6 +738,41 @@ int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
 
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+                                 int lineout1_diff, int lineout2_diff,
+                                 int lineout1fb, int lineout2fb,
+                                 int jd_scthr, int jd_thr, int micbias1_lvl,
+                                 int micbias2_lvl)
+{
+       if (!lineout1_diff)
+               snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+                                   WM8993_LINEOUT1_MODE,
+                                   WM8993_LINEOUT1_MODE);
+       if (!lineout2_diff)
+               snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+                                   WM8993_LINEOUT2_MODE,
+                                   WM8993_LINEOUT2_MODE);
+
+       if (lineout1fb)
+               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+                                   WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+       if (lineout2fb)
+               snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+                                   WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+       snd_soc_update_bits(codec, WM8993_MICBIAS,
+                           WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+                           WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+                           jd_scthr << WM8993_JD_SCTHR_SHIFT |
+                           jd_thr << WM8993_JD_THR_SHIFT |
+                           micbias1_lvl |
+                           micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
 MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index ec09cb6..36d3fba 100644 (file)
@@ -20,5 +20,10 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
 
 extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
 extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
+                                        int lineout1_diff, int lineout2_diff,
+                                        int lineout1fb, int lineout2fb,
+                                        int jd_scthr, int jd_thr,
+                                        int micbias1_lvl, int micbias2_lvl);
 
 #endif
index 4dfd4ad..047ee39 100644 (file)
@@ -13,9 +13,9 @@ config SND_DAVINCI_SOC_MCASP
        tristate
 
 config SND_DAVINCI_SOC_EVM
-       tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
+       tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
        depends on SND_DAVINCI_SOC
-       depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
+       depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM  || MACH_DAVINCI_DM365_EVM
        select SND_DAVINCI_SOC_I2S
        select SND_SOC_TLV320AIC3X
        help
index 67414f6..7ccbe66 100644 (file)
@@ -45,7 +45,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        unsigned sysclk;
 
        /* ASP1 on DM355 EVM is clocked by an external oscillator */
-       if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
+       if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
+           machine_is_davinci_dm365_evm())
                sysclk = 27000000;
 
        /* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -176,7 +177,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = {
        .ops = &evm_ops,
 };
 
-/* davinci-evm audio machine driver */
+/* davinci dm6446, dm355 or dm365 evm audio machine driver */
 static struct snd_soc_card snd_soc_card_evm = {
        .name = "DaVinci EVM",
        .platform = &davinci_soc_platform,
@@ -243,7 +244,7 @@ static int __init evm_init(void)
        int index;
        int ret;
 
-       if (machine_is_davinci_evm()) {
+       if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
                evm_snd_dev_data = &evm_snd_devdata;
                index = 0;
        } else if (machine_is_davinci_dm355_evm()) {
index 4ae7070..2ab8093 100644 (file)
@@ -397,6 +397,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
        }
 
        dma_params->acnt  = dma_params->data_type;
+       dma_params->fifo_level = 0;
+
        rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
        xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
 
index 5d1f98a..50ad051 100644 (file)
@@ -714,16 +714,13 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        struct davinci_pcm_dma_params *dma_params =
                                        &dev->dma_params[substream->stream];
        int word_length;
-       u8 numevt;
+       u8 fifo_level;
 
        davinci_hw_common_param(dev, substream->stream);
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               numevt = dev->txnumevt;
+               fifo_level = dev->txnumevt;
        else
-               numevt = dev->rxnumevt;
-
-       if (!numevt)
-               numevt = 1;
+               fifo_level = dev->rxnumevt;
 
        if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
                davinci_hw_dit_param(dev);
@@ -751,12 +748,12 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       if (dev->version == MCASP_VERSION_2) {
-               dma_params->data_type *= numevt;
-               dma_params->acnt = 4 * numevt;
-       } else
+       if (dev->version == MCASP_VERSION_2 && !fifo_level)
+               dma_params->acnt = 4;
+       else
                dma_params->acnt = dma_params->data_type;
 
+       dma_params->fifo_level = fifo_level;
        davinci_config_channel_size(dev, word_length);
 
        return 0;
index c73a915..fb10f1d 100644 (file)
@@ -66,38 +66,53 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
        dma_addr_t dma_pos;
        dma_addr_t src, dst;
        unsigned short src_bidx, dst_bidx;
+       unsigned short src_cidx, dst_cidx;
        unsigned int data_type;
        unsigned short acnt;
        unsigned int count;
+       unsigned int fifo_level;
 
        period_size = snd_pcm_lib_period_bytes(substream);
        dma_offset = prtd->period * period_size;
        dma_pos = runtime->dma_addr + dma_offset;
+       fifo_level = prtd->params->fifo_level;
 
        pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
                "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
 
        data_type = prtd->params->data_type;
        count = period_size / data_type;
+       if (fifo_level)
+               count /= fifo_level;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                src = dma_pos;
                dst = prtd->params->dma_addr;
                src_bidx = data_type;
                dst_bidx = 0;
+               src_cidx = data_type * fifo_level;
+               dst_cidx = 0;
        } else {
                src = prtd->params->dma_addr;
                dst = dma_pos;
                src_bidx = 0;
                dst_bidx = data_type;
+               src_cidx = 0;
+               dst_cidx = data_type * fifo_level;
        }
 
        acnt = prtd->params->acnt;
        edma_set_src(lch, src, INCR, W8BIT);
        edma_set_dest(lch, dst, INCR, W8BIT);
-       edma_set_src_index(lch, src_bidx, 0);
-       edma_set_dest_index(lch, dst_bidx, 0);
-       edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+
+       edma_set_src_index(lch, src_bidx, src_cidx);
+       edma_set_dest_index(lch, dst_bidx, dst_cidx);
+
+       if (!fifo_level)
+               edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+       else
+               edma_set_transfer_params(lch, acnt, fifo_level, count,
+                                                       fifo_level, ABSYNC);
 
        prtd->period++;
        if (unlikely(prtd->period >= runtime->periods))
index 8746606..c8b0d2b 100644 (file)
@@ -23,6 +23,7 @@ struct davinci_pcm_dma_params {
        enum dma_event_q eventq_no;     /* event queue number */
        unsigned char data_type;        /* xfer data type */
        unsigned char convert_mono_stereo;
+       unsigned int fifo_level;
 };
 
 
index e4dcb53..0267d2d 100644 (file)
@@ -157,7 +157,7 @@ static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
 
 
        /* codec PLL input is 25 MHz */
-       ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+       ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
                                        25000000, pll_out);
        if (ret < 0) {
                printk(KERN_ERR "Error when setting PLL input\n");
index 9f7c61e..4c8d99a 100644 (file)
@@ -213,7 +213,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* set SSP audio pll clock */
-       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
+       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
        if (ret < 0)
                return ret;
 
index d11a6d7..3bd7712 100644 (file)
@@ -305,8 +305,8 @@ static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
 /*
  * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
  */
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+       int source, unsigned int freq_in, unsigned int freq_out)
 {
        struct ssp_priv *priv = cpu_dai->private_data;
        struct ssp_device *ssp = priv->dev.ssp;
@@ -760,13 +760,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                         .channels_min = 1,
-                        .channels_max = 2,
+                        .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
@@ -780,13 +780,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
@@ -801,13 +801,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
@@ -822,13 +822,13 @@ struct snd_soc_dai pxa_ssp_dai[] = {
                .resume = pxa_ssp_resume,
                .playback = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                },
                .capture = {
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 8,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
index 9a386b4..dd678ae 100644 (file)
@@ -74,7 +74,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int zylonite_wm9713_init(struct snd_soc_codec *codec)
 {
        if (clk_pout)
-               snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
+               snd_soc_dai_set_pll(&codec->dai[0], 0, 0,
+                                   clk_get_rate(pout), 0);
 
        snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
                                  ARRAY_SIZE(zylonite_dapm_widgets));
@@ -128,7 +129,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
-       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+       ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
        if (ret < 0)
                return ret;
 
index 923428f..d7912f1 100644 (file)
@@ -56,6 +56,15 @@ config SND_S3C24XX_SOC_JIVE_WM8750
        help
          Sat Y if you want to add support for SoC audio on the Jive.
 
+config SND_S3C64XX_SOC_WM8580
+       tristate "SoC I2S Audio support for WM8580 on SMDK64XX"
+       depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410)
+       depends on BROKEN
+       select SND_SOC_WM8580
+       select SND_S3C64XX_SOC_I2S
+       help
+         Sat Y if you want to add support for SoC audio on the SMDK64XX.
+
 config SND_S3C24XX_SOC_SMDK2443_WM9710
        tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
        depends on SND_S3C24XX_SOC && MACH_SMDK2443
index 99f5a7d..7790406 100644 (file)
@@ -23,6 +23,7 @@ snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
 snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
 snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
 snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
+snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -33,4 +34,5 @@ obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
+obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
 
index 0c52e36..26409a9 100644 (file)
@@ -119,7 +119,7 @@ static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* codec PLL input is PCLK/4 */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
                iis_clkrate / 4, pll_out);
        if (ret < 0)
                return ret;
@@ -133,7 +133,7 @@ static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
 
 /*
@@ -183,7 +183,7 @@ static int neo1973_gta02_voice_hw_params(
                return ret;
 
        /* configue and enable PLL for 12.288MHz output */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
                iis_clkrate / 4, 12288000);
        if (ret < 0)
                return ret;
@@ -197,7 +197,7 @@ static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_gta02_voice_ops = {
index 906709e..c9b7948 100644 (file)
@@ -137,7 +137,7 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* codec PLL input is PCLK/4 */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
                iis_clkrate / 4, pll_out);
        if (ret < 0)
                return ret;
@@ -153,7 +153,7 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
        pr_debug("Entered %s\n", __func__);
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
 
 /*
@@ -203,7 +203,7 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        /* configue and enable PLL for 12.288MHz output */
-       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+       ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
                iis_clkrate / 4, 12288000);
        if (ret < 0)
                return ret;
@@ -219,7 +219,7 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
        pr_debug("Entered %s\n", __func__);
 
        /* disable the PLL */
-       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+       return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_voice_ops = {
index 9bc4aa3..11c45a3 100644 (file)
@@ -312,12 +312,15 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_RIGHT_J:
+               iismod |= S3C2412_IISMOD_LR_RLOW;
                iismod |= S3C2412_IISMOD_SDF_MSB;
                break;
        case SND_SOC_DAIFMT_LEFT_J:
+               iismod |= S3C2412_IISMOD_LR_RLOW;
                iismod |= S3C2412_IISMOD_SDF_LSB;
                break;
        case SND_SOC_DAIFMT_I2S:
+               iismod &= ~S3C2412_IISMOD_LR_RLOW;
                iismod |= S3C2412_IISMOD_SDF_IIS;
                break;
        default:
@@ -467,6 +470,31 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 
        switch (div_id) {
        case S3C_I2SV2_DIV_BCLK:
+               if (div > 3) {
+                       /* convert value to bit field */
+
+                       switch (div) {
+                       case 16:
+                               div = S3C2412_IISMOD_BCLK_16FS;
+                               break;
+
+                       case 32:
+                               div = S3C2412_IISMOD_BCLK_32FS;
+                               break;
+
+                       case 24:
+                               div = S3C2412_IISMOD_BCLK_24FS;
+                               break;
+
+                       case 48:
+                               div = S3C2412_IISMOD_BCLK_48FS;
+                               break;
+
+                       default:
+                               return -EINVAL;
+                       }
+               }
+
                reg = readl(i2s->regs + S3C2412_IISMOD);
                reg &= ~S3C2412_IISMOD_BCLK_MASK;
                writel(reg | div, i2s->regs + S3C2412_IISMOD);
@@ -626,7 +654,7 @@ int s3c_i2sv2_probe(struct platform_device *pdev,
        }
 
        i2s->iis_pclk = clk_get(dev, "iis");
-       if (i2s->iis_pclk == NULL) {
+       if (IS_ERR(i2s->iis_pclk)) {
                dev_err(dev, "failed to get iis_clock\n");
                iounmap(i2s->regs);
                return -ENOENT;
index 3c06c40..43fb253 100644 (file)
@@ -99,6 +99,19 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
                iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
                break;
 
+       case S3C64XX_CLKSRC_CDCLK:
+               switch (dir) {
+               case SND_SOC_CLOCK_IN:
+                       iismod |= S3C64XX_IISMOD_CDCLKCON;
+                       break;
+               case SND_SOC_CLOCK_OUT:
+                       iismod &= ~S3C64XX_IISMOD_CDCLKCON;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
+
        default:
                return -EINVAL;
        }
@@ -111,8 +124,12 @@ static int s3c64xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
 {
        struct s3c_i2sv2_info *i2s = to_info(dai);
+       u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
 
-       return i2s->iis_cclk;
+       if (iismod & S3C64XX_IISMOD_IMS_SYSMUX)
+               return i2s->iis_cclk;
+       else
+               return i2s->iis_pclk;
 }
 EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
 
index 02148ce..abe7253 100644 (file)
@@ -25,6 +25,7 @@ struct clk;
 
 #define S3C64XX_CLKSRC_PCLK    (0)
 #define S3C64XX_CLKSRC_MUX     (1)
+#define S3C64XX_CLKSRC_CDCLK    (2)
 
 extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c
new file mode 100644 (file)
index 0000000..482aaf1
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *  smdk64xx_wm8580.c
+ *
+ *  Copyright (c) 2009 Samsung Electronics Co. Ltd
+ *  Author: Jaswinder Singh <jassi.brar@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8580.h"
+#include "s3c24xx-pcm.h"
+#include "s3c64xx-i2s.h"
+
+#define S3C64XX_I2S_V4 2
+
+/* SMDK64XX has a 12MHZ crystal attached to WM8580 */
+#define SMDK64XX_WM8580_FREQ 12000000
+
+static int smdk64xx_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_dai *cpu_dai = rtd->dai->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       unsigned int pll_out;
+       int bfs, rfs, ret;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_U8:
+       case SNDRV_PCM_FORMAT_S8:
+               bfs = 16;
+               break;
+       case SNDRV_PCM_FORMAT_U16_LE:
+       case SNDRV_PCM_FORMAT_S16_LE:
+               bfs = 32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
+        * This criterion can't be met if we request PLL output
+        * as {8000x256, 64000x256, 11025x256}Hz.
+        * As a wayout, we rather change rfs to a minimum value that
+        * results in (params_rate(params) * rfs), and itself, acceptable
+        * to both - the CODEC and the CPU.
+        */
+       switch (params_rate(params)) {
+       case 16000:
+       case 22050:
+       case 32000:
+       case 44100:
+       case 48000:
+       case 88200:
+       case 96000:
+               rfs = 256;
+               break;
+       case 64000:
+               rfs = 384;
+               break;
+       case 8000:
+       case 11025:
+               rfs = 512;
+               break;
+       default:
+               return -EINVAL;
+       }
+       pll_out = params_rate(params) * rfs;
+
+       /* Set the Codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+                                        | SND_SOC_DAIFMT_NB_NF
+                                        | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       /* Set the AP DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+                                        | SND_SOC_DAIFMT_NB_NF
+                                        | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK,
+                                       0, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       /* We use PCLK for basic ops in SoC-Slave mode */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK,
+                                       0, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       /* Set WM8580 to drive MCLK from it's PLLA */
+       ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+                                       WM8580_CLKSRC_PLLA);
+       if (ret < 0)
+               return ret;
+
+       /* Explicitly set WM8580-DAC to source from MCLK */
+       ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+                                       WM8580_CLKSRC_MCLK);
+       if (ret < 0)
+               return ret;
+
+       /* Assuming the CODEC driver evaluates it's rfs too from this call */
+       ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA,
+                                       SMDK64XX_WM8580_FREQ, pll_out);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * SMDK64XX WM8580 DAI operations.
+ */
+static struct snd_soc_ops smdk64xx_ops = {
+       .hw_params = smdk64xx_hw_params,
+};
+
+/* SMDK64xx Playback widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = {
+       SND_SOC_DAPM_HP("Front-L/R", NULL),
+       SND_SOC_DAPM_HP("Center/Sub", NULL),
+       SND_SOC_DAPM_HP("Rear-L/R", NULL),
+};
+
+/* SMDK64xx Capture widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = {
+       SND_SOC_DAPM_MIC("MicIn", NULL),
+       SND_SOC_DAPM_LINE("LineIn", NULL),
+};
+
+/* SMDK-PAIFTX connections */
+static const struct snd_soc_dapm_route audio_map_tx[] = {
+       /* MicIn feeds AINL */
+       {"AINL", NULL, "MicIn"},
+
+       /* LineIn feeds AINL/R */
+       {"AINL", NULL, "LineIn"},
+       {"AINR", NULL, "LineIn"},
+};
+
+/* SMDK-PAIFRX connections */
+static const struct snd_soc_dapm_route audio_map_rx[] = {
+       /* Front Left/Right are fed VOUT1L/R */
+       {"Front-L/R", NULL, "VOUT1L"},
+       {"Front-L/R", NULL, "VOUT1R"},
+
+       /* Center/Sub are fed VOUT2L/R */
+       {"Center/Sub", NULL, "VOUT2L"},
+       {"Center/Sub", NULL, "VOUT2R"},
+
+       /* Rear Left/Right are fed VOUT3L/R */
+       {"Rear-L/R", NULL, "VOUT3L"},
+       {"Rear-L/R", NULL, "VOUT3R"},
+};
+
+static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec)
+{
+       /* Add smdk64xx specific Capture widgets */
+       snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt,
+                                 ARRAY_SIZE(wm8580_dapm_widgets_cpt));
+
+       /* Set up PAIFTX audio path */
+       snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx));
+
+       /* All enabled by default */
+       snd_soc_dapm_enable_pin(codec, "MicIn");
+       snd_soc_dapm_enable_pin(codec, "LineIn");
+
+       /* signal a DAPM event */
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec)
+{
+       /* Add smdk64xx specific Playback widgets */
+       snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk,
+                                 ARRAY_SIZE(wm8580_dapm_widgets_pbk));
+
+       /* Set up PAIFRX audio path */
+       snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx));
+
+       /* All enabled by default */
+       snd_soc_dapm_enable_pin(codec, "Front-L/R");
+       snd_soc_dapm_enable_pin(codec, "Center/Sub");
+       snd_soc_dapm_enable_pin(codec, "Rear-L/R");
+
+       /* signal a DAPM event */
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link smdk64xx_dai[] = {
+{ /* Primary Playback i/f */
+       .name = "WM8580 PAIF RX",
+       .stream_name = "Playback",
+       .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+       .codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX],
+       .init = smdk64xx_wm8580_init_paifrx,
+       .ops = &smdk64xx_ops,
+},
+{ /* Primary Capture i/f */
+       .name = "WM8580 PAIF TX",
+       .stream_name = "Capture",
+       .cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+       .codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX],
+       .init = smdk64xx_wm8580_init_paiftx,
+       .ops = &smdk64xx_ops,
+},
+};
+
+static struct snd_soc_card smdk64xx = {
+       .name = "smdk64xx",
+       .platform = &s3c24xx_soc_platform,
+       .dai_link = smdk64xx_dai,
+       .num_links = ARRAY_SIZE(smdk64xx_dai),
+};
+
+static struct snd_soc_device smdk64xx_snd_devdata = {
+       .card = &smdk64xx,
+       .codec_dev = &soc_codec_dev_wm8580,
+};
+
+static struct platform_device *smdk64xx_snd_device;
+
+static int __init smdk64xx_audio_init(void)
+{
+       int ret;
+
+       smdk64xx_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!smdk64xx_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_snd_devdata);
+       smdk64xx_snd_devdata.dev = &smdk64xx_snd_device->dev;
+       ret = platform_device_add(smdk64xx_snd_device);
+
+       if (ret)
+               platform_device_put(smdk64xx_snd_device);
+
+       return ret;
+}
+module_init(smdk64xx_audio_init);
+
+MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580");
+MODULE_LICENSE("GPL");
index c8ceddc..d2505e8 100644 (file)
@@ -77,6 +77,35 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data,
 #define snd_soc_7_9_spi_write NULL
 #endif
 
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 *cache = codec->reg_cache;
+       u8 data[2];
+
+       BUG_ON(codec->volatile_register);
+
+       data[0] = reg & 0xff;
+       data[1] = value & 0xff;
+
+       if (reg < codec->reg_cache_size)
+               cache[reg] = value;
+
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
+                                    unsigned int reg)
+{
+       u8 *cache = codec->reg_cache;
+       if (reg >= codec->reg_cache_size)
+               return -1;
+       return cache[reg];
+}
+
 static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
                              unsigned int value)
 {
@@ -150,9 +179,20 @@ static struct {
        unsigned int (*read)(struct snd_soc_codec *, unsigned int);
        unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
 } io_types[] = {
-       { 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
-       { 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
-         snd_soc_8_16_read_i2c },
+       {
+               .addr_bits = 7, .data_bits = 9,
+               .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
+               .spi_write = snd_soc_7_9_spi_write 
+       },
+       {
+               .addr_bits = 8, .data_bits = 8,
+               .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
+       },
+       {
+               .addr_bits = 8, .data_bits = 16,
+               .write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
+               .i2c_read = snd_soc_8_16_read_i2c,
+       },
 };
 
 /**
index 7ff04ad..1dec9d2 100644 (file)
@@ -1254,21 +1254,39 @@ static const struct file_operations codec_reg_fops = {
 
 static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
 {
+       char codec_root[128];
+
+       if (codec->dev)
+               snprintf(codec_root, sizeof(codec_root),
+                       "%s.%s", codec->name, dev_name(codec->dev));
+       else
+               snprintf(codec_root, sizeof(codec_root),
+                       "%s", codec->name);
+
+       codec->debugfs_codec_root = debugfs_create_dir(codec_root,
+                                                      debugfs_root);
+       if (!codec->debugfs_codec_root) {
+               printk(KERN_WARNING
+                      "ASoC: Failed to create codec debugfs directory\n");
+               return;
+       }
+
        codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
-                                                debugfs_root, codec,
-                                                &codec_reg_fops);
+                                                codec->debugfs_codec_root,
+                                                codec, &codec_reg_fops);
        if (!codec->debugfs_reg)
                printk(KERN_WARNING
                       "ASoC: Failed to create codec register debugfs file\n");
 
        codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
-                                                    debugfs_root,
+                                                    codec->debugfs_codec_root,
                                                     &codec->pop_time);
        if (!codec->debugfs_pop_time)
                printk(KERN_WARNING
                       "Failed to create pop time debugfs file\n");
 
-       codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root);
+       codec->debugfs_dapm = debugfs_create_dir("dapm",
+                                                codec->debugfs_codec_root);
        if (!codec->debugfs_dapm)
                printk(KERN_WARNING
                       "Failed to create DAPM debugfs directory\n");
@@ -1278,9 +1296,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
 
 static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
 {
-       debugfs_remove_recursive(codec->debugfs_dapm);
-       debugfs_remove(codec->debugfs_pop_time);
-       debugfs_remove(codec->debugfs_reg);
+       debugfs_remove_recursive(codec->debugfs_codec_root);
 }
 
 #else
@@ -2197,16 +2213,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
  * snd_soc_dai_set_pll - configure DAI PLL.
  * @dai: DAI
  * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
  * @freq_in: PLL input clock frequency in Hz
  * @freq_out: requested PLL output clock frequency in Hz
  *
  * Configures and enables PLL to generate output clock based on input clock.
  */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+       unsigned int freq_in, unsigned int freq_out)
 {
        if (dai->ops && dai->ops->set_pll)
-               return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
+               return dai->ops->set_pll(dai, pll_id, source,
+                                        freq_in, freq_out);
        else
                return -EINVAL;
 }
@@ -2250,6 +2268,30 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
+/**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ *           0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ *           0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+       unsigned int tx_num, unsigned int *tx_slot,
+       unsigned int rx_num, unsigned int *rx_slot)
+{
+       if (dai->ops && dai->ops->set_channel_map)
+               return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+                       rx_num, rx_slot);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
 /**
  * snd_soc_dai_set_tristate - configure DAI system or master clock.
  * @dai: DAI
index 8de6f9d..d2af872 100644 (file)
@@ -719,6 +719,10 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 
        /* Check if one of our outputs is connected */
        list_for_each_entry(path, &w->sinks, list_source) {
+               if (path->connected &&
+                   !path->connected(path->source, path->sink))
+                       continue;
+
                if (path->sink && path->sink->power_check &&
                    path->sink->power_check(path->sink)) {
                        power = 1;
@@ -1138,6 +1142,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                w->active ? "active" : "inactive");
 
        list_for_each_entry(p, &w->sources, list_sink) {
+               if (p->connected && !p->connected(w, p->sink))
+                       continue;
+
                if (p->connect)
                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
                                        " in  %s %s\n",
@@ -1145,6 +1152,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                                        p->source->name);
        }
        list_for_each_entry(p, &w->sinks, list_source) {
+               if (p->connected && !p->connected(w, p->sink))
+                       continue;
+
                if (p->connect)
                        ret += snprintf(buf + ret, PAGE_SIZE - ret,
                                        " out %s %s\n",
@@ -1192,8 +1202,8 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec)
 
 /* test and update the power status of a mux widget */
 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
-                                struct snd_kcontrol *kcontrol, int mask,
-                                int mux, int val, struct soc_enum *e)
+                                struct snd_kcontrol *kcontrol, int change,
+                                int mux, struct soc_enum *e)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
@@ -1202,7 +1212,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
            widget->id != snd_soc_dapm_value_mux)
                return -ENODEV;
 
-       if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+       if (!change)
                return 0;
 
        /* find dapm widget path assoc with kcontrol */
@@ -1387,10 +1397,13 @@ int snd_soc_dapm_sync(struct snd_soc_codec *codec)
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
 static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
-       const char *sink, const char *control, const char *source)
+                                 const struct snd_soc_dapm_route *route)
 {
        struct snd_soc_dapm_path *path;
        struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+       const char *sink = route->sink;
+       const char *control = route->control;
+       const char *source = route->source;
        int ret = 0;
 
        /* find src and dest widgets */
@@ -1414,6 +1427,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
 
        path->source = wsource;
        path->sink = wsink;
+       path->connected = route->connected;
        INIT_LIST_HEAD(&path->list);
        INIT_LIST_HEAD(&path->list_source);
        INIT_LIST_HEAD(&path->list_sink);
@@ -1514,8 +1528,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
        int i, ret;
 
        for (i = 0; i < num; i++) {
-               ret = snd_soc_dapm_add_route(codec, route->sink,
-                                            route->control, route->source);
+               ret = snd_soc_dapm_add_route(codec, route);
                if (ret < 0) {
                        printk(KERN_ERR "Failed to add route %s->%s\n",
                               route->source,
@@ -1752,7 +1765,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, mux;
+       unsigned int val, mux, change;
        unsigned int mask, bitmask;
        int ret = 0;
 
@@ -1772,20 +1785,21 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
-       dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
-       if (widget->event) {
-               if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_PRE_REG);
-                       if (ret < 0)
-                               goto out;
-               }
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
-               if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_POST_REG);
-       } else
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+       change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+       if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_PRE_REG);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+       if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_POST_REG);
 
 out:
        mutex_unlock(&widget->codec->mutex);
@@ -1793,6 +1807,54 @@ out:
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 
+/**
+ * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = widget->value;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
+
+/**
+ * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e =
+               (struct soc_enum *)kcontrol->private_value;
+       int change;
+       int ret = 0;
+
+       if (ucontrol->value.enumerated.item[0] >= e->max)
+               return -EINVAL;
+
+       mutex_lock(&widget->codec->mutex);
+
+       change = widget->value != ucontrol->value.enumerated.item[0];
+       widget->value = ucontrol->value.enumerated.item[0];
+       dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+
+       mutex_unlock(&widget->codec->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
+
 /**
  * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
  *                                     callback
@@ -1851,7 +1913,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, mux;
+       unsigned int val, mux, change;
        unsigned int mask;
        int ret = 0;
 
@@ -1869,20 +1931,21 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
-       dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
-       if (widget->event) {
-               if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_PRE_REG);
-                       if (ret < 0)
-                               goto out;
-               }
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
-               if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-                       ret = widget->event(widget,
-                               kcontrol, SND_SOC_DAPM_POST_REG);
-       } else
-               ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+       change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+       if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_PRE_REG);
+               if (ret < 0)
+                       goto out;
+       }
+
+       ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+       if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+               ret = widget->event(widget,
+                                   kcontrol, SND_SOC_DAPM_POST_REG);
 
 out:
        mutex_unlock(&widget->codec->mutex);