const: constify remaining dev_pm_ops
[safe/jmp/linux-2.6] / sound / soc / soc-core.c
index 8313d52..0a6440c 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/platform_device.h>
+#include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -36,7 +37,6 @@
 #include <sound/initval.h>
 
 static DEFINE_MUTEX(pcm_mutex);
-static DEFINE_MUTEX(io_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
 #ifdef CONFIG_DEBUG_FS
@@ -80,6 +80,173 @@ static int run_delayed_work(struct delayed_work *dwork)
        return ret;
 }
 
+/* codec register dump */
+static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
+{
+       int i, step = 1, count = 0;
+
+       if (!codec->reg_cache_size)
+               return 0;
+
+       if (codec->reg_cache_step)
+               step = codec->reg_cache_step;
+
+       count += sprintf(buf, "%s registers\n", codec->name);
+       for (i = 0; i < codec->reg_cache_size; i += step) {
+               if (codec->readable_register && !codec->readable_register(i))
+                       continue;
+
+               count += sprintf(buf + count, "%2x: ", i);
+               if (count >= PAGE_SIZE - 1)
+                       break;
+
+               if (codec->display_register)
+                       count += codec->display_register(codec, buf + count,
+                                                        PAGE_SIZE - count, i);
+               else
+                       count += snprintf(buf + count, PAGE_SIZE - count,
+                                         "%4x", codec->read(codec, i));
+
+               if (count >= PAGE_SIZE - 1)
+                       break;
+
+               count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+               if (count >= PAGE_SIZE - 1)
+                       break;
+       }
+
+       /* Truncate count; min() would cause a warning */
+       if (count >= PAGE_SIZE)
+               count = PAGE_SIZE - 1;
+
+       return count;
+}
+static ssize_t codec_reg_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct snd_soc_device *devdata = dev_get_drvdata(dev);
+       return soc_codec_reg_show(devdata->card->codec, buf);
+}
+
+static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
+
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       ssize_t ret;
+       struct snd_soc_codec *codec = file->private_data;
+       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       ret = soc_codec_reg_show(codec, buf);
+       if (ret >= 0)
+               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+               const char __user *user_buf, size_t count, loff_t *ppos)
+{
+       char buf[32];
+       int buf_size;
+       char *start = buf;
+       unsigned long reg, value;
+       int step = 1;
+       struct snd_soc_codec *codec = file->private_data;
+
+       buf_size = min(count, (sizeof(buf)-1));
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+       buf[buf_size] = 0;
+
+       if (codec->reg_cache_step)
+               step = codec->reg_cache_step;
+
+       while (*start == ' ')
+               start++;
+       reg = simple_strtoul(start, &start, 16);
+       if ((reg >= codec->reg_cache_size) || (reg % step))
+               return -EINVAL;
+       while (*start == ' ')
+               start++;
+       if (strict_strtoul(start, 16, &value))
+               return -EINVAL;
+       codec->write(codec, reg, value);
+       return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+       .open = codec_reg_open_file,
+       .read = codec_reg_read_file,
+       .write = codec_reg_write_file,
+};
+
+static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+       char codec_root[128];
+
+       if (codec->dev)
+               snprintf(codec_root, sizeof(codec_root),
+                       "%s.%s", codec->name, dev_name(codec->dev));
+       else
+               snprintf(codec_root, sizeof(codec_root),
+                       "%s", codec->name);
+
+       codec->debugfs_codec_root = debugfs_create_dir(codec_root,
+                                                      debugfs_root);
+       if (!codec->debugfs_codec_root) {
+               printk(KERN_WARNING
+                      "ASoC: Failed to create codec debugfs directory\n");
+               return;
+       }
+
+       codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
+                                                codec->debugfs_codec_root,
+                                                codec, &codec_reg_fops);
+       if (!codec->debugfs_reg)
+               printk(KERN_WARNING
+                      "ASoC: Failed to create codec register debugfs file\n");
+
+       codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
+                                                    codec->debugfs_codec_root,
+                                                    &codec->pop_time);
+       if (!codec->debugfs_pop_time)
+               printk(KERN_WARNING
+                      "Failed to create pop time debugfs file\n");
+
+       codec->debugfs_dapm = debugfs_create_dir("dapm",
+                                                codec->debugfs_codec_root);
+       if (!codec->debugfs_dapm)
+               printk(KERN_WARNING
+                      "Failed to create DAPM debugfs directory\n");
+
+       snd_soc_dapm_debugfs_init(codec);
+}
+
+static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+       debugfs_remove_recursive(codec->debugfs_codec_root);
+}
+
+#else
+
+static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+
+static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 #ifdef CONFIG_SND_SOC_AC97_BUS
 /* unregister ac97 codec */
 static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -98,7 +265,7 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
        int err;
 
        codec->ac97->dev.bus = &ac97_bus_type;
-       codec->ac97->dev.parent = NULL;
+       codec->ac97->dev.parent = codec->card->dev;
        codec->ac97->dev.release = soc_ac97_device_release;
 
        dev_set_name(&codec->ac97->dev, "%d-%d:%s",
@@ -113,6 +280,35 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
+static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_card *card = socdev->card;
+       struct snd_soc_dai_link *machine = rtd->dai;
+       struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+       struct snd_soc_dai *codec_dai = machine->codec_dai;
+       int ret;
+
+       if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates ||
+           machine->symmetric_rates) {
+               dev_dbg(card->dev, "Symmetry forces %dHz rate\n", 
+                       machine->rate);
+
+               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                                  SNDRV_PCM_HW_PARAM_RATE,
+                                                  machine->rate,
+                                                  machine->rate);
+               if (ret < 0) {
+                       dev_err(card->dev,
+                               "Unable to apply rate symmetry constraint: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
  * then initialized and any private data can be allocated. This also calls
@@ -133,8 +329,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        mutex_lock(&pcm_mutex);
 
        /* startup the audio subsystem */
-       if (cpu_dai->ops.startup) {
-               ret = cpu_dai->ops.startup(substream, cpu_dai);
+       if (cpu_dai->ops->startup) {
+               ret = cpu_dai->ops->startup(substream, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't open interface %s\n",
                                cpu_dai->name);
@@ -150,8 +346,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                }
        }
 
-       if (codec_dai->ops.startup) {
-               ret = codec_dai->ops.startup(substream, codec_dai);
+       if (codec_dai->ops->startup) {
+               ret = codec_dai->ops->startup(substream, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't open codec %s\n",
                                codec_dai->name);
@@ -221,6 +417,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                goto machine_err;
        }
 
+       /* Symmetry only applies if we've already got an active stream. */
+       if (cpu_dai->active || codec_dai->active) {
+               ret = soc_pcm_apply_symmetry(substream);
+               if (ret != 0)
+                       goto machine_err;
+       }
+
        pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
        pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
        pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
@@ -234,7 +437,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                cpu_dai->capture.active = codec_dai->capture.active = 1;
        cpu_dai->active = codec_dai->active = 1;
        cpu_dai->runtime = runtime;
-       socdev->codec->active++;
+       card->codec->active++;
        mutex_unlock(&pcm_mutex);
        return 0;
 
@@ -247,8 +450,8 @@ codec_dai_err:
                platform->pcm_ops->close(substream);
 
 platform_err:
-       if (cpu_dai->ops.shutdown)
-               cpu_dai->ops.shutdown(substream, cpu_dai);
+       if (cpu_dai->ops->shutdown)
+               cpu_dai->ops->shutdown(substream, cpu_dai);
 out:
        mutex_unlock(&pcm_mutex);
        return ret;
@@ -263,8 +466,7 @@ static void close_delayed_work(struct work_struct *work)
 {
        struct snd_soc_card *card = container_of(work, struct snd_soc_card,
                                                 delayed_work.work);
-       struct snd_soc_device *socdev = card->socdev;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = card->codec;
        struct snd_soc_dai *codec_dai;
        int i;
 
@@ -279,27 +481,10 @@ static void close_delayed_work(struct work_struct *work)
 
                /* are we waiting on this codec DAI stream */
                if (codec_dai->pop_wait == 1) {
-
-                       /* Reduce power if no longer active */
-                       if (codec->active == 0) {
-                               pr_debug("pop wq D1 %s %s\n", codec->name,
-                                        codec_dai->playback.stream_name);
-                               snd_soc_dapm_set_bias_level(socdev,
-                                       SND_SOC_BIAS_PREPARE);
-                       }
-
                        codec_dai->pop_wait = 0;
                        snd_soc_dapm_stream_event(codec,
                                codec_dai->playback.stream_name,
                                SND_SOC_DAPM_STREAM_STOP);
-
-                       /* Fall into standby if no longer active */
-                       if (codec->active == 0) {
-                               pr_debug("pop wq D3 %s %s\n", codec->name,
-                                        codec_dai->playback.stream_name);
-                               snd_soc_dapm_set_bias_level(socdev,
-                                       SND_SOC_BIAS_STANDBY);
-                       }
                }
        }
        mutex_unlock(&pcm_mutex);
@@ -319,7 +504,7 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = card->codec;
 
        mutex_lock(&pcm_mutex);
 
@@ -340,11 +525,11 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                snd_soc_dai_digital_mute(codec_dai, 1);
 
-       if (cpu_dai->ops.shutdown)
-               cpu_dai->ops.shutdown(substream, cpu_dai);
+       if (cpu_dai->ops->shutdown)
+               cpu_dai->ops->shutdown(substream, cpu_dai);
 
-       if (codec_dai->ops.shutdown)
-               codec_dai->ops.shutdown(substream, codec_dai);
+       if (codec_dai->ops->shutdown)
+               codec_dai->ops->shutdown(substream, codec_dai);
 
        if (machine->ops && machine->ops->shutdown)
                machine->ops->shutdown(substream);
@@ -363,10 +548,6 @@ static int soc_codec_close(struct snd_pcm_substream *substream)
                snd_soc_dapm_stream_event(codec,
                        codec_dai->capture.stream_name,
                        SND_SOC_DAPM_STREAM_STOP);
-
-               if (codec->active == 0 && codec_dai->pop_wait == 0)
-                       snd_soc_dapm_set_bias_level(socdev,
-                                               SND_SOC_BIAS_STANDBY);
        }
 
        mutex_unlock(&pcm_mutex);
@@ -387,7 +568,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = card->codec;
        int ret = 0;
 
        mutex_lock(&pcm_mutex);
@@ -408,16 +589,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
-       if (codec_dai->ops.prepare) {
-               ret = codec_dai->ops.prepare(substream, codec_dai);
+       if (codec_dai->ops->prepare) {
+               ret = codec_dai->ops->prepare(substream, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: codec DAI prepare error\n");
                        goto out;
                }
        }
 
-       if (cpu_dai->ops.prepare) {
-               ret = cpu_dai->ops.prepare(substream, cpu_dai);
+       if (cpu_dai->ops->prepare) {
+               ret = cpu_dai->ops->prepare(substream, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: cpu DAI prepare error\n");
                        goto out;
@@ -431,36 +612,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                cancel_delayed_work(&card->delayed_work);
        }
 
-       /* do we need to power up codec */
-       if (codec->bias_level != SND_SOC_BIAS_ON) {
-               snd_soc_dapm_set_bias_level(socdev,
-                                           SND_SOC_BIAS_PREPARE);
-
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->playback.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-               else
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->capture.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-
-               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
-               snd_soc_dai_digital_mute(codec_dai, 0);
-
-       } else {
-               /* codec already powered - power on widgets */
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->playback.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
-               else
-                       snd_soc_dapm_stream_event(codec,
-                                       codec_dai->capture.stream_name,
-                                       SND_SOC_DAPM_STREAM_START);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_dapm_stream_event(codec,
+                                         codec_dai->playback.stream_name,
+                                         SND_SOC_DAPM_STREAM_START);
+       else
+               snd_soc_dapm_stream_event(codec,
+                                         codec_dai->capture.stream_name,
+                                         SND_SOC_DAPM_STREAM_START);
 
-               snd_soc_dai_digital_mute(codec_dai, 0);
-       }
+       snd_soc_dai_digital_mute(codec_dai, 0);
 
 out:
        mutex_unlock(&pcm_mutex);
@@ -494,8 +655,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
-       if (codec_dai->ops.hw_params) {
-               ret = codec_dai->ops.hw_params(substream, params, codec_dai);
+       if (codec_dai->ops->hw_params) {
+               ret = codec_dai->ops->hw_params(substream, params, codec_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: can't set codec %s hw params\n",
                                codec_dai->name);
@@ -503,8 +664,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
-       if (cpu_dai->ops.hw_params) {
-               ret = cpu_dai->ops.hw_params(substream, params, cpu_dai);
+       if (cpu_dai->ops->hw_params) {
+               ret = cpu_dai->ops->hw_params(substream, params, cpu_dai);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: interface %s hw params failed\n",
                                cpu_dai->name);
@@ -521,17 +682,19 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
+       machine->rate = params_rate(params);
+
 out:
        mutex_unlock(&pcm_mutex);
        return ret;
 
 platform_err:
-       if (cpu_dai->ops.hw_free)
-               cpu_dai->ops.hw_free(substream, cpu_dai);
+       if (cpu_dai->ops->hw_free)
+               cpu_dai->ops->hw_free(substream, cpu_dai);
 
 interface_err:
-       if (codec_dai->ops.hw_free)
-               codec_dai->ops.hw_free(substream, codec_dai);
+       if (codec_dai->ops->hw_free)
+               codec_dai->ops->hw_free(substream, codec_dai);
 
 codec_err:
        if (machine->ops && machine->ops->hw_free)
@@ -553,7 +716,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *cpu_dai = machine->cpu_dai;
        struct snd_soc_dai *codec_dai = machine->codec_dai;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = card->codec;
 
        mutex_lock(&pcm_mutex);
 
@@ -570,11 +733,11 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
                platform->pcm_ops->hw_free(substream);
 
        /* now free hw params for the DAI's  */
-       if (codec_dai->ops.hw_free)
-               codec_dai->ops.hw_free(substream, codec_dai);
+       if (codec_dai->ops->hw_free)
+               codec_dai->ops->hw_free(substream, codec_dai);
 
-       if (cpu_dai->ops.hw_free)
-               cpu_dai->ops.hw_free(substream, cpu_dai);
+       if (cpu_dai->ops->hw_free)
+               cpu_dai->ops->hw_free(substream, cpu_dai);
 
        mutex_unlock(&pcm_mutex);
        return 0;
@@ -591,8 +754,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        struct snd_soc_dai *codec_dai = machine->codec_dai;
        int ret;
 
-       if (codec_dai->ops.trigger) {
-               ret = codec_dai->ops.trigger(substream, cmd, codec_dai);
+       if (codec_dai->ops->trigger) {
+               ret = codec_dai->ops->trigger(substream, cmd, codec_dai);
                if (ret < 0)
                        return ret;
        }
@@ -603,8 +766,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                        return ret;
        }
 
-       if (cpu_dai->ops.trigger) {
-               ret = cpu_dai->ops.trigger(substream, cmd, cpu_dai);
+       if (cpu_dai->ops->trigger) {
+               ret = cpu_dai->ops->trigger(substream, cmd, cpu_dai);
                if (ret < 0)
                        return ret;
        }
@@ -623,15 +786,22 @@ static struct snd_pcm_ops soc_pcm_ops = {
 
 #ifdef CONFIG_PM
 /* powers down audio subsystem for suspend */
-static int soc_suspend(struct platform_device *pdev, pm_message_t state)
+static int soc_suspend(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_card *card = socdev->card;
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = card->codec;
        int i;
 
+       /* If the initialization of this soc device failed, there is no codec
+        * associated with it. Just bail out in this case.
+        */
+       if (!codec)
+               return 0;
+
        /* Due to the resume being scheduled into a workqueue we could
        * suspend before that's finished - wait for it to complete.
         */
@@ -645,8 +815,8 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        /* mute any active DAC's */
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
-               if (dai->ops.digital_mute && dai->playback.active)
-                       dai->ops.digital_mute(dai, 1);
+               if (dai->ops->digital_mute && dai->playback.active)
+                       dai->ops->digital_mute(dai, 1);
        }
 
        /* suspend all pcms */
@@ -654,7 +824,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
                snd_pcm_suspend_all(card->dai_link[i].pcm);
 
        if (card->suspend_pre)
-               card->suspend_pre(pdev, state);
+               card->suspend_pre(pdev, PMSG_SUSPEND);
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
@@ -680,7 +850,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        }
 
        if (codec_dev->suspend)
-               codec_dev->suspend(pdev, state);
+               codec_dev->suspend(pdev, PMSG_SUSPEND);
 
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
@@ -689,7 +859,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        }
 
        if (card->suspend_post)
-               card->suspend_post(pdev, state);
+               card->suspend_post(pdev, PMSG_SUSPEND);
 
        return 0;
 }
@@ -705,7 +875,7 @@ static void soc_resume_deferred(struct work_struct *work)
        struct snd_soc_device *socdev = card->socdev;
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = card->codec;
        struct platform_device *pdev = to_platform_device(socdev->dev);
        int i;
 
@@ -741,8 +911,8 @@ static void soc_resume_deferred(struct work_struct *work)
        /* unmute any active DACs */
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *dai = card->dai_link[i].codec_dai;
-               if (dai->ops.digital_mute && dai->playback.active)
-                       dai->ops.digital_mute(dai, 0);
+               if (dai->ops->digital_mute && dai->playback.active)
+                       dai->ops->digital_mute(dai, 0);
        }
 
        for (i = 0; i < card->num_links; i++) {
@@ -763,30 +933,44 @@ static void soc_resume_deferred(struct work_struct *work)
 }
 
 /* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+static int soc_resume(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_card *card = socdev->card;
+       struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
 
-       dev_dbg(socdev->dev, "scheduling resume work\n");
-
-       if (!schedule_work(&card->deferred_resume_work))
-               dev_err(socdev->dev, "resume work item may be lost\n");
+       /* AC97 devices might have other drivers hanging off them so
+        * need to resume immediately.  Other drivers don't have that
+        * problem and may take a substantial amount of time to resume
+        * due to I/O costs and anti-pop so handle them out of line.
+        */
+       if (cpu_dai->ac97_control) {
+               dev_dbg(socdev->dev, "Resuming AC97 immediately\n");
+               soc_resume_deferred(&card->deferred_resume_work);
+       } else {
+               dev_dbg(socdev->dev, "Scheduling resume work\n");
+               if (!schedule_work(&card->deferred_resume_work))
+                       dev_err(socdev->dev, "resume work item may be lost\n");
+       }
 
        return 0;
 }
-
 #else
 #define soc_suspend    NULL
 #define soc_resume     NULL
 #endif
 
+static struct snd_soc_dai_ops null_dai_ops = {
+};
+
 static void snd_soc_instantiate_card(struct snd_soc_card *card)
 {
        struct platform_device *pdev = container_of(card->dev,
                                                    struct platform_device,
                                                    dev);
        struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+       struct snd_soc_codec *codec;
        struct snd_soc_platform *platform;
        struct snd_soc_dai *dai;
        int i, found, ret, ac97;
@@ -824,6 +1008,11 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                        ac97 = 1;
        }
 
+       for (i = 0; i < card->num_links; i++) {
+               if (!card->dai_link[i].codec_dai->ops)
+                       card->dai_link[i].codec_dai->ops = &null_dai_ops;
+       }
+
        /* If we have AC97 in the system then don't wait for the
         * codec.  This will need revisiting if we have to handle
         * systems with mixed AC97 and non-AC97 parts.  Only check for
@@ -870,6 +1059,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                if (ret < 0)
                        goto cpu_dai_err;
        }
+       codec = card->codec;
 
        if (platform->probe) {
                ret = platform->probe(pdev);
@@ -884,10 +1074,69 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 #endif
 
+       for (i = 0; i < card->num_links; i++) {
+               if (card->dai_link[i].init) {
+                       ret = card->dai_link[i].init(codec);
+                       if (ret < 0) {
+                               printk(KERN_ERR "asoc: failed to init %s\n",
+                                       card->dai_link[i].stream_name);
+                               continue;
+                       }
+               }
+               if (card->dai_link[i].codec_dai->ac97_control)
+                       ac97 = 1;
+       }
+
+       snprintf(codec->card->shortname, sizeof(codec->card->shortname),
+                "%s",  card->name);
+       snprintf(codec->card->longname, sizeof(codec->card->longname),
+                "%s (%s)", card->name, codec->name);
+
+       /* Make sure all DAPM widgets are instantiated */
+       snd_soc_dapm_new_widgets(codec);
+
+       ret = snd_card_register(codec->card);
+       if (ret < 0) {
+               printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
+                               codec->name);
+               goto card_err;
+       }
+
+       mutex_lock(&codec->mutex);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+       /* Only instantiate AC97 if not already done by the adaptor
+        * for the generic AC97 subsystem.
+        */
+       if (ac97 && strcmp(codec->name, "AC97") != 0) {
+               ret = soc_ac97_dev_register(codec);
+               if (ret < 0) {
+                       printk(KERN_ERR "asoc: AC97 device register failed\n");
+                       snd_card_free(codec->card);
+                       mutex_unlock(&codec->mutex);
+                       goto card_err;
+               }
+       }
+#endif
+
+       ret = snd_soc_dapm_sys_add(card->socdev->dev);
+       if (ret < 0)
+               printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
+
+       ret = device_create_file(card->socdev->dev, &dev_attr_codec_reg);
+       if (ret < 0)
+               printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
+
+       soc_init_codec_debugfs(codec);
+       mutex_unlock(&codec->mutex);
+
        card->instantiated = 1;
 
        return;
 
+card_err:
+       if (platform->remove)
+               platform->remove(pdev);
+
 platform_err:
        if (codec_dev->remove)
                codec_dev->remove(pdev);
@@ -944,6 +1193,9 @@ static int soc_remove(struct platform_device *pdev)
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 
+       if (!card->instantiated)
+               return 0;
+
        run_delayed_work(&card->delayed_work);
 
        if (platform->remove)
@@ -966,24 +1218,47 @@ static int soc_remove(struct platform_device *pdev)
        return 0;
 }
 
+static int soc_poweroff(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = socdev->card;
+
+       if (!card->instantiated)
+               return 0;
+
+       /* Flush out pmdown_time work - we actually do want to run it
+        * now, we're shutting down so no imminent restart. */
+       run_delayed_work(&card->delayed_work);
+
+       snd_soc_dapm_shutdown(socdev);
+
+       return 0;
+}
+
+static const struct dev_pm_ops soc_pm_ops = {
+       .suspend = soc_suspend,
+       .resume = soc_resume,
+       .poweroff = soc_poweroff,
+};
+
 /* ASoC platform driver */
 static struct platform_driver soc_driver = {
        .driver         = {
                .name           = "soc-audio",
                .owner          = THIS_MODULE,
+               .pm             = &soc_pm_ops,
        },
        .probe          = soc_probe,
        .remove         = soc_remove,
-       .suspend        = soc_suspend,
-       .resume         = soc_resume,
 };
 
 /* create a new pcm */
 static int soc_new_pcm(struct snd_soc_device *socdev,
        struct snd_soc_dai_link *dai_link, int num)
 {
-       struct snd_soc_codec *codec = socdev->codec;
        struct snd_soc_card *card = socdev->card;
+       struct snd_soc_codec *codec = card->codec;
        struct snd_soc_platform *platform = card->platform;
        struct snd_soc_dai *codec_dai = dai_link->codec_dai;
        struct snd_soc_dai *cpu_dai = dai_link->cpu_dai;
@@ -998,7 +1273,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
 
        rtd->dai = dai_link;
        rtd->socdev = socdev;
-       codec_dai->codec = socdev->codec;
+       codec_dai->codec = card->codec;
 
        /* check client and interface hw capabilities */
        sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
@@ -1047,148 +1322,22 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
        return ret;
 }
 
-/* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_device *devdata, char *buf)
+/**
+ * snd_soc_codec_volatile_register: Report if a register is volatile.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indiciating if a CODEC register is volatile.
+ */
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
 {
-       struct snd_soc_codec *codec = devdata->codec;
-       int i, step = 1, count = 0;
-
-       if (!codec->reg_cache_size)
+       if (codec->volatile_register)
+               return codec->volatile_register(reg);
+       else
                return 0;
-
-       if (codec->reg_cache_step)
-               step = codec->reg_cache_step;
-
-       count += sprintf(buf, "%s registers\n", codec->name);
-       for (i = 0; i < codec->reg_cache_size; i += step) {
-               count += sprintf(buf + count, "%2x: ", i);
-               if (count >= PAGE_SIZE - 1)
-                       break;
-
-               if (codec->display_register)
-                       count += codec->display_register(codec, buf + count,
-                                                        PAGE_SIZE - count, i);
-               else
-                       count += snprintf(buf + count, PAGE_SIZE - count,
-                                         "%4x", codec->read(codec, i));
-
-               if (count >= PAGE_SIZE - 1)
-                       break;
-
-               count += snprintf(buf + count, PAGE_SIZE - count, "\n");
-               if (count >= PAGE_SIZE - 1)
-                       break;
-       }
-
-       /* Truncate count; min() would cause a warning */
-       if (count >= PAGE_SIZE)
-               count = PAGE_SIZE - 1;
-
-       return count;
-}
-static ssize_t codec_reg_show(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       struct snd_soc_device *devdata = dev_get_drvdata(dev);
-       return soc_codec_reg_show(devdata, buf);
-}
-
-static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_reg_open_file(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return 0;
-}
-
-static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
-                              size_t count, loff_t *ppos)
-{
-       ssize_t ret;
-       struct snd_soc_codec *codec = file->private_data;
-       struct device *card_dev = codec->card->dev;
-       struct snd_soc_device *devdata = card_dev->driver_data;
-       char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-       ret = soc_codec_reg_show(devdata, buf);
-       if (ret >= 0)
-               ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
-       kfree(buf);
-       return ret;
-}
-
-static ssize_t codec_reg_write_file(struct file *file,
-               const char __user *user_buf, size_t count, loff_t *ppos)
-{
-       char buf[32];
-       int buf_size;
-       char *start = buf;
-       unsigned long reg, value;
-       int step = 1;
-       struct snd_soc_codec *codec = file->private_data;
-
-       buf_size = min(count, (sizeof(buf)-1));
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-       buf[buf_size] = 0;
-
-       if (codec->reg_cache_step)
-               step = codec->reg_cache_step;
-
-       while (*start == ' ')
-               start++;
-       reg = simple_strtoul(start, &start, 16);
-       if ((reg >= codec->reg_cache_size) || (reg % step))
-               return -EINVAL;
-       while (*start == ' ')
-               start++;
-       if (strict_strtoul(start, 16, &value))
-               return -EINVAL;
-       codec->write(codec, reg, value);
-       return buf_size;
-}
-
-static const struct file_operations codec_reg_fops = {
-       .open = codec_reg_open_file,
-       .read = codec_reg_read_file,
-       .write = codec_reg_write_file,
-};
-
-static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-       codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
-                                                debugfs_root, codec,
-                                                &codec_reg_fops);
-       if (!codec->debugfs_reg)
-               printk(KERN_WARNING
-                      "ASoC: Failed to create codec register debugfs file\n");
-
-       codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
-                                                    debugfs_root,
-                                                    &codec->pop_time);
-       if (!codec->debugfs_pop_time)
-               printk(KERN_WARNING
-                      "Failed to create pop time debugfs file\n");
-}
-
-static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-       debugfs_remove(codec->debugfs_pop_time);
-       debugfs_remove(codec->debugfs_reg);
 }
-
-#else
-
-static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-
-static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-#endif
+EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
 
 /**
  * snd_soc_new_ac97_codec - initailise AC97 device
@@ -1252,24 +1401,46 @@ EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
  * Returns 1 for change else 0.
  */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned short mask, unsigned short value)
+                               unsigned int mask, unsigned int value)
 {
        int change;
-       unsigned short old, new;
+       unsigned int old, new;
 
-       mutex_lock(&io_mutex);
        old = snd_soc_read(codec, reg);
        new = (old & ~mask) | value;
        change = old != new;
        if (change)
                snd_soc_write(codec, reg, new);
 
-       mutex_unlock(&io_mutex);
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_update_bits);
 
 /**
+ * snd_soc_update_bits_locked - update codec register bits
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
+ *
+ * Writes new register value, and takes the codec mutex.
+ *
+ * Returns 1 for change else 0.
+ */
+static int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+                               unsigned short reg, unsigned int mask,
+                               unsigned int value)
+{
+       int change;
+
+       mutex_lock(&codec->mutex);
+       change = snd_soc_update_bits(codec, reg, mask, value);
+       mutex_unlock(&codec->mutex);
+
+       return change;
+}
+
+/**
  * snd_soc_test_bits - test register for change
  * @codec: audio codec
  * @reg: codec register
@@ -1282,16 +1453,14 @@ EXPORT_SYMBOL_GPL(snd_soc_update_bits);
  * Returns 1 for change else 0.
  */
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
-                               unsigned short mask, unsigned short value)
+                               unsigned int mask, unsigned int value)
 {
        int change;
-       unsigned short old, new;
+       unsigned int old, new;
 
-       mutex_lock(&io_mutex);
        old = snd_soc_read(codec, reg);
        new = (old & ~mask) | value;
        change = old != new;
-       mutex_unlock(&io_mutex);
 
        return change;
 }
@@ -1309,21 +1478,22 @@ EXPORT_SYMBOL_GPL(snd_soc_test_bits);
  */
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
 {
-       struct snd_soc_codec *codec = socdev->codec;
        struct snd_soc_card *card = socdev->card;
-       int ret = 0, i;
+       struct snd_soc_codec *codec = card->codec;
+       int ret, i;
 
        mutex_lock(&codec->mutex);
 
        /* register a sound card */
-       codec->card = snd_card_new(idx, xid, codec->owner, 0);
-       if (!codec->card) {
+       ret = snd_card_create(idx, xid, codec->owner, 0, &codec->card);
+       if (ret < 0) {
                printk(KERN_ERR "asoc: can't create sound card for codec %s\n",
                        codec->name);
                mutex_unlock(&codec->mutex);
-               return -ENODEV;
+               return ret;
        }
 
+       codec->socdev = socdev;
        codec->card->dev = socdev->dev;
        codec->card->private_data = codec;
        strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
@@ -1337,80 +1507,16 @@ int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid)
                        mutex_unlock(&codec->mutex);
                        return ret;
                }
-       }
-
-       mutex_unlock(&codec->mutex);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
-
-/**
- * snd_soc_init_card - register sound card
- * @socdev: the SoC audio device
- *
- * Register a SoC sound card. Also registers an AC97 device if the
- * codec is AC97 for ad hoc devices.
- *
- * Returns 0 for success, else error.
- */
-int snd_soc_init_card(struct snd_soc_device *socdev)
-{
-       struct snd_soc_codec *codec = socdev->codec;
-       struct snd_soc_card *card = socdev->card;
-       int ret = 0, i, ac97 = 0, err = 0;
-
-       for (i = 0; i < card->num_links; i++) {
-               if (card->dai_link[i].init) {
-                       err = card->dai_link[i].init(codec);
-                       if (err < 0) {
-                               printk(KERN_ERR "asoc: failed to init %s\n",
-                                       card->dai_link[i].stream_name);
-                               continue;
-                       }
+               if (card->dai_link[i].codec_dai->ac97_control) {
+                       snd_ac97_dev_add_pdata(codec->ac97,
+                               card->dai_link[i].cpu_dai->ac97_pdata);
                }
-               if (card->dai_link[i].codec_dai->ac97_control)
-                       ac97 = 1;
        }
-       snprintf(codec->card->shortname, sizeof(codec->card->shortname),
-                "%s",  card->name);
-       snprintf(codec->card->longname, sizeof(codec->card->longname),
-                "%s (%s)", card->name, codec->name);
 
-       ret = snd_card_register(codec->card);
-       if (ret < 0) {
-               printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
-                               codec->name);
-               goto out;
-       }
-
-       mutex_lock(&codec->mutex);
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       if (ac97) {
-               ret = soc_ac97_dev_register(codec);
-               if (ret < 0) {
-                       printk(KERN_ERR "asoc: AC97 device register failed\n");
-                       snd_card_free(codec->card);
-                       mutex_unlock(&codec->mutex);
-                       goto out;
-               }
-       }
-#endif
-
-       err = snd_soc_dapm_sys_add(socdev->dev);
-       if (err < 0)
-               printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
-
-       err = device_create_file(socdev->dev, &dev_attr_codec_reg);
-       if (err < 0)
-               printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
-
-       soc_init_codec_debugfs(socdev->codec);
        mutex_unlock(&codec->mutex);
-
-out:
        return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_init_card);
+EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
 
 /**
  * snd_soc_free_pcms - free sound card and pcms
@@ -1421,18 +1527,19 @@ EXPORT_SYMBOL_GPL(snd_soc_init_card);
  */
 void snd_soc_free_pcms(struct snd_soc_device *socdev)
 {
-       struct snd_soc_codec *codec = socdev->codec;
+       struct snd_soc_codec *codec = socdev->card->codec;
 #ifdef CONFIG_SND_SOC_AC97_BUS
        struct snd_soc_dai *codec_dai;
        int i;
 #endif
 
        mutex_lock(&codec->mutex);
-       soc_cleanup_codec_debugfs(socdev->codec);
+       soc_cleanup_codec_debugfs(codec);
 #ifdef CONFIG_SND_SOC_AC97_BUS
        for (i = 0; i < codec->num_dai; i++) {
                codec_dai = &codec->dai[i];
-               if (codec_dai->ac97_control && codec->ac97) {
+               if (codec_dai->ac97_control && codec->ac97 &&
+                   strcmp(codec->name, "AC97") != 0) {
                        soc_ac97_dev_unregister(codec);
                        goto free_card;
                }
@@ -1566,7 +1673,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val, bitmask;
+       unsigned int val, bitmask;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1595,8 +1702,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val;
-       unsigned short mask, bitmask;
+       unsigned int val;
+       unsigned int mask, bitmask;
 
        for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
                ;
@@ -1611,7 +1718,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
                mask |= (bitmask - 1) << e->shift_r;
        }
 
-       return snd_soc_update_bits(codec, e->reg, mask, val);
+       return snd_soc_update_bits_locked(codec, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
@@ -1632,7 +1739,7 @@ int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short reg_val, val, mux;
+       unsigned int reg_val, val, mux;
 
        reg_val = snd_soc_read(codec, e->reg);
        val = (reg_val >> e->shift_l) & e->mask;
@@ -1671,8 +1778,8 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned short val;
-       unsigned short mask;
+       unsigned int val;
+       unsigned int mask;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
@@ -1685,7 +1792,7 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
                mask |= e->mask << e->shift_r;
        }
 
-       return snd_soc_update_bits(codec, e->reg, mask, val);
+       return snd_soc_update_bits_locked(codec, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
 
@@ -1730,7 +1837,7 @@ int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
 {
        int max = kcontrol->private_value;
 
-       if (max == 1)
+       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1760,7 +1867,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
 
-       if (max == 1)
+       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1832,7 +1939,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned short val, val2, val_mask;
+       unsigned int val, val2, val_mask;
 
        val = (ucontrol->value.integer.value[0] & mask);
        if (invert)
@@ -1846,7 +1953,7 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
                val_mask |= mask << rshift;
                val |= val2 << rshift;
        }
-       return snd_soc_update_bits(codec, reg, val_mask, val);
+       return snd_soc_update_bits_locked(codec, reg, val_mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
@@ -1867,7 +1974,7 @@ int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        int max = mc->max;
 
-       if (max == 1)
+       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1898,7 +2005,7 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
        unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
        int max = mc->max;
-       unsigned int mask = (1<<fls(max))-1;
+       unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
 
        ucontrol->value.integer.value[0] =
@@ -1938,7 +2045,7 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        int err;
-       unsigned short val, val2, val_mask;
+       unsigned int val, val2, val_mask;
 
        val_mask = mask << shift;
        val = (ucontrol->value.integer.value[0] & mask);
@@ -1952,11 +2059,11 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
        val = val << shift;
        val2 = val2 << shift;
 
-       err = snd_soc_update_bits(codec, reg, val_mask, val);
+       err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
        if (err < 0)
                return err;
 
-       err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+       err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
        return err;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
@@ -2030,12 +2137,12 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
        int min = mc->min;
-       unsigned short val;
+       unsigned int val;
 
        val = (ucontrol->value.integer.value[0]+min) & 0xff;
        val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
 
-       return snd_soc_update_bits(codec, reg, 0xffff, val);
+       return snd_soc_update_bits_locked(codec, reg, 0xffff, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
@@ -2051,8 +2158,8 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        unsigned int freq, int dir)
 {
-       if (dai->ops.set_sysclk)
-               return dai->ops.set_sysclk(dai, clk_id, freq, dir);
+       if (dai->ops && dai->ops->set_sysclk)
+               return dai->ops->set_sysclk(dai, clk_id, freq, dir);
        else
                return -EINVAL;
 }
@@ -2071,8 +2178,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
        int div_id, int div)
 {
-       if (dai->ops.set_clkdiv)
-               return dai->ops.set_clkdiv(dai, div_id, div);
+       if (dai->ops && dai->ops->set_clkdiv)
+               return dai->ops->set_clkdiv(dai, div_id, div);
        else
                return -EINVAL;
 }
@@ -2082,16 +2189,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
  * snd_soc_dai_set_pll - configure DAI PLL.
  * @dai: DAI
  * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
  * @freq_in: PLL input clock frequency in Hz
  * @freq_out: requested PLL output clock frequency in Hz
  *
  * Configures and enables PLL to generate output clock based on input clock.
  */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-       int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+       unsigned int freq_in, unsigned int freq_out)
 {
-       if (dai->ops.set_pll)
-               return dai->ops.set_pll(dai, pll_id, freq_in, freq_out);
+       if (dai->ops && dai->ops->set_pll)
+               return dai->ops->set_pll(dai, pll_id, source,
+                                        freq_in, freq_out);
        else
                return -EINVAL;
 }
@@ -2106,8 +2215,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
  */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-       if (dai->ops.set_fmt)
-               return dai->ops.set_fmt(dai, fmt);
+       if (dai->ops && dai->ops->set_fmt)
+               return dai->ops->set_fmt(dai, fmt);
        else
                return -EINVAL;
 }
@@ -2116,23 +2225,50 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 /**
  * snd_soc_dai_set_tdm_slot - configure DAI TDM.
  * @dai: DAI
- * @mask: DAI specific mask representing used slots.
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
  * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
  *
  * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
  * specific.
  */
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
-       unsigned int mask, int slots)
+       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
 {
-       if (dai->ops.set_sysclk)
-               return dai->ops.set_tdm_slot(dai, mask, slots);
+       if (dai->ops && dai->ops->set_tdm_slot)
+               return dai->ops->set_tdm_slot(dai, tx_mask, rx_mask,
+                               slots, slot_width);
        else
                return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
 /**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ *           0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ *           0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+       unsigned int tx_num, unsigned int *tx_slot,
+       unsigned int rx_num, unsigned int *rx_slot)
+{
+       if (dai->ops && dai->ops->set_channel_map)
+               return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+                       rx_num, rx_slot);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
+/**
  * snd_soc_dai_set_tristate - configure DAI system or master clock.
  * @dai: DAI
  * @tristate: tristate enable
@@ -2141,8 +2277,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
  */
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
 {
-       if (dai->ops.set_sysclk)
-               return dai->ops.set_tristate(dai, tristate);
+       if (dai->ops && dai->ops->set_tristate)
+               return dai->ops->set_tristate(dai, tristate);
        else
                return -EINVAL;
 }
@@ -2157,8 +2293,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
  */
 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
 {
-       if (dai->ops.digital_mute)
-               return dai->ops.digital_mute(dai, mute);
+       if (dai->ops && dai->ops->digital_mute)
+               return dai->ops->digital_mute(dai, mute);
        else
                return -EINVAL;
 }
@@ -2225,6 +2361,9 @@ int snd_soc_register_dai(struct snd_soc_dai *dai)
        if (!dai->dev)
                printk(KERN_WARNING "No device for DAI %s\n", dai->name);
 
+       if (!dai->ops)
+               dai->ops = &null_dai_ops;
+
        INIT_LIST_HEAD(&dai->list);
 
        mutex_lock(&client_mutex);
@@ -2332,6 +2471,39 @@ void snd_soc_unregister_platform(struct snd_soc_platform *platform)
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
 
+static u64 codec_format_map[] = {
+       SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE,
+       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE,
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE,
+       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE,
+       SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
+       SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE,
+       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
+       SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
+       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE,
+       SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE,
+       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE,
+       SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE,
+       SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE,
+       SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE,
+       SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+       | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
+};
+
+/* Fix up the DAI formats for endianness: codecs don't actually see
+ * the endianness of the data but we're using the CPU format
+ * definitions which do need to include endianness so we ensure that
+ * codec DAIs always have both big and little endian variants set.
+ */
+static void fixup_codec_formats(struct snd_soc_pcm_stream *stream)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(codec_format_map); i++)
+               if (stream->formats & codec_format_map[i])
+                       stream->formats |= codec_format_map[i];
+}
+
 /**
  * snd_soc_register_codec - Register a codec with the ASoC core
  *
@@ -2339,6 +2511,8 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
  */
 int snd_soc_register_codec(struct snd_soc_codec *codec)
 {
+       int i;
+
        if (!codec->name)
                return -EINVAL;
 
@@ -2348,6 +2522,11 @@ int snd_soc_register_codec(struct snd_soc_codec *codec)
 
        INIT_LIST_HEAD(&codec->list);
 
+       for (i = 0; i < codec->num_dai; i++) {
+               fixup_codec_formats(&codec->dai[i].playback);
+               fixup_codec_formats(&codec->dai[i].capture);
+       }
+
        mutex_lock(&client_mutex);
        list_add(&codec->list, &codec_list);
        snd_soc_instantiate_cards();