ASoC: set idle_bias_off=1 for all platform DAPM contexts
[linux-2.6.git] / sound / soc / soc-core.c
index 1665181..8d2ebf5 100644 (file)
@@ -30,7 +30,9 @@
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/platform_device.h>
+#include <linux/ctype.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 #include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
@@ -57,8 +59,6 @@ static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
 
-int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
-
 /*
  * This is a timeout to do a DAPM powerdown after a stream is closed().
  * It can be used to eliminate pops between different playback streams, e.g.
@@ -105,7 +105,7 @@ static int format_register_str(struct snd_soc_codec *codec,
        if (wordsize + regsize + 2 + 1 != len)
                return -EINVAL;
 
-       ret = snd_soc_read(codec , reg);
+       ret = snd_soc_read(codec, reg);
        if (ret < 0) {
                memset(regbuf, 'X', regsize);
                regbuf[regsize] = '\0';
@@ -143,7 +143,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
                step = codec->driver->reg_cache_step;
 
        for (i = 0; i < codec->driver->reg_cache_size; i += step) {
-               if (codec->readable_register && !codec->readable_register(codec, i))
+               if (!snd_soc_codec_readable_register(codec, i))
                        continue;
                if (codec->driver->display_register) {
                        count += codec->driver->display_register(codec, buf + count,
@@ -169,8 +169,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
 static ssize_t codec_reg_show(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
-       struct snd_soc_pcm_runtime *rtd =
-                       container_of(dev, struct snd_soc_pcm_runtime, dev);
+       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
 
        return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0);
 }
@@ -180,8 +179,7 @@ static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
 static ssize_t pmdown_time_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct snd_soc_pcm_runtime *rtd =
-                       container_of(dev, struct snd_soc_pcm_runtime, dev);
+       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
 
        return sprintf(buf, "%ld\n", rtd->pmdown_time);
 }
@@ -190,8 +188,7 @@ static ssize_t pmdown_time_set(struct device *dev,
                               struct device_attribute *attr,
                               const char *buf, size_t count)
 {
-       struct snd_soc_pcm_runtime *rtd =
-                       container_of(dev, struct snd_soc_pcm_runtime, dev);
+       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
        int ret;
 
        ret = strict_strtol(buf, 10, &rtd->pmdown_time);
@@ -244,7 +241,6 @@ static ssize_t codec_reg_write_file(struct file *file,
        size_t 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));
@@ -252,9 +248,6 @@ static ssize_t codec_reg_write_file(struct file *file,
                return -EFAULT;
        buf[buf_size] = 0;
 
-       if (codec->driver->reg_cache_step)
-               step = codec->driver->reg_cache_step;
-
        while (*start == ' ')
                start++;
        reg = simple_strtoul(start, &start, 16);
@@ -284,8 +277,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
        codec->debugfs_codec_root = debugfs_create_dir(codec->name,
                                                       debugfs_card_root);
        if (!codec->debugfs_codec_root) {
-               printk(KERN_WARNING
-                      "ASoC: Failed to create codec debugfs directory\n");
+               dev_warn(codec->dev, "Failed to create codec debugfs directory\n");
                return;
        }
 
@@ -298,8 +290,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
                                                 codec->debugfs_codec_root,
                                                 codec, &codec_reg_fops);
        if (!codec->debugfs_reg)
-               printk(KERN_WARNING
-                      "ASoC: Failed to create codec register debugfs file\n");
+               dev_warn(codec->dev, "Failed to create codec register debugfs file\n");
 
        snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
 }
@@ -309,6 +300,27 @@ static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
        debugfs_remove_recursive(codec->debugfs_codec_root);
 }
 
+static void soc_init_platform_debugfs(struct snd_soc_platform *platform)
+{
+       struct dentry *debugfs_card_root = platform->card->debugfs_card_root;
+
+       platform->debugfs_platform_root = debugfs_create_dir(platform->name,
+                                                      debugfs_card_root);
+       if (!platform->debugfs_platform_root) {
+               dev_warn(platform->dev,
+                       "Failed to create platform debugfs directory\n");
+               return;
+       }
+
+       snd_soc_dapm_debugfs_init(&platform->dapm,
+               platform->debugfs_platform_root);
+}
+
+static void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform)
+{
+       debugfs_remove_recursive(platform->debugfs_platform_root);
+}
+
 static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
                                    size_t count, loff_t *ppos)
 {
@@ -415,7 +427,7 @@ static void soc_init_card_debugfs(struct snd_soc_card *card)
                                                     snd_soc_debugfs_root);
        if (!card->debugfs_card_root) {
                dev_warn(card->dev,
-                        "ASoC: Failed to create codec debugfs directory\n");
+                        "ASoC: Failed to create card debugfs directory\n");
                return;
        }
 
@@ -442,6 +454,14 @@ static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
 {
 }
 
+static inline void soc_init_platform_debugfs(struct snd_soc_platform *platform)
+{
+}
+
+static inline void soc_cleanup_platform_debugfs(struct snd_soc_platform *platform)
+{
+}
+
 static inline void soc_init_card_debugfs(struct snd_soc_card *card)
 {
 }
@@ -553,18 +573,20 @@ int snd_soc_suspend(struct device *dev)
        }
 
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
+               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (driver->playback.stream_name != NULL)
-                       snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name,
-                               SND_SOC_DAPM_STREAM_SUSPEND);
+               snd_soc_dapm_stream_event(&card->rtd[i],
+                                         SNDRV_PCM_STREAM_PLAYBACK,
+                                         codec_dai,
+                                         SND_SOC_DAPM_STREAM_SUSPEND);
 
-               if (driver->capture.stream_name != NULL)
-                       snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name,
-                               SND_SOC_DAPM_STREAM_SUSPEND);
+               snd_soc_dapm_stream_event(&card->rtd[i],
+                                         SNDRV_PCM_STREAM_CAPTURE,
+                                         codec_dai,
+                                         SND_SOC_DAPM_STREAM_SUSPEND);
        }
 
        /* suspend all CODECs */
@@ -574,9 +596,21 @@ int snd_soc_suspend(struct device *dev)
                if (!codec->suspended && codec->driver->suspend) {
                        switch (codec->dapm.bias_level) {
                        case SND_SOC_BIAS_STANDBY:
+                               /*
+                                * If the CODEC is capable of idle
+                                * bias off then being in STANDBY
+                                * means it's doing something,
+                                * otherwise fall through.
+                                */
+                               if (codec->dapm.idle_bias_off) {
+                                       dev_dbg(codec->dev,
+                                               "idle_bias_off CODEC on over suspend\n");
+                                       break;
+                               }
                        case SND_SOC_BIAS_OFF:
-                               codec->driver->suspend(codec, PMSG_SUSPEND);
+                               codec->driver->suspend(codec);
                                codec->suspended = 1;
+                               codec->cache_sync = 1;
                                break;
                        default:
                                dev_dbg(codec->dev, "CODEC is on over suspend\n");
@@ -655,18 +689,18 @@ static void soc_resume_deferred(struct work_struct *work)
        }
 
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai_driver *driver = card->rtd[i].codec_dai->driver;
+               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (driver->playback.stream_name != NULL)
-                       snd_soc_dapm_stream_event(&card->rtd[i], driver->playback.stream_name,
-                               SND_SOC_DAPM_STREAM_RESUME);
+               snd_soc_dapm_stream_event(&card->rtd[i],
+                                         SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
+                                         SND_SOC_DAPM_STREAM_RESUME);
 
-               if (driver->capture.stream_name != NULL)
-                       snd_soc_dapm_stream_event(&card->rtd[i], driver->capture.stream_name,
-                               SND_SOC_DAPM_STREAM_RESUME);
+               snd_soc_dapm_stream_event(&card->rtd[i],
+                                         SNDRV_PCM_STREAM_CAPTURE, codec_dai,
+                                         SND_SOC_DAPM_STREAM_RESUME);
        }
 
        /* unmute any active DACs */
@@ -711,6 +745,12 @@ int snd_soc_resume(struct device *dev)
        struct snd_soc_card *card = dev_get_drvdata(dev);
        int i, ac97_control = 0;
 
+       /* If the initialization of this soc device failed, there is no codec
+        * associated with it. Just bail out in this case.
+        */
+       if (list_empty(&card->codec_dev_list))
+               return 0;
+
        /* 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
@@ -737,7 +777,7 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
 #define snd_soc_resume NULL
 #endif
 
-static struct snd_soc_dai_ops null_dai_ops = {
+static const struct snd_soc_dai_ops null_dai_ops = {
 };
 
 static int soc_bind_dai_link(struct snd_soc_card *card, int num)
@@ -759,10 +799,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
        }
        /* no, then find CPU DAI from registered DAIs*/
        list_for_each_entry(cpu_dai, &dai_list, list) {
-               if (!strcmp(cpu_dai->name, dai_link->cpu_dai_name)) {
-                       rtd->cpu_dai = cpu_dai;
-                       goto find_codec;
+               if (dai_link->cpu_dai_of_node) {
+                       if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
+                               continue;
+               } else {
+                       if (strcmp(cpu_dai->name, dai_link->cpu_dai_name))
+                               continue;
                }
+
+               rtd->cpu_dai = cpu_dai;
+               goto find_codec;
        }
        dev_dbg(card->dev, "CPU DAI %s not registered\n",
                        dai_link->cpu_dai_name);
@@ -775,22 +821,33 @@ find_codec:
 
        /* no, then find CODEC from registered CODECs*/
        list_for_each_entry(codec, &codec_list, list) {
-               if (!strcmp(codec->name, dai_link->codec_name)) {
-                       rtd->codec = codec;
-
-                       /* CODEC found, so find CODEC DAI from registered DAIs from this CODEC*/
-                       list_for_each_entry(codec_dai, &dai_list, list) {
-                               if (codec->dev == codec_dai->dev &&
-                                               !strcmp(codec_dai->name, dai_link->codec_dai_name)) {
-                                       rtd->codec_dai = codec_dai;
-                                       goto find_platform;
-                               }
-                       }
-                       dev_dbg(card->dev, "CODEC DAI %s not registered\n",
-                                       dai_link->codec_dai_name);
+               if (dai_link->codec_of_node) {
+                       if (codec->dev->of_node != dai_link->codec_of_node)
+                               continue;
+               } else {
+                       if (strcmp(codec->name, dai_link->codec_name))
+                               continue;
+               }
+
+               rtd->codec = codec;
+
+               /*
+                * CODEC found, so find CODEC DAI from registered DAIs from
+                * this CODEC
+                */
+               list_for_each_entry(codec_dai, &dai_list, list) {
+                       if (codec->dev == codec_dai->dev &&
+                               !strcmp(codec_dai->name,
+                                       dai_link->codec_dai_name)) {
 
-                       goto find_platform;
+                               rtd->codec_dai = codec_dai;
+                               goto find_platform;
+                       }
                }
+               dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+                               dai_link->codec_dai_name);
+
+               goto find_platform;
        }
        dev_dbg(card->dev, "CODEC %s not registered\n",
                        dai_link->codec_name);
@@ -802,15 +859,22 @@ find_platform:
 
        /* if there's no platform we match on the empty platform */
        platform_name = dai_link->platform_name;
-       if (!platform_name)
+       if (!platform_name && !dai_link->platform_of_node)
                platform_name = "snd-soc-dummy";
 
        /* no, then find one from the set of registered platforms */
        list_for_each_entry(platform, &platform_list, list) {
-               if (!strcmp(platform->name, platform_name)) {
-                       rtd->platform = platform;
-                       goto out;
+               if (dai_link->platform_of_node) {
+                       if (platform->dev->of_node !=
+                           dai_link->platform_of_node)
+                               continue;
+               } else {
+                       if (strcmp(platform->name, platform_name))
+                               continue;
                }
+
+               rtd->platform = platform;
+               goto out;
        }
 
        dev_dbg(card->dev, "platform %s not registered\n",
@@ -857,9 +921,9 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
 
        /* unregister the rtd device */
        if (rtd->dev_registered) {
-               device_remove_file(&rtd->dev, &dev_attr_pmdown_time);
-               device_remove_file(&rtd->dev, &dev_attr_codec_reg);
-               device_unregister(&rtd->dev);
+               device_remove_file(rtd->dev, &dev_attr_pmdown_time);
+               device_remove_file(rtd->dev, &dev_attr_codec_reg);
+               device_unregister(rtd->dev);
                rtd->dev_registered = 0;
        }
 
@@ -869,7 +933,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
                if (codec_dai->driver->remove) {
                        err = codec_dai->driver->remove(codec_dai);
                        if (err < 0)
-                               printk(KERN_ERR "asoc: failed to remove %s\n", codec_dai->name);
+                               pr_err("asoc: failed to remove %s: %d\n",
+                                                       codec_dai->name, err);
                }
                codec_dai->probed = 0;
                list_del(&codec_dai->card_list);
@@ -881,8 +946,14 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
                if (platform->driver->remove) {
                        err = platform->driver->remove(platform);
                        if (err < 0)
-                               printk(KERN_ERR "asoc: failed to remove %s\n", platform->name);
+                               pr_err("asoc: failed to remove %s: %d\n",
+                                                       platform->name, err);
                }
+
+               /* Make sure all DAPM widgets are freed */
+               snd_soc_dapm_free(&platform->dapm);
+
+               soc_cleanup_platform_debugfs(platform);
                platform->probed = 0;
                list_del(&platform->card_list);
                module_put(platform->dev->driver->owner);
@@ -899,7 +970,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
                if (cpu_dai->driver->remove) {
                        err = cpu_dai->driver->remove(cpu_dai);
                        if (err < 0)
-                               printk(KERN_ERR "asoc: failed to remove %s\n", cpu_dai->name);
+                               pr_err("asoc: failed to remove %s: %d\n",
+                                                       cpu_dai->name, err);
                }
                cpu_dai->probed = 0;
                list_del(&cpu_dai->card_list);
@@ -941,6 +1013,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
 {
        int ret = 0;
        const struct snd_soc_codec_driver *driver = codec->driver;
+       struct snd_soc_dai *dai;
 
        codec->card = card;
        codec->dapm.card = card;
@@ -955,6 +1028,16 @@ static int soc_probe_codec(struct snd_soc_card *card,
                snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
                                          driver->num_dapm_widgets);
 
+       /* Create DAPM widgets for each DAI stream */
+       list_for_each_entry(dai, &dai_list, list) {
+               if (dai->dev != codec->dev)
+                       continue;
+
+               snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
+       }
+
+       codec->dapm.idle_bias_off = driver->idle_bias_off;
+
        if (driver->probe) {
                ret = driver->probe(codec);
                if (ret < 0) {
@@ -966,7 +1049,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
        }
 
        if (driver->controls)
-               snd_soc_add_controls(codec, driver->controls,
+               snd_soc_add_codec_controls(codec, driver->controls,
                                     driver->num_controls);
        if (driver->dapm_routes)
                snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
@@ -993,10 +1076,19 @@ static int soc_probe_platform(struct snd_soc_card *card,
        const struct snd_soc_platform_driver *driver = platform->driver;
 
        platform->card = card;
+       platform->dapm.card = card;
 
        if (!try_module_get(platform->dev->driver->owner))
                return -ENODEV;
 
+       soc_init_platform_debugfs(platform);
+
+       if (driver->dapm_widgets)
+               snd_soc_dapm_new_controls(&platform->dapm,
+                       driver->dapm_widgets, driver->num_dapm_widgets);
+
+       platform->dapm.idle_bias_off = 1;
+
        if (driver->probe) {
                ret = driver->probe(platform);
                if (ret < 0) {
@@ -1007,19 +1099,31 @@ static int soc_probe_platform(struct snd_soc_card *card,
                }
        }
 
+       if (driver->controls)
+               snd_soc_add_platform_controls(platform, driver->controls,
+                                    driver->num_controls);
+       if (driver->dapm_routes)
+               snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,
+                                       driver->num_dapm_routes);
+
        /* mark platform as probed and add to card platform list */
        platform->probed = 1;
        list_add(&platform->card_list, &card->platform_dev_list);
+       list_add(&platform->dapm.list, &card->dapm_list);
 
        return 0;
 
 err_probe:
+       soc_cleanup_platform_debugfs(platform);
        module_put(platform->dev->driver->owner);
 
        return ret;
 }
 
-static void rtd_release(struct device *dev) {}
+static void rtd_release(struct device *dev)
+{
+       kfree(dev);
+}
 
 static int soc_post_component_init(struct snd_soc_card *card,
                                   struct snd_soc_codec *codec,
@@ -1042,6 +1146,9 @@ static int soc_post_component_init(struct snd_soc_card *card,
        }
        rtd->card = card;
 
+       /* Make sure all DAPM widgets are instantiated */
+       snd_soc_dapm_new_widgets(&codec->dapm);
+
        /* machine controls, routes and widgets are not prefixed */
        temp = codec->name_prefix;
        codec->name_prefix = NULL;
@@ -1057,16 +1164,19 @@ static int soc_post_component_init(struct snd_soc_card *card,
        }
        codec->name_prefix = temp;
 
-       /* Make sure all DAPM widgets are instantiated */
-       snd_soc_dapm_new_widgets(&codec->dapm);
-
        /* register the rtd device */
        rtd->codec = codec;
-       rtd->dev.parent = card->dev;
-       rtd->dev.release = rtd_release;
-       rtd->dev.init_name = name;
+
+       rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+       if (!rtd->dev)
+               return -ENOMEM;
+       device_initialize(rtd->dev);
+       rtd->dev->parent = card->dev;
+       rtd->dev->release = rtd_release;
+       rtd->dev->init_name = name;
+       dev_set_drvdata(rtd->dev, rtd);
        mutex_init(&rtd->pcm_mutex);
-       ret = device_register(&rtd->dev);
+       ret = device_add(rtd->dev);
        if (ret < 0) {
                dev_err(card->dev,
                        "asoc: failed to register runtime device: %d\n", ret);
@@ -1075,14 +1185,14 @@ static int soc_post_component_init(struct snd_soc_card *card,
        rtd->dev_registered = 1;
 
        /* add DAPM sysfs entries for this codec */
-       ret = snd_soc_dapm_sys_add(&rtd->dev);
+       ret = snd_soc_dapm_sys_add(rtd->dev);
        if (ret < 0)
                dev_err(codec->dev,
                        "asoc: failed to add codec dapm sysfs entries: %d\n",
                        ret);
 
        /* add codec sysfs entries */
-       ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);
+       ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
        if (ret < 0)
                dev_err(codec->dev,
                        "asoc: failed to add codec sysfs files: %d\n", ret);
@@ -1120,14 +1230,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
                if (cpu_dai->driver->probe) {
                        ret = cpu_dai->driver->probe(cpu_dai);
                        if (ret < 0) {
-                               printk(KERN_ERR "asoc: failed to probe CPU DAI %s\n",
-                                               cpu_dai->name);
+                               pr_err("asoc: failed to probe CPU DAI %s: %d\n",
+                                                       cpu_dai->name, ret);
                                module_put(cpu_dai->dev->driver->owner);
                                return ret;
                        }
                }
                cpu_dai->probed = 1;
-               /* mark cpu_dai as probed and add to card cpu_dai list */
+               /* mark cpu_dai as probed and add to card dai list */
                list_add(&cpu_dai->card_list, &card->dai_dev_list);
        }
 
@@ -1152,13 +1262,13 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
                if (codec_dai->driver->probe) {
                        ret = codec_dai->driver->probe(codec_dai);
                        if (ret < 0) {
-                               printk(KERN_ERR "asoc: failed to probe CODEC DAI %s\n",
-                                               codec_dai->name);
+                               pr_err("asoc: failed to probe CODEC DAI %s: %d\n",
+                                                       codec_dai->name, ret);
                                return ret;
                        }
                }
 
-               /* mark cpu_dai as probed and add to card cpu_dai list */
+               /* mark codec_dai as probed and add to card dai list */
                codec_dai->probed = 1;
                list_add(&codec_dai->card_list, &card->dai_dev_list);
        }
@@ -1171,14 +1281,15 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
        if (ret)
                return ret;
 
-       ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time);
+       ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
        if (ret < 0)
-               printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");
+               pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
 
        /* create the pcm */
        ret = soc_new_pcm(rtd, num);
        if (ret < 0) {
-               printk(KERN_ERR "asoc: can't create pcm %s\n", dai_link->stream_name);
+               pr_err("asoc: can't create pcm %s :%d\n",
+                               dai_link->stream_name, ret);
                return ret;
        }
 
@@ -1211,7 +1322,7 @@ static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
 
                ret = soc_ac97_dev_register(rtd->codec);
                if (ret < 0) {
-                       printk(KERN_ERR "asoc: AC97 device register failed\n");
+                       pr_err("asoc: AC97 device register failed:%d\n", ret);
                        return ret;
                }
 
@@ -1269,8 +1380,8 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
 
        /* unregister the rtd device */
        if (rtd->dev_registered) {
-               device_remove_file(&rtd->dev, &dev_attr_codec_reg);
-               device_unregister(&rtd->dev);
+               device_remove_file(rtd->dev, &dev_attr_codec_reg);
+               device_del(rtd->dev);
                rtd->dev_registered = 0;
        }
 
@@ -1304,6 +1415,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        struct snd_soc_codec *codec;
        struct snd_soc_codec_conf *codec_conf;
        enum snd_soc_compress_type compress_type;
+       struct snd_soc_dai_link *dai_link;
        int ret, i, order;
 
        mutex_lock(&card->mutex);
@@ -1350,8 +1462,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
                        card->owner, 0, &card->snd_card);
        if (ret < 0) {
-               printk(KERN_ERR "asoc: can't create sound card for card %s\n",
-                       card->name);
+               pr_err("asoc: can't create sound card for card %s: %d\n",
+                       card->name, ret);
                mutex_unlock(&card->mutex);
                return;
        }
@@ -1404,24 +1516,55 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
-       /* We should have a non-codec control add function but we don't */
+       snd_soc_dapm_link_dai_widgets(card);
+
        if (card->controls)
-               snd_soc_add_controls(list_first_entry(&card->codec_dev_list,
-                                                     struct snd_soc_codec,
-                                                     card_list),
-                                    card->controls,
-                                    card->num_controls);
+               snd_soc_add_card_controls(card, card->controls, card->num_controls);
 
        if (card->dapm_routes)
                snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
                                        card->num_dapm_routes);
 
+       snd_soc_dapm_new_widgets(&card->dapm);
+
+       for (i = 0; i < card->num_links; i++) {
+               dai_link = &card->dai_link[i];
+
+               if (dai_link->dai_fmt) {
+                       ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
+                                                 dai_link->dai_fmt);
+                       if (ret != 0 && ret != -ENOTSUPP)
+                               dev_warn(card->rtd[i].codec_dai->dev,
+                                        "Failed to set DAI format: %d\n",
+                                        ret);
+
+                       ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
+                                                 dai_link->dai_fmt);
+                       if (ret != 0 && ret != -ENOTSUPP)
+                               dev_warn(card->rtd[i].cpu_dai->dev,
+                                        "Failed to set DAI format: %d\n",
+                                        ret);
+               }
+       }
+
        snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
                 "%s", card->name);
        snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
                 "%s", card->long_name ? card->long_name : card->name);
        snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
                 "%s", card->driver_name ? card->driver_name : card->name);
+       for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
+               switch (card->snd_card->driver[i]) {
+               case '_':
+               case '-':
+               case '\0':
+                       break;
+               default:
+                       if (!isalnum(card->snd_card->driver[i]))
+                               card->snd_card->driver[i] = '_';
+                       break;
+               }
+       }
 
        if (card->late_probe) {
                ret = card->late_probe(card);
@@ -1432,9 +1575,16 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
+       snd_soc_dapm_new_widgets(&card->dapm);
+
+       if (card->fully_routed)
+               list_for_each_entry(codec, &card->codec_dev_list, card_list)
+                       snd_soc_dapm_auto_nc_codec_pins(codec);
+
        ret = snd_card_register(card->snd_card);
        if (ret < 0) {
-               printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
+               pr_err("asoc: failed to register soundcard for %s: %d\n",
+                                                       card->name, ret);
                goto probe_aux_dev_err;
        }
 
@@ -1443,7 +1593,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        for (i = 0; i < card->num_rtd; i++) {
                ret = soc_register_ac97_dai_link(&card->rtd[i]);
                if (ret < 0) {
-                       printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name);
+                       pr_err("asoc: failed to register AC97 %s: %d\n",
+                                                       card->name, ret);
                        while (--i >= 0)
                                soc_unregister_ac97_dai_link(card->rtd[i].codec);
                        goto probe_aux_dev_err;
@@ -1452,6 +1603,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
 #endif
 
        card->instantiated = 1;
+       snd_soc_dapm_sync(&card->dapm);
        mutex_unlock(&card->mutex);
        return;
 
@@ -1495,6 +1647,10 @@ static int soc_probe(struct platform_device *pdev)
        if (!card)
                return -EINVAL;
 
+       dev_warn(&pdev->dev,
+                "ASoC machine %s should use snd_soc_register_card()\n",
+                card->name);
+
        /* Bodge while we unpick instantiation */
        card->dev = &pdev->dev;
 
@@ -1532,7 +1688,6 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
 
        snd_soc_dapm_free(&card->dapm);
 
-       kfree(card->rtd);
        snd_card_free(card->snd_card);
        return 0;
 
@@ -1571,7 +1726,10 @@ EXPORT_SYMBOL_GPL(snd_soc_poweroff);
 const struct dev_pm_ops snd_soc_pm_ops = {
        .suspend = snd_soc_suspend,
        .resume = snd_soc_resume,
+       .freeze = snd_soc_suspend,
+       .thaw = snd_soc_resume,
        .poweroff = snd_soc_poweroff,
+       .restore = snd_soc_resume,
 };
 EXPORT_SYMBOL_GPL(snd_soc_pm_ops);
 
@@ -1618,7 +1776,7 @@ int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
        if (codec->readable_register)
                return codec->readable_register(codec, reg);
        else
-               return 0;
+               return 1;
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_readable_register);
 
@@ -1636,7 +1794,7 @@ int snd_soc_codec_writable_register(struct snd_soc_codec *codec,
        if (codec->writable_register)
                return codec->writable_register(codec, reg);
        else
-               return 0;
+               return 1;
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_writable_register);
 
@@ -1652,6 +1810,7 @@ int snd_soc_platform_read(struct snd_soc_platform *platform,
 
        ret = platform->driver->read(platform, reg);
        dev_dbg(platform->dev, "read %x => %x\n", reg, ret);
+       trace_snd_soc_preg_read(platform, reg, ret);
 
        return ret;
 }
@@ -1666,6 +1825,7 @@ int snd_soc_platform_write(struct snd_soc_platform *platform,
        }
 
        dev_dbg(platform->dev, "write %x = %x\n", reg, val);
+       trace_snd_soc_preg_write(platform, reg, val);
        return platform->driver->write(platform, reg, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_platform_write);
@@ -1773,23 +1933,28 @@ EXPORT_SYMBOL_GPL(snd_soc_bulk_write_raw);
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
                                unsigned int mask, unsigned int value)
 {
-       int change;
+       bool change;
        unsigned int old, new;
        int ret;
 
-       ret = snd_soc_read(codec, reg);
-       if (ret < 0)
-               return ret;
-
-       old = ret;
-       new = (old & ~mask) | (value & mask);
-       change = old != new;
-       if (change) {
-               ret = snd_soc_write(codec, reg, new);
+       if (codec->using_regmap) {
+               ret = regmap_update_bits_check(codec->control_data, reg,
+                                              mask, value, &change);
+       } else {
+               ret = snd_soc_read(codec, reg);
                if (ret < 0)
                        return ret;
+
+               old = ret;
+               new = (old & ~mask) | (value & mask);
+               change = old != new;
+               if (change)
+                       ret = snd_soc_write(codec, reg, new);
        }
 
+       if (ret < 0)
+               return ret;
+
        return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_update_bits);
@@ -1880,7 +2045,7 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
  * Returns 0 for success, else error.
  */
 struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
-                                 void *data, char *long_name,
+                                 void *data, const char *long_name,
                                  const char *prefix)
 {
        struct snd_kcontrol_new template;
@@ -1896,7 +2061,7 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
 
        if (prefix) {
                name_len = strlen(long_name) + strlen(prefix) + 2;
-               name = kmalloc(name_len, GFP_ATOMIC);
+               name = kmalloc(name_len, GFP_KERNEL);
                if (!name)
                        return NULL;
 
@@ -1915,9 +2080,28 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
 }
 EXPORT_SYMBOL_GPL(snd_soc_cnew);
 
+static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
+       const struct snd_kcontrol_new *controls, int num_controls,
+       const char *prefix, void *data)
+{
+       int err, i;
+
+       for (i = 0; i < num_controls; i++) {
+               const struct snd_kcontrol_new *control = &controls[i];
+               err = snd_ctl_add(card, snd_soc_cnew(control, data,
+                                                    control->name, prefix));
+               if (err < 0) {
+                       dev_err(dev, "Failed to add %s: %d\n", control->name, err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
 /**
- * snd_soc_add_controls - add an array of controls to a codec.
- * Convienience function to add a list of controls. Many codecs were
+ * snd_soc_add_codec_controls - add an array of controls to a codec.
+ * Convenience function to add a list of controls. Many codecs were
  * duplicating this code.
  *
  * @codec: codec to add controls to
@@ -1926,31 +2110,19 @@ EXPORT_SYMBOL_GPL(snd_soc_cnew);
  *
  * Return 0 for success, else error.
  */
-int snd_soc_add_controls(struct snd_soc_codec *codec,
+int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
        const struct snd_kcontrol_new *controls, int num_controls)
 {
        struct snd_card *card = codec->card->snd_card;
-       int err, i;
-
-       for (i = 0; i < num_controls; i++) {
-               const struct snd_kcontrol_new *control = &controls[i];
-               err = snd_ctl_add(card, snd_soc_cnew(control, codec,
-                                                    control->name,
-                                                    codec->name_prefix));
-               if (err < 0) {
-                       dev_err(codec->dev, "%s: Failed to add %s: %d\n",
-                               codec->name, control->name, err);
-                       return err;
-               }
-       }
 
-       return 0;
+       return snd_soc_add_controls(card, codec->dev, controls, num_controls,
+                       codec->name_prefix, codec);
 }
-EXPORT_SYMBOL_GPL(snd_soc_add_controls);
+EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
 
 /**
  * snd_soc_add_platform_controls - add an array of controls to a platform.
- * Convienience function to add a list of controls.
+ * Convenience function to add a list of controls.
  *
  * @platform: platform to add controls to
  * @controls: array of controls to add
@@ -1962,23 +2134,53 @@ int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
        const struct snd_kcontrol_new *controls, int num_controls)
 {
        struct snd_card *card = platform->card->snd_card;
-       int err, i;
 
-       for (i = 0; i < num_controls; i++) {
-               const struct snd_kcontrol_new *control = &controls[i];
-               err = snd_ctl_add(card, snd_soc_cnew(control, platform,
-                               control->name, NULL));
-               if (err < 0) {
-                       dev_err(platform->dev, "Failed to add %s %d\n",control->name, err);
-                       return err;
-               }
-       }
-
-       return 0;
+       return snd_soc_add_controls(card, platform->dev, controls, num_controls,
+                       NULL, platform);
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
 
 /**
+ * snd_soc_add_card_controls - add an array of controls to a SoC card.
+ * Convenience function to add a list of controls.
+ *
+ * @soc_card: SoC card to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
+       const struct snd_kcontrol_new *controls, int num_controls)
+{
+       struct snd_card *card = soc_card->snd_card;
+
+       return snd_soc_add_controls(card, soc_card->dev, controls, num_controls,
+                       NULL, soc_card);
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
+
+/**
+ * snd_soc_add_dai_controls - add an array of controls to a DAI.
+ * Convienience function to add a list of controls.
+ *
+ * @dai: DAI to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
+       const struct snd_kcontrol_new *controls, int num_controls)
+{
+       struct snd_card *card = dai->card->snd_card;
+
+       return snd_soc_add_controls(card, dai->dev, controls, num_controls,
+                       NULL, dai);
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
+
+/**
  * snd_soc_info_enum_double - enumerated double mixer info callback
  * @kcontrol: mixer control
  * @uinfo: control element information
@@ -2200,7 +2402,8 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
  * @kcontrol: mixer control
  * @uinfo: control element information
  *
- * Callback to provide information about a single mixer control.
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers.
  *
  * Returns 0 for success.
  */
@@ -2210,8 +2413,6 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        int platform_max;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
 
        if (!mc->platform_max)
                mc->platform_max = mc->max;
@@ -2222,7 +2423,7 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        else
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 
-       uinfo->count = shift == rshift ? 1 : 2;
+       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
        uinfo->value.integer.min = 0;
        uinfo->value.integer.max = platform_max;
        return 0;
@@ -2234,7 +2435,8 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
  * @kcontrol: mixer control
  * @ucontrol: control element information
  *
- * Callback to get the value of a single mixer control.
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
  *
  * Returns 0 for success.
  */
@@ -2245,6 +2447,7 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
        int max = mc->max;
@@ -2253,13 +2456,18 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
 
        ucontrol->value.integer.value[0] =
                (snd_soc_read(codec, reg) >> shift) & mask;
-       if (shift != rshift)
-               ucontrol->value.integer.value[1] =
-                       (snd_soc_read(codec, reg) >> rshift) & mask;
-       if (invert) {
+       if (invert)
                ucontrol->value.integer.value[0] =
                        max - ucontrol->value.integer.value[0];
-               if (shift != rshift)
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               if (reg == reg2)
+                       ucontrol->value.integer.value[1] =
+                               (snd_soc_read(codec, reg) >> rshift) & mask;
+               else
+                       ucontrol->value.integer.value[1] =
+                               (snd_soc_read(codec, reg2) >> shift) & mask;
+               if (invert)
                        ucontrol->value.integer.value[1] =
                                max - ucontrol->value.integer.value[1];
        }
@@ -2273,7 +2481,8 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
  * @kcontrol: mixer control
  * @ucontrol: control element information
  *
- * Callback to set the value of a single mixer control.
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
  *
  * Returns 0 for success.
  */
@@ -2284,143 +2493,44 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
        unsigned int shift = mc->shift;
        unsigned int rshift = mc->rshift;
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned int val, val2, val_mask;
+       int err;
+       bool type_2r = 0;
+       unsigned int val2 = 0;
+       unsigned int val, val_mask;
 
        val = (ucontrol->value.integer.value[0] & mask);
        if (invert)
                val = max - val;
        val_mask = mask << shift;
        val = val << shift;
-       if (shift != rshift) {
+       if (snd_soc_volsw_is_stereo(mc)) {
                val2 = (ucontrol->value.integer.value[1] & mask);
                if (invert)
                        val2 = max - val2;
-               val_mask |= mask << rshift;
-               val |= val2 << rshift;
-       }
-       return snd_soc_update_bits_locked(codec, reg, val_mask, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
-
-/**
- * snd_soc_info_volsw_2r - double mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a double mixer control that
- * spans 2 codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       else
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
-       uinfo->count = 2;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
-
-/**
- * snd_soc_get_volsw_2r - double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-
-       ucontrol->value.integer.value[0] =
-               (snd_soc_read(codec, reg) >> shift) & mask;
-       ucontrol->value.integer.value[1] =
-               (snd_soc_read(codec, reg2) >> shift) & mask;
-       if (invert) {
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
-               ucontrol->value.integer.value[1] =
-                       max - ucontrol->value.integer.value[1];
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r);
-
-/**
- * snd_soc_put_volsw_2r - double mixer set callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int err;
-       unsigned int val, val2, val_mask;
-
-       val_mask = mask << shift;
-       val = (ucontrol->value.integer.value[0] & mask);
-       val2 = (ucontrol->value.integer.value[1] & mask);
-
-       if (invert) {
-               val = max - val;
-               val2 = max - val2;
+               if (reg == reg2) {
+                       val_mask |= mask << rshift;
+                       val |= val2 << rshift;
+               } else {
+                       val2 = val2 << shift;
+                       type_2r = 1;
+               }
        }
-
-       val = val << shift;
-       val2 = val2 << shift;
-
        err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
        if (err < 0)
                return err;
 
-       err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
+       if (type_2r)
+               err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
+
        return err;
 }
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
 /**
  * snd_soc_info_volsw_s8 - signed mixer info callback
@@ -2636,6 +2746,115 @@ int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
 
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = params->num_regs * codec->val_bytes;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
+
+int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int ret;
+
+       if (codec->using_regmap)
+               ret = regmap_raw_read(codec->control_data, params->base,
+                                     ucontrol->value.bytes.data,
+                                     params->num_regs * codec->val_bytes);
+       else
+               ret = -EINVAL;
+
+       /* Hide any masked bytes to ensure consistent data reporting */
+       if (ret == 0 && params->mask) {
+               switch (codec->val_bytes) {
+               case 1:
+                       ucontrol->value.bytes.data[0] &= ~params->mask;
+                       break;
+               case 2:
+                       ((u16 *)(&ucontrol->value.bytes.data))[0]
+                               &= ~params->mask;
+                       break;
+               case 4:
+                       ((u32 *)(&ucontrol->value.bytes.data))[0]
+                               &= ~params->mask;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
+
+int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int ret, len;
+       unsigned int val;
+       void *data;
+
+       if (!codec->using_regmap)
+               return -EINVAL;
+
+       data = ucontrol->value.bytes.data;
+       len = params->num_regs * codec->val_bytes;
+
+       /*
+        * If we've got a mask then we need to preserve the register
+        * bits.  We shouldn't modify the incoming data so take a
+        * copy.
+        */
+       if (params->mask) {
+               ret = regmap_read(codec->control_data, params->base, &val);
+               if (ret != 0)
+                       return ret;
+
+               val &= params->mask;
+
+               data = kmemdup(data, len, GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+
+               switch (codec->val_bytes) {
+               case 1:
+                       ((u8 *)data)[0] &= ~params->mask;
+                       ((u8 *)data)[0] |= val;
+                       break;
+               case 2:
+                       ((u16 *)data)[0] &= cpu_to_be16(~params->mask);
+                       ((u16 *)data)[0] |= cpu_to_be16(val);
+                       break;
+               case 4:
+                       ((u32 *)data)[0] &= cpu_to_be32(~params->mask);
+                       ((u32 *)data)[0] |= cpu_to_be32(val);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       ret = regmap_raw_write(codec->control_data, params->base,
+                              data, len);
+
+       if (params->mask)
+               kfree(data);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
 /**
  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  * @dai: DAI
@@ -2651,7 +2870,7 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        if (dai->driver && dai->driver->ops->set_sysclk)
                return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
        else if (dai->codec && dai->codec->driver->set_sysclk)
-               return dai->codec->driver->set_sysclk(dai->codec, clk_id,
+               return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
                                                      freq, dir);
        else
                return -EINVAL;
@@ -2662,16 +2881,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
  * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
  * @codec: CODEC
  * @clk_id: DAI specific clock ID
+ * @source: Source for the clock
  * @freq: new clock frequency in Hz
  * @dir: new clock direction - input/output.
  *
  * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
  */
 int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
-       unsigned int freq, int dir)
+                            int source, unsigned int freq, int dir)
 {
        if (codec->driver->set_sysclk)
-               return codec->driver->set_sysclk(codec, clk_id, freq, dir);
+               return codec->driver->set_sysclk(codec, clk_id, source,
+                                                freq, dir);
        else
                return -EINVAL;
 }
@@ -2751,10 +2972,11 @@ EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll);
  */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-       if (dai->driver && dai->driver->ops->set_fmt)
-               return dai->driver->ops->set_fmt(dai, fmt);
-       else
+       if (dai->driver == NULL)
                return -EINVAL;
+       if (dai->driver->ops->set_fmt == NULL)
+               return -ENOTSUPP;
+       return dai->driver->ops->set_fmt(dai, fmt);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt);
 
@@ -2849,15 +3071,52 @@ int snd_soc_register_card(struct snd_soc_card *card)
        if (!card->name || !card->dev)
                return -EINVAL;
 
+       for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_dai_link *link = &card->dai_link[i];
+
+               /*
+                * Codec must be specified by 1 of name or OF node,
+                * not both or neither.
+                */
+               if (!!link->codec_name == !!link->codec_of_node) {
+                       dev_err(card->dev,
+                               "Neither/both codec name/of_node are set for %s\n",
+                               link->name);
+                       return -EINVAL;
+               }
+
+               /*
+                * Platform may be specified by either name or OF node, but
+                * can be left unspecified, and a dummy platform will be used.
+                */
+               if (link->platform_name && link->platform_of_node) {
+                       dev_err(card->dev,
+                               "Both platform name/of_node are set for %s\n", link->name);
+                       return -EINVAL;
+               }
+
+               /*
+                * CPU DAI must be specified by 1 of name or OF node,
+                * not both or neither.
+                */
+               if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) {
+                       dev_err(card->dev,
+                               "Neither/both cpu_dai name/of_node are set for %s\n",
+                               link->name);
+                       return -EINVAL;
+               }
+       }
+
        dev_set_drvdata(card->dev, card);
 
        snd_soc_initialize_card_lists(card);
 
        soc_init_card_debugfs(card);
 
-       card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
-                           (card->num_links + card->num_aux_devs),
-                           GFP_KERNEL);
+       card->rtd = devm_kzalloc(card->dev,
+                                sizeof(struct snd_soc_pcm_runtime) *
+                                (card->num_links + card->num_aux_devs),
+                                GFP_KERNEL);
        if (card->rtd == NULL)
                return -ENOMEM;
        card->rtd_aux = &card->rtd[card->num_links];
@@ -2866,6 +3125,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
                card->rtd[i].dai_link = &card->dai_link[i];
 
        INIT_LIST_HEAD(&card->list);
+       INIT_LIST_HEAD(&card->dapm_dirty);
        card->instantiated = 0;
        mutex_init(&card->mutex);
 
@@ -2950,7 +3210,7 @@ static inline char *fmt_multiple_name(struct device *dev,
                struct snd_soc_dai_driver *dai_drv)
 {
        if (dai_drv->name == NULL) {
-               printk(KERN_ERR "asoc: error - multiple DAI %s registered with no name\n",
+               pr_err("asoc: error - multiple DAI %s registered with no name\n",
                                dev_name(dev));
                return NULL;
        }
@@ -3122,6 +3382,10 @@ int snd_soc_register_platform(struct device *dev,
 
        platform->dev = dev;
        platform->driver = platform_drv;
+       platform->dapm.dev = dev;
+       platform->dapm.platform = platform;
+       platform->dapm.stream_event = platform_drv->stream_event;
+       mutex_init(&platform->mutex);
 
        mutex_lock(&client_mutex);
        list_add(&platform->list, &platform_list);
@@ -3230,10 +3494,12 @@ int snd_soc_register_codec(struct device *dev,
        codec->volatile_register = codec_drv->volatile_register;
        codec->readable_register = codec_drv->readable_register;
        codec->writable_register = codec_drv->writable_register;
+       codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
        codec->dapm.bias_level = SND_SOC_BIAS_OFF;
        codec->dapm.dev = dev;
        codec->dapm.codec = codec;
        codec->dapm.seq_notifier = codec_drv->seq_notifier;
+       codec->dapm.stream_event = codec_drv->stream_event;
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->num_dai = num_dai;
@@ -3331,13 +3597,93 @@ found:
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
 
+/* Retrieve a card's name from device tree */
+int snd_soc_of_parse_card_name(struct snd_soc_card *card,
+                              const char *propname)
+{
+       struct device_node *np = card->dev->of_node;
+       int ret;
+
+       ret = of_property_read_string_index(np, propname, 0, &card->name);
+       /*
+        * EINVAL means the property does not exist. This is fine providing
+        * card->name was previously set, which is checked later in
+        * snd_soc_register_card.
+        */
+       if (ret < 0 && ret != -EINVAL) {
+               dev_err(card->dev,
+                       "Property '%s' could not be read: %d\n",
+                       propname, ret);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
+
+int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
+                                  const char *propname)
+{
+       struct device_node *np = card->dev->of_node;
+       int num_routes;
+       struct snd_soc_dapm_route *routes;
+       int i, ret;
+
+       num_routes = of_property_count_strings(np, propname);
+       if (num_routes & 1) {
+               dev_err(card->dev,
+                       "Property '%s's length is not even\n",
+                       propname);
+               return -EINVAL;
+       }
+       num_routes /= 2;
+       if (!num_routes) {
+               dev_err(card->dev,
+                       "Property '%s's length is zero\n",
+                       propname);
+               return -EINVAL;
+       }
+
+       routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+                             GFP_KERNEL);
+       if (!routes) {
+               dev_err(card->dev,
+                       "Could not allocate DAPM route table\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < num_routes; i++) {
+               ret = of_property_read_string_index(np, propname,
+                       2 * i, &routes[i].sink);
+               if (ret) {
+                       dev_err(card->dev,
+                               "Property '%s' index %d could not be read: %d\n",
+                               propname, 2 * i, ret);
+                       return -EINVAL;
+               }
+               ret = of_property_read_string_index(np, propname,
+                       (2 * i) + 1, &routes[i].source);
+               if (ret) {
+                       dev_err(card->dev,
+                               "Property '%s' index %d could not be read: %d\n",
+                               propname, (2 * i) + 1, ret);
+                       return -EINVAL;
+               }
+       }
+
+       card->num_dapm_routes = num_routes;
+       card->dapm_routes = routes;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
        snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
        if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
-               printk(KERN_WARNING
-                      "ASoC: Failed to create debugfs directory\n");
+               pr_warn("ASoC: Failed to create debugfs directory\n");
                snd_soc_debugfs_root = NULL;
        }