Merge branch 'for-3.0' into for-3.1
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 13 Jun 2011 18:21:09 +0000 (19:21 +0100)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Mon, 13 Jun 2011 18:21:09 +0000 (19:21 +0100)
Trival fixup for move of I/O code into separate file.

Conflicts:
sound/soc/soc-cache.c

56 files changed:
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
sound/soc/Makefile
sound/soc/atmel/atmel-pcm.c
sound/soc/atmel/atmel-pcm.h
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/au1x/dbdma2.c
sound/soc/blackfin/bf5xx-ac97-pcm.c
sound/soc/blackfin/bf5xx-i2s-pcm.c
sound/soc/blackfin/bf5xx-tdm-pcm.c
sound/soc/codecs/ad1836.c
sound/soc/codecs/ad1836.h
sound/soc/codecs/ak4641.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/max98088.c
sound/soc/codecs/max98095.c
sound/soc/codecs/wm8915.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8962.c
sound/soc/davinci/davinci-pcm.c
sound/soc/ep93xx/ep93xx-pcm.c
sound/soc/fsl/fsl_dma.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/mpc5200_dma.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/p1022_ds.c
sound/soc/imx/imx-pcm-fiq.c
sound/soc/imx/imx-ssi.c
sound/soc/imx/imx-ssi.h
sound/soc/jz4740/jz4740-pcm.c
sound/soc/kirkwood/kirkwood-dma.c
sound/soc/mid-x86/sst_platform.c
sound/soc/nuc900/nuc900-ac97.c
sound/soc/nuc900/nuc900-pcm.c
sound/soc/omap/ams-delta.c
sound/soc/omap/omap-pcm.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/s6000/s6000-pcm.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/dma.c
sound/soc/samsung/speyside.c
sound/soc/samsung/speyside_wm8962.c [new file with mode: 0644]
sound/soc/sh/dma-sh7760.c
sound/soc/sh/fsi.c
sound/soc/sh/siu_pcm.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-io.c [new file with mode: 0644]
sound/soc/soc-pcm.c [new file with mode: 0644]
sound/soc/tegra/tegra_pcm.c
sound/soc/tegra/tegra_wm8903.c
sound/soc/txx9/txx9aclc.c

index 1bafe95..5ad5f3a 100644 (file)
@@ -209,6 +209,10 @@ struct snd_soc_dai_driver {
        struct snd_soc_pcm_stream capture;
        struct snd_soc_pcm_stream playback;
        unsigned int symmetric_rates:1;
+
+       /* probe ordering - for components with runtime dependencies */
+       int probe_order;
+       int remove_order;
 };
 
 /*
index c46e7d8..7c5465b 100644 (file)
@@ -348,6 +348,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
                            const struct snd_soc_dapm_route *route, int num);
+int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
+                            const struct snd_soc_dapm_route *route, int num);
 
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
@@ -510,7 +512,7 @@ struct snd_soc_dapm_context {
        struct snd_soc_card *card; /* parent card */
 
        /* used during DAPM updates */
-       int dev_power;
+       enum snd_soc_bias_level target_bias_level;
        struct list_head list;
 
 #ifdef CONFIG_DEBUG_FS
index 3a4bd3a..6424b10 100644 (file)
 #define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
        SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
 
+/*
+ * Component probe and remove ordering levels for components with runtime
+ * dependencies.
+ */
+#define SND_SOC_COMP_ORDER_FIRST               -2
+#define SND_SOC_COMP_ORDER_EARLY               -1
+#define SND_SOC_COMP_ORDER_NORMAL              0
+#define SND_SOC_COMP_ORDER_LATE                1
+#define SND_SOC_COMP_ORDER_LAST                2
+
 /*
  * Bias levels
  *
  * @OFF:     Power Off. No restrictions on transition times.
  */
 enum snd_soc_bias_level {
-       SND_SOC_BIAS_OFF,
-       SND_SOC_BIAS_STANDBY,
-       SND_SOC_BIAS_PREPARE,
-       SND_SOC_BIAS_ON,
+       SND_SOC_BIAS_OFF = 0,
+       SND_SOC_BIAS_STANDBY = 1,
+       SND_SOC_BIAS_PREPARE = 2,
+       SND_SOC_BIAS_ON = 3,
 };
 
 struct snd_jack;
@@ -258,6 +268,11 @@ enum snd_soc_compress_type {
        SND_SOC_RBTREE_COMPRESSION
 };
 
+enum snd_soc_pcm_subclass {
+       SND_SOC_PCM_CLASS_PCM   = 0,
+       SND_SOC_PCM_CLASS_BE    = 1,
+};
+
 int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
                             unsigned int freq, int dir);
 int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
@@ -612,6 +627,10 @@ struct snd_soc_codec_driver {
 
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                             enum snd_soc_dapm_type, int);
+
+       /* probe ordering - for components with runtime dependencies */
+       int probe_order;
+       int remove_order;
 };
 
 /* SoC platform interface */
@@ -623,8 +642,7 @@ struct snd_soc_platform_driver {
        int (*resume)(struct snd_soc_dai *dai);
 
        /* pcm creation and destruction */
-       int (*pcm_new)(struct snd_card *, struct snd_soc_dai *,
-               struct snd_pcm *);
+       int (*pcm_new)(struct snd_soc_pcm_runtime *);
        void (*pcm_free)(struct snd_pcm *);
 
        /*
@@ -636,6 +654,10 @@ struct snd_soc_platform_driver {
 
        /* platform stream ops */
        struct snd_pcm_ops *ops;
+
+       /* probe ordering - for components with runtime dependencies */
+       int probe_order;
+       int remove_order;
 };
 
 struct snd_soc_platform {
@@ -725,8 +747,10 @@ struct snd_soc_card {
 
        /* callbacks */
        int (*set_bias_level)(struct snd_soc_card *,
+                             struct snd_soc_dapm_context *dapm,
                              enum snd_soc_bias_level level);
        int (*set_bias_level_post)(struct snd_soc_card *,
+                                  struct snd_soc_dapm_context *dapm,
                                   enum snd_soc_bias_level level);
 
        long pmdown_time;
@@ -789,6 +813,9 @@ struct snd_soc_pcm_runtime  {
        struct device dev;
        struct snd_soc_card *card;
        struct snd_soc_dai_link *dai_link;
+       struct mutex pcm_mutex;
+       enum snd_soc_pcm_subclass pcm_subclass;
+       struct snd_pcm_ops ops;
 
        unsigned int complete:1;
        unsigned int dev_registered:1;
index 1ed61c5..4f91387 100644 (file)
@@ -1,4 +1,5 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
+snd-soc-core-objs += soc-pcm.o soc-io.o
 
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
 obj-$(CONFIG_SND_SOC)  += codecs/
index d0e7532..f81d4c3 100644 (file)
@@ -364,9 +364,11 @@ static struct snd_pcm_ops atmel_pcm_ops = {
 \*--------------------------------------------------------------------------*/
 static u64 atmel_pcm_dmamask = 0xffffffff;
 
-static int atmel_pcm_new(struct snd_card *card,
-       struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
@@ -382,7 +384,7 @@ static int atmel_pcm_new(struct snd_card *card,
        }
 
        if (dai->driver->capture.channels_min) {
-               pr_debug("at32-pcm:"
+               pr_debug("atmel-pcm:"
                                "Allocating PCM capture DMA buffer\n");
                ret = atmel_pcm_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_CAPTURE);
index 2597329..5e0a95e 100644 (file)
@@ -60,7 +60,7 @@ struct atmel_ssc_mask {
  * This structure, shared between the PCM driver and the interface,
  * contains all information required by the PCM driver to perform the
  * PDC DMA operation.  All fields except dma_intr_handler() are initialized
- * by the interface.  The dms_intr_handler() pointer is set by the PCM
+ * by the interface.  The dma_intr_handler() pointer is set by the PCM
  * driver and called by the interface SSC interrupt handler if it is
  * non-NULL.
  */
index eda955b..7122509 100644 (file)
@@ -402,7 +402,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
        if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
                && bits > 16) {
                printk(KERN_WARNING
-                               "atmel_ssc_dai: sample size %d"
+                               "atmel_ssc_dai: sample size %d "
                                "is too large for I2S\n", bits);
                return -EINVAL;
        }
@@ -838,10 +838,8 @@ int atmel_ssc_set_audio(int ssc_id)
        }
 
        ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
-       if (!ssc_pdev) {
-               ssc_free(ssc);
+       if (!ssc_pdev)
                return -ENOMEM;
-       }
 
        /* If we can grab the SSC briefly to parent the DAI device off it */
        ssc = ssc_request(ssc_id);
index 95572d2..bad3aa1 100644 (file)
@@ -92,6 +92,7 @@ static struct snd_soc_ops at91sam9g20ek_ops = {
 };
 
 static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
+                                       struct snd_soc_dapm_context *dapm,
                                        enum snd_soc_bias_level level)
 {
        static int mclk_on;
index 10fdd28..20bb53a 100644 (file)
@@ -319,10 +319,11 @@ static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static int au1xpsc_pcm_new(struct snd_card *card,
-                          struct snd_soc_dai *dai,
-                          struct snd_pcm *pcm)
+static int au1xpsc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_pcm *pcm = rtd->pcm;
+
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1);
 
index 98b44b3..9e59f68 100644 (file)
@@ -418,9 +418,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
 
-int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+int bf5xx_pcm_ac97_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        pr_debug("%s enter\n", __func__);
index b5101ef..96d0d90 100644 (file)
@@ -248,9 +248,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
 
-int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        pr_debug("%s enter\n", __func__);
index 07cfc7a..c95cc03 100644 (file)
@@ -283,9 +283,11 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
 
-static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index 754c496..e3a9493 100644 (file)
 #include <linux/spi/spi.h>
 #include "ad1836.h"
 
+enum ad1836_type {
+       AD1835,
+       AD1836,
+       AD1838,
+};
+
 /* codec private data */
 struct ad1836_priv {
-       enum snd_soc_control_type control_type;
-       void *control_data;
+       enum ad1836_type type;
 };
 
 /*
@@ -44,29 +49,60 @@ static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
 static const struct soc_enum ad1836_deemp_enum =
        SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
 
-static const struct snd_kcontrol_new ad1836_snd_controls[] = {
-       /* DAC volume control */
-       SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
-                       AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
-       SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
-                       AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
-       SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
-                       AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
-
-       /* ADC switch control */
-       SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
-               AD1836_ADCR1_MUTE, 1, 1),
-       SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
-               AD1836_ADCR2_MUTE, 1, 1),
-
-       /* DAC switch control */
-       SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
-               AD1836_DACR1_MUTE, 1, 1),
-       SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
-               AD1836_DACR2_MUTE, 1, 1),
-       SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
-               AD1836_DACR3_MUTE, 1, 1),
+#define AD1836_DAC_VOLUME(x) \
+       SOC_DOUBLE_R("DAC" #x " Playback Volume", AD1836_DAC_L_VOL(x), \
+                       AD1836_DAC_R_VOL(x), 0, 0x3FF, 0)
+
+#define AD1836_DAC_SWITCH(x) \
+       SOC_DOUBLE("DAC" #x " Playback Switch", AD1836_DAC_CTRL2, \
+                       AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+#define AD1836_ADC_SWITCH(x) \
+       SOC_DOUBLE("ADC" #x " Capture Switch", AD1836_ADC_CTRL2, \
+               AD1836_MUTE_LEFT(x), AD1836_MUTE_RIGHT(x), 1, 1)
+
+static const struct snd_kcontrol_new ad183x_dac_controls[] = {
+       AD1836_DAC_VOLUME(1),
+       AD1836_DAC_SWITCH(1),
+       AD1836_DAC_VOLUME(2),
+       AD1836_DAC_SWITCH(2),
+       AD1836_DAC_VOLUME(3),
+       AD1836_DAC_SWITCH(3),
+       AD1836_DAC_VOLUME(4),
+       AD1836_DAC_SWITCH(4),
+};
+
+static const struct snd_soc_dapm_widget ad183x_dac_dapm_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+       SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+       SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+       SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_route ad183x_dac_routes[] = {
+       { "DAC1OUT", NULL, "DAC" },
+       { "DAC2OUT", NULL, "DAC" },
+       { "DAC3OUT", NULL, "DAC" },
+       { "DAC4OUT", NULL, "DAC" },
+};
+
+static const struct snd_kcontrol_new ad183x_adc_controls[] = {
+       AD1836_ADC_SWITCH(1),
+       AD1836_ADC_SWITCH(2),
+       AD1836_ADC_SWITCH(3),
+};
+
+static const struct snd_soc_dapm_widget ad183x_adc_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("ADC1IN"),
+       SND_SOC_DAPM_INPUT("ADC2IN"),
+};
 
+static const struct snd_soc_dapm_route ad183x_adc_routes[] = {
+       { "ADC", NULL, "ADC1IN" },
+       { "ADC", NULL, "ADC2IN" },
+};
+
+static const struct snd_kcontrol_new ad183x_controls[] = {
        /* ADC high-pass filter */
        SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
                        AD1836_ADC_HIGHPASS_FILTER, 1, 0),
@@ -75,27 +111,24 @@ static const struct snd_kcontrol_new ad1836_snd_controls[] = {
        SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
 };
 
-static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ad183x_dapm_widgets[] = {
        SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
                                AD1836_DAC_POWERDOWN, 1),
        SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
                                AD1836_ADC_POWERDOWN, 1, NULL, 0),
-       SND_SOC_DAPM_OUTPUT("DAC1OUT"),
-       SND_SOC_DAPM_OUTPUT("DAC2OUT"),
-       SND_SOC_DAPM_OUTPUT("DAC3OUT"),
-       SND_SOC_DAPM_INPUT("ADC1IN"),
-       SND_SOC_DAPM_INPUT("ADC2IN"),
 };
 
-static const struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route ad183x_dapm_routes[] = {
        { "DAC", NULL, "ADC_PWR" },
        { "ADC", NULL, "ADC_PWR" },
-       { "DAC1OUT", "DAC1 Switch", "DAC" },
-       { "DAC2OUT", "DAC2 Switch", "DAC" },
-       { "DAC3OUT", "DAC3 Switch", "DAC" },
-       { "ADC", "ADC1 Switch", "ADC1IN" },
-       { "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+static const DECLARE_TLV_DB_SCALE(ad1836_in_tlv, 0, 300, 0);
+
+static const struct snd_kcontrol_new ad1836_controls[] = {
+       SOC_DOUBLE_TLV("ADC2 Capture Volume", AD1836_ADC_CTRL1, 3, 0, 4, 0,
+           ad1836_in_tlv),
 };
 
 /*
@@ -170,19 +203,15 @@ static int ad1836_soc_suspend(struct snd_soc_codec *codec,
                pm_message_t state)
 {
        /* reset clock control mode */
-       u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
-       adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
-
-       return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+       return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+               AD1836_ADC_SERFMT_MASK, 0);
 }
 
 static int ad1836_soc_resume(struct snd_soc_codec *codec)
 {
        /* restore clock control mode */
-       u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
-       adc_ctrl2 |= AD1836_ADC_AUX;
-
-       return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+       return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+               AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX);
 }
 #else
 #define ad1836_soc_suspend NULL
@@ -194,35 +223,45 @@ static struct snd_soc_dai_ops ad1836_dai_ops = {
        .set_fmt = ad1836_set_dai_fmt,
 };
 
-/* codec DAI instance */
-static struct snd_soc_dai_driver ad1836_dai = {
-       .name = "ad1836-hifi",
-       .playback = {
-               .stream_name = "Playback",
-               .channels_min = 2,
-               .channels_max = 6,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
-                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
-       },
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 2,
-               .channels_max = 4,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
-                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
-       },
-       .ops = &ad1836_dai_ops,
+#define AD183X_DAI(_name, num_dacs, num_adcs) \
+{ \
+       .name = _name "-hifi", \
+       .playback = { \
+               .stream_name = "Playback", \
+               .channels_min = 2, \
+               .channels_max = (num_dacs) * 2, \
+               .rates = SNDRV_PCM_RATE_48000,  \
+               .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+       }, \
+       .capture = { \
+               .stream_name = "Capture", \
+               .channels_min = 2, \
+               .channels_max = (num_adcs) * 2, \
+               .rates = SNDRV_PCM_RATE_48000, \
+               .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, \
+       }, \
+       .ops = &ad1836_dai_ops, \
+}
+
+static struct snd_soc_dai_driver ad183x_dais[] = {
+       [AD1835] = AD183X_DAI("ad1835", 4, 1),
+       [AD1836] = AD183X_DAI("ad1836", 3, 2),
+       [AD1838] = AD183X_DAI("ad1838", 3, 1),
 };
 
 static int ad1836_probe(struct snd_soc_codec *codec)
 {
        struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
        struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int num_dacs, num_adcs;
        int ret = 0;
+       int i;
+
+       num_dacs = ad183x_dais[ad1836->type].playback.channels_max / 2;
+       num_adcs = ad183x_dais[ad1836->type].capture.channels_max / 2;
 
-       codec->control_data = ad1836->control_data;
        ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
        if (ret < 0) {
                dev_err(codec->dev, "failed to set cache I/O: %d\n",
@@ -239,21 +278,46 @@ static int ad1836_probe(struct snd_soc_codec *codec)
        snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
        /* unmute adc channles, adc aux mode */
        snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
-       /* left/right diff:PGA/MUX */
-       snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
        /* volume */
-       snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
-       snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
-       snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
-       snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
-       snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
-       snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
-
-       snd_soc_add_controls(codec, ad1836_snd_controls,
-                            ARRAY_SIZE(ad1836_snd_controls));
-       snd_soc_dapm_new_controls(dapm, ad1836_dapm_widgets,
-                                 ARRAY_SIZE(ad1836_dapm_widgets));
-       snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
+       for (i = 1; i <= num_dacs; ++i) {
+               snd_soc_write(codec, AD1836_DAC_L_VOL(i), 0x3FF);
+               snd_soc_write(codec, AD1836_DAC_R_VOL(i), 0x3FF);
+       }
+
+       if (ad1836->type == AD1836) {
+               /* left/right diff:PGA/MUX */
+               snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
+               ret = snd_soc_add_controls(codec, ad1836_controls,
+                               ARRAY_SIZE(ad1836_controls));
+               if (ret)
+                       return ret;
+       } else {
+               snd_soc_write(codec, AD1836_ADC_CTRL3, 0x00);
+       }
+
+       ret = snd_soc_add_controls(codec, ad183x_dac_controls, num_dacs * 2);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_add_controls(codec, ad183x_adc_controls, num_adcs);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_new_controls(dapm, ad183x_dac_dapm_widgets, num_dacs);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_new_controls(dapm, ad183x_adc_dapm_widgets, num_adcs);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, ad183x_dac_routes, num_dacs);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
+       if (ret)
+               return ret;
 
        return ret;
 }
@@ -262,10 +326,8 @@ static int ad1836_probe(struct snd_soc_codec *codec)
 static int ad1836_remove(struct snd_soc_codec *codec)
 {
        /* reset clock control mode */
-       u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
-       adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
-
-       return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
+       return snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+               AD1836_ADC_SERFMT_MASK, 0);
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
@@ -275,6 +337,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
        .resume =       ad1836_soc_resume,
        .reg_cache_size = AD1836_NUM_REGS,
        .reg_word_size = sizeof(u16),
+
+       .controls = ad183x_controls,
+       .num_controls = ARRAY_SIZE(ad183x_controls),
+       .dapm_widgets = ad183x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
+       .dapm_routes = ad183x_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
 };
 
 static int __devinit ad1836_spi_probe(struct spi_device *spi)
@@ -286,12 +355,12 @@ static int __devinit ad1836_spi_probe(struct spi_device *spi)
        if (ad1836 == NULL)
                return -ENOMEM;
 
+       ad1836->type = spi_get_device_id(spi)->driver_data;
+
        spi_set_drvdata(spi, ad1836);
-       ad1836->control_data = spi;
-       ad1836->control_type = SND_SOC_SPI;
 
        ret = snd_soc_register_codec(&spi->dev,
-                       &soc_codec_dev_ad1836, &ad1836_dai, 1);
+                       &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
        if (ret < 0)
                kfree(ad1836);
        return ret;
@@ -303,6 +372,15 @@ static int __devexit ad1836_spi_remove(struct spi_device *spi)
        kfree(spi_get_drvdata(spi));
        return 0;
 }
+static const struct spi_device_id ad1836_ids[] = {
+       { "ad1835", AD1835 },
+       { "ad1836", AD1836 },
+       { "ad1837", AD1835 },
+       { "ad1838", AD1838 },
+       { "ad1839", AD1838 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, ad1836_ids);
 
 static struct spi_driver ad1836_spi_driver = {
        .driver = {
@@ -311,6 +389,7 @@ static struct spi_driver ad1836_spi_driver = {
        },
        .probe          = ad1836_spi_probe,
        .remove         = __devexit_p(ad1836_spi_remove),
+       .id_table       = ad1836_ids,
 };
 
 static int __init ad1836_init(void)
index 9d6a3f8..f13402f 100644 (file)
 #define AD1836_DAC_WORD_LEN_OFFSET     3
 
 #define AD1836_DAC_CTRL2               1
-#define AD1836_DACL1_MUTE              0
-#define AD1836_DACR1_MUTE              1
-#define AD1836_DACL2_MUTE              2
-#define AD1836_DACR2_MUTE              3
-#define AD1836_DACL3_MUTE              4
-#define AD1836_DACR3_MUTE              5
 
-#define AD1836_DAC_L1_VOL              2
-#define AD1836_DAC_R1_VOL              3
-#define AD1836_DAC_L2_VOL              4
-#define AD1836_DAC_R2_VOL              5
-#define AD1836_DAC_L3_VOL              6
-#define AD1836_DAC_R3_VOL              7
+/* These macros are one-based. So AD183X_MUTE_LEFT(1) will return the mute bit
+ * for the first ADC/DAC */
+#define AD1836_MUTE_LEFT(x) (((x) * 2) - 2)
+#define AD1836_MUTE_RIGHT(x) (((x) * 2) - 1)
+
+#define AD1836_DAC_L_VOL(x) ((x) * 2)
+#define AD1836_DAC_R_VOL(x) (1 + ((x) * 2))
 
 #define AD1836_ADC_CTRL1               12
 #define AD1836_ADC_POWERDOWN           7
 #define AD1836_ADC_HIGHPASS_FILTER     8
 
 #define AD1836_ADC_CTRL2               13
-#define AD1836_ADCL1_MUTE              0
-#define AD1836_ADCR1_MUTE              1
-#define AD1836_ADCL2_MUTE              2
-#define AD1836_ADCR2_MUTE              3
 #define AD1836_ADC_WORD_LEN_MASK       0x30
 #define AD1836_ADC_WORD_OFFSET         5
 #define AD1836_ADC_SERFMT_MASK        (7 << 6)
index ed96f24..7a64e58 100644 (file)
@@ -457,7 +457,7 @@ static struct snd_soc_dai_ops ak4641_pcm_dai_ops = {
        .set_sysclk   = ak4641_set_dai_sysclk,
 };
 
-struct snd_soc_dai_driver ak4641_dai[] = {
+static struct snd_soc_dai_driver ak4641_dai[] = {
 {
        .name = "ak4641-hifi",
        .id = 1,
index 0206a17..6cc8678 100644 (file)
@@ -636,10 +636,7 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
 #endif /* CONFIG_PM */
 
 /*
- * ASoC codec device structure
- *
- * Assign this variable to the codec_dev field of the machine driver's
- * snd_soc_device structure.
+ * ASoC codec driver structure
  */
 static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
        .probe =                cs4270_probe,
index 4173b67..ac65a2d 100644 (file)
@@ -1397,8 +1397,6 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
        if (freq == max98088->sysclk)
                return 0;
 
-       max98088->sysclk = freq; /* remember current sysclk */
-
        /* Setup clocks for slave mode, and using the PLL
         * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
         *         0x02 (when master clk is 20MHz to 30MHz)..
index e1d282d..872a5fa 100644 (file)
@@ -1517,8 +1517,6 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
        if (freq == max98095->sysclk)
                return 0;
 
-       max98095->sysclk = freq; /* remember current sysclk */
-
        /* Setup clocks for slave mode, and using the PLL
         * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
         *         0x02 (when master clk is 20MHz to 40MHz)..
index e2ab4fa..423baa9 100644 (file)
 #define HPOUT2L 4
 #define HPOUT2R 8
 
-#define WM8915_NUM_SUPPLIES 6
+#define WM8915_NUM_SUPPLIES 4
 static const char *wm8915_supply_names[WM8915_NUM_SUPPLIES] = {
-       "DCVDD",
        "DBVDD",
        "AVDD1",
        "AVDD2",
        "CPVDD",
-       "MICVDD",
 };
 
 struct wm8915_priv {
@@ -57,6 +55,7 @@ struct wm8915_priv {
        int ldo1ena;
 
        int sysclk;
+       int sysclk_src;
 
        int fll_src;
        int fll_fref;
@@ -76,6 +75,7 @@ struct wm8915_priv {
        struct wm8915_pdata pdata;
 
        int rx_rate[WM8915_AIFS];
+       int bclk_rate[WM8915_AIFS];
 
        /* Platform dependant ReTune mobile configuration */
        int num_retune_mobile_texts;
@@ -113,8 +113,6 @@ WM8915_REGULATOR_EVENT(0)
 WM8915_REGULATOR_EVENT(1)
 WM8915_REGULATOR_EVENT(2)
 WM8915_REGULATOR_EVENT(3)
-WM8915_REGULATOR_EVENT(4)
-WM8915_REGULATOR_EVENT(5)
 
 static const u16 wm8915_reg[WM8915_MAX_REGISTER] = {
        [WM8915_SOFTWARE_RESET] = 0x8915,
@@ -1565,6 +1563,50 @@ static int wm8915_reset(struct snd_soc_codec *codec)
        return snd_soc_write(codec, WM8915_SOFTWARE_RESET, 0x8915);
 }
 
+static const int bclk_divs[] = {
+       1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
+};
+
+static void wm8915_update_bclk(struct snd_soc_codec *codec)
+{
+       struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+       int aif, best, cur_val, bclk_rate, bclk_reg, i;
+
+       /* Don't bother if we're in a low frequency idle mode that
+        * can't support audio.
+        */
+       if (wm8915->sysclk < 64000)
+               return;
+
+       for (aif = 0; aif < WM8915_AIFS; aif++) {
+               switch (aif) {
+               case 0:
+                       bclk_reg = WM8915_AIF1_BCLK;
+                       break;
+               case 1:
+                       bclk_reg = WM8915_AIF2_BCLK;
+                       break;
+               }
+
+               bclk_rate = wm8915->bclk_rate[aif];
+
+               /* Pick a divisor for BCLK as close as we can get to ideal */
+               best = 0;
+               for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+                       cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
+                       if (cur_val < 0) /* BCLK table is sorted */
+                               break;
+                       best = i;
+               }
+               bclk_rate = wm8915->sysclk / bclk_divs[best];
+               dev_dbg(codec->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+                       bclk_divs[best], bclk_rate);
+
+               snd_soc_update_bits(codec, bclk_reg,
+                                   WM8915_AIF1_BCLK_DIV_MASK, best);
+       }
+}
+
 static int wm8915_set_bias_level(struct snd_soc_codec *codec,
                                 enum snd_soc_bias_level level)
 {
@@ -1717,10 +1759,6 @@ static int wm8915_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
-static const int bclk_divs[] = {
-       1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
-};
-
 static const int dsp_divs[] = {
        48000, 32000, 16000, 8000
 };
@@ -1731,17 +1769,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_codec *codec = dai->codec;
        struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
-       int bits, i, bclk_rate, best, cur_val;
+       int bits, i, bclk_rate;
        int aifdata = 0;
-       int bclk = 0;
        int lrclk = 0;
        int dsp = 0;
-       int aifdata_reg, bclk_reg, lrclk_reg, dsp_shift;
-
-       if (!wm8915->sysclk) {
-               dev_err(codec->dev, "SYSCLK not configured\n");
-               return -EINVAL;
-       }
+       int aifdata_reg, lrclk_reg, dsp_shift;
 
        switch (dai->id) {
        case 0:
@@ -1753,7 +1785,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
                        aifdata_reg = WM8915_AIF1TX_DATA_CONFIGURATION_1;
                        lrclk_reg = WM8915_AIF1_TX_LRCLK_1;
                }
-               bclk_reg = WM8915_AIF1_BCLK;
                dsp_shift = 0;
                break;
        case 1:
@@ -1765,7 +1796,6 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
                        aifdata_reg = WM8915_AIF2TX_DATA_CONFIGURATION_1;
                        lrclk_reg = WM8915_AIF2_TX_LRCLK_1;
                }
-               bclk_reg = WM8915_AIF2_BCLK;
                dsp_shift = WM8915_DSP2_DIV_SHIFT;
                break;
        default:
@@ -1779,6 +1809,9 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
                return bclk_rate;
        }
 
+       wm8915->bclk_rate[dai->id] = bclk_rate;
+       wm8915->rx_rate[dai->id] = params_rate(params);
+
        /* Needs looking at for TDM */
        bits = snd_pcm_format_width(params_format(params));
        if (bits < 0)
@@ -1796,18 +1829,7 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
        }
        dsp |= i << dsp_shift;
 
-       /* Pick a divisor for BCLK as close as we can get to ideal */
-       best = 0;
-       for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
-               cur_val = (wm8915->sysclk / bclk_divs[i]) - bclk_rate;
-               if (cur_val < 0) /* BCLK table is sorted */
-                       break;
-               best = i;
-       }
-       bclk_rate = wm8915->sysclk / bclk_divs[best];
-       dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
-               bclk_divs[best], bclk_rate);
-       bclk |= best;
+       wm8915_update_bclk(codec);
 
        lrclk = bclk_rate / params_rate(params);
        dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
@@ -1817,14 +1839,11 @@ static int wm8915_hw_params(struct snd_pcm_substream *substream,
                            WM8915_AIF1TX_WL_MASK |
                            WM8915_AIF1TX_SLOT_LEN_MASK,
                            aifdata);
-       snd_soc_update_bits(codec, bclk_reg, WM8915_AIF1_BCLK_DIV_MASK, bclk);
        snd_soc_update_bits(codec, lrclk_reg, WM8915_AIF1RX_RATE_MASK,
                            lrclk);
        snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_2,
                            WM8915_DSP1_DIV_SHIFT << dsp_shift, dsp);
 
-       wm8915->rx_rate[dai->id] = params_rate(params);
-
        return 0;
 }
 
@@ -1838,6 +1857,9 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
        int src;
        int old;
 
+       if (freq == wm8915->sysclk && clk_id == wm8915->sysclk_src)
+               return 0;
+
        /* Disable SYSCLK while we reconfigure */
        old = snd_soc_read(codec, WM8915_AIF_CLOCKING_1) & WM8915_SYSCLK_ENA;
        snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
@@ -1882,6 +1904,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
+       wm8915_update_bclk(codec);
+
        snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
                            WM8915_SYSCLK_SRC_MASK | WM8915_SYSCLK_DIV_MASK,
                            src << WM8915_SYSCLK_SRC_SHIFT | ratediv);
@@ -1889,6 +1913,8 @@ static int wm8915_set_sysclk(struct snd_soc_dai *dai,
        snd_soc_update_bits(codec, WM8915_AIF_CLOCKING_1,
                            WM8915_SYSCLK_ENA, old);
 
+       wm8915->sysclk_src = clk_id;
+
        return 0;
 }
 
@@ -2007,6 +2033,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
                          unsigned int Fref, unsigned int Fout)
 {
        struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
+       struct i2c_client *i2c = to_i2c_client(codec->dev);
        struct _fll_div fll_div;
        unsigned long timeout;
        int ret, reg;
@@ -2093,7 +2120,18 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        else
                timeout = msecs_to_jiffies(2);
 
-       wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+       /* Allow substantially longer if we've actually got the IRQ */
+       if (i2c->irq)
+               timeout *= 1000;
+
+       ret = wait_for_completion_timeout(&wm8915->fll_lock, timeout);
+
+       if (ret == 0 && i2c->irq) {
+               dev_err(codec->dev, "Timed out waiting for FLL\n");
+               ret = -ETIMEDOUT;
+       } else {
+               ret = 0;
+       }
 
        dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
 
@@ -2101,7 +2139,7 @@ static int wm8915_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        wm8915->fll_fout = Fout;
        wm8915->fll_src = source;
 
-       return 0;
+       return ret;
 }
 
 #ifdef CONFIG_GPIOLIB
@@ -2293,6 +2331,12 @@ static void wm8915_micd(struct snd_soc_codec *codec)
                                    SND_JACK_HEADSET | SND_JACK_BTN_0);
                wm8915->jack_mic = true;
                wm8915->detecting = false;
+
+               /* Increase poll rate to give better responsiveness
+                * for buttons */
+               snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+                                   WM8915_MICD_RATE_MASK,
+                                   5 << WM8915_MICD_RATE_SHIFT);
        }
 
        /* If we detected a lower impedence during initial startup
@@ -2333,15 +2377,17 @@ static void wm8915_micd(struct snd_soc_codec *codec)
                                            SND_JACK_HEADPHONE,
                                            SND_JACK_HEADSET |
                                            SND_JACK_BTN_0);
+
+                       /* Increase the detection rate a bit for
+                        * responsiveness.
+                        */
+                       snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
+                                           WM8915_MICD_RATE_MASK,
+                                           7 << WM8915_MICD_RATE_SHIFT);
+
                        wm8915->detecting = false;
                }
        }
-
-       /* Increase poll rate to give better responsiveness for buttons */
-       if (!wm8915->detecting)
-               snd_soc_update_bits(codec, WM8915_MIC_DETECT_1,
-                                   WM8915_MICD_RATE_MASK,
-                                   5 << WM8915_MICD_RATE_SHIFT);
 }
 
 static irqreturn_t wm8915_irq(int irq, void *data)
@@ -2383,6 +2429,20 @@ static irqreturn_t wm8915_irq(int irq, void *data)
        }
 }
 
+static irqreturn_t wm8915_edge_irq(int irq, void *data)
+{
+       irqreturn_t ret = IRQ_NONE;
+       irqreturn_t val;
+
+       do {
+               val = wm8915_irq(irq, data);
+               if (val != IRQ_NONE)
+                       ret = val;
+       } while (val != IRQ_NONE);
+
+       return ret;
+}
+
 static void wm8915_retune_mobile_pdata(struct snd_soc_codec *codec)
 {
        struct wm8915_priv *wm8915 = snd_soc_codec_get_drvdata(codec);
@@ -2482,8 +2542,6 @@ static int wm8915_probe(struct snd_soc_codec *codec)
        wm8915->disable_nb[1].notifier_call = wm8915_regulator_event_1;
        wm8915->disable_nb[2].notifier_call = wm8915_regulator_event_2;
        wm8915->disable_nb[3].notifier_call = wm8915_regulator_event_3;
-       wm8915->disable_nb[4].notifier_call = wm8915_regulator_event_4;
-       wm8915->disable_nb[5].notifier_call = wm8915_regulator_event_5;
 
        /* This should really be moved into the regulator core */
        for (i = 0; i < ARRAY_SIZE(wm8915->supplies); i++) {
@@ -2709,8 +2767,14 @@ static int wm8915_probe(struct snd_soc_codec *codec)
 
                irq_flags |= IRQF_ONESHOT;
 
-               ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
-                                          irq_flags, "wm8915", codec);
+               if (irq_flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))
+                       ret = request_threaded_irq(i2c->irq, NULL,
+                                                  wm8915_edge_irq,
+                                                  irq_flags, "wm8915", codec);
+               else
+                       ret = request_threaded_irq(i2c->irq, NULL, wm8915_irq,
+                                                  irq_flags, "wm8915", codec);
+
                if (ret == 0) {
                        /* Unmask the interrupt */
                        snd_soc_update_bits(codec, WM8915_INTERRUPT_CONTROL,
index 25580e3..056daa0 100644 (file)
@@ -297,8 +297,6 @@ static int wm8940_add_widgets(struct snd_soc_codec *codec)
        if (ret)
                goto error_ret;
        ret = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-       if (ret)
-               goto error_ret;
 
 error_ret:
        return ret;
@@ -683,8 +681,6 @@ static int wm8940_resume(struct snd_soc_codec *codec)
                }
        }
        ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       if (ret)
-               goto error_ret;
 
 error_ret:
        return ret;
@@ -730,9 +726,6 @@ static int wm8940_probe(struct snd_soc_codec *codec)
        if (ret)
                return ret;
        ret = wm8940_add_widgets(codec);
-       if (ret)
-               return ret;
-
        return ret;
 }
 
index 5e05eed..8499c56 100644 (file)
@@ -78,6 +78,8 @@ struct wm8962_priv {
 #ifdef CONFIG_GPIOLIB
        struct gpio_chip gpio_chip;
 #endif
+
+       int irq;
 };
 
 /* We can't use the same notifier block for more than one supply and
@@ -1982,6 +1984,7 @@ static const unsigned int classd_tlv[] = {
        0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
        7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
 };
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 
 /* The VU bits for the headphones are in a different register to the mute
  * bits and only take effect on the PGA if it is actually powered.
@@ -2119,6 +2122,18 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4,
 
 SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0,
               classd_tlv),
+
+SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22,
+                WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22,
+                WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22,
+                WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
+                WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
+                WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
 };
 
 static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2184,6 +2199,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
                        struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = w->codec;
+       struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+       unsigned long timeout;
        int src;
        int fll;
 
@@ -2203,9 +2220,19 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
-               if (fll)
+               if (fll) {
                        snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
                                            WM8962_FLL_ENA, WM8962_FLL_ENA);
+                       if (wm8962->irq) {
+                               timeout = msecs_to_jiffies(5);
+                               timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+                                                                     timeout);
+
+                               if (timeout == 0)
+                                       dev_err(codec->dev,
+                                               "Timed out starting FLL\n");
+                       }
+               }
                break;
 
        case SND_SOC_DAPM_POST_PMD:
@@ -2763,18 +2790,44 @@ static const int bclk_divs[] = {
        1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32
 };
 
+static const int sysclk_rates[] = {
+       64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
+};
+
 static void wm8962_configure_bclk(struct snd_soc_codec *codec)
 {
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        int dspclk, i;
        int clocking2 = 0;
+       int clocking4 = 0;
        int aif2 = 0;
 
-       if (!wm8962->bclk) {
-               dev_dbg(codec->dev, "No BCLK rate configured\n");
+       if (!wm8962->sysclk_rate) {
+               dev_dbg(codec->dev, "No SYSCLK configured\n");
+               return;
+       }
+
+       if (!wm8962->bclk || !wm8962->lrclk) {
+               dev_dbg(codec->dev, "No audio clocks configured\n");
                return;
        }
 
+       for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
+               if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) {
+                       clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
+                       break;
+               }
+       }
+
+       if (i == ARRAY_SIZE(sysclk_rates)) {
+               dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
+                       wm8962->sysclk_rate / wm8962->lrclk);
+               return;
+       }
+
+       snd_soc_update_bits(codec, WM8962_CLOCKING_4,
+                           WM8962_SYSCLK_RATE_MASK, clocking4);
+
        dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
        if (dspclk < 0) {
                dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
@@ -2844,6 +2897,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
                /* VMID 2*50k */
                snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
                                    WM8962_VMID_SEL_MASK, 0x80);
+
+               wm8962_configure_bclk(codec);
                break;
 
        case SND_SOC_BIAS_STANDBY:
@@ -2876,8 +2931,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
                        snd_soc_update_bits(codec, WM8962_CLOCKING2,
                                            WM8962_CLKREG_OVD,
                                            WM8962_CLKREG_OVD);
-
-                       wm8962_configure_bclk(codec);
                }
 
                /* VMID 2*250k */
@@ -2918,10 +2971,6 @@ static const struct {
        { 96000, 6 },
 };
 
-static const int sysclk_rates[] = {
-       64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
-};
-
 static int wm8962_hw_params(struct snd_pcm_substream *substream,
                            struct snd_pcm_hw_params *params,
                            struct snd_soc_dai *dai)
@@ -2929,41 +2978,27 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
-       int rate = params_rate(params);
        int i;
        int aif0 = 0;
        int adctl3 = 0;
-       int clocking4 = 0;
 
        wm8962->bclk = snd_soc_params_to_bclk(params);
        wm8962->lrclk = params_rate(params);
 
        for (i = 0; i < ARRAY_SIZE(sr_vals); i++) {
-               if (sr_vals[i].rate == rate) {
+               if (sr_vals[i].rate == wm8962->lrclk) {
                        adctl3 |= sr_vals[i].reg;
                        break;
                }
        }
        if (i == ARRAY_SIZE(sr_vals)) {
-               dev_err(codec->dev, "Unsupported rate %dHz\n", rate);
+               dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
                return -EINVAL;
        }
 
-       if (rate % 8000 == 0)
+       if (wm8962->lrclk % 8000 == 0)
                adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
 
-       for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
-               if (sysclk_rates[i] == wm8962->sysclk_rate / rate) {
-                       clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
-                       break;
-               }
-       }
-       if (i == ARRAY_SIZE(sysclk_rates)) {
-               dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
-                       wm8962->sysclk_rate / rate);
-               return -EINVAL;
-       }
-
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                break;
@@ -2985,8 +3020,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
        snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
                            WM8962_SAMPLE_RATE_INT_MODE |
                            WM8962_SAMPLE_RATE_MASK, adctl3);
-       snd_soc_update_bits(codec, WM8962_CLOCKING_4,
-                           WM8962_SYSCLK_RATE_MASK, clocking4);
 
        wm8962_configure_bclk(codec);
 
@@ -3261,16 +3294,31 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
        dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
 
-       /* This should be a massive overestimate */
-       timeout = msecs_to_jiffies(1);
+       ret = 0;
+
+       if (fll1 & WM8962_FLL_ENA) {
+               /* This should be a massive overestimate but go even
+                * higher if we'll error out
+                */
+               if (wm8962->irq)
+                       timeout = msecs_to_jiffies(5);
+               else
+                       timeout = msecs_to_jiffies(1);
+
+               timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+                                                     timeout);
 
-       wait_for_completion_timeout(&wm8962->fll_lock, timeout);
+               if (timeout == 0 && wm8962->irq) {
+                       dev_err(codec->dev, "FLL lock timed out");
+                       ret = -ETIMEDOUT;
+               }
+       }
 
        wm8962->fll_fref = Fref;
        wm8962->fll_fout = Fout;
        wm8962->fll_src = source;
 
-       return 0;
+       return ret;
 }
 
 static int wm8962_mute(struct snd_soc_dai *dai, int mute)
@@ -3731,8 +3779,6 @@ static int wm8962_probe(struct snd_soc_codec *codec)
        int ret;
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
-       struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
-                                             dev);
        u16 *reg_cache = codec->reg_cache;
        int i, trigger, irq_pol;
        bool dmicclk, dmicdat;
@@ -3871,6 +3917,9 @@ static int wm8962_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
                            WM8962_HPOUT_VU, WM8962_HPOUT_VU);
 
+       /* Stereo control for EQ */
+       snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0);
+
        wm8962_add_widgets(codec);
 
        /* Save boards having to disable DMIC when not in use */
@@ -3899,7 +3948,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
        wm8962_init_beep(codec);
        wm8962_init_gpio(codec);
 
-       if (i2c->irq) {
+       if (wm8962->irq) {
                if (pdata && pdata->irq_active_low) {
                        trigger = IRQF_TRIGGER_LOW;
                        irq_pol = WM8962_IRQ_POL;
@@ -3911,12 +3960,13 @@ static int wm8962_probe(struct snd_soc_codec *codec)
                snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL,
                                    WM8962_IRQ_POL, irq_pol);
 
-               ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq,
+               ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq,
                                           trigger | IRQF_ONESHOT,
                                           "wm8962", codec);
                if (ret != 0) {
                        dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
-                               i2c->irq, ret);
+                               wm8962->irq, ret);
+                       wm8962->irq = 0;
                        /* Non-fatal */
                } else {
                        /* Enable some IRQs by default */
@@ -3941,12 +3991,10 @@ err:
 static int wm8962_remove(struct snd_soc_codec *codec)
 {
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
-       struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
-                                             dev);
        int i;
 
-       if (i2c->irq)
-               free_irq(i2c->irq, codec);
+       if (wm8962->irq)
+               free_irq(wm8962->irq, codec);
 
        cancel_delayed_work_sync(&wm8962->mic_work);
 
@@ -3986,6 +4034,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, wm8962);
 
+       wm8962->irq = i2c->irq;
+
        ret = snd_soc_register_codec(&i2c->dev,
                                     &soc_codec_dev_wm8962, &wm8962_dai, 1);
        if (ret < 0)
index 9d35b8c..a49e667 100644 (file)
@@ -46,11 +46,28 @@ static void print_buf_info(int slot, char *name)
 }
 #endif
 
+#define DAVINCI_PCM_FMTBITS    (\
+                               SNDRV_PCM_FMTBIT_S8     |\
+                               SNDRV_PCM_FMTBIT_U8     |\
+                               SNDRV_PCM_FMTBIT_S16_LE |\
+                               SNDRV_PCM_FMTBIT_S16_BE |\
+                               SNDRV_PCM_FMTBIT_U16_LE |\
+                               SNDRV_PCM_FMTBIT_U16_BE |\
+                               SNDRV_PCM_FMTBIT_S24_LE |\
+                               SNDRV_PCM_FMTBIT_S24_BE |\
+                               SNDRV_PCM_FMTBIT_U24_LE |\
+                               SNDRV_PCM_FMTBIT_U24_BE |\
+                               SNDRV_PCM_FMTBIT_S32_LE |\
+                               SNDRV_PCM_FMTBIT_S32_BE |\
+                               SNDRV_PCM_FMTBIT_U32_LE |\
+                               SNDRV_PCM_FMTBIT_U32_BE)
+
 static struct snd_pcm_hardware pcm_hardware_playback = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
-       .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+                SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
+                SNDRV_PCM_INFO_BATCH),
+       .formats = DAVINCI_PCM_FMTBITS,
        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
                  SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
                  SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
@@ -59,7 +76,7 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
        .rate_min = 8000,
        .rate_max = 96000,
        .channels_min = 2,
-       .channels_max = 2,
+       .channels_max = 384,
        .buffer_bytes_max = 128 * 1024,
        .period_bytes_min = 32,
        .period_bytes_max = 8 * 1024,
@@ -71,8 +88,9 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
 static struct snd_pcm_hardware pcm_hardware_capture = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-                SNDRV_PCM_INFO_PAUSE),
-       .formats = (SNDRV_PCM_FMTBIT_S16_LE),
+                SNDRV_PCM_INFO_PAUSE |
+                SNDRV_PCM_INFO_BATCH),
+       .formats = DAVINCI_PCM_FMTBITS,
        .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
                  SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
                  SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
@@ -81,7 +99,7 @@ static struct snd_pcm_hardware pcm_hardware_capture = {
        .rate_min = 8000,
        .rate_max = 96000,
        .channels_min = 2,
-       .channels_max = 2,
+       .channels_max = 384,
        .buffer_bytes_max = 128 * 1024,
        .period_bytes_min = 32,
        .period_bytes_max = 8 * 1024,
@@ -139,6 +157,22 @@ struct davinci_runtime_data {
        struct edmacc_param ram_params;
 };
 
+static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+       struct davinci_runtime_data *prtd = substream->runtime->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       prtd->period++;
+       if (unlikely(prtd->period >= runtime->periods))
+               prtd->period = 0;
+}
+
+static void davinci_pcm_period_reset(struct snd_pcm_substream *substream)
+{
+       struct davinci_runtime_data *prtd = substream->runtime->private_data;
+
+       prtd->period = 0;
+}
 /*
  * Not used with ping/pong
  */
@@ -199,10 +233,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
        else
                edma_set_transfer_params(link, acnt, fifo_level, count,
                                                        fifo_level, ABSYNC);
-
-       prtd->period++;
-       if (unlikely(prtd->period >= runtime->periods))
-               prtd->period = 0;
 }
 
 static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
@@ -217,12 +247,13 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
                return;
 
        if (snd_pcm_running(substream)) {
+               spin_lock(&prtd->lock);
                if (prtd->ram_channel < 0) {
                        /* No ping/pong must fix up link dma data*/
-                       spin_lock(&prtd->lock);
                        davinci_pcm_enqueue_dma(substream);
-                       spin_unlock(&prtd->lock);
                }
+               davinci_pcm_period_elapsed(substream);
+               spin_unlock(&prtd->lock);
                snd_pcm_period_elapsed(substream);
        }
 }
@@ -425,7 +456,8 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
 
        edma_read_slot(link, &prtd->asp_params);
        prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
-       prtd->asp_params.opt |= TCCHEN | EDMA_TCC(prtd->ram_channel & 0x3f);
+       prtd->asp_params.opt |= TCCHEN |
+               EDMA_TCC(prtd->ram_channel & 0x3f);
        edma_write_slot(link, &prtd->asp_params);
 
        /* pong */
@@ -439,7 +471,7 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
        prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
        /* interrupt after every pong completion */
        prtd->asp_params.opt |= TCINTEN | TCCHEN |
-               EDMA_TCC(EDMA_CHAN_SLOT(prtd->ram_channel));
+               EDMA_TCC(prtd->ram_channel & 0x3f);
        edma_write_slot(link, &prtd->asp_params);
 
        /* ram */
@@ -527,6 +559,13 @@ static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
+               edma_start(prtd->asp_channel);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+                   prtd->ram_channel >= 0) {
+                       /* copy 1st iram buffer */
+                       edma_start(prtd->ram_channel);
+               }
+               break;
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                edma_resume(prtd->asp_channel);
@@ -550,6 +589,7 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
 {
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
 
+       davinci_pcm_period_reset(substream);
        if (prtd->ram_channel >= 0) {
                int ret = ping_pong_dma_setup(substream);
                if (ret < 0)
@@ -565,21 +605,31 @@ static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
                print_buf_info(prtd->asp_link[0], "asp_link[0]");
                print_buf_info(prtd->asp_link[1], "asp_link[1]");
 
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       /* copy 1st iram buffer */
-                       edma_start(prtd->ram_channel);
-               }
-               edma_start(prtd->asp_channel);
+               /*
+                * There is a phase offset of 2 periods between the position
+                * used by dma setup and the position reported in the pointer
+                * function.
+                *
+                * The phase offset, when not using ping-pong buffers, is due to
+                * the two consecutive calls to davinci_pcm_enqueue_dma() below.
+                *
+                * Whereas here, with ping-pong buffers, the phase is due to
+                * there being an entire buffer transfer complete before the
+                * first dma completion event triggers davinci_pcm_dma_irq().
+                */
+               davinci_pcm_period_elapsed(substream);
+               davinci_pcm_period_elapsed(substream);
+
                return 0;
        }
-       prtd->period = 0;
        davinci_pcm_enqueue_dma(substream);
+       davinci_pcm_period_elapsed(substream);
 
        /* Copy self-linked parameter RAM entry into master channel */
        edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
        edma_write_slot(prtd->asp_channel, &prtd->asp_params);
        davinci_pcm_enqueue_dma(substream);
-       edma_start(prtd->asp_channel);
+       davinci_pcm_period_elapsed(substream);
 
        return 0;
 }
@@ -591,51 +641,23 @@ davinci_pcm_pointer(struct snd_pcm_substream *substream)
        struct davinci_runtime_data *prtd = runtime->private_data;
        unsigned int offset;
        int asp_count;
-       dma_addr_t asp_src, asp_dst;
-
+       unsigned int period_size = snd_pcm_lib_period_bytes(substream);
+
+       /*
+        * There is a phase offset of 2 periods between the position used by dma
+        * setup and the position reported in the pointer function. Either +2 in
+        * the dma setup or -2 here in the pointer function (with wrapping,
+        * both) accounts for this offset -- choose the latter since it makes
+        * the first-time setup clearer.
+        */
        spin_lock(&prtd->lock);
-       if (prtd->ram_channel >= 0) {
-               int ram_count;
-               int mod_ram;
-               dma_addr_t ram_src, ram_dst;
-               unsigned int period_size = snd_pcm_lib_period_bytes(substream);
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       /* reading ram before asp should be safe
-                        * as long as the asp transfers less than a ping size
-                        * of bytes between the 2 reads
-                        */
-                       edma_get_position(prtd->ram_channel,
-                                       &ram_src, &ram_dst);
-                       edma_get_position(prtd->asp_channel,
-                                       &asp_src, &asp_dst);
-                       asp_count = asp_src - prtd->asp_params.src;
-                       ram_count = ram_src - prtd->ram_params.src;
-                       mod_ram = ram_count % period_size;
-                       mod_ram -= asp_count;
-                       if (mod_ram < 0)
-                               mod_ram += period_size;
-                       else if (mod_ram == 0) {
-                               if (snd_pcm_running(substream))
-                                       mod_ram += period_size;
-                       }
-                       ram_count -= mod_ram;
-                       if (ram_count < 0)
-                               ram_count += period_size * runtime->periods;
-               } else {
-                       edma_get_position(prtd->ram_channel,
-                                       &ram_src, &ram_dst);
-                       ram_count = ram_dst - prtd->ram_params.dst;
-               }
-               asp_count = ram_count;
-       } else {
-               edma_get_position(prtd->asp_channel, &asp_src, &asp_dst);
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       asp_count = asp_src - runtime->dma_addr;
-               else
-                       asp_count = asp_dst - runtime->dma_addr;
-       }
+       asp_count = prtd->period - 2;
        spin_unlock(&prtd->lock);
 
+       if (asp_count < 0)
+               asp_count += runtime->periods;
+       asp_count *= period_size;
+
        offset = bytes_to_frames(runtime, asp_count);
        if (offset >= runtime->buffer_size)
                offset = 0;
@@ -811,9 +833,11 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
 
 static u64 davinci_pcm_dmamask = 0xffffffff;
 
-static int davinci_pcm_new(struct snd_card *card,
-                          struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret;
 
        if (!card->dev->dma_mask)
index a456e49..e27c417 100644 (file)
@@ -266,9 +266,11 @@ static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
 
 static u64 ep93xx_pcm_dmamask = 0xffffffff;
 
-static int ep93xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-                         struct snd_pcm *pcm)
+static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index 6680c0b..732208c 100644 (file)
@@ -294,9 +294,11 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
  * Regardless of where the memory is actually allocated, since the device can
  * technically DMA to any 36-bit address, we do need to set the DMA mask to 36.
  */
-static int fsl_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        static u64 fsl_dma_dmamask = DMA_BIT_MASK(36);
        int ret;
 
@@ -939,7 +941,7 @@ static int __devinit fsl_soc_dma_probe(struct platform_device *pdev)
 
        iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
        if (iprop)
-               dma->ssi_fifo_depth = *iprop;
+               dma->ssi_fifo_depth = be32_to_cpup(iprop);
        else
                 /* Older 8610 DTs didn't have the fifo-depth property */
                dma->ssi_fifo_depth = 8;
index 313e0cc..d48afea 100644 (file)
@@ -678,7 +678,12 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
                kfree(ssi_private);
                return ret;
        }
-       ssi_private->ssi = ioremap(res.start, 1 + res.end - res.start);
+       ssi_private->ssi = of_iomap(np, 0);
+       if (!ssi_private->ssi) {
+               dev_err(&pdev->dev, "could not map device resources\n");
+               kfree(ssi_private);
+               return -ENOMEM;
+       }
        ssi_private->ssi_phys = res.start;
        ssi_private->irq = irq_of_parse_and_map(np, 0);
 
@@ -691,7 +696,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
        /* Determine the FIFO depth. */
        iprop = of_get_property(np, "fsl,fifo-depth", NULL);
        if (iprop)
-               ssi_private->fifo_depth = *iprop;
+               ssi_private->fifo_depth = be32_to_cpup(iprop);
        else
                 /* Older 8610 DTs didn't have the fifo-depth property */
                ssi_private->fifo_depth = 8;
index fff695c..19ad0c1 100644 (file)
@@ -299,10 +299,11 @@ static struct snd_pcm_ops psc_dma_ops = {
 };
 
 static u64 psc_dma_dmamask = 0xffffffff;
-static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
-                          struct snd_pcm *pcm)
+static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(rtd->cpu_dai);
        size_t size = psc_dma_hardware.buffer_bytes_max;
        int rc = 0;
index c16c6b2..a192979 100644 (file)
@@ -233,7 +233,7 @@ static int get_parent_cell_index(struct device_node *np)
        if (!iprop)
                return -1;
 
-       return *iprop;
+       return be32_to_cpup(iprop);
 }
 
 /**
@@ -258,7 +258,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
        if (!iprop)
                return -EINVAL;
 
-       addr = *iprop;
+       addr = be32_to_cpup(iprop);
 
        bus = get_parent_cell_index(np);
        if (bus < 0)
@@ -305,7 +305,7 @@ static int get_dma_channel(struct device_node *ssi_np,
                return -EINVAL;
        }
 
-       *dma_channel_id = *iprop;
+       *dma_channel_id = be32_to_cpup(iprop);
        *dma_id = get_parent_cell_index(dma_channel_np);
        of_node_put(dma_channel_np);
 
@@ -379,7 +379,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
                ret = -EINVAL;
                goto error;
        }
-       machine_data->ssi_id = *iprop;
+       machine_data->ssi_id = be32_to_cpup(iprop);
 
        /* Get the serial format and clock direction. */
        sprop = of_get_property(np, "fsl,mode", NULL);
@@ -405,7 +405,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
                        ret = -EINVAL;
                        goto error;
                }
-               machine_data->clk_frequency = *iprop;
+               machine_data->clk_frequency = be32_to_cpup(iprop);
        } else if (strcasecmp(sprop, "i2s-master") == 0) {
                machine_data->dai_format = SND_SOC_DAIFMT_I2S;
                machine_data->codec_clk_direction = SND_SOC_CLOCK_IN;
index 66e0b68..8fa4d5f 100644 (file)
@@ -232,7 +232,7 @@ static int get_parent_cell_index(struct device_node *np)
 
        iprop = of_get_property(parent, "cell-index", NULL);
        if (iprop)
-               ret = *iprop;
+               ret = be32_to_cpup(iprop);
 
        of_node_put(parent);
 
@@ -261,7 +261,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
        if (!iprop)
                return -EINVAL;
 
-       addr = *iprop;
+       addr = be32_to_cpup(iprop);
 
        bus = get_parent_cell_index(np);
        if (bus < 0)
@@ -308,7 +308,7 @@ static int get_dma_channel(struct device_node *ssi_np,
                return -EINVAL;
        }
 
-       *dma_channel_id = *iprop;
+       *dma_channel_id = be32_to_cpup(iprop);
        *dma_id = get_parent_cell_index(dma_channel_np);
        of_node_put(dma_channel_np);
 
@@ -379,7 +379,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
                ret = -EINVAL;
                goto error;
        }
-       mdata->ssi_id = *iprop;
+       mdata->ssi_id = be32_to_cpup(iprop);
 
        /* Get the serial format and clock direction. */
        sprop = of_get_property(np, "fsl,mode", NULL);
@@ -405,7 +405,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
                        ret = -EINVAL;
                        goto error;
                }
-               mdata->clk_frequency = *iprop;
+               mdata->clk_frequency = be32_to_cpup(iprop);
        } else if (strcasecmp(sprop, "i2s-master") == 0) {
                mdata->dai_format = SND_SOC_DAIFMT_I2S;
                mdata->codec_clk_direction = SND_SOC_CLOCK_IN;
index 413b78d..309c59e 100644 (file)
@@ -238,12 +238,14 @@ static struct snd_pcm_ops imx_pcm_ops = {
 
 static int ssi_irq = 0;
 
-static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret;
 
-       ret = imx_pcm_new(card, dai, pcm);
+       ret = imx_pcm_new(rtd);
        if (ret)
                return ret;
 
index 5b13fec..158a91c 100644 (file)
@@ -388,10 +388,11 @@ static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
 static u64 imx_pcm_dmamask = DMA_BIT_MASK(32);
 
-int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
-
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index dc8a875..0a84cec 100644 (file)
@@ -225,8 +225,7 @@ struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
                struct imx_ssi *ssi);
 
 int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
-int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm);
+int imx_pcm_new(struct snd_soc_pcm_runtime *rtd);
 void imx_pcm_free(struct snd_pcm *pcm);
 
 /*
index fb1483f..a7c9578 100644 (file)
@@ -299,9 +299,11 @@ static void jz4740_pcm_free(struct snd_pcm *pcm)
 
 static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
 
-int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index e13c6ce..cd33de1 100644 (file)
@@ -312,9 +312,11 @@ static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
        return 0;
 }
 
-static int kirkwood_dma_new(struct snd_card *card,
-               struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret;
 
        if (!card->dev->dma_mask)
index 5a946b4..3e78260 100644 (file)
@@ -402,9 +402,10 @@ static void sst_pcm_free(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-                       struct snd_pcm *pcm)
+int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int retval = 0;
 
        pr_debug("sst_pcm_new called\n");
index dac6732..9c0edad 100644 (file)
@@ -356,7 +356,7 @@ static int __devinit nuc900_ac97_drvprobe(struct platform_device *pdev)
        nuc900_audio->irq_num = platform_get_irq(pdev, 0);
        if (!nuc900_audio->irq_num) {
                ret = -EBUSY;
-               goto out2;
+               goto out3;
        }
 
        nuc900_ac97_data = nuc900_audio;
index 8263f56..d589ef1 100644 (file)
@@ -315,9 +315,12 @@ static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
 }
 
 static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
-static int nuc900_dma_new(struct snd_card *card,
-       struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
+
        if (!card->dev->dma_mask)
                card->dev->dma_mask = &nuc900_pcm_dmamask;
        if (!card->dev->coherent_dma_mask)
index 462cbcb..b40095a 100644 (file)
@@ -427,7 +427,8 @@ static struct snd_soc_ops ams_delta_ops = {
 
 /* Board specific codec bias level control */
 static int ams_delta_set_bias_level(struct snd_soc_card *card,
-                                       enum snd_soc_bias_level level)
+                                   struct snd_soc_dapm_context *dapm,
+                                   enum snd_soc_bias_level level)
 {
        struct snd_soc_codec *codec = card->rtd->codec;
 
index e6a6b99..b2f5751 100644 (file)
@@ -366,9 +366,11 @@ static void omap_pcm_free_dma_buffers(struct snd_pcm *pcm)
        }
 }
 
-static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-                struct snd_pcm *pcm)
+static int omap_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index 2ce0b2d..d73d6f6 100644 (file)
@@ -85,9 +85,11 @@ static struct snd_pcm_ops pxa2xx_pcm_ops = {
 
 static u64 pxa2xx_pcm_dmamask = DMA_BIT_MASK(32);
 
-static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-       struct snd_pcm *pcm)
+static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index ab3ccae..80c85fd 100644 (file)
@@ -443,10 +443,11 @@ static void s6000_pcm_free(struct snd_pcm *pcm)
 
 static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
 
-static int s6000_pcm_new(struct snd_card *card,
-                        struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime)
 {
-       struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+       struct snd_card *card = runtime->card->snd_card;
+       struct snd_soc_dai *dai = runtime->cpu_dai;
+       struct snd_pcm *pcm = runtime->pcm;
        struct s6000_pcm_dma_params *params;
        int res;
 
index d155cbb..c611e45 100644 (file)
@@ -158,7 +158,7 @@ config SND_SOC_GONI_AQUILA_WM8994
 
 config SND_SOC_SAMSUNG_SMDK_SPDIF
        tristate "SoC S/PDIF Audio support for SMDK"
-       depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210)
+       depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310)
        select SND_SAMSUNG_SPDIF
        help
          Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
@@ -177,3 +177,9 @@ config SND_SOC_SPEYSIDE
        select SND_SAMSUNG_I2S
        select SND_SOC_WM8915
        select SND_SOC_WM9081
+
+config SND_SOC_SPEYSIDE_WM8962
+       tristate "Audio support for Wolfson Speyside with WM8962"
+       depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+       select SND_SAMSUNG_I2S
+       select SND_SOC_WM8962
index 683843a..e04df65 100644 (file)
@@ -36,6 +36,7 @@ snd-soc-goni-wm8994-objs := goni_wm8994.o
 snd-soc-smdk-spdif-objs := smdk_spdif.o
 snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
 snd-soc-speyside-objs := speyside.o
+snd-soc-speyside-wm8962-objs := speyside_wm8962.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -55,3 +56,4 @@ obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
 obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
 obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
 obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
+obj-$(CONFIG_SND_SOC_SPEYSIDE_WM8962) += snd-soc-speyside-wm8962.o
index 5cb3b88..9465588 100644 (file)
@@ -425,9 +425,11 @@ static void dma_free_dma_buffers(struct snd_pcm *pcm)
 
 static u64 dma_mask = DMA_BIT_MASK(32);
 
-static int dma_new(struct snd_card *card,
-       struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int dma_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        pr_debug("Entered %s\n", __func__);
index 360a333..d6dee4d 100644 (file)
 #define WM8915_HPSEL_GPIO 214
 
 static int speyside_set_bias_level(struct snd_soc_card *card,
+                                  struct snd_soc_dapm_context *dapm,
                                   enum snd_soc_bias_level level)
 {
        struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
        int ret;
 
+       if (dapm->dev != codec_dai->dev)
+               return 0;
+
        switch (level) {
        case SND_SOC_BIAS_STANDBY:
-               ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK1,
+               ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_MCLK2,
                                             32768, SND_SOC_CLOCK_IN);
                if (ret < 0)
                        return ret;
 
-               ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK1,
+               ret = snd_soc_dai_set_pll(codec_dai, WM8915_FLL_MCLK2,
                                          0, 0, 0);
                if (ret < 0) {
                        pr_err("Failed to stop FLL\n");
                        return ret;
                }
+               break;
 
        default:
                break;
@@ -46,6 +51,45 @@ static int speyside_set_bias_level(struct snd_soc_card *card,
        return 0;
 }
 
+static int speyside_set_bias_level_post(struct snd_soc_card *card,
+                                       struct snd_soc_dapm_context *dapm,
+                                       enum snd_soc_bias_level level)
+{
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       int ret;
+
+       if (dapm->dev != codec_dai->dev)
+               return 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+                       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                                                 WM8915_FLL_MCLK2,
+                                                 32768, 48000 * 256);
+                       if (ret < 0) {
+                               pr_err("Failed to start FLL\n");
+                               return ret;
+                       }
+
+                       ret = snd_soc_dai_set_sysclk(codec_dai,
+                                                    WM8915_SYSCLK_FLL,
+                                                    48000 * 256,
+                                                    SND_SOC_CLOCK_IN);
+                       if (ret < 0)
+                               return ret;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       card->dapm.bias_level = level;
+
+       return 0;
+}
+
 static int speyside_hw_params(struct snd_pcm_substream *substream,
                              struct snd_pcm_hw_params *params)
 {
@@ -66,16 +110,6 @@ static int speyside_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
-       ret = snd_soc_dai_set_pll(codec_dai, 0, WM8915_FLL_MCLK1,
-                                 32768, 256 * 48000);
-       if (ret < 0)
-               return ret;
-
-       ret = snd_soc_dai_set_sysclk(codec_dai, WM8915_SYSCLK_FLL,
-                                    256 * 48000, SND_SOC_CLOCK_IN);
-       if (ret < 0)
-               return ret;
-
        return 0;
 }
 
@@ -127,7 +161,7 @@ static int speyside_wm8915_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_codec *codec = rtd->codec;
        int ret;
 
-       ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK1, 32768, 0);
+       ret = snd_soc_dai_set_sysclk(dai, WM8915_SYSCLK_MCLK2, 32768, 0);
        if (ret < 0)
                return ret;
 
@@ -267,6 +301,7 @@ static struct snd_soc_card speyside = {
        .num_configs = ARRAY_SIZE(speyside_codec_conf),
 
        .set_bias_level = speyside_set_bias_level,
+       .set_bias_level_post = speyside_set_bias_level_post,
 
        .controls = controls,
        .num_controls = ARRAY_SIZE(controls),
diff --git a/sound/soc/samsung/speyside_wm8962.c b/sound/soc/samsung/speyside_wm8962.c
new file mode 100644 (file)
index 0000000..c0ba0bf
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Speyside with WM8962 audio support
+ *
+ * Copyright 2011 Wolfson Microelectronics
+ *
+ * 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 <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <linux/gpio.h>
+
+#include "../codecs/wm8962.h"
+
+static int speyside_wm8962_set_bias_level(struct snd_soc_card *card,
+                                         struct snd_soc_dapm_context *dapm,
+                                         enum snd_soc_bias_level level)
+{
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+                       ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+                                                 WM8962_FLL_MCLK, 32768,
+                                                 44100 * 256);
+                       if (ret < 0)
+                               pr_err("Failed to start FLL\n");
+
+                       ret = snd_soc_dai_set_sysclk(codec_dai,
+                                                    WM8962_SYSCLK_FLL,
+                                                    44100 * 256,
+                                                    SND_SOC_CLOCK_IN);
+                       if (ret < 0)
+                               return ret;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int speyside_wm8962_set_bias_level_post(struct snd_soc_card *card,
+                                              struct snd_soc_dapm_context *dapm,
+                                              enum snd_soc_bias_level level)
+{
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_STANDBY:
+               ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
+                                            32768, SND_SOC_CLOCK_IN);
+               if (ret < 0)
+                       return ret;
+
+               ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
+                                         0, 0, 0);
+               if (ret < 0) {
+                       pr_err("Failed to stop FLL\n");
+                       return ret;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       dapm->bias_level = level;
+
+       return 0;
+}
+
+static int speyside_wm8962_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->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       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;
+
+       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;
+
+       return 0;
+}
+
+static struct snd_soc_ops speyside_wm8962_ops = {
+       .hw_params = speyside_wm8962_hw_params,
+};
+
+static struct snd_soc_dai_link speyside_wm8962_dai[] = {
+       {
+               .name = "CPU",
+               .stream_name = "CPU",
+               .cpu_dai_name = "samsung-i2s.0",
+               .codec_dai_name = "wm8962",
+               .platform_name = "samsung-audio",
+               .codec_name = "wm8962.1-001a",
+               .ops = &speyside_wm8962_ops,
+       },
+};
+
+static const struct snd_kcontrol_new controls[] = {
+       SOC_DAPM_PIN_SWITCH("Main Speaker"),
+};
+
+static struct snd_soc_dapm_widget widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+
+       SND_SOC_DAPM_MIC("DMIC", NULL),
+
+       SND_SOC_DAPM_SPK("Main Speaker", NULL),
+};
+
+static struct snd_soc_dapm_route audio_paths[] = {
+       { "Headphone", NULL, "HPOUTL" },
+       { "Headphone", NULL, "HPOUTR" },
+
+       { "Main Speaker", NULL, "SPKOUTL" },
+       { "Main Speaker", NULL, "SPKOUTR" },
+
+       { "MICBIAS", NULL, "Headset Mic" },
+       { "IN4L", NULL, "MICBIAS" },
+       { "IN4R", NULL, "MICBIAS" },
+
+       { "MICBIAS", NULL, "DMIC" },
+       { "DMICDAT", NULL, "MICBIAS" },
+};
+
+static struct snd_soc_jack speyside_wm8962_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin speyside_wm8962_headset_pins[] = {
+       {
+               .pin = "Headset Mic",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone",
+               .mask = SND_JACK_MICROPHONE,
+       },
+};
+
+static int speyside_wm8962_late_probe(struct snd_soc_card *card)
+{
+       struct snd_soc_codec *codec = card->rtd[0].codec;
+       struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK,
+                                    32768, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_jack_new(codec, "Headset",
+                              SND_JACK_HEADSET | SND_JACK_BTN_0,
+                              &speyside_wm8962_headset);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_jack_add_pins(&speyside_wm8962_headset,
+                                   ARRAY_SIZE(speyside_wm8962_headset_pins),
+                                   speyside_wm8962_headset_pins);
+       if (ret)
+               return ret;
+
+       wm8962_mic_detect(codec, &speyside_wm8962_headset);
+
+       return 0;
+}
+
+static struct snd_soc_card speyside_wm8962 = {
+       .name = "Speyside WM8962",
+       .dai_link = speyside_wm8962_dai,
+       .num_links = ARRAY_SIZE(speyside_wm8962_dai),
+
+       .set_bias_level = speyside_wm8962_set_bias_level,
+       .set_bias_level_post = speyside_wm8962_set_bias_level_post,
+
+       .controls = controls,
+       .num_controls = ARRAY_SIZE(controls),
+       .dapm_widgets = widgets,
+       .num_dapm_widgets = ARRAY_SIZE(widgets),
+       .dapm_routes = audio_paths,
+       .num_dapm_routes = ARRAY_SIZE(audio_paths),
+
+       .late_probe = speyside_wm8962_late_probe,
+};
+
+static __devinit int speyside_wm8962_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &speyside_wm8962;
+       int ret;
+
+       card->dev = &pdev->dev;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit speyside_wm8962_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+static struct platform_driver speyside_wm8962_driver = {
+       .driver = {
+               .name = "speyside-wm8962",
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = speyside_wm8962_probe,
+       .remove = __devexit_p(speyside_wm8962_remove),
+};
+
+static int __init speyside_wm8962_audio_init(void)
+{
+       return platform_driver_register(&speyside_wm8962_driver);
+}
+module_init(speyside_wm8962_audio_init);
+
+static void __exit speyside_wm8962_audio_exit(void)
+{
+       platform_driver_unregister(&speyside_wm8962_driver);
+}
+module_exit(speyside_wm8962_audio_exit);
+
+MODULE_DESCRIPTION("Speyside WM8962 audio support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:speyside-wm8962");
index c326d29..db74005 100644 (file)
@@ -327,10 +327,10 @@ static void camelot_pcm_free(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static int camelot_pcm_new(struct snd_card *card,
-                          struct snd_soc_dai *dai,
-                          struct snd_pcm *pcm)
+static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_pcm *pcm = rtd->pcm;
+
        /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
         * in MMAP mode (i.e. aplay -M)
         */
index 4a9da6b..8e112cc 100644 (file)
@@ -118,10 +118,38 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
 /*
  * FSI driver use below type name for variable
  *
- * xxx_len     : data length
- * xxx_width   : data width
- * xxx_offset  : data offset
  * xxx_num     : number of data
+ * xxx_pos     : position of data
+ * xxx_capa    : capacity of data
+ */
+
+/*
+ *     period/frame/sample image
+ *
+ * ex) PCM (2ch)
+ *
+ * period pos                                     period pos
+ *   [n]                                            [n + 1]
+ *   |<-------------------- period--------------------->|
+ * ==|============================================ ... =|==
+ *   |                                                 |
+ *   ||<-----  frame ----->|<------ frame ----->|  ... |
+ *   |+--------------------+--------------------+- ... |
+ *   ||[ sample ][ sample ]|[ sample ][ sample ]|  ... |
+ *   |+--------------------+--------------------+- ... |
+ * ==|============================================ ... =|==
+ */
+
+/*
+ *     FSI FIFO image
+ *
+ *     |            |
+ *     |            |
+ *     | [ sample ] |
+ *     | [ sample ] |
+ *     | [ sample ] |
+ *     | [ sample ] |
+ *             --> go to codecs
  */
 
 /*
@@ -131,12 +159,11 @@ typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int ena
 struct fsi_stream {
        struct snd_pcm_substream *substream;
 
-       int fifo_max_num;
-
-       int buff_offset;
-       int buff_len;
-       int period_len;
-       int period_num;
+       int fifo_sample_capa;   /* sample capacity of FSI FIFO */
+       int buff_sample_capa;   /* sample capacity of ALSA buffer */
+       int buff_sample_pos;    /* sample position of ALSA buffer */
+       int period_samples;     /* sample number / 1 period */
+       int period_pos;         /* current period position */
 
        int uerr_num;
        int oerr_num;
@@ -149,17 +176,14 @@ struct fsi_priv {
        struct fsi_stream playback;
        struct fsi_stream capture;
 
+       u32 do_fmt;
+       u32 di_fmt;
+
        int chan_num:16;
        int clk_master:1;
+       int spdif:1;
 
        long rate;
-
-       /* for suspend/resume */
-       u32 saved_do_fmt;
-       u32 saved_di_fmt;
-       u32 saved_ckg1;
-       u32 saved_ckg2;
-       u32 saved_out_sel;
 };
 
 struct fsi_core {
@@ -180,14 +204,6 @@ struct fsi_master {
        struct fsi_core *core;
        struct sh_fsi_platform_info *info;
        spinlock_t lock;
-
-       /* for suspend/resume */
-       u32 saved_a_mclk;
-       u32 saved_b_mclk;
-       u32 saved_iemsk;
-       u32 saved_imsk;
-       u32 saved_clk_rst;
-       u32 saved_soft_rst;
 };
 
 /*
@@ -271,6 +287,11 @@ static int fsi_is_port_a(struct fsi_priv *fsi)
        return fsi->master->base == fsi->base;
 }
 
+static int fsi_is_spdif(struct fsi_priv *fsi)
+{
+       return fsi->spdif;
+}
+
 static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -342,28 +363,59 @@ static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play)
        return shift;
 }
 
+static int fsi_frame2sample(struct fsi_priv *fsi, int frames)
+{
+       return frames * fsi->chan_num;
+}
+
+static int fsi_sample2frame(struct fsi_priv *fsi, int samples)
+{
+       return samples / fsi->chan_num;
+}
+
+static int fsi_stream_is_working(struct fsi_priv *fsi,
+                                 int is_play)
+{
+       struct fsi_stream *io = fsi_get_stream(fsi, is_play);
+       struct fsi_master *master = fsi_get_master(fsi);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&master->lock, flags);
+       ret = !!io->substream;
+       spin_unlock_irqrestore(&master->lock, flags);
+
+       return ret;
+}
+
 static void fsi_stream_push(struct fsi_priv *fsi,
                            int is_play,
-                           struct snd_pcm_substream *substream,
-                           u32 buffer_len,
-                           u32 period_len)
+                           struct snd_pcm_substream *substream)
 {
        struct fsi_stream *io = fsi_get_stream(fsi, is_play);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct fsi_master *master = fsi_get_master(fsi);
+       unsigned long flags;
 
+       spin_lock_irqsave(&master->lock, flags);
        io->substream   = substream;
-       io->buff_len    = buffer_len;
-       io->buff_offset = 0;
-       io->period_len  = period_len;
-       io->period_num  = 0;
+       io->buff_sample_capa    = fsi_frame2sample(fsi, runtime->buffer_size);
+       io->buff_sample_pos     = 0;
+       io->period_samples      = fsi_frame2sample(fsi, runtime->period_size);
+       io->period_pos          = 0;
        io->oerr_num    = -1; /* ignore 1st err */
        io->uerr_num    = -1; /* ignore 1st err */
+       spin_unlock_irqrestore(&master->lock, flags);
 }
 
 static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
 {
        struct fsi_stream *io = fsi_get_stream(fsi, is_play);
        struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+       struct fsi_master *master = fsi_get_master(fsi);
+       unsigned long flags;
 
+       spin_lock_irqsave(&master->lock, flags);
 
        if (io->oerr_num > 0)
                dev_err(dai->dev, "over_run = %d\n", io->oerr_num);
@@ -372,47 +424,27 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play)
                dev_err(dai->dev, "under_run = %d\n", io->uerr_num);
 
        io->substream   = NULL;
-       io->buff_len    = 0;
-       io->buff_offset = 0;
-       io->period_len  = 0;
-       io->period_num  = 0;
+       io->buff_sample_capa    = 0;
+       io->buff_sample_pos     = 0;
+       io->period_samples      = 0;
+       io->period_pos          = 0;
        io->oerr_num    = 0;
        io->uerr_num    = 0;
+       spin_unlock_irqrestore(&master->lock, flags);
 }
 
-static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play)
+static int fsi_get_current_fifo_samples(struct fsi_priv *fsi, int is_play)
 {
        u32 status;
-       int data_num;
+       int frames;
 
        status = is_play ?
                fsi_reg_read(fsi, DOFF_ST) :
                fsi_reg_read(fsi, DIFF_ST);
 
-       data_num = 0x1ff & (status >> 8);
-       data_num *= fsi->chan_num;
-
-       return data_num;
-}
-
-static int fsi_len2num(int len, int width)
-{
-       return len / width;
-}
-
-#define fsi_num2offset(a, b) fsi_num2len(a, b)
-static int fsi_num2len(int num, int width)
-{
-       return num * width;
-}
-
-static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play)
-{
-       struct fsi_stream *io = fsi_get_stream(fsi, is_play);
-       struct snd_pcm_substream *substream = io->substream;
-       struct snd_pcm_runtime *runtime = substream->runtime;
+       frames = 0x1ff & (status >> 8);
 
-       return frames_to_bytes(runtime, 1) / fsi->chan_num;
+       return fsi_frame2sample(fsi, frames);
 }
 
 static void fsi_count_fifo_err(struct fsi_priv *fsi)
@@ -444,8 +476,10 @@ static u8 *fsi_dma_get_area(struct fsi_priv *fsi, int stream)
 {
        int is_play = fsi_stream_is_play(stream);
        struct fsi_stream *io = fsi_get_stream(fsi, is_play);
+       struct snd_pcm_runtime *runtime = io->substream->runtime;
 
-       return io->substream->runtime->dma_area + io->buff_offset;
+       return runtime->dma_area +
+               samples_to_bytes(runtime, io->buff_sample_pos);
 }
 
 static void fsi_dma_soft_push16(struct fsi_priv *fsi, int num)
@@ -559,37 +593,94 @@ static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable)
 /*
  *             clock function
  */
-#define fsi_module_init(m, d)  __fsi_module_clk_ctrl(m, d, 1)
-#define fsi_module_kill(m, d)  __fsi_module_clk_ctrl(m, d, 0)
-static void __fsi_module_clk_ctrl(struct fsi_master *master,
-                                 struct device *dev,
-                                 int enable)
+static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
+                             long rate, int enable)
 {
-       pm_runtime_get_sync(dev);
+       struct fsi_master *master = fsi_get_master(fsi);
+       set_rate_func set_rate = fsi_get_info_set_rate(master);
+       int fsi_ver = master->core->ver;
+       int ret;
 
-       if (enable) {
-               /* enable only SR */
-               fsi_master_mask_set(master, SOFT_RST, FSISR, FSISR);
-               fsi_master_mask_set(master, SOFT_RST, PASR | PBSR, 0);
-       } else {
-               /* clear all registers */
-               fsi_master_mask_set(master, SOFT_RST, FSISR, 0);
+       ret = set_rate(dev, fsi_is_port_a(fsi), rate, enable);
+       if (ret < 0) /* error */
+               return ret;
+
+       if (!enable)
+               return 0;
+
+       if (ret > 0) {
+               u32 data = 0;
+
+               switch (ret & SH_FSI_ACKMD_MASK) {
+               default:
+                       /* FALL THROUGH */
+               case SH_FSI_ACKMD_512:
+                       data |= (0x0 << 12);
+                       break;
+               case SH_FSI_ACKMD_256:
+                       data |= (0x1 << 12);
+                       break;
+               case SH_FSI_ACKMD_128:
+                       data |= (0x2 << 12);
+                       break;
+               case SH_FSI_ACKMD_64:
+                       data |= (0x3 << 12);
+                       break;
+               case SH_FSI_ACKMD_32:
+                       if (fsi_ver < 2)
+                               dev_err(dev, "unsupported ACKMD\n");
+                       else
+                               data |= (0x4 << 12);
+                       break;
+               }
+
+               switch (ret & SH_FSI_BPFMD_MASK) {
+               default:
+                       /* FALL THROUGH */
+               case SH_FSI_BPFMD_32:
+                       data |= (0x0 << 8);
+                       break;
+               case SH_FSI_BPFMD_64:
+                       data |= (0x1 << 8);
+                       break;
+               case SH_FSI_BPFMD_128:
+                       data |= (0x2 << 8);
+                       break;
+               case SH_FSI_BPFMD_256:
+                       data |= (0x3 << 8);
+                       break;
+               case SH_FSI_BPFMD_512:
+                       data |= (0x4 << 8);
+                       break;
+               case SH_FSI_BPFMD_16:
+                       if (fsi_ver < 2)
+                               dev_err(dev, "unsupported ACKMD\n");
+                       else
+                               data |= (0x7 << 8);
+                       break;
+               }
+
+               fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
+               udelay(10);
+               ret = 0;
        }
 
-       pm_runtime_put_sync(dev);
+       return ret;
 }
 
-#define fsi_port_start(f)      __fsi_port_clk_ctrl(f, 1)
-#define fsi_port_stop(f)       __fsi_port_clk_ctrl(f, 0)
-static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
+#define fsi_port_start(f, i)   __fsi_port_clk_ctrl(f, i, 1)
+#define fsi_port_stop(f, i)    __fsi_port_clk_ctrl(f, i, 0)
+static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int is_play, int enable)
 {
        struct fsi_master *master = fsi_get_master(fsi);
-       u32 soft = fsi_is_port_a(fsi) ? PASR : PBSR;
        u32 clk  = fsi_is_port_a(fsi) ? CRA  : CRB;
-       int is_master = fsi_is_clk_master(fsi);
 
-       fsi_master_mask_set(master, SOFT_RST, soft, (enable) ? soft : 0);
-       if (is_master)
+       if (enable)
+               fsi_irq_enable(fsi, is_play);
+       else
+               fsi_irq_disable(fsi, is_play);
+
+       if (fsi_is_clk_master(fsi))
                fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
 }
 
@@ -598,18 +689,19 @@ static void __fsi_port_clk_ctrl(struct fsi_priv *fsi, int enable)
  */
 static void fsi_fifo_init(struct fsi_priv *fsi,
                          int is_play,
-                         struct snd_soc_dai *dai)
+                         struct device *dev)
 {
        struct fsi_master *master = fsi_get_master(fsi);
        struct fsi_stream *io = fsi_get_stream(fsi, is_play);
        u32 shift, i;
+       int frame_capa;
 
        /* get on-chip RAM capacity */
        shift = fsi_master_read(master, FIFO_SZ);
        shift >>= fsi_get_port_shift(fsi, is_play);
        shift &= FIFO_SZ_MASK;
-       io->fifo_max_num = 256 << shift;
-       dev_dbg(dai->dev, "fifo = %d words\n", io->fifo_max_num);
+       frame_capa = 256 << shift;
+       dev_dbg(dev, "fifo = %d words\n", frame_capa);
 
        /*
         * The maximum number of sample data varies depending
@@ -631,9 +723,11 @@ static void fsi_fifo_init(struct fsi_priv *fsi,
         * 8 channels:  32 ( 32 x 8 = 256)
         */
        for (i = 1; i < fsi->chan_num; i <<= 1)
-               io->fifo_max_num >>= 1;
-       dev_dbg(dai->dev, "%d channel %d store\n",
-               fsi->chan_num, io->fifo_max_num);
+               frame_capa >>= 1;
+       dev_dbg(dev, "%d channel %d store\n",
+               fsi->chan_num, frame_capa);
+
+       io->fifo_sample_capa = fsi_frame2sample(fsi, frame_capa);
 
        /*
         * set interrupt generation factor
@@ -654,10 +748,10 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
        struct snd_pcm_substream *substream = NULL;
        int is_play = fsi_stream_is_play(stream);
        struct fsi_stream *io = fsi_get_stream(fsi, is_play);
-       int data_residue_num;
-       int data_num;
-       int data_num_max;
-       int ch_width;
+       int sample_residues;
+       int sample_width;
+       int samples;
+       int samples_max;
        int over_period;
        void (*fn)(struct fsi_priv *fsi, int size);
 
@@ -673,36 +767,35 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
        /* FSI FIFO has limit.
         * So, this driver can not send periods data at a time
         */
-       if (io->buff_offset >=
-           fsi_num2offset(io->period_num + 1, io->period_len)) {
+       if (io->buff_sample_pos >=
+           io->period_samples * (io->period_pos + 1)) {
 
                over_period = 1;
-               io->period_num = (io->period_num + 1) % runtime->periods;
+               io->period_pos = (io->period_pos + 1) % runtime->periods;
 
-               if (0 == io->period_num)
-                       io->buff_offset = 0;
+               if (0 == io->period_pos)
+                       io->buff_sample_pos = 0;
        }
 
-       /* get 1 channel data width */
-       ch_width = fsi_get_frame_width(fsi, is_play);
+       /* get 1 sample data width */
+       sample_width = samples_to_bytes(runtime, 1);
 
-       /* get residue data number of alsa */
-       data_residue_num = fsi_len2num(io->buff_len - io->buff_offset,
-                                      ch_width);
+       /* get number of residue samples */
+       sample_residues = io->buff_sample_capa - io->buff_sample_pos;
 
        if (is_play) {
                /*
                 * for play-back
                 *
-                * data_num_max : number of FSI fifo free space
-                * data_num     : number of ALSA residue data
+                * samples_max  : number of FSI fifo free samples space
+                * samples      : number of ALSA residue samples
                 */
-               data_num_max  = io->fifo_max_num * fsi->chan_num;
-               data_num_max -= fsi_get_fifo_data_num(fsi, is_play);
+               samples_max  = io->fifo_sample_capa;
+               samples_max -= fsi_get_current_fifo_samples(fsi, is_play);
 
-               data_num = data_residue_num;
+               samples = sample_residues;
 
-               switch (ch_width) {
+               switch (sample_width) {
                case 2:
                        fn = fsi_dma_soft_push16;
                        break;
@@ -716,13 +809,13 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
                /*
                 * for capture
                 *
-                * data_num_max : number of ALSA free space
-                * data_num     : number of data in FSI fifo
+                * samples_max  : number of ALSA free samples space
+                * samples      : number of samples in FSI fifo
                 */
-               data_num_max = data_residue_num;
-               data_num     = fsi_get_fifo_data_num(fsi, is_play);
+               samples_max = sample_residues;
+               samples     = fsi_get_current_fifo_samples(fsi, is_play);
 
-               switch (ch_width) {
+               switch (sample_width) {
                case 2:
                        fn = fsi_dma_soft_pop16;
                        break;
@@ -734,12 +827,12 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream)
                }
        }
 
-       data_num = min(data_num, data_num_max);
+       samples = min(samples, samples_max);
 
-       fn(fsi, data_num);
+       fn(fsi, samples);
 
-       /* update buff_offset */
-       io->buff_offset += fsi_num2offset(data_num, ch_width);
+       /* update buff_sample_pos */
+       io->buff_sample_pos += samples;
 
        if (over_period)
                snd_pcm_period_elapsed(substream);
@@ -788,16 +881,20 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
  *             dai ops
  */
 
-static int fsi_dai_startup(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *dai)
+static int fsi_hw_startup(struct fsi_priv *fsi,
+                         int is_play,
+                         struct device *dev)
 {
-       struct fsi_priv *fsi = fsi_get_priv(substream);
        u32 flags = fsi_get_info_flags(fsi);
-       u32 data;
-       int is_play = fsi_is_play(substream);
+       u32 data = 0;
 
-       pm_runtime_get_sync(dai->dev);
+       pm_runtime_get_sync(dev);
 
+       /* clock setting */
+       if (fsi_is_clk_master(fsi))
+               data = DIMD | DOMD;
+
+       fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
 
        /* clock inversion (CKG2) */
        data = 0;
@@ -812,54 +909,70 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
 
        fsi_reg_write(fsi, CKG2, data);
 
+       /* set format */
+       fsi_reg_write(fsi, DO_FMT, fsi->do_fmt);
+       fsi_reg_write(fsi, DI_FMT, fsi->di_fmt);
+
+       /* spdif ? */
+       if (fsi_is_spdif(fsi)) {
+               fsi_spdif_clk_ctrl(fsi, 1);
+               fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+       }
+
        /* irq clear */
        fsi_irq_disable(fsi, is_play);
        fsi_irq_clear_status(fsi);
 
        /* fifo init */
-       fsi_fifo_init(fsi, is_play, dai);
+       fsi_fifo_init(fsi, is_play, dev);
 
        return 0;
 }
 
-static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
-                            struct snd_soc_dai *dai)
+static void fsi_hw_shutdown(struct fsi_priv *fsi,
+                           int is_play,
+                           struct device *dev)
+{
+       if (fsi_is_clk_master(fsi))
+               fsi_set_master_clk(dev, fsi, fsi->rate, 0);
+
+       pm_runtime_put_sync(dev);
+}
+
+static int fsi_dai_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
        int is_play = fsi_is_play(substream);
-       struct fsi_master *master = fsi_get_master(fsi);
-       set_rate_func set_rate = fsi_get_info_set_rate(master);
 
-       fsi_irq_disable(fsi, is_play);
+       return fsi_hw_startup(fsi, is_play, dai->dev);
+}
 
-       if (fsi_is_clk_master(fsi))
-               set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
+static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct fsi_priv *fsi = fsi_get_priv(substream);
+       int is_play = fsi_is_play(substream);
 
+       fsi_hw_shutdown(fsi, is_play, dai->dev);
        fsi->rate = 0;
-
-       pm_runtime_put_sync(dai->dev);
 }
 
 static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                           struct snd_soc_dai *dai)
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int is_play = fsi_is_play(substream);
        int ret = 0;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               fsi_stream_push(fsi, is_play, substream,
-                               frames_to_bytes(runtime, runtime->buffer_size),
-                               frames_to_bytes(runtime, runtime->period_size));
+               fsi_stream_push(fsi, is_play, substream);
                ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
-               fsi_irq_enable(fsi, is_play);
-               fsi_port_start(fsi);
+               fsi_port_start(fsi, is_play);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               fsi_port_stop(fsi);
-               fsi_irq_disable(fsi, is_play);
+               fsi_port_stop(fsi, is_play);
                fsi_stream_pop(fsi, is_play);
                break;
        }
@@ -884,8 +997,8 @@ static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
                return -EINVAL;
        }
 
-       fsi_reg_write(fsi, DO_FMT, data);
-       fsi_reg_write(fsi, DI_FMT, data);
+       fsi->do_fmt = data;
+       fsi->di_fmt = data;
 
        return 0;
 }
@@ -900,11 +1013,10 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
 
        data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
        fsi->chan_num = 2;
-       fsi_spdif_clk_ctrl(fsi, 1);
-       fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+       fsi->spdif = 1;
 
-       fsi_reg_write(fsi, DO_FMT, data);
-       fsi_reg_write(fsi, DI_FMT, data);
+       fsi->do_fmt = data;
+       fsi->di_fmt = data;
 
        return 0;
 }
@@ -915,32 +1027,24 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        struct fsi_master *master = fsi_get_master(fsi);
        set_rate_func set_rate = fsi_get_info_set_rate(master);
        u32 flags = fsi_get_info_flags(fsi);
-       u32 data = 0;
        int ret;
 
-       pm_runtime_get_sync(dai->dev);
-
        /* set master/slave audio interface */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
-               data = DIMD | DOMD;
                fsi->clk_master = 1;
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
                break;
        default:
-               ret = -EINVAL;
-               goto set_fmt_exit;
+               return -EINVAL;
        }
 
        if (fsi_is_clk_master(fsi) && !set_rate) {
                dev_err(dai->dev, "platform doesn't have set_rate\n");
-               ret = -EINVAL;
-               goto set_fmt_exit;
+               return -EINVAL;
        }
 
-       fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
-
        /* set format */
        switch (flags & SH_FSI_FMT_MASK) {
        case SH_FSI_FMT_DAI:
@@ -953,9 +1057,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                ret = -EINVAL;
        }
 
-set_fmt_exit:
-       pm_runtime_put_sync(dai->dev);
-
        return ret;
 }
 
@@ -964,79 +1065,19 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
-       struct fsi_master *master = fsi_get_master(fsi);
-       set_rate_func set_rate = fsi_get_info_set_rate(master);
-       int fsi_ver = master->core->ver;
        long rate = params_rate(params);
        int ret;
 
        if (!fsi_is_clk_master(fsi))
                return 0;
 
-       ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1);
-       if (ret < 0) /* error */
+       ret = fsi_set_master_clk(dai->dev, fsi, rate, 1);
+       if (ret < 0)
                return ret;
 
        fsi->rate = rate;
-       if (ret > 0) {
-               u32 data = 0;
-
-               switch (ret & SH_FSI_ACKMD_MASK) {
-               default:
-                       /* FALL THROUGH */
-               case SH_FSI_ACKMD_512:
-                       data |= (0x0 << 12);
-                       break;
-               case SH_FSI_ACKMD_256:
-                       data |= (0x1 << 12);
-                       break;
-               case SH_FSI_ACKMD_128:
-                       data |= (0x2 << 12);
-                       break;
-               case SH_FSI_ACKMD_64:
-                       data |= (0x3 << 12);
-                       break;
-               case SH_FSI_ACKMD_32:
-                       if (fsi_ver < 2)
-                               dev_err(dai->dev, "unsupported ACKMD\n");
-                       else
-                               data |= (0x4 << 12);
-                       break;
-               }
-
-               switch (ret & SH_FSI_BPFMD_MASK) {
-               default:
-                       /* FALL THROUGH */
-               case SH_FSI_BPFMD_32:
-                       data |= (0x0 << 8);
-                       break;
-               case SH_FSI_BPFMD_64:
-                       data |= (0x1 << 8);
-                       break;
-               case SH_FSI_BPFMD_128:
-                       data |= (0x2 << 8);
-                       break;
-               case SH_FSI_BPFMD_256:
-                       data |= (0x3 << 8);
-                       break;
-               case SH_FSI_BPFMD_512:
-                       data |= (0x4 << 8);
-                       break;
-               case SH_FSI_BPFMD_16:
-                       if (fsi_ver < 2)
-                               dev_err(dai->dev, "unsupported ACKMD\n");
-                       else
-                               data |= (0x7 << 8);
-                       break;
-               }
-
-               fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
-               udelay(10);
-               ret = 0;
-       }
 
        return ret;
-
 }
 
 static struct snd_soc_dai_ops fsi_dai_ops = {
@@ -1097,16 +1138,14 @@ static int fsi_hw_free(struct snd_pcm_substream *substream)
 
 static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
        struct fsi_priv *fsi = fsi_get_priv(substream);
        struct fsi_stream *io = fsi_get_stream(fsi, fsi_is_play(substream));
-       long location;
+       int samples_pos = io->buff_sample_pos - 1;
 
-       location = (io->buff_offset - 1);
-       if (location < 0)
-               location = 0;
+       if (samples_pos < 0)
+               samples_pos = 0;
 
-       return bytes_to_frames(runtime, location);
+       return fsi_sample2frame(fsi, samples_pos);
 }
 
 static struct snd_pcm_ops fsi_pcm_ops = {
@@ -1129,10 +1168,10 @@ static void fsi_pcm_free(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static int fsi_pcm_new(struct snd_card *card,
-                      struct snd_soc_dai *dai,
-                      struct snd_pcm *pcm)
+static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_pcm *pcm = rtd->pcm;
+
        /*
         * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
         * in MMAP mode (i.e. aplay -M)
@@ -1246,8 +1285,6 @@ static int fsi_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        dev_set_drvdata(&pdev->dev, master);
 
-       fsi_module_init(master, &pdev->dev);
-
        ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
                          id_entry->name, master);
        if (ret) {
@@ -1290,8 +1327,6 @@ static int fsi_remove(struct platform_device *pdev)
 
        master = dev_get_drvdata(&pdev->dev);
 
-       fsi_module_kill(master, &pdev->dev);
-
        free_irq(master->irq, master);
        pm_runtime_disable(&pdev->dev);
 
@@ -1305,53 +1340,43 @@ static int fsi_remove(struct platform_device *pdev)
 }
 
 static void __fsi_suspend(struct fsi_priv *fsi,
-                         struct device *dev,
-                         set_rate_func set_rate)
+                         int is_play,
+                         struct device *dev)
 {
-       fsi->saved_do_fmt       = fsi_reg_read(fsi, DO_FMT);
-       fsi->saved_di_fmt       = fsi_reg_read(fsi, DI_FMT);
-       fsi->saved_ckg1         = fsi_reg_read(fsi, CKG1);
-       fsi->saved_ckg2         = fsi_reg_read(fsi, CKG2);
-       fsi->saved_out_sel      = fsi_reg_read(fsi, OUT_SEL);
+       if (!fsi_stream_is_working(fsi, is_play))
+               return;
 
-       if (fsi_is_clk_master(fsi))
-               set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 0);
+       fsi_port_stop(fsi, is_play);
+       fsi_hw_shutdown(fsi, is_play, dev);
 }
 
 static void __fsi_resume(struct fsi_priv *fsi,
-                        struct device *dev,
-                        set_rate_func set_rate)
+                        int is_play,
+                        struct device *dev)
 {
-       fsi_reg_write(fsi, DO_FMT,      fsi->saved_do_fmt);
-       fsi_reg_write(fsi, DI_FMT,      fsi->saved_di_fmt);
-       fsi_reg_write(fsi, CKG1,        fsi->saved_ckg1);
-       fsi_reg_write(fsi, CKG2,        fsi->saved_ckg2);
-       fsi_reg_write(fsi, OUT_SEL,     fsi->saved_out_sel);
+       if (!fsi_stream_is_working(fsi, is_play))
+               return;
+
+       fsi_hw_startup(fsi, is_play, dev);
+
+       if (fsi_is_clk_master(fsi) && fsi->rate)
+               fsi_set_master_clk(dev, fsi, fsi->rate, 1);
+
+       fsi_port_start(fsi, is_play);
 
-       if (fsi_is_clk_master(fsi))
-               set_rate(dev, fsi_is_port_a(fsi), fsi->rate, 1);
 }
 
 static int fsi_suspend(struct device *dev)
 {
        struct fsi_master *master = dev_get_drvdata(dev);
-       set_rate_func set_rate = fsi_get_info_set_rate(master);
-
-       pm_runtime_get_sync(dev);
-
-       __fsi_suspend(&master->fsia, dev, set_rate);
-       __fsi_suspend(&master->fsib, dev, set_rate);
+       struct fsi_priv *fsia = &master->fsia;
+       struct fsi_priv *fsib = &master->fsib;
 
-       master->saved_a_mclk    = fsi_core_read(master, a_mclk);
-       master->saved_b_mclk    = fsi_core_read(master, b_mclk);
-       master->saved_iemsk     = fsi_core_read(master, iemsk);
-       master->saved_imsk      = fsi_core_read(master, imsk);
-       master->saved_clk_rst   = fsi_master_read(master, CLK_RST);
-       master->saved_soft_rst  = fsi_master_read(master, SOFT_RST);
+       __fsi_suspend(fsia, 1, dev);
+       __fsi_suspend(fsia, 0, dev);
 
-       fsi_module_kill(master, dev);
-
-       pm_runtime_put_sync(dev);
+       __fsi_suspend(fsib, 1, dev);
+       __fsi_suspend(fsib, 0, dev);
 
        return 0;
 }
@@ -1359,23 +1384,14 @@ static int fsi_suspend(struct device *dev)
 static int fsi_resume(struct device *dev)
 {
        struct fsi_master *master = dev_get_drvdata(dev);
-       set_rate_func set_rate = fsi_get_info_set_rate(master);
-
-       pm_runtime_get_sync(dev);
-
-       fsi_module_init(master, dev);
+       struct fsi_priv *fsia = &master->fsia;
+       struct fsi_priv *fsib = &master->fsib;
 
-       fsi_master_mask_set(master, SOFT_RST, 0xffff, master->saved_soft_rst);
-       fsi_master_mask_set(master, CLK_RST, 0xffff, master->saved_clk_rst);
-       fsi_core_mask_set(master, a_mclk, 0xffff, master->saved_a_mclk);
-       fsi_core_mask_set(master, b_mclk, 0xffff, master->saved_b_mclk);
-       fsi_core_mask_set(master, iemsk, 0xffff, master->saved_iemsk);
-       fsi_core_mask_set(master, imsk, 0xffff, master->saved_imsk);
+       __fsi_resume(fsia, 1, dev);
+       __fsi_resume(fsia, 0, dev);
 
-       __fsi_resume(&master->fsia, dev, set_rate);
-       __fsi_resume(&master->fsib, dev, set_rate);
-
-       pm_runtime_put_sync(dev);
+       __fsi_resume(fsib, 1, dev);
+       __fsi_resume(fsib, 0, dev);
 
        return 0;
 }
index a423bab..f8f6816 100644 (file)
@@ -527,10 +527,11 @@ static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
        return bytes_to_frames(ss->runtime, ptr);
 }
 
-static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-                      struct snd_pcm *pcm)
+static int siu_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
        /* card->dev == socdev->dev, see snd_soc_new_pcms() */
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_pcm *pcm = rtd->pcm;
        struct siu_info *info = siu_i2s_data;
        struct platform_device *pdev = to_platform_device(card->dev);
        int ret;
index 039b953..d9f8ade 100644 (file)
 
 #include <trace/events/asoc.h>
 
-#ifdef CONFIG_SPI_MASTER
-static int do_spi_write(void *control, const char *data, int len)
-{
-       struct spi_device *spi = control;
-       int ret;
-
-       ret = spi_write(spi, data, len);
-       if (ret < 0)
-               return ret;
-
-       return len;
-}
-#endif
-
-static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
-                      unsigned int value, const void *data, int len)
-{
-       int ret;
-
-       if (!snd_soc_codec_volatile_register(codec, reg) &&
-           reg < codec->driver->reg_cache_size &&
-           !codec->cache_bypass) {
-               ret = snd_soc_cache_write(codec, reg, value);
-               if (ret < 0)
-                       return -1;
-       }
-
-       if (codec->cache_only) {
-               codec->cache_sync = 1;
-               return 0;
-       }
-
-       ret = codec->hw_write(codec->control_data, data, len);
-       if (ret == len)
-               return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
-}
-
-static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg)
-{
-       int ret;
-       unsigned int val;
-
-       if (reg >= codec->driver->reg_cache_size ||
-           snd_soc_codec_volatile_register(codec, reg) ||
-           codec->cache_bypass) {
-               if (codec->cache_only)
-                       return -1;
-
-               BUG_ON(!codec->hw_read);
-               return codec->hw_read(codec, reg);
-       }
-
-       ret = snd_soc_cache_read(codec, reg, &val);
-       if (ret < 0)
-               return -1;
-       return val;
-}
-
-static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
-                                     unsigned int reg)
-{
-       return do_hw_read(codec, reg);
-}
-
-static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
-{
-       u16 data;
-
-       data = cpu_to_be16((reg << 12) | (value & 0xffffff));
-
-       return do_hw_write(codec, reg, value, &data, 2);
-}
-
-static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
-                                    unsigned int reg)
-{
-       return do_hw_read(codec, reg);
-}
-
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
-{
-       u8 data[2];
-
-       data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-       data[1] = value & 0x00ff;
-
-       return do_hw_write(codec, reg, value, data, 2);
-}
-
-static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
-{
-       u8 data[2];
-
-       reg &= 0xff;
-       data[0] = reg;
-       data[1] = value & 0xff;
-
-       return do_hw_write(codec, reg, value, data, 2);
-}
-
-static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
-                                    unsigned int reg)
-{
-       return do_hw_read(codec, reg);
-}
-
-static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
-{
-       u8 data[3];
-
-       data[0] = reg;
-       data[1] = (value >> 8) & 0xff;
-       data[2] = value & 0xff;
-
-       return do_hw_write(codec, reg, value, data, 3);
-}
-
-static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
-                                     unsigned int reg)
-{
-       return do_hw_read(codec, reg);
-}
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int do_i2c_read(struct snd_soc_codec *codec,
-                               void *reg, int reglen,
-                               void *data, int datalen)
-{
-       struct i2c_msg xfer[2];
-       int ret;
-       struct i2c_client *client = codec->control_data;
-
-       /* Write register */
-       xfer[0].addr = client->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = reglen;
-       xfer[0].buf = reg;
-
-       /* Read data */
-       xfer[1].addr = client->addr;
-       xfer[1].flags = I2C_M_RD;
-       xfer[1].len = datalen;
-       xfer[1].buf = data;
-
-       ret = i2c_transfer(client->adapter, xfer, 2);
-       if (ret == 2)
-               return 0;
-       else if (ret < 0)
-               return ret;
-       else
-               return -EIO;
-}
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
-                                        unsigned int r)
-{
-       u8 reg = r;
-       u8 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 1, &data, 1);
-       if (ret < 0)
-               return 0;
-       return data;
-}
-#else
-#define snd_soc_8_8_read_i2c NULL
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
-                                         unsigned int r)
-{
-       u8 reg = r;
-       u16 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 1, &data, 2);
-       if (ret < 0)
-               return 0;
-       return (data >> 8) | ((data & 0xff) << 8);
-}
-#else
-#define snd_soc_8_16_read_i2c NULL
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
-                                         unsigned int r)
-{
-       u16 reg = r;
-       u8 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 2, &data, 1);
-       if (ret < 0)
-               return 0;
-       return data;
-}
-#else
-#define snd_soc_16_8_read_i2c NULL
-#endif
-
-static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
-                                     unsigned int reg)
-{
-       return do_hw_read(codec, reg);
-}
-
-static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
-{
-       u8 data[3];
-
-       data[0] = (reg >> 8) & 0xff;
-       data[1] = reg & 0xff;
-       data[2] = value;
-
-       return do_hw_write(codec, reg, value, data, 3);
-}
-
-#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)
-{
-       u16 reg = cpu_to_be16(r);
-       u16 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 2, &data, 2);
-       if (ret < 0)
-               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)
-{
-       return do_hw_read(codec, reg);
-}
-
-static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
-                              unsigned int value)
-{
-       u8 data[4];
-
-       data[0] = (reg >> 8) & 0xff;
-       data[1] = reg & 0xff;
-       data[2] = (value >> 8) & 0xff;
-       data[3] = value & 0xff;
-
-       return do_hw_write(codec, reg, value, data, 4);
-}
-
-/* Primitive bulk write support for soc-cache.  The data pointed to by
- * `data' needs to already be in the form the hardware expects
- * including any leading register specific data.  Any data written
- * through this function will not go through the cache as it only
- * handles writing to volatile or out of bounds registers.
- */
-static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
-                                    const void *data, size_t len)
-{
-       int ret;
-
-       /* To ensure that we don't get out of sync with the cache, check
-        * whether the base register is volatile or if we've directly asked
-        * to bypass the cache.  Out of bounds registers are considered
-        * volatile.
-        */
-       if (!codec->cache_bypass
-           && !snd_soc_codec_volatile_register(codec, reg)
-           && reg < codec->driver->reg_cache_size)
-               return -EINVAL;
-
-       switch (codec->control_type) {
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-       case SND_SOC_I2C:
-               ret = i2c_master_send(codec->control_data, data, len);
-               break;
-#endif
-#if defined(CONFIG_SPI_MASTER)
-       case SND_SOC_SPI:
-               ret = spi_write(codec->control_data, data, len);
-               break;
-#endif
-       default:
-               BUG();
-       }
-
-       if (ret == len)
-               return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
-}
-
-static struct {
-       int addr_bits;
-       int data_bits;
-       int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
-       unsigned int (*read)(struct snd_soc_codec *, unsigned int);
-       unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
-} io_types[] = {
-       {
-               .addr_bits = 4, .data_bits = 12,
-               .write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
-       },
-       {
-               .addr_bits = 7, .data_bits = 9,
-               .write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
-       },
-       {
-               .addr_bits = 8, .data_bits = 8,
-               .write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
-               .i2c_read = snd_soc_8_8_read_i2c,
-       },
-       {
-               .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,
-       },
-       {
-               .addr_bits = 16, .data_bits = 8,
-               .write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
-               .i2c_read = snd_soc_16_8_read_i2c,
-       },
-       {
-               .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,
-       },
-};
-
-/**
- * snd_soc_codec_set_cache_io: Set up standard I/O functions.
- *
- * @codec: CODEC to configure.
- * @addr_bits: Number of bits of register address data.
- * @data_bits: Number of bits of data per register.
- * @control: Control bus used.
- *
- * Register formats are frequently shared between many I2C and SPI
- * devices.  In order to promote code reuse the ASoC core provides
- * some standard implementations of CODEC read and write operations
- * which can be set up using this function.
- *
- * The caller is responsible for allocating and initialising the
- * actual cache.
- *
- * Note that at present this code cannot be used by CODECs with
- * volatile registers.
- */
-int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
-                              int addr_bits, int data_bits,
-                              enum snd_soc_control_type control)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(io_types); i++)
-               if (io_types[i].addr_bits == addr_bits &&
-                   io_types[i].data_bits == data_bits)
-                       break;
-       if (i == ARRAY_SIZE(io_types)) {
-               printk(KERN_ERR
-                      "No I/O functions for %d bit address %d bit data\n",
-                      addr_bits, data_bits);
-               return -EINVAL;
-       }
-
-       codec->write = io_types[i].write;
-       codec->read = io_types[i].read;
-       codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
-
-       switch (control) {
-       case SND_SOC_I2C:
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-               codec->hw_write = (hw_write_t)i2c_master_send;
-#endif
-               if (io_types[i].i2c_read)
-                       codec->hw_read = io_types[i].i2c_read;
-
-               codec->control_data = container_of(codec->dev,
-                                                  struct i2c_client,
-                                                  dev);
-               break;
-
-       case SND_SOC_SPI:
-#ifdef CONFIG_SPI_MASTER
-               codec->hw_write = do_spi_write;
-#endif
-
-               codec->control_data = container_of(codec->dev,
-                                                  struct spi_device,
-                                                  dev);
-               break;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-
 static bool snd_soc_set_cache_val(void *base, unsigned int idx,
                                  unsigned int val, unsigned int word_size)
 {
@@ -483,31 +67,86 @@ static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
 }
 
 struct snd_soc_rbtree_node {
-       struct rb_node node;
-       unsigned int reg;
-       unsigned int value;
-       unsigned int defval;
+       struct rb_node node; /* the actual rbtree node holding this block */
+       unsigned int base_reg; /* base register handled by this block */
+       unsigned int word_size; /* number of bytes needed to represent the register index */
+       void *block; /* block of adjacent registers */
+       unsigned int blklen; /* number of registers available in the block */
 } __attribute__ ((packed));
 
 struct snd_soc_rbtree_ctx {
        struct rb_root root;
+       struct snd_soc_rbtree_node *cached_rbnode;
 };
 
+static inline void snd_soc_rbtree_get_base_top_reg(
+       struct snd_soc_rbtree_node *rbnode,
+       unsigned int *base, unsigned int *top)
+{
+       *base = rbnode->base_reg;
+       *top = rbnode->base_reg + rbnode->blklen - 1;
+}
+
+static unsigned int snd_soc_rbtree_get_register(
+       struct snd_soc_rbtree_node *rbnode, unsigned int idx)
+{
+       unsigned int val;
+
+       switch (rbnode->word_size) {
+       case 1: {
+               u8 *p = rbnode->block;
+               val = p[idx];
+               return val;
+       }
+       case 2: {
+               u16 *p = rbnode->block;
+               val = p[idx];
+               return val;
+       }
+       default:
+               BUG();
+               break;
+       }
+       return -1;
+}
+
+static void snd_soc_rbtree_set_register(struct snd_soc_rbtree_node *rbnode,
+                                       unsigned int idx, unsigned int val)
+{
+       switch (rbnode->word_size) {
+       case 1: {
+               u8 *p = rbnode->block;
+               p[idx] = val;
+               break;
+       }
+       case 2: {
+               u16 *p = rbnode->block;
+               p[idx] = val;
+               break;
+       }
+       default:
+               BUG();
+               break;
+       }
+}
+
 static struct snd_soc_rbtree_node *snd_soc_rbtree_lookup(
        struct rb_root *root, unsigned int reg)
 {
        struct rb_node *node;
        struct snd_soc_rbtree_node *rbnode;
+       unsigned int base_reg, top_reg;
 
        node = root->rb_node;
        while (node) {
                rbnode = container_of(node, struct snd_soc_rbtree_node, node);
-               if (rbnode->reg < reg)
-                       node = node->rb_left;
-               else if (rbnode->reg > reg)
-                       node = node->rb_right;
-               else
+               snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+               if (reg >= base_reg && reg <= top_reg)
                        return rbnode;
+               else if (reg > top_reg)
+                       node = node->rb_right;
+               else if (reg < base_reg)
+                       node = node->rb_left;
        }
 
        return NULL;
@@ -518,19 +157,28 @@ static int snd_soc_rbtree_insert(struct rb_root *root,
 {
        struct rb_node **new, *parent;
        struct snd_soc_rbtree_node *rbnode_tmp;
+       unsigned int base_reg_tmp, top_reg_tmp;
+       unsigned int base_reg;
 
        parent = NULL;
        new = &root->rb_node;
        while (*new) {
                rbnode_tmp = container_of(*new, struct snd_soc_rbtree_node,
                                          node);
+               /* base and top registers of the current rbnode */
+               snd_soc_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
+                                               &top_reg_tmp);
+               /* base register of the rbnode to be added */
+               base_reg = rbnode->base_reg;
                parent = *new;
-               if (rbnode_tmp->reg < rbnode->reg)
-                       new = &((*new)->rb_left);
-               else if (rbnode_tmp->reg > rbnode->reg)
-                       new = &((*new)->rb_right);
-               else
+               /* if this register has already been inserted, just return */
+               if (base_reg >= base_reg_tmp &&
+                   base_reg <= top_reg_tmp)
                        return 0;
+               else if (base_reg > top_reg_tmp)
+                       new = &((*new)->rb_right);
+               else if (base_reg < base_reg_tmp)
+                       new = &((*new)->rb_left);
        }
 
        /* insert the node into the rbtree */
@@ -545,58 +193,146 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec)
        struct snd_soc_rbtree_ctx *rbtree_ctx;
        struct rb_node *node;
        struct snd_soc_rbtree_node *rbnode;
-       unsigned int val;
+       unsigned int regtmp;
+       unsigned int val, def;
        int ret;
+       int i;
 
        rbtree_ctx = codec->reg_cache;
        for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
                rbnode = rb_entry(node, struct snd_soc_rbtree_node, node);
-               if (rbnode->value == rbnode->defval)
-                       continue;
-               WARN_ON(codec->writable_register &&
-                       codec->writable_register(codec, rbnode->reg));
-               ret = snd_soc_cache_read(codec, rbnode->reg, &val);
-               if (ret)
-                       return ret;
-               codec->cache_bypass = 1;
-               ret = snd_soc_write(codec, rbnode->reg, val);
-               codec->cache_bypass = 0;
-               if (ret)
-                       return ret;
-               dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
-                       rbnode->reg, val);
+               for (i = 0; i < rbnode->blklen; ++i) {
+                       regtmp = rbnode->base_reg + i;
+                       WARN_ON(codec->writable_register &&
+                               codec->writable_register(codec, regtmp));
+                       val = snd_soc_rbtree_get_register(rbnode, i);
+                       def = snd_soc_get_cache_val(codec->reg_def_copy, i,
+                                                   rbnode->word_size);
+                       if (val == def)
+                               continue;
+
+                       codec->cache_bypass = 1;
+                       ret = snd_soc_write(codec, regtmp, val);
+                       codec->cache_bypass = 0;
+                       if (ret)
+                               return ret;
+                       dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
+                               regtmp, val);
+               }
        }
 
        return 0;
 }
 
+static int snd_soc_rbtree_insert_to_block(struct snd_soc_rbtree_node *rbnode,
+                                         unsigned int pos, unsigned int reg,
+                                         unsigned int value)
+{
+       u8 *blk;
+
+       blk = krealloc(rbnode->block,
+                      (rbnode->blklen + 1) * rbnode->word_size, GFP_KERNEL);
+       if (!blk)
+               return -ENOMEM;
+
+       /* insert the register value in the correct place in the rbnode block */
+       memmove(blk + (pos + 1) * rbnode->word_size,
+               blk + pos * rbnode->word_size,
+               (rbnode->blklen - pos) * rbnode->word_size);
+
+       /* update the rbnode block, its size and the base register */
+       rbnode->block = blk;
+       rbnode->blklen++;
+       if (!pos)
+               rbnode->base_reg = reg;
+
+       snd_soc_rbtree_set_register(rbnode, pos, value);
+       return 0;
+}
+
 static int snd_soc_rbtree_cache_write(struct snd_soc_codec *codec,
                                      unsigned int reg, unsigned int value)
 {
        struct snd_soc_rbtree_ctx *rbtree_ctx;
-       struct snd_soc_rbtree_node *rbnode;
+       struct snd_soc_rbtree_node *rbnode, *rbnode_tmp;
+       struct rb_node *node;
+       unsigned int val;
+       unsigned int reg_tmp;
+       unsigned int base_reg, top_reg;
+       unsigned int pos;
+       int i;
+       int ret;
 
        rbtree_ctx = codec->reg_cache;
+       /* look up the required register in the cached rbnode */
+       rbnode = rbtree_ctx->cached_rbnode;
+       if (rbnode) {
+               snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+               if (reg >= base_reg && reg <= top_reg) {
+                       reg_tmp = reg - base_reg;
+                       val = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+                       if (val == value)
+                               return 0;
+                       snd_soc_rbtree_set_register(rbnode, reg_tmp, value);
+                       return 0;
+               }
+       }
+       /* if we can't locate it in the cached rbnode we'll have
+        * to traverse the rbtree looking for it.
+        */
        rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
        if (rbnode) {
-               if (rbnode->value == value)
+               reg_tmp = reg - rbnode->base_reg;
+               val = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+               if (val == value)
                        return 0;
-               rbnode->value = value;
+               snd_soc_rbtree_set_register(rbnode, reg_tmp, value);
+               rbtree_ctx->cached_rbnode = rbnode;
        } else {
                /* bail out early, no need to create the rbnode yet */
                if (!value)
                        return 0;
-               /*
-                * for uninitialized registers whose value is changed
-                * from the default zero, create an rbnode and insert
-                * it into the tree.
+               /* look for an adjacent register to the one we are about to add */
+               for (node = rb_first(&rbtree_ctx->root); node;
+                    node = rb_next(node)) {
+                       rbnode_tmp = rb_entry(node, struct snd_soc_rbtree_node, node);
+                       for (i = 0; i < rbnode_tmp->blklen; ++i) {
+                               reg_tmp = rbnode_tmp->base_reg + i;
+                               if (abs(reg_tmp - reg) != 1)
+                                       continue;
+                               /* decide where in the block to place our register */
+                               if (reg_tmp + 1 == reg)
+                                       pos = i + 1;
+                               else
+                                       pos = i;
+                               ret = snd_soc_rbtree_insert_to_block(rbnode_tmp, pos,
+                                                                    reg, value);
+                               if (ret)
+                                       return ret;
+                               rbtree_ctx->cached_rbnode = rbnode_tmp;
+                               return 0;
+                       }
+               }
+               /* we did not manage to find a place to insert it in an existing
+                * block so create a new rbnode with a single register in its block.
+                * This block will get populated further if any other adjacent
+                * registers get modified in the future.
                 */
                rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL);
                if (!rbnode)
                        return -ENOMEM;
-               rbnode->reg = reg;
-               rbnode->value = value;
+               rbnode->blklen = 1;
+               rbnode->base_reg = reg;
+               rbnode->word_size = codec->driver->reg_word_size;
+               rbnode->block = kmalloc(rbnode->blklen * rbnode->word_size,
+                                       GFP_KERNEL);
+               if (!rbnode->block) {
+                       kfree(rbnode);
+                       return -ENOMEM;
+               }
+               snd_soc_rbtree_set_register(rbnode, 0, value);
                snd_soc_rbtree_insert(&rbtree_ctx->root, rbnode);
+               rbtree_ctx->cached_rbnode = rbnode;
        }
 
        return 0;
@@ -607,11 +343,28 @@ static int snd_soc_rbtree_cache_read(struct snd_soc_codec *codec,
 {
        struct snd_soc_rbtree_ctx *rbtree_ctx;
        struct snd_soc_rbtree_node *rbnode;
+       unsigned int base_reg, top_reg;
+       unsigned int reg_tmp;
 
        rbtree_ctx = codec->reg_cache;
+       /* look up the required register in the cached rbnode */
+       rbnode = rbtree_ctx->cached_rbnode;
+       if (rbnode) {
+               snd_soc_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
+               if (reg >= base_reg && reg <= top_reg) {
+                       reg_tmp = reg - base_reg;
+                       *value = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+                       return 0;
+               }
+       }
+       /* if we can't locate it in the cached rbnode we'll have
+        * to traverse the rbtree looking for it.
+        */
        rbnode = snd_soc_rbtree_lookup(&rbtree_ctx->root, reg);
        if (rbnode) {
-               *value = rbnode->value;
+               reg_tmp = reg - rbnode->base_reg;
+               *value = snd_soc_rbtree_get_register(rbnode, reg_tmp);
+               rbtree_ctx->cached_rbnode = rbnode;
        } else {
                /* uninitialized registers default to 0 */
                *value = 0;
@@ -637,6 +390,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
                rbtree_node = rb_entry(next, struct snd_soc_rbtree_node, node);
                next = rb_next(&rbtree_node->node);
                rb_erase(&rbtree_node->node, &rbtree_ctx->root);
+               kfree(rbtree_node->block);
                kfree(rbtree_node);
        }
 
@@ -649,10 +403,9 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec)
 
 static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
 {
-       struct snd_soc_rbtree_node *rbtree_node;
        struct snd_soc_rbtree_ctx *rbtree_ctx;
-       unsigned int val;
        unsigned int word_size;
+       unsigned int val;
        int i;
        int ret;
 
@@ -662,32 +415,27 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
 
        rbtree_ctx = codec->reg_cache;
        rbtree_ctx->root = RB_ROOT;
+       rbtree_ctx->cached_rbnode = NULL;
 
        if (!codec->reg_def_copy)
                return 0;
 
-       /*
-        * populate the rbtree with the initialized registers.  All other
-        * registers will be inserted when they are first modified.
-        */
        word_size = codec->driver->reg_word_size;
        for (i = 0; i < codec->driver->reg_cache_size; ++i) {
-               val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
+               val = snd_soc_get_cache_val(codec->reg_def_copy, i,
+                                           word_size);
                if (!val)
                        continue;
-               rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
-               if (!rbtree_node) {
-                       ret = -ENOMEM;
-                       snd_soc_cache_exit(codec);
-                       break;
-               }
-               rbtree_node->reg = i;
-               rbtree_node->value = val;
-               rbtree_node->defval = val;
-               snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
+               ret = snd_soc_rbtree_cache_write(codec, i, val);
+               if (ret)
+                       goto err;
        }
 
        return 0;
+
+err:
+       snd_soc_cache_exit(codec);
+       return ret;
 }
 
 #ifdef CONFIG_SND_SOC_CACHE_LZO
index d75043e..32bc503 100644 (file)
@@ -44,7 +44,6 @@
 
 #define NAME_SIZE      32
 
-static DEFINE_MUTEX(pcm_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
 #ifdef CONFIG_DEBUG_FS
@@ -58,7 +57,7 @@ static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
 
-static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
+int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
 
 /*
  * This is a timeout to do a DAPM powerdown after a stream is closed().
@@ -485,552 +484,6 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
-static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       if (!codec_dai->driver->symmetric_rates &&
-           !cpu_dai->driver->symmetric_rates &&
-           !rtd->dai_link->symmetric_rates)
-               return 0;
-
-       /* This can happen if multiple streams are starting simultaneously -
-        * the second can need to get its constraints before the first has
-        * picked a rate.  Complain and allow the application to carry on.
-        */
-       if (!rtd->rate) {
-               dev_warn(&rtd->dev,
-                        "Not enforcing symmetric_rates due to race\n");
-               return 0;
-       }
-
-       dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
-
-       ret = snd_pcm_hw_constraint_minmax(substream->runtime,
-                                          SNDRV_PCM_HW_PARAM_RATE,
-                                          rtd->rate, rtd->rate);
-       if (ret < 0) {
-               dev_err(&rtd->dev,
-                       "Unable to apply rate symmetry constraint: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-/*
- * Called by ALSA when a PCM substream is opened, the runtime->hw record is
- * then initialized and any private data can be allocated. This also calls
- * startup for the cpu DAI, platform, machine and codec DAI.
- */
-static int soc_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
-       struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
-       int ret = 0;
-
-       mutex_lock(&pcm_mutex);
-
-       /* startup the audio subsystem */
-       if (cpu_dai->driver->ops->startup) {
-               ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: can't open interface %s\n",
-                               cpu_dai->name);
-                       goto out;
-               }
-       }
-
-       if (platform->driver->ops && platform->driver->ops->open) {
-               ret = platform->driver->ops->open(substream);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
-                       goto platform_err;
-               }
-       }
-
-       if (codec_dai->driver->ops->startup) {
-               ret = codec_dai->driver->ops->startup(substream, codec_dai);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: can't open codec %s\n",
-                               codec_dai->name);
-                       goto codec_dai_err;
-               }
-       }
-
-       if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
-               ret = rtd->dai_link->ops->startup(substream);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
-                       goto machine_err;
-               }
-       }
-
-       /* Check that the codec and cpu DAIs are compatible */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               runtime->hw.rate_min =
-                       max(codec_dai_drv->playback.rate_min,
-                           cpu_dai_drv->playback.rate_min);
-               runtime->hw.rate_max =
-                       min(codec_dai_drv->playback.rate_max,
-                           cpu_dai_drv->playback.rate_max);
-               runtime->hw.channels_min =
-                       max(codec_dai_drv->playback.channels_min,
-                               cpu_dai_drv->playback.channels_min);
-               runtime->hw.channels_max =
-                       min(codec_dai_drv->playback.channels_max,
-                               cpu_dai_drv->playback.channels_max);
-               runtime->hw.formats =
-                       codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
-               runtime->hw.rates =
-                       codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
-               if (codec_dai_drv->playback.rates
-                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
-                       runtime->hw.rates |= cpu_dai_drv->playback.rates;
-               if (cpu_dai_drv->playback.rates
-                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
-                       runtime->hw.rates |= codec_dai_drv->playback.rates;
-       } else {
-               runtime->hw.rate_min =
-                       max(codec_dai_drv->capture.rate_min,
-                           cpu_dai_drv->capture.rate_min);
-               runtime->hw.rate_max =
-                       min(codec_dai_drv->capture.rate_max,
-                           cpu_dai_drv->capture.rate_max);
-               runtime->hw.channels_min =
-                       max(codec_dai_drv->capture.channels_min,
-                               cpu_dai_drv->capture.channels_min);
-               runtime->hw.channels_max =
-                       min(codec_dai_drv->capture.channels_max,
-                               cpu_dai_drv->capture.channels_max);
-               runtime->hw.formats =
-                       codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
-               runtime->hw.rates =
-                       codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
-               if (codec_dai_drv->capture.rates
-                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
-                       runtime->hw.rates |= cpu_dai_drv->capture.rates;
-               if (cpu_dai_drv->capture.rates
-                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
-                       runtime->hw.rates |= codec_dai_drv->capture.rates;
-       }
-
-       ret = -EINVAL;
-       snd_pcm_limit_hw_rates(runtime);
-       if (!runtime->hw.rates) {
-               printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
-                       codec_dai->name, cpu_dai->name);
-               goto config_err;
-       }
-       if (!runtime->hw.formats) {
-               printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
-                       codec_dai->name, cpu_dai->name);
-               goto config_err;
-       }
-       if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
-           runtime->hw.channels_min > runtime->hw.channels_max) {
-               printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
-                               codec_dai->name, cpu_dai->name);
-               goto config_err;
-       }
-
-       /* Symmetry only applies if we've already got an active stream. */
-       if (cpu_dai->active || codec_dai->active) {
-               ret = soc_pcm_apply_symmetry(substream);
-               if (ret != 0)
-                       goto config_err;
-       }
-
-       pr_debug("asoc: %s <-> %s info:\n",
-                       codec_dai->name, cpu_dai->name);
-       pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
-       pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
-                runtime->hw.channels_max);
-       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++;
-       } else {
-               cpu_dai->capture_active++;
-               codec_dai->capture_active++;
-       }
-       cpu_dai->active++;
-       codec_dai->active++;
-       rtd->codec->active++;
-       mutex_unlock(&pcm_mutex);
-       return 0;
-
-config_err:
-       if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
-               rtd->dai_link->ops->shutdown(substream);
-
-machine_err:
-       if (codec_dai->driver->ops->shutdown)
-               codec_dai->driver->ops->shutdown(substream, codec_dai);
-
-codec_dai_err:
-       if (platform->driver->ops && platform->driver->ops->close)
-               platform->driver->ops->close(substream);
-
-platform_err:
-       if (cpu_dai->driver->ops->shutdown)
-               cpu_dai->driver->ops->shutdown(substream, cpu_dai);
-out:
-       mutex_unlock(&pcm_mutex);
-       return ret;
-}
-
-/*
- * Power down the audio subsystem pmdown_time msecs after close is called.
- * This is to ensure there are no pops or clicks in between any music tracks
- * due to DAPM power cycling.
- */
-static void close_delayed_work(struct work_struct *work)
-{
-       struct snd_soc_pcm_runtime *rtd =
-                       container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
-       mutex_lock(&pcm_mutex);
-
-       pr_debug("pop wq checking: %s status: %s waiting: %s\n",
-                codec_dai->driver->playback.stream_name,
-                codec_dai->playback_active ? "active" : "inactive",
-                codec_dai->pop_wait ? "yes" : "no");
-
-       /* are we waiting on this codec DAI stream */
-       if (codec_dai->pop_wait == 1) {
-               codec_dai->pop_wait = 0;
-               snd_soc_dapm_stream_event(rtd,
-                       codec_dai->driver->playback.stream_name,
-                       SND_SOC_DAPM_STREAM_STOP);
-       }
-
-       mutex_unlock(&pcm_mutex);
-}
-
-/*
- * Called by ALSA when a PCM substream is closed. Private data can be
- * freed here. The cpu DAI, codec DAI, machine and platform are also
- * shutdown.
- */
-static int soc_codec_close(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = rtd->codec;
-
-       mutex_lock(&pcm_mutex);
-
-       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
-        * shutdown, for example from stopping clocks.
-        */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               snd_soc_dai_digital_mute(codec_dai, 1);
-
-       if (cpu_dai->driver->ops->shutdown)
-               cpu_dai->driver->ops->shutdown(substream, cpu_dai);
-
-       if (codec_dai->driver->ops->shutdown)
-               codec_dai->driver->ops->shutdown(substream, codec_dai);
-
-       if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
-               rtd->dai_link->ops->shutdown(substream);
-
-       if (platform->driver->ops && platform->driver->ops->close)
-               platform->driver->ops->close(substream);
-       cpu_dai->runtime = NULL;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               /* start delayed pop wq here for playback streams */
-               codec_dai->pop_wait = 1;
-               schedule_delayed_work(&rtd->delayed_work,
-                       msecs_to_jiffies(rtd->pmdown_time));
-       } else {
-               /* capture streams can be powered down now */
-               snd_soc_dapm_stream_event(rtd,
-                       codec_dai->driver->capture.stream_name,
-                       SND_SOC_DAPM_STREAM_STOP);
-       }
-
-       mutex_unlock(&pcm_mutex);
-       return 0;
-}
-
-/*
- * Called by ALSA when the PCM substream is prepared, can set format, sample
- * rate, etc.  This function is non atomic and can be called multiple times,
- * it can refer to the runtime info.
- */
-static int soc_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret = 0;
-
-       mutex_lock(&pcm_mutex);
-
-       if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
-               ret = rtd->dai_link->ops->prepare(substream);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: machine prepare error\n");
-                       goto out;
-               }
-       }
-
-       if (platform->driver->ops && platform->driver->ops->prepare) {
-               ret = platform->driver->ops->prepare(substream);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: platform prepare error\n");
-                       goto out;
-               }
-       }
-
-       if (codec_dai->driver->ops->prepare) {
-               ret = codec_dai->driver->ops->prepare(substream, codec_dai);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: codec DAI prepare error\n");
-                       goto out;
-               }
-       }
-
-       if (cpu_dai->driver->ops->prepare) {
-               ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: cpu DAI prepare error\n");
-                       goto out;
-               }
-       }
-
-       /* cancel any delayed stream shutdown that is pending */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
-           codec_dai->pop_wait) {
-               codec_dai->pop_wait = 0;
-               cancel_delayed_work(&rtd->delayed_work);
-       }
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               snd_soc_dapm_stream_event(rtd,
-                                         codec_dai->driver->playback.stream_name,
-                                         SND_SOC_DAPM_STREAM_START);
-       else
-               snd_soc_dapm_stream_event(rtd,
-                                         codec_dai->driver->capture.stream_name,
-                                         SND_SOC_DAPM_STREAM_START);
-
-       snd_soc_dai_digital_mute(codec_dai, 0);
-
-out:
-       mutex_unlock(&pcm_mutex);
-       return ret;
-}
-
-/*
- * Called by ALSA when the hardware params are set by application. This
- * function can also be called multiple times and can allocate buffers
- * (using snd_pcm_lib_* ). It's non-atomic.
- */
-static int soc_pcm_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_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret = 0;
-
-       mutex_lock(&pcm_mutex);
-
-       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
-               ret = rtd->dai_link->ops->hw_params(substream, params);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: machine hw_params failed\n");
-                       goto out;
-               }
-       }
-
-       if (codec_dai->driver->ops->hw_params) {
-               ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: can't set codec %s hw params\n",
-                               codec_dai->name);
-                       goto codec_err;
-               }
-       }
-
-       if (cpu_dai->driver->ops->hw_params) {
-               ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: interface %s hw params failed\n",
-                               cpu_dai->name);
-                       goto interface_err;
-               }
-       }
-
-       if (platform->driver->ops && platform->driver->ops->hw_params) {
-               ret = platform->driver->ops->hw_params(substream, params);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: platform %s hw params failed\n",
-                               platform->name);
-                       goto platform_err;
-               }
-       }
-
-       rtd->rate = params_rate(params);
-
-out:
-       mutex_unlock(&pcm_mutex);
-       return ret;
-
-platform_err:
-       if (cpu_dai->driver->ops->hw_free)
-               cpu_dai->driver->ops->hw_free(substream, cpu_dai);
-
-interface_err:
-       if (codec_dai->driver->ops->hw_free)
-               codec_dai->driver->ops->hw_free(substream, codec_dai);
-
-codec_err:
-       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
-               rtd->dai_link->ops->hw_free(substream);
-
-       mutex_unlock(&pcm_mutex);
-       return ret;
-}
-
-/*
- * Frees resources allocated by hw_params, can be called multiple times
- */
-static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = rtd->codec;
-
-       mutex_lock(&pcm_mutex);
-
-       /* apply codec digital mute */
-       if (!codec->active)
-               snd_soc_dai_digital_mute(codec_dai, 1);
-
-       /* free any machine hw params */
-       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
-               rtd->dai_link->ops->hw_free(substream);
-
-       /* free any DMA resources */
-       if (platform->driver->ops && platform->driver->ops->hw_free)
-               platform->driver->ops->hw_free(substream);
-
-       /* now free hw params for the DAIs  */
-       if (codec_dai->driver->ops->hw_free)
-               codec_dai->driver->ops->hw_free(substream, codec_dai);
-
-       if (cpu_dai->driver->ops->hw_free)
-               cpu_dai->driver->ops->hw_free(substream, cpu_dai);
-
-       mutex_unlock(&pcm_mutex);
-       return 0;
-}
-
-static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       if (codec_dai->driver->ops->trigger) {
-               ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (platform->driver->ops && platform->driver->ops->trigger) {
-               ret = platform->driver->ops->trigger(substream, cmd);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (cpu_dai->driver->ops->trigger) {
-               ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
-               if (ret < 0)
-                       return ret;
-       }
-       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_platform *platform = rtd->platform;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       snd_pcm_uframes_t offset = 0;
-       snd_pcm_sframes_t delay = 0;
-
-       if (platform->driver->ops && platform->driver->ops->pointer)
-               offset = platform->driver->ops->pointer(substream);
-
-       if (cpu_dai->driver->ops->delay)
-               delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
-
-       if (codec_dai->driver->ops->delay)
-               delay += codec_dai->driver->ops->delay(substream, codec_dai);
-
-       if (platform->driver->delay)
-               delay += platform->driver->delay(substream, codec_dai);
-
-       runtime->delay = delay;
-
-       return offset;
-}
-
-/* ASoC PCM operations */
-static struct snd_pcm_ops soc_pcm_ops = {
-       .open           = soc_pcm_open,
-       .close          = soc_codec_close,
-       .hw_params      = soc_pcm_hw_params,
-       .hw_free        = soc_pcm_hw_free,
-       .prepare        = soc_pcm_prepare,
-       .trigger        = soc_pcm_trigger,
-       .pointer        = soc_pcm_pointer,
-};
-
 #ifdef CONFIG_PM_SLEEP
 /* powers down audio subsystem for suspend */
 int snd_soc_suspend(struct device *dev)
@@ -1256,7 +709,7 @@ static void soc_resume_deferred(struct work_struct *work)
 int snd_soc_resume(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
-       int i;
+       int i, ac97_control = 0;
 
        /* AC97 devices might have other drivers hanging off them so
         * need to resume immediately.  Other drivers don't have that
@@ -1265,14 +718,15 @@ int snd_soc_resume(struct device *dev)
         */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               if (cpu_dai->driver->ac97_control) {
-                       dev_dbg(dev, "Resuming AC97 immediately\n");
-                       soc_resume_deferred(&card->deferred_resume_work);
-               } else {
-                       dev_dbg(dev, "Scheduling resume work\n");
-                       if (!schedule_work(&card->deferred_resume_work))
-                               dev_err(dev, "resume work item may be lost\n");
-               }
+               ac97_control |= cpu_dai->driver->ac97_control;
+       }
+       if (ac97_control) {
+               dev_dbg(dev, "Resuming AC97 immediately\n");
+               soc_resume_deferred(&card->deferred_resume_work);
+       } else {
+               dev_dbg(dev, "Scheduling resume work\n");
+               if (!schedule_work(&card->deferred_resume_work))
+                       dev_err(dev, "resume work item may be lost\n");
        }
 
        return 0;
@@ -1393,7 +847,7 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
        module_put(codec->dev->driver->owner);
 }
 
-static void soc_remove_dai_link(struct snd_soc_card *card, int num)
+static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_codec *codec = rtd->codec;
@@ -1410,7 +864,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
        }
 
        /* remove the CODEC DAI */
-       if (codec_dai && codec_dai->probed) {
+       if (codec_dai && codec_dai->probed &&
+                       codec_dai->driver->remove_order == order) {
                if (codec_dai->driver->remove) {
                        err = codec_dai->driver->remove(codec_dai);
                        if (err < 0)
@@ -1421,7 +876,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
        }
 
        /* remove the platform */
-       if (platform && platform->probed) {
+       if (platform && platform->probed &&
+                       platform->driver->remove_order == order) {
                if (platform->driver->remove) {
                        err = platform->driver->remove(platform);
                        if (err < 0)
@@ -1433,11 +889,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
        }
 
        /* remove the CODEC */
-       if (codec && codec->probed)
+       if (codec && codec->probed &&
+                       codec->driver->remove_order == order)
                soc_remove_codec(codec);
 
        /* remove the cpu_dai */
-       if (cpu_dai && cpu_dai->probed) {
+       if (cpu_dai && cpu_dai->probed &&
+                       cpu_dai->driver->remove_order == order) {
                if (cpu_dai->driver->remove) {
                        err = cpu_dai->driver->remove(cpu_dai);
                        if (err < 0)
@@ -1451,11 +909,13 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
 
 static void soc_remove_dai_links(struct snd_soc_card *card)
 {
-       int i;
-
-       for (i = 0; i < card->num_rtd; i++)
-               soc_remove_dai_link(card, i);
+       int dai, order;
 
+       for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+                       order++) {
+               for (dai = 0; dai < card->num_rtd; dai++)
+                       soc_remove_dai_link(card, dai, order);
+       }
        card->num_rtd = 0;
 }
 
@@ -1572,6 +1032,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
        rtd->dev.parent = card->dev;
        rtd->dev.release = rtd_release;
        rtd->dev.init_name = name;
+       mutex_init(&rtd->pcm_mutex);
        ret = device_register(&rtd->dev);
        if (ret < 0) {
                dev_err(card->dev,
@@ -1596,7 +1057,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
        return 0;
 }
 
-static int soc_probe_dai_link(struct snd_soc_card *card, int num)
+static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
@@ -1605,7 +1066,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
        struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
        int ret;
 
-       dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);
+       dev_dbg(card->dev, "probe %s dai link %d late %d\n",
+                       card->name, num, order);
 
        /* config components */
        codec_dai->codec = codec;
@@ -1617,7 +1079,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
        rtd->pmdown_time = pmdown_time;
 
        /* probe the cpu_dai */
-       if (!cpu_dai->probed) {
+       if (!cpu_dai->probed &&
+                       cpu_dai->driver->probe_order == order) {
                if (!try_module_get(cpu_dai->dev->driver->owner))
                        return -ENODEV;
 
@@ -1636,14 +1099,16 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
        }
 
        /* probe the CODEC */
-       if (!codec->probed) {
+       if (!codec->probed &&
+                       codec->driver->probe_order == order) {
                ret = soc_probe_codec(card, codec);
                if (ret < 0)
                        return ret;
        }
 
        /* probe the platform */
-       if (!platform->probed) {
+       if (!platform->probed &&
+                       platform->driver->probe_order == order) {
                if (!try_module_get(platform->dev->driver->owner))
                        return -ENODEV;
 
@@ -1662,7 +1127,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
        }
 
        /* probe the CODEC DAI */
-       if (!codec_dai->probed) {
+       if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
                if (codec_dai->driver->probe) {
                        ret = codec_dai->driver->probe(codec_dai);
                        if (ret < 0) {
@@ -1677,8 +1142,9 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
                list_add(&codec_dai->card_list, &card->dai_dev_list);
        }
 
-       /* DAPM dai link stream work */
-       INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+       /* complete DAI probe during last probe */
+       if (order != SND_SOC_COMP_ORDER_LAST)
+               return 0;
 
        ret = soc_post_component_init(card, codec, num, 0);
        if (ret)
@@ -1817,7 +1283,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        struct snd_soc_codec *codec;
        struct snd_soc_codec_conf *codec_conf;
        enum snd_soc_compress_type compress_type;
-       int ret, i;
+       int ret, i, order;
 
        mutex_lock(&card->mutex);
 
@@ -1895,12 +1361,16 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                        goto card_probe_error;
        }
 
-       for (i = 0; i < card->num_links; i++) {
-               ret = soc_probe_dai_link(card, i);
-               if (ret < 0) {
-                       pr_err("asoc: failed to instantiate card %s: %d\n",
+       /* early DAI link probe */
+       for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+                       order++) {
+               for (i = 0; i < card->num_links; i++) {
+                       ret = soc_probe_dai_link(card, i, order);
+                       if (ret < 0) {
+                               pr_err("asoc: failed to instantiate card %s: %d\n",
                               card->name, ret);
-                       goto probe_dai_err;
+                               goto probe_dai_err;
+                       }
                }
        }
 
@@ -2095,67 +1565,6 @@ static struct platform_driver soc_driver = {
        .remove         = soc_remove,
 };
 
-/* create a new pcm */
-static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_pcm *pcm;
-       char new_name[64];
-       int ret = 0, playback = 0, capture = 0;
-
-       /* check client and interface hw capabilities */
-       snprintf(new_name, sizeof(new_name), "%s %s-%d",
-                       rtd->dai_link->stream_name, codec_dai->name, num);
-
-       if (codec_dai->driver->playback.channels_min)
-               playback = 1;
-       if (codec_dai->driver->capture.channels_min)
-               capture = 1;
-
-       dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
-       ret = snd_pcm_new(rtd->card->snd_card, new_name,
-                       num, playback, capture, &pcm);
-       if (ret < 0) {
-               printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
-               return ret;
-       }
-
-       rtd->pcm = pcm;
-       pcm->private_data = rtd;
-       if (platform->driver->ops) {
-               soc_pcm_ops.mmap = platform->driver->ops->mmap;
-               soc_pcm_ops.pointer = platform->driver->ops->pointer;
-               soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
-               soc_pcm_ops.copy = platform->driver->ops->copy;
-               soc_pcm_ops.silence = platform->driver->ops->silence;
-               soc_pcm_ops.ack = platform->driver->ops->ack;
-               soc_pcm_ops.page = platform->driver->ops->page;
-       }
-
-       if (playback)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
-
-       if (capture)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
-
-       if (platform->driver->pcm_new) {
-               ret = platform->driver->pcm_new(rtd->card->snd_card,
-                                               codec_dai, pcm);
-               if (ret < 0) {
-                       pr_err("asoc: platform pcm constructor failed\n");
-                       return ret;
-               }
-       }
-
-       pcm->private_free = platform->driver->pcm_free;
-       printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
-               cpu_dai->name);
-       return ret;
-}
-
 /**
  * snd_soc_codec_volatile_register: Report if a register is volatile.
  *
@@ -2322,7 +1731,7 @@ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
                return ret;
 
        old = ret;
-       new = (old & ~mask) | value;
+       new = (old & ~mask) | (value & mask);
        change = old != new;
        if (change) {
                ret = snd_soc_write(codec, reg, new);
index 32ab7fc..746349f 100644 (file)
@@ -139,39 +139,26 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
        struct snd_soc_card *card = dapm->card;
        int ret = 0;
 
-       switch (level) {
-       case SND_SOC_BIAS_ON:
-               dev_dbg(dapm->dev, "Setting full bias\n");
-               break;
-       case SND_SOC_BIAS_PREPARE:
-               dev_dbg(dapm->dev, "Setting bias prepare\n");
-               break;
-       case SND_SOC_BIAS_STANDBY:
-               dev_dbg(dapm->dev, "Setting standby bias\n");
-               break;
-       case SND_SOC_BIAS_OFF:
-               dev_dbg(dapm->dev, "Setting bias off\n");
-               break;
-       default:
-               dev_err(dapm->dev, "Setting invalid bias %d\n", level);
-               return -EINVAL;
-       }
-
        trace_snd_soc_bias_level_start(card, level);
 
        if (card && card->set_bias_level)
-               ret = card->set_bias_level(card, level);
-       if (ret == 0) {
-               if (dapm->codec && dapm->codec->driver->set_bias_level)
-                       ret = dapm->codec->driver->set_bias_level(dapm->codec, level);
+               ret = card->set_bias_level(card, dapm, level);
+       if (ret != 0)
+               goto out;
+
+       if (dapm->codec) {
+               if (dapm->codec->driver->set_bias_level)
+                       ret = dapm->codec->driver->set_bias_level(dapm->codec,
+                                                                 level);
                else
                        dapm->bias_level = level;
        }
-       if (ret == 0) {
-               if (card && card->set_bias_level_post)
-                       ret = card->set_bias_level_post(card, level);
-       }
+       if (ret != 0)
+               goto out;
 
+       if (card && card->set_bias_level_post)
+               ret = card->set_bias_level_post(card, dapm, level);
+out:
        trace_snd_soc_bias_level_done(card, level);
 
        return ret;
@@ -209,7 +196,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
                int val, item, bitmask;
 
                for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
-               ;
+                       ;
                val = snd_soc_read(w->codec, e->reg);
                item = (val >> e->shift_l) & (bitmask - 1);
 
@@ -606,6 +593,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
        }
 
        list_for_each_entry(path, &widget->sinks, list_source) {
+               if (path->weak)
+                       continue;
+
                if (path->walked)
                        continue;
 
@@ -656,6 +646,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        }
 
        list_for_each_entry(path, &widget->sources, list_sink) {
+               if (path->weak)
+                       continue;
+
                if (path->walked)
                        continue;
 
@@ -737,6 +730,9 @@ 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->weak)
+                       continue;
+
                if (path->connected &&
                    !path->connected(path->source, path->sink))
                        continue;
@@ -1041,16 +1037,17 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
        struct snd_soc_dapm_context *d = data;
        int ret;
 
-       if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) {
+       /* If we're off and we're not supposed to be go into STANDBY */
+       if (d->bias_level == SND_SOC_BIAS_OFF &&
+           d->target_bias_level != SND_SOC_BIAS_OFF) {
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
                if (ret != 0)
                        dev_err(d->dev,
                                "Failed to turn on bias: %d\n", ret);
        }
 
-       /* If we're changing to all on or all off then prepare */
-       if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) ||
-           (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) {
+       /* Prepare for a STADDBY->ON or ON->STANDBY transition */
+       if (d->bias_level != d->target_bias_level) {
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
                if (ret != 0)
                        dev_err(d->dev,
@@ -1067,7 +1064,9 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
        int ret;
 
        /* If we just powered the last thing off drop to standby bias */
-       if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) {
+       if (d->bias_level == SND_SOC_BIAS_PREPARE &&
+           (d->target_bias_level == SND_SOC_BIAS_STANDBY ||
+            d->target_bias_level == SND_SOC_BIAS_OFF)) {
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
                if (ret != 0)
                        dev_err(d->dev, "Failed to apply standby bias: %d\n",
@@ -1075,14 +1074,16 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
        }
 
        /* If we're in standby and can support bias off then do that */
-       if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) {
+       if (d->bias_level == SND_SOC_BIAS_STANDBY &&
+           d->target_bias_level == SND_SOC_BIAS_OFF) {
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
                if (ret != 0)
                        dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
        }
 
        /* If we just powered up then move to active bias */
-       if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) {
+       if (d->bias_level == SND_SOC_BIAS_PREPARE &&
+           d->target_bias_level == SND_SOC_BIAS_ON) {
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
                if (ret != 0)
                        dev_err(d->dev, "Failed to apply active bias: %d\n",
@@ -1107,13 +1108,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        LIST_HEAD(up_list);
        LIST_HEAD(down_list);
        LIST_HEAD(async_domain);
+       enum snd_soc_bias_level bias;
        int power;
 
        trace_snd_soc_dapm_start(card);
 
-       list_for_each_entry(d, &card->dapm_list, list)
-               if (d->n_widgets || d->codec == NULL)
-                       d->dev_power = 0;
+       list_for_each_entry(d, &card->dapm_list, list) {
+               if (d->n_widgets || d->codec == NULL) {
+                       if (d->idle_bias_off)
+                               d->target_bias_level = SND_SOC_BIAS_OFF;
+                       else
+                               d->target_bias_level = SND_SOC_BIAS_STANDBY;
+               }
+       }
 
        /* Check which widgets we need to power and store them in
         * lists indicating if they should be powered up or down.
@@ -1135,8 +1142,27 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                                power = w->power_check(w);
                        else
                                power = 1;
-                       if (power)
-                               w->dapm->dev_power = 1;
+
+                       if (power) {
+                               d = w->dapm;
+
+                               /* Supplies and micbiases only bring
+                                * the context up to STANDBY as unless
+                                * something else is active and
+                                * passing audio they generally don't
+                                * require full power.
+                                */
+                               switch (w->id) {
+                               case snd_soc_dapm_supply:
+                               case snd_soc_dapm_micbias:
+                                       if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
+                                               d->target_bias_level = SND_SOC_BIAS_STANDBY;
+                                       break;
+                               default:
+                                       d->target_bias_level = SND_SOC_BIAS_ON;
+                                       break;
+                               }
+                       }
 
                        if (w->power == power)
                                continue;
@@ -1160,24 +1186,19 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                switch (event) {
                case SND_SOC_DAPM_STREAM_START:
                case SND_SOC_DAPM_STREAM_RESUME:
-                       dapm->dev_power = 1;
+                       dapm->target_bias_level = SND_SOC_BIAS_ON;
                        break;
                case SND_SOC_DAPM_STREAM_STOP:
-                       dapm->dev_power = !!dapm->codec->active;
+                       if (dapm->codec->active)
+                               dapm->target_bias_level = SND_SOC_BIAS_ON;
+                       else
+                               dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
                        break;
                case SND_SOC_DAPM_STREAM_SUSPEND:
-                       dapm->dev_power = 0;
+                       dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
                        break;
                case SND_SOC_DAPM_STREAM_NOP:
-                       switch (dapm->bias_level) {
-                               case SND_SOC_BIAS_STANDBY:
-                               case SND_SOC_BIAS_OFF:
-                                       dapm->dev_power = 0;
-                                       break;
-                               default:
-                                       dapm->dev_power = 1;
-                                       break;
-                       }
+                       dapm->target_bias_level = dapm->bias_level;
                        break;
                default:
                        break;
@@ -1185,12 +1206,12 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        }
 
        /* Force all contexts in the card to the same bias state */
-       power = 0;
+       bias = SND_SOC_BIAS_OFF;
        list_for_each_entry(d, &card->dapm_list, list)
-               if (d->dev_power)
-                       power = 1;
+               if (d->target_bias_level > bias)
+                       bias = d->target_bias_level;
        list_for_each_entry(d, &card->dapm_list, list)
-               d->dev_power = power;
+               d->target_bias_level = bias;
 
 
        /* Run all the bias changes in parallel */
@@ -1794,6 +1815,84 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 
+static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
+                                  const struct snd_soc_dapm_route *route)
+{
+       struct snd_soc_dapm_widget *source = dapm_find_widget(dapm,
+                                                             route->source,
+                                                             true);
+       struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm,
+                                                           route->sink,
+                                                           true);
+       struct snd_soc_dapm_path *path;
+       int count = 0;
+
+       if (!source) {
+               dev_err(dapm->dev, "Unable to find source %s for weak route\n",
+                       route->source);
+               return -ENODEV;
+       }
+
+       if (!sink) {
+               dev_err(dapm->dev, "Unable to find sink %s for weak route\n",
+                       route->sink);
+               return -ENODEV;
+       }
+
+       if (route->control || route->connected)
+               dev_warn(dapm->dev, "Ignoring control for weak route %s->%s\n",
+                        route->source, route->sink);
+
+       list_for_each_entry(path, &source->sinks, list_source) {
+               if (path->sink == sink) {
+                       path->weak = 1;
+                       count++;
+               }
+       }
+
+       if (count == 0)
+               dev_err(dapm->dev, "No path found for weak route %s->%s\n",
+                       route->source, route->sink);
+       if (count > 1)
+               dev_warn(dapm->dev, "%d paths found for weak route %s->%s\n",
+                        count, route->source, route->sink);
+
+       return 0;
+}
+
+/**
+ * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak
+ * @dapm: DAPM context
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Mark existing routes matching those specified in the passed array
+ * as being weak, meaning that they are ignored for the purpose of
+ * power decisions.  The main intended use case is for sidetone paths
+ * which couple audio between other independent paths if they are both
+ * active in order to make the combination work better at the user
+ * level but which aren't intended to be "used".
+ *
+ * Note that CODEC drivers should not use this as sidetone type paths
+ * can frequently also be used as bypass paths.
+ */
+int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
+                            const struct snd_soc_dapm_route *route, int num)
+{
+       int i, err;
+       int ret = 0;
+
+       for (i = 0; i < num; i++) {
+               err = snd_soc_dapm_weak_route(dapm, route);
+               if (err)
+                       ret = err;
+               route++;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
+
 /**
  * snd_soc_dapm_new_widgets - add new dapm widgets
  * @dapm: DAPM context
diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c
new file mode 100644 (file)
index 0000000..cca490c
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * soc-io.c  --  ASoC register I/O helpers
+ *
+ * Copyright 2009-2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include <trace/events/asoc.h>
+
+#ifdef CONFIG_SPI_MASTER
+static int do_spi_write(void *control, const char *data, int len)
+{
+       struct spi_device *spi = control;
+       int ret;
+
+       ret = spi_write(spi, data, len);
+       if (ret < 0)
+               return ret;
+
+       return len;
+}
+#endif
+
+static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                      unsigned int value, const void *data, int len)
+{
+       int ret;
+
+       if (!snd_soc_codec_volatile_register(codec, reg) &&
+           reg < codec->driver->reg_cache_size &&
+           !codec->cache_bypass) {
+               ret = snd_soc_cache_write(codec, reg, value);
+               if (ret < 0)
+                       return -1;
+       }
+
+       if (codec->cache_only) {
+               codec->cache_sync = 1;
+               return 0;
+       }
+
+       ret = codec->hw_write(codec->control_data, data, len);
+       if (ret == len)
+               return 0;
+       if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+
+static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       int ret;
+       unsigned int val;
+
+       if (reg >= codec->driver->reg_cache_size ||
+           snd_soc_codec_volatile_register(codec, reg) ||
+           codec->cache_bypass) {
+               if (codec->cache_only)
+                       return -1;
+
+               BUG_ON(!codec->hw_read);
+               return codec->hw_read(codec, reg);
+       }
+
+       ret = snd_soc_cache_read(codec, reg, &val);
+       if (ret < 0)
+               return -1;
+       return val;
+}
+
+static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
+                             unsigned int value)
+{
+       u16 data;
+
+       data = cpu_to_be16((reg << 12) | (value & 0xffffff));
+
+       return do_hw_write(codec, reg, value, &data, 2);
+}
+
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u16 data;
+
+       data = cpu_to_be16((reg << 9) | (value & 0x1ff));
+
+       return do_hw_write(codec, reg, value, &data, 2);
+}
+
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+                            unsigned int value)
+{
+       u8 data[2];
+
+       reg &= 0xff;
+       data[0] = reg;
+       data[1] = value & 0xff;
+
+       return do_hw_write(codec, reg, value, data, 2);
+}
+
+static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
+                             unsigned int value)
+{
+       u8 data[3];
+       u16 val = cpu_to_be16(value);
+
+       data[0] = reg;
+       memcpy(&data[1], &val, sizeof(val));
+
+       return do_hw_write(codec, reg, value, data, 3);
+}
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int do_i2c_read(struct snd_soc_codec *codec,
+                               void *reg, int reglen,
+                               void *data, int datalen)
+{
+       struct i2c_msg xfer[2];
+       int ret;
+       struct i2c_client *client = codec->control_data;
+
+       /* Write register */
+       xfer[0].addr = client->addr;
+       xfer[0].flags = 0;
+       xfer[0].len = reglen;
+       xfer[0].buf = reg;
+
+       /* Read data */
+       xfer[1].addr = client->addr;
+       xfer[1].flags = I2C_M_RD;
+       xfer[1].len = datalen;
+       xfer[1].buf = data;
+
+       ret = i2c_transfer(client->adapter, xfer, 2);
+       if (ret == 2)
+               return 0;
+       else if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+#endif
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
+                                        unsigned int r)
+{
+       u8 reg = r;
+       u8 data;
+       int ret;
+
+       ret = do_i2c_read(codec, &reg, 1, &data, 1);
+       if (ret < 0)
+               return 0;
+       return data;
+}
+#else
+#define snd_soc_8_8_read_i2c NULL
+#endif
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
+                                         unsigned int r)
+{
+       u8 reg = r;
+       u16 data;
+       int ret;
+
+       ret = do_i2c_read(codec, &reg, 1, &data, 2);
+       if (ret < 0)
+               return 0;
+       return (data >> 8) | ((data & 0xff) << 8);
+}
+#else
+#define snd_soc_8_16_read_i2c NULL
+#endif
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
+                                         unsigned int r)
+{
+       u16 reg = r;
+       u8 data;
+       int ret;
+
+       ret = do_i2c_read(codec, &reg, 2, &data, 1);
+       if (ret < 0)
+               return 0;
+       return data;
+}
+#else
+#define snd_soc_16_8_read_i2c NULL
+#endif
+
+static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
+                             unsigned int value)
+{
+       u8 data[3];
+       u16 rval = cpu_to_be16(reg);
+
+       memcpy(data, &rval, sizeof(rval));
+       data[2] = value;
+
+       return do_hw_write(codec, reg, value, data, 3);
+}
+
+#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)
+{
+       u16 reg = cpu_to_be16(r);
+       u16 data;
+       int ret;
+
+       ret = do_i2c_read(codec, &reg, 2, &data, 2);
+       if (ret < 0)
+               return 0;
+       return be16_to_cpu(data);
+}
+#else
+#define snd_soc_16_16_read_i2c NULL
+#endif
+
+static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
+                              unsigned int value)
+{
+       u16 data[2];
+
+       data[0] = cpu_to_be16(reg);
+       data[1] = cpu_to_be16(value);
+
+       return do_hw_write(codec, reg, value, data, sizeof(data));
+}
+
+/* Primitive bulk write support for soc-cache.  The data pointed to by
+ * `data' needs to already be in the form the hardware expects
+ * including any leading register specific data.  Any data written
+ * through this function will not go through the cache as it only
+ * handles writing to volatile or out of bounds registers.
+ */
+static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
+                                    const void *data, size_t len)
+{
+       int ret;
+
+       /* To ensure that we don't get out of sync with the cache, check
+        * whether the base register is volatile or if we've directly asked
+        * to bypass the cache.  Out of bounds registers are considered
+        * volatile.
+        */
+       if (!codec->cache_bypass
+           && !snd_soc_codec_volatile_register(codec, reg)
+           && reg < codec->driver->reg_cache_size)
+               return -EINVAL;
+
+       switch (codec->control_type) {
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+       case SND_SOC_I2C:
+               ret = i2c_master_send(to_i2c_client(codec->dev), data, len);
+               break;
+#endif
+#if defined(CONFIG_SPI_MASTER)
+       case SND_SOC_SPI:
+               ret = spi_write(to_spi_device(codec->dev), data, len);
+               break;
+#endif
+       default:
+               BUG();
+       }
+
+       if (ret == len)
+               return 0;
+       if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+
+static struct {
+       int addr_bits;
+       int data_bits;
+       int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
+       unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+       unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
+} io_types[] = {
+       {
+               .addr_bits = 4, .data_bits = 12,
+               .write = snd_soc_4_12_write,
+       },
+       {
+               .addr_bits = 7, .data_bits = 9,
+               .write = snd_soc_7_9_write,
+       },
+       {
+               .addr_bits = 8, .data_bits = 8,
+               .write = snd_soc_8_8_write,
+               .i2c_read = snd_soc_8_8_read_i2c,
+       },
+       {
+               .addr_bits = 8, .data_bits = 16,
+               .write = snd_soc_8_16_write,
+               .i2c_read = snd_soc_8_16_read_i2c,
+       },
+       {
+               .addr_bits = 16, .data_bits = 8,
+               .write = snd_soc_16_8_write,
+               .i2c_read = snd_soc_16_8_read_i2c,
+       },
+       {
+               .addr_bits = 16, .data_bits = 16,
+               .write = snd_soc_16_16_write,
+               .i2c_read = snd_soc_16_16_read_i2c,
+       },
+};
+
+/**
+ * snd_soc_codec_set_cache_io: Set up standard I/O functions.
+ *
+ * @codec: CODEC to configure.
+ * @addr_bits: Number of bits of register address data.
+ * @data_bits: Number of bits of data per register.
+ * @control: Control bus used.
+ *
+ * Register formats are frequently shared between many I2C and SPI
+ * devices.  In order to promote code reuse the ASoC core provides
+ * some standard implementations of CODEC read and write operations
+ * which can be set up using this function.
+ *
+ * The caller is responsible for allocating and initialising the
+ * actual cache.
+ *
+ * Note that at present this code cannot be used by CODECs with
+ * volatile registers.
+ */
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+                              int addr_bits, int data_bits,
+                              enum snd_soc_control_type control)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(io_types); i++)
+               if (io_types[i].addr_bits == addr_bits &&
+                   io_types[i].data_bits == data_bits)
+                       break;
+       if (i == ARRAY_SIZE(io_types)) {
+               printk(KERN_ERR
+                      "No I/O functions for %d bit address %d bit data\n",
+                      addr_bits, data_bits);
+               return -EINVAL;
+       }
+
+       codec->write = io_types[i].write;
+       codec->read = hw_read;
+       codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
+
+       switch (control) {
+       case SND_SOC_I2C:
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+               codec->hw_write = (hw_write_t)i2c_master_send;
+#endif
+               if (io_types[i].i2c_read)
+                       codec->hw_read = io_types[i].i2c_read;
+
+               codec->control_data = container_of(codec->dev,
+                                                  struct i2c_client,
+                                                  dev);
+               break;
+
+       case SND_SOC_SPI:
+#ifdef CONFIG_SPI_MASTER
+               codec->hw_write = do_spi_write;
+#endif
+
+               codec->control_data = container_of(codec->dev,
+                                                  struct spi_device,
+                                                  dev);
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
+
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
new file mode 100644 (file)
index 0000000..b575939
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ * soc-pcm.c  --  ALSA SoC PCM
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Authors: Liam Girdwood <lrg@ti.com>
+ *          Mark Brown <broonie@opensource.wolfsonmicro.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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+static DEFINE_MUTEX(pcm_mutex);
+
+static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       if (!codec_dai->driver->symmetric_rates &&
+           !cpu_dai->driver->symmetric_rates &&
+           !rtd->dai_link->symmetric_rates)
+               return 0;
+
+       /* This can happen if multiple streams are starting simultaneously -
+        * the second can need to get its constraints before the first has
+        * picked a rate.  Complain and allow the application to carry on.
+        */
+       if (!rtd->rate) {
+               dev_warn(&rtd->dev,
+                        "Not enforcing symmetric_rates due to race\n");
+               return 0;
+       }
+
+       dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate);
+
+       ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                          SNDRV_PCM_HW_PARAM_RATE,
+                                          rtd->rate, rtd->rate);
+       if (ret < 0) {
+               dev_err(&rtd->dev,
+                       "Unable to apply rate symmetry constraint: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Called by ALSA when a PCM substream is opened, the runtime->hw record is
+ * then initialized and any private data can be allocated. This also calls
+ * startup for the cpu DAI, platform, machine and codec DAI.
+ */
+static int soc_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
+       struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
+       int ret = 0;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       /* startup the audio subsystem */
+       if (cpu_dai->driver->ops->startup) {
+               ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: can't open interface %s\n",
+                               cpu_dai->name);
+                       goto out;
+               }
+       }
+
+       if (platform->driver->ops && platform->driver->ops->open) {
+               ret = platform->driver->ops->open(substream);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: can't open platform %s\n", platform->name);
+                       goto platform_err;
+               }
+       }
+
+       if (codec_dai->driver->ops->startup) {
+               ret = codec_dai->driver->ops->startup(substream, codec_dai);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: can't open codec %s\n",
+                               codec_dai->name);
+                       goto codec_dai_err;
+               }
+       }
+
+       if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
+               ret = rtd->dai_link->ops->startup(substream);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: %s startup failed\n", rtd->dai_link->name);
+                       goto machine_err;
+               }
+       }
+
+       /* Check that the codec and cpu DAIs are compatible */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               runtime->hw.rate_min =
+                       max(codec_dai_drv->playback.rate_min,
+                           cpu_dai_drv->playback.rate_min);
+               runtime->hw.rate_max =
+                       min(codec_dai_drv->playback.rate_max,
+                           cpu_dai_drv->playback.rate_max);
+               runtime->hw.channels_min =
+                       max(codec_dai_drv->playback.channels_min,
+                               cpu_dai_drv->playback.channels_min);
+               runtime->hw.channels_max =
+                       min(codec_dai_drv->playback.channels_max,
+                               cpu_dai_drv->playback.channels_max);
+               runtime->hw.formats =
+                       codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats;
+               runtime->hw.rates =
+                       codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates;
+               if (codec_dai_drv->playback.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= cpu_dai_drv->playback.rates;
+               if (cpu_dai_drv->playback.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= codec_dai_drv->playback.rates;
+       } else {
+               runtime->hw.rate_min =
+                       max(codec_dai_drv->capture.rate_min,
+                           cpu_dai_drv->capture.rate_min);
+               runtime->hw.rate_max =
+                       min(codec_dai_drv->capture.rate_max,
+                           cpu_dai_drv->capture.rate_max);
+               runtime->hw.channels_min =
+                       max(codec_dai_drv->capture.channels_min,
+                               cpu_dai_drv->capture.channels_min);
+               runtime->hw.channels_max =
+                       min(codec_dai_drv->capture.channels_max,
+                               cpu_dai_drv->capture.channels_max);
+               runtime->hw.formats =
+                       codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
+               runtime->hw.rates =
+                       codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
+               if (codec_dai_drv->capture.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= cpu_dai_drv->capture.rates;
+               if (cpu_dai_drv->capture.rates
+                          & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))
+                       runtime->hw.rates |= codec_dai_drv->capture.rates;
+       }
+
+       ret = -EINVAL;
+       snd_pcm_limit_hw_rates(runtime);
+       if (!runtime->hw.rates) {
+               printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
+                       codec_dai->name, cpu_dai->name);
+               goto config_err;
+       }
+       if (!runtime->hw.formats) {
+               printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
+                       codec_dai->name, cpu_dai->name);
+               goto config_err;
+       }
+       if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
+           runtime->hw.channels_min > runtime->hw.channels_max) {
+               printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
+                               codec_dai->name, cpu_dai->name);
+               goto config_err;
+       }
+
+       /* Symmetry only applies if we've already got an active stream. */
+       if (cpu_dai->active || codec_dai->active) {
+               ret = soc_pcm_apply_symmetry(substream);
+               if (ret != 0)
+                       goto config_err;
+       }
+
+       pr_debug("asoc: %s <-> %s info:\n",
+                       codec_dai->name, cpu_dai->name);
+       pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
+       pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
+                runtime->hw.channels_max);
+       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++;
+       } else {
+               cpu_dai->capture_active++;
+               codec_dai->capture_active++;
+       }
+       cpu_dai->active++;
+       codec_dai->active++;
+       rtd->codec->active++;
+       mutex_unlock(&rtd->pcm_mutex);
+       return 0;
+
+config_err:
+       if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+               rtd->dai_link->ops->shutdown(substream);
+
+machine_err:
+       if (codec_dai->driver->ops->shutdown)
+               codec_dai->driver->ops->shutdown(substream, codec_dai);
+
+codec_dai_err:
+       if (platform->driver->ops && platform->driver->ops->close)
+               platform->driver->ops->close(substream);
+
+platform_err:
+       if (cpu_dai->driver->ops->shutdown)
+               cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+out:
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+
+/*
+ * Power down the audio subsystem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+static void close_delayed_work(struct work_struct *work)
+{
+       struct snd_soc_pcm_runtime *rtd =
+                       container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       pr_debug("pop wq checking: %s status: %s waiting: %s\n",
+                codec_dai->driver->playback.stream_name,
+                codec_dai->playback_active ? "active" : "inactive",
+                codec_dai->pop_wait ? "yes" : "no");
+
+       /* are we waiting on this codec DAI stream */
+       if (codec_dai->pop_wait == 1) {
+               codec_dai->pop_wait = 0;
+               snd_soc_dapm_stream_event(rtd,
+                       codec_dai->driver->playback.stream_name,
+                       SND_SOC_DAPM_STREAM_STOP);
+       }
+
+       mutex_unlock(&rtd->pcm_mutex);
+}
+
+/*
+ * Called by ALSA when a PCM substream is closed. Private data can be
+ * freed here. The cpu DAI, codec DAI, machine and platform are also
+ * shutdown.
+ */
+static int soc_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_codec *codec = rtd->codec;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       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
+        * shutdown, for example from stopping clocks.
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_dai_digital_mute(codec_dai, 1);
+
+       if (cpu_dai->driver->ops->shutdown)
+               cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+
+       if (codec_dai->driver->ops->shutdown)
+               codec_dai->driver->ops->shutdown(substream, codec_dai);
+
+       if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+               rtd->dai_link->ops->shutdown(substream);
+
+       if (platform->driver->ops && platform->driver->ops->close)
+               platform->driver->ops->close(substream);
+       cpu_dai->runtime = NULL;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* start delayed pop wq here for playback streams */
+               codec_dai->pop_wait = 1;
+               schedule_delayed_work(&rtd->delayed_work,
+                       msecs_to_jiffies(rtd->pmdown_time));
+       } else {
+               /* capture streams can be powered down now */
+               snd_soc_dapm_stream_event(rtd,
+                       codec_dai->driver->capture.stream_name,
+                       SND_SOC_DAPM_STREAM_STOP);
+       }
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return 0;
+}
+
+/*
+ * Called by ALSA when the PCM substream is prepared, can set format, sample
+ * rate, etc.  This function is non atomic and can be called multiple times,
+ * it can refer to the runtime info.
+ */
+static int soc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret = 0;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
+               ret = rtd->dai_link->ops->prepare(substream);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: machine prepare error\n");
+                       goto out;
+               }
+       }
+
+       if (platform->driver->ops && platform->driver->ops->prepare) {
+               ret = platform->driver->ops->prepare(substream);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: platform prepare error\n");
+                       goto out;
+               }
+       }
+
+       if (codec_dai->driver->ops->prepare) {
+               ret = codec_dai->driver->ops->prepare(substream, codec_dai);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: codec DAI prepare error\n");
+                       goto out;
+               }
+       }
+
+       if (cpu_dai->driver->ops->prepare) {
+               ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: cpu DAI prepare error\n");
+                       goto out;
+               }
+       }
+
+       /* cancel any delayed stream shutdown that is pending */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           codec_dai->pop_wait) {
+               codec_dai->pop_wait = 0;
+               cancel_delayed_work(&rtd->delayed_work);
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_dapm_stream_event(rtd,
+                                         codec_dai->driver->playback.stream_name,
+                                         SND_SOC_DAPM_STREAM_START);
+       else
+               snd_soc_dapm_stream_event(rtd,
+                                         codec_dai->driver->capture.stream_name,
+                                         SND_SOC_DAPM_STREAM_START);
+
+       snd_soc_dai_digital_mute(codec_dai, 0);
+
+out:
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+
+/*
+ * Called by ALSA when the hardware params are set by application. This
+ * function can also be called multiple times and can allocate buffers
+ * (using snd_pcm_lib_* ). It's non-atomic.
+ */
+static int soc_pcm_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_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret = 0;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
+               ret = rtd->dai_link->ops->hw_params(substream, params);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: machine hw_params failed\n");
+                       goto out;
+               }
+       }
+
+       if (codec_dai->driver->ops->hw_params) {
+               ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: can't set codec %s hw params\n",
+                               codec_dai->name);
+                       goto codec_err;
+               }
+       }
+
+       if (cpu_dai->driver->ops->hw_params) {
+               ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: interface %s hw params failed\n",
+                               cpu_dai->name);
+                       goto interface_err;
+               }
+       }
+
+       if (platform->driver->ops && platform->driver->ops->hw_params) {
+               ret = platform->driver->ops->hw_params(substream, params);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: platform %s hw params failed\n",
+                               platform->name);
+                       goto platform_err;
+               }
+       }
+
+       rtd->rate = params_rate(params);
+
+out:
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+
+platform_err:
+       if (cpu_dai->driver->ops->hw_free)
+               cpu_dai->driver->ops->hw_free(substream, cpu_dai);
+
+interface_err:
+       if (codec_dai->driver->ops->hw_free)
+               codec_dai->driver->ops->hw_free(substream, codec_dai);
+
+codec_err:
+       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
+               rtd->dai_link->ops->hw_free(substream);
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+
+/*
+ * Frees resources allocated by hw_params, can be called multiple times
+ */
+static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_codec *codec = rtd->codec;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       /* apply codec digital mute */
+       if (!codec->active)
+               snd_soc_dai_digital_mute(codec_dai, 1);
+
+       /* free any machine hw params */
+       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
+               rtd->dai_link->ops->hw_free(substream);
+
+       /* free any DMA resources */
+       if (platform->driver->ops && platform->driver->ops->hw_free)
+               platform->driver->ops->hw_free(substream);
+
+       /* now free hw params for the DAIs  */
+       if (codec_dai->driver->ops->hw_free)
+               codec_dai->driver->ops->hw_free(substream, codec_dai);
+
+       if (cpu_dai->driver->ops->hw_free)
+               cpu_dai->driver->ops->hw_free(substream, cpu_dai);
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return 0;
+}
+
+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       if (codec_dai->driver->ops->trigger) {
+               ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (platform->driver->ops && platform->driver->ops->trigger) {
+               ret = platform->driver->ops->trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (cpu_dai->driver->ops->trigger) {
+               ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
+               if (ret < 0)
+                       return ret;
+       }
+       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_platform *platform = rtd->platform;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_uframes_t offset = 0;
+       snd_pcm_sframes_t delay = 0;
+
+       if (platform->driver->ops && platform->driver->ops->pointer)
+               offset = platform->driver->ops->pointer(substream);
+
+       if (cpu_dai->driver->ops->delay)
+               delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
+
+       if (codec_dai->driver->ops->delay)
+               delay += codec_dai->driver->ops->delay(substream, codec_dai);
+
+       if (platform->driver->delay)
+               delay += platform->driver->delay(substream, codec_dai);
+
+       runtime->delay = delay;
+
+       return offset;
+}
+
+/* ASoC PCM operations */
+static struct snd_pcm_ops soc_pcm_ops = {
+       .open           = soc_pcm_open,
+       .close          = soc_pcm_close,
+       .hw_params      = soc_pcm_hw_params,
+       .hw_free        = soc_pcm_hw_free,
+       .prepare        = soc_pcm_prepare,
+       .trigger        = soc_pcm_trigger,
+       .pointer        = soc_pcm_pointer,
+};
+
+/* create a new pcm */
+int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_pcm *pcm;
+       char new_name[64];
+       int ret = 0, playback = 0, capture = 0;
+
+       /* check client and interface hw capabilities */
+       snprintf(new_name, sizeof(new_name), "%s %s-%d",
+                       rtd->dai_link->stream_name, codec_dai->name, num);
+
+       if (codec_dai->driver->playback.channels_min)
+               playback = 1;
+       if (codec_dai->driver->capture.channels_min)
+               capture = 1;
+
+       dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name);
+       ret = snd_pcm_new(rtd->card->snd_card, new_name,
+                       num, playback, capture, &pcm);
+       if (ret < 0) {
+               printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+               return ret;
+       }
+
+       /* DAPM dai link stream work */
+       INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+
+       rtd->pcm = pcm;
+       pcm->private_data = rtd;
+       if (platform->driver->ops) {
+               soc_pcm_ops.mmap = platform->driver->ops->mmap;
+               soc_pcm_ops.pointer = platform->driver->ops->pointer;
+               soc_pcm_ops.ioctl = platform->driver->ops->ioctl;
+               soc_pcm_ops.copy = platform->driver->ops->copy;
+               soc_pcm_ops.silence = platform->driver->ops->silence;
+               soc_pcm_ops.ack = platform->driver->ops->ack;
+               soc_pcm_ops.page = platform->driver->ops->page;
+       }
+
+       if (playback)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
+
+       if (capture)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
+
+       if (platform->driver->pcm_new) {
+               ret = platform->driver->pcm_new(rtd);
+               if (ret < 0) {
+                       pr_err("asoc: platform pcm constructor failed\n");
+                       return ret;
+               }
+       }
+
+       pcm->private_free = platform->driver->pcm_free;
+       printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
+               cpu_dai->name);
+       return ret;
+}
index 3c271f9..ff86e5e 100644 (file)
@@ -322,9 +322,11 @@ static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
 static u64 tegra_dma_mask = DMA_BIT_MASK(32);
 
-static int tegra_pcm_new(struct snd_card *card,
-                               struct snd_soc_dai *dai, struct snd_pcm *pcm)
+static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        int ret = 0;
 
        if (!card->dev->dma_mask)
index 0d6738a..a42e9ac 100644 (file)
@@ -267,7 +267,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
                }
                machine->gpio_requested |= GPIO_HP_MUTE;
 
-               gpio_direction_output(pdata->gpio_hp_mute, 0);
+               gpio_direction_output(pdata->gpio_hp_mute, 1);
        }
 
        if (gpio_is_valid(pdata->gpio_int_mic_en)) {
index f4aa4e0..34aa972 100644 (file)
@@ -288,9 +288,10 @@ static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-                           struct snd_pcm *pcm)
+static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_soc_dai *dai = rtd->cpu_dai;
+       struct snd_pcm *pcm = rtd->pcm;
        struct platform_device *pdev = to_platform_device(dai->platform->dev);
        struct txx9aclc_soc_device *dev;
        struct resource *r;