]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
ASoC: Merge dai_ops factor out
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 11 Mar 2009 16:51:31 +0000 (16:51 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 11 Mar 2009 16:51:31 +0000 (16:51 +0000)
Merge Eric Maio's patch to merge snd_soc_dai_ops out of line.  Fixed
merge issues and updated drivers, plus an issue with the ops for the two
s3c2443 AC97 DAIs having been merged.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
13 files changed:
1  2 
sound/soc/codecs/ak4104.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/uda1380.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8753.c
sound/soc/fsl/fsl_ssi.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/s3c24xx/s3c2412-i2s.c
sound/soc/s3c24xx/s3c2443-ac97.c
sound/soc/s3c24xx/s3c24xx-i2s.c

index 338381f4fe1eb509bab2c9e8123e10f8b163bda9,0000000000000000000000000000000000000000..4d47bc4f74284764ec24434167371d3245009df8
mode 100644,000000..100644
--- /dev/null
@@@ -1,363 -1,0 +1,365 @@@
-       .ops = {
-               .hw_params = ak4104_hw_params,
-               .set_fmt = ak4104_set_dai_fmt,
-       }
 +/*
 + * AK4104 ALSA SoC (ASoC) driver
 + *
 + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
 + *
 + *  This program is free software; you can redistribute  it and/or modify it
 + *  under the terms of  the GNU General  Public License as published by the
 + *  Free Software Foundation;  either version 2 of the  License, or (at your
 + *  option) any later version.
 + */
 +
 +#include <linux/module.h>
 +#include <sound/core.h>
 +#include <sound/soc.h>
 +#include <sound/initval.h>
 +#include <linux/spi/spi.h>
 +#include <sound/asoundef.h>
 +
 +#include "ak4104.h"
 +
 +/* AK4104 registers addresses */
 +#define AK4104_REG_CONTROL1           0x00
 +#define AK4104_REG_RESERVED           0x01
 +#define AK4104_REG_CONTROL2           0x02
 +#define AK4104_REG_TX                 0x03
 +#define AK4104_REG_CHN_STATUS(x)      ((x) + 0x04)
 +#define AK4104_NUM_REGS                       10
 +
 +#define AK4104_REG_MASK                       0x1f
 +#define AK4104_READ                   0xc0
 +#define AK4104_WRITE                  0xe0
 +#define AK4104_RESERVED_VAL           0x5b
 +
 +/* Bit masks for AK4104 registers */
 +#define AK4104_CONTROL1_RSTN          (1 << 0)
 +#define AK4104_CONTROL1_PW            (1 << 1)
 +#define AK4104_CONTROL1_DIF0          (1 << 2)
 +#define AK4104_CONTROL1_DIF1          (1 << 3)
 +
 +#define AK4104_CONTROL2_SEL0          (1 << 0)
 +#define AK4104_CONTROL2_SEL1          (1 << 1)
 +#define AK4104_CONTROL2_MODE          (1 << 2)
 +
 +#define AK4104_TX_TXE                 (1 << 0)
 +#define AK4104_TX_V                   (1 << 1)
 +
 +#define DRV_NAME "ak4104"
 +
 +struct ak4104_private {
 +      struct snd_soc_codec codec;
 +      u8 reg_cache[AK4104_NUM_REGS];
 +};
 +
 +static int ak4104_fill_cache(struct snd_soc_codec *codec)
 +{
 +      int i;
 +      u8 *reg_cache = codec->reg_cache;
 +      struct spi_device *spi = codec->control_data;
 +
 +      for (i = 0; i < codec->reg_cache_size; i++) {
 +              int ret = spi_w8r8(spi, i | AK4104_READ);
 +              if (ret < 0) {
 +                      dev_err(&spi->dev, "SPI write failure\n");
 +                      return ret;
 +              }
 +
 +              reg_cache[i] = ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static unsigned int ak4104_read_reg_cache(struct snd_soc_codec *codec,
 +                                        unsigned int reg)
 +{
 +      u8 *reg_cache = codec->reg_cache;
 +
 +      if (reg >= codec->reg_cache_size)
 +              return -EINVAL;
 +
 +      return reg_cache[reg];
 +}
 +
 +static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
 +                          unsigned int value)
 +{
 +      u8 *cache = codec->reg_cache;
 +      struct spi_device *spi = codec->control_data;
 +
 +      if (reg >= codec->reg_cache_size)
 +              return -EINVAL;
 +
 +      reg &= AK4104_REG_MASK;
 +      reg |= AK4104_WRITE;
 +
 +      /* only write to the hardware if value has changed */
 +      if (cache[reg] != value) {
 +              u8 tmp[2] = { reg, value };
 +              if (spi_write(spi, tmp, sizeof(tmp))) {
 +                      dev_err(&spi->dev, "SPI write failed\n");
 +                      return -EIO;
 +              }
 +
 +              cache[reg] = value;
 +      }
 +
 +      return 0;
 +}
 +
 +static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
 +                            unsigned int format)
 +{
 +      struct snd_soc_codec *codec = codec_dai->codec;
 +      int val = 0;
 +
 +      val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
 +      if (val < 0)
 +              return val;
 +
 +      val &= ~(AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1);
 +
 +      /* set DAI format */
 +      switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
 +      case SND_SOC_DAIFMT_RIGHT_J:
 +              break;
 +      case SND_SOC_DAIFMT_LEFT_J:
 +              val |= AK4104_CONTROL1_DIF0;
 +              break;
 +      case SND_SOC_DAIFMT_I2S:
 +              val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
 +              break;
 +      default:
 +              dev_err(codec->dev, "invalid dai format\n");
 +              return -EINVAL;
 +      }
 +
 +      /* This device can only be slave */
 +      if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
 +              return -EINVAL;
 +
 +      return ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
 +}
 +
 +static int ak4104_hw_params(struct snd_pcm_substream *substream,
 +                          struct snd_pcm_hw_params *params,
 +                          struct snd_soc_dai *dai)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct snd_soc_device *socdev = rtd->socdev;
 +      struct snd_soc_codec *codec = socdev->card->codec;
 +      int val = 0;
 +
 +      /* set the IEC958 bits: consumer mode, no copyright bit */
 +      val |= IEC958_AES0_CON_NOT_COPYRIGHT;
 +      ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(0), val);
 +
 +      val = 0;
 +
 +      switch (params_rate(params)) {
 +      case 44100:
 +              val |= IEC958_AES3_CON_FS_44100;
 +              break;
 +      case 48000:
 +              val |= IEC958_AES3_CON_FS_48000;
 +              break;
 +      case 32000:
 +              val |= IEC958_AES3_CON_FS_32000;
 +              break;
 +      default:
 +              dev_err(codec->dev, "unsupported sampling rate\n");
 +              return -EINVAL;
 +      }
 +
 +      return ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(3), val);
 +}
 +
++static struct snd_soc_dai_ops ak4101_dai_ops = {
++      .hw_params = ak4104_hw_params,
++      .set_fmt = ak4104_set_dai_fmt,
++};
++
 +struct snd_soc_dai ak4104_dai = {
 +      .name = DRV_NAME,
 +      .playback = {
 +              .stream_name = "Playback",
 +              .channels_min = 2,
 +              .channels_max = 2,
 +              .rates = SNDRV_PCM_RATE_44100 |
 +                       SNDRV_PCM_RATE_48000 |
 +                       SNDRV_PCM_RATE_32000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE  |
 +                         SNDRV_PCM_FMTBIT_S24_3LE |
 +                         SNDRV_PCM_FMTBIT_S24_LE
 +      },
++      .ops = &ak4101_dai_ops,
 +};
 +
 +static struct snd_soc_codec *ak4104_codec;
 +
 +static int ak4104_spi_probe(struct spi_device *spi)
 +{
 +      struct snd_soc_codec *codec;
 +      struct ak4104_private *ak4104;
 +      int ret, val;
 +
 +      spi->bits_per_word = 8;
 +      spi->mode = SPI_MODE_0;
 +      ret = spi_setup(spi);
 +      if (ret < 0)
 +              return ret;
 +
 +      ak4104 = kzalloc(sizeof(struct ak4104_private), GFP_KERNEL);
 +      if (!ak4104) {
 +              dev_err(&spi->dev, "could not allocate codec\n");
 +              return -ENOMEM;
 +      }
 +
 +      codec = &ak4104->codec;
 +      mutex_init(&codec->mutex);
 +      INIT_LIST_HEAD(&codec->dapm_widgets);
 +      INIT_LIST_HEAD(&codec->dapm_paths);
 +
 +      codec->dev = &spi->dev;
 +      codec->name = DRV_NAME;
 +      codec->owner = THIS_MODULE;
 +      codec->dai = &ak4104_dai;
 +      codec->num_dai = 1;
 +      codec->private_data = ak4104;
 +      codec->control_data = spi;
 +      codec->reg_cache = ak4104->reg_cache;
 +      codec->reg_cache_size = AK4104_NUM_REGS;
 +
 +      /* read all regs and fill the cache */
 +      ret = ak4104_fill_cache(codec);
 +      if (ret < 0) {
 +              dev_err(&spi->dev, "failed to fill register cache\n");
 +              return ret;
 +      }
 +
 +      /* read the 'reserved' register - according to the datasheet, it
 +       * should contain 0x5b. Not a good way to verify the presence of
 +       * the device, but there is no hardware ID register. */
 +      if (ak4104_read_reg_cache(codec, AK4104_REG_RESERVED) !=
 +                                       AK4104_RESERVED_VAL) {
 +              ret = -ENODEV;
 +              goto error_free_codec;
 +      }
 +
 +      /* set power-up and non-reset bits */
 +      val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
 +      val |= AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN;
 +      ret = ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
 +      if (ret < 0)
 +              goto error_free_codec;
 +
 +      /* enable transmitter */
 +      val = ak4104_read_reg_cache(codec, AK4104_REG_TX);
 +      val |= AK4104_TX_TXE;
 +      ret = ak4104_spi_write(codec, AK4104_REG_TX, val);
 +      if (ret < 0)
 +              goto error_free_codec;
 +
 +      ak4104_codec = codec;
 +      ret = snd_soc_register_dai(&ak4104_dai);
 +      if (ret < 0) {
 +              dev_err(&spi->dev, "failed to register DAI\n");
 +              goto error_free_codec;
 +      }
 +
 +      spi_set_drvdata(spi, ak4104);
 +      dev_info(&spi->dev, "SPI device initialized\n");
 +      return 0;
 +
 +error_free_codec:
 +      kfree(ak4104);
 +      ak4104_dai.dev = NULL;
 +      return ret;
 +}
 +
 +static int __devexit ak4104_spi_remove(struct spi_device *spi)
 +{
 +      int ret, val;
 +      struct ak4104_private *ak4104 = spi_get_drvdata(spi);
 +
 +      val = ak4104_read_reg_cache(&ak4104->codec, AK4104_REG_CONTROL1);
 +      if (val < 0)
 +              return val;
 +
 +      /* clear power-up and non-reset bits */
 +      val &= ~(AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
 +      ret = ak4104_spi_write(&ak4104->codec, AK4104_REG_CONTROL1, val);
 +      if (ret < 0)
 +              return ret;
 +
 +      ak4104_codec = NULL;
 +      kfree(ak4104);
 +      return 0;
 +}
 +
 +static int ak4104_probe(struct platform_device *pdev)
 +{
 +      struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 +      struct snd_soc_codec *codec = ak4104_codec;
 +      int ret;
 +
 +      /* Connect the codec to the socdev.  snd_soc_new_pcms() needs this. */
 +      socdev->card->codec = codec;
 +
 +      /* Register PCMs */
 +      ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 +      if (ret < 0) {
 +              dev_err(codec->dev, "failed to create pcms\n");
 +              return ret;
 +      }
 +
 +      /* Register the socdev */
 +      ret = snd_soc_init_card(socdev);
 +      if (ret < 0) {
 +              dev_err(codec->dev, "failed to register card\n");
 +              snd_soc_free_pcms(socdev);
 +              return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +static int ak4104_remove(struct platform_device *pdev)
 +{
 +      struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 +      snd_soc_free_pcms(socdev);
 +      return 0;
 +};
 +
 +struct snd_soc_codec_device soc_codec_device_ak4104 = {
 +      .probe =        ak4104_probe,
 +      .remove =       ak4104_remove
 +};
 +EXPORT_SYMBOL_GPL(soc_codec_device_ak4104);
 +
 +static struct spi_driver ak4104_spi_driver = {
 +      .driver  = {
 +              .name   = DRV_NAME,
 +              .owner  = THIS_MODULE,
 +      },
 +      .probe  = ak4104_spi_probe,
 +      .remove = __devexit_p(ak4104_spi_remove),
 +};
 +
 +static int __init ak4104_init(void)
 +{
 +      pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
 +      return spi_register_driver(&ak4104_spi_driver);
 +}
 +module_init(ak4104_init);
 +
 +static void __exit ak4104_exit(void)
 +{
 +      spi_unregister_driver(&ak4104_spi_driver);
 +}
 +module_exit(ak4104_exit);
 +
 +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
 +MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver");
 +MODULE_LICENSE("GPL");
 +
index 0e0c23ee6afc95f274f47a6e36992f94edc5cbe9,7ae3d6520e3fd4bba42cc6daeaa48cbb0ad1e31a..2137670c9b789b076a6b4f6894d8fafcf93ad8bd
   *
   * Current features/limitations:
   *
 - * 1) Software mode is supported.  Stand-alone mode is not supported.
 - * 2) Only I2C is supported, not SPI
 - * 3) Only Master mode is supported, not Slave.
 - * 4) The machine driver's 'startup' function must call
 - *    cs4270_set_dai_sysclk() with the value of MCLK.
 - * 5) Only I2S and left-justified modes are supported
 - * 6) Power management is not supported
 - * 7) The only supported control is volume and hardware mute (if enabled)
 + * - Software mode is supported.  Stand-alone mode is not supported.
 + * - Only I2C is supported, not SPI
 + * - Support for master and slave mode
 + * - The machine driver's 'startup' function must call
 + *   cs4270_set_dai_sysclk() with the value of MCLK.
 + * - Only I2S and left-justified modes are supported
 + * - Power management is not supported
   */
  
  #include <linux/module.h>
@@@ -502,6 -503,13 +502,13 @@@ static const struct snd_kcontrol_new cs
   */
  static struct snd_soc_codec *cs4270_codec;
  
+ static struct snd_soc_dai_ops cs4270_dai_ops = {
+       .hw_params      = cs4270_hw_params,
+       .set_sysclk     = cs4270_set_dai_sysclk,
+       .set_fmt        = cs4270_set_dai_fmt,
+       .digital_mute   = cs4270_mute,
+ };
  struct snd_soc_dai cs4270_dai = {
        .name = "cs4270",
        .playback = {
                .rates = 0,
                .formats = CS4270_FORMATS,
        },
-       .ops = {
-               .hw_params = cs4270_hw_params,
-               .set_sysclk = cs4270_set_dai_sysclk,
-               .set_fmt = cs4270_set_dai_fmt,
-               .digital_mute = cs4270_mute,
-       },
+       .ops = &cs4270_dai_ops,
  };
  EXPORT_SYMBOL_GPL(cs4270_dai);
  
index 1b10f488328c015479c65393071a01ed4f4b85b2,cafa7684c0e73d5ee8ad58be36d5563f88ab359d..5b21594e0e58283ee30d3ee6ea209d8636929aef
@@@ -25,7 -25,6 +25,7 @@@
  #include <linux/ioctl.h>
  #include <linux/delay.h>
  #include <linux/i2c.h>
 +#include <linux/workqueue.h>
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/initval.h>
@@@ -36,8 -35,7 +36,8 @@@
  
  #include "uda1380.h"
  
 -#define UDA1380_VERSION "0.6"
 +static struct work_struct uda1380_work;
 +static struct snd_soc_codec *uda1380_codec;
  
  /*
   * uda1380 register cache
@@@ -54,8 -52,6 +54,8 @@@ static const u16 uda1380_reg[UDA1380_CA
        0x0000, 0x8000, 0x0002, 0x0000,
  };
  
 +static unsigned long uda1380_cache_dirty;
 +
  /*
   * read uda1380 register cache
   */
@@@ -77,11 -73,8 +77,11 @@@ static inline void uda1380_write_reg_ca
        u16 reg, unsigned int value)
  {
        u16 *cache = codec->reg_cache;
 +
        if (reg >= UDA1380_CACHEREGNUM)
                return;
 +      if ((reg >= 0x10) && (cache[reg] != value))
 +              set_bit(reg - 0x10, &uda1380_cache_dirty);
        cache[reg] = value;
  }
  
@@@ -120,8 -113,6 +120,8 @@@ static int uda1380_write(struct snd_soc
                                        (data[0]<<8) | data[1]);
                        return -EIO;
                }
 +              if (reg >= 0x10)
 +                      clear_bit(reg - 0x10, &uda1380_cache_dirty);
                return 0;
        } else
                return -EIO;
  
  #define uda1380_reset(c)      uda1380_write(c, UDA1380_RESET, 0)
  
 +static void uda1380_flush_work(struct work_struct *work)
 +{
 +      int bit, reg;
 +
 +      for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
 +              reg = 0x10 + bit;
 +              pr_debug("uda1380: flush reg %x val %x:\n", reg,
 +                              uda1380_read_reg_cache(uda1380_codec, reg));
 +              uda1380_write(uda1380_codec, reg,
 +                              uda1380_read_reg_cache(uda1380_codec, reg));
 +              clear_bit(bit, &uda1380_cache_dirty);
 +      }
 +}
 +
  /* declarations of ALSA reg_elem_REAL controls */
  static const char *uda1380_deemp[] = {
        "None",
@@@ -277,6 -254,7 +277,6 @@@ static const struct snd_kcontrol_new ud
        SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0),   /* DA_POL_INV */
        SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum),                          /* SEL_NS */
        SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum),             /* MIX_POS, MIX */
 -      SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0),                   /* SILENCE, force DAC output to silence */
        SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0),          /* SDET_ON */
        SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum),                /* SD_VALUE */
        SOC_ENUM("Oversampling Input", uda1380_os_enum),                        /* OS */
@@@ -399,9 -377,8 +399,9 @@@ static int uda1380_set_dai_fmt_both(str
                iface |= R01_SFORI_MSB | R01_SFORO_MSB;
        }
  
 -      if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
 -              iface |= R01_SIM;
 +      /* DATAI is slave only, so in single-link mode, this has to be slave */
 +      if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
 +              return -EINVAL;
  
        uda1380_write(codec, UDA1380_IFACE, iface);
  
@@@ -429,10 -406,6 +429,10 @@@ static int uda1380_set_dai_fmt_playback
                iface |= R01_SFORI_MSB;
        }
  
 +      /* DATAI is slave only, so this has to be slave */
 +      if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
 +              return -EINVAL;
 +
        uda1380_write(codec, UDA1380_IFACE, iface);
  
        return 0;
@@@ -467,28 -440,41 +467,28 @@@ static int uda1380_set_dai_fmt_capture(
        return 0;
  }
  
 -/*
 - * Flush reg cache
 - * We can only write the interpolator and decimator registers
 - * when the DAI is being clocked by the CPU DAI. It's up to the
 - * machine and cpu DAI driver to do this before we are called.
 - */
 -static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
 -                             struct snd_soc_dai *dai)
 +static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
 +              struct snd_soc_dai *dai)
  {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_device *socdev = rtd->socdev;
        struct snd_soc_codec *codec = socdev->card->codec;
 -      int reg, reg_start, reg_end, clk;
 -
 -      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 -              reg_start = UDA1380_MVOL;
 -              reg_end = UDA1380_MIXER;
 -      } else {
 -              reg_start = UDA1380_DEC;
 -              reg_end = UDA1380_AGC;
 -      }
 -
 -      /* FIXME disable DAC_CLK */
 -      clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
 -      uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
 -
 -      for (reg = reg_start; reg <= reg_end; reg++) {
 -              pr_debug("uda1380: flush reg %x val %x:", reg,
 -                              uda1380_read_reg_cache(codec, reg));
 -              uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
 +      int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
 +
 +      switch (cmd) {
 +      case SNDRV_PCM_TRIGGER_START:
 +      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 +              uda1380_write_reg_cache(codec, UDA1380_MIXER,
 +                                      mixer & ~R14_SILENCE);
 +              schedule_work(&uda1380_work);
 +              break;
 +      case SNDRV_PCM_TRIGGER_STOP:
 +      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 +              uda1380_write_reg_cache(codec, UDA1380_MIXER,
 +                                      mixer | R14_SILENCE);
 +              schedule_work(&uda1380_work);
 +              break;
        }
 -
 -      /* FIXME restore DAC_CLK */
 -      uda1380_write(codec, UDA1380_CLK, clk);
 -
        return 0;
  }
  
@@@ -554,6 -540,24 +554,6 @@@ static void uda1380_pcm_shutdown(struc
        uda1380_write(codec, UDA1380_CLK, clk);
  }
  
 -static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
 -{
 -      struct snd_soc_codec *codec = codec_dai->codec;
 -      u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
 -
 -      /* FIXME: mute(codec,0) is called when the magician clock is already
 -       * set to WSPLL, but for some unknown reason writing to interpolator
 -       * registers works only when clocked by SYSCLK */
 -      u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
 -      uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
 -      if (mute)
 -              uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
 -      else
 -              uda1380_write(codec, UDA1380_DEEMP, mute_reg);
 -      uda1380_write(codec, UDA1380_CLK, clk);
 -      return 0;
 -}
 -
  static int uda1380_set_bias_level(struct snd_soc_codec *codec,
        enum snd_soc_bias_level level)
  {
                       SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
  
 -      .prepare        = uda1380_pcm_prepare,
 -      .digital_mute   = uda1380_mute,
+ static struct snd_soc_dai_ops uda1380_dai_ops = {
+       .hw_params      = uda1380_pcm_hw_params,
+       .shutdown       = uda1380_pcm_shutdown,
 -      .prepare        = uda1380_pcm_prepare,
 -      .digital_mute   = uda1380_mute,
++      .trigger        = uda1380_trigger,
+       .set_fmt        = uda1380_set_dai_fmt_both,
+ };
+ static struct snd_soc_dai_ops uda1380_dai_ops_playback = {
+       .hw_params      = uda1380_pcm_hw_params,
+       .shutdown       = uda1380_pcm_shutdown,
 -      .prepare        = uda1380_pcm_prepare,
++      .trigger        = uda1380_trigger,
+       .set_fmt        = uda1380_set_dai_fmt_playback,
+ };
+ static struct snd_soc_dai_ops uda1380_dai_ops_capture = {
+       .hw_params      = uda1380_pcm_hw_params,
+       .shutdown       = uda1380_pcm_shutdown,
++      .trigger        = uda1380_trigger,
+       .set_fmt        = uda1380_set_dai_fmt_capture,
+ };
  struct snd_soc_dai uda1380_dai[] = {
  {
        .name = "UDA1380",
                .channels_max = 2,
                .rates = UDA1380_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .trigger = uda1380_trigger,
-               .hw_params = uda1380_pcm_hw_params,
-               .shutdown = uda1380_pcm_shutdown,
-               .set_fmt = uda1380_set_dai_fmt_both,
-       },
+       .ops = &uda1380_dai_ops,
  },
  { /* playback only - dual interface */
        .name = "UDA1380",
                .rates = UDA1380_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
-       .ops = {
-               .trigger = uda1380_trigger,
-               .hw_params = uda1380_pcm_hw_params,
-               .shutdown = uda1380_pcm_shutdown,
-               .set_fmt = uda1380_set_dai_fmt_playback,
-       },
+       .ops = &uda1380_dai_ops_playback,
  },
  { /* capture only - dual interface*/
        .name = "UDA1380",
                .rates = UDA1380_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
-       .ops = {
-               .trigger = uda1380_trigger,
-               .hw_params = uda1380_pcm_hw_params,
-               .shutdown = uda1380_pcm_shutdown,
-               .set_fmt = uda1380_set_dai_fmt_capture,
-       },
+       .ops = &uda1380_dai_ops_capture,
  },
  };
  EXPORT_SYMBOL_GPL(uda1380_dai);
@@@ -688,9 -700,6 +694,9 @@@ static int uda1380_init(struct snd_soc_
        codec->reg_cache_step = 1;
        uda1380_reset(codec);
  
 +      uda1380_codec = codec;
 +      INIT_WORK(&uda1380_work, uda1380_flush_work);
 +
        /* register pcms */
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
        if (ret < 0) {
@@@ -823,6 -832,8 +829,6 @@@ static int uda1380_probe(struct platfor
        struct snd_soc_codec *codec;
        int ret;
  
 -      pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
 -
        setup = socdev->codec_data;
        codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
        if (codec == NULL)
index 9cb73d9d023d647e433cbf7c7391ae63815347d2,0000000000000000000000000000000000000000..4e1cefff8483c27bc18ae08589c089975900f4df
mode 100644,000000..100644
--- /dev/null
@@@ -1,1479 -1,0 +1,1481 @@@
-       .ops = {
-               .hw_params = wm8400_hw_params,
-               .digital_mute = wm8400_mute,
-               .set_fmt = wm8400_set_dai_fmt,
-               .set_clkdiv = wm8400_set_dai_clkdiv,
-               .set_sysclk = wm8400_set_dai_sysclk,
-       },
 +/*
 + * wm8400.c  --  WM8400 ALSA Soc Audio driver
 + *
 + * Copyright 2008, 2009 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/module.h>
 +#include <linux/moduleparam.h>
 +#include <linux/kernel.h>
 +#include <linux/init.h>
 +#include <linux/delay.h>
 +#include <linux/pm.h>
 +#include <linux/platform_device.h>
 +#include <linux/regulator/consumer.h>
 +#include <linux/mfd/wm8400-audio.h>
 +#include <linux/mfd/wm8400-private.h>
 +#include <sound/core.h>
 +#include <sound/pcm.h>
 +#include <sound/pcm_params.h>
 +#include <sound/soc.h>
 +#include <sound/soc-dapm.h>
 +#include <sound/initval.h>
 +#include <sound/tlv.h>
 +
 +#include "wm8400.h"
 +
 +/* Fake register for internal state */
 +#define WM8400_INTDRIVBITS      (WM8400_REGISTER_COUNT + 1)
 +#define WM8400_INMIXL_PWR                     0
 +#define WM8400_AINLMUX_PWR                    1
 +#define WM8400_INMIXR_PWR                     2
 +#define WM8400_AINRMUX_PWR                    3
 +
 +static struct regulator_bulk_data power[] = {
 +      {
 +              .supply = "I2S1VDD",
 +      },
 +      {
 +              .supply = "I2S2VDD",
 +      },
 +      {
 +              .supply = "DCVDD",
 +      },
 +      {
 +              .supply = "FLLVDD",
 +      },
 +      {
 +              .supply = "HPVDD",
 +      },
 +      {
 +              .supply = "SPKVDD",
 +      },
 +};
 +
 +/* codec private data */
 +struct wm8400_priv {
 +      struct snd_soc_codec codec;
 +      struct wm8400 *wm8400;
 +      u16 fake_register;
 +      unsigned int sysclk;
 +      unsigned int pcmclk;
 +      struct work_struct work;
 +};
 +
 +static inline unsigned int wm8400_read(struct snd_soc_codec *codec,
 +                                     unsigned int reg)
 +{
 +      struct wm8400_priv *wm8400 = codec->private_data;
 +
 +      if (reg == WM8400_INTDRIVBITS)
 +              return wm8400->fake_register;
 +      else
 +              return wm8400_reg_read(wm8400->wm8400, reg);
 +}
 +
 +/*
 + * write to the wm8400 register space
 + */
 +static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg,
 +      unsigned int value)
 +{
 +      struct wm8400_priv *wm8400 = codec->private_data;
 +
 +      if (reg == WM8400_INTDRIVBITS) {
 +              wm8400->fake_register = value;
 +              return 0;
 +      } else
 +              return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value);
 +}
 +
 +static void wm8400_codec_reset(struct snd_soc_codec *codec)
 +{
 +      struct wm8400_priv *wm8400 = codec->private_data;
 +
 +      wm8400_reset_codec_reg_cache(wm8400->wm8400);
 +}
 +
 +static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
 +
 +static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000);
 +
 +static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, -2100, 0);
 +
 +static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600);
 +
 +static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0);
 +
 +static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0);
 +
 +static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763);
 +
 +static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0);
 +
 +static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
 +        struct snd_ctl_elem_value *ucontrol)
 +{
 +        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 +      struct soc_mixer_control *mc =
 +              (struct soc_mixer_control *)kcontrol->private_value;
 +      int reg = mc->reg;
 +        int ret;
 +        u16 val;
 +
 +        ret = snd_soc_put_volsw(kcontrol, ucontrol);
 +        if (ret < 0)
 +                return ret;
 +
 +        /* now hit the volume update bits (always bit 8) */
 +        val = wm8400_read(codec, reg);
 +        return wm8400_write(codec, reg, val | 0x0100);
 +}
 +
 +#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
 +{     .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 +      .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
 +              SNDRV_CTL_ELEM_ACCESS_READWRITE,\
 +      .tlv.p = (tlv_array), \
 +      .info = snd_soc_info_volsw, \
 +      .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \
 +      .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
 +
 +
 +static const char *wm8400_digital_sidetone[] =
 +      {"None", "Left ADC", "Right ADC", "Reserved"};
 +
 +static const struct soc_enum wm8400_left_digital_sidetone_enum =
 +SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE,
 +              WM8400_ADC_TO_DACL_SHIFT, 2, wm8400_digital_sidetone);
 +
 +static const struct soc_enum wm8400_right_digital_sidetone_enum =
 +SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE,
 +              WM8400_ADC_TO_DACR_SHIFT, 2, wm8400_digital_sidetone);
 +
 +static const char *wm8400_adcmode[] =
 +      {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
 +
 +static const struct soc_enum wm8400_right_adcmode_enum =
 +SOC_ENUM_SINGLE(WM8400_ADC_CTRL, WM8400_ADC_HPF_CUT_SHIFT, 3, wm8400_adcmode);
 +
 +static const struct snd_kcontrol_new wm8400_snd_controls[] = {
 +/* INMIXL */
 +SOC_SINGLE("LIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L12MNBST_SHIFT,
 +         1, 0),
 +SOC_SINGLE("LIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_L34MNBST_SHIFT,
 +         1, 0),
 +/* INMIXR */
 +SOC_SINGLE("RIN12 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R12MNBST_SHIFT,
 +         1, 0),
 +SOC_SINGLE("RIN34 PGA Boost", WM8400_INPUT_MIXER3, WM8400_R34MNBST_SHIFT,
 +         1, 0),
 +
 +/* LOMIX */
 +SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER3,
 +      WM8400_LLI3LOVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3,
 +      WM8400_LR12LOVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER3,
 +      WM8400_LL12LOVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER5,
 +      WM8400_LRI3LOVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER5,
 +      WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER5,
 +      WM8400_LRBLOVOL_SHIFT, 7, 0, out_mix_tlv),
 +
 +/* ROMIX */
 +SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8400_OUTPUT_MIXER4,
 +      WM8400_RRI3ROVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4,
 +      WM8400_RL12ROVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8400_OUTPUT_MIXER4,
 +      WM8400_RR12ROVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8400_OUTPUT_MIXER6,
 +      WM8400_RLI3ROVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8400_OUTPUT_MIXER6,
 +      WM8400_RLBROVOL_SHIFT, 7, 0, out_mix_tlv),
 +SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8400_OUTPUT_MIXER6,
 +      WM8400_RRBROVOL_SHIFT, 7, 0, out_mix_tlv),
 +
 +/* LOUT */
 +WM8400_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8400_LEFT_OUTPUT_VOLUME,
 +      WM8400_LOUTVOL_SHIFT, WM8400_LOUTVOL_MASK, 0, out_pga_tlv),
 +SOC_SINGLE("LOUT ZC", WM8400_LEFT_OUTPUT_VOLUME, WM8400_LOZC_SHIFT, 1, 0),
 +
 +/* ROUT */
 +WM8400_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8400_RIGHT_OUTPUT_VOLUME,
 +      WM8400_ROUTVOL_SHIFT, WM8400_ROUTVOL_MASK, 0, out_pga_tlv),
 +SOC_SINGLE("ROUT ZC", WM8400_RIGHT_OUTPUT_VOLUME, WM8400_ROZC_SHIFT, 1, 0),
 +
 +/* LOPGA */
 +WM8400_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8400_LEFT_OPGA_VOLUME,
 +      WM8400_LOPGAVOL_SHIFT, WM8400_LOPGAVOL_MASK, 0, out_pga_tlv),
 +SOC_SINGLE("LOPGA ZC Switch", WM8400_LEFT_OPGA_VOLUME,
 +      WM8400_LOPGAZC_SHIFT, 1, 0),
 +
 +/* ROPGA */
 +WM8400_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8400_RIGHT_OPGA_VOLUME,
 +      WM8400_ROPGAVOL_SHIFT, WM8400_ROPGAVOL_MASK, 0, out_pga_tlv),
 +SOC_SINGLE("ROPGA ZC Switch", WM8400_RIGHT_OPGA_VOLUME,
 +      WM8400_ROPGAZC_SHIFT, 1, 0),
 +
 +SOC_SINGLE("LON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
 +      WM8400_LONMUTE_SHIFT, 1, 0),
 +SOC_SINGLE("LOP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
 +      WM8400_LOPMUTE_SHIFT, 1, 0),
 +SOC_SINGLE("LOP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME,
 +      WM8400_LOATTN_SHIFT, 1, 0),
 +SOC_SINGLE("RON Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
 +      WM8400_RONMUTE_SHIFT, 1, 0),
 +SOC_SINGLE("ROP Mute Switch", WM8400_LINE_OUTPUTS_VOLUME,
 +      WM8400_ROPMUTE_SHIFT, 1, 0),
 +SOC_SINGLE("ROP Attenuation Switch", WM8400_LINE_OUTPUTS_VOLUME,
 +      WM8400_ROATTN_SHIFT, 1, 0),
 +
 +SOC_SINGLE("OUT3 Mute Switch", WM8400_OUT3_4_VOLUME,
 +      WM8400_OUT3MUTE_SHIFT, 1, 0),
 +SOC_SINGLE("OUT3 Attenuation Switch", WM8400_OUT3_4_VOLUME,
 +      WM8400_OUT3ATTN_SHIFT, 1, 0),
 +
 +SOC_SINGLE("OUT4 Mute Switch", WM8400_OUT3_4_VOLUME,
 +      WM8400_OUT4MUTE_SHIFT, 1, 0),
 +SOC_SINGLE("OUT4 Attenuation Switch", WM8400_OUT3_4_VOLUME,
 +      WM8400_OUT4ATTN_SHIFT, 1, 0),
 +
 +SOC_SINGLE("Speaker Mode Switch", WM8400_CLASSD1,
 +      WM8400_CDMODE_SHIFT, 1, 0),
 +
 +SOC_SINGLE("Speaker Output Attenuation Volume", WM8400_SPEAKER_VOLUME,
 +      WM8400_SPKATTN_SHIFT, WM8400_SPKATTN_MASK, 0),
 +SOC_SINGLE("Speaker DC Boost Volume", WM8400_CLASSD3,
 +      WM8400_DCGAIN_SHIFT, 6, 0),
 +SOC_SINGLE("Speaker AC Boost Volume", WM8400_CLASSD3,
 +      WM8400_ACGAIN_SHIFT, 6, 0),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
 +      WM8400_LEFT_DAC_DIGITAL_VOLUME, WM8400_DACL_VOL_SHIFT,
 +      127, 0, out_dac_tlv),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
 +      WM8400_RIGHT_DAC_DIGITAL_VOLUME, WM8400_DACR_VOL_SHIFT,
 +      127, 0, out_dac_tlv),
 +
 +SOC_ENUM("Left Digital Sidetone", wm8400_left_digital_sidetone_enum),
 +SOC_ENUM("Right Digital Sidetone", wm8400_right_digital_sidetone_enum),
 +
 +SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE,
 +      WM8400_ADCL_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv),
 +SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8400_DIGITAL_SIDE_TONE,
 +      WM8400_ADCR_DAC_SVOL_SHIFT, 15, 0, out_sidetone_tlv),
 +
 +SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8400_ADC_CTRL,
 +      WM8400_ADC_HPF_ENA_SHIFT, 1, 0),
 +
 +SOC_ENUM("ADC HPF Mode", wm8400_right_adcmode_enum),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
 +      WM8400_LEFT_ADC_DIGITAL_VOLUME,
 +      WM8400_ADCL_VOL_SHIFT,
 +      WM8400_ADCL_VOL_MASK,
 +      0,
 +      in_adc_tlv),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
 +      WM8400_RIGHT_ADC_DIGITAL_VOLUME,
 +      WM8400_ADCR_VOL_SHIFT,
 +      WM8400_ADCR_VOL_MASK,
 +      0,
 +      in_adc_tlv),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
 +      WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
 +      WM8400_LIN12VOL_SHIFT,
 +      WM8400_LIN12VOL_MASK,
 +      0,
 +      in_pga_tlv),
 +
 +SOC_SINGLE("LIN12 ZC Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
 +      WM8400_LI12ZC_SHIFT, 1, 0),
 +
 +SOC_SINGLE("LIN12 Mute Switch", WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
 +      WM8400_LI12MUTE_SHIFT, 1, 0),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
 +      WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
 +      WM8400_LIN34VOL_SHIFT,
 +      WM8400_LIN34VOL_MASK,
 +      0,
 +      in_pga_tlv),
 +
 +SOC_SINGLE("LIN34 ZC Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
 +      WM8400_LI34ZC_SHIFT, 1, 0),
 +
 +SOC_SINGLE("LIN34 Mute Switch", WM8400_LEFT_LINE_INPUT_3_4_VOLUME,
 +      WM8400_LI34MUTE_SHIFT, 1, 0),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
 +      WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
 +      WM8400_RIN12VOL_SHIFT,
 +      WM8400_RIN12VOL_MASK,
 +      0,
 +      in_pga_tlv),
 +
 +SOC_SINGLE("RIN12 ZC Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
 +      WM8400_RI12ZC_SHIFT, 1, 0),
 +
 +SOC_SINGLE("RIN12 Mute Switch", WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
 +      WM8400_RI12MUTE_SHIFT, 1, 0),
 +
 +WM8400_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
 +      WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
 +      WM8400_RIN34VOL_SHIFT,
 +      WM8400_RIN34VOL_MASK,
 +      0,
 +      in_pga_tlv),
 +
 +SOC_SINGLE("RIN34 ZC Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
 +      WM8400_RI34ZC_SHIFT, 1, 0),
 +
 +SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME,
 +      WM8400_RI34MUTE_SHIFT, 1, 0),
 +
 +};
 +
 +/* add non dapm controls */
 +static int wm8400_add_controls(struct snd_soc_codec *codec)
 +{
 +      int err, i;
 +
 +      for (i = 0; i < ARRAY_SIZE(wm8400_snd_controls); i++) {
 +              err = snd_ctl_add(codec->card,
 +                              snd_soc_cnew(&wm8400_snd_controls[i],codec,
 +                                      NULL));
 +              if (err < 0)
 +                      return err;
 +      }
 +      return 0;
 +}
 +
 +/*
 + * _DAPM_ Controls
 + */
 +
 +static int inmixer_event (struct snd_soc_dapm_widget *w,
 +      struct snd_kcontrol *kcontrol, int event)
 +{
 +      u16 reg, fakepower;
 +
 +      reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2);
 +      fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS);
 +
 +      if (fakepower & ((1 << WM8400_INMIXL_PWR) |
 +              (1 << WM8400_AINLMUX_PWR))) {
 +              reg |= WM8400_AINL_ENA;
 +      } else {
 +              reg &= ~WM8400_AINL_ENA;
 +      }
 +
 +      if (fakepower & ((1 << WM8400_INMIXR_PWR) |
 +              (1 << WM8400_AINRMUX_PWR))) {
 +              reg |= WM8400_AINR_ENA;
 +      } else {
 +              reg &= ~WM8400_AINL_ENA;
 +      }
 +      wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg);
 +
 +      return 0;
 +}
 +
 +static int outmixer_event (struct snd_soc_dapm_widget *w,
 +      struct snd_kcontrol * kcontrol, int event)
 +{
 +      struct soc_mixer_control *mc =
 +              (struct soc_mixer_control *)kcontrol->private_value;
 +      u32 reg_shift = mc->shift;
 +      int ret = 0;
 +      u16 reg;
 +
 +      switch (reg_shift) {
 +      case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) :
 +              reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1);
 +              if (reg & WM8400_LDLO) {
 +                      printk(KERN_WARNING
 +                      "Cannot set as Output Mixer 1 LDLO Set\n");
 +                      ret = -1;
 +              }
 +              break;
 +      case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8):
 +              reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2);
 +              if (reg & WM8400_RDRO) {
 +                      printk(KERN_WARNING
 +                      "Cannot set as Output Mixer 2 RDRO Set\n");
 +                      ret = -1;
 +              }
 +              break;
 +      case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8):
 +              reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
 +              if (reg & WM8400_LDSPK) {
 +                      printk(KERN_WARNING
 +                      "Cannot set as Speaker Mixer LDSPK Set\n");
 +                      ret = -1;
 +              }
 +              break;
 +      case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8):
 +              reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER);
 +              if (reg & WM8400_RDSPK) {
 +                      printk(KERN_WARNING
 +                      "Cannot set as Speaker Mixer RDSPK Set\n");
 +                      ret = -1;
 +              }
 +              break;
 +      }
 +
 +      return ret;
 +}
 +
 +/* INMIX dB values */
 +static const unsigned int in_mix_tlv[] = {
 +      TLV_DB_RANGE_HEAD(1),
 +      0,7, TLV_DB_LINEAR_ITEM(-1200, 600),
 +};
 +
 +/* Left In PGA Connections */
 +static const struct snd_kcontrol_new wm8400_dapm_lin12_pga_controls[] = {
 +SOC_DAPM_SINGLE("LIN1 Switch", WM8400_INPUT_MIXER2, WM8400_LMN1_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LIN2 Switch", WM8400_INPUT_MIXER2, WM8400_LMP2_SHIFT, 1, 0),
 +};
 +
 +static const struct snd_kcontrol_new wm8400_dapm_lin34_pga_controls[] = {
 +SOC_DAPM_SINGLE("LIN3 Switch", WM8400_INPUT_MIXER2, WM8400_LMN3_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LIN4 Switch", WM8400_INPUT_MIXER2, WM8400_LMP4_SHIFT, 1, 0),
 +};
 +
 +/* Right In PGA Connections */
 +static const struct snd_kcontrol_new wm8400_dapm_rin12_pga_controls[] = {
 +SOC_DAPM_SINGLE("RIN1 Switch", WM8400_INPUT_MIXER2, WM8400_RMN1_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("RIN2 Switch", WM8400_INPUT_MIXER2, WM8400_RMP2_SHIFT, 1, 0),
 +};
 +
 +static const struct snd_kcontrol_new wm8400_dapm_rin34_pga_controls[] = {
 +SOC_DAPM_SINGLE("RIN3 Switch", WM8400_INPUT_MIXER2, WM8400_RMN3_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("RIN4 Switch", WM8400_INPUT_MIXER2, WM8400_RMP4_SHIFT, 1, 0),
 +};
 +
 +/* INMIXL */
 +static const struct snd_kcontrol_new wm8400_dapm_inmixl_controls[] = {
 +SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8400_INPUT_MIXER3,
 +      WM8400_LDBVOL_SHIFT, WM8400_LDBVOL_MASK, 0, in_mix_tlv),
 +SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8400_INPUT_MIXER5, WM8400_LI2BVOL_SHIFT,
 +      7, 0, in_mix_tlv),
 +SOC_DAPM_SINGLE("LINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT,
 +              1, 0),
 +SOC_DAPM_SINGLE("LINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT,
 +              1, 0),
 +};
 +
 +/* INMIXR */
 +static const struct snd_kcontrol_new wm8400_dapm_inmixr_controls[] = {
 +SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8400_INPUT_MIXER4,
 +      WM8400_RDBVOL_SHIFT, WM8400_RDBVOL_MASK, 0, in_mix_tlv),
 +SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8400_INPUT_MIXER6, WM8400_RI2BVOL_SHIFT,
 +      7, 0, in_mix_tlv),
 +SOC_DAPM_SINGLE("RINPGA12 Switch", WM8400_INPUT_MIXER3, WM8400_L12MNB_SHIFT,
 +      1, 0),
 +SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT,
 +      1, 0),
 +};
 +
 +/* AINLMUX */
 +static const char *wm8400_ainlmux[] =
 +      {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
 +
 +static const struct soc_enum wm8400_ainlmux_enum =
 +SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINLMODE_SHIFT,
 +      ARRAY_SIZE(wm8400_ainlmux), wm8400_ainlmux);
 +
 +static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls =
 +SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum);
 +
 +/* DIFFINL */
 +
 +/* AINRMUX */
 +static const char *wm8400_ainrmux[] =
 +      {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
 +
 +static const struct soc_enum wm8400_ainrmux_enum =
 +SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINRMODE_SHIFT,
 +      ARRAY_SIZE(wm8400_ainrmux), wm8400_ainrmux);
 +
 +static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls =
 +SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum);
 +
 +/* RXVOICE */
 +static const struct snd_kcontrol_new wm8400_dapm_rxvoice_controls[] = {
 +SOC_DAPM_SINGLE_TLV("LIN4/RXN", WM8400_INPUT_MIXER5, WM8400_LR4BVOL_SHIFT,
 +                      WM8400_LR4BVOL_MASK, 0, in_mix_tlv),
 +SOC_DAPM_SINGLE_TLV("RIN4/RXP", WM8400_INPUT_MIXER6, WM8400_RL4BVOL_SHIFT,
 +                      WM8400_RL4BVOL_MASK, 0, in_mix_tlv),
 +};
 +
 +/* LOMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_lomix_controls[] = {
 +SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LRBLO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LLBLO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LRI3LO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LLI3LO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LR12LO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LL12LO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8400_OUTPUT_MIXER1,
 +      WM8400_LDLO_SHIFT, 1, 0),
 +};
 +
 +/* ROMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_romix_controls[] = {
 +SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RLBRO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RRBRO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RLI3RO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RRI3RO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RL12RO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RR12RO_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8400_OUTPUT_MIXER2,
 +      WM8400_RDRO_SHIFT, 1, 0),
 +};
 +
 +/* LONMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_lonmix_controls[] = {
 +SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1,
 +      WM8400_LLOPGALON_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER1,
 +      WM8400_LROPGALON_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8400_LINE_MIXER1,
 +      WM8400_LOPLON_SHIFT, 1, 0),
 +};
 +
 +/* LOPMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_lopmix_controls[] = {
 +SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER1,
 +      WM8400_LR12LOP_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER1,
 +      WM8400_LL12LOP_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8400_LINE_MIXER1,
 +      WM8400_LLOPGALOP_SHIFT, 1, 0),
 +};
 +
 +/* RONMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_ronmix_controls[] = {
 +SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2,
 +      WM8400_RROPGARON_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8400_LINE_MIXER2,
 +      WM8400_RLOPGARON_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8400_LINE_MIXER2,
 +      WM8400_ROPRON_SHIFT, 1, 0),
 +};
 +
 +/* ROPMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_ropmix_controls[] = {
 +SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8400_LINE_MIXER2,
 +      WM8400_RL12ROP_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8400_LINE_MIXER2,
 +      WM8400_RR12ROP_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8400_LINE_MIXER2,
 +      WM8400_RROPGAROP_SHIFT, 1, 0),
 +};
 +
 +/* OUT3MIX */
 +static const struct snd_kcontrol_new wm8400_dapm_out3mix_controls[] = {
 +SOC_DAPM_SINGLE("OUT3MIX LIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER,
 +      WM8400_LI4O3_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8400_OUT3_4_MIXER,
 +      WM8400_LPGAO3_SHIFT, 1, 0),
 +};
 +
 +/* OUT4MIX */
 +static const struct snd_kcontrol_new wm8400_dapm_out4mix_controls[] = {
 +SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8400_OUT3_4_MIXER,
 +      WM8400_RPGAO4_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("OUT4MIX RIN4/RXP Bypass Switch", WM8400_OUT3_4_MIXER,
 +      WM8400_RI4O4_SHIFT, 1, 0),
 +};
 +
 +/* SPKMIX */
 +static const struct snd_kcontrol_new wm8400_dapm_spkmix_controls[] = {
 +SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_LI2SPK_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_LB2SPK_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_LOPGASPK_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_LDSPK_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_RDSPK_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_ROPGASPK_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_RL12ROP_SHIFT, 1, 0),
 +SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8400_SPEAKER_MIXER,
 +      WM8400_RI2SPK_SHIFT, 1, 0),
 +};
 +
 +static const struct snd_soc_dapm_widget wm8400_dapm_widgets[] = {
 +/* Input Side */
 +/* Input Lines */
 +SND_SOC_DAPM_INPUT("LIN1"),
 +SND_SOC_DAPM_INPUT("LIN2"),
 +SND_SOC_DAPM_INPUT("LIN3"),
 +SND_SOC_DAPM_INPUT("LIN4/RXN"),
 +SND_SOC_DAPM_INPUT("RIN3"),
 +SND_SOC_DAPM_INPUT("RIN4/RXP"),
 +SND_SOC_DAPM_INPUT("RIN1"),
 +SND_SOC_DAPM_INPUT("RIN2"),
 +SND_SOC_DAPM_INPUT("Internal ADC Source"),
 +
 +/* DACs */
 +SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8400_POWER_MANAGEMENT_2,
 +      WM8400_ADCL_ENA_SHIFT, 0),
 +SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8400_POWER_MANAGEMENT_2,
 +      WM8400_ADCR_ENA_SHIFT, 0),
 +
 +/* Input PGAs */
 +SND_SOC_DAPM_MIXER("LIN12 PGA", WM8400_POWER_MANAGEMENT_2,
 +                 WM8400_LIN12_ENA_SHIFT,
 +                 0, &wm8400_dapm_lin12_pga_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_lin12_pga_controls)),
 +SND_SOC_DAPM_MIXER("LIN34 PGA", WM8400_POWER_MANAGEMENT_2,
 +                 WM8400_LIN34_ENA_SHIFT,
 +                 0, &wm8400_dapm_lin34_pga_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_lin34_pga_controls)),
 +SND_SOC_DAPM_MIXER("RIN12 PGA", WM8400_POWER_MANAGEMENT_2,
 +                 WM8400_RIN12_ENA_SHIFT,
 +                 0, &wm8400_dapm_rin12_pga_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_rin12_pga_controls)),
 +SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2,
 +                 WM8400_RIN34_ENA_SHIFT,
 +                 0, &wm8400_dapm_rin34_pga_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)),
 +
 +/* INMIXL */
 +SND_SOC_DAPM_MIXER_E("INMIXL", WM8400_INTDRIVBITS, WM8400_INMIXL_PWR, 0,
 +      &wm8400_dapm_inmixl_controls[0],
 +      ARRAY_SIZE(wm8400_dapm_inmixl_controls),
 +      inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 +
 +/* AINLMUX */
 +SND_SOC_DAPM_MUX_E("AILNMUX", WM8400_INTDRIVBITS, WM8400_AINLMUX_PWR, 0,
 +      &wm8400_dapm_ainlmux_controls, inmixer_event,
 +      SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 +
 +/* INMIXR */
 +SND_SOC_DAPM_MIXER_E("INMIXR", WM8400_INTDRIVBITS, WM8400_INMIXR_PWR, 0,
 +      &wm8400_dapm_inmixr_controls[0],
 +      ARRAY_SIZE(wm8400_dapm_inmixr_controls),
 +      inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 +
 +/* AINRMUX */
 +SND_SOC_DAPM_MUX_E("AIRNMUX", WM8400_INTDRIVBITS, WM8400_AINRMUX_PWR, 0,
 +      &wm8400_dapm_ainrmux_controls, inmixer_event,
 +      SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 +
 +/* Output Side */
 +/* DACs */
 +SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8400_POWER_MANAGEMENT_3,
 +      WM8400_DACL_ENA_SHIFT, 0),
 +SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8400_POWER_MANAGEMENT_3,
 +      WM8400_DACR_ENA_SHIFT, 0),
 +
 +/* LOMIX */
 +SND_SOC_DAPM_MIXER_E("LOMIX", WM8400_POWER_MANAGEMENT_3,
 +                   WM8400_LOMIX_ENA_SHIFT,
 +                   0, &wm8400_dapm_lomix_controls[0],
 +                   ARRAY_SIZE(wm8400_dapm_lomix_controls),
 +                   outmixer_event, SND_SOC_DAPM_PRE_REG),
 +
 +/* LONMIX */
 +SND_SOC_DAPM_MIXER("LONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LON_ENA_SHIFT,
 +                 0, &wm8400_dapm_lonmix_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_lonmix_controls)),
 +
 +/* LOPMIX */
 +SND_SOC_DAPM_MIXER("LOPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_LOP_ENA_SHIFT,
 +                 0, &wm8400_dapm_lopmix_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_lopmix_controls)),
 +
 +/* OUT3MIX */
 +SND_SOC_DAPM_MIXER("OUT3MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT3_ENA_SHIFT,
 +                 0, &wm8400_dapm_out3mix_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_out3mix_controls)),
 +
 +/* SPKMIX */
 +SND_SOC_DAPM_MIXER_E("SPKMIX", WM8400_POWER_MANAGEMENT_1, WM8400_SPK_ENA_SHIFT,
 +                   0, &wm8400_dapm_spkmix_controls[0],
 +                   ARRAY_SIZE(wm8400_dapm_spkmix_controls), outmixer_event,
 +                   SND_SOC_DAPM_PRE_REG),
 +
 +/* OUT4MIX */
 +SND_SOC_DAPM_MIXER("OUT4MIX", WM8400_POWER_MANAGEMENT_1, WM8400_OUT4_ENA_SHIFT,
 +      0, &wm8400_dapm_out4mix_controls[0],
 +      ARRAY_SIZE(wm8400_dapm_out4mix_controls)),
 +
 +/* ROPMIX */
 +SND_SOC_DAPM_MIXER("ROPMIX", WM8400_POWER_MANAGEMENT_3, WM8400_ROP_ENA_SHIFT,
 +                 0, &wm8400_dapm_ropmix_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_ropmix_controls)),
 +
 +/* RONMIX */
 +SND_SOC_DAPM_MIXER("RONMIX", WM8400_POWER_MANAGEMENT_3, WM8400_RON_ENA_SHIFT,
 +                 0, &wm8400_dapm_ronmix_controls[0],
 +                 ARRAY_SIZE(wm8400_dapm_ronmix_controls)),
 +
 +/* ROMIX */
 +SND_SOC_DAPM_MIXER_E("ROMIX", WM8400_POWER_MANAGEMENT_3,
 +                   WM8400_ROMIX_ENA_SHIFT,
 +                   0, &wm8400_dapm_romix_controls[0],
 +                   ARRAY_SIZE(wm8400_dapm_romix_controls),
 +                   outmixer_event, SND_SOC_DAPM_PRE_REG),
 +
 +/* LOUT PGA */
 +SND_SOC_DAPM_PGA("LOUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_LOUT_ENA_SHIFT,
 +               0, NULL, 0),
 +
 +/* ROUT PGA */
 +SND_SOC_DAPM_PGA("ROUT PGA", WM8400_POWER_MANAGEMENT_1, WM8400_ROUT_ENA_SHIFT,
 +               0, NULL, 0),
 +
 +/* LOPGA */
 +SND_SOC_DAPM_PGA("LOPGA", WM8400_POWER_MANAGEMENT_3, WM8400_LOPGA_ENA_SHIFT, 0,
 +      NULL, 0),
 +
 +/* ROPGA */
 +SND_SOC_DAPM_PGA("ROPGA", WM8400_POWER_MANAGEMENT_3, WM8400_ROPGA_ENA_SHIFT, 0,
 +      NULL, 0),
 +
 +/* MICBIAS */
 +SND_SOC_DAPM_MICBIAS("MICBIAS", WM8400_POWER_MANAGEMENT_1,
 +      WM8400_MIC1BIAS_ENA_SHIFT, 0),
 +
 +SND_SOC_DAPM_OUTPUT("LON"),
 +SND_SOC_DAPM_OUTPUT("LOP"),
 +SND_SOC_DAPM_OUTPUT("OUT3"),
 +SND_SOC_DAPM_OUTPUT("LOUT"),
 +SND_SOC_DAPM_OUTPUT("SPKN"),
 +SND_SOC_DAPM_OUTPUT("SPKP"),
 +SND_SOC_DAPM_OUTPUT("ROUT"),
 +SND_SOC_DAPM_OUTPUT("OUT4"),
 +SND_SOC_DAPM_OUTPUT("ROP"),
 +SND_SOC_DAPM_OUTPUT("RON"),
 +
 +SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
 +};
 +
 +static const struct snd_soc_dapm_route audio_map[] = {
 +      /* Make DACs turn on when playing even if not mixed into any outputs */
 +      {"Internal DAC Sink", NULL, "Left DAC"},
 +      {"Internal DAC Sink", NULL, "Right DAC"},
 +
 +      /* Make ADCs turn on when recording
 +       * even if not mixed from any inputs */
 +      {"Left ADC", NULL, "Internal ADC Source"},
 +      {"Right ADC", NULL, "Internal ADC Source"},
 +
 +      /* Input Side */
 +      /* LIN12 PGA */
 +      {"LIN12 PGA", "LIN1 Switch", "LIN1"},
 +      {"LIN12 PGA", "LIN2 Switch", "LIN2"},
 +      /* LIN34 PGA */
 +      {"LIN34 PGA", "LIN3 Switch", "LIN3"},
 +      {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"},
 +      /* INMIXL */
 +      {"INMIXL", "Record Left Volume", "LOMIX"},
 +      {"INMIXL", "LIN2 Volume", "LIN2"},
 +      {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
 +      {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
 +      /* AILNMUX */
 +      {"AILNMUX", "INMIXL Mix", "INMIXL"},
 +      {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"},
 +      {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"},
 +      {"AILNMUX", "RXVOICE Mix", "LIN4/RXN"},
 +      {"AILNMUX", "RXVOICE Mix", "RIN4/RXP"},
 +      /* ADC */
 +      {"Left ADC", NULL, "AILNMUX"},
 +
 +      /* RIN12 PGA */
 +      {"RIN12 PGA", "RIN1 Switch", "RIN1"},
 +      {"RIN12 PGA", "RIN2 Switch", "RIN2"},
 +      /* RIN34 PGA */
 +      {"RIN34 PGA", "RIN3 Switch", "RIN3"},
 +      {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"},
 +      /* INMIXL */
 +      {"INMIXR", "Record Right Volume", "ROMIX"},
 +      {"INMIXR", "RIN2 Volume", "RIN2"},
 +      {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
 +      {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
 +      /* AIRNMUX */
 +      {"AIRNMUX", "INMIXR Mix", "INMIXR"},
 +      {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"},
 +      {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"},
 +      {"AIRNMUX", "RXVOICE Mix", "LIN4/RXN"},
 +      {"AIRNMUX", "RXVOICE Mix", "RIN4/RXP"},
 +      /* ADC */
 +      {"Right ADC", NULL, "AIRNMUX"},
 +
 +      /* LOMIX */
 +      {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
 +      {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
 +      {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
 +      {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
 +      {"LOMIX", "LOMIX Right ADC Bypass Switch", "AIRNMUX"},
 +      {"LOMIX", "LOMIX Left ADC Bypass Switch", "AILNMUX"},
 +      {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
 +
 +      /* ROMIX */
 +      {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
 +      {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
 +      {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
 +      {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
 +      {"ROMIX", "ROMIX Right ADC Bypass Switch", "AIRNMUX"},
 +      {"ROMIX", "ROMIX Left ADC Bypass Switch", "AILNMUX"},
 +      {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
 +
 +      /* SPKMIX */
 +      {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
 +      {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
 +      {"SPKMIX", "SPKMIX LADC Bypass Switch", "AILNMUX"},
 +      {"SPKMIX", "SPKMIX RADC Bypass Switch", "AIRNMUX"},
 +      {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
 +      {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
 +      {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
 +      {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
 +
 +      /* LONMIX */
 +      {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
 +      {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
 +      {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
 +
 +      /* LOPMIX */
 +      {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
 +      {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
 +      {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
 +
 +      /* OUT3MIX */
 +      {"OUT3MIX", "OUT3MIX LIN4/RXP Bypass Switch", "LIN4/RXN"},
 +      {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
 +
 +      /* OUT4MIX */
 +      {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
 +      {"OUT4MIX", "OUT4MIX RIN4/RXP Bypass Switch", "RIN4/RXP"},
 +
 +      /* RONMIX */
 +      {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
 +      {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
 +      {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
 +
 +      /* ROPMIX */
 +      {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
 +      {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
 +      {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
 +
 +      /* Out Mixer PGAs */
 +      {"LOPGA", NULL, "LOMIX"},
 +      {"ROPGA", NULL, "ROMIX"},
 +
 +      {"LOUT PGA", NULL, "LOMIX"},
 +      {"ROUT PGA", NULL, "ROMIX"},
 +
 +      /* Output Pins */
 +      {"LON", NULL, "LONMIX"},
 +      {"LOP", NULL, "LOPMIX"},
 +      {"OUT3", NULL, "OUT3MIX"},
 +      {"LOUT", NULL, "LOUT PGA"},
 +      {"SPKN", NULL, "SPKMIX"},
 +      {"ROUT", NULL, "ROUT PGA"},
 +      {"OUT4", NULL, "OUT4MIX"},
 +      {"ROP", NULL, "ROPMIX"},
 +      {"RON", NULL, "RONMIX"},
 +};
 +
 +static int wm8400_add_widgets(struct snd_soc_codec *codec)
 +{
 +      snd_soc_dapm_new_controls(codec, wm8400_dapm_widgets,
 +                                ARRAY_SIZE(wm8400_dapm_widgets));
 +
 +      snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 +
 +      snd_soc_dapm_new_widgets(codec);
 +      return 0;
 +}
 +
 +/*
 + * Clock after FLL and dividers
 + */
 +static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 +              int clk_id, unsigned int freq, int dir)
 +{
 +      struct snd_soc_codec *codec = codec_dai->codec;
 +      struct wm8400_priv *wm8400 = codec->private_data;
 +
 +      wm8400->sysclk = freq;
 +      return 0;
 +}
 +
 +/*
 + * Sets ADC and Voice DAC format.
 + */
 +static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai,
 +              unsigned int fmt)
 +{
 +      struct snd_soc_codec *codec = codec_dai->codec;
 +      u16 audio1, audio3;
 +
 +      audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
 +      audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3);
 +
 +      /* set master/slave audio interface */
 +      switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 +      case SND_SOC_DAIFMT_CBS_CFS:
 +              audio3 &= ~WM8400_AIF_MSTR1;
 +              break;
 +      case SND_SOC_DAIFMT_CBM_CFM:
 +              audio3 |= WM8400_AIF_MSTR1;
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      audio1 &= ~WM8400_AIF_FMT_MASK;
 +
 +      /* interface format */
 +      switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 +      case SND_SOC_DAIFMT_I2S:
 +              audio1 |= WM8400_AIF_FMT_I2S;
 +              audio1 &= ~WM8400_AIF_LRCLK_INV;
 +              break;
 +      case SND_SOC_DAIFMT_RIGHT_J:
 +              audio1 |= WM8400_AIF_FMT_RIGHTJ;
 +              audio1 &= ~WM8400_AIF_LRCLK_INV;
 +              break;
 +      case SND_SOC_DAIFMT_LEFT_J:
 +              audio1 |= WM8400_AIF_FMT_LEFTJ;
 +              audio1 &= ~WM8400_AIF_LRCLK_INV;
 +              break;
 +      case SND_SOC_DAIFMT_DSP_A:
 +              audio1 |= WM8400_AIF_FMT_DSP;
 +              audio1 &= ~WM8400_AIF_LRCLK_INV;
 +              break;
 +      case SND_SOC_DAIFMT_DSP_B:
 +              audio1 |= WM8400_AIF_FMT_DSP | WM8400_AIF_LRCLK_INV;
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
 +      wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3);
 +      return 0;
 +}
 +
 +static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 +              int div_id, int div)
 +{
 +      struct snd_soc_codec *codec = codec_dai->codec;
 +      u16 reg;
 +
 +      switch (div_id) {
 +      case WM8400_MCLK_DIV:
 +              reg = wm8400_read(codec, WM8400_CLOCKING_2) &
 +                      ~WM8400_MCLK_DIV_MASK;
 +              wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
 +              break;
 +      case WM8400_DACCLK_DIV:
 +              reg = wm8400_read(codec, WM8400_CLOCKING_2) &
 +                      ~WM8400_DAC_CLKDIV_MASK;
 +              wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
 +              break;
 +      case WM8400_ADCCLK_DIV:
 +              reg = wm8400_read(codec, WM8400_CLOCKING_2) &
 +                      ~WM8400_ADC_CLKDIV_MASK;
 +              wm8400_write(codec, WM8400_CLOCKING_2, reg | div);
 +              break;
 +      case WM8400_BCLK_DIV:
 +              reg = wm8400_read(codec, WM8400_CLOCKING_1) &
 +                      ~WM8400_BCLK_DIV_MASK;
 +              wm8400_write(codec, WM8400_CLOCKING_1, reg | div);
 +              break;
 +      default:
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Set PCM DAI bit size and sample rate.
 + */
 +static int wm8400_hw_params(struct snd_pcm_substream *substream,
 +      struct snd_pcm_hw_params *params,
 +      struct snd_soc_dai *dai)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct snd_soc_device *socdev = rtd->socdev;
 +      struct snd_soc_codec *codec = socdev->card->codec;
 +      u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1);
 +
 +      audio1 &= ~WM8400_AIF_WL_MASK;
 +      /* bit size */
 +      switch (params_format(params)) {
 +      case SNDRV_PCM_FORMAT_S16_LE:
 +              break;
 +      case SNDRV_PCM_FORMAT_S20_3LE:
 +              audio1 |= WM8400_AIF_WL_20BITS;
 +              break;
 +      case SNDRV_PCM_FORMAT_S24_LE:
 +              audio1 |= WM8400_AIF_WL_24BITS;
 +              break;
 +      case SNDRV_PCM_FORMAT_S32_LE:
 +              audio1 |= WM8400_AIF_WL_32BITS;
 +              break;
 +      }
 +
 +      wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1);
 +      return 0;
 +}
 +
 +static int wm8400_mute(struct snd_soc_dai *dai, int mute)
 +{
 +      struct snd_soc_codec *codec = dai->codec;
 +      u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE;
 +
 +      if (mute)
 +              wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
 +      else
 +              wm8400_write(codec, WM8400_DAC_CTRL, val);
 +
 +      return 0;
 +}
 +
 +/* TODO: set bias for best performance at standby */
 +static int wm8400_set_bias_level(struct snd_soc_codec *codec,
 +                               enum snd_soc_bias_level level)
 +{
 +      struct wm8400_priv *wm8400 = codec->private_data;
 +      u16 val;
 +      int ret;
 +
 +      switch (level) {
 +      case SND_SOC_BIAS_ON:
 +              break;
 +
 +      case SND_SOC_BIAS_PREPARE:
 +              /* VMID=2*50k */
 +              val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
 +                      ~WM8400_VMID_MODE_MASK;
 +              wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2);
 +              break;
 +
 +      case SND_SOC_BIAS_STANDBY:
 +              if (codec->bias_level == SND_SOC_BIAS_OFF) {
 +                      ret = regulator_bulk_enable(ARRAY_SIZE(power),
 +                                                  &power[0]);
 +                      if (ret != 0) {
 +                              dev_err(wm8400->wm8400->dev,
 +                                      "Failed to enable regulators: %d\n",
 +                                      ret);
 +                              return ret;
 +                      }
 +
 +                      wm8400_write(codec, WM8400_POWER_MANAGEMENT_1,
 +                                   WM8400_CODEC_ENA | WM8400_SYSCLK_ENA);
 +
 +                      /* Enable all output discharge bits */
 +                      wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
 +                              WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
 +                              WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
 +                              WM8400_DIS_ROUT);
 +
 +                      /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
 +                      wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 +                                   WM8400_BUFDCOPEN | WM8400_POBCTRL);
 +
 +                      msleep(500);
 +
 +                      /* Enable outputs */
 +                      val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
 +                      val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
 +                              WM8400_OUT4_ENA | WM8400_LOUT_ENA |
 +                              WM8400_ROUT_ENA;
 +                      wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 +
 +                      /* disable all output discharge bits */
 +                      wm8400_write(codec, WM8400_ANTIPOP1, 0);
 +
 +                      /* Enable VREF & VMID at 2x50k */
 +                      val |= 0x2 | WM8400_VREF_ENA;
 +                      wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 +
 +                      msleep(600);
 +
 +                      /* Enable BUFIOEN */
 +                      wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 +                                   WM8400_BUFDCOPEN | WM8400_POBCTRL |
 +                                   WM8400_BUFIOEN);
 +
 +                      /* Disable outputs */
 +                      val &= ~(WM8400_SPK_ENA | WM8400_OUT3_ENA |
 +                               WM8400_OUT4_ENA | WM8400_LOUT_ENA |
 +                               WM8400_ROUT_ENA);
 +                      wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 +
 +                      /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
 +                      wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN);
 +              }
 +
 +              /* VMID=2*300k */
 +              val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) &
 +                      ~WM8400_VMID_MODE_MASK;
 +              wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4);
 +              break;
 +
 +      case SND_SOC_BIAS_OFF:
 +              /* Enable POBCTRL and SOFT_ST */
 +              wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 +                      WM8400_POBCTRL | WM8400_BUFIOEN);
 +
 +              /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
 +              wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST |
 +                      WM8400_BUFDCOPEN | WM8400_POBCTRL |
 +                      WM8400_BUFIOEN);
 +
 +              /* mute DAC */
 +              val = wm8400_read(codec, WM8400_DAC_CTRL);
 +              wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE);
 +
 +              /* Enable any disabled outputs */
 +              val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
 +              val |= WM8400_SPK_ENA | WM8400_OUT3_ENA |
 +                      WM8400_OUT4_ENA | WM8400_LOUT_ENA |
 +                      WM8400_ROUT_ENA;
 +              wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 +
 +              /* Disable VMID */
 +              val &= ~WM8400_VMID_MODE_MASK;
 +              wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 +
 +              msleep(300);
 +
 +              /* Enable all output discharge bits */
 +              wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE |
 +                      WM8400_DIS_RLINE | WM8400_DIS_OUT3 |
 +                      WM8400_DIS_OUT4 | WM8400_DIS_LOUT |
 +                      WM8400_DIS_ROUT);
 +
 +              /* Disable VREF */
 +              val &= ~WM8400_VREF_ENA;
 +              wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val);
 +
 +              /* disable POBCTRL, SOFT_ST and BUFDCOPEN */
 +              wm8400_write(codec, WM8400_ANTIPOP2, 0x0);
 +
 +              ret = regulator_bulk_disable(ARRAY_SIZE(power),
 +                                           &power[0]);
 +              if (ret != 0)
 +                      return ret;
 +
 +              break;
 +      }
 +
 +      codec->bias_level = level;
 +      return 0;
 +}
 +
 +#define WM8400_RATES SNDRV_PCM_RATE_8000_96000
 +
 +#define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 +      SNDRV_PCM_FMTBIT_S24_LE)
 +
++static struct snd_soc_dai_ops wm8400_dai_ops = {
++      .hw_params = wm8400_hw_params,
++      .digital_mute = wm8400_mute,
++      .set_fmt = wm8400_set_dai_fmt,
++      .set_clkdiv = wm8400_set_dai_clkdiv,
++      .set_sysclk = wm8400_set_dai_sysclk,
++};
++
 +/*
 + * The WM8400 supports 2 different and mutually exclusive DAI
 + * configurations.
 + *
 + * 1. ADC/DAC on Primary Interface
 + * 2. ADC on Primary Interface/DAC on secondary
 + */
 +struct snd_soc_dai wm8400_dai = {
 +/* ADC/DAC on primary */
 +      .name = "WM8400 ADC/DAC Primary",
 +      .id = 1,
 +      .playback = {
 +              .stream_name = "Playback",
 +              .channels_min = 1,
 +              .channels_max = 2,
 +              .rates = WM8400_RATES,
 +              .formats = WM8400_FORMATS,
 +      },
 +      .capture = {
 +              .stream_name = "Capture",
 +              .channels_min = 1,
 +              .channels_max = 2,
 +              .rates = WM8400_RATES,
 +              .formats = WM8400_FORMATS,
 +      },
++      .ops = &wm8400_dai_ops,
 +};
 +EXPORT_SYMBOL_GPL(wm8400_dai);
 +
 +static int wm8400_suspend(struct platform_device *pdev, pm_message_t state)
 +{
 +      struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 +      struct snd_soc_codec *codec = socdev->card->codec;
 +
 +      wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
 +
 +      return 0;
 +}
 +
 +static int wm8400_resume(struct platform_device *pdev)
 +{
 +      struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 +      struct snd_soc_codec *codec = socdev->card->codec;
 +
 +      wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 +
 +      return 0;
 +}
 +
 +static struct snd_soc_codec *wm8400_codec;
 +
 +static int wm8400_probe(struct platform_device *pdev)
 +{
 +      struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 +      struct snd_soc_codec *codec;
 +      int ret;
 +
 +      if (!wm8400_codec) {
 +              dev_err(&pdev->dev, "wm8400 not yet discovered\n");
 +              return -ENODEV;
 +      }
 +      codec = wm8400_codec;
 +
 +      socdev->card->codec = codec;
 +
 +      /* register pcms */
 +      ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 +      if (ret < 0) {
 +              dev_err(&pdev->dev, "failed to create pcms\n");
 +              goto pcm_err;
 +      }
 +
 +      wm8400_add_controls(codec);
 +      wm8400_add_widgets(codec);
 +
 +      ret = snd_soc_init_card(socdev);
 +      if (ret < 0) {
 +              dev_err(&pdev->dev, "failed to register card\n");
 +              goto card_err;
 +      }
 +
 +      return ret;
 +
 +card_err:
 +      snd_soc_free_pcms(socdev);
 +      snd_soc_dapm_free(socdev);
 +pcm_err:
 +      return ret;
 +}
 +
 +/* power down chip */
 +static int wm8400_remove(struct platform_device *pdev)
 +{
 +      struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 +
 +      snd_soc_free_pcms(socdev);
 +      snd_soc_dapm_free(socdev);
 +
 +      return 0;
 +}
 +
 +struct snd_soc_codec_device soc_codec_dev_wm8400 = {
 +      .probe =        wm8400_probe,
 +      .remove =       wm8400_remove,
 +      .suspend =      wm8400_suspend,
 +      .resume =       wm8400_resume,
 +};
 +
 +static void wm8400_probe_deferred(struct work_struct *work)
 +{
 +      struct wm8400_priv *priv = container_of(work, struct wm8400_priv,
 +                                              work);
 +      struct snd_soc_codec *codec = &priv->codec;
 +      int ret;
 +
 +      /* charge output caps */
 +      wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 +
 +      /* We're done, tell the subsystem. */
 +      ret = snd_soc_register_codec(codec);
 +      if (ret != 0) {
 +              dev_err(priv->wm8400->dev,
 +                      "Failed to register codec: %d\n", ret);
 +              goto err;
 +      }
 +
 +      ret = snd_soc_register_dai(&wm8400_dai);
 +      if (ret != 0) {
 +              dev_err(priv->wm8400->dev,
 +                      "Failed to register DAI: %d\n", ret);
 +              goto err_codec;
 +      }
 +
 +      return;
 +
 +err_codec:
 +      snd_soc_unregister_codec(codec);
 +err:
 +      wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
 +}
 +
 +static int wm8400_codec_probe(struct platform_device *dev)
 +{
 +      struct wm8400_priv *priv;
 +      int ret;
 +      u16 reg;
 +      struct snd_soc_codec *codec;
 +
 +      priv = kzalloc(sizeof(struct wm8400_priv), GFP_KERNEL);
 +      if (priv == NULL)
 +              return -ENOMEM;
 +
 +      codec = &priv->codec;
 +      codec->private_data = priv;
 +      codec->control_data = dev->dev.driver_data;
 +      priv->wm8400 = dev->dev.driver_data;
 +
 +      ret = regulator_bulk_get(priv->wm8400->dev,
 +                               ARRAY_SIZE(power), &power[0]);
 +      if (ret != 0) {
 +              dev_err(&dev->dev, "Failed to get regulators: %d\n", ret);
 +              goto err;
 +      }
 +
 +      codec->dev = &dev->dev;
 +      wm8400_dai.dev = &dev->dev;
 +
 +      codec->name = "WM8400";
 +      codec->owner = THIS_MODULE;
 +      codec->read = wm8400_read;
 +      codec->write = wm8400_write;
 +      codec->bias_level = SND_SOC_BIAS_OFF;
 +      codec->set_bias_level = wm8400_set_bias_level;
 +      codec->dai = &wm8400_dai;
 +      codec->num_dai = 1;
 +      codec->reg_cache_size = WM8400_REGISTER_COUNT;
 +      mutex_init(&codec->mutex);
 +      INIT_LIST_HEAD(&codec->dapm_widgets);
 +      INIT_LIST_HEAD(&codec->dapm_paths);
 +      INIT_WORK(&priv->work, wm8400_probe_deferred);
 +
 +      wm8400_codec_reset(codec);
 +
 +      reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1);
 +      wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA);
 +
 +      /* Latch volume update bits */
 +      reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME);
 +      wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME,
 +                   reg & WM8400_IPVU);
 +      reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME);
 +      wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME,
 +                   reg & WM8400_IPVU);
 +
 +      wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
 +      wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
 +
 +      wm8400_codec = codec;
 +
 +      if (!schedule_work(&priv->work)) {
 +              ret = -EINVAL;
 +              goto err_regulator;
 +      }
 +
 +      return 0;
 +
 +err_regulator:
 +      wm8400_codec = NULL;
 +      regulator_bulk_free(ARRAY_SIZE(power), power);
 +err:
 +      kfree(priv);
 +      return ret;
 +}
 +
 +static int __exit wm8400_codec_remove(struct platform_device *dev)
 +{
 +      struct wm8400_priv *priv = wm8400_codec->private_data;
 +      u16 reg;
 +
 +      snd_soc_unregister_dai(&wm8400_dai);
 +      snd_soc_unregister_codec(wm8400_codec);
 +
 +      reg = wm8400_read(wm8400_codec, WM8400_POWER_MANAGEMENT_1);
 +      wm8400_write(wm8400_codec, WM8400_POWER_MANAGEMENT_1,
 +                   reg & (~WM8400_CODEC_ENA));
 +
 +      regulator_bulk_free(ARRAY_SIZE(power), power);
 +      kfree(priv);
 +
 +      wm8400_codec = NULL;
 +
 +      return 0;
 +}
 +
 +static struct platform_driver wm8400_codec_driver = {
 +      .driver = {
 +              .name = "wm8400-codec",
 +              .owner = THIS_MODULE,
 +      },
 +      .probe = wm8400_codec_probe,
 +      .remove = __exit_p(wm8400_codec_remove),
 +};
 +
 +static int __init wm8400_codec_init(void)
 +{
 +      return platform_driver_register(&wm8400_codec_driver);
 +}
 +module_init(wm8400_codec_init);
 +
 +static void __exit wm8400_codec_exit(void)
 +{
 +      platform_driver_unregister(&wm8400_codec_driver);
 +}
 +module_exit(wm8400_codec_exit);
 +
 +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8400);
 +
 +MODULE_DESCRIPTION("ASoC WM8400 driver");
 +MODULE_AUTHOR("Mark Brown");
 +MODULE_LICENSE("GPL");
 +MODULE_ALIAS("platform:wm8400-codec");
index 6d4ef71e9195210284c52d7d24b43686ada63a48,cc975a62fa5c0633d50a507af457f814655fe095..6a4cea09c45dbe624ba2941442c68e3fdb5971c2
@@@ -336,7 -336,7 +336,7 @@@ static int wm8510_set_dai_pll(struct sn
                return 0;
        }
  
 -      pll_factors(freq_out*8, freq_in);
 +      pll_factors(freq_out*4, freq_in);
  
        wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
        wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
@@@ -367,7 -367,7 +367,7 @@@ static int wm8510_set_dai_clkdiv(struc
                wm8510_write(codec, WM8510_GPIO, reg | div);
                break;
        case WM8510_MCLKDIV:
 -              reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
 +              reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f;
                wm8510_write(codec, WM8510_CLOCK, reg | div);
                break;
        case WM8510_ADCCLK:
@@@ -554,6 -554,14 +554,14 @@@ static int wm8510_set_bias_level(struc
  #define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
  
+ static struct snd_soc_dai_ops wm8510_dai_ops = {
+       .hw_params      = wm8510_pcm_hw_params,
+       .digital_mute   = wm8510_mute,
+       .set_fmt        = wm8510_set_dai_fmt,
+       .set_clkdiv     = wm8510_set_dai_clkdiv,
+       .set_pll        = wm8510_set_dai_pll,
+ };
  struct snd_soc_dai wm8510_dai = {
        .name = "WM8510 HiFi",
        .playback = {
                .channels_max = 2,
                .rates = WM8510_RATES,
                .formats = WM8510_FORMATS,},
-       .ops = {
-               .hw_params = wm8510_pcm_hw_params,
-               .digital_mute = wm8510_mute,
-               .set_fmt = wm8510_set_dai_fmt,
-               .set_clkdiv = wm8510_set_dai_clkdiv,
-               .set_pll = wm8510_set_dai_pll,
-       },
+       .ops = &wm8510_dai_ops,
  };
  EXPORT_SYMBOL_GPL(wm8510_dai);
  
index 6cab82a9c9d74826379057254ea1f64144916bda,ee0af23a1accecf1977496eafa72c32bf5b448a9..27f9e231bf69c2524ce2247f0e39fb8882c7ebd7
@@@ -35,6 -35,8 +35,6 @@@
  
  #include "wm8580.h"
  
 -#define WM8580_VERSION "0.1"
 -
  struct pll_state {
        unsigned int in;
        unsigned int out;
@@@ -769,6 -771,21 +769,21 @@@ static int wm8580_set_bias_level(struc
  #define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
  
+ static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
+       .hw_params      = wm8580_paif_hw_params,
+       .set_fmt        = wm8580_set_paif_dai_fmt,
+       .set_clkdiv     = wm8580_set_dai_clkdiv,
+       .set_pll        = wm8580_set_dai_pll,
+       .digital_mute   = wm8580_digital_mute,
+ };
+ static struct snd_soc_dai_ops wm8580_dai_ops_capture = {
+       .hw_params      = wm8580_paif_hw_params,
+       .set_fmt        = wm8580_set_paif_dai_fmt,
+       .set_clkdiv     = wm8580_set_dai_clkdiv,
+       .set_pll        = wm8580_set_dai_pll,
+ };
  struct snd_soc_dai wm8580_dai[] = {
        {
                .name = "WM8580 PAIFRX",
                        .rates = SNDRV_PCM_RATE_8000_192000,
                        .formats = WM8580_FORMATS,
                },
-               .ops = {
-                        .hw_params = wm8580_paif_hw_params,
-                        .set_fmt = wm8580_set_paif_dai_fmt,
-                        .set_clkdiv = wm8580_set_dai_clkdiv,
-                        .set_pll = wm8580_set_dai_pll,
-                        .digital_mute = wm8580_digital_mute,
-                },
+               .ops = &wm8580_dai_ops_playback,
        },
        {
                .name = "WM8580 PAIFTX",
                        .rates = SNDRV_PCM_RATE_8000_192000,
                        .formats = WM8580_FORMATS,
                },
-               .ops = {
-                        .hw_params = wm8580_paif_hw_params,
-                        .set_fmt = wm8580_set_paif_dai_fmt,
-                        .set_clkdiv = wm8580_set_dai_clkdiv,
-                        .set_pll = wm8580_set_dai_pll,
-                },
+               .ops = &wm8580_dai_ops_capture,
        },
  };
  EXPORT_SYMBOL_GPL(wm8580_dai);
@@@ -970,6 -976,8 +974,6 @@@ static int wm8580_probe(struct platform
        struct wm8580_priv *wm8580;
        int ret = 0;
  
 -      pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION);
 -
        setup = socdev->codec_data;
        codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
        if (codec == NULL)
index 1d5eca89de60817ac98ba21135243cd00589a805,cc6e57f9acf8db416773e2cc850a195ccaaa6786..a6e8f3f7f052ee4dc45b8ef4540f4cf31c9f411e
  
  #include "wm8753.h"
  
 -#ifdef CONFIG_SPI_MASTER
 -static struct spi_driver wm8753_spi_driver;
 -static int wm8753_spi_write(struct spi_device *spi, const char *data, int len);
 -#endif
 -
  static int caps_charge = 2000;
  module_param(caps_charge, int, 0);
  MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@@@ -1301,6 -1306,51 +1301,51 @@@ static int wm8753_set_bias_level(struc
   * 3. Voice disabled - HIFI over HIFI
   * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
   */
+ static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = {
+       .hw_params      = wm8753_i2s_hw_params,
+       .digital_mute   = wm8753_mute,
+       .set_fmt        = wm8753_mode1h_set_dai_fmt,
+       .set_clkdiv     = wm8753_set_dai_clkdiv,
+       .set_pll        = wm8753_set_dai_pll,
+       .set_sysclk     = wm8753_set_dai_sysclk,
+ };
+ static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = {
+       .hw_params      = wm8753_pcm_hw_params,
+       .digital_mute   = wm8753_mute,
+       .set_fmt        = wm8753_mode1v_set_dai_fmt,
+       .set_clkdiv     = wm8753_set_dai_clkdiv,
+       .set_pll        = wm8753_set_dai_pll,
+       .set_sysclk     = wm8753_set_dai_sysclk,
+ };
+ static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = {
+       .hw_params      = wm8753_pcm_hw_params,
+       .digital_mute   = wm8753_mute,
+       .set_fmt        = wm8753_mode2_set_dai_fmt,
+       .set_clkdiv     = wm8753_set_dai_clkdiv,
+       .set_pll        = wm8753_set_dai_pll,
+       .set_sysclk     = wm8753_set_dai_sysclk,
+ };
+ static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3       = {
+       .hw_params      = wm8753_i2s_hw_params,
+       .digital_mute   = wm8753_mute,
+       .set_fmt        = wm8753_mode3_4_set_dai_fmt,
+       .set_clkdiv     = wm8753_set_dai_clkdiv,
+       .set_pll        = wm8753_set_dai_pll,
+       .set_sysclk     = wm8753_set_dai_sysclk,
+ };
+ static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4       = {
+       .hw_params      = wm8753_i2s_hw_params,
+       .digital_mute   = wm8753_mute,
+       .set_fmt        = wm8753_mode3_4_set_dai_fmt,
+       .set_clkdiv     = wm8753_set_dai_clkdiv,
+       .set_pll        = wm8753_set_dai_pll,
+       .set_sysclk     = wm8753_set_dai_sysclk,
+ };
  static const struct snd_soc_dai wm8753_all_dai[] = {
  /* DAI HiFi mode 1 */
  {     .name = "WM8753 HiFi",
                .channels_max = 2,
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS},
-       .ops = {
-               .hw_params = wm8753_i2s_hw_params,
-               .digital_mute = wm8753_mute,
-               .set_fmt = wm8753_mode1h_set_dai_fmt,
-               .set_clkdiv = wm8753_set_dai_clkdiv,
-               .set_pll = wm8753_set_dai_pll,
-               .set_sysclk = wm8753_set_dai_sysclk,
-       },
+       .ops = &wm8753_dai_ops_hifi_mode1,
  },
  /* DAI Voice mode 1 */
  {     .name = "WM8753 Voice",
                .channels_max = 2,
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
-       .ops = {
-               .hw_params = wm8753_pcm_hw_params,
-               .digital_mute = wm8753_mute,
-               .set_fmt = wm8753_mode1v_set_dai_fmt,
-               .set_clkdiv = wm8753_set_dai_clkdiv,
-               .set_pll = wm8753_set_dai_pll,
-               .set_sysclk = wm8753_set_dai_sysclk,
-       },
+       .ops = &wm8753_dai_ops_voice_mode1,
  },
  /* DAI HiFi mode 2 - dummy */
  {     .name = "WM8753 HiFi",
                .channels_max = 2,
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
-       .ops = {
-               .hw_params = wm8753_pcm_hw_params,
-               .digital_mute = wm8753_mute,
-               .set_fmt = wm8753_mode2_set_dai_fmt,
-               .set_clkdiv = wm8753_set_dai_clkdiv,
-               .set_pll = wm8753_set_dai_pll,
-               .set_sysclk = wm8753_set_dai_sysclk,
-       },
+       .ops = &wm8753_dai_ops_voice_mode2,
  },
  /* DAI HiFi mode 3 */
  {     .name = "WM8753 HiFi",
                .channels_max = 2,
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
-       .ops = {
-               .hw_params = wm8753_i2s_hw_params,
-               .digital_mute = wm8753_mute,
-               .set_fmt = wm8753_mode3_4_set_dai_fmt,
-               .set_clkdiv = wm8753_set_dai_clkdiv,
-               .set_pll = wm8753_set_dai_pll,
-               .set_sysclk = wm8753_set_dai_sysclk,
-       },
+       .ops = &wm8753_dai_ops_hifi_mode3,
  },
  /* DAI Voice mode 3 - dummy */
  {     .name = "WM8753 Voice",
                .channels_max = 2,
                .rates = WM8753_RATES,
                .formats = WM8753_FORMATS,},
-       .ops = {
-               .hw_params = wm8753_i2s_hw_params,
-               .digital_mute = wm8753_mute,
-               .set_fmt = wm8753_mode3_4_set_dai_fmt,
-               .set_clkdiv = wm8753_set_dai_clkdiv,
-               .set_pll = wm8753_set_dai_pll,
-               .set_sysclk = wm8753_set_dai_sysclk,
-       },
+       .ops = &wm8753_dai_ops_hifi_mode4,
  },
  /* DAI Voice mode 4 - dummy */
  {     .name = "WM8753 Voice",
diff --combined sound/soc/fsl/fsl_ssi.c
index b7733e6be192fe0bf0d2792c4bb7e086695ddc5c,0fddd437a7c9e5b329336ce16027dd0dc37892b4..169bca295b7831f263739042aa1ed3645d59c99a
@@@ -72,7 -72,6 +72,7 @@@
   * @dev: struct device pointer
   * @playback: the number of playback streams opened
   * @capture: the number of capture streams opened
 + * @asynchronous: 0=synchronous mode, 1=asynchronous mode
   * @cpu_dai: the CPU DAI for this device
   * @dev_attr: the sysfs device attribute structure
   * @stats: SSI statistics
@@@ -87,7 -86,6 +87,7 @@@ struct fsl_ssi_private 
        struct device *dev;
        unsigned int playback;
        unsigned int capture;
 +      int asynchronous;
        struct snd_soc_dai cpu_dai;
        struct device_attribute dev_attr;
  
@@@ -303,10 -301,9 +303,10 @@@ static int fsl_ssi_startup(struct snd_p
                 *
                 * FIXME: Little-endian samples require a different shift dir
                 */
 -              clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK,
 -                      CCSR_SSI_SCR_TFR_CLK_DIS |
 -                      CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN);
 +              clrsetbits_be32(&ssi->scr,
 +                      CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
 +                      CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
 +                      | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
  
                out_be32(&ssi->stcr,
                         CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
                        SNDRV_PCM_HW_PARAM_RATE,
                        first_runtime->rate, first_runtime->rate);
  
 -              snd_pcm_hw_constraint_minmax(substream->runtime,
 -                      SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
 -                      first_runtime->sample_bits,
 -                      first_runtime->sample_bits);
 +              /* If we're in synchronous mode, then we need to constrain
 +               * the sample size as well.  We don't support independent sample
 +               * rates in asynchronous mode.
 +               */
 +              if (!ssi_private->asynchronous)
 +                      snd_pcm_hw_constraint_minmax(substream->runtime,
 +                              SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
 +                              first_runtime->sample_bits,
 +                              first_runtime->sample_bits);
  
                ssi_private->second_stream = substream;
        }
@@@ -429,18 -421,13 +429,18 @@@ static int fsl_ssi_hw_params(struct snd
                struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
                unsigned int sample_size =
                        snd_pcm_format_width(params_format(hw_params));
 -              u32 wl;
 +              u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
  
                /* The SSI should always be disabled at this points (SSIEN=0) */
 -              wl = CCSR_SSI_SxCCR_WL(sample_size);
  
                /* In synchronous mode, the SSI uses STCCR for capture */
 -              clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
 +              if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
 +                  !ssi_private->asynchronous)
 +                      clrsetbits_be32(&ssi->stccr,
 +                                      CCSR_SSI_SxCCR_WL_MASK, wl);
 +              else
 +                      clrsetbits_be32(&ssi->srccr,
 +                                      CCSR_SSI_SxCCR_WL_MASK, wl);
        }
  
        return 0;
@@@ -464,33 -451,28 +464,33 @@@ static int fsl_ssi_trigger(struct snd_p
  
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
 -      case SNDRV_PCM_TRIGGER_RESUME:
 +              clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 -                      clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
                        setbits32(&ssi->scr,
                                CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
                } else {
 -                      clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
 +                      long timeout = jiffies + 10;
 +
                        setbits32(&ssi->scr,
                                CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
  
 -                      /*
 -                       * I think we need this delay to allow time for the SSI
 -                       * to put data into its FIFO.  Without it, ALSA starts
 -                       * to complain about overruns.
 +                      /* Wait until the SSI has filled its FIFO. Without this
 +                       * delay, ALSA complains about overruns.  When the FIFO
 +                       * is full, the DMA controller initiates its first
 +                       * transfer.  Until then, however, the DMA's DAR
 +                       * register is zero, which translates to an
 +                       * out-of-bounds pointer.  This makes ALSA think an
 +                       * overrun has occurred.
                         */
 -                      mdelay(1);
 +                      while (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0) &&
 +                             (jiffies < timeout));
 +                      if (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0))
 +                              return -EIO;
                }
                break;
  
        case SNDRV_PCM_TRIGGER_STOP:
 -      case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
@@@ -580,6 -562,15 +580,15 @@@ static int fsl_ssi_set_fmt(struct snd_s
  /**
   * fsl_ssi_dai_template: template CPU DAI for the SSI
   */
+ static struct snd_soc_dai_ops fsl_ssi_dai_ops = {
+       .startup        = fsl_ssi_startup,
+       .hw_params      = fsl_ssi_hw_params,
+       .shutdown       = fsl_ssi_shutdown,
+       .trigger        = fsl_ssi_trigger,
+       .set_sysclk     = fsl_ssi_set_sysclk,
+       .set_fmt        = fsl_ssi_set_fmt,
+ };
  static struct snd_soc_dai fsl_ssi_dai_template = {
        .playback = {
                /* The SSI does not support monaural audio. */
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
-       .ops = {
-               .startup = fsl_ssi_startup,
-               .hw_params = fsl_ssi_hw_params,
-               .shutdown = fsl_ssi_shutdown,
-               .trigger = fsl_ssi_trigger,
-               .set_sysclk = fsl_ssi_set_sysclk,
-               .set_fmt = fsl_ssi_set_fmt,
-       },
+       .ops = &fsl_ssi_dai_ops,
  };
  
  /**
@@@ -671,7 -655,6 +673,7 @@@ struct snd_soc_dai *fsl_ssi_create_dai(
        ssi_private->ssi_phys = ssi_info->ssi_phys;
        ssi_private->irq = ssi_info->irq;
        ssi_private->dev = ssi_info->dev;
 +      ssi_private->asynchronous = ssi_info->asynchronous;
  
        ssi_private->dev->driver_data = fsl_ssi_dai;
  
@@@ -722,14 -705,6 +724,14 @@@ void fsl_ssi_destroy_dai(struct snd_soc
  }
  EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
  
 +static int __init fsl_ssi_init(void)
 +{
 +      printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n");
 +
 +      return 0;
 +}
 +module_init(fsl_ssi_init);
 +
  MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
  MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
  MODULE_LICENSE("GPL");
diff --combined sound/soc/pxa/pxa-ssp.c
index 52d97c4b82b19725e9528706eeae02c1c044c004,3e18064e86b2e0bb02dc3df5732f759ecf28c1d3..d3fa6357a9fd9a1eabc29aeef41843dc118501f2
@@@ -300,7 -300,7 +300,7 @@@ static int pxa_ssp_set_dai_sysclk(struc
        int val;
  
        u32 sscr0 = ssp_read_reg(ssp, SSCR0) &
 -              ~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
 +              ~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
  
        dev_dbg(&ssp->pdev->dev,
                "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
        case PXA_SSP_CLK_AUDIO:
                priv->sysclk = 0;
                ssp_set_scr(&priv->dev, 1);
 -              sscr0 |= SSCR0_ADC;
 +              sscr0 |= SSCR0_ACS;
                break;
        default:
                return -ENODEV;
@@@ -522,20 -522,9 +522,20 @@@ static int pxa_ssp_set_dai_fmt(struct s
        u32 sscr1;
        u32 sspsp;
  
 +      /* check if we need to change anything at all */
 +      if (priv->dai_fmt == fmt)
 +              return 0;
 +
 +      /* we can only change the settings if the port is not in use */
 +      if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
 +              dev_err(&ssp->pdev->dev,
 +                      "can't change hardware dai format: stream is in use");
 +              return -EINVAL;
 +      }
 +
        /* reset port settings */
        sscr0 = ssp_read_reg(ssp, SSCR0) &
 -              (SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
 +              (SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
        sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7);
        sspsp = 0;
  
@@@ -655,7 -644,8 +655,7 @@@ static int pxa_ssp_hw_params(struct snd
                        sscr0 |= SSCR0_FPCKE;
  #endif
                sscr0 |= SSCR0_DataSize(16);
 -              if (params_channels(params) > 1)
 -                      sscr0 |= SSCR0_EDSS;
 +              /* use network mode (2 slots) for 16 bit stereo */
                break;
        case SNDRV_PCM_FORMAT_S24_LE:
                sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8));
@@@ -794,6 -784,19 +794,19 @@@ static void pxa_ssp_remove(struct platf
                            SNDRV_PCM_FMTBIT_S24_LE |   \
                            SNDRV_PCM_FMTBIT_S32_LE)
  
+ static struct snd_soc_dai_ops pxa_ssp_dai_ops = {
+       .startup        = pxa_ssp_startup,
+       .shutdown       = pxa_ssp_shutdown,
+       .trigger        = pxa_ssp_trigger,
+       .hw_params      = pxa_ssp_hw_params,
+       .set_sysclk     = pxa_ssp_set_dai_sysclk,
+       .set_clkdiv     = pxa_ssp_set_dai_clkdiv,
+       .set_pll        = pxa_ssp_set_dai_pll,
+       .set_fmt        = pxa_ssp_set_dai_fmt,
+       .set_tdm_slot   = pxa_ssp_set_dai_tdm_slot,
+       .set_tristate   = pxa_ssp_set_dai_tristate,
+ };
  struct snd_soc_dai pxa_ssp_dai[] = {
        {
                .name = "pxa2xx-ssp1",
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
-               .ops = {
-                       .startup = pxa_ssp_startup,
-                       .shutdown = pxa_ssp_shutdown,
-                       .trigger = pxa_ssp_trigger,
-                       .hw_params = pxa_ssp_hw_params,
-                       .set_sysclk = pxa_ssp_set_dai_sysclk,
-                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
-                       .set_pll = pxa_ssp_set_dai_pll,
-                       .set_fmt = pxa_ssp_set_dai_fmt,
-                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
-                       .set_tristate = pxa_ssp_set_dai_tristate,
-               },
+               .ops = &pxa_ssp_dai_ops,
        },
        {       .name = "pxa2xx-ssp2",
                .id = 1,
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
-               .ops = {
-                       .startup = pxa_ssp_startup,
-                       .shutdown = pxa_ssp_shutdown,
-                       .trigger = pxa_ssp_trigger,
-                       .hw_params = pxa_ssp_hw_params,
-                       .set_sysclk = pxa_ssp_set_dai_sysclk,
-                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
-                       .set_pll = pxa_ssp_set_dai_pll,
-                       .set_fmt = pxa_ssp_set_dai_fmt,
-                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
-                       .set_tristate = pxa_ssp_set_dai_tristate,
-               },
+               .ops = &pxa_ssp_dai_ops,
        },
        {
                .name = "pxa2xx-ssp3",
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
-               .ops = {
-                       .startup = pxa_ssp_startup,
-                       .shutdown = pxa_ssp_shutdown,
-                       .trigger = pxa_ssp_trigger,
-                       .hw_params = pxa_ssp_hw_params,
-                       .set_sysclk = pxa_ssp_set_dai_sysclk,
-                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
-                       .set_pll = pxa_ssp_set_dai_pll,
-                       .set_fmt = pxa_ssp_set_dai_fmt,
-                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
-                       .set_tristate = pxa_ssp_set_dai_tristate,
-               },
+               .ops = &pxa_ssp_dai_ops,
        },
        {
                .name = "pxa2xx-ssp4",
                        .rates = PXA_SSP_RATES,
                        .formats = PXA_SSP_FORMATS,
                 },
-               .ops = {
-                       .startup = pxa_ssp_startup,
-                       .shutdown = pxa_ssp_shutdown,
-                       .trigger = pxa_ssp_trigger,
-                       .hw_params = pxa_ssp_hw_params,
-                       .set_sysclk = pxa_ssp_set_dai_sysclk,
-                       .set_clkdiv = pxa_ssp_set_dai_clkdiv,
-                       .set_pll = pxa_ssp_set_dai_pll,
-                       .set_fmt = pxa_ssp_set_dai_fmt,
-                       .set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
-                       .set_tristate = pxa_ssp_set_dai_tristate,
-               },
+               .ops = &pxa_ssp_dai_ops,
        },
  };
  EXPORT_SYMBOL_GPL(pxa_ssp_dai);
index 49a2810ca58c70f4174d9da5eee246c498c99908,11cd0f289c160e95e8b46c6373df79c7d0ce1f8d..cf809049272afff65ac85d096f680c5beff47352
@@@ -106,13 -106,13 +106,13 @@@ static int pxa2xx_ac97_resume(struct sn
  static int pxa2xx_ac97_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
  {
 -      return pxa2xx_ac97_hw_probe(pdev);
 +      return pxa2xx_ac97_hw_probe(to_platform_device(dai->dev));
  }
  
  static void pxa2xx_ac97_remove(struct platform_device *pdev,
                               struct snd_soc_dai *dai)
  {
 -      pxa2xx_ac97_hw_remove(pdev);
 +      pxa2xx_ac97_hw_remove(to_platform_device(dai->dev));
  }
  
  static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
@@@ -164,6 -164,10 +164,10 @@@ static int pxa2xx_ac97_hw_mic_params(st
                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
                SNDRV_PCM_RATE_48000)
  
+ static struct snd_soc_dai_ops pxa_ac97_dai_ops = {
+       .hw_params      = pxa2xx_ac97_hw_params,
+ };
  /*
   * There is only 1 physical AC97 interface for pxa2xx, but it
   * has extra fifo's that can be used for aux DACs and ADCs.
@@@ -189,8 -193,7 +193,7 @@@ struct snd_soc_dai pxa_ac97_dai[] = 
                .channels_max = 2,
                .rates = PXA2XX_AC97_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .hw_params = pxa2xx_ac97_hw_params,},
+       .ops = &pxa_ac97_dai_ops,
  },
  {
        .name = "pxa2xx-ac97-aux",
                .channels_max = 1,
                .rates = PXA2XX_AC97_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .hw_params = pxa2xx_ac97_hw_aux_params,},
+       .ops = &pxa_ac97_dai_ops,
  },
  {
        .name = "pxa2xx-ac97-mic",
                .channels_max = 1,
                .rates = PXA2XX_AC97_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .hw_params = pxa2xx_ac97_hw_mic_params,},
+       .ops = &pxa_ac97_dai_ops,
  },
  };
  
  EXPORT_SYMBOL_GPL(pxa_ac97_dai);
  EXPORT_SYMBOL_GPL(soc_ac97_ops);
  
 -static int __init pxa_ac97_init(void)
 +static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev)
  {
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++)
 +              pxa_ac97_dai[i].dev = &pdev->dev;
 +
 +      /* Punt most of the init to the SoC probe; we may need the machine
 +       * driver to do interesting things with the clocking to get us up
 +       * and running.
 +       */
        return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
  }
 +
 +static int __devexit pxa2xx_ac97_dev_remove(struct platform_device *pdev)
 +{
 +      snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
 +
 +      return 0;
 +}
 +
 +static struct platform_driver pxa2xx_ac97_driver = {
 +      .probe          = pxa2xx_ac97_dev_probe,
 +      .remove         = __devexit_p(pxa2xx_ac97_dev_remove),
 +      .driver         = {
 +              .name   = "pxa2xx-ac97",
 +              .owner  = THIS_MODULE,
 +      },
 +};
 +
 +static int __init pxa_ac97_init(void)
 +{
 +      return platform_driver_register(&pxa2xx_ac97_driver);
 +}
  module_init(pxa_ac97_init);
  
  static void __exit pxa_ac97_exit(void)
  {
 -      snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai));
 +      platform_driver_unregister(&pxa2xx_ac97_driver);
  }
  module_exit(pxa_ac97_exit);
  
index 1ceae690d019c80ab6140c87ac40d982907855dc,382d7eee53efafc8d7855a59aa616b3fa765500a..1ca3cdaa82133b009b57b2fd571bc9069524eb80
@@@ -22,7 -22,6 +22,7 @@@
  #include <linux/delay.h>
  #include <linux/clk.h>
  #include <linux/kernel.h>
 +#include <linux/io.h>
  
  #include <sound/core.h>
  #include <sound/pcm.h>
  #include <sound/soc.h>
  #include <mach/hardware.h>
  
 -#include <linux/io.h>
 -#include <asm/dma.h>
 -
 -#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
 +#include <plat/regs-s3c2412-iis.h>
  
 -#include <mach/regs-gpio.h>
 -#include <mach/audio.h>
 +#include <plat/regs-gpio.h>
 +#include <plat/audio.h>
  #include <mach/dma.h>
  
  #include "s3c24xx-pcm.h"
  #include "s3c2412-i2s.h"
  
  #define S3C2412_I2S_DEBUG 0
 -#define S3C2412_I2S_DEBUG_CON 0
 -
 -#if S3C2412_I2S_DEBUG
 -#define DBG(x...) printk(KERN_INFO x)
 -#else
 -#define DBG(x...) do { } while (0)
 -#endif
  
  static struct s3c2410_dma_client s3c2412_dma_client_out = {
        .name           = "I2S PCM Stereo out"
@@@ -64,7 -73,431 +64,7 @@@ static struct s3c24xx_pcm_dma_params s3
        .dma_size       = 4,
  };
  
 -struct s3c2412_i2s_info {
 -      struct device   *dev;
 -      void __iomem    *regs;
 -      struct clk      *iis_clk;
 -      struct clk      *iis_pclk;
 -      struct clk      *iis_cclk;
 -
 -      u32              suspend_iismod;
 -      u32              suspend_iiscon;
 -      u32              suspend_iispsr;
 -};
 -
 -static struct s3c2412_i2s_info s3c2412_i2s;
 -
 -#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
 -
 -#if S3C2412_I2S_DEBUG_CON
 -static void dbg_showcon(const char *fn, u32 con)
 -{
 -      printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
 -             bit_set(con, S3C2412_IISCON_LRINDEX),
 -             bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
 -             bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
 -             bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
 -             bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
 -
 -      printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
 -             fn,
 -             bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
 -             bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
 -             bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
 -             bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
 -      printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
 -             bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
 -             bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
 -             bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
 -}
 -#else
 -static inline void dbg_showcon(const char *fn, u32 con)
 -{
 -}
 -#endif
 -
 -/* Turn on or off the transmission path. */
 -static void s3c2412_snd_txctrl(int on)
 -{
 -      struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 -      void __iomem *regs = i2s->regs;
 -      u32 fic, con, mod;
 -
 -      DBG("%s(%d)\n", __func__, on);
 -
 -      fic = readl(regs + S3C2412_IISFIC);
 -      con = readl(regs + S3C2412_IISCON);
 -      mod = readl(regs + S3C2412_IISMOD);
 -
 -      DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 -
 -      if (on) {
 -              con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
 -              con &= ~S3C2412_IISCON_TXDMA_PAUSE;
 -              con &= ~S3C2412_IISCON_TXCH_PAUSE;
 -
 -              switch (mod & S3C2412_IISMOD_MODE_MASK) {
 -              case S3C2412_IISMOD_MODE_TXONLY:
 -              case S3C2412_IISMOD_MODE_TXRX:
 -                      /* do nothing, we are in the right mode */
 -                      break;
 -
 -              case S3C2412_IISMOD_MODE_RXONLY:
 -                      mod &= ~S3C2412_IISMOD_MODE_MASK;
 -                      mod |= S3C2412_IISMOD_MODE_TXRX;
 -                      break;
 -
 -              default:
 -                      dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
 -              }
 -
 -              writel(con, regs + S3C2412_IISCON);
 -              writel(mod, regs + S3C2412_IISMOD);
 -      } else {
 -              /* Note, we do not have any indication that the FIFO problems
 -               * tha the S3C2410/2440 had apply here, so we should be able
 -               * to disable the DMA and TX without resetting the FIFOS.
 -               */
 -
 -              con |=  S3C2412_IISCON_TXDMA_PAUSE;
 -              con |=  S3C2412_IISCON_TXCH_PAUSE;
 -              con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
 -
 -              switch (mod & S3C2412_IISMOD_MODE_MASK) {
 -              case S3C2412_IISMOD_MODE_TXRX:
 -                      mod &= ~S3C2412_IISMOD_MODE_MASK;
 -                      mod |= S3C2412_IISMOD_MODE_RXONLY;
 -                      break;
 -
 -              case S3C2412_IISMOD_MODE_TXONLY:
 -                      mod &= ~S3C2412_IISMOD_MODE_MASK;
 -                      con &= ~S3C2412_IISCON_IIS_ACTIVE;
 -                      break;
 -
 -              default:
 -                      dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
 -              }
 -
 -              writel(mod, regs + S3C2412_IISMOD);
 -              writel(con, regs + S3C2412_IISCON);
 -      }
 -
 -      fic = readl(regs + S3C2412_IISFIC);
 -      dbg_showcon(__func__, con);
 -      DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 -}
 -
 -static void s3c2412_snd_rxctrl(int on)
 -{
 -      struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 -      void __iomem *regs = i2s->regs;
 -      u32 fic, con, mod;
 -
 -      DBG("%s(%d)\n", __func__, on);
 -
 -      fic = readl(regs + S3C2412_IISFIC);
 -      con = readl(regs + S3C2412_IISCON);
 -      mod = readl(regs + S3C2412_IISMOD);
 -
 -      DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 -
 -      if (on) {
 -              con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
 -              con &= ~S3C2412_IISCON_RXDMA_PAUSE;
 -              con &= ~S3C2412_IISCON_RXCH_PAUSE;
 -
 -              switch (mod & S3C2412_IISMOD_MODE_MASK) {
 -              case S3C2412_IISMOD_MODE_TXRX:
 -              case S3C2412_IISMOD_MODE_RXONLY:
 -                      /* do nothing, we are in the right mode */
 -                      break;
 -
 -              case S3C2412_IISMOD_MODE_TXONLY:
 -                      mod &= ~S3C2412_IISMOD_MODE_MASK;
 -                      mod |= S3C2412_IISMOD_MODE_TXRX;
 -                      break;
 -
 -              default:
 -                      dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
 -              }
 -
 -              writel(mod, regs + S3C2412_IISMOD);
 -              writel(con, regs + S3C2412_IISCON);
 -      } else {
 -              /* See txctrl notes on FIFOs. */
 -
 -              con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
 -              con |=  S3C2412_IISCON_RXDMA_PAUSE;
 -              con |=  S3C2412_IISCON_RXCH_PAUSE;
 -
 -              switch (mod & S3C2412_IISMOD_MODE_MASK) {
 -              case S3C2412_IISMOD_MODE_RXONLY:
 -                      con &= ~S3C2412_IISCON_IIS_ACTIVE;
 -                      mod &= ~S3C2412_IISMOD_MODE_MASK;
 -                      break;
 -
 -              case S3C2412_IISMOD_MODE_TXRX:
 -                      mod &= ~S3C2412_IISMOD_MODE_MASK;
 -                      mod |= S3C2412_IISMOD_MODE_TXONLY;
 -                      break;
 -
 -              default:
 -                      dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
 -              }
 -
 -              writel(con, regs + S3C2412_IISCON);
 -              writel(mod, regs + S3C2412_IISMOD);
 -      }
 -
 -      fic = readl(regs + S3C2412_IISFIC);
 -      DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 -}
 -
 -
 -/*
 - * Wait for the LR signal to allow synchronisation to the L/R clock
 - * from the codec. May only be needed for slave mode.
 - */
 -static int s3c2412_snd_lrsync(void)
 -{
 -      u32 iiscon;
 -      unsigned long timeout = jiffies + msecs_to_jiffies(5);
 -
 -      DBG("Entered %s\n", __func__);
 -
 -      while (1) {
 -              iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON);
 -              if (iiscon & S3C2412_IISCON_LRINDEX)
 -                      break;
 -
 -              if (timeout < jiffies) {
 -                      printk(KERN_ERR "%s: timeout\n", __func__);
 -                      return -ETIMEDOUT;
 -              }
 -      }
 -
 -      return 0;
 -}
 -
 -/*
 - * Check whether CPU is the master or slave
 - */
 -static inline int s3c2412_snd_is_clkmaster(void)
 -{
 -      u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
 -
 -      DBG("Entered %s\n", __func__);
 -
 -      iismod &= S3C2412_IISMOD_MASTER_MASK;
 -      return !(iismod == S3C2412_IISMOD_SLAVE);
 -}
 -
 -/*
 - * Set S3C2412 I2S DAI format
 - */
 -static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 -                             unsigned int fmt)
 -{
 -      u32 iismod;
 -
 -
 -      DBG("Entered %s\n", __func__);
 -
 -      iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
 -      DBG("hw_params r: IISMOD: %x \n", iismod);
 -
 -      switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 -      case SND_SOC_DAIFMT_CBM_CFM:
 -              iismod &= ~S3C2412_IISMOD_MASTER_MASK;
 -              iismod |= S3C2412_IISMOD_SLAVE;
 -              break;
 -      case SND_SOC_DAIFMT_CBS_CFS:
 -              iismod &= ~S3C2412_IISMOD_MASTER_MASK;
 -              iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
 -              break;
 -      default:
 -              DBG("unknwon master/slave format\n");
 -              return -EINVAL;
 -      }
 -
 -      iismod &= ~S3C2412_IISMOD_SDF_MASK;
 -
 -      switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 -      case SND_SOC_DAIFMT_RIGHT_J:
 -              iismod |= S3C2412_IISMOD_SDF_MSB;
 -              break;
 -      case SND_SOC_DAIFMT_LEFT_J:
 -              iismod |= S3C2412_IISMOD_SDF_LSB;
 -              break;
 -      case SND_SOC_DAIFMT_I2S:
 -              iismod |= S3C2412_IISMOD_SDF_IIS;
 -              break;
 -      default:
 -              DBG("Unknown data format\n");
 -              return -EINVAL;
 -      }
 -
 -      writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
 -      DBG("hw_params w: IISMOD: %x \n", iismod);
 -      return 0;
 -}
 -
 -static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream,
 -                               struct snd_pcm_hw_params *params,
 -                               struct snd_soc_dai *dai)
 -{
 -      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 -      u32 iismod;
 -
 -      DBG("Entered %s\n", __func__);
 -
 -      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 -              rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out;
 -      else
 -              rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in;
 -
 -      /* Working copies of register */
 -      iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
 -      DBG("%s: r: IISMOD: %x\n", __func__, iismod);
 -
 -      switch (params_format(params)) {
 -      case SNDRV_PCM_FORMAT_S8:
 -              iismod |= S3C2412_IISMOD_8BIT;
 -              break;
 -      case SNDRV_PCM_FORMAT_S16_LE:
 -              iismod &= ~S3C2412_IISMOD_8BIT;
 -              break;
 -      }
 -
 -      writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD);
 -      DBG("%s: w: IISMOD: %x\n", __func__, iismod);
 -      return 0;
 -}
 -
 -static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 -                             struct snd_soc_dai *dai)
 -{
 -      int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
 -      unsigned long irqs;
 -      int ret = 0;
 -
 -      DBG("Entered %s\n", __func__);
 -
 -      switch (cmd) {
 -      case SNDRV_PCM_TRIGGER_START:
 -              /* On start, ensure that the FIFOs are cleared and reset. */
 -
 -              writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
 -                     s3c2412_i2s.regs + S3C2412_IISFIC);
 -
 -              /* clear again, just in case */
 -              writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC);
 -
 -      case SNDRV_PCM_TRIGGER_RESUME:
 -      case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 -              if (!s3c2412_snd_is_clkmaster()) {
 -                      ret = s3c2412_snd_lrsync();
 -                      if (ret)
 -                              goto exit_err;
 -              }
 -
 -              local_irq_save(irqs);
 -
 -              if (capture)
 -                      s3c2412_snd_rxctrl(1);
 -              else
 -                      s3c2412_snd_txctrl(1);
 -
 -              local_irq_restore(irqs);
 -              break;
 -
 -      case SNDRV_PCM_TRIGGER_STOP:
 -      case SNDRV_PCM_TRIGGER_SUSPEND:
 -      case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 -              local_irq_save(irqs);
 -
 -              if (capture)
 -                      s3c2412_snd_rxctrl(0);
 -              else
 -                      s3c2412_snd_txctrl(0);
 -
 -              local_irq_restore(irqs);
 -              break;
 -      default:
 -              ret = -EINVAL;
 -              break;
 -      }
 -
 -exit_err:
 -      return ret;
 -}
 -
 -/* default table of all avaialable root fs divisors */
 -static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 };
 -
 -int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info,
 -                        unsigned int *fstab,
 -                        unsigned int rate, struct clk *clk)
 -{
 -      unsigned long clkrate = clk_get_rate(clk);
 -      unsigned int div;
 -      unsigned int fsclk;
 -      unsigned int actual;
 -      unsigned int fs;
 -      unsigned int fsdiv;
 -      signed int deviation = 0;
 -      unsigned int best_fs = 0;
 -      unsigned int best_div = 0;
 -      unsigned int best_rate = 0;
 -      unsigned int best_deviation = INT_MAX;
 -
 -
 -      if (fstab == NULL)
 -              fstab = s3c2412_iis_fs;
 -
 -      for (fs = 0;; fs++) {
 -              fsdiv = s3c2412_iis_fs[fs];
 -
 -              if (fsdiv == 0)
 -                      break;
 -
 -              fsclk = clkrate / fsdiv;
 -              div = fsclk / rate;
 -
 -              if ((fsclk % rate) > (rate / 2))
 -                      div++;
 -
 -              if (div <= 1)
 -                      continue;
 -
 -              actual = clkrate / (fsdiv * div);
 -              deviation = actual - rate;
 -
 -              printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
 -                     fsdiv, div, actual, deviation);
 -
 -              deviation = abs(deviation);
 -
 -              if (deviation < best_deviation) {
 -                      best_fs = fsdiv;
 -                      best_div = div;
 -                      best_rate = actual;
 -                      best_deviation = deviation;
 -              }
 -
 -              if (deviation == 0)
 -                      break;
 -      }
 -
 -      printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
 -             best_fs, best_div, best_rate);
 -
 -      info->fs_div = best_fs;
 -      info->clk_div = best_div;
 -
 -      return 0;
 -}
 -EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate);
 +static struct s3c_i2sv2_info s3c2412_i2s;
  
  /*
   * Set S3C2412 Clock source
@@@ -74,17 -507,15 +74,17 @@@ static int s3c2412_i2s_set_sysclk(struc
  {
        u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
  
 -      DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
 +      pr_debug("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id,
            freq, dir);
  
        switch (clk_id) {
        case S3C2412_CLKSRC_PCLK:
 +              s3c2412_i2s.master = 1;
                iismod &= ~S3C2412_IISMOD_MASTER_MASK;
                iismod |= S3C2412_IISMOD_MASTER_INTERNAL;
                break;
        case S3C2412_CLKSRC_I2SCLK:
 +              s3c2412_i2s.master = 0;
                iismod &= ~S3C2412_IISMOD_MASTER_MASK;
                iismod |= S3C2412_IISMOD_MASTER_EXTERNAL;
                break;
        return 0;
  }
  
 -/*
 - * Set S3C2412 Clock dividers
 - */
 -static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 -                                int div_id, int div)
 -{
 -      struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 -      u32 reg;
 -
 -      DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
 -
 -      switch (div_id) {
 -      case S3C2412_DIV_BCLK:
 -              reg = readl(i2s->regs + S3C2412_IISMOD);
 -              reg &= ~S3C2412_IISMOD_BCLK_MASK;
 -              writel(reg | div, i2s->regs + S3C2412_IISMOD);
 -
 -              DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
 -              break;
 -
 -      case S3C2412_DIV_RCLK:
 -              if (div > 3) {
 -                      /* convert value to bit field */
 -
 -                      switch (div) {
 -                      case 256:
 -                              div = S3C2412_IISMOD_RCLK_256FS;
 -                              break;
 -
 -                      case 384:
 -                              div = S3C2412_IISMOD_RCLK_384FS;
 -                              break;
 -
 -                      case 512:
 -                              div = S3C2412_IISMOD_RCLK_512FS;
 -                              break;
 -
 -                      case 768:
 -                              div = S3C2412_IISMOD_RCLK_768FS;
 -                              break;
 -
 -                      default:
 -                              return -EINVAL;
 -                      }
 -              }
 -
 -              reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD);
 -              reg &= ~S3C2412_IISMOD_RCLK_MASK;
 -              writel(reg | div, i2s->regs + S3C2412_IISMOD);
 -              DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
 -              break;
 -
 -      case S3C2412_DIV_PRESCALER:
 -              if (div >= 0) {
 -                      writel((div << 8) | S3C2412_IISPSR_PSREN,
 -                             i2s->regs + S3C2412_IISPSR);
 -              } else {
 -                      writel(0x0, i2s->regs + S3C2412_IISPSR);
 -              }
 -              DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
 -              break;
 -
 -      default:
 -              return -EINVAL;
 -      }
 -
 -      return 0;
 -}
  
  struct clk *s3c2412_get_iisclk(void)
  {
@@@ -107,30 -606,34 +107,30 @@@ EXPORT_SYMBOL_GPL(s3c2412_get_iisclk)
  static int s3c2412_i2s_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
  {
 -      DBG("Entered %s\n", __func__);
 +      int ret;
  
 -      s3c2412_i2s.dev = &pdev->dev;
 +      pr_debug("Entered %s\n", __func__);
  
 -      s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
 -      if (s3c2412_i2s.regs == NULL)
 -              return -ENXIO;
 +      ret = s3c_i2sv2_probe(pdev, dai, &s3c2412_i2s, S3C2410_PA_IIS);
 +      if (ret)
 +              return ret;
  
 -      s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis");
 -      if (s3c2412_i2s.iis_pclk == NULL) {
 -              DBG("failed to get iis_clock\n");
 -              iounmap(s3c2412_i2s.regs);
 -              return -ENODEV;
 -      }
 +      s3c2412_i2s.dma_capture = &s3c2412_i2s_pcm_stereo_in;
 +      s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
  
        s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
        if (s3c2412_i2s.iis_cclk == NULL) {
 -              DBG("failed to get i2sclk clock\n");
 +              pr_debug("failed to get i2sclk clock\n");
                iounmap(s3c2412_i2s.regs);
                return -ENODEV;
        }
  
 -      clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
 +      /* Set MPLL as the source for IIS CLK */
  
 -      clk_enable(s3c2412_i2s.iis_pclk);
 +      clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll"));
        clk_enable(s3c2412_i2s.iis_cclk);
  
 -      s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk;
 +      s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
  
        /* Configure the I2S pins in correct mode */
        s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
        s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
        s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
  
 -      s3c2412_snd_txctrl(0);
 -      s3c2412_snd_rxctrl(0);
 -
 -      return 0;
 -}
 -
 -#ifdef CONFIG_PM
 -static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
 -{
 -      struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 -      u32 iismod;
 -
 -      if (dai->active) {
 -              i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
 -              i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
 -              i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
 -
 -              /* some basic suspend checks */
 -
 -              iismod = readl(i2s->regs + S3C2412_IISMOD);
 -
 -              if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
 -                      pr_warning("%s: RXDMA active?\n", __func__);
 -
 -              if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
 -                      pr_warning("%s: TXDMA active?\n", __func__);
 -
 -              if (iismod & S3C2412_IISCON_IIS_ACTIVE)
 -                      pr_warning("%s: IIS active\n", __func__);
 -      }
 -
 -      return 0;
 -}
 -
 -static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
 -{
 -      struct s3c2412_i2s_info *i2s = &s3c2412_i2s;
 -
 -      pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
 -              dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
 -
 -      if (dai->active) {
 -              writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
 -              writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
 -              writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
 -
 -              writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
 -                     i2s->regs + S3C2412_IISFIC);
 -
 -              ndelay(250);
 -              writel(0x0, i2s->regs + S3C2412_IISFIC);
 -
 -      }
 -
        return 0;
  }
 -#else
 -#define s3c2412_i2s_suspend NULL
 -#define s3c2412_i2s_resume  NULL
 -#endif /* CONFIG_PM */
  
  #define S3C2412_I2S_RATES \
        (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
  
 -      .trigger        = s3c2412_i2s_trigger,
 -      .hw_params      = s3c2412_i2s_hw_params,
 -      .set_fmt        = s3c2412_i2s_set_fmt,
 -      .set_clkdiv     = s3c2412_i2s_set_clkdiv,
+ static struct snd_soc_dai_ops s3c2412_i2s_dai_ops = {
+       .set_sysclk     = s3c2412_i2s_set_sysclk,
+ };
  struct snd_soc_dai s3c2412_i2s_dai = {
 -      .name   = "s3c2412-i2s",
 -      .id     = 0,
 -      .probe  = s3c2412_i2s_probe,
 -      .suspend = s3c2412_i2s_suspend,
 -      .resume = s3c2412_i2s_resume,
 +      .name           = "s3c2412-i2s",
 +      .id             = 0,
 +      .probe          = s3c2412_i2s_probe,
        .playback = {
                .channels_min   = 2,
                .channels_max   = 2,
                .rates          = S3C2412_I2S_RATES,
                .formats        = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,
        },
-       .ops = {
-               .set_sysclk     = s3c2412_i2s_set_sysclk,
-       },
+       .ops = &s3c2412_i2s_dai_ops,
  };
  EXPORT_SYMBOL_GPL(s3c2412_i2s_dai);
  
  static int __init s3c2412_i2s_init(void)
  {
 -      return snd_soc_register_dai(&s3c2412_i2s_dai);
 +      return  s3c_i2sv2_register_dai(&s3c2412_i2s_dai);
  }
  module_init(s3c2412_i2s_init);
  
@@@ -181,6 -750,7 +183,6 @@@ static void __exit s3c2412_i2s_exit(voi
  }
  module_exit(s3c2412_i2s_exit);
  
 -
  /* Module information */
  MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
  MODULE_DESCRIPTION("S3C2412 I2S SoC Interface");
index 5c7f18a226456f0aaf39bca4ca7b439093410ee1,83ea623234e78c4e35acea9b5c725a69eb554133..3698f707c44d0e53fdbc916d717211bf97a279ca
@@@ -31,7 -31,7 +31,7 @@@
  #include <plat/regs-ac97.h>
  #include <mach/regs-gpio.h>
  #include <mach/regs-clock.h>
 -#include <mach/audio.h>
 +#include <plat/audio.h>
  #include <asm/dma.h>
  #include <mach/dma.h>
  
@@@ -355,6 -355,11 +355,16 @@@ static int s3c2443_ac97_mic_trigger(str
                SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
  
+ static struct snd_soc_dai_ops s3c2443_ac97_dai_ops = {
+       .hw_params      = s3c2443_ac97_hw_params,
+       .trigger        = s3c2443_ac97_trigger,
+ };
++static struct snd_soc_dai_ops s3c2443_ac97_mic_dai_ops = {
++      .hw_params      = s3c2443_ac97_hw_mic_params,
++      .trigger        = s3c2443_ac97_mic_trigger,
++};
++
  struct snd_soc_dai s3c2443_ac97_dai[] = {
  {
        .name = "s3c2443-ac97",
                .channels_max = 2,
                .rates = s3c2443_AC97_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .hw_params = s3c2443_ac97_hw_params,
-               .trigger = s3c2443_ac97_trigger},
+       .ops = &s3c2443_ac97_dai_ops,
  },
  {
        .name = "pxa2xx-ac97-mic",
                .channels_max = 1,
                .rates = s3c2443_AC97_RATES,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .hw_params = s3c2443_ac97_hw_mic_params,
-               .trigger = s3c2443_ac97_mic_trigger,},
 -      .ops = &s3c2443_ac97_dai_ops,
++      .ops = &s3c2443_ac97_mic_dai_ops,
  },
  };
  EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
index 407ccd7180fe6f5e3a4a2f2b94dd351ed62c233c,4473fb584c4c9c380605303aa5c32528dd11f9c3..cc066964dad6fe34eb7709115474c522ac0d80ba
  #include <mach/hardware.h>
  #include <mach/regs-gpio.h>
  #include <mach/regs-clock.h>
 -#include <mach/audio.h>
 +#include <plat/audio.h>
  #include <asm/dma.h>
  #include <mach/dma.h>
  
 -#include <asm/plat-s3c24xx/regs-iis.h>
 +#include <plat/regs-iis.h>
  
  #include "s3c24xx-pcm.h"
  #include "s3c24xx-i2s.h"
  
 -#define S3C24XX_I2S_DEBUG 0
 -#if S3C24XX_I2S_DEBUG
 -#define DBG(x...) printk(KERN_DEBUG "s3c24xx-i2s: " x)
 -#else
 -#define DBG(x...)
 -#endif
 -
  static struct s3c2410_dma_client s3c24xx_dma_client_out = {
        .name = "I2S PCM Stereo out"
  };
@@@ -77,13 -84,13 +77,13 @@@ static void s3c24xx_snd_txctrl(int on
        u32 iiscon;
        u32 iismod;
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
        iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
        iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
  
 -      DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
 +      pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
  
        if (on) {
                iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
                writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
        }
  
 -      DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
 +      pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
  }
  
  static void s3c24xx_snd_rxctrl(int on)
        u32 iiscon;
        u32 iismod;
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
        iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
        iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
  
 -      DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
 +      pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
  
        if (on) {
                iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
                writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
        }
  
 -      DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
 +      pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
  }
  
  /*
@@@ -170,7 -177,7 +170,7 @@@ static int s3c24xx_snd_lrsync(void
        u32 iiscon;
        int timeout = 50; /* 5ms */
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        while (1) {
                iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
   */
  static inline int s3c24xx_snd_is_clkmaster(void)
  {
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
  }
@@@ -203,10 -210,10 +203,10 @@@ static int s3c24xx_i2s_set_fmt(struct s
  {
        u32 iismod;
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 -      DBG("hw_params r: IISMOD: %lx \n", iismod);
 +      pr_debug("hw_params r: IISMOD: %x \n", iismod);
  
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
        }
  
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
 -      DBG("hw_params w: IISMOD: %lx \n", iismod);
 +      pr_debug("hw_params w: IISMOD: %x \n", iismod);
        return 0;
  }
  
@@@ -242,7 -249,7 +242,7 @@@ static int s3c24xx_i2s_hw_params(struc
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        u32 iismod;
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
  
        /* Working copies of register */
        iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 -      DBG("hw_params r: IISMOD: %lx\n", iismod);
 +      pr_debug("hw_params r: IISMOD: %x\n", iismod);
  
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
        }
  
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
 -      DBG("hw_params w: IISMOD: %lx\n", iismod);
 +      pr_debug("hw_params w: IISMOD: %x\n", iismod);
        return 0;
  }
  
@@@ -278,7 -285,7 +278,7 @@@ static int s3c24xx_i2s_trigger(struct s
  {
        int ret = 0;
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@@ -320,7 -327,7 +320,7 @@@ static int s3c24xx_i2s_set_sysclk(struc
  {
        u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        iismod &= ~S3C2440_IISMOD_MPLL;
  
@@@ -346,7 -353,7 +346,7 @@@ static int s3c24xx_i2s_set_clkdiv(struc
  {
        u32 reg;
  
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        switch (div_id) {
        case S3C24XX_DIV_BCLK:
@@@ -382,7 -389,7 +382,7 @@@ EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clock
  static int s3c24xx_i2s_probe(struct platform_device *pdev,
                             struct snd_soc_dai *dai)
  {
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
        if (s3c24xx_i2s.regs == NULL)
  
        s3c24xx_i2s.iis_clk = clk_get(&pdev->dev, "iis");
        if (s3c24xx_i2s.iis_clk == NULL) {
 -              DBG("failed to get iis_clock\n");
 +              pr_err("failed to get iis_clock\n");
                iounmap(s3c24xx_i2s.regs);
                return -ENODEV;
        }
  #ifdef CONFIG_PM
  static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
  {
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
  
        s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
        s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
  
  static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
  {
 -      DBG("Entered %s\n", __func__);
 +      pr_debug("Entered %s\n", __func__);
        clk_enable(s3c24xx_i2s.iis_clk);
  
        writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
  
+ static struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
+       .trigger        = s3c24xx_i2s_trigger,
+       .hw_params      = s3c24xx_i2s_hw_params,
+       .set_fmt        = s3c24xx_i2s_set_fmt,
+       .set_clkdiv     = s3c24xx_i2s_set_clkdiv,
+       .set_sysclk     = s3c24xx_i2s_set_sysclk,
+ };
  struct snd_soc_dai s3c24xx_i2s_dai = {
        .name = "s3c24xx-i2s",
        .id = 0,
                .channels_max = 2,
                .rates = S3C24XX_I2S_RATES,
                .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
-       .ops = {
-               .trigger = s3c24xx_i2s_trigger,
-               .hw_params = s3c24xx_i2s_hw_params,
-               .set_fmt = s3c24xx_i2s_set_fmt,
-               .set_clkdiv = s3c24xx_i2s_set_clkdiv,
-               .set_sysclk = s3c24xx_i2s_set_sysclk,
-       },
+       .ops = &s3c24xx_i2s_dai_ops,
  };
  EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);