ASoC: set idle_bias_off=1 for all platform DAPM contexts
[linux-2.6.git] / sound / soc / soc-core.c
index bf41d90..8d2ebf5 100644 (file)
@@ -32,6 +32,7 @@
 #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>
@@ -58,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.
@@ -170,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);
 }
@@ -181,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);
 }
@@ -191,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);
@@ -281,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;
        }
 
@@ -295,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);
 }
@@ -306,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)
 {
@@ -412,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;
        }
 
@@ -439,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)
 {
 }
@@ -550,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 */
@@ -571,8 +596,19 @@ 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;
@@ -653,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 */
@@ -709,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
@@ -757,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);
@@ -773,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);
@@ -800,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",
@@ -855,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;
        }
 
@@ -867,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);
@@ -879,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);
@@ -897,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);
@@ -939,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;
@@ -953,6 +1028,14 @@ 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) {
@@ -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,
@@ -998,10 +1081,14 @@ static int soc_probe_platform(struct snd_soc_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) {
@@ -1027,12 +1114,16 @@ static int soc_probe_platform(struct snd_soc_card *card,
        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,
@@ -1075,11 +1166,17 @@ static int soc_post_component_init(struct snd_soc_card *card,
 
        /* 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);
@@ -1088,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);
@@ -1133,8 +1230,8 @@ 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;
                        }
@@ -1165,8 +1262,8 @@ 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;
                        }
                }
@@ -1184,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;
        }
 
@@ -1224,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;
                }
 
@@ -1282,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;
        }
 
@@ -1364,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;
        }
@@ -1418,13 +1516,10 @@ 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,
@@ -1438,14 +1533,14 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                if (dai_link->dai_fmt) {
                        ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
                                                  dai_link->dai_fmt);
-                       if (ret != 0)
+                       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)
+                       if (ret != 0 && ret != -ENOTSUPP)
                                dev_warn(card->rtd[i].cpu_dai->dev,
                                         "Failed to set DAI format: %d\n",
                                         ret);
@@ -1482,9 +1577,14 @@ 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;
        }
 
@@ -1493,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;
@@ -1546,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;
 
@@ -1583,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;
 
@@ -1622,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);
 
@@ -1826,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);
@@ -1933,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;
@@ -1968,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
@@ -1979,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
@@ -2015,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
@@ -2597,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
@@ -2714,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);
 
@@ -2812,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];
@@ -2914,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;
        }
@@ -3089,6 +3385,7 @@ int snd_soc_register_platform(struct device *dev,
        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);
@@ -3197,6 +3494,7 @@ 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;
@@ -3299,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;
        }