]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Merge commit 'v2.6.34-rc1' into for-2.6.35
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 10 Mar 2010 15:02:37 +0000 (15:02 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 10 Mar 2010 15:02:37 +0000 (15:02 +0000)
22 files changed:
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/wm8960.h [new file with mode: 0644]
sound/soc/atmel/atmel-pcm.c
sound/soc/blackfin/bf5xx-sport.h
sound/soc/codecs/da7210.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8960.h
sound/soc/davinci/davinci-evm.c
sound/soc/imx/Kconfig
sound/soc/imx/Makefile
sound/soc/imx/wm1133-ev1.c [new file with mode: 0644]
sound/soc/s3c24xx/s3c-i2s-v2.c
sound/soc/s3c24xx/s3c-i2s-v2.h
sound/soc/s3c24xx/s3c2412-i2s.h
sound/soc/s3c24xx/s3c64xx-i2s.c
sound/soc/s3c24xx/s3c64xx-i2s.h
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 061f16d4c8780b18f15cafaefb760a5e0780af59..6cf76a41501e687826afffffb4dd655dddfc6b03 100644 (file)
@@ -182,6 +182,12 @@ struct snd_soc_dai_ops {
                struct snd_soc_dai *);
        int (*trigger)(struct snd_pcm_substream *, int,
                struct snd_soc_dai *);
+       /*
+        * For hardware based FIFO caused delay reporting.
+        * Optional.
+        */
+       snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
+               struct snd_soc_dai *);
 };
 
 /*
@@ -215,7 +221,6 @@ struct snd_soc_dai {
        unsigned int symmetric_rates:1;
 
        /* DAI runtime info */
-       struct snd_pcm_runtime *runtime;
        struct snd_soc_codec *codec;
        unsigned int active;
        unsigned char pop_wait:1;
index c0922a0342238bc1b9f10335710208467d83bc27..2c8eb0a331c148e6dc5bd387b9f315c53c15ff96 100644 (file)
@@ -427,7 +427,6 @@ struct snd_soc_dapm_widget {
        unsigned char ext:1;                    /* has external widgets */
        unsigned char muted:1;                  /* muted for pop reduction */
        unsigned char suspend:1;                /* was active before suspend */
-       unsigned char pmdown:1;                 /* waiting for timeout */
 
        int (*power_check)(struct snd_soc_dapm_widget *w);
 
index 5d234a8c2506c74df25d5fbc7f9220c0bdb106ad..dbfec16015de1327b4f3ea4ec5b99c0e9be60a56 100644 (file)
@@ -212,6 +212,7 @@ struct snd_soc_dai_mode;
 struct snd_soc_pcm_runtime;
 struct snd_soc_dai;
 struct snd_soc_platform;
+struct snd_soc_dai_link;
 struct snd_soc_codec;
 struct soc_enum;
 struct snd_soc_ac97_ops;
@@ -374,7 +375,7 @@ struct snd_soc_pcm_stream {
        unsigned int rate_max;          /* max rate */
        unsigned int channels_min;      /* min channels */
        unsigned int channels_max;      /* max channels */
-       unsigned int active:1;          /* stream is in use */
+       unsigned int active;            /* num of active users of the stream */
 };
 
 /* SoC audio ops */
@@ -461,14 +462,21 @@ struct snd_soc_platform {
 
        int (*probe)(struct platform_device *pdev);
        int (*remove)(struct platform_device *pdev);
-       int (*suspend)(struct snd_soc_dai *dai);
-       int (*resume)(struct snd_soc_dai *dai);
+       int (*suspend)(struct snd_soc_dai_link *dai_link);
+       int (*resume)(struct snd_soc_dai_link *dai_link);
 
        /* pcm creation and destruction */
        int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
                struct snd_pcm *);
        void (*pcm_free)(struct snd_pcm *);
 
+       /*
+        * For platform caused delay reporting.
+        * Optional.
+        */
+       snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
+               struct snd_soc_dai *);
+
        /* platform stream ops */
        struct snd_pcm_ops *pcm_ops;
 };
diff --git a/include/sound/wm8960.h b/include/sound/wm8960.h
new file mode 100644 (file)
index 0000000..74e9a95
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * wm8960.h  --  WM8960 Soc Audio driver platform data
+ *
+ * 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 _WM8960_PDATA_H
+#define _WM8960_PDATA_H
+
+#define WM8960_DRES_400R 0
+#define WM8960_DRES_200R 1
+#define WM8960_DRES_600R 2
+#define WM8960_DRES_150R 3
+#define WM8960_DRES_MAX  3
+
+struct wm8960_data {
+       bool capless;  /* Headphone outputs configured in capless mode */
+
+       int dres;  /* Discharge resistance for headphone outputs */
+};
+
+#endif
index 9ef6b96373f598cbb2bf670953a6a85a96bf710c..fdb2553721273b8b2f5423fd676e635e1b15c380 100644 (file)
@@ -415,9 +415,12 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
 }
 
 #ifdef CONFIG_PM
-static int atmel_pcm_suspend(struct snd_soc_dai *dai)
+static int atmel_pcm_suspend(struct snd_soc_dai_link *dai_link)
 {
-       struct snd_pcm_runtime *runtime = dai->runtime;
+       struct snd_pcm *pcm = dai_link->pcm;
+       struct snd_pcm_str *stream = &pcm->streams[0];
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct atmel_runtime_data *prtd;
        struct atmel_pcm_dma_params *params;
 
@@ -439,9 +442,12 @@ static int atmel_pcm_suspend(struct snd_soc_dai *dai)
        return 0;
 }
 
-static int atmel_pcm_resume(struct snd_soc_dai *dai)
+static int atmel_pcm_resume(struct snd_soc_dai_link *dai_link)
 {
-       struct snd_pcm_runtime *runtime = dai->runtime;
+       struct snd_pcm *pcm = dai_link->pcm;
+       struct snd_pcm_str *stream = &pcm->streams[0];
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct atmel_runtime_data *prtd;
        struct atmel_pcm_dma_params *params;
 
index 2e63dea73e9c64631a980dd8cc5934504e19f84b..a86e8cc0b2d30ab45b36e7ab489e8cbc7d7e8f2f 100644 (file)
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <asm/dma.h>
-
-struct sport_register {
-       u16 tcr1;       u16 reserved0;
-       u16 tcr2;       u16 reserved1;
-       u16 tclkdiv;    u16 reserved2;
-       u16 tfsdiv;     u16 reserved3;
-       u32 tx;
-       u32 reserved_l0;
-       u32 rx;
-       u32 reserved_l1;
-       u16 rcr1;       u16 reserved4;
-       u16 rcr2;       u16 reserved5;
-       u16 rclkdiv;    u16 reserved6;
-       u16 rfsdiv;     u16 reserved7;
-       u16 stat;       u16 reserved8;
-       u16 chnl;       u16 reserved9;
-       u16 mcmc1;      u16 reserved10;
-       u16 mcmc2;      u16 reserved11;
-       u32 mtcs0;
-       u32 mtcs1;
-       u32 mtcs2;
-       u32 mtcs3;
-       u32 mrcs0;
-       u32 mrcs1;
-       u32 mrcs2;
-       u32 mrcs3;
-};
+#include <asm/bfin_sport.h>
 
 #define DESC_ELEMENT_COUNT 9
 
index cf2975a7294af95ec306851c92785d64d0e8199c..3bd867de597b14b4a87dd52e8f04aa53abb9de3a 100644 (file)
 /* INMIX_R bit fields */
 #define DA7210_IN_R_EN                 (1 << 7)
 
-/* ADC_HPF bit fields */
-#define DA7210_ADC_VOICE_EN            (1 << 7)
-
 /* ADC bit fields */
 #define DA7210_ADC_L_EN                        (1 << 3)
 #define DA7210_ADC_R_EN                        (1 << 7)
 
-/* DAC_HPF fields */
-#define DA7210_DAC_VOICE_EN            (1 << 7)
+/* DAC/ADC HPF fields */
+#define DA7210_VOICE_F0_MASK           (0x7 << 4)
+#define DA7210_VOICE_F0_25             (1 << 4)
+#define DA7210_VOICE_EN                        (1 << 7)
 
 /* DAC_SEL bit fields */
 #define DA7210_DAC_L_SRC_DAI_L         (4 << 0)
 #define DA7210_PLL_BYP                 (1 << 6)
 
 /* PLL bit fields */
-#define DA7210_PLL_FS_48000            (11 << 0)
+#define DA7210_PLL_FS_MASK             (0xF << 0)
+#define DA7210_PLL_FS_8000             (0x1 << 0)
+#define DA7210_PLL_FS_12000            (0x3 << 0)
+#define DA7210_PLL_FS_16000            (0x5 << 0)
+#define DA7210_PLL_FS_24000            (0x7 << 0)
+#define DA7210_PLL_FS_32000            (0x9 << 0)
+#define DA7210_PLL_FS_48000            (0xB << 0)
+#define DA7210_PLL_FS_96000            (0xF << 0)
+
 
 #define DA7210_VERSION "0.0.1"
 
@@ -241,7 +248,8 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->card->codec;
        u32 dai_cfg1;
-       u32 reg, mask;
+       u32 hpf_reg, hpf_mask, hpf_value;
+       u32 fs;
 
        /* set DAI source to Left and Right ADC */
        da7210_write(codec, DA7210_DAI_SRC_SEL,
@@ -265,25 +273,46 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
 
        da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
 
-       /* FIXME
-        *
-        * It support 48K only now
-        */
+       hpf_reg = (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) ?
+               DA7210_DAC_HPF : DA7210_ADC_HPF;
+
        switch (params_rate(params)) {
+       case 8000:
+               fs              = DA7210_PLL_FS_8000;
+               hpf_mask        = DA7210_VOICE_F0_MASK  | DA7210_VOICE_EN;
+               hpf_value       = DA7210_VOICE_F0_25    | DA7210_VOICE_EN;
+               break;
+       case 12000:
+               fs              = DA7210_PLL_FS_12000;
+               hpf_mask        = DA7210_VOICE_F0_MASK  | DA7210_VOICE_EN;
+               hpf_value       = DA7210_VOICE_F0_25    | DA7210_VOICE_EN;
+               break;
+       case 16000:
+               fs              = DA7210_PLL_FS_16000;
+               hpf_mask        = DA7210_VOICE_F0_MASK  | DA7210_VOICE_EN;
+               hpf_value       = DA7210_VOICE_F0_25    | DA7210_VOICE_EN;
+               break;
+       case 32000:
+               fs              = DA7210_PLL_FS_32000;
+               hpf_mask        = DA7210_VOICE_EN;
+               hpf_value       = 0;
+               break;
        case 48000:
-               if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
-                       reg  = DA7210_DAC_HPF;
-                       mask = DA7210_DAC_VOICE_EN;
-               } else {
-                       reg  = DA7210_ADC_HPF;
-                       mask = DA7210_ADC_VOICE_EN;
-               }
+               fs              = DA7210_PLL_FS_48000;
+               hpf_mask        = DA7210_VOICE_EN;
+               hpf_value       = 0;
+               break;
+       case 96000:
+               fs              = DA7210_PLL_FS_96000;
+               hpf_mask        = DA7210_VOICE_EN;
+               hpf_value       = 0;
                break;
        default:
                return -EINVAL;
        }
 
-       snd_soc_update_bits(codec, reg, mask, 0);
+       snd_soc_update_bits(codec, hpf_reg, hpf_mask, hpf_value);
+       snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
 
        return 0;
 }
index d2ff1cde6883c31412559540c7f3a37dcc2d06f8..942f5dc30801ee6ec2606e0bca43910a12f799e1 100644 (file)
@@ -139,6 +139,7 @@ SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0),
 SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
 
 SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0),
 SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
 
 SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
@@ -604,8 +605,7 @@ static int ssm2602_init(struct snd_soc_device *socdev)
        reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V);
        ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
        /*select Line in as default input*/
-       ssm2602_write(codec, SSM2602_APANA,
-                       APANA_ENABLE_MIC_BOOST2 | APANA_SELECT_DAC |
+       ssm2602_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
                        APANA_ENABLE_MIC_BOOST);
        ssm2602_write(codec, SSM2602_PWR, 0);
 
index d07bcc1e1c603ed9a86e06c2e293bdb51358d271..c2960d3ec6df1646e3a3f9dc366361a9c88f9d1f 100644 (file)
@@ -22,6 +22,7 @@
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 #include <sound/tlv.h>
+#include <sound/wm8960.h>
 
 #include "wm8960.h"
 
 struct snd_soc_codec_device soc_codec_dev_wm8960;
 
 /* R25 - Power 1 */
+#define WM8960_VMID_MASK 0x180
 #define WM8960_VREF      0x40
 
+/* R26 - Power 2 */
+#define WM8960_PWR2_LOUT1      0x40
+#define WM8960_PWR2_ROUT1      0x20
+#define WM8960_PWR2_OUT3       0x02
+
 /* R28 - Anti-pop 1 */
 #define WM8960_POBCTRL   0x80
 #define WM8960_BUFDCOPEN 0x10
@@ -41,6 +48,7 @@ struct snd_soc_codec_device soc_codec_dev_wm8960;
 
 /* R29 - Anti-pop 2 */
 #define WM8960_DISOP     0x40
+#define WM8960_DRES_MASK 0x30
 
 /*
  * wm8960 register cache
@@ -67,6 +75,9 @@ static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
 struct wm8960_priv {
        u16 reg_cache[WM8960_CACHEREGNUM];
        struct snd_soc_codec codec;
+       struct snd_soc_dapm_widget *lout1;
+       struct snd_soc_dapm_widget *rout1;
+       struct snd_soc_dapm_widget *out3;
 };
 
 #define wm8960_reset(c)        snd_soc_write(c, WM8960_RESET, 0)
@@ -225,10 +236,6 @@ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
        &wm8960_routput_mixer[0],
        ARRAY_SIZE(wm8960_routput_mixer)),
 
-SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
-       &wm8960_mono_out[0],
-       ARRAY_SIZE(wm8960_mono_out)),
-
 SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
 SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
 
@@ -247,6 +254,17 @@ SND_SOC_DAPM_OUTPUT("SPK_RN"),
 SND_SOC_DAPM_OUTPUT("OUT3"),
 };
 
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = {
+SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
+       &wm8960_mono_out[0],
+       ARRAY_SIZE(wm8960_mono_out)),
+};
+
+/* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = {
+SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0),
+};
+
 static const struct snd_soc_dapm_route audio_paths[] = {
        { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
        { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
@@ -277,9 +295,6 @@ static const struct snd_soc_dapm_route audio_paths[] = {
        { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
        { "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
 
-       { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
-       { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
-
        { "LOUT1 PGA", NULL, "Left Output Mixer" },
        { "ROUT1 PGA", NULL, "Right Output Mixer" },
 
@@ -296,17 +311,65 @@ static const struct snd_soc_dapm_route audio_paths[] = {
        { "SPK_LP", NULL, "Left Speaker Output" },
        { "SPK_RN", NULL, "Right Speaker Output" },
        { "SPK_RP", NULL, "Right Speaker Output" },
+};
+
+static const struct snd_soc_dapm_route audio_paths_out3[] = {
+       { "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
+       { "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
 
        { "OUT3", NULL, "Mono Output Mixer", }
 };
 
+static const struct snd_soc_dapm_route audio_paths_capless[] = {
+       { "HP_L", NULL, "OUT3 VMID" },
+       { "HP_R", NULL, "OUT3 VMID" },
+
+       { "OUT3 VMID", NULL, "Left Output Mixer" },
+       { "OUT3 VMID", NULL, "Right Output Mixer" },
+};
+
 static int wm8960_add_widgets(struct snd_soc_codec *codec)
 {
+       struct wm8960_data *pdata = codec->dev->platform_data;
+       struct wm8960_priv *wm8960 = codec->private_data;
+       struct snd_soc_dapm_widget *w;
+
        snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
                                  ARRAY_SIZE(wm8960_dapm_widgets));
 
        snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 
+       /* In capless mode OUT3 is used to provide VMID for the
+        * headphone outputs, otherwise it is used as a mono mixer.
+        */
+       if (pdata && pdata->capless) {
+               snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_capless,
+                                         ARRAY_SIZE(wm8960_dapm_widgets_capless));
+
+               snd_soc_dapm_add_routes(codec, audio_paths_capless,
+                                       ARRAY_SIZE(audio_paths_capless));
+       } else {
+               snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets_out3,
+                                         ARRAY_SIZE(wm8960_dapm_widgets_out3));
+
+               snd_soc_dapm_add_routes(codec, audio_paths_out3,
+                                       ARRAY_SIZE(audio_paths_out3));
+       }
+
+       /* We need to power up the headphone output stage out of
+        * sequence for capless mode.  To save scanning the widget
+        * list each time to find the desired power state do so now
+        * and save the result.
+        */
+       list_for_each_entry(w, &codec->dapm_widgets, list) {
+               if (strcmp(w->name, "LOUT1 PGA") == 0)
+                       wm8960->lout1 = w;
+               if (strcmp(w->name, "ROUT1 PGA") == 0)
+                       wm8960->rout1 = w;
+               if (strcmp(w->name, "OUT3 VMID") == 0)
+                       wm8960->out3 = w;
+       }
+       
        return 0;
 }
 
@@ -407,10 +470,9 @@ static int wm8960_mute(struct snd_soc_dai *dai, int mute)
        return 0;
 }
 
-static int wm8960_set_bias_level(struct snd_soc_codec *codec,
-                                enum snd_soc_bias_level level)
+static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec,
+                                     enum snd_soc_bias_level level)
 {
-       struct wm8960_data *pdata = codec->dev->platform_data;
        u16 reg;
 
        switch (level) {
@@ -429,18 +491,8 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
                if (codec->bias_level == SND_SOC_BIAS_OFF) {
                        /* Enable anti-pop features */
                        snd_soc_write(codec, WM8960_APOP1,
-                                    WM8960_POBCTRL | WM8960_SOFT_ST |
-                                    WM8960_BUFDCOPEN | WM8960_BUFIOEN);
-
-                       /* Discharge HP output */
-                       reg = WM8960_DISOP;
-                       if (pdata)
-                               reg |= pdata->dres << 4;
-                       snd_soc_write(codec, WM8960_APOP2, reg);
-
-                       msleep(400);
-
-                       snd_soc_write(codec, WM8960_APOP2, 0);
+                                     WM8960_POBCTRL | WM8960_SOFT_ST |
+                                     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
 
                        /* Enable & ramp VMID at 2x50k */
                        reg = snd_soc_read(codec, WM8960_POWER1);
@@ -471,8 +523,101 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
                /* Disable VMID and VREF, let them discharge */
                snd_soc_write(codec, WM8960_POWER1, 0);
                msleep(600);
+               break;
+       }
+
+       codec->bias_level = level;
+
+       return 0;
+}
+
+static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec,
+                                        enum snd_soc_bias_level level)
+{
+       struct wm8960_priv *wm8960 = codec->private_data;
+       int reg;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               switch (codec->bias_level) {
+               case SND_SOC_BIAS_STANDBY:
+                       /* Enable anti pop mode */
+                       snd_soc_update_bits(codec, WM8960_APOP1,
+                                           WM8960_POBCTRL | WM8960_SOFT_ST |
+                                           WM8960_BUFDCOPEN,
+                                           WM8960_POBCTRL | WM8960_SOFT_ST |
+                                           WM8960_BUFDCOPEN);
+
+                       /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */
+                       reg = 0;
+                       if (wm8960->lout1 && wm8960->lout1->power)
+                               reg |= WM8960_PWR2_LOUT1;
+                       if (wm8960->rout1 && wm8960->rout1->power)
+                               reg |= WM8960_PWR2_ROUT1;
+                       if (wm8960->out3 && wm8960->out3->power)
+                               reg |= WM8960_PWR2_OUT3;
+                       snd_soc_update_bits(codec, WM8960_POWER2,
+                                           WM8960_PWR2_LOUT1 |
+                                           WM8960_PWR2_ROUT1 |
+                                           WM8960_PWR2_OUT3, reg);
+
+                       /* Enable VMID at 2*50k */
+                       snd_soc_update_bits(codec, WM8960_POWER1,
+                                           WM8960_VMID_MASK, 0x80);
+
+                       /* Ramp */
+                       msleep(100);
+
+                       /* Enable VREF */
+                       snd_soc_update_bits(codec, WM8960_POWER1,
+                                           WM8960_VREF, WM8960_VREF);
+
+                       msleep(100);
+                       break;
+
+               case SND_SOC_BIAS_ON:
+                       /* Enable anti-pop mode */
+                       snd_soc_update_bits(codec, WM8960_APOP1,
+                                           WM8960_POBCTRL | WM8960_SOFT_ST |
+                                           WM8960_BUFDCOPEN,
+                                           WM8960_POBCTRL | WM8960_SOFT_ST |
+                                           WM8960_BUFDCOPEN);
+
+                       /* Disable VMID and VREF */
+                       snd_soc_update_bits(codec, WM8960_POWER1,
+                                           WM8960_VREF | WM8960_VMID_MASK, 0);
+                       break;
+
+               default:
+                       break;
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               switch (codec->bias_level) {
+               case SND_SOC_BIAS_PREPARE:
+                       /* Disable HP discharge */
+                       snd_soc_update_bits(codec, WM8960_APOP2,
+                                           WM8960_DISOP | WM8960_DRES_MASK,
+                                           0);
+
+                       /* Disable anti-pop features */
+                       snd_soc_update_bits(codec, WM8960_APOP1,
+                                           WM8960_POBCTRL | WM8960_SOFT_ST |
+                                           WM8960_BUFDCOPEN,
+                                           WM8960_POBCTRL | WM8960_SOFT_ST |
+                                           WM8960_BUFDCOPEN);
+                       break;
+
+               default:
+                       break;
+               }
+               break;
 
-               snd_soc_write(codec, WM8960_APOP1, 0);
+       case SND_SOC_BIAS_OFF:
                break;
        }
 
@@ -662,7 +807,7 @@ static int wm8960_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;
 
-       wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       codec->set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
@@ -681,8 +826,8 @@ static int wm8960_resume(struct platform_device *pdev)
                codec->hw_write(codec->control_data, data, 2);
        }
 
-       wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       wm8960_set_bias_level(codec, codec->suspend_bias_level);
+       codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       codec->set_bias_level(codec, codec->suspend_bias_level);
        return 0;
 }
 
@@ -752,6 +897,8 @@ static int wm8960_register(struct wm8960_priv *wm8960,
                goto err;
        }
 
+       codec->set_bias_level = wm8960_set_bias_level_out3;
+
        if (!pdata) {
                dev_warn(codec->dev, "No platform data supplied\n");
        } else {
@@ -759,6 +906,9 @@ static int wm8960_register(struct wm8960_priv *wm8960,
                        dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
                        pdata->dres = 0;
                }
+
+               if (pdata->capless)
+                       codec->set_bias_level = wm8960_set_bias_level_capless;
        }
 
        mutex_init(&codec->mutex);
@@ -769,7 +919,6 @@ static int wm8960_register(struct wm8960_priv *wm8960,
        codec->name = "WM8960";
        codec->owner = THIS_MODULE;
        codec->bias_level = SND_SOC_BIAS_OFF;
-       codec->set_bias_level = wm8960_set_bias_level;
        codec->dai = &wm8960_dai;
        codec->num_dai = 1;
        codec->reg_cache_size = WM8960_CACHEREGNUM;
@@ -791,7 +940,7 @@ static int wm8960_register(struct wm8960_priv *wm8960,
 
        wm8960_dai.dev = codec->dev;
 
-       wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       codec->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        /* Latch the update bits */
        reg = snd_soc_read(codec, WM8960_LINVOL);
@@ -840,7 +989,7 @@ err:
 
 static void wm8960_unregister(struct wm8960_priv *wm8960)
 {
-       wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
+       wm8960->codec.set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
        snd_soc_unregister_dai(&wm8960_dai);
        snd_soc_unregister_codec(&wm8960->codec);
        kfree(wm8960);
@@ -882,7 +1031,7 @@ MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
 
 static struct i2c_driver wm8960_i2c_driver = {
        .driver = {
-               .name = "WM8960 I2C Codec",
+               .name = "wm8960",
                .owner = THIS_MODULE,
        },
        .probe =    wm8960_i2c_probe,
index c9af56c9d9d413227aaab29dc56505253255810e..d67bfe1300dab6f7424a6f50d4703fc9ed2a46bd 100644 (file)
 extern struct snd_soc_dai wm8960_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8960;
 
-#define WM8960_DRES_400R 0
-#define WM8960_DRES_200R 1
-#define WM8960_DRES_600R 2
-#define WM8960_DRES_150R 3
-#define WM8960_DRES_MAX  3
-
-struct wm8960_data {
-       int dres;
-};
-
 #endif
index 7ccbe6684fc2b30c96babcf5c6e497c5ef708cd9..dba6651547c1da81308a0f94517e8270cc3dbe43 100644 (file)
@@ -81,10 +81,24 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int evm_spdif_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;
+
+       /* set cpu DAI configuration */
+       return snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
+}
+
 static struct snd_soc_ops evm_ops = {
        .hw_params = evm_hw_params,
 };
 
+static struct snd_soc_ops evm_spdif_ops = {
+       .hw_params = evm_spdif_hw_params,
+};
+
 /* davinci-evm machine dapm widgets */
 static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphone Jack", NULL),
@@ -165,7 +179,7 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = {
                .stream_name = "spdif",
                .cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_DIT_DAI],
                .codec_dai = &dit_stub_dai,
-               .ops = &evm_ops,
+               .ops = &evm_spdif_ops,
        },
 };
 static struct snd_soc_dai_link da8xx_evm_dai = {
index c7d0fd9b7de884522ee02f1ef838dccd8ed53269..c045da8ff61c8d34bda02e0e17e6c8f62c4fb31c 100644 (file)
@@ -11,3 +11,11 @@ config SND_IMX_SOC
 config SND_MXC_SOC_SSI
        tristate
 
+config SND_MXC_SOC_WM1133_EV1
+       tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted"
+       depends on SND_IMX_SOC && EXPERIMENTAL
+       select SND_SOC_WM8350
+       select SND_MXC_SOC_SSI
+       help
+         Enable support for audio on the i.MX31ADS with the WM1133-EV1
+         PMIC board with WM8835x fitted.
index 9f8bb92ddfcc0cf3406ecf09fe2f2ad46e2628e1..2d203635ac116e6126c08fd3626af7cdc49088cc 100644 (file)
@@ -9,4 +9,7 @@ obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
 
 # i.MX Machine Support
 snd-soc-phycore-ac97-objs := phycore-ac97.o
+snd-soc-wm1133-ev1-objs := wm1133-ev1.o
+
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
+obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/imx/wm1133-ev1.c
new file mode 100644 (file)
index 0000000..b75fcde
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ *  wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS
+ *
+ *  Copyright (c) 2010 Wolfson Microelectronics plc
+ *  Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  Based on an earlier driver for the same hardware by Liam Girdwood.
+ *
+ *  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/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <mach/audmux.h>
+
+#include "imx-ssi.h"
+#include "../codecs/wm8350.h"
+
+/* There is a silicon mic on the board optionally connected via a solder pad
+ * SP1.  Define this to enable it.
+ */
+#undef USE_SIMIC
+
+struct _wm8350_audio {
+       unsigned int channels;
+       snd_pcm_format_t format;
+       unsigned int rate;
+       unsigned int sysclk;
+       unsigned int bclkdiv;
+       unsigned int clkdiv;
+       unsigned int lr_rate;
+};
+
+/* in order of power consumption per rate (lowest first) */
+static const struct _wm8350_audio wm8350_audio[] = {
+       /* 16bit mono modes */
+       {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1,
+        WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,},
+
+       /* 16 bit stereo modes */
+       {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000,
+        WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000,
+        WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000,
+        WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000,
+        WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000,
+        WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600,
+        WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600,
+        WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600,
+        WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+       {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200,
+        WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,},
+
+       /* 24bit stereo modes */
+       {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000,
+        WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+       {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000,
+        WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+       {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600,
+        WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+       {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200,
+        WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,},
+};
+
+static int wm1133_ev1_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 *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       int i, found = 0;
+       snd_pcm_format_t format = params_format(params);
+       unsigned int rate = params_rate(params);
+       unsigned int channels = params_channels(params);
+       u32 dai_format;
+
+       /* find the correct audio parameters */
+       for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) {
+               if (rate == wm8350_audio[i].rate &&
+                   format == wm8350_audio[i].format &&
+                   channels == wm8350_audio[i].channels) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (!found)
+               return -EINVAL;
+
+       /* codec FLL input is 14.75 MHz from MCLK */
+       snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk);
+
+       dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+               SND_SOC_DAIFMT_CBM_CFM;
+
+       /* set codec DAI configuration */
+       snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+       /* set cpu DAI configuration */
+       snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+       /* TODO: The SSI driver should figure this out for us */
+       switch (channels) {
+       case 2:
+               snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0);
+               break;
+       case 1:
+               snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffe, 0xffffffe, 1, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set MCLK as the codec system clock for DAC and ADC */
+       snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK,
+                              wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN);
+
+       /* set codec BCLK division for sample rate */
+       snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV,
+                              wm8350_audio[i].bclkdiv);
+
+       /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */
+       snd_soc_dai_set_clkdiv(codec_dai,
+                              WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate);
+       snd_soc_dai_set_clkdiv(codec_dai,
+                              WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate);
+
+       /* now configure DAC and ADC clocks */
+       snd_soc_dai_set_clkdiv(codec_dai,
+                              WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv);
+
+       snd_soc_dai_set_clkdiv(codec_dai,
+                              WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv);
+
+       return 0;
+}
+
+static struct snd_soc_ops wm1133_ev1_ops = {
+       .hw_params = wm1133_ev1_hw_params,
+};
+
+static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = {
+#ifdef USE_SIMIC
+       SND_SOC_DAPM_MIC("SiMIC", NULL),
+#endif
+       SND_SOC_DAPM_MIC("Mic1 Jack", NULL),
+       SND_SOC_DAPM_MIC("Mic2 Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In Jack", NULL),
+       SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+};
+
+/* imx32ads soc_card audio map */
+static const struct snd_soc_dapm_route wm1133_ev1_map[] = {
+
+#ifdef USE_SIMIC
+       /* SiMIC --> IN1LN (with automatic bias) via SP1 */
+       { "IN1LN", NULL, "Mic Bias" },
+       { "Mic Bias", NULL, "SiMIC" },
+#endif
+
+       /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */
+       { "IN1LN", NULL, "Mic Bias" },
+       { "IN1LP", NULL, "Mic1 Jack" },
+       { "Mic Bias", NULL, "Mic1 Jack" },
+
+       /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */
+       { "IN1RN", NULL, "Mic Bias" },
+       { "IN1RP", NULL, "Mic1 Jack" },
+       { "Mic Bias", NULL, "Mic1 Jack" },
+
+       /* Line in Jack --> AUX (L+R) */
+       { "IN3R", NULL, "Line In Jack" },
+       { "IN3L", NULL, "Line In Jack" },
+
+       /* Out1 --> Headphone Jack */
+       { "Headphone Jack", NULL, "OUT1R" },
+       { "Headphone Jack", NULL, "OUT1L" },
+
+       /* Out1 --> Line Out Jack */
+       { "Line Out Jack", NULL, "OUT2R" },
+       { "Line Out Jack", NULL, "OUT2L" },
+};
+
+static struct snd_soc_jack hp_jack;
+
+static struct snd_soc_jack_pin hp_jack_pins[] = {
+       { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE },
+};
+
+static int wm1133_ev1_init(struct snd_soc_codec *codec)
+{
+       struct snd_soc_card *card = codec->socdev->card;
+
+       snd_soc_dapm_new_controls(codec, wm1133_ev1_widgets,
+                                 ARRAY_SIZE(wm1133_ev1_widgets));
+
+       snd_soc_dapm_add_routes(codec, wm1133_ev1_map,
+                               ARRAY_SIZE(wm1133_ev1_map));
+
+       /* Headphone jack detection */
+       snd_soc_jack_new(card, "Headphone", SND_JACK_HEADPHONE, &hp_jack);
+       snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
+                             hp_jack_pins);
+       wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
+
+       return 0;
+}
+
+
+static struct snd_soc_dai_link wm1133_ev1_dai = {
+       .name = "WM1133-EV1",
+       .stream_name = "Audio",
+       .cpu_dai = &imx_ssi_pcm_dai[0],
+       .codec_dai = &wm8350_dai,
+       .init = wm1133_ev1_init,
+       .ops = &wm1133_ev1_ops,
+       .symmetric_rates = 1,
+};
+
+static struct snd_soc_card wm1133_ev1 = {
+       .name = "WM1133-EV1",
+       .platform = &imx_soc_platform,
+       .dai_link = &wm1133_ev1_dai,
+       .num_links = 1,
+};
+
+static struct snd_soc_device wm1133_ev1_snd_devdata = {
+       .card = &wm1133_ev1,
+       .codec_dev = &soc_codec_dev_wm8350,
+};
+
+static struct platform_device *wm1133_ev1_snd_device;
+
+static int __init wm1133_ev1_audio_init(void)
+{
+       int ret;
+       unsigned int ptcr, pdcr;
+
+       /* SSI0 mastered by port 5 */
+       ptcr = MXC_AUDMUX_V2_PTCR_SYN |
+               MXC_AUDMUX_V2_PTCR_TFSDIR |
+               MXC_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) |
+               MXC_AUDMUX_V2_PTCR_TCLKDIR |
+               MXC_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
+       pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5);
+       mxc_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr);
+
+       ptcr = MXC_AUDMUX_V2_PTCR_SYN;
+       pdcr = MXC_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0);
+       mxc_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr);
+
+       wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1);
+       if (!wm1133_ev1_snd_device)
+               return -ENOMEM;
+
+       platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1_snd_devdata);
+       wm1133_ev1_snd_devdata.dev = &wm1133_ev1_snd_device->dev;
+       ret = platform_device_add(wm1133_ev1_snd_device);
+
+       if (ret)
+               platform_device_put(wm1133_ev1_snd_device);
+
+       return ret;
+}
+module_init(wm1133_ev1_audio_init);
+
+static void __exit wm1133_ev1_audio_exit(void)
+{
+       platform_device_unregister(wm1133_ev1_snd_device);
+}
+module_exit(wm1133_ev1_audio_exit);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS");
+MODULE_LICENSE("GPL");
index e994d8374fe62dc313aca6077e8bed20c5fb186f..b846f563cb50606eb5924e3e482634cf734d763c 100644 (file)
  * option) any later version.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
 
 #include <plat/regs-s3c2412-iis.h>
@@ -469,29 +463,25 @@ 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;
+               switch (div) {
+               case 16:
+                       div = S3C2412_IISMOD_BCLK_16FS;
+                       break;
 
-                       case 32:
-                               div = S3C2412_IISMOD_BCLK_32FS;
-                               break;
+               case 32:
+                       div = S3C2412_IISMOD_BCLK_32FS;
+                       break;
 
-                       case 24:
-                               div = S3C2412_IISMOD_BCLK_24FS;
-                               break;
+               case 24:
+                       div = S3C2412_IISMOD_BCLK_24FS;
+                       break;
 
-                       case 48:
-                               div = S3C2412_IISMOD_BCLK_48FS;
-                               break;
+               case 48:
+                       div = S3C2412_IISMOD_BCLK_48FS;
+                       break;
 
-                       default:
-                               return -EINVAL;
-                       }
+               default:
+                       return -EINVAL;
                }
 
                reg = readl(i2s->regs + S3C2412_IISMOD);
@@ -502,29 +492,25 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
                break;
 
        case S3C_I2SV2_DIV_RCLK:
-               if (div > 3) {
-                       /* convert value to bit field */
-
-                       switch (div) {
-                       case 256:
-                               div = S3C2412_IISMOD_RCLK_256FS;
-                               break;
+               switch (div) {
+               case 256:
+                       div = S3C2412_IISMOD_RCLK_256FS;
+                       break;
 
-                       case 384:
-                               div = S3C2412_IISMOD_RCLK_384FS;
-                               break;
+               case 384:
+                       div = S3C2412_IISMOD_RCLK_384FS;
+                       break;
 
-                       case 512:
-                               div = S3C2412_IISMOD_RCLK_512FS;
-                               break;
+               case 512:
+                       div = S3C2412_IISMOD_RCLK_512FS;
+                       break;
 
-                       case 768:
-                               div = S3C2412_IISMOD_RCLK_768FS;
-                               break;
+               case 768:
+                       div = S3C2412_IISMOD_RCLK_768FS;
+                       break;
 
-                       default:
-                               return -EINVAL;
-                       }
+               default:
+                       return -EINVAL;
                }
 
                reg = readl(i2s->regs + S3C2412_IISMOD);
@@ -550,6 +536,21 @@ static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
        return 0;
 }
 
+static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
+                                          struct snd_soc_dai *dai)
+{
+       struct s3c_i2sv2_info *i2s = to_info(dai);
+       u32 reg = readl(i2s->regs + S3C2412_IISFIC);
+       snd_pcm_sframes_t delay;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               delay = S3C2412_IISFIC_TXCOUNT(reg);
+       else
+               delay = S3C2412_IISFIC_RXCOUNT(reg);
+
+       return delay;
+}
+
 /* default table of all avaialable root fs divisors */
 static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
 
@@ -736,6 +737,10 @@ int s3c_i2sv2_register_dai(struct snd_soc_dai *dai)
        ops->set_fmt = s3c2412_i2s_set_fmt;
        ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
 
+       /* Allow overriding by (for example) IISv4 */
+       if (!ops->delay)
+               ops->delay = s3c2412_i2s_delay;
+
        dai->suspend = s3c2412_i2s_suspend;
        dai->resume = s3c2412_i2s_resume;
 
index ecf8eaaed1db7a4c61e561c831a186d5b6a3c6e2..b094d3c23cbe47db0b1a7ea5d641ab9941487348 100644 (file)
 #define S3C_I2SV2_DIV_RCLK     (2)
 #define S3C_I2SV2_DIV_PRESCALER        (3)
 
+#define S3C_I2SV2_CLKSRC_PCLK          0
+#define S3C_I2SV2_CLKSRC_AUDIOBUS      1
+#define S3C_I2SV2_CLKSRC_CDCLK         2
+
 /**
  * struct s3c_i2sv2_info - S3C I2S-V2 information
  * @dev: The parent device passed to use from the probe.
index 92848e54be16d049a71b7b7f91bdbe6df04a878e..60cac002a8303620b44bad4bf9056fa7e8c2c754 100644 (file)
@@ -21,8 +21,8 @@
 #define S3C2412_DIV_RCLK       S3C_I2SV2_DIV_RCLK
 #define S3C2412_DIV_PRESCALER  S3C_I2SV2_DIV_PRESCALER
 
-#define S3C2412_CLKSRC_PCLK    (0)
-#define S3C2412_CLKSRC_I2SCLK  (1)
+#define S3C2412_CLKSRC_PCLK    S3C_I2SV2_CLKSRC_PCLK
+#define S3C2412_CLKSRC_I2SCLK  S3C_I2SV2_CLKSRC_AUDIOBUS
 
 extern struct clk *s3c2412_get_iisclk(void);
 
index 93ed3aad1631bd2090a5da8bc3af312a6f24b9fa..65528943579b80750e8e93c132f4e4c26321b551 100644 (file)
@@ -12,9 +12,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/io.h>
@@ -130,15 +127,6 @@ static int s3c64xx_i2s_probe(struct platform_device *pdev,
 }
 
 
-#define S3C64XX_I2S_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 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-
-#define S3C64XX_I2S_FMTS \
-       (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
-        SNDRV_PCM_FMTBIT_S24_LE)
-
 static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = {
        .set_sysclk     = s3c64xx_i2s_set_sysclk,       
 };
index abe7253b55fc37bf72447c69ea7e80feeb9b23b0..53d2a0a0df362492838ab37b38a07a9fe3f6716e 100644 (file)
@@ -23,9 +23,18 @@ struct clk;
 #define S3C64XX_DIV_RCLK       S3C_I2SV2_DIV_RCLK
 #define S3C64XX_DIV_PRESCALER  S3C_I2SV2_DIV_PRESCALER
 
-#define S3C64XX_CLKSRC_PCLK    (0)
-#define S3C64XX_CLKSRC_MUX     (1)
-#define S3C64XX_CLKSRC_CDCLK    (2)
+#define S3C64XX_CLKSRC_PCLK    S3C_I2SV2_CLKSRC_PCLK
+#define S3C64XX_CLKSRC_MUX     S3C_I2SV2_CLKSRC_AUDIOBUS
+#define S3C64XX_CLKSRC_CDCLK    S3C_I2SV2_CLKSRC_CDCLK
+
+#define S3C64XX_I2S_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 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+
+#define S3C64XX_I2S_FMTS \
+       (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+        SNDRV_PCM_FMTBIT_S24_LE)
 
 extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
index 5869dc3be7815cc6cbadb03165c2f9d84d4dae86..bf593a834f5ac2aa16aae9600bcfdc3953a4ffb1 100644 (file)
@@ -366,6 +366,84 @@ static int snd_soc_16_8_spi_write(void *control_data, const char *data,
 #define snd_soc_16_8_spi_write NULL
 #endif
 
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
+                                          unsigned int r)
+{
+       struct i2c_msg xfer[2];
+       u16 reg = cpu_to_be16(r);
+       u16 data;
+       int ret;
+       struct i2c_client *client = codec->control_data;
+
+       /* Write register */
+       xfer[0].addr = client->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = 2;
+       xfer[0].buf = (u8 *)&reg;
+
+       /* Read data */
+       xfer[1].addr = client->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = 2;
+       xfer[1].buf = (u8 *)&data;
+
+       ret = i2c_transfer(client->adapter, xfer, 2);
+       if (ret != 2) {
+               dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+               return 0;
+       }
+
+       return be16_to_cpu(data);
+}
+#else
+#define snd_soc_16_16_read_i2c NULL
+#endif
+
+static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec,
+                                      unsigned int reg)
+{
+       u16 *cache = codec->reg_cache;
+
+       if (reg >= codec->reg_cache_size ||
+           snd_soc_codec_volatile_register(codec, reg)) {
+               if (codec->cache_only)
+                       return -EINVAL;
+
+               return codec->hw_read(codec, reg);
+       }
+
+       return cache[reg];
+}
+
+static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
+                              unsigned int value)
+{
+       u16 *cache = codec->reg_cache;
+       u8 data[4];
+       int ret;
+
+       data[0] = (reg >> 8) & 0xff;
+       data[1] = reg & 0xff;
+       data[2] = (value >> 8) & 0xff;
+       data[3] = value & 0xff;
+
+       if (reg < codec->reg_cache_size)
+               cache[reg] = value;
+
+       if (codec->cache_only) {
+               codec->cache_sync = 1;
+               return 0;
+       }
+
+       ret = codec->hw_write(codec->control_data, data, 4);
+       if (ret == 4)
+               return 0;
+       if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
 
 static struct {
        int addr_bits;
@@ -400,6 +478,11 @@ static struct {
                .i2c_read = snd_soc_16_8_read_i2c,
                .spi_write = snd_soc_16_8_spi_write,
        },
+       {
+               .addr_bits = 16, .data_bits = 16,
+               .write = snd_soc_16_16_write, .read = snd_soc_16_16_read,
+               .i2c_read = snd_soc_16_16_read_i2c,
+       },
 };
 
 /**
index c8b0556ef4316a0030a4b392226fc733dbf48877..06c38d1502b76510f26cb89cdda51004f70b9304 100644 (file)
@@ -315,7 +315,7 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
 
        if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates ||
            machine->symmetric_rates) {
-               dev_dbg(card->dev, "Symmetry forces %dHz rate\n", 
+               dev_dbg(card->dev, "Symmetry forces %dHz rate\n",
                        machine->rate);
 
                ret = snd_pcm_hw_constraint_minmax(substream->runtime,
@@ -454,12 +454,15 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        pr_debug("asoc: min rate %d max rate %d\n", runtime->hw.rate_min,
                 runtime->hw.rate_max);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               cpu_dai->playback.active = codec_dai->playback.active = 1;
-       else
-               cpu_dai->capture.active = codec_dai->capture.active = 1;
-       cpu_dai->active = codec_dai->active = 1;
-       cpu_dai->runtime = runtime;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               cpu_dai->playback.active++;
+               codec_dai->playback.active++;
+       } else {
+               cpu_dai->capture.active++;
+               codec_dai->capture.active++;
+       }
+       cpu_dai->active++;
+       codec_dai->active++;
        card->codec->active++;
        mutex_unlock(&pcm_mutex);
        return 0;
@@ -535,15 +538,16 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
 
        mutex_lock(&pcm_mutex);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               cpu_dai->playback.active = codec_dai->playback.active = 0;
-       else
-               cpu_dai->capture.active = codec_dai->capture.active = 0;
-
-       if (codec_dai->playback.active == 0 &&
-               codec_dai->capture.active == 0) {
-               cpu_dai->active = codec_dai->active = 0;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               cpu_dai->playback.active--;
+               codec_dai->playback.active--;
+       } else {
+               cpu_dai->capture.active--;
+               codec_dai->capture.active--;
        }
+
+       cpu_dai->active--;
+       codec_dai->active--;
        codec->active--;
 
        /* Muting the DAC suppresses artifacts caused during digital
@@ -563,7 +567,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
 
        if (platform->pcm_ops->close)
                platform->pcm_ops->close(substream);
-       cpu_dai->runtime = NULL;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                /* start delayed pop wq here for playback streams */
@@ -801,6 +804,41 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        return 0;
 }
 
+/*
+ * soc level wrapper for pointer callback
+ * If cpu_dai, codec_dai, platform driver has the delay callback, than
+ * the runtime->delay will be updated accordingly.
+ */
+static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_platform *platform = card->platform;
+       struct snd_soc_dai_link *machine = rtd->dai;
+       struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+       struct snd_soc_dai *codec_dai = machine->codec_dai;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t offset = 0;
+       snd_pcm_sframes_t delay = 0;
+
+       if (platform->pcm_ops->pointer)
+               offset = platform->pcm_ops->pointer(substream);
+
+       if (cpu_dai->ops->delay)
+               delay += cpu_dai->ops->delay(substream, cpu_dai);
+
+       if (codec_dai->ops->delay)
+               delay += codec_dai->ops->delay(substream, codec_dai);
+
+       if (platform->delay)
+               delay += platform->delay(substream, codec_dai);
+
+       runtime->delay = delay;
+
+       return offset;
+}
+
 /* ASoC PCM operations */
 static struct snd_pcm_ops soc_pcm_ops = {
        .open           = soc_pcm_open,
@@ -809,6 +847,7 @@ static struct snd_pcm_ops soc_pcm_ops = {
        .hw_free        = soc_pcm_hw_free,
        .prepare        = soc_pcm_prepare,
        .trigger        = soc_pcm_trigger,
+       .pointer        = soc_pcm_pointer,
 };
 
 #ifdef CONFIG_PM
@@ -858,7 +897,7 @@ static int soc_suspend(struct device *dev)
                if (cpu_dai->suspend && !cpu_dai->ac97_control)
                        cpu_dai->suspend(cpu_dai);
                if (platform->suspend)
-                       platform->suspend(cpu_dai);
+                       platform->suspend(&card->dai_link[i]);
        }
 
        /* close any waiting streams and save state */
@@ -947,7 +986,7 @@ static void soc_resume_deferred(struct work_struct *work)
                if (cpu_dai->resume && !cpu_dai->ac97_control)
                        cpu_dai->resume(cpu_dai);
                if (platform->resume)
-                       platform->resume(cpu_dai);
+                       platform->resume(&card->dai_link[i]);
        }
 
        if (card->resume_post)
@@ -1335,7 +1374,6 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        dai_link->pcm = pcm;
        pcm->private_data = rtd;
        soc_pcm_ops.mmap = platform->pcm_ops->mmap;
-       soc_pcm_ops.pointer = platform->pcm_ops->pointer;
        soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
        soc_pcm_ops.copy = platform->pcm_ops->copy;
        soc_pcm_ops.silence = platform->pcm_ops->silence;
index 6c3351095786f224448f62277d61aa583dfe000a..86ded22e36afe33953bd6e78b0a75d2979b03985 100644 (file)
@@ -97,7 +97,6 @@ static void pop_dbg(u32 pop_time, const char *fmt, ...)
 
        if (pop_time) {
                vprintk(fmt, args);
-               pop_wait(pop_time);
        }
 
        va_end(args);
@@ -314,8 +313,8 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
                pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
                        widget->name, widget->power ? "on" : "off",
                        codec->pop_time);
-               snd_soc_write(codec, widget->reg, new);
                pop_wait(codec->pop_time);
+               snd_soc_write(codec, widget->reg, new);
        }
        pr_debug("reg %x old %x new %x change %d\n", widget->reg,
                 old, new, change);
@@ -1075,6 +1074,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 
        pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
                codec->pop_time);
+       pop_wait(codec->pop_time);
 
        return 0;
 }