]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - sound/soc/soc-core.c
Merge tag 'sound-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
[linux-3.10.git] / sound / soc / soc-core.c
index b390f00b4e9946a1b3526048de0e608035ff569f..d1198627fc40397b772adad1104579c35842507f 100644 (file)
@@ -39,6 +39,7 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/soc-dpcm.h>
 #include <sound/initval.h>
 
 #define CREATE_TRACE_POINTS
@@ -54,7 +55,6 @@ EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
 #endif
 
 static DEFINE_MUTEX(client_mutex);
-static LIST_HEAD(card_list);
 static LIST_HEAD(dai_list);
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
@@ -201,12 +201,6 @@ static ssize_t pmdown_time_set(struct device *dev,
 static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
 
 #ifdef CONFIG_DEBUG_FS
-static int codec_reg_open_file(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return 0;
-}
-
 static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
                                   size_t count, loff_t *ppos)
 {
@@ -264,7 +258,7 @@ static ssize_t codec_reg_write_file(struct file *file,
 }
 
 static const struct file_operations codec_reg_fops = {
-       .open = codec_reg_open_file,
+       .open = simple_open,
        .read = codec_reg_read_file,
        .write = codec_reg_write_file,
        .llseek = default_llseek,
@@ -471,6 +465,35 @@ static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
 }
 #endif
 
+struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
+               const char *dai_link, int stream)
+{
+       int i;
+
+       for (i = 0; i < card->num_links; i++) {
+               if (card->rtd[i].dai_link->no_pcm &&
+                       !strcmp(card->rtd[i].dai_link->name, dai_link))
+                       return card->rtd[i].pcm->streams[stream].substream;
+       }
+       dev_dbg(card->dev, "failed to find dai link %s\n", dai_link);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
+
+struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
+               const char *dai_link)
+{
+       int i;
+
+       for (i = 0; i < card->num_links; i++) {
+               if (!strcmp(card->rtd[i].dai_link->name, dai_link))
+                       return &card->rtd[i];
+       }
+       dev_dbg(card->dev, "failed to find rtd %s\n", dai_link);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
+
 #ifdef CONFIG_SND_SOC_AC97_BUS
 /* unregister ac97 codec */
 static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -568,27 +591,28 @@ int snd_soc_suspend(struct device *dev)
 
        /* close any waiting streams and save state */
        for (i = 0; i < card->num_rtd; i++) {
-               flush_delayed_work_sync(&card->rtd[i].delayed_work);
+               flush_delayed_work(&card->rtd[i].delayed_work);
                card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level;
        }
 
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
                snd_soc_dapm_stream_event(&card->rtd[i],
                                          SNDRV_PCM_STREAM_PLAYBACK,
-                                         codec_dai,
                                          SND_SOC_DAPM_STREAM_SUSPEND);
 
                snd_soc_dapm_stream_event(&card->rtd[i],
                                          SNDRV_PCM_STREAM_CAPTURE,
-                                         codec_dai,
                                          SND_SOC_DAPM_STREAM_SUSPEND);
        }
 
+       /* Recheck all analogue paths too */
+       dapm_mark_io_dirty(&card->dapm);
+       snd_soc_dapm_sync(&card->dapm);
+
        /* suspend all CODECs */
        list_for_each_entry(codec, &card->codec_dev_list, card_list) {
                /* If there are paths active then the CODEC will be held with
@@ -611,6 +635,8 @@ int snd_soc_suspend(struct device *dev)
                                codec->driver->suspend(codec);
                                codec->suspended = 1;
                                codec->cache_sync = 1;
+                               if (codec->using_regmap)
+                                       regcache_mark_dirty(codec->control_data);
                                break;
                        default:
                                dev_dbg(codec->dev, "CODEC is on over suspend\n");
@@ -689,17 +715,16 @@ static void soc_resume_deferred(struct work_struct *work)
        }
 
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
                snd_soc_dapm_stream_event(&card->rtd[i],
-                                         SNDRV_PCM_STREAM_PLAYBACK, codec_dai,
+                                         SNDRV_PCM_STREAM_PLAYBACK,
                                          SND_SOC_DAPM_STREAM_RESUME);
 
                snd_soc_dapm_stream_event(&card->rtd[i],
-                                         SNDRV_PCM_STREAM_CAPTURE, codec_dai,
+                                         SNDRV_PCM_STREAM_CAPTURE,
                                          SND_SOC_DAPM_STREAM_RESUME);
        }
 
@@ -737,6 +762,10 @@ static void soc_resume_deferred(struct work_struct *work)
 
        /* userspace can access us now we are back as we were before */
        snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
+
+       /* Recheck all analogue paths too */
+       dapm_mark_io_dirty(&card->dapm);
+       snd_soc_dapm_sync(&card->dapm);
 }
 
 /* powers up audio subsystem after a suspend */
@@ -789,37 +818,30 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
        struct snd_soc_dai *codec_dai, *cpu_dai;
        const char *platform_name;
 
-       if (rtd->complete)
-               return 1;
        dev_dbg(card->dev, "binding %s at idx %d\n", dai_link->name, num);
 
-       /* do we already have the CPU DAI for this link ? */
-       if (rtd->cpu_dai) {
-               goto find_codec;
-       }
-       /* no, then find CPU DAI from registered DAIs*/
+       /* Find CPU DAI from registered DAIs*/
        list_for_each_entry(cpu_dai, &dai_list, list) {
-               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;
-               }
+               if (dai_link->cpu_of_node &&
+                   (cpu_dai->dev->of_node != dai_link->cpu_of_node))
+                       continue;
+               if (dai_link->cpu_name &&
+                   strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name))
+                       continue;
+               if (dai_link->cpu_dai_name &&
+                   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);
 
-find_codec:
-       /* do we already have the CODEC for this link ? */
-       if (rtd->codec) {
-               goto find_platform;
+       if (!rtd->cpu_dai) {
+               dev_err(card->dev, "CPU DAI %s not registered\n",
+                       dai_link->cpu_dai_name);
+               return -EPROBE_DEFER;
        }
 
-       /* no, then find CODEC from registered CODECs*/
+       /* Find CODEC from registered CODECs */
        list_for_each_entry(codec, &codec_list, list) {
                if (dai_link->codec_of_node) {
                        if (codec->dev->of_node != dai_link->codec_of_node)
@@ -841,28 +863,28 @@ find_codec:
                                        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);
 
-               goto find_platform;
+               if (!rtd->codec_dai) {
+                       dev_err(card->dev, "CODEC DAI %s not registered\n",
+                               dai_link->codec_dai_name);
+                       return -EPROBE_DEFER;
+               }
        }
-       dev_dbg(card->dev, "CODEC %s not registered\n",
-                       dai_link->codec_name);
 
-find_platform:
-       /* do we need a platform? */
-       if (rtd->platform)
-               goto out;
+       if (!rtd->codec) {
+               dev_err(card->dev, "CODEC %s not registered\n",
+                       dai_link->codec_name);
+               return -EPROBE_DEFER;
+       }
 
        /* if there's no platform we match on the empty platform */
        platform_name = dai_link->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 */
+       /* find one from the set of registered platforms */
        list_for_each_entry(platform, &platform_list, list) {
                if (dai_link->platform_of_node) {
                        if (platform->dev->of_node !=
@@ -874,20 +896,38 @@ find_platform:
                }
 
                rtd->platform = platform;
-               goto out;
        }
-
-       dev_dbg(card->dev, "platform %s not registered\n",
+       if (!rtd->platform) {
+               dev_err(card->dev, "platform %s not registered\n",
                        dai_link->platform_name);
+               return -EPROBE_DEFER;
+       }
+
+       card->num_rtd++;
+
        return 0;
+}
 
-out:
-       /* mark rtd as complete if we found all 4 of our client devices */
-       if (rtd->codec && rtd->codec_dai && rtd->platform && rtd->cpu_dai) {
-               rtd->complete = 1;
-               card->num_rtd++;
+static int soc_remove_platform(struct snd_soc_platform *platform)
+{
+       int ret;
+
+       if (platform->driver->remove) {
+               ret = platform->driver->remove(platform);
+               if (ret < 0)
+                       pr_err("asoc: failed to remove %s: %d\n",
+                               platform->name, ret);
        }
-       return 1;
+
+       /* 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);
+
+       return 0;
 }
 
 static void soc_remove_codec(struct snd_soc_codec *codec)
@@ -911,11 +951,9 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
        module_put(codec->dev->driver->owner);
 }
 
-static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
+static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
        int err;
 
@@ -940,30 +978,6 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
                list_del(&codec_dai->card_list);
        }
 
-       /* remove the platform */
-       if (platform && platform->probed &&
-                       platform->driver->remove_order == order) {
-               if (platform->driver->remove) {
-                       err = platform->driver->remove(platform);
-                       if (err < 0)
-                               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);
-       }
-
-       /* remove the CODEC */
-       if (codec && codec->probed &&
-                       codec->driver->remove_order == order)
-               soc_remove_codec(codec);
-
        /* remove the cpu_dai */
        if (cpu_dai && cpu_dai->probed &&
                        cpu_dai->driver->remove_order == order) {
@@ -975,7 +989,43 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
                }
                cpu_dai->probed = 0;
                list_del(&cpu_dai->card_list);
-               module_put(cpu_dai->dev->driver->owner);
+
+               if (!cpu_dai->codec) {
+                       snd_soc_dapm_free(&cpu_dai->dapm);
+                       module_put(cpu_dai->dev->driver->owner);
+               }
+       }
+}
+
+static void soc_remove_link_components(struct snd_soc_card *card, int num,
+                                      int order)
+{
+       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_codec *codec;
+
+       /* remove the platform */
+       if (platform && platform->probed &&
+           platform->driver->remove_order == order) {
+               soc_remove_platform(platform);
+       }
+
+       /* remove the CODEC-side CODEC */
+       if (codec_dai) {
+               codec = codec_dai->codec;
+               if (codec && codec->probed &&
+                   codec->driver->remove_order == order)
+                       soc_remove_codec(codec);
+       }
+
+       /* remove any CPU-side CODEC */
+       if (cpu_dai) {
+               codec = cpu_dai->codec;
+               if (codec && codec->probed &&
+                   codec->driver->remove_order == order)
+                       soc_remove_codec(codec);
        }
 }
 
@@ -986,8 +1036,15 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
        for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
                        order++) {
                for (dai = 0; dai < card->num_rtd; dai++)
-                       soc_remove_dai_link(card, dai, order);
+                       soc_remove_link_dais(card, dai, order);
+       }
+
+       for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+                       order++) {
+               for (dai = 0; dai < card->num_rtd; dai++)
+                       soc_remove_link_components(card, dai, order);
        }
+
        card->num_rtd = 0;
 }
 
@@ -1048,6 +1105,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
                }
        }
 
+       /* If the driver didn't set I/O up try regmap */
+       if (!codec->write && dev_get_regmap(codec->dev, NULL))
+               snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
+
        if (driver->controls)
                snd_soc_add_codec_controls(codec, driver->controls,
                                     driver->num_controls);
@@ -1074,6 +1135,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
 {
        int ret = 0;
        const struct snd_soc_platform_driver *driver = platform->driver;
+       struct snd_soc_dai *dai;
 
        platform->card = card;
        platform->dapm.card = card;
@@ -1087,6 +1149,14 @@ static int soc_probe_platform(struct snd_soc_card *card,
                snd_soc_dapm_new_controls(&platform->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 != platform->dev)
+                       continue;
+
+               snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+       }
+
        platform->dapm.idle_bias_off = 1;
 
        if (driver->probe) {
@@ -1176,6 +1246,10 @@ static int soc_post_component_init(struct snd_soc_card *card,
        rtd->dev->init_name = name;
        dev_set_drvdata(rtd->dev, rtd);
        mutex_init(&rtd->pcm_mutex);
+       INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].be_clients);
+       INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].be_clients);
+       INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].fe_clients);
+       INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
        ret = device_add(rtd->dev);
        if (ret < 0) {
                dev_err(card->dev,
@@ -1197,23 +1271,72 @@ static int soc_post_component_init(struct snd_soc_card *card,
                dev_err(codec->dev,
                        "asoc: failed to add codec sysfs files: %d\n", ret);
 
+#ifdef CONFIG_DEBUG_FS
+       /* add DPCM sysfs entries */
+       if (!dailess && !dai_link->dynamic)
+               goto out;
+
+       ret = soc_dpcm_debugfs_add(rtd);
+       if (ret < 0)
+               dev_err(rtd->dev, "asoc: failed to add dpcm sysfs entries: %d\n", ret);
+
+out:
+#endif
+       return 0;
+}
+
+static int soc_probe_link_components(struct snd_soc_card *card, int num,
+                                    int order)
+{
+       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_platform *platform = rtd->platform;
+       int ret;
+
+       /* probe the CPU-side component, if it is a CODEC */
+       if (cpu_dai->codec &&
+           !cpu_dai->codec->probed &&
+           cpu_dai->codec->driver->probe_order == order) {
+               ret = soc_probe_codec(card, cpu_dai->codec);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* probe the CODEC-side component */
+       if (!codec_dai->codec->probed &&
+           codec_dai->codec->driver->probe_order == order) {
+               ret = soc_probe_codec(card, codec_dai->codec);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* probe the platform */
+       if (!platform->probed &&
+           platform->driver->probe_order == order) {
+               ret = soc_probe_platform(card, platform);
+               if (ret < 0)
+                       return ret;
+       }
+
        return 0;
 }
 
-static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
+static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dapm_widget *play_w, *capture_w;
        int ret;
 
        dev_dbg(card->dev, "probe %s dai link %d late %d\n",
                        card->name, num, order);
 
        /* config components */
-       codec_dai->codec = codec;
        cpu_dai->platform = platform;
        codec_dai->card = card;
        cpu_dai->card = card;
@@ -1224,8 +1347,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
        /* probe the cpu_dai */
        if (!cpu_dai->probed &&
                        cpu_dai->driver->probe_order == order) {
-               if (!try_module_get(cpu_dai->dev->driver->owner))
-                       return -ENODEV;
+               if (!cpu_dai->codec) {
+                       cpu_dai->dapm.card = card;
+                       if (!try_module_get(cpu_dai->dev->driver->owner))
+                               return -ENODEV;
+
+                       list_add(&cpu_dai->dapm.list, &card->dapm_list);
+                       snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
+               }
 
                if (cpu_dai->driver->probe) {
                        ret = cpu_dai->driver->probe(cpu_dai);
@@ -1241,22 +1370,6 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
                list_add(&cpu_dai->card_list, &card->dai_dev_list);
        }
 
-       /* probe the CODEC */
-       if (!codec->probed &&
-                       codec->driver->probe_order == order) {
-               ret = soc_probe_codec(card, codec);
-               if (ret < 0)
-                       return ret;
-       }
-
-       /* probe the platform */
-       if (!platform->probed &&
-                       platform->driver->probe_order == order) {
-               ret = soc_probe_platform(card, platform);
-               if (ret < 0)
-                       return ret;
-       }
-
        /* probe the CODEC DAI */
        if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
                if (codec_dai->driver->probe) {
@@ -1285,12 +1398,50 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
        if (ret < 0)
                pr_warn("asoc: failed to add pmdown_time sysfs:%d\n", ret);
 
-       /* create the pcm */
-       ret = soc_new_pcm(rtd, num);
-       if (ret < 0) {
-               pr_err("asoc: can't create pcm %s :%d\n",
-                               dai_link->stream_name, ret);
-               return ret;
+       if (cpu_dai->driver->compress_dai) {
+               /*create compress_device"*/
+               ret = soc_new_compress(rtd, num);
+               if (ret < 0) {
+                       pr_err("asoc: can't create compress %s\n",
+                                        dai_link->stream_name);
+                       return ret;
+               }
+       } else {
+
+               if (!dai_link->params) {
+                       /* create the pcm */
+                       ret = soc_new_pcm(rtd, num);
+                       if (ret < 0) {
+                               pr_err("asoc: can't create pcm %s :%d\n",
+                                      dai_link->stream_name, ret);
+                               return ret;
+                       }
+               } else {
+                       /* link the DAI widgets */
+                       play_w = codec_dai->playback_widget;
+                       capture_w = cpu_dai->capture_widget;
+                       if (play_w && capture_w) {
+                               ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+                                                  capture_w, play_w);
+                               if (ret != 0) {
+                                       dev_err(card->dev, "Can't link %s to %s: %d\n",
+                                               play_w->name, capture_w->name, ret);
+                                       return ret;
+                               }
+                       }
+
+                       play_w = cpu_dai->playback_widget;
+                       capture_w = codec_dai->capture_widget;
+                       if (play_w && capture_w) {
+                               ret = snd_soc_dapm_new_pcm(card, dai_link->params,
+                                                  capture_w, play_w);
+                               if (ret != 0) {
+                                       dev_err(card->dev, "Can't link %s to %s: %d\n",
+                                               play_w->name, capture_w->name, ret);
+                                       return ret;
+                               }
+                       }
+               }
        }
 
        /* add platform data for AC97 devices */
@@ -1340,6 +1491,22 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec)
 }
 #endif
 
+static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+{
+       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+       struct snd_soc_codec *codec;
+
+       /* find CODEC from registered CODECs*/
+       list_for_each_entry(codec, &codec_list, list) {
+               if (!strcmp(codec->name, aux_dev->codec_name))
+                       return 0;
+       }
+
+       dev_err(card->dev, "%s not registered\n", aux_dev->codec_name);
+
+       return -EPROBE_DEFER;
+}
+
 static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
@@ -1360,7 +1527,7 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
        }
        /* codec not found */
        dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name);
-       goto out;
+       return -EPROBE_DEFER;
 
 found:
        ret = soc_probe_codec(card, codec);
@@ -1410,29 +1577,28 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec,
        return 0;
 }
 
-static void snd_soc_instantiate_card(struct snd_soc_card *card)
+static int 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);
+       int ret, i, order, dai_fmt;
 
-       if (card->instantiated) {
-               mutex_unlock(&card->mutex);
-               return;
-       }
+       mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
 
        /* bind DAIs */
-       for (i = 0; i < card->num_links; i++)
-               soc_bind_dai_link(card, i);
+       for (i = 0; i < card->num_links; i++) {
+               ret = soc_bind_dai_link(card, i);
+               if (ret != 0)
+                       goto base_error;
+       }
 
-       /* bind completed ? */
-       if (card->num_rtd != card->num_links) {
-               mutex_unlock(&card->mutex);
-               return;
+       /* check aux_devs too */
+       for (i = 0; i < card->num_aux_devs; i++) {
+               ret = soc_check_aux_dev(card, i);
+               if (ret != 0)
+                       goto base_error;
        }
 
        /* initialize the register cache for each available codec */
@@ -1452,10 +1618,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                        }
                }
                ret = snd_soc_init_codec_cache(codec, compress_type);
-               if (ret < 0) {
-                       mutex_unlock(&card->mutex);
-                       return;
-               }
+               if (ret < 0)
+                       goto base_error;
        }
 
        /* card bind complete so register a sound card */
@@ -1464,8 +1628,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        if (ret < 0) {
                pr_err("asoc: can't create sound card for card %s: %d\n",
                        card->name, ret);
-               mutex_unlock(&card->mutex);
-               return;
+               goto base_error;
        }
        card->snd_card->dev = card->dev;
 
@@ -1494,14 +1657,27 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                        goto card_probe_error;
        }
 
-       /* early DAI link probe */
+       /* probe all components used by DAI links on this card */
        for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
                        order++) {
                for (i = 0; i < card->num_links; i++) {
-                       ret = soc_probe_dai_link(card, i, order);
+                       ret = soc_probe_link_components(card, i, order);
                        if (ret < 0) {
                                pr_err("asoc: failed to instantiate card %s: %d\n",
-                              card->name, ret);
+                                      card->name, ret);
+                               goto probe_dai_err;
+                       }
+               }
+       }
+
+       /* probe all DAI links on this card */
+       for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+                       order++) {
+               for (i = 0; i < card->num_links; i++) {
+                       ret = soc_probe_link_dais(card, i, order);
+                       if (ret < 0) {
+                               pr_err("asoc: failed to instantiate card %s: %d\n",
+                                      card->name, ret);
                                goto probe_dai_err;
                        }
                }
@@ -1529,17 +1705,47 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
 
        for (i = 0; i < card->num_links; i++) {
                dai_link = &card->dai_link[i];
+               dai_fmt = dai_link->dai_fmt;
 
-               if (dai_link->dai_fmt) {
+               if (dai_fmt) {
                        ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
-                                                 dai_link->dai_fmt);
+                                                 dai_fmt);
                        if (ret != 0 && ret != -ENOTSUPP)
                                dev_warn(card->rtd[i].codec_dai->dev,
                                         "Failed to set DAI format: %d\n",
                                         ret);
+               }
+
+               /* If this is a regular CPU link there will be a platform */
+               if (dai_fmt &&
+                   (dai_link->platform_name || dai_link->platform_of_node)) {
+                       ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
+                                                 dai_fmt);
+                       if (ret != 0 && ret != -ENOTSUPP)
+                               dev_warn(card->rtd[i].cpu_dai->dev,
+                                        "Failed to set DAI format: %d\n",
+                                        ret);
+               } else if (dai_fmt) {
+                       /* Flip the polarity for the "CPU" end */
+                       dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+                       switch (dai_link->dai_fmt &
+                               SND_SOC_DAIFMT_MASTER_MASK) {
+                       case SND_SOC_DAIFMT_CBM_CFM:
+                               dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+                               break;
+                       case SND_SOC_DAIFMT_CBM_CFS:
+                               dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+                               break;
+                       case SND_SOC_DAIFMT_CBS_CFM:
+                               dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+                               break;
+                       case SND_SOC_DAIFMT_CBS_CFS:
+                               dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+                               break;
+                       }
 
                        ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
-                                                 dai_link->dai_fmt);
+                                                 dai_fmt);
                        if (ret != 0 && ret != -ENOTSUPP)
                                dev_warn(card->rtd[i].cpu_dai->dev,
                                         "Failed to set DAI format: %d\n",
@@ -1605,7 +1811,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        card->instantiated = 1;
        snd_soc_dapm_sync(&card->dapm);
        mutex_unlock(&card->mutex);
-       return;
+
+       return 0;
 
 probe_aux_dev_err:
        for (i = 0; i < card->num_aux_devs; i++)
@@ -1620,25 +1827,16 @@ card_probe_error:
 
        snd_card_free(card->snd_card);
 
+base_error:
        mutex_unlock(&card->mutex);
-}
 
-/*
- * Attempt to initialise any uninitialised cards.  Must be called with
- * client_mutex.
- */
-static void snd_soc_instantiate_cards(void)
-{
-       struct snd_soc_card *card;
-       list_for_each_entry(card, &card_list, list)
-               snd_soc_instantiate_card(card);
+       return ret;
 }
 
 /* probes a new socdev */
 static int soc_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
-       int ret = 0;
 
        /*
         * no card, so machine driver should be registering card
@@ -1654,13 +1852,7 @@ static int soc_probe(struct platform_device *pdev)
        /* Bodge while we unpick instantiation */
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "Failed to register card\n");
-               return ret;
-       }
-
-       return 0;
+       return snd_soc_register_card(card);
 }
 
 static int soc_cleanup_card_resources(struct snd_soc_card *card)
@@ -1670,7 +1862,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
        /* make sure any delayed work runs */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
-               flush_delayed_work_sync(&rtd->delayed_work);
+               flush_delayed_work(&rtd->delayed_work);
        }
 
        /* remove auxiliary devices */
@@ -1714,7 +1906,7 @@ int snd_soc_poweroff(struct device *dev)
         * now, we're shutting down so no imminent restart. */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
-               flush_delayed_work_sync(&rtd->delayed_work);
+               flush_delayed_work(&rtd->delayed_work);
        }
 
        snd_soc_dapm_shutdown(card);
@@ -2221,16 +2413,14 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, bitmask;
+       unsigned int val;
 
-       for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
-               ;
        val = snd_soc_read(codec, e->reg);
        ucontrol->value.enumerated.item[0]
-               = (val >> e->shift_l) & (bitmask - 1);
+               = (val >> e->shift_l) & e->mask;
        if (e->shift_l != e->shift_r)
                ucontrol->value.enumerated.item[1] =
-                       (val >> e->shift_r) & (bitmask - 1);
+                       (val >> e->shift_r) & e->mask;
 
        return 0;
 }
@@ -2251,19 +2441,17 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val;
-       unsigned int mask, bitmask;
+       unsigned int mask;
 
-       for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
-               ;
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
        val = ucontrol->value.enumerated.item[0] << e->shift_l;
-       mask = (bitmask - 1) << e->shift_l;
+       mask = e->mask << e->shift_l;
        if (e->shift_l != e->shift_r) {
                if (ucontrol->value.enumerated.item[1] > e->max - 1)
                        return -EINVAL;
                val |= ucontrol->value.enumerated.item[1] << e->shift_r;
-               mask |= (bitmask - 1) << e->shift_r;
+               mask |= e->mask << e->shift_r;
        }
 
        return snd_soc_update_bits_locked(codec, e->reg, mask, val);
@@ -2533,29 +2721,110 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
 /**
- * snd_soc_info_volsw_s8 - signed mixer info callback
+ * snd_soc_get_volsw_sx - single mixer get callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
- * Callback to provide information about a signed 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.
  */
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
 {
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
+           (struct soc_mixer_control *)kcontrol->private_value;
+
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
        int min = mc->min;
+       int mask = (1 << (fls(min + max) - 1)) - 1;
 
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
+       ucontrol->value.integer.value[0] =
+           ((snd_soc_read(codec, reg) >> shift) - min) & mask;
 
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 2;
-       uinfo->value.integer.min = 0;
+       if (snd_soc_volsw_is_stereo(mc))
+               ucontrol->value.integer.value[1] =
+                       ((snd_soc_read(codec, reg2) >> rshift) - min) & mask;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: 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_sx(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+           (struct soc_mixer_control *)kcontrol->private_value;
+
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int mask = (1 << (fls(min + max) - 1)) - 1;
+       int err = 0;
+       unsigned short val, val_mask, val2 = 0;
+
+       val_mask = mask << shift;
+       val = (ucontrol->value.integer.value[0] + min) & mask;
+       val = val << shift;
+
+       if (snd_soc_update_bits_locked(codec, reg, val_mask, val))
+                       return err;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val_mask = mask << rshift;
+               val2 = (ucontrol->value.integer.value[1] + min) & mask;
+               val2 = val2 << rshift;
+
+               if (snd_soc_update_bits_locked(codec, reg2, val_mask, val2))
+                       return err;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
+ * snd_soc_info_volsw_s8 - signed mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a signed mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_s8(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;
+       int min = mc->min;
+
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
        uinfo->value.integer.max = platform_max - min;
        return 0;
 }
@@ -2615,136 +2884,141 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
 /**
- * snd_soc_limit_volume - Set new limit to an existing volume control.
- *
- * @codec: where to look for the control
- * @name: Name of the control
- * @max: new maximum limit
- *
- * Return 0 for success, else error.
- */
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
-       const char *name, int max)
-{
-       struct snd_card *card = codec->card->snd_card;
-       struct snd_kcontrol *kctl;
-       struct soc_mixer_control *mc;
-       int found = 0;
-       int ret = -EINVAL;
-
-       /* Sanity check for name and max */
-       if (unlikely(!name || max <= 0))
-               return -EINVAL;
-
-       list_for_each_entry(kctl, &card->controls, list) {
-               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
-                       found = 1;
-                       break;
-               }
-       }
-       if (found) {
-               mc = (struct soc_mixer_control *)kctl->private_value;
-               if (max <= mc->max) {
-                       mc->platform_max = max;
-                       ret = 0;
-               }
-       }
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-
-/**
- * snd_soc_info_volsw_2r_sx - double with tlv and variable data size
- *  mixer info callback
+ * snd_soc_info_volsw_range - single mixer info callback with range.
  * @kcontrol: mixer control
  * @uinfo: control element information
  *
- * Returns 0 for success.
+ * Callback to provide information, within a range, about a single
+ * mixer control.
+ *
+ * returns 0 for success.
  */
-int snd_soc_info_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-                       struct snd_ctl_elem_info *uinfo)
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
 {
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
-       int max = mc->max;
+       int platform_max;
        int min = mc->min;
 
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 2;
+       uinfo->count = 1;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = max-min;
+       uinfo->value.integer.max = platform_max - min;
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r_sx);
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
 
 /**
- * snd_soc_get_volsw_2r_sx - double with tlv and variable data size
- *  mixer get callback
+ * snd_soc_put_volsw_range - single mixer put value callback with range.
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
+ *
+ * Callback to set the value, within a range, for a single mixer control.
  *
  * Returns 0 for success.
  */
-int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-                       struct snd_ctl_elem_value *ucontrol)
+int snd_soc_put_volsw_range(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 mask = (1<<mc->shift)-1;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
        int min = mc->min;
-       int val = snd_soc_read(codec, mc->reg) & mask;
-       int valr = snd_soc_read(codec, mc->rreg) & mask;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       unsigned int val, val_mask;
 
-       ucontrol->value.integer.value[0] = ((val & 0xff)-min) & mask;
-       ucontrol->value.integer.value[1] = ((valr & 0xff)-min) & mask;
-       return 0;
+       val = ((ucontrol->value.integer.value[0] + min) & mask);
+       if (invert)
+               val = max - val;
+       val_mask = mask << shift;
+       val = val << shift;
+
+       return snd_soc_update_bits_locked(codec, reg, val_mask, val);
 }
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r_sx);
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
 
 /**
- * snd_soc_put_volsw_2r_sx - double with tlv and variable data size
- *  mixer put callback
+ * snd_soc_get_volsw_range - single mixer get callback with range
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
+ *
+ * Callback to get the value, within a range, of a single mixer control.
  *
  * Returns 0 for success.
  */
-int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
-                       struct snd_ctl_elem_value *ucontrol)
+int snd_soc_get_volsw_range(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 mask = (1<<mc->shift)-1;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
        int min = mc->min;
-       int ret;
-       unsigned int val, valr, oval, ovalr;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
 
-       val = ((ucontrol->value.integer.value[0]+min) & 0xff);
-       val &= mask;
-       valr = ((ucontrol->value.integer.value[1]+min) & 0xff);
-       valr &= mask;
+       ucontrol->value.integer.value[0] =
+               (snd_soc_read(codec, reg) >> shift) & mask;
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       max - ucontrol->value.integer.value[0];
+       ucontrol->value.integer.value[0] =
+               ucontrol->value.integer.value[0] - min;
 
-       oval = snd_soc_read(codec, mc->reg) & mask;
-       ovalr = snd_soc_read(codec, mc->rreg) & mask;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
 
-       ret = 0;
-       if (oval != val) {
-               ret = snd_soc_write(codec, mc->reg, val);
-               if (ret < 0)
-                       return ret;
+/**
+ * snd_soc_limit_volume - Set new limit to an existing volume control.
+ *
+ * @codec: where to look for the control
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+       const char *name, int max)
+{
+       struct snd_card *card = codec->card->snd_card;
+       struct snd_kcontrol *kctl;
+       struct soc_mixer_control *mc;
+       int found = 0;
+       int ret = -EINVAL;
+
+       /* Sanity check for name and max */
+       if (unlikely(!name || max <= 0))
+               return -EINVAL;
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+                       found = 1;
+                       break;
+               }
        }
-       if (ovalr != valr) {
-               ret = snd_soc_write(codec, mc->rreg, valr);
-               if (ret < 0)
-                       return ret;
+       if (found) {
+               mc = (struct soc_mixer_control *)kctl->private_value;
+               if (max <= mc->max) {
+                       mc->platform_max = max;
+                       ret = 0;
+               }
        }
-
-       return 0;
+       return ret;
 }
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r_sx);
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
 
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_info *uinfo)
@@ -2855,6 +3129,186 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
 
+/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = mc->min;
+       uinfo->value.integer.max = mc->max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int regbase = mc->regbase;
+       unsigned int regcount = mc->regcount;
+       unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+       unsigned int regwmask = (1<<regwshift)-1;
+       unsigned int invert = mc->invert;
+       unsigned long mask = (1UL<<mc->nbits)-1;
+       long min = mc->min;
+       long max = mc->max;
+       long val = 0;
+       unsigned long regval;
+       unsigned int i;
+
+       for (i = 0; i < regcount; i++) {
+               regval = snd_soc_read(codec, regbase+i) & regwmask;
+               val |= regval << (regwshift*(regcount-i-1));
+       }
+       val &= mask;
+       if (min < 0 && val > max)
+               val |= ~mask;
+       if (invert)
+               val = max - val;
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int regbase = mc->regbase;
+       unsigned int regcount = mc->regcount;
+       unsigned int regwshift = codec->driver->reg_word_size * BITS_PER_BYTE;
+       unsigned int regwmask = (1<<regwshift)-1;
+       unsigned int invert = mc->invert;
+       unsigned long mask = (1UL<<mc->nbits)-1;
+       long max = mc->max;
+       long val = ucontrol->value.integer.value[0];
+       unsigned int i, regval, regmask;
+       int err;
+
+       if (invert)
+               val = max - val;
+       val &= mask;
+       for (i = 0; i < regcount; i++) {
+               regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+               regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+               err = snd_soc_update_bits_locked(codec, regbase+i,
+                               regmask, regval);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(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 shift = mc->shift;
+       unsigned int mask = 1 << shift;
+       unsigned int invert = mc->invert != 0;
+       unsigned int val = snd_soc_read(codec, reg) & mask;
+
+       if (shift != 0 && val != 0)
+               val = val >> shift;
+       ucontrol->value.enumerated.item[0] = val ^ invert;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(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 shift = mc->shift;
+       unsigned int mask = 1 << shift;
+       unsigned int invert = mc->invert != 0;
+       unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+       unsigned int val1 = (strobe ^ invert) ? mask : 0;
+       unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+       int err;
+
+       err = snd_soc_update_bits_locked(codec, reg, mask, val1);
+       if (err < 0)
+               return err;
+
+       err = snd_soc_update_bits_locked(codec, reg, mask, val2);
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
+
 /**
  * snd_soc_dai_set_sysclk - configure DAI system or master clock.
  * @dai: DAI
@@ -3054,7 +3508,7 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
        if (dai->driver && dai->driver->ops->digital_mute)
                return dai->driver->ops->digital_mute(dai, mute);
        else
-               return -EINVAL;
+               return -ENOTSUPP;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
@@ -3066,7 +3520,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
  */
 int snd_soc_register_card(struct snd_soc_card *card)
 {
-       int i;
+       int i, ret;
 
        if (!card->name || !card->dev)
                return -EINVAL;
@@ -3084,6 +3538,12 @@ int snd_soc_register_card(struct snd_soc_card *card)
                                link->name);
                        return -EINVAL;
                }
+               /* Codec DAI name must be specified */
+               if (!link->codec_dai_name) {
+                       dev_err(card->dev, "codec_dai_name not set for %s\n",
+                               link->name);
+                       return -EINVAL;
+               }
 
                /*
                 * Platform may be specified by either name or OF node, but
@@ -3096,12 +3556,24 @@ int snd_soc_register_card(struct snd_soc_card *card)
                }
 
                /*
-                * CPU DAI must be specified by 1 of name or OF node,
-                * not both or neither.
+                * CPU device may be specified by either name or OF node, but
+                * can be left unspecified, and will be matched based on DAI
+                * name alone..
                 */
-               if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) {
+               if (link->cpu_name && link->cpu_of_node) {
                        dev_err(card->dev,
-                               "Neither/both cpu_dai name/of_node are set for %s\n",
+                               "Neither/both cpu name/of_node are set for %s\n",
+                               link->name);
+                       return -EINVAL;
+               }
+               /*
+                * At least one of CPU DAI name or CPU device name/node must be
+                * specified
+                */
+               if (!link->cpu_dai_name &&
+                   !(link->cpu_name || link->cpu_of_node)) {
+                       dev_err(card->dev,
+                               "Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
                                link->name);
                        return -EINVAL;
                }
@@ -3129,15 +3601,13 @@ int snd_soc_register_card(struct snd_soc_card *card)
        INIT_LIST_HEAD(&card->dapm_dirty);
        card->instantiated = 0;
        mutex_init(&card->mutex);
+       mutex_init(&card->dapm_mutex);
 
-       mutex_lock(&client_mutex);
-       list_add(&card->list, &card_list);
-       snd_soc_instantiate_cards();
-       mutex_unlock(&client_mutex);
-
-       dev_dbg(card->dev, "Registered card '%s'\n", card->name);
+       ret = snd_soc_instantiate_card(card);
+       if (ret != 0)
+               soc_cleanup_card_debugfs(card);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_register_card);
 
@@ -3151,9 +3621,6 @@ int snd_soc_unregister_card(struct snd_soc_card *card)
 {
        if (card->instantiated)
                soc_cleanup_card_resources(card);
-       mutex_lock(&client_mutex);
-       list_del(&card->list);
-       mutex_unlock(&client_mutex);
        dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
 
        return 0;
@@ -3227,6 +3694,7 @@ static inline char *fmt_multiple_name(struct device *dev,
 int snd_soc_register_dai(struct device *dev,
                struct snd_soc_dai_driver *dai_drv)
 {
+       struct snd_soc_codec *codec;
        struct snd_soc_dai *dai;
 
        dev_dbg(dev, "dai register %s\n", dev_name(dev));
@@ -3244,12 +3712,26 @@ int snd_soc_register_dai(struct device *dev,
 
        dai->dev = dev;
        dai->driver = dai_drv;
+       dai->dapm.dev = dev;
        if (!dai->driver->ops)
                dai->driver->ops = &null_dai_ops;
 
        mutex_lock(&client_mutex);
+
+       list_for_each_entry(codec, &codec_list, list) {
+               if (codec->dev == dev) {
+                       dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+                               dai->name, codec->name);
+                       dai->codec = codec;
+                       break;
+               }
+       }
+
+       if (!dai->codec)
+               dai->dapm.idle_bias_off = 1;
+
        list_add(&dai->list, &dai_list);
-       snd_soc_instantiate_cards();
+
        mutex_unlock(&client_mutex);
 
        pr_debug("Registered DAI '%s'\n", dai->name);
@@ -3293,6 +3775,7 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
 int snd_soc_register_dais(struct device *dev,
                struct snd_soc_dai_driver *dai_drv, size_t count)
 {
+       struct snd_soc_codec *codec;
        struct snd_soc_dai *dai;
        int i, ret = 0;
 
@@ -3320,19 +3803,31 @@ int snd_soc_register_dais(struct device *dev,
                        dai->id = dai->driver->id;
                else
                        dai->id = i;
+               dai->dapm.dev = dev;
                if (!dai->driver->ops)
                        dai->driver->ops = &null_dai_ops;
 
                mutex_lock(&client_mutex);
+
+               list_for_each_entry(codec, &codec_list, list) {
+                       if (codec->dev == dev) {
+                               dev_dbg(dev, "Mapped DAI %s to CODEC %s\n",
+                                       dai->name, codec->name);
+                               dai->codec = codec;
+                               break;
+                       }
+               }
+
+               if (!dai->codec)
+                       dai->dapm.idle_bias_off = 1;
+
                list_add(&dai->list, &dai_list);
+
                mutex_unlock(&client_mutex);
 
                pr_debug("Registered DAI '%s'\n", dai->name);
        }
 
-       mutex_lock(&client_mutex);
-       snd_soc_instantiate_cards();
-       mutex_unlock(&client_mutex);
        return 0;
 
 err:
@@ -3390,7 +3885,6 @@ int snd_soc_register_platform(struct device *dev,
 
        mutex_lock(&client_mutex);
        list_add(&platform->list, &platform_list);
-       snd_soc_instantiate_cards();
        mutex_unlock(&client_mutex);
 
        pr_debug("Registered platform '%s'\n", platform->name);
@@ -3540,24 +4034,22 @@ int snd_soc_register_codec(struct device *dev,
                fixup_codec_formats(&dai_drv[i].capture);
        }
 
+       mutex_lock(&client_mutex);
+       list_add(&codec->list, &codec_list);
+       mutex_unlock(&client_mutex);
+
        /* register any DAIs */
        if (num_dai) {
                ret = snd_soc_register_dais(dev, dai_drv, num_dai);
                if (ret < 0)
-                       goto fail;
+                       dev_err(codec->dev, "Failed to regster DAIs: %d\n",
+                               ret);
        }
 
-       mutex_lock(&client_mutex);
-       list_add(&codec->list, &codec_list);
-       snd_soc_instantiate_cards();
-       mutex_unlock(&client_mutex);
-
        pr_debug("Registered codec '%s'\n", codec->name);
        return 0;
 
 fail:
-       kfree(codec->reg_def_copy);
-       codec->reg_def_copy = NULL;
        kfree(codec->name);
        kfree(codec);
        return ret;
@@ -3660,6 +4152,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                        dev_err(card->dev,
                                "Property '%s' index %d could not be read: %d\n",
                                propname, 2 * i, ret);
+                       kfree(routes);
                        return -EINVAL;
                }
                ret = of_property_read_string_index(np, propname,
@@ -3668,6 +4161,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                        dev_err(card->dev,
                                "Property '%s' index %d could not be read: %d\n",
                                propname, (2 * i) + 1, ret);
+                       kfree(routes);
                        return -EINVAL;
                }
        }