Merge remote-tracking branch 'asoc/topic/component' into asoc-next
authorMark Brown <broonie@linaro.org>
Mon, 4 Aug 2014 15:31:15 +0000 (16:31 +0100)
committerMark Brown <broonie@linaro.org>
Mon, 4 Aug 2014 15:31:15 +0000 (16:31 +0100)
23 files changed:
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/trace/events/asoc.h
sound/soc/codecs/ac97.c
sound/soc/codecs/cx20442.c
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm_adsp.c
sound/soc/omap/ams-delta.c
sound/soc/soc-cache.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-pcm.c
sound/soc/tegra/tegra_alc5632.c
sound/soc/tegra/tegra_max98090.c
sound/soc/tegra/tegra_rt5640.c
sound/soc/tegra/tegra_wm8753.c
sound/soc/tegra/tegra_wm8903.c
sound/soc/tegra/trimslice.c

index 688f2ba8009fc0a713d716a0e1fc35b1ceb750f6..e8b3080d196a7d6a336caa7b56ede832f4e44607 100644 (file)
@@ -257,7 +257,6 @@ struct snd_soc_dai {
 
        struct snd_soc_dapm_widget *playback_widget;
        struct snd_soc_dapm_widget *capture_widget;
-       struct snd_soc_dapm_context dapm;
 
        /* DAI DMA data */
        void *playback_dma_data;
@@ -273,6 +272,10 @@ struct snd_soc_dai {
        struct snd_soc_codec *codec;
        struct snd_soc_component *component;
 
+       /* CODEC TDM slot masks and params (for fixup) */
+       unsigned int tx_mask;
+       unsigned int rx_mask;
+
        struct snd_soc_card *card;
 
        struct list_head list;
index 6b59471cdf447dd2ec69f046ee4ed5539ccdf3ea..aac04ff84eea5674279fd22984db6ca9b79ba8c7 100644 (file)
@@ -431,7 +431,7 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
                                           const char *pin);
 int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
                                const char *pin);
-void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec);
+void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
 
 /* Mostly internal - should not normally be used */
 void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
@@ -441,6 +441,8 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        struct snd_soc_dapm_widget_list **list);
 
 struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol);
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
+       struct snd_kcontrol *kcontrol);
 
 /* dapm widget types */
 enum snd_soc_dapm_type {
@@ -524,7 +526,6 @@ struct snd_soc_dapm_widget {
        const char *name;               /* widget name */
        const char *sname;      /* stream name */
        struct snd_soc_codec *codec;
-       struct snd_soc_platform *platform;
        struct list_head list;
        struct snd_soc_dapm_context *dapm;
 
@@ -593,7 +594,6 @@ struct snd_soc_dapm_context {
        struct device *dev; /* from parent - for debug */
        struct snd_soc_component *component; /* parent component */
        struct snd_soc_codec *codec; /* parent codec */
-       struct snd_soc_platform *platform; /* parent platform */
        struct snd_soc_card *card; /* parent card */
 
        /* used during DAPM updates */
@@ -601,6 +601,8 @@ struct snd_soc_dapm_context {
        struct list_head list;
 
        int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
+       int (*set_bias_level)(struct snd_soc_dapm_context *dapm,
+                             enum snd_soc_bias_level level);
 
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_dapm;
index ed9e2d7e5fdc531cd8a8cb846fcc2204f01131db..50ff6f3459aabb4343e7a6d2fb32bae3727f4d87 100644 (file)
@@ -436,6 +436,10 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
 int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
                int cmd, struct snd_soc_platform *platform);
 
+int soc_dai_hw_params(struct snd_pcm_substream *substream,
+                     struct snd_pcm_hw_params *params,
+                     struct snd_soc_dai *dai);
+
 /* Jack reporting */
 int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
                     struct snd_soc_jack *jack);
@@ -503,10 +507,12 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
                                  const char *prefix);
 struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
                                               const char *name);
+int snd_soc_add_component_controls(struct snd_soc_component *component,
+       const struct snd_kcontrol_new *controls, unsigned int num_controls);
 int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
-       const struct snd_kcontrol_new *controls, int num_controls);
+       const struct snd_kcontrol_new *controls, unsigned int num_controls);
 int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
-       const struct snd_kcontrol_new *controls, int num_controls);
+       const struct snd_kcontrol_new *controls, unsigned int num_controls);
 int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
        const struct snd_kcontrol_new *controls, int num_controls);
 int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
@@ -677,12 +683,17 @@ struct snd_soc_component_driver {
        int (*of_xlate_dai_name)(struct snd_soc_component *component,
                                 struct of_phandle_args *args,
                                 const char **dai_name);
+       void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
+               int subseq);
+       int (*stream_event)(struct snd_soc_component *, int event);
 };
 
 struct snd_soc_component {
        const char *name;
        int id;
+       const char *name_prefix;
        struct device *dev;
+       struct snd_soc_card *card;
 
        unsigned int active;
 
@@ -705,18 +716,18 @@ struct snd_soc_component {
        int val_bytes;
 
        struct mutex io_mutex;
+
+       /* Don't use these, use snd_soc_component_get_dapm() */
+       struct snd_soc_dapm_context dapm;
+       struct snd_soc_dapm_context *dapm_ptr;
 };
 
 /* SoC Audio Codec device */
 struct snd_soc_codec {
-       const char *name;
-       const char *name_prefix;
-       int id;
        struct device *dev;
        const struct snd_soc_codec_driver *driver;
 
        struct mutex mutex;
-       struct snd_soc_card *card;
        struct list_head list;
        struct list_head card_list;
 
@@ -790,9 +801,6 @@ struct snd_soc_codec_driver {
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                             enum snd_soc_dapm_type, int);
 
-       /* codec stream completion event */
-       int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
-
        bool ignore_pmdown_time;  /* Doesn't benefit from pmdown delay */
 
        /* probe ordering - for components with runtime dependencies */
@@ -834,9 +842,6 @@ struct snd_soc_platform_driver {
        /* platform stream compress ops */
        const struct snd_compr_ops *compr_ops;
 
-       /* platform stream completion event */
-       int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
-
        /* probe ordering - for components with runtime dependencies */
        int probe_order;
        int remove_order;
@@ -847,23 +852,23 @@ struct snd_soc_platform_driver {
        int (*bespoke_trigger)(struct snd_pcm_substream *, int);
 };
 
-struct snd_soc_platform {
+struct snd_soc_dai_link_component {
        const char *name;
-       int id;
+       const struct device_node *of_node;
+       const char *dai_name;
+};
+
+struct snd_soc_platform {
        struct device *dev;
        const struct snd_soc_platform_driver *driver;
 
        unsigned int suspended:1; /* platform is suspended */
        unsigned int probed:1;
 
-       struct snd_soc_card *card;
        struct list_head list;
-       struct list_head card_list;
 
        struct snd_soc_component component;
 
-       struct snd_soc_dapm_context dapm;
-
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_platform_root;
 #endif
@@ -896,6 +901,10 @@ struct snd_soc_dai_link {
        const struct device_node *codec_of_node;
        /* You MUST specify the DAI name within the codec */
        const char *codec_dai_name;
+
+       struct snd_soc_dai_link_component *codecs;
+       unsigned int num_codecs;
+
        /*
         * You MAY specify the link's platform/PCM/DMA driver, either by
         * device name, or by DT/OF node, but not both. Some forms of link
@@ -1047,7 +1056,6 @@ struct snd_soc_card {
 
        /* lists of probed devices belonging to this card */
        struct list_head codec_dev_list;
-       struct list_head platform_dev_list;
 
        struct list_head widgets;
        struct list_head paths;
@@ -1094,6 +1102,9 @@ struct snd_soc_pcm_runtime {
        struct snd_soc_dai *codec_dai;
        struct snd_soc_dai *cpu_dai;
 
+       struct snd_soc_dai **codec_dais;
+       unsigned int num_codecs;
+
        struct delayed_work delayed_work;
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_dpcm_root;
@@ -1164,6 +1175,21 @@ static inline struct snd_soc_platform *snd_soc_component_to_platform(
        return container_of(component, struct snd_soc_platform, component);
 }
 
+/**
+ * snd_soc_dapm_to_component() - Casts a DAPM context to the component it is
+ *  embedded in
+ * @dapm: The DAPM context to cast to the component
+ *
+ * This function must only be used on DAPM contexts that are known to be part of
+ * a component (e.g. in a component driver). Otherwise the behavior is
+ * undefined.
+ */
+static inline struct snd_soc_component *snd_soc_dapm_to_component(
+       struct snd_soc_dapm_context *dapm)
+{
+       return container_of(dapm, struct snd_soc_component, dapm);
+}
+
 /**
  * snd_soc_dapm_to_codec() - Casts a DAPM context to the CODEC it is embedded in
  * @dapm: The DAPM context to cast to the CODEC
@@ -1188,7 +1214,18 @@ static inline struct snd_soc_codec *snd_soc_dapm_to_codec(
 static inline struct snd_soc_platform *snd_soc_dapm_to_platform(
        struct snd_soc_dapm_context *dapm)
 {
-       return container_of(dapm, struct snd_soc_platform, dapm);
+       return snd_soc_component_to_platform(snd_soc_dapm_to_component(dapm));
+}
+
+/**
+ * snd_soc_component_get_dapm() - Returns the DAPM context associated with a
+ *  component
+ * @component: The component for which to get the DAPM context
+ */
+static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm(
+       struct snd_soc_component *component)
+{
+       return component->dapm_ptr;
 }
 
 /* codec IO */
@@ -1261,7 +1298,6 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd)
 static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
 {
        INIT_LIST_HEAD(&card->codec_dev_list);
-       INIT_LIST_HEAD(&card->platform_dev_list);
        INIT_LIST_HEAD(&card->widgets);
        INIT_LIST_HEAD(&card->paths);
        INIT_LIST_HEAD(&card->dapm_list);
index c75c795a377b5c383b59c945c8608e4d8635da6f..0194a641e4e2589c71176fabee75c43ad1baec7d 100644 (file)
@@ -296,17 +296,17 @@ TRACE_EVENT(snd_soc_cache_sync,
        TP_ARGS(codec, type, status),
 
        TP_STRUCT__entry(
-               __string(       name,           codec->name     )
+               __string(       name,           codec->component.name)
                __string(       status,         status          )
                __string(       type,           type            )
                __field(        int,            id              )
        ),
 
        TP_fast_assign(
-               __assign_str(name, codec->name);
+               __assign_str(name, codec->component.name);
                __assign_str(status, status);
                __assign_str(type, type);
-               __entry->id = codec->id;
+               __entry->id = codec->component.id;
        ),
 
        TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
index 8d9ba4ba4bfe0b2fece2dc5c30f955fd5d6dcc00..e889e1b84192bcc8d0baf354e1a66a8892c72e4f 100644 (file)
@@ -89,8 +89,8 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
        int ret;
 
        /* add codec as bus device for standard ac97 */
-       ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL,
-                          &ac97_bus);
+       ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
+                          NULL, &ac97_bus);
        if (ret < 0)
                return ret;
 
index d5fd00a64748097ad1794ed9cc45178c513c6b68..4ba60eb2cde1136c7942f785366ebf932deeaba7 100644 (file)
@@ -253,7 +253,7 @@ static void v253_close(struct tty_struct *tty)
        /* Prevent the codec driver from further accessing the modem */
        codec->hw_write = NULL;
        cx20442->control_data = NULL;
-       codec->card->pop_time = 0;
+       codec->component.card->pop_time = 0;
 }
 
 /* Line discipline .hangup() */
@@ -281,7 +281,7 @@ static void v253_receive(struct tty_struct *tty,
                /* Set up codec driver access to modem controls */
                cx20442->control_data = tty;
                codec->hw_write = (hw_write_t)tty->ops->write;
-               codec->card->pop_time = 1;
+               codec->component.card->pop_time = 1;
        }
 }
 
@@ -372,7 +372,7 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
 
        snd_soc_codec_set_drvdata(codec, cx20442);
        codec->hw_write = NULL;
-       codec->card->pop_time = 0;
+       codec->component.card->pop_time = 0;
 
        return 0;
 }
index df3a7506c023b53787d9fa1d8d76d6c0ffb1403b..ff006cc9552006bc086ddd2b4402bff495a781d9 100644 (file)
@@ -1404,7 +1404,7 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
        if (dac33->irq >= 0) {
                ret = request_irq(dac33->irq, dac33_interrupt_handler,
                                  IRQF_TRIGGER_RISING,
-                                 codec->name, codec);
+                                 codec->component.name, codec);
                if (ret < 0) {
                        dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
                                                dac33->irq, ret);
index edf27acc1d77da6ba457cef5e8a403867e753fb2..12fc0aed7503188390a96966ff4c59626cb66325 100644 (file)
@@ -479,7 +479,7 @@ static struct snd_soc_dai_driver uda134x_dai = {
 static int uda134x_soc_probe(struct snd_soc_codec *codec)
 {
        struct uda134x_priv *uda134x;
-       struct uda134x_platform_data *pd = codec->card->dev->platform_data;
+       struct uda134x_platform_data *pd = codec->component.card->dev->platform_data;
        const struct snd_soc_dapm_widget *widgets;
        unsigned num_widgets;
 
index a145d0431b63dbc11c975f8b226a6f250ed17008..e96349b04ba6553a1c26b6a4bc532cb3d7d364de 100644 (file)
@@ -472,7 +472,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
         * list each time to find the desired power state do so now
         * and save the result.
         */
-       list_for_each_entry(w, &codec->card->widgets, list) {
+       list_for_each_entry(w, &codec->component.card->widgets, list) {
                if (w->dapm != &codec->dapm)
                        continue;
                if (strcmp(w->name, "LOUT1 PGA") == 0)
index 2537725dd53ff43563122dd8b2566c1ef08af5e8..f412a9911a75d2fe531930f6a0108fb977c41e77 100644 (file)
@@ -1382,7 +1382,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
        int ret;
        int val;
 
-       dsp->card = codec->card;
+       dsp->card = codec->component.card;
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
@@ -1617,7 +1617,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
 
-       dsp->card = codec->card;
+       dsp->card = codec->component.card;
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
index 0cc41f94de4e591b617486a7982327eb0a7eec13..8c9cc64a9dfb320a62ec0c204b57b1d5fcaae8f3 100644 (file)
@@ -301,7 +301,7 @@ static int cx81801_open(struct tty_struct *tty)
 static void cx81801_close(struct tty_struct *tty)
 {
        struct snd_soc_codec *codec = tty->disc_data;
-       struct snd_soc_dapm_context *dapm = &codec->card->dapm;
+       struct snd_soc_dapm_context *dapm = &codec->component.card->dapm;
 
        del_timer_sync(&cx81801_timer);
 
index 00e70b6c7da2ea78e622ca3d0ce36360f3ede593..a9f82b5aba9d0387a1f6952ae264aa1b1bb85982 100644 (file)
@@ -78,7 +78,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
        mutex_init(&codec->cache_rw_mutex);
 
        dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
-                               codec->name);
+                               codec->component.name);
 
        if (codec_drv->reg_cache_default)
                codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
@@ -98,8 +98,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
 int snd_soc_cache_exit(struct snd_soc_codec *codec)
 {
        dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n",
-                       codec->name);
-
+                       codec->component.name);
        kfree(codec->reg_cache);
        codec->reg_cache = NULL;
        return 0;
@@ -192,7 +191,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec)
                return 0;
 
        dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
-               codec->name);
+               codec->component.name);
        trace_snd_soc_cache_sync(codec, name, "start");
        ret = snd_soc_flat_cache_sync(codec);
        if (!ret)
index 10f7f1da2acaf3e192f0b6b1ed519483c4a60dd0..27c06acce205c0887a3663cc0854b851eddefcf3 100644 (file)
@@ -37,7 +37,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
                ret = platform->driver->compr_ops->open(cstream);
                if (ret < 0) {
-                       pr_err("compress asoc: can't open platform %s\n", platform->name);
+                       pr_err("compress asoc: can't open platform %s\n",
+                               platform->component.name);
                        goto out;
                }
        }
@@ -84,7 +85,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
        if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
                ret = platform->driver->compr_ops->open(cstream);
                if (ret < 0) {
-                       pr_err("compress asoc: can't open platform %s\n", platform->name);
+                       pr_err("compress asoc: can't open platform %s\n",
+                               platform->component.name);
                        goto out;
                }
        }
@@ -627,6 +629,11 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
        char new_name[64];
        int ret = 0, direction = 0;
 
+       if (rtd->num_codecs > 1) {
+               dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n");
+               return -EINVAL;
+       }
+
        /* check client and interface hw capabilities */
        snprintf(new_name, sizeof(new_name), "%s %s-%d",
                        rtd->dai_link->stream_name, codec_dai->name, num);
@@ -680,7 +687,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
        ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
        if (ret < 0) {
                pr_err("compress asoc: can't create compress for codec %s\n",
-                       codec->name);
+                       codec->component.name);
                goto compr_err;
        }
 
index 91120b8e283e03384dc70ee466cb04c795365354..28caa63ae526c260617724ff08c9121370f66026 100644 (file)
@@ -292,10 +292,11 @@ static struct dentry *soc_debugfs_create_dir(struct dentry *parent,
 
 static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
 {
-       struct dentry *debugfs_card_root = codec->card->debugfs_card_root;
+       struct dentry *debugfs_card_root = codec->component.card->debugfs_card_root;
 
        codec->debugfs_codec_root = soc_debugfs_create_dir(debugfs_card_root,
-                                               "codec:%s", codec->name);
+                                               "codec:%s",
+                                               codec->component.name);
        if (!codec->debugfs_codec_root) {
                dev_warn(codec->dev,
                        "ASoC: Failed to create codec debugfs directory\n");
@@ -324,17 +325,18 @@ static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
 
 static void soc_init_platform_debugfs(struct snd_soc_platform *platform)
 {
-       struct dentry *debugfs_card_root = platform->card->debugfs_card_root;
+       struct dentry *debugfs_card_root = platform->component.card->debugfs_card_root;
 
        platform->debugfs_platform_root = soc_debugfs_create_dir(debugfs_card_root,
-                                               "platform:%s", platform->name);
+                                               "platform:%s",
+                                               platform->component.name);
        if (!platform->debugfs_platform_root) {
                dev_warn(platform->dev,
                        "ASoC: Failed to create platform debugfs directory\n");
                return;
        }
 
-       snd_soc_dapm_debugfs_init(&platform->dapm,
+       snd_soc_dapm_debugfs_init(&platform->component.dapm,
                platform->debugfs_platform_root);
 }
 
@@ -355,7 +357,7 @@ static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
 
        list_for_each_entry(codec, &codec_list, list) {
                len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
-                              codec->name);
+                              codec->component.name);
                if (len >= 0)
                        ret += len;
                if (ret > PAGE_SIZE) {
@@ -426,7 +428,7 @@ static ssize_t platform_list_read_file(struct file *file,
 
        list_for_each_entry(platform, &platform_list, list) {
                len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
-                              platform->name);
+                              platform->component.name);
                if (len >= 0)
                        ret += len;
                if (ret > PAGE_SIZE) {
@@ -544,11 +546,12 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
        int err;
 
        codec->ac97->dev.bus = &ac97_bus_type;
-       codec->ac97->dev.parent = codec->card->dev;
+       codec->ac97->dev.parent = codec->component.card->dev;
        codec->ac97->dev.release = soc_ac97_device_release;
 
        dev_set_name(&codec->ac97->dev, "%d-%d:%s",
-                    codec->card->snd_card->number, 0, codec->name);
+                    codec->component.card->snd_card->number, 0,
+                    codec->component.name);
        err = device_register(&codec->ac97->dev);
        if (err < 0) {
                dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
@@ -574,7 +577,7 @@ int snd_soc_suspend(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
        struct snd_soc_codec *codec;
-       int i;
+       int i, j;
 
        /* If the initialization of this soc device failed, there is no codec
         * associated with it. Just bail out in this case.
@@ -594,14 +597,17 @@ int snd_soc_suspend(struct device *dev)
 
        /* mute any active DACs */
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *dai = card->rtd[i].codec_dai;
-               struct snd_soc_dai_driver *drv = dai->driver;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (drv->ops->digital_mute && dai->playback_active)
-                       drv->ops->digital_mute(dai, 1);
+               for (j = 0; j < card->rtd[i].num_codecs; j++) {
+                       struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+                       struct snd_soc_dai_driver *drv = dai->driver;
+
+                       if (drv->ops->digital_mute && dai->playback_active)
+                               drv->ops->digital_mute(dai, 1);
+               }
        }
 
        /* suspend all pcms */
@@ -632,8 +638,12 @@ int snd_soc_suspend(struct device *dev)
 
        /* close any waiting streams and save state */
        for (i = 0; i < card->num_rtd; i++) {
+               struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais;
                flush_delayed_work(&card->rtd[i].delayed_work);
-               card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level;
+               for (j = 0; j < card->rtd[i].num_codecs; j++) {
+                       codec_dais[j]->codec->dapm.suspend_bias_level =
+                                       codec_dais[j]->codec->dapm.bias_level;
+               }
        }
 
        for (i = 0; i < card->num_rtd; i++) {
@@ -717,7 +727,7 @@ static void soc_resume_deferred(struct work_struct *work)
        struct snd_soc_card *card =
                        container_of(work, struct snd_soc_card, deferred_resume_work);
        struct snd_soc_codec *codec;
-       int i;
+       int i, j;
 
        /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
         * so userspace apps are blocked from touching us
@@ -778,14 +788,17 @@ static void soc_resume_deferred(struct work_struct *work)
 
        /* unmute any active DACs */
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *dai = card->rtd[i].codec_dai;
-               struct snd_soc_dai_driver *drv = dai->driver;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (drv->ops->digital_mute && dai->playback_active)
-                       drv->ops->digital_mute(dai, 0);
+               for (j = 0; j < card->rtd[i].num_codecs; j++) {
+                       struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+                       struct snd_soc_dai_driver *drv = dai->driver;
+
+                       if (drv->ops->digital_mute && dai->playback_active)
+                               drv->ops->digital_mute(dai, 0);
+               }
        }
 
        for (i = 0; i < card->num_rtd; i++) {
@@ -830,12 +843,19 @@ int snd_soc_resume(struct device *dev)
 
        /* activate pins from sleep state */
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
+               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+               struct snd_soc_dai **codec_dais = rtd->codec_dais;
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+               int j;
+
                if (cpu_dai->active)
                        pinctrl_pm_select_default_state(cpu_dai->dev);
-               if (codec_dai->active)
-                       pinctrl_pm_select_default_state(codec_dai->dev);
+
+               for (j = 0; j < rtd->num_codecs; j++) {
+                       struct snd_soc_dai *codec_dai = codec_dais[j];
+                       if (codec_dai->active)
+                               pinctrl_pm_select_default_state(codec_dai->dev);
+               }
        }
 
        /* AC97 devices might have other drivers hanging off them so
@@ -867,8 +887,9 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
 static const struct snd_soc_dai_ops null_dai_ops = {
 };
 
-static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_node,
-                                           const char *codec_name)
+static struct snd_soc_codec *soc_find_codec(
+                                       const struct device_node *codec_of_node,
+                                       const char *codec_name)
 {
        struct snd_soc_codec *codec;
 
@@ -877,7 +898,7 @@ static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_n
                        if (codec->dev->of_node != codec_of_node)
                                continue;
                } else {
-                       if (strcmp(codec->name, codec_name))
+                       if (strcmp(codec->component.name, codec_name))
                                continue;
                }
 
@@ -906,9 +927,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
        struct snd_soc_component *component;
+       struct snd_soc_dai_link_component *codecs = dai_link->codecs;
+       struct snd_soc_dai **codec_dais = rtd->codec_dais;
        struct snd_soc_platform *platform;
        struct snd_soc_dai *cpu_dai;
        const char *platform_name;
+       int i;
 
        dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
 
@@ -935,24 +959,30 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
                return -EPROBE_DEFER;
        }
 
-       /* Find CODEC from registered list */
-       rtd->codec = soc_find_codec(dai_link->codec_of_node,
-                                   dai_link->codec_name);
-       if (!rtd->codec) {
-               dev_err(card->dev, "ASoC: CODEC %s not registered\n",
-                       dai_link->codec_name);
-               return -EPROBE_DEFER;
-       }
+       rtd->num_codecs = dai_link->num_codecs;
 
-       /* Find CODEC DAI from registered list */
-       rtd->codec_dai = soc_find_codec_dai(rtd->codec,
-                                           dai_link->codec_dai_name);
-       if (!rtd->codec_dai) {
-               dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
-                       dai_link->codec_dai_name);
-               return -EPROBE_DEFER;
+       /* Find CODEC from registered CODECs */
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_codec *codec;
+               codec = soc_find_codec(codecs[i].of_node, codecs[i].name);
+               if (!codec) {
+                       dev_err(card->dev, "ASoC: CODEC %s not registered\n",
+                               codecs[i].name);
+                       return -EPROBE_DEFER;
+               }
+
+               codec_dais[i] = soc_find_codec_dai(codec, codecs[i].dai_name);
+               if (!codec_dais[i]) {
+                       dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
+                               codecs[i].dai_name);
+                       return -EPROBE_DEFER;
+               }
        }
 
+       /* Single codec links expect codec and codec_dai in runtime data */
+       rtd->codec_dai = codec_dais[0];
+       rtd->codec = rtd->codec_dai->codec;
+
        /* 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)
@@ -965,7 +995,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
                            dai_link->platform_of_node)
                                continue;
                } else {
-                       if (strcmp(platform->name, platform_name))
+                       if (strcmp(platform->component.name, platform_name))
                                continue;
                }
 
@@ -994,11 +1024,10 @@ static int soc_remove_platform(struct snd_soc_platform *platform)
        }
 
        /* Make sure all DAPM widgets are freed */
-       snd_soc_dapm_free(&platform->dapm);
+       snd_soc_dapm_free(&platform->component.dapm);
 
        soc_cleanup_platform_debugfs(platform);
        platform->probed = 0;
-       list_del(&platform->card_list);
        module_put(platform->dev->driver->owner);
 
        return 0;
@@ -1043,8 +1072,8 @@ static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, 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_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
-       int err;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       int i, err;
 
        /* unregister the rtd device */
        if (rtd->dev_registered) {
@@ -1055,7 +1084,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
        }
 
        /* remove the CODEC DAI */
-       soc_remove_codec_dai(codec_dai, order);
+       for (i = 0; i < rtd->num_codecs; i++)
+               soc_remove_codec_dai(rtd->codec_dais[i], order);
 
        /* remove the cpu_dai */
        if (cpu_dai && cpu_dai->probed &&
@@ -1068,11 +1098,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
                                        cpu_dai->name, err);
                }
                cpu_dai->probed = 0;
-
-               if (!cpu_dai->codec) {
-                       snd_soc_dapm_free(&cpu_dai->dapm);
+               if (!cpu_dai->codec)
                        module_put(cpu_dai->dev->driver->owner);
-               }
        }
 }
 
@@ -1081,9 +1108,9 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
 {
        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;
+       int i;
 
        /* remove the platform */
        if (platform && platform->probed &&
@@ -1092,8 +1119,8 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
        }
 
        /* remove the CODEC-side CODEC */
-       if (codec_dai) {
-               codec = codec_dai->codec;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec = rtd->codec_dais[i]->codec;
                if (codec && codec->probed &&
                    codec->driver->remove_order == order)
                        soc_remove_codec(codec);
@@ -1128,7 +1155,7 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
 }
 
 static void soc_set_name_prefix(struct snd_soc_card *card,
-                               struct snd_soc_codec *codec)
+                               struct snd_soc_component *component)
 {
        int i;
 
@@ -1137,11 +1164,11 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
 
        for (i = 0; i < card->num_configs; i++) {
                struct snd_soc_codec_conf *map = &card->codec_conf[i];
-               if (map->of_node && codec->dev->of_node != map->of_node)
+               if (map->of_node && component->dev->of_node != map->of_node)
                        continue;
-               if (map->dev_name && strcmp(codec->name, map->dev_name))
+               if (map->dev_name && strcmp(component->name, map->dev_name))
                        continue;
-               codec->name_prefix = map->name_prefix;
+               component->name_prefix = map->name_prefix;
                break;
        }
 }
@@ -1153,9 +1180,9 @@ static int soc_probe_codec(struct snd_soc_card *card,
        const struct snd_soc_codec_driver *driver = codec->driver;
        struct snd_soc_dai *dai;
 
-       codec->card = card;
+       codec->component.card = card;
        codec->dapm.card = card;
-       soc_set_name_prefix(card, codec);
+       soc_set_name_prefix(card, &codec->component);
 
        if (!try_module_get(codec->dev->driver->owner))
                return -ENODEV;
@@ -1197,7 +1224,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
                WARN(codec->dapm.idle_bias_off &&
                        codec->dapm.bias_level != SND_SOC_BIAS_OFF,
                        "codec %s can not start from non-off bias with idle_bias_off==1\n",
-                       codec->name);
+                       codec->component.name);
        }
 
        if (driver->controls)
@@ -1229,8 +1256,8 @@ static int soc_probe_platform(struct snd_soc_card *card,
        struct snd_soc_component *component;
        struct snd_soc_dai *dai;
 
-       platform->card = card;
-       platform->dapm.card = card;
+       platform->component.card = card;
+       platform->component.dapm.card = card;
 
        if (!try_module_get(platform->dev->driver->owner))
                return -ENODEV;
@@ -1238,7 +1265,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
        soc_init_platform_debugfs(platform);
 
        if (driver->dapm_widgets)
-               snd_soc_dapm_new_controls(&platform->dapm,
+               snd_soc_dapm_new_controls(&platform->component.dapm,
                        driver->dapm_widgets, driver->num_dapm_widgets);
 
        /* Create DAPM widgets for each DAI stream */
@@ -1246,10 +1273,11 @@ static int soc_probe_platform(struct snd_soc_card *card,
                if (component->dev != platform->dev)
                        continue;
                list_for_each_entry(dai, &component->dai_list, list)
-                       snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+                       snd_soc_dapm_new_dai_widgets(&platform->component.dapm,
+                               dai);
        }
 
-       platform->dapm.idle_bias_off = 1;
+       platform->component.dapm.idle_bias_off = 1;
 
        if (driver->probe) {
                ret = driver->probe(platform);
@@ -1264,13 +1292,12 @@ static int soc_probe_platform(struct snd_soc_card *card,
                snd_soc_add_platform_controls(platform, driver->controls,
                                     driver->num_controls);
        if (driver->dapm_routes)
-               snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,
-                                       driver->num_dapm_routes);
+               snd_soc_dapm_add_routes(&platform->component.dapm,
+                       driver->dapm_routes, driver->num_dapm_routes);
 
        /* mark platform as probed and add to card platform list */
        platform->probed = 1;
-       list_add(&platform->card_list, &card->platform_dev_list);
-       list_add(&platform->dapm.list, &card->dapm_list);
+       list_add(&platform->component.dapm.list, &card->dapm_list);
 
        return 0;
 
@@ -1286,83 +1313,17 @@ static void rtd_release(struct device *dev)
        kfree(dev);
 }
 
-static int soc_aux_dev_init(struct snd_soc_card *card,
-                           struct snd_soc_codec *codec,
-                           int num)
+static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
+       const char *name)
 {
-       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
-       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
-       int ret;
-
-       rtd->card = card;
-
-       /* do machine specific initialization */
-       if (aux_dev->init) {
-               ret = aux_dev->init(&codec->dapm);
-               if (ret < 0)
-                       return ret;
-       }
-
-       rtd->codec = codec;
-
-       return 0;
-}
-
-static int soc_dai_link_init(struct snd_soc_card *card,
-                            struct snd_soc_codec *codec,
-                            int num)
-{
-       struct snd_soc_dai_link *dai_link =  &card->dai_link[num];
-       struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       int ret;
-
-       rtd->card = card;
-
-       /* do machine specific initialization */
-       if (dai_link->init) {
-               ret = dai_link->init(rtd);
-               if (ret < 0)
-                       return ret;
-       }
-
-       rtd->codec = codec;
-
-       return 0;
-}
-
-static int soc_post_component_init(struct snd_soc_card *card,
-                                  struct snd_soc_codec *codec,
-                                  int num, int dailess)
-{
-       struct snd_soc_dai_link *dai_link = NULL;
-       struct snd_soc_aux_dev *aux_dev = NULL;
-       struct snd_soc_pcm_runtime *rtd;
-       const char *name;
        int ret = 0;
 
-       if (!dailess) {
-               dai_link = &card->dai_link[num];
-               rtd = &card->rtd[num];
-               name = dai_link->name;
-               ret = soc_dai_link_init(card, codec, num);
-       } else {
-               aux_dev = &card->aux_dev[num];
-               rtd = &card->rtd_aux[num];
-               name = aux_dev->name;
-               ret = soc_aux_dev_init(card, codec, num);
-       }
-
-       if (ret < 0) {
-               dev_err(card->dev, "ASoC: failed to init %s: %d\n", name, ret);
-               return ret;
-       }
-
        /* register the rtd device */
        rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
        if (!rtd->dev)
                return -ENOMEM;
        device_initialize(rtd->dev);
-       rtd->dev->parent = card->dev;
+       rtd->dev->parent = rtd->card->dev;
        rtd->dev->release = rtd_release;
        rtd->dev->init_name = name;
        dev_set_drvdata(rtd->dev, rtd);
@@ -1375,7 +1336,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
        if (ret < 0) {
                /* calling put_device() here to free the rtd->dev */
                put_device(rtd->dev);
-               dev_err(card->dev,
+               dev_err(rtd->card->dev,
                        "ASoC: failed to register runtime device: %d\n", ret);
                return ret;
        }
@@ -1384,26 +1345,15 @@ static int soc_post_component_init(struct snd_soc_card *card,
        /* add DAPM sysfs entries for this codec */
        ret = snd_soc_dapm_sys_add(rtd->dev);
        if (ret < 0)
-               dev_err(codec->dev,
+               dev_err(rtd->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);
        if (ret < 0)
-               dev_err(codec->dev,
+               dev_err(rtd->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;
 }
 
@@ -1412,9 +1362,8 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
 {
        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;
+       int i, ret;
 
        /* probe the CPU-side component, if it is a CODEC */
        if (cpu_dai->codec &&
@@ -1425,12 +1374,14 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
                        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 CODEC-side components */
+       for (i = 0; i < rtd->num_codecs; i++) {
+               if (!rtd->codec_dais[i]->codec->probed &&
+                   rtd->codec_dais[i]->codec->driver->probe_order == order) {
+                       ret = soc_probe_codec(card, rtd->codec_dais[i]->codec);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        /* probe the platform */
@@ -1470,12 +1421,16 @@ static int soc_probe_codec_dai(struct snd_soc_card *card,
 
 static int soc_link_dai_widgets(struct snd_soc_card *card,
                                struct snd_soc_dai_link *dai_link,
-                               struct snd_soc_dai *cpu_dai,
-                               struct snd_soc_dai *codec_dai)
+                               struct snd_soc_pcm_runtime *rtd)
 {
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dapm_widget *play_w, *capture_w;
        int ret;
 
+       if (rtd->num_codecs > 1)
+               dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
+
        /* link the DAI widgets */
        play_w = codec_dai->playback_widget;
        capture_w = cpu_dai->capture_widget;
@@ -1508,19 +1463,18 @@ 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;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       int ret;
+       int i, ret;
 
        dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
                        card->name, num, order);
 
        /* config components */
        cpu_dai->platform = platform;
-       codec_dai->card = card;
        cpu_dai->card = card;
+       for (i = 0; i < rtd->num_codecs; i++)
+               rtd->codec_dais[i]->card = card;
 
        /* set default power off timeout */
        rtd->pmdown_time = pmdown_time;
@@ -1529,11 +1483,8 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
        if (!cpu_dai->probed &&
                        cpu_dai->driver->probe_order == order) {
                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);
                }
 
                if (cpu_dai->driver->probe) {
@@ -1550,18 +1501,43 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
        }
 
        /* probe the CODEC DAI */
-       ret = soc_probe_codec_dai(card, codec_dai, order);
-       if (ret)
-               return ret;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+               if (ret)
+                       return ret;
+       }
 
        /* complete DAI probe during last probe */
        if (order != SND_SOC_COMP_ORDER_LAST)
                return 0;
 
-       ret = soc_post_component_init(card, codec, num, 0);
+       /* do machine specific initialization */
+       if (dai_link->init) {
+               ret = dai_link->init(rtd);
+               if (ret < 0) {
+                       dev_err(card->dev, "ASoC: failed to init %s: %d\n",
+                               dai_link->name, ret);
+                       return ret;
+               }
+       }
+
+       ret = soc_post_component_init(rtd, dai_link->name);
        if (ret)
                return ret;
 
+#ifdef CONFIG_DEBUG_FS
+       /* add DPCM sysfs entries */
+       if (dai_link->dynamic) {
+               ret = soc_dpcm_debugfs_add(rtd);
+               if (ret < 0) {
+                       dev_err(rtd->dev,
+                               "ASoC: failed to add dpcm sysfs entries: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+#endif
+
        ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
        if (ret < 0)
                dev_warn(rtd->dev, "ASoC: failed to add pmdown_time sysfs: %d\n",
@@ -1590,16 +1566,18 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                                                codec2codec_close_delayed_work);
 
                        /* link the DAI widgets */
-                       ret = soc_link_dai_widgets(card, dai_link,
-                                       cpu_dai, codec_dai);
+                       ret = soc_link_dai_widgets(card, dai_link, rtd);
                        if (ret)
                                return ret;
                }
        }
 
        /* add platform data for AC97 devices */
-       if (rtd->codec_dai->driver->ac97_control)
-               snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               if (rtd->codec_dais[i]->driver->ac97_control)
+                       snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
+                                              rtd->cpu_dai->ac97_pdata);
+       }
 
        return 0;
 }
@@ -1637,11 +1615,6 @@ static int soc_register_ac97_codec(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       return soc_register_ac97_codec(rtd->codec, rtd->codec_dai);
-}
-
 static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
 {
        if (codec->ac97_registered) {
@@ -1650,74 +1623,77 @@ static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
        }
 }
 
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
+static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
 {
-       soc_unregister_ac97_codec(rtd->codec);
-}
-#endif
+       int i, ret;
 
-static struct snd_soc_codec *soc_find_matching_codec(struct snd_soc_card *card,
-       int num)
-{
-       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
-       struct snd_soc_codec *codec;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
 
-       /* find CODEC from registered CODECs */
-       list_for_each_entry(codec, &codec_list, list) {
-               if (aux_dev->codec_of_node &&
-                  (codec->dev->of_node != aux_dev->codec_of_node))
-                       continue;
-               if (aux_dev->codec_name && strcmp(codec->name, aux_dev->codec_name))
-                       continue;
-               return codec;
+               ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
+               if (ret) {
+                       while (--i >= 0)
+                               soc_unregister_ac97_codec(codec_dai->codec);
+                       return ret;
+               }
        }
 
-       return NULL;
+       return 0;
 }
 
-static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
-       const char *codecname = aux_dev->codec_name;
-       struct snd_soc_codec *codec = soc_find_matching_codec(card, num);
-
-       if (codec)
-               return 0;
-       if (aux_dev->codec_of_node)
-               codecname = of_node_full_name(aux_dev->codec_of_node);
+       int i;
 
-       dev_err(card->dev, "ASoC: %s not registered\n", codecname);
-       return -EPROBE_DEFER;
+       for (i = 0; i < rtd->num_codecs; i++)
+               soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
 }
+#endif
 
-static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
+static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
+       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
        struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
        const char *codecname = aux_dev->codec_name;
-       int ret = -ENODEV;
-       struct snd_soc_codec *codec = soc_find_matching_codec(card, num);
 
-       if (!codec) {
+       rtd->codec = soc_find_codec(aux_dev->codec_of_node, codecname);
+       if (!rtd->codec) {
                if (aux_dev->codec_of_node)
                        codecname = of_node_full_name(aux_dev->codec_of_node);
 
-               /* codec not found */
-               dev_err(card->dev, "ASoC: codec %s not found", codecname);
+               dev_err(card->dev, "ASoC: %s not registered\n", codecname);
                return -EPROBE_DEFER;
        }
 
-       if (codec->probed) {
-               dev_err(codec->dev, "ASoC: codec already probed");
+       return 0;
+}
+
+static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
+{
+       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
+       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+       int ret;
+
+       if (rtd->codec->probed) {
+               dev_err(rtd->codec->dev, "ASoC: codec already probed\n");
                return -EBUSY;
        }
 
-       ret = soc_probe_codec(card, codec);
+       ret = soc_probe_codec(card, rtd->codec);
        if (ret < 0)
                return ret;
 
-       ret = soc_post_component_init(card, codec, num, 1);
+       /* do machine specific initialization */
+       if (aux_dev->init) {
+               ret = aux_dev->init(&rtd->codec->dapm);
+               if (ret < 0) {
+                       dev_err(card->dev, "ASoC: failed to init %s: %d\n",
+                               aux_dev->name, ret);
+                       return ret;
+               }
+       }
 
-       return ret;
+       return soc_post_component_init(rtd, aux_dev->name);
 }
 
 static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
@@ -1769,9 +1745,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                        goto base_error;
        }
 
-       /* check aux_devs too */
+       /* bind aux_devs too */
        for (i = 0; i < card->num_aux_devs; i++) {
-               ret = soc_check_aux_dev(card, i);
+               ret = soc_bind_aux_dev(card, i);
                if (ret != 0)
                        goto base_error;
        }
@@ -1869,16 +1845,23 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                                        card->num_dapm_routes);
 
        for (i = 0; i < card->num_links; i++) {
+               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
                dai_link = &card->dai_link[i];
                dai_fmt = dai_link->dai_fmt;
 
                if (dai_fmt) {
-                       ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
-                                                 dai_fmt);
-                       if (ret != 0 && ret != -ENOTSUPP)
-                               dev_warn(card->rtd[i].codec_dai->dev,
-                                        "ASoC: Failed to set DAI format: %d\n",
-                                        ret);
+                       struct snd_soc_dai **codec_dais = rtd->codec_dais;
+                       int j;
+
+                       for (j = 0; j < rtd->num_codecs; j++) {
+                               struct snd_soc_dai *codec_dai = codec_dais[j];
+
+                               ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+                               if (ret != 0 && ret != -ENOTSUPP)
+                                       dev_warn(codec_dai->dev,
+                                                "ASoC: Failed to set DAI format: %d\n",
+                                                ret);
+                       }
                }
 
                /* If this is a regular CPU link there will be a platform */
@@ -1947,8 +1930,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
        }
 
        if (card->fully_routed)
-               list_for_each_entry(codec, &card->codec_dev_list, card_list)
-                       snd_soc_dapm_auto_nc_codec_pins(codec);
+               snd_soc_dapm_auto_nc_pins(card);
 
        snd_soc_dapm_new_widgets(card);
 
@@ -2078,10 +2060,15 @@ int snd_soc_poweroff(struct device *dev)
 
        /* deactivate pins to sleep state */
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
-               pinctrl_pm_select_sleep_state(codec_dai->dev);
+               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+               int j;
+
                pinctrl_pm_select_sleep_state(cpu_dai->dev);
+               for (j = 0; j < rtd->num_codecs; j++) {
+                       struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+                       pinctrl_pm_select_sleep_state(codec_dai->dev);
+               }
        }
 
        return 0;
@@ -2406,6 +2393,25 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
 
+/**
+ * snd_soc_add_component_controls - Add an array of controls to a component.
+ *
+ * @component: Component 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_component_controls(struct snd_soc_component *component,
+       const struct snd_kcontrol_new *controls, unsigned int num_controls)
+{
+       struct snd_card *card = component->card->snd_card;
+
+       return snd_soc_add_controls(card, component->dev, controls,
+                       num_controls, component->name_prefix, component);
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_component_controls);
+
 /**
  * snd_soc_add_codec_controls - add an array of controls to a codec.
  * Convenience function to add a list of controls. Many codecs were
@@ -2418,12 +2424,10 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
  * Return 0 for success, else error.
  */
 int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
-       const struct snd_kcontrol_new *controls, int num_controls)
+       const struct snd_kcontrol_new *controls, unsigned int num_controls)
 {
-       struct snd_card *card = codec->card->snd_card;
-
-       return snd_soc_add_controls(card, codec->dev, controls, num_controls,
-                       codec->name_prefix, &codec->component);
+       return snd_soc_add_component_controls(&codec->component, controls,
+               num_controls);
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
 
@@ -2438,12 +2442,10 @@ EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
  * Return 0 for success, else error.
  */
 int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
-       const struct snd_kcontrol_new *controls, int num_controls)
+       const struct snd_kcontrol_new *controls, unsigned int num_controls)
 {
-       struct snd_card *card = platform->card->snd_card;
-
-       return snd_soc_add_controls(card, platform->dev, controls, num_controls,
-                       NULL, &platform->component);
+       return snd_soc_add_component_controls(&platform->component, controls,
+               num_controls);
 }
 EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
 
@@ -3115,7 +3117,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
 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_card *card = codec->component.card->snd_card;
        struct snd_kcontrol *kctl;
        struct soc_mixer_control *mc;
        int found = 0;
@@ -3661,6 +3663,9 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
        else
                snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
 
+       dai->tx_mask = tx_mask;
+       dai->rx_mask = rx_mask;
+
        if (dai->driver && dai->driver->ops->set_tdm_slot)
                return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
                                slots, slot_width);
@@ -3733,6 +3738,33 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
+static int snd_soc_init_multicodec(struct snd_soc_card *card,
+                                  struct snd_soc_dai_link *dai_link)
+{
+       /* Legacy codec/codec_dai link is a single entry in multicodec */
+       if (dai_link->codec_name || dai_link->codec_of_node ||
+           dai_link->codec_dai_name) {
+               dai_link->num_codecs = 1;
+
+               dai_link->codecs = devm_kzalloc(card->dev,
+                               sizeof(struct snd_soc_dai_link_component),
+                               GFP_KERNEL);
+               if (!dai_link->codecs)
+                       return -ENOMEM;
+
+               dai_link->codecs[0].name = dai_link->codec_name;
+               dai_link->codecs[0].of_node = dai_link->codec_of_node;
+               dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
+       }
+
+       if (!dai_link->codecs) {
+               dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /**
  * snd_soc_register_card - Register a card with the ASoC core
  *
@@ -3741,7 +3773,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
  */
 int snd_soc_register_card(struct snd_soc_card *card)
 {
-       int i, ret;
+       int i, j, ret;
 
        if (!card->name || !card->dev)
                return -EINVAL;
@@ -3749,22 +3781,29 @@ int snd_soc_register_card(struct snd_soc_card *card)
        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,
-                               "ASoC: Neither/both codec name/of_node are set for %s\n",
-                               link->name);
-                       return -EINVAL;
+               ret = snd_soc_init_multicodec(card, link);
+               if (ret) {
+                       dev_err(card->dev, "ASoC: failed to init multicodec\n");
+                       return ret;
                }
-               /* Codec DAI name must be specified */
-               if (!link->codec_dai_name) {
-                       dev_err(card->dev,
-                               "ASoC: codec_dai_name not set for %s\n",
-                               link->name);
-                       return -EINVAL;
+
+               for (j = 0; j < link->num_codecs; j++) {
+                       /*
+                        * Codec must be specified by 1 of name or OF node,
+                        * not both or neither.
+                        */
+                       if (!!link->codecs[j].name ==
+                           !!link->codecs[j].of_node) {
+                               dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
+                                       link->name);
+                               return -EINVAL;
+                       }
+                       /* Codec DAI name must be specified */
+                       if (!link->codecs[j].dai_name) {
+                               dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
+                                       link->name);
+                               return -EINVAL;
+                       }
                }
 
                /*
@@ -3817,8 +3856,19 @@ int snd_soc_register_card(struct snd_soc_card *card)
        card->num_rtd = 0;
        card->rtd_aux = &card->rtd[card->num_links];
 
-       for (i = 0; i < card->num_links; i++)
+       for (i = 0; i < card->num_links; i++) {
+               card->rtd[i].card = card;
                card->rtd[i].dai_link = &card->dai_link[i];
+               card->rtd[i].codec_dais = devm_kzalloc(card->dev,
+                                       sizeof(struct snd_soc_dai *) *
+                                       (card->rtd[i].dai_link->num_codecs),
+                                       GFP_KERNEL);
+               if (card->rtd[i].codec_dais == NULL)
+                       return -ENOMEM;
+       }
+
+       for (i = 0; i < card->num_aux_devs; i++)
+               card->rtd_aux[i].card = card;
 
        INIT_LIST_HEAD(&card->dapm_dirty);
        card->instantiated = 0;
@@ -3831,10 +3881,16 @@ int snd_soc_register_card(struct snd_soc_card *card)
 
        /* deactivate pins to sleep state */
        for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
-               if (!codec_dai->active)
-                       pinctrl_pm_select_sleep_state(codec_dai->dev);
+               struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+               struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+               int j;
+
+               for (j = 0; j < rtd->num_codecs; j++) {
+                       struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+                       if (!codec_dai->active)
+                               pinctrl_pm_select_sleep_state(codec_dai->dev);
+               }
+
                if (!cpu_dai->active)
                        pinctrl_pm_select_sleep_state(cpu_dai->dev);
        }
@@ -3941,16 +3997,14 @@ static void snd_soc_unregister_dais(struct snd_soc_component *component)
  * snd_soc_register_dais - Register a DAI with the ASoC core
  *
  * @component: The component the DAIs are registered for
- * @codec: The CODEC that the DAIs are registered for, NULL if the component is
- *         not a CODEC.
  * @dai_drv: DAI driver to use for the DAIs
  * @count: Number of DAIs
  * @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the
  *                     parent's name.
  */
 static int snd_soc_register_dais(struct snd_soc_component *component,
-       struct snd_soc_codec *codec, struct snd_soc_dai_driver *dai_drv,
-       size_t count, bool legacy_dai_naming)
+       struct snd_soc_dai_driver *dai_drv, size_t count,
+       bool legacy_dai_naming)
 {
        struct device *dev = component->dev;
        struct snd_soc_dai *dai;
@@ -3959,6 +4013,9 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
 
        dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
 
+       component->dai_drv = dai_drv;
+       component->num_dai = count;
+
        for (i = 0; i < count; i++) {
 
                dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
@@ -3991,16 +4048,11 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
                }
 
                dai->component = component;
-               dai->codec = codec;
                dai->dev = dev;
                dai->driver = &dai_drv[i];
-               dai->dapm.dev = dev;
                if (!dai->driver->ops)
                        dai->driver->ops = &null_dai_ops;
 
-               if (!dai->codec)
-                       dai->dapm.idle_bias_off = 1;
-
                list_add(&dai->list, &component->dai_list);
 
                dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
@@ -4014,60 +4066,82 @@ err:
        return ret;
 }
 
-/**
- * snd_soc_register_component - Register a component with the ASoC core
- *
- */
-static int
-__snd_soc_register_component(struct device *dev,
-                            struct snd_soc_component *cmpnt,
-                            const struct snd_soc_component_driver *cmpnt_drv,
-                            struct snd_soc_codec *codec,
-                            struct snd_soc_dai_driver *dai_drv,
-                            int num_dai, bool allow_single_dai)
+static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm,
+       enum snd_soc_dapm_type type, int subseq)
 {
-       int ret;
+       struct snd_soc_component *component = dapm->component;
 
-       dev_dbg(dev, "component register %s\n", dev_name(dev));
+       component->driver->seq_notifier(component, type, subseq);
+}
 
-       if (!cmpnt) {
-               dev_err(dev, "ASoC: Failed to connecting component\n");
-               return -ENOMEM;
-       }
+static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm,
+       int event)
+{
+       struct snd_soc_component *component = dapm->component;
 
-       mutex_init(&cmpnt->io_mutex);
+       return component->driver->stream_event(component, event);
+}
+
+static int snd_soc_component_initialize(struct snd_soc_component *component,
+       const struct snd_soc_component_driver *driver, struct device *dev)
+{
+       struct snd_soc_dapm_context *dapm;
 
-       cmpnt->name = fmt_single_name(dev, &cmpnt->id);
-       if (!cmpnt->name) {
-               dev_err(dev, "ASoC: Failed to simplifying name\n");
+       component->name = fmt_single_name(dev, &component->id);
+       if (!component->name) {
+               dev_err(dev, "ASoC: Failed to allocate name\n");
                return -ENOMEM;
        }
 
-       cmpnt->dev      = dev;
-       cmpnt->driver   = cmpnt_drv;
-       cmpnt->dai_drv  = dai_drv;
-       cmpnt->num_dai  = num_dai;
-       INIT_LIST_HEAD(&cmpnt->dai_list);
+       component->dev = dev;
+       component->driver = driver;
 
-       ret = snd_soc_register_dais(cmpnt, codec, dai_drv, num_dai,
-               allow_single_dai);
-       if (ret < 0) {
-               dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
-               goto error_component_name;
-       }
+       if (!component->dapm_ptr)
+               component->dapm_ptr = &component->dapm;
+
+       dapm = component->dapm_ptr;
+       dapm->dev = dev;
+       dapm->component = component;
+       dapm->bias_level = SND_SOC_BIAS_OFF;
+       if (driver->seq_notifier)
+               dapm->seq_notifier = snd_soc_component_seq_notifier;
+       if (driver->stream_event)
+               dapm->stream_event = snd_soc_component_stream_event;
+
+       INIT_LIST_HEAD(&component->dai_list);
+       mutex_init(&component->io_mutex);
+
+       return 0;
+}
+
+static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
+{
+       list_add(&component->list, &component_list);
+}
 
+static void snd_soc_component_add(struct snd_soc_component *component)
+{
        mutex_lock(&client_mutex);
-       list_add(&cmpnt->list, &component_list);
+       snd_soc_component_add_unlocked(component);
        mutex_unlock(&client_mutex);
+}
 
-       dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name);
-
-       return ret;
+static void snd_soc_component_cleanup(struct snd_soc_component *component)
+{
+       snd_soc_unregister_dais(component);
+       kfree(component->name);
+}
 
-error_component_name:
-       kfree(cmpnt->name);
+static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
+{
+       list_del(&component->list);
+}
 
-       return ret;
+static void snd_soc_component_del(struct snd_soc_component *component)
+{
+       mutex_lock(&client_mutex);
+       snd_soc_component_del_unlocked(component);
+       mutex_unlock(&client_mutex);
 }
 
 int snd_soc_register_component(struct device *dev,
@@ -4076,32 +4150,38 @@ int snd_soc_register_component(struct device *dev,
                               int num_dai)
 {
        struct snd_soc_component *cmpnt;
+       int ret;
 
-       cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL);
+       cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);
        if (!cmpnt) {
                dev_err(dev, "ASoC: Failed to allocate memory\n");
                return -ENOMEM;
        }
 
+       ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);
+       if (ret)
+               goto err_free;
+
        cmpnt->ignore_pmdown_time = true;
        cmpnt->registered_as_component = true;
 
-       return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL,
-                                           dai_drv, num_dai, true);
-}
-EXPORT_SYMBOL_GPL(snd_soc_register_component);
+       ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);
+       if (ret < 0) {
+               dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
+               goto err_cleanup;
+       }
 
-static void __snd_soc_unregister_component(struct snd_soc_component *cmpnt)
-{
-       snd_soc_unregister_dais(cmpnt);
+       snd_soc_component_add(cmpnt);
 
-       mutex_lock(&client_mutex);
-       list_del(&cmpnt->list);
-       mutex_unlock(&client_mutex);
+       return 0;
 
-       dev_dbg(cmpnt->dev, "ASoC: Unregistered component '%s'\n", cmpnt->name);
-       kfree(cmpnt->name);
+err_cleanup:
+       snd_soc_component_cleanup(cmpnt);
+err_free:
+       kfree(cmpnt);
+       return ret;
 }
+EXPORT_SYMBOL_GPL(snd_soc_register_component);
 
 /**
  * snd_soc_unregister_component - Unregister a component from the ASoC core
@@ -4118,7 +4198,9 @@ void snd_soc_unregister_component(struct device *dev)
        return;
 
 found:
-       __snd_soc_unregister_component(cmpnt);
+       snd_soc_component_del(cmpnt);
+       snd_soc_component_cleanup(cmpnt);
+       kfree(cmpnt);
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
 
@@ -4151,37 +4233,25 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
 {
        int ret;
 
-       /* create platform component name */
-       platform->name = fmt_single_name(dev, &platform->id);
-       if (platform->name == NULL)
-               return -ENOMEM;
+       ret = snd_soc_component_initialize(&platform->component,
+                       &platform_drv->component_driver, dev);
+       if (ret)
+               return ret;
 
        platform->dev = dev;
        platform->driver = platform_drv;
-       platform->dapm.dev = dev;
-       platform->dapm.platform = platform;
-       platform->dapm.component = &platform->component;
-       platform->dapm.stream_event = platform_drv->stream_event;
        if (platform_drv->write)
                platform->component.write = snd_soc_platform_drv_write;
        if (platform_drv->read)
                platform->component.read = snd_soc_platform_drv_read;
 
-       /* register component */
-       ret = __snd_soc_register_component(dev, &platform->component,
-                                          &platform_drv->component_driver,
-                                          NULL, NULL, 0, false);
-       if (ret < 0) {
-               dev_err(platform->component.dev,
-                       "ASoC: Failed to register component: %d\n", ret);
-               return ret;
-       }
-
        mutex_lock(&client_mutex);
+       snd_soc_component_add_unlocked(&platform->component);
        list_add(&platform->list, &platform_list);
        mutex_unlock(&client_mutex);
 
-       dev_dbg(dev, "ASoC: Registered platform '%s'\n", platform->name);
+       dev_dbg(dev, "ASoC: Registered platform '%s'\n",
+               platform->component.name);
 
        return 0;
 }
@@ -4218,15 +4288,16 @@ EXPORT_SYMBOL_GPL(snd_soc_register_platform);
  */
 void snd_soc_remove_platform(struct snd_soc_platform *platform)
 {
-       __snd_soc_unregister_component(&platform->component);
 
        mutex_lock(&client_mutex);
        list_del(&platform->list);
+       snd_soc_component_del_unlocked(&platform->component);
        mutex_unlock(&client_mutex);
 
+       snd_soc_component_cleanup(&platform->component);
+
        dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n",
-               platform->name);
-       kfree(platform->name);
+               platform->component.name);
 }
 EXPORT_SYMBOL_GPL(snd_soc_remove_platform);
 
@@ -4312,6 +4383,14 @@ static int snd_soc_codec_drv_read(struct snd_soc_component *component,
        return 0;
 }
 
+static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm,
+       enum snd_soc_bias_level level)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+
+       return codec->driver->set_bias_level(codec, level);
+}
+
 /**
  * snd_soc_register_codec - Register a codec with the ASoC core
  *
@@ -4323,6 +4402,7 @@ int snd_soc_register_codec(struct device *dev,
                           int num_dai)
 {
        struct snd_soc_codec *codec;
+       struct snd_soc_dai *dai;
        struct regmap *regmap;
        int ret, i;
 
@@ -4332,24 +4412,23 @@ int snd_soc_register_codec(struct device *dev,
        if (codec == NULL)
                return -ENOMEM;
 
-       /* create CODEC component name */
-       codec->name = fmt_single_name(dev, &codec->id);
-       if (codec->name == NULL) {
-               ret = -ENOMEM;
-               goto fail_codec;
-       }
+       codec->component.dapm_ptr = &codec->dapm;
+
+       ret = snd_soc_component_initialize(&codec->component,
+                       &codec_drv->component_driver, dev);
+       if (ret)
+               goto err_free;
 
        if (codec_drv->write)
                codec->component.write = snd_soc_codec_drv_write;
        if (codec_drv->read)
                codec->component.read = snd_soc_codec_drv_read;
        codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
-       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
-       codec->dapm.dev = dev;
        codec->dapm.codec = codec;
-       codec->dapm.component = &codec->component;
-       codec->dapm.seq_notifier = codec_drv->seq_notifier;
-       codec->dapm.stream_event = codec_drv->stream_event;
+       if (codec_drv->seq_notifier)
+               codec->dapm.seq_notifier = codec_drv->seq_notifier;
+       if (codec_drv->set_bias_level)
+               codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->component.val_bytes = codec_drv->reg_word_size;
@@ -4368,7 +4447,7 @@ int snd_soc_register_codec(struct device *dev,
                                dev_err(codec->dev,
                                                "Failed to set cache I/O:%d\n",
                                                ret);
-                               return ret;
+                               goto err_cleanup;
                        }
                }
        }
@@ -4378,29 +4457,27 @@ 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 component */
-       ret = __snd_soc_register_component(dev, &codec->component,
-                                          &codec_drv->component_driver,
-                                          codec, dai_drv, num_dai, false);
+       ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
        if (ret < 0) {
-               dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret);
-               goto fail_codec_name;
+               dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
+               goto err_cleanup;
        }
 
-       dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", codec->name);
-       return 0;
+       list_for_each_entry(dai, &codec->component.dai_list, list)
+               dai->codec = codec;
 
-fail_codec_name:
        mutex_lock(&client_mutex);
-       list_del(&codec->list);
+       snd_soc_component_add_unlocked(&codec->component);
+       list_add(&codec->list, &codec_list);
        mutex_unlock(&client_mutex);
 
-       kfree(codec->name);
-fail_codec:
+       dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
+               codec->component.name);
+       return 0;
+
+err_cleanup:
+       snd_soc_component_cleanup(&codec->component);
+err_free:
        kfree(codec);
        return ret;
 }
@@ -4422,16 +4499,17 @@ void snd_soc_unregister_codec(struct device *dev)
        return;
 
 found:
-       __snd_soc_unregister_component(&codec->component);
 
        mutex_lock(&client_mutex);
        list_del(&codec->list);
+       snd_soc_component_del_unlocked(&codec->component);
        mutex_unlock(&client_mutex);
 
-       dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", codec->name);
+       dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n",
+                       codec->component.name);
 
+       snd_soc_component_cleanup(&codec->component);
        snd_soc_cache_exit(codec);
-       kfree(codec->name);
        kfree(codec);
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
index cdc837ed144d7d1bd8d06287aa1111b85b67fc6d..8348352dc2c62c484a1efba6f04c290367b93864 100644 (file)
@@ -349,13 +349,28 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
        return true;
 }
 
+/**
+ * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
+ *  kcontrol
+ * @kcontrol: The kcontrol
+ *
+ * Note: This function must only be used on kcontrols that are known to have
+ * been registered for a CODEC. Otherwise the behaviour is undefined.
+ */
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
+       struct snd_kcontrol *kcontrol)
+{
+       return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);
+
 /**
  * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
  * @kcontrol: The kcontrol
  */
 struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol)
 {
-       return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec;
+       return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol));
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
 
@@ -375,23 +390,38 @@ static void dapm_reset(struct snd_soc_card *card)
        }
 }
 
-static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg,
+static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm)
+{
+       if (!dapm->component)
+               return NULL;
+       return dapm->component->name_prefix;
+}
+
+static int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg,
        unsigned int *value)
 {
-       if (!w->dapm->component)
+       if (!dapm->component)
                return -EIO;
-       return snd_soc_component_read(w->dapm->component, reg, value);
+       return snd_soc_component_read(dapm->component, reg, value);
 }
 
-static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
        int reg, unsigned int mask, unsigned int value)
 {
-       if (!w->dapm->component)
+       if (!dapm->component)
                return -EIO;
-       return snd_soc_component_update_bits_async(w->dapm->component, reg,
+       return snd_soc_component_update_bits_async(dapm->component, reg,
                mask, value);
 }
 
+static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,
+       int reg, unsigned int mask, unsigned int value)
+{
+       if (!dapm->component)
+               return -EIO;
+       return snd_soc_component_test_bits(dapm->component, reg, mask, value);
+}
+
 static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
 {
        if (dapm->component)
@@ -420,15 +450,10 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
        if (ret != 0)
                goto out;
 
-       if (dapm->codec) {
-               if (dapm->codec->driver->set_bias_level)
-                       ret = dapm->codec->driver->set_bias_level(dapm->codec,
-                                                                 level);
-               else
-                       dapm->bias_level = level;
-       } else if (!card || dapm != &card->dapm) {
+       if (dapm->set_bias_level)
+               ret = dapm->set_bias_level(dapm, level);
+       else if (!card || dapm != &card->dapm)
                dapm->bias_level = level;
-       }
 
        if (ret != 0)
                goto out;
@@ -452,7 +477,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
        int i;
 
        if (e->reg != SND_SOC_NOPM) {
-               soc_widget_read(dest, e->reg, &val);
+               soc_dapm_read(dapm, e->reg, &val);
                val = (val >> e->shift_l) & e->mask;
                item = snd_soc_enum_val_to_item(e, val);
        } else {
@@ -496,7 +521,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
        unsigned int val;
 
        if (reg != SND_SOC_NOPM) {
-               soc_widget_read(w, reg, &val);
+               soc_dapm_read(w->dapm, reg, &val);
                val = (val >> shift) & mask;
                if (invert)
                        val = max - val;
@@ -570,11 +595,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
        const char *name;
        int ret;
 
-       if (dapm->codec)
-               prefix = dapm->codec->name_prefix;
-       else
-               prefix = NULL;
-
+       prefix = soc_dapm_prefix(dapm);
        if (prefix)
                prefix_len = strlen(prefix) + 1;
        else
@@ -1308,16 +1329,18 @@ static void dapm_seq_check_event(struct snd_soc_card *card,
 static void dapm_seq_run_coalesced(struct snd_soc_card *card,
                                   struct list_head *pending)
 {
+       struct snd_soc_dapm_context *dapm;
        struct snd_soc_dapm_widget *w;
        int reg;
        unsigned int value = 0;
        unsigned int mask = 0;
 
-       reg = list_first_entry(pending, struct snd_soc_dapm_widget,
-                              power_list)->reg;
+       w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list);
+       reg = w->reg;
+       dapm = w->dapm;
 
        list_for_each_entry(w, pending, power_list) {
-               WARN_ON(reg != w->reg);
+               WARN_ON(reg != w->reg || dapm != w->dapm);
                w->power = w->new_power;
 
                mask |= w->mask << w->shift;
@@ -1326,7 +1349,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
                else
                        value |= w->off_val << w->shift;
 
-               pop_dbg(w->dapm->dev, card->pop_time,
+               pop_dbg(dapm->dev, card->pop_time,
                        "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
                        w->name, reg, value, mask);
 
@@ -1339,14 +1362,12 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
                /* Any widget will do, they should all be updating the
                 * same register.
                 */
-               w = list_first_entry(pending, struct snd_soc_dapm_widget,
-                                    power_list);
 
-               pop_dbg(w->dapm->dev, card->pop_time,
+               pop_dbg(dapm->dev, card->pop_time,
                        "pop test : Applying 0x%x/0x%x to %x in %dms\n",
                        value, mask, reg, card->pop_time);
                pop_wait(card->pop_time);
-               soc_widget_update_bits(w, reg, mask, value);
+               soc_dapm_update_bits(dapm, reg, mask, value);
        }
 
        list_for_each_entry(w, pending, power_list) {
@@ -1492,7 +1513,8 @@ static void dapm_widget_update(struct snd_soc_card *card)
        if (!w)
                return;
 
-       ret = soc_widget_update_bits(w, update->reg, update->mask, update->val);
+       ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask,
+               update->val);
        if (ret < 0)
                dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
                        w->name, ret);
@@ -2062,17 +2084,13 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
 
-/* show dapm widget status in sys fs */
-static ssize_t dapm_widget_show(struct device *dev,
-       struct device_attribute *attr, char *buf)
+static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf)
 {
-       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
-       struct snd_soc_codec *codec =rtd->codec;
        struct snd_soc_dapm_widget *w;
        int count = 0;
        char *state = "not set";
 
-       list_for_each_entry(w, &codec->card->widgets, list) {
+       list_for_each_entry(w, &codec->component.card->widgets, list) {
                if (w->dapm != &codec->dapm)
                        continue;
 
@@ -2120,6 +2138,21 @@ static ssize_t dapm_widget_show(struct device *dev,
        return count;
 }
 
+/* show dapm widget status in sys fs */
+static ssize_t dapm_widget_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
+       int i, count = 0;
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_codec *codec = rtd->codec_dais[i]->codec;
+               count += dapm_widget_show_codec(codec, buf + count);
+       }
+
+       return count;
+}
+
 static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
 
 int snd_soc_dapm_sys_add(struct device *dev)
@@ -2371,14 +2404,16 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        const char *source;
        char prefixed_sink[80];
        char prefixed_source[80];
+       const char *prefix;
        int ret;
 
-       if (dapm->codec && dapm->codec->name_prefix) {
+       prefix = soc_dapm_prefix(dapm);
+       if (prefix) {
                snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
-                        dapm->codec->name_prefix, route->sink);
+                        prefix, route->sink);
                sink = prefixed_sink;
                snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
-                        dapm->codec->name_prefix, route->source);
+                        prefix, route->source);
                source = prefixed_source;
        } else {
                sink = route->sink;
@@ -2439,6 +2474,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
        const char *source;
        char prefixed_sink[80];
        char prefixed_source[80];
+       const char *prefix;
 
        if (route->control) {
                dev_err(dapm->dev,
@@ -2446,12 +2482,13 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
                return -EINVAL;
        }
 
-       if (dapm->codec && dapm->codec->name_prefix) {
+       prefix = soc_dapm_prefix(dapm);
+       if (prefix) {
                snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
-                        dapm->codec->name_prefix, route->sink);
+                        prefix, route->sink);
                sink = prefixed_sink;
                snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
-                        dapm->codec->name_prefix, route->source);
+                        prefix, route->source);
                source = prefixed_source;
        } else {
                sink = route->sink;
@@ -2670,7 +2707,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
 
                /* Read the initial power state from the device */
                if (w->reg >= 0) {
-                       soc_widget_read(w, w->reg, &val);
+                       soc_dapm_read(w->dapm, w->reg, &val);
                        val = val >> w->shift;
                        val &= w->mask;
                        if (val == w->on_val)
@@ -2701,8 +2738,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_card *card = dapm->card;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        int reg = mc->reg;
@@ -2711,17 +2748,20 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        unsigned int val;
+       int ret = 0;
 
        if (snd_soc_volsw_is_stereo(mc))
-               dev_warn(codec->dapm.dev,
+               dev_warn(dapm->dev,
                         "ASoC: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-       if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM)
-               val = (snd_soc_read(codec, reg) >> shift) & mask;
-       else
+       if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
+               ret = soc_dapm_read(dapm, reg, &val);
+               val = (val >> shift) & mask;
+       } else {
                val = dapm_kcontrol_get_value(kcontrol);
+       }
        mutex_unlock(&card->dapm_mutex);
 
        if (invert)
@@ -2729,7 +2769,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        else
                ucontrol->value.integer.value[0] = val;
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
 
@@ -2745,8 +2785,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_card *card = dapm->card;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        int reg = mc->reg;
@@ -2760,7 +2800,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        int ret = 0;
 
        if (snd_soc_volsw_is_stereo(mc))
-               dev_warn(codec->dapm.dev,
+               dev_warn(dapm->dev,
                         "ASoC: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);
 
@@ -2778,7 +2818,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
                mask = mask << shift;
                val = val << shift;
 
-               reg_change = snd_soc_test_bits(codec, reg, mask, val);
+               reg_change = soc_dapm_test_bits(dapm, reg, mask, val);
        }
 
        if (change || reg_change) {
@@ -2817,12 +2857,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int reg_val, val;
+       int ret = 0;
 
        if (e->reg != SND_SOC_NOPM)
-               reg_val = snd_soc_read(codec, e->reg);
+               ret = soc_dapm_read(dapm, e->reg, &reg_val);
        else
                reg_val = dapm_kcontrol_get_value(kcontrol);
 
@@ -2834,7 +2875,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
                ucontrol->value.enumerated.item[1] = val;
        }
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
 
@@ -2850,8 +2891,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_card *card = dapm->card;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int *item = ucontrol->value.enumerated.item;
        unsigned int val, change;
@@ -2874,7 +2915,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        if (e->reg != SND_SOC_NOPM)
-               change = snd_soc_test_bits(codec, e->reg, mask, val);
+               change = soc_dapm_test_bits(dapm, e->reg, mask, val);
        else
                change = dapm_kcontrol_set_value(kcontrol, val);
 
@@ -2971,6 +3012,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                         const struct snd_soc_dapm_widget *widget)
 {
        struct snd_soc_dapm_widget *w;
+       const char *prefix;
        int ret;
 
        if ((w = dapm_cnew_widget(widget)) == NULL)
@@ -3011,9 +3053,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                break;
        }
 
-       if (dapm->codec && dapm->codec->name_prefix)
-               w->name = kasprintf(GFP_KERNEL, "%s %s",
-                       dapm->codec->name_prefix, widget->name);
+       prefix = soc_dapm_prefix(dapm);
+       if (prefix)
+               w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
        else
                w->name = kasprintf(GFP_KERNEL, "%s", widget->name);
 
@@ -3066,7 +3108,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 
        w->dapm = dapm;
        w->codec = dapm->codec;
-       w->platform = dapm->platform;
        INIT_LIST_HEAD(&w->sources);
        INIT_LIST_HEAD(&w->sinks);
        INIT_LIST_HEAD(&w->list);
@@ -3173,27 +3214,15 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
-               if (source->driver->ops && source->driver->ops->hw_params) {
-                       substream.stream = SNDRV_PCM_STREAM_CAPTURE;
-                       ret = source->driver->ops->hw_params(&substream,
-                                                            params, source);
-                       if (ret != 0) {
-                               dev_err(source->dev,
-                                       "ASoC: hw_params() failed: %d\n", ret);
-                               goto out;
-                       }
-               }
+               substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+               ret = soc_dai_hw_params(&substream, params, source);
+               if (ret < 0)
+                       goto out;
 
-               if (sink->driver->ops && sink->driver->ops->hw_params) {
-                       substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
-                       ret = sink->driver->ops->hw_params(&substream, params,
-                                                          sink);
-                       if (ret != 0) {
-                               dev_err(sink->dev,
-                                       "ASoC: hw_params() failed: %d\n", ret);
-                               goto out;
-                       }
-               }
+               substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+               ret = soc_dai_hw_params(&substream, params, sink);
+               if (ret < 0)
+                       goto out;
                break;
 
        case SND_SOC_DAPM_POST_PMU:
@@ -3365,25 +3394,15 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
        return 0;
 }
 
-void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
+static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
+                                         struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_pcm_runtime *rtd = card->rtd;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dapm_widget *sink, *source;
-       struct snd_soc_dai *cpu_dai, *codec_dai;
        int i;
 
-       /* for each BE DAI link... */
-       for (i = 0; i < card->num_rtd; i++) {
-               rtd = &card->rtd[i];
-               cpu_dai = rtd->cpu_dai;
-               codec_dai = rtd->codec_dai;
-
-               /*
-                * dynamic FE links have no fixed DAI mapping.
-                * CODEC<->CODEC links have no direct connection.
-                */
-               if (rtd->dai_link->dynamic || rtd->dai_link->params)
-                       continue;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
 
                /* there is no point in connecting BE DAI links with dummies */
                if (snd_soc_dai_is_dummy(codec_dai) ||
@@ -3395,8 +3414,8 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
                        source = cpu_dai->playback_widget;
                        sink = codec_dai->playback_widget;
                        dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
-                               cpu_dai->codec->name, source->name,
-                               codec_dai->platform->name, sink->name);
+                               cpu_dai->component->name, source->name,
+                               codec_dai->component->name, sink->name);
 
                        snd_soc_dapm_add_path(&card->dapm, source, sink,
                                NULL, NULL);
@@ -3407,8 +3426,8 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
                        source = codec_dai->capture_widget;
                        sink = cpu_dai->capture_widget;
                        dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
-                               codec_dai->codec->name, source->name,
-                               cpu_dai->platform->name, sink->name);
+                               codec_dai->component->name, source->name,
+                               cpu_dai->component->name, sink->name);
 
                        snd_soc_dapm_add_path(&card->dapm, source, sink,
                                NULL, NULL);
@@ -3445,11 +3464,34 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
        }
 }
 
+void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
+{
+       struct snd_soc_pcm_runtime *rtd = card->rtd;
+       int i;
+
+       /* for each BE DAI link... */
+       for (i = 0; i < card->num_rtd; i++) {
+               rtd = &card->rtd[i];
+
+               /*
+                * dynamic FE links have no fixed DAI mapping.
+                * CODEC<->CODEC links have no direct connection.
+                */
+               if (rtd->dai_link->dynamic || rtd->dai_link->params)
+                       continue;
+
+               dapm_connect_dai_link_widgets(card, rtd);
+       }
+}
+
 static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
        int event)
 {
+       int i;
+
        soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event);
-       soc_dapm_dai_stream_event(rtd->codec_dai, stream, event);
+       for (i = 0; i < rtd->num_codecs; i++)
+               soc_dapm_dai_stream_event(rtd->codec_dais[i], stream, event);
 
        dapm_power_widgets(rtd->card, event);
 }
@@ -3758,36 +3800,31 @@ static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
 }
 
 /**
- * snd_soc_dapm_auto_nc_codec_pins - call snd_soc_dapm_nc_pin for unused pins
- * @codec: The codec whose pins should be processed
+ * snd_soc_dapm_auto_nc_pins - call snd_soc_dapm_nc_pin for unused pins
+ * @card: The card whose pins should be processed
  *
- * Automatically call snd_soc_dapm_nc_pin() for any external pins in the codec
- * which are unused. Pins are used if they are connected externally to the
- * codec, whether that be to some other device, or a loop-back connection to
- * the codec itself.
+ * Automatically call snd_soc_dapm_nc_pin() for any external pins in the card
+ * which are unused. Pins are used if they are connected externally to a
+ * component, whether that be to some other device, or a loop-back connection to
+ * the component itself.
  */
-void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec)
+void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = codec->card;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
        struct snd_soc_dapm_widget *w;
 
-       dev_dbg(codec->dev, "ASoC: Auto NC: DAPMs: card:%p codec:%p\n",
-               &card->dapm, &codec->dapm);
+       dev_dbg(card->dev, "ASoC: Auto NC: DAPMs: card:%p\n", &card->dapm);
 
        list_for_each_entry(w, &card->widgets, list) {
-               if (w->dapm != dapm)
-                       continue;
                switch (w->id) {
                case snd_soc_dapm_input:
                case snd_soc_dapm_output:
                case snd_soc_dapm_micbias:
-                       dev_dbg(codec->dev, "ASoC: Auto NC: Checking widget %s\n",
+                       dev_dbg(card->dev, "ASoC: Auto NC: Checking widget %s\n",
                                w->name);
                        if (!snd_soc_dapm_widget_in_card_paths(card, w)) {
-                               dev_dbg(codec->dev,
+                               dev_dbg(card->dev,
                                        "... Not in map; disabling\n");
-                               snd_soc_dapm_nc_pin(dapm, w->name);
+                               snd_soc_dapm_nc_pin(w->dapm, w->name);
                        }
                        break;
                default:
index d0d98810af9170baf86120b77767d6b365745f86..ab47fea997a3cd4d9b8897ab9e2ea6cf495c56dc 100644 (file)
@@ -43,7 +43,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
        INIT_LIST_HEAD(&jack->jack_zones);
        BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
 
-       return snd_jack_new(codec->card->snd_card, id, type, &jack->jack);
+       return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack);
 }
 EXPORT_SYMBOL_GPL(snd_soc_jack_new);
 
@@ -260,7 +260,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
 static irqreturn_t gpio_handler(int irq, void *data)
 {
        struct snd_soc_jack_gpio *gpio = data;
-       struct device *dev = gpio->jack->codec->card->dev;
+       struct device *dev = gpio->jack->codec->component.card->dev;
 
        trace_snd_soc_jack_irq(gpio->name);
 
index 4ea656770d65a93b55921cc8b8d7bd34d039747b..731fdb5b5f9bf898c84533641252b561cc0065fa 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (C) 2010 Texas Instruments Inc.
  *
  * Authors: Liam Girdwood <lrg@ti.com>
- *          Mark Brown <broonie@opensource.wolfsonmicro.com>       
+ *          Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  under  the terms of  the GNU General  Public License as published by the
 void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
 {
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int i;
 
        lockdep_assert_held(&rtd->pcm_mutex);
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                cpu_dai->playback_active++;
-               codec_dai->playback_active++;
+               for (i = 0; i < rtd->num_codecs; i++)
+                       rtd->codec_dais[i]->playback_active++;
        } else {
                cpu_dai->capture_active++;
-               codec_dai->capture_active++;
+               for (i = 0; i < rtd->num_codecs; i++)
+                       rtd->codec_dais[i]->capture_active++;
        }
 
        cpu_dai->active++;
-       codec_dai->active++;
        cpu_dai->component->active++;
-       codec_dai->component->active++;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               rtd->codec_dais[i]->active++;
+               rtd->codec_dais[i]->component->active++;
+       }
 }
 
 /**
@@ -78,22 +82,26 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
 void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
 {
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int i;
 
        lockdep_assert_held(&rtd->pcm_mutex);
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                cpu_dai->playback_active--;
-               codec_dai->playback_active--;
+               for (i = 0; i < rtd->num_codecs; i++)
+                       rtd->codec_dais[i]->playback_active--;
        } else {
                cpu_dai->capture_active--;
-               codec_dai->capture_active--;
+               for (i = 0; i < rtd->num_codecs; i++)
+                       rtd->codec_dais[i]->capture_active--;
        }
 
        cpu_dai->active--;
-       codec_dai->active--;
        cpu_dai->component->active--;
-       codec_dai->component->active--;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               rtd->codec_dais[i]->component->active--;
+               rtd->codec_dais[i]->active--;
+       }
 }
 
 /**
@@ -107,11 +115,16 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
  */
 bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
 {
+       int i;
+       bool ignore = true;
+
        if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
                return true;
 
-       return rtd->cpu_dai->component->ignore_pmdown_time &&
-                       rtd->codec_dai->component->ignore_pmdown_time;
+       for (i = 0; i < rtd->num_codecs; i++)
+               ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time;
+
+       return rtd->cpu_dai->component->ignore_pmdown_time && ignore;
 }
 
 /**
@@ -222,8 +235,7 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       unsigned int rate, channels, sample_bits, symmetry;
+       unsigned int rate, channels, sample_bits, symmetry, i;
 
        rate = params_rate(params);
        channels = params_channels(params);
@@ -231,8 +243,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
 
        /* reject unmatched parameters when applying symmetry */
        symmetry = cpu_dai->driver->symmetric_rates ||
-               codec_dai->driver->symmetric_rates ||
                rtd->dai_link->symmetric_rates;
+
+       for (i = 0; i < rtd->num_codecs; i++)
+               symmetry |= rtd->codec_dais[i]->driver->symmetric_rates;
+
        if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) {
                dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n",
                                cpu_dai->rate, rate);
@@ -240,8 +255,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
        }
 
        symmetry = cpu_dai->driver->symmetric_channels ||
-               codec_dai->driver->symmetric_channels ||
                rtd->dai_link->symmetric_channels;
+
+       for (i = 0; i < rtd->num_codecs; i++)
+               symmetry |= rtd->codec_dais[i]->driver->symmetric_channels;
+
        if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) {
                dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n",
                                cpu_dai->channels, channels);
@@ -249,8 +267,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
        }
 
        symmetry = cpu_dai->driver->symmetric_samplebits ||
-               codec_dai->driver->symmetric_samplebits ||
                rtd->dai_link->symmetric_samplebits;
+
+       for (i = 0; i < rtd->num_codecs; i++)
+               symmetry |= rtd->codec_dais[i]->driver->symmetric_samplebits;
+
        if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) {
                dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n",
                                cpu_dai->sample_bits, sample_bits);
@@ -264,15 +285,20 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver;
-       struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver;
        struct snd_soc_dai_link *link = rtd->dai_link;
+       unsigned int symmetry, i;
 
-       return cpu_driver->symmetric_rates || codec_driver->symmetric_rates ||
-               link->symmetric_rates || cpu_driver->symmetric_channels ||
-               codec_driver->symmetric_channels || link->symmetric_channels ||
-               cpu_driver->symmetric_samplebits ||
-               codec_driver->symmetric_samplebits ||
-               link->symmetric_samplebits;
+       symmetry = cpu_driver->symmetric_rates || link->symmetric_rates ||
+               cpu_driver->symmetric_channels || link->symmetric_channels ||
+               cpu_driver->symmetric_samplebits || link->symmetric_samplebits;
+
+       for (i = 0; i < rtd->num_codecs; i++)
+               symmetry = symmetry ||
+                       rtd->codec_dais[i]->driver->symmetric_rates ||
+                       rtd->codec_dais[i]->driver->symmetric_channels ||
+                       rtd->codec_dais[i]->driver->symmetric_samplebits;
+
+       return symmetry;
 }
 
 /*
@@ -284,15 +310,10 @@ static int sample_sizes[] = {
        24, 32,
 };
 
-static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
-                             struct snd_soc_dai *dai)
+static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
 {
-       int ret, i, bits;
-
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               bits = dai->driver->playback.sig_bits;
-       else
-               bits = dai->driver->capture.sig_bits;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int ret, i;
 
        if (!bits)
                return;
@@ -304,38 +325,105 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
                ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
                                                   sample_sizes[i], bits);
                if (ret != 0)
-                       dev_warn(dai->dev,
+                       dev_warn(rtd->dev,
                                 "ASoC: Failed to set MSB %d/%d: %d\n",
                                 bits, sample_sizes[i], ret);
        }
 }
 
-static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
-       struct snd_soc_pcm_stream *codec_stream,
-       struct snd_soc_pcm_stream *cpu_stream)
+static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai;
+       int i;
+       unsigned int bits = 0, cpu_bits;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       codec_dai = rtd->codec_dais[i];
+                       if (codec_dai->driver->playback.sig_bits == 0) {
+                               bits = 0;
+                               break;
+                       }
+                       bits = max(codec_dai->driver->playback.sig_bits, bits);
+               }
+               cpu_bits = cpu_dai->driver->playback.sig_bits;
+       } else {
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       codec_dai = rtd->codec_dais[i];
+                       if (codec_dai->driver->playback.sig_bits == 0) {
+                               bits = 0;
+                               break;
+                       }
+                       bits = max(codec_dai->driver->capture.sig_bits, bits);
+               }
+               cpu_bits = cpu_dai->driver->capture.sig_bits;
+       }
+
+       soc_pcm_set_msb(substream, bits);
+       soc_pcm_set_msb(substream, cpu_bits);
+}
+
+static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
 {
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_pcm_hardware *hw = &runtime->hw;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver;
+       struct snd_soc_dai_driver *codec_dai_drv;
+       struct snd_soc_pcm_stream *codec_stream;
+       struct snd_soc_pcm_stream *cpu_stream;
+       unsigned int chan_min = 0, chan_max = UINT_MAX;
+       unsigned int rate_min = 0, rate_max = UINT_MAX;
+       unsigned int rates = UINT_MAX;
+       u64 formats = ULLONG_MAX;
+       int i;
 
-       hw->channels_min = max(codec_stream->channels_min,
-               cpu_stream->channels_min);
-       hw->channels_max = min(codec_stream->channels_max,
-               cpu_stream->channels_max);
-       if (hw->formats)
-               hw->formats &= codec_stream->formats & cpu_stream->formats;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               cpu_stream = &cpu_dai_drv->playback;
        else
-               hw->formats = codec_stream->formats & cpu_stream->formats;
-       hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates,
-               cpu_stream->rates);
+               cpu_stream = &cpu_dai_drv->capture;
 
-       hw->rate_min = 0;
-       hw->rate_max = UINT_MAX;
+       /* first calculate min/max only for CODECs in the DAI link */
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai_drv = rtd->codec_dais[i]->driver;
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       codec_stream = &codec_dai_drv->playback;
+               else
+                       codec_stream = &codec_dai_drv->capture;
+               chan_min = max(chan_min, codec_stream->channels_min);
+               chan_max = min(chan_max, codec_stream->channels_max);
+               rate_min = max(rate_min, codec_stream->rate_min);
+               rate_max = min_not_zero(rate_max, codec_stream->rate_max);
+               formats &= codec_stream->formats;
+               rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates);
+       }
+
+       /*
+        * chan min/max cannot be enforced if there are multiple CODEC DAIs
+        * connected to a single CPU DAI, use CPU DAI's directly and let
+        * channel allocation be fixed up later
+        */
+       if (rtd->num_codecs > 1) {
+               chan_min = cpu_stream->channels_min;
+               chan_max = cpu_stream->channels_max;
+       }
+
+       hw->channels_min = max(chan_min, cpu_stream->channels_min);
+       hw->channels_max = min(chan_max, cpu_stream->channels_max);
+       if (hw->formats)
+               hw->formats &= formats & cpu_stream->formats;
+       else
+               hw->formats = formats & cpu_stream->formats;
+       hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates);
 
        snd_pcm_limit_hw_rates(runtime);
 
        hw->rate_min = max(hw->rate_min, cpu_stream->rate_min);
-       hw->rate_min = max(hw->rate_min, codec_stream->rate_min);
+       hw->rate_min = max(hw->rate_min, rate_min);
        hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
-       hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max);
+       hw->rate_max = min_not_zero(hw->rate_max, rate_max);
 }
 
 /*
@@ -349,15 +437,16 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
-       struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
-       int ret = 0;
+       struct snd_soc_dai *codec_dai;
+       const char *codec_dai_name = "multicodec";
+       int i, ret = 0;
 
        pinctrl_pm_select_default_state(cpu_dai->dev);
-       pinctrl_pm_select_default_state(codec_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++)
+               pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
        pm_runtime_get_sync(cpu_dai->dev);
-       pm_runtime_get_sync(codec_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++)
+               pm_runtime_get_sync(rtd->codec_dais[i]->dev);
        pm_runtime_get_sync(platform->dev);
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -376,18 +465,28 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                ret = platform->driver->ops->open(substream);
                if (ret < 0) {
                        dev_err(platform->dev, "ASoC: can't open platform"
-                               " %s: %d\n", platform->name, ret);
+                               " %s: %d\n", platform->component.name, ret);
                        goto platform_err;
                }
        }
 
-       if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
-               ret = codec_dai->driver->ops->startup(substream, codec_dai);
-               if (ret < 0) {
-                       dev_err(codec_dai->dev, "ASoC: can't open codec"
-                               " %s: %d\n", codec_dai->name, ret);
-                       goto codec_dai_err;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
+                       ret = codec_dai->driver->ops->startup(substream,
+                                                             codec_dai);
+                       if (ret < 0) {
+                               dev_err(codec_dai->dev,
+                                       "ASoC: can't open codec %s: %d\n",
+                                       codec_dai->name, ret);
+                               goto codec_dai_err;
+                       }
                }
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       codec_dai->tx_mask = 0;
+               else
+                       codec_dai->rx_mask = 0;
        }
 
        if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
@@ -404,13 +503,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                goto dynamic;
 
        /* Check that the codec and cpu DAIs are compatible */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback,
-                       &cpu_dai_drv->playback);
-       } else {
-               soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture,
-                       &cpu_dai_drv->capture);
-       }
+       soc_pcm_init_runtime_hw(substream);
+
+       if (rtd->num_codecs == 1)
+               codec_dai_name = rtd->codec_dai->name;
 
        if (soc_pcm_has_symmetry(substream))
                runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
@@ -418,23 +514,22 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        ret = -EINVAL;
        if (!runtime->hw.rates) {
                printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
-                       codec_dai->name, cpu_dai->name);
+                       codec_dai_name, cpu_dai->name);
                goto config_err;
        }
        if (!runtime->hw.formats) {
                printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
-                       codec_dai->name, cpu_dai->name);
+                       codec_dai_name, cpu_dai->name);
                goto config_err;
        }
        if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
            runtime->hw.channels_min > runtime->hw.channels_max) {
                printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
-                               codec_dai->name, cpu_dai->name);
+                               codec_dai_name, cpu_dai->name);
                goto config_err;
        }
 
-       soc_pcm_apply_msb(substream, codec_dai);
-       soc_pcm_apply_msb(substream, cpu_dai);
+       soc_pcm_apply_msb(substream);
 
        /* Symmetry only applies if we've already got an active stream. */
        if (cpu_dai->active) {
@@ -443,14 +538,17 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                        goto config_err;
        }
 
-       if (codec_dai->active) {
-               ret = soc_pcm_apply_symmetry(substream, codec_dai);
-               if (ret != 0)
-                       goto config_err;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               if (rtd->codec_dais[i]->active) {
+                       ret = soc_pcm_apply_symmetry(substream,
+                                                    rtd->codec_dais[i]);
+                       if (ret != 0)
+                               goto config_err;
+               }
        }
 
        pr_debug("ASoC: %s <-> %s info:\n",
-                       codec_dai->name, cpu_dai->name);
+                       codec_dai_name, cpu_dai->name);
        pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
        pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
                 runtime->hw.channels_max);
@@ -469,10 +567,15 @@ config_err:
                rtd->dai_link->ops->shutdown(substream);
 
 machine_err:
-       if (codec_dai->driver->ops->shutdown)
-               codec_dai->driver->ops->shutdown(substream, codec_dai);
+       i = rtd->num_codecs;
 
 codec_dai_err:
+       while (--i >= 0) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops->shutdown)
+                       codec_dai->driver->ops->shutdown(substream, codec_dai);
+       }
+
        if (platform->driver->ops && platform->driver->ops->close)
                platform->driver->ops->close(substream);
 
@@ -483,10 +586,13 @@ out:
        mutex_unlock(&rtd->pcm_mutex);
 
        pm_runtime_put(platform->dev);
-       pm_runtime_put(codec_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++)
+               pm_runtime_put(rtd->codec_dais[i]->dev);
        pm_runtime_put(cpu_dai->dev);
-       if (!codec_dai->active)
-               pinctrl_pm_select_sleep_state(codec_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               if (!rtd->codec_dais[i]->active)
+                       pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
+       }
        if (!cpu_dai->active)
                pinctrl_pm_select_sleep_state(cpu_dai->dev);
 
@@ -502,7 +608,7 @@ static void close_delayed_work(struct work_struct *work)
 {
        struct snd_soc_pcm_runtime *rtd =
                        container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dais[0];
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -531,7 +637,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *codec_dai;
+       int i;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -541,14 +648,20 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
        if (!cpu_dai->active)
                cpu_dai->rate = 0;
 
-       if (!codec_dai->active)
-               codec_dai->rate = 0;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (!codec_dai->active)
+                       codec_dai->rate = 0;
+       }
 
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 
-       if (codec_dai->driver->ops->shutdown)
-               codec_dai->driver->ops->shutdown(substream, codec_dai);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops->shutdown)
+                       codec_dai->driver->ops->shutdown(substream, codec_dai);
+       }
 
        if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
                rtd->dai_link->ops->shutdown(substream);
@@ -578,10 +691,13 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
        mutex_unlock(&rtd->pcm_mutex);
 
        pm_runtime_put(platform->dev);
-       pm_runtime_put(codec_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++)
+               pm_runtime_put(rtd->codec_dais[i]->dev);
        pm_runtime_put(cpu_dai->dev);
-       if (!codec_dai->active)
-               pinctrl_pm_select_sleep_state(codec_dai->dev);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               if (!rtd->codec_dais[i]->active)
+                       pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
+       }
        if (!cpu_dai->active)
                pinctrl_pm_select_sleep_state(cpu_dai->dev);
 
@@ -598,8 +714,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret = 0;
+       struct snd_soc_dai *codec_dai;
+       int i, ret = 0;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -621,12 +737,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
-       if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) {
-               ret = codec_dai->driver->ops->prepare(substream, codec_dai);
-               if (ret < 0) {
-                       dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d\n",
-                               ret);
-                       goto out;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) {
+                       ret = codec_dai->driver->ops->prepare(substream,
+                                                             codec_dai);
+                       if (ret < 0) {
+                               dev_err(codec_dai->dev,
+                                       "ASoC: DAI prepare error: %d\n", ret);
+                               goto out;
+                       }
                }
        }
 
@@ -649,13 +769,44 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        snd_soc_dapm_stream_event(rtd, substream->stream,
                        SND_SOC_DAPM_STREAM_START);
 
-       snd_soc_dai_digital_mute(codec_dai, 0, substream->stream);
+       for (i = 0; i < rtd->num_codecs; i++)
+               snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
+                                        substream->stream);
 
 out:
        mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
+static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
+                                      unsigned int mask)
+{
+       struct snd_interval *interval;
+       int channels = hweight_long(mask);
+
+       interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       interval->min = channels;
+       interval->max = channels;
+}
+
+int soc_dai_hw_params(struct snd_pcm_substream *substream,
+                     struct snd_pcm_hw_params *params,
+                     struct snd_soc_dai *dai)
+{
+       int ret;
+
+       if (dai->driver->ops && dai->driver->ops->hw_params) {
+               ret = dai->driver->ops->hw_params(substream, params, dai);
+               if (ret < 0) {
+                       dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n",
+                               dai->name, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Called by ALSA when the hardware params are set by application. This
  * function can also be called multiple times and can allocate buffers
@@ -667,8 +818,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret = 0;
+       int i, ret = 0;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -685,29 +835,40 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
-       if (codec_dai->driver->ops && codec_dai->driver->ops->hw_params) {
-               ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
-               if (ret < 0) {
-                       dev_err(codec_dai->dev, "ASoC: can't set %s hw params:"
-                               " %d\n", codec_dai->name, ret);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+               struct snd_pcm_hw_params codec_params;
+
+               /* copy params for each codec */
+               codec_params = *params;
+
+               /* fixup params based on TDM slot masks */
+               if (codec_dai->tx_mask)
+                       soc_pcm_codec_params_fixup(&codec_params,
+                                                  codec_dai->tx_mask);
+               if (codec_dai->rx_mask)
+                       soc_pcm_codec_params_fixup(&codec_params,
+                                                  codec_dai->rx_mask);
+
+               ret = soc_dai_hw_params(substream, &codec_params, codec_dai);
+               if(ret < 0)
                        goto codec_err;
-               }
-       }
 
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) {
-               ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
-               if (ret < 0) {
-                       dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n",
-                               cpu_dai->name, ret);
-                       goto interface_err;
-               }
+               codec_dai->rate = params_rate(&codec_params);
+               codec_dai->channels = params_channels(&codec_params);
+               codec_dai->sample_bits = snd_pcm_format_physical_width(
+                                               params_format(&codec_params));
        }
 
+       ret = soc_dai_hw_params(substream, params, cpu_dai);
+       if (ret < 0)
+               goto interface_err;
+
        if (platform->driver->ops && platform->driver->ops->hw_params) {
                ret = platform->driver->ops->hw_params(substream, params);
                if (ret < 0) {
                        dev_err(platform->dev, "ASoC: %s hw params failed: %d\n",
-                              platform->name, ret);
+                              platform->component.name, ret);
                        goto platform_err;
                }
        }
@@ -718,11 +879,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        cpu_dai->sample_bits =
                snd_pcm_format_physical_width(params_format(params));
 
-       codec_dai->rate = params_rate(params);
-       codec_dai->channels = params_channels(params);
-       codec_dai->sample_bits =
-               snd_pcm_format_physical_width(params_format(params));
-
 out:
        mutex_unlock(&rtd->pcm_mutex);
        return ret;
@@ -732,10 +888,16 @@ platform_err:
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
 
 interface_err:
-       if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
-               codec_dai->driver->ops->hw_free(substream, codec_dai);
+       i = rtd->num_codecs;
 
 codec_err:
+       while (--i >= 0) {
+               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
+                       codec_dai->driver->ops->hw_free(substream, codec_dai);
+               codec_dai->rate = 0;
+       }
+
        if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
                rtd->dai_link->ops->hw_free(substream);
 
@@ -751,8 +913,9 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *codec_dai;
        bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int i;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
@@ -763,16 +926,22 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
                cpu_dai->sample_bits = 0;
        }
 
-       if (codec_dai->active == 1) {
-               codec_dai->rate = 0;
-               codec_dai->channels = 0;
-               codec_dai->sample_bits = 0;
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->active == 1) {
+                       codec_dai->rate = 0;
+                       codec_dai->channels = 0;
+                       codec_dai->sample_bits = 0;
+               }
        }
 
        /* apply codec digital mute */
-       if ((playback && codec_dai->playback_active == 1) ||
-           (!playback && codec_dai->capture_active == 1))
-               snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               if ((playback && rtd->codec_dais[i]->playback_active == 1) ||
+                   (!playback && rtd->codec_dais[i]->capture_active == 1))
+                       snd_soc_dai_digital_mute(rtd->codec_dais[i], 1,
+                                                substream->stream);
+       }
 
        /* free any machine hw params */
        if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
@@ -783,8 +952,11 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
                platform->driver->ops->hw_free(substream);
 
        /* now free hw params for the DAIs  */
-       if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
-               codec_dai->driver->ops->hw_free(substream, codec_dai);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
+                       codec_dai->driver->ops->hw_free(substream, codec_dai);
+       }
 
        if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
@@ -798,13 +970,17 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
-               ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
-               if (ret < 0)
-                       return ret;
+       struct snd_soc_dai *codec_dai;
+       int i, ret;
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
+                       ret = codec_dai->driver->ops->trigger(substream,
+                                                             cmd, codec_dai);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        if (platform->driver->ops && platform->driver->ops->trigger) {
@@ -834,14 +1010,18 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       if (codec_dai->driver->ops &&
-           codec_dai->driver->ops->bespoke_trigger) {
-               ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai);
-               if (ret < 0)
-                       return ret;
+       struct snd_soc_dai *codec_dai;
+       int i, ret;
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops &&
+                   codec_dai->driver->ops->bespoke_trigger) {
+                       ret = codec_dai->driver->ops->bespoke_trigger(substream,
+                                                               cmd, codec_dai);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        if (platform->driver->bespoke_trigger) {
@@ -867,10 +1047,12 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *codec_dai;
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_uframes_t offset = 0;
        snd_pcm_sframes_t delay = 0;
+       snd_pcm_sframes_t codec_delay = 0;
+       int i;
 
        if (platform->driver->ops && platform->driver->ops->pointer)
                offset = platform->driver->ops->pointer(substream);
@@ -878,11 +1060,21 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
        if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay)
                delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
 
-       if (codec_dai->driver->ops && codec_dai->driver->ops->delay)
-               delay += codec_dai->driver->ops->delay(substream, codec_dai);
+       for (i = 0; i < rtd->num_codecs; i++) {
+               codec_dai = rtd->codec_dais[i];
+               if (codec_dai->driver->ops && codec_dai->driver->ops->delay)
+                       codec_delay = max(codec_delay,
+                                       codec_dai->driver->ops->delay(substream,
+                                                                   codec_dai));
+       }
+       delay += codec_delay;
 
+       /*
+        * None of the existing platform drivers implement delay(), so
+        * for now the codec_dai of first multicodec entry is used
+        */
        if (platform->driver->delay)
-               delay += platform->driver->delay(substream, codec_dai);
+               delay += platform->driver->delay(substream, rtd->codec_dais[0]);
 
        runtime->delay = delay;
 
@@ -985,7 +1177,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                struct snd_soc_dapm_widget *widget, int stream)
 {
        struct snd_soc_pcm_runtime *be;
-       int i;
+       int i, j;
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                for (i = 0; i < card->num_links; i++) {
@@ -994,9 +1186,14 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                        if (!be->dai_link->no_pcm)
                                continue;
 
-                       if (be->cpu_dai->playback_widget == widget ||
-                               be->codec_dai->playback_widget == widget)
+                       if (be->cpu_dai->playback_widget == widget)
                                return be;
+
+                       for (j = 0; j < be->num_codecs; j++) {
+                               struct snd_soc_dai *dai = be->codec_dais[j];
+                               if (dai->playback_widget == widget)
+                                       return be;
+                       }
                }
        } else {
 
@@ -1006,9 +1203,14 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
                        if (!be->dai_link->no_pcm)
                                continue;
 
-                       if (be->cpu_dai->capture_widget == widget ||
-                               be->codec_dai->capture_widget == widget)
+                       if (be->cpu_dai->capture_widget == widget)
                                return be;
+
+                       for (j = 0; j < be->num_codecs; j++) {
+                               struct snd_soc_dai *dai = be->codec_dais[j];
+                               if (dai->capture_widget == widget)
+                                       return be;
+                       }
                }
        }
 
@@ -1071,6 +1273,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
 
        /* Destroy any old FE <--> BE connections */
        list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+               unsigned int i;
 
                /* is there a valid CPU DAI widget for this BE */
                widget = dai_get_widget(dpcm->be->cpu_dai, stream);
@@ -1080,11 +1283,14 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
                        continue;
 
                /* is there a valid CODEC DAI widget for this BE */
-               widget = dai_get_widget(dpcm->be->codec_dai, stream);
+               for (i = 0; i < dpcm->be->num_codecs; i++) {
+                       struct snd_soc_dai *dai = dpcm->be->codec_dais[i];
+                       widget = dai_get_widget(dai, stream);
 
-               /* prune the BE if it's no longer in our active list */
-               if (widget && widget_in_list(list, widget))
-                       continue;
+                       /* prune the BE if it's no longer in our active list */
+                       if (widget && widget_in_list(list, widget))
+                               continue;
+               }
 
                dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
                        stream ? "capture" : "playback",
@@ -2114,16 +2320,22 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
        list_for_each_entry(dpcm, clients, list_be) {
 
                struct snd_soc_pcm_runtime *be = dpcm->be;
-               struct snd_soc_dai *dai = be->codec_dai;
-               struct snd_soc_dai_driver *drv = dai->driver;
+               int i;
 
                if (be->dai_link->ignore_suspend)
                        continue;
 
-               dev_dbg(be->dev, "ASoC: BE digital mute %s\n", be->dai_link->name);
+               for (i = 0; i < be->num_codecs; i++) {
+                       struct snd_soc_dai *dai = be->codec_dais[i];
+                       struct snd_soc_dai_driver *drv = dai->driver;
+
+                       dev_dbg(be->dev, "ASoC: BE digital mute %s\n",
+                                        be->dai_link->name);
 
-               if (drv->ops && drv->ops->digital_mute && dai->playback_active)
-                       drv->ops->digital_mute(dai, mute);
+                       if (drv->ops && drv->ops->digital_mute &&
+                                                       dai->playback_active)
+                               drv->ops->digital_mute(dai, mute);
+               }
        }
 
        return 0;
@@ -2188,22 +2400,27 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 {
        struct snd_soc_platform *platform = rtd->platform;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_dai *codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_pcm *pcm;
        char new_name[64];
        int ret = 0, playback = 0, capture = 0;
+       int i;
 
        if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
                playback = rtd->dai_link->dpcm_playback;
                capture = rtd->dai_link->dpcm_capture;
        } else {
-               if (codec_dai->driver->playback.channels_min &&
-                   cpu_dai->driver->playback.channels_min)
-                       playback = 1;
-               if (codec_dai->driver->capture.channels_min &&
-                   cpu_dai->driver->capture.channels_min)
-                       capture = 1;
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       codec_dai = rtd->codec_dais[i];
+                       if (codec_dai->driver->playback.channels_min)
+                               playback = 1;
+                       if (codec_dai->driver->capture.channels_min)
+                               capture = 1;
+               }
+
+               capture = capture && cpu_dai->driver->capture.channels_min;
+               playback = playback && cpu_dai->driver->playback.channels_min;
        }
 
        if (rtd->dai_link->playback_only) {
@@ -2229,7 +2446,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                                rtd->dai_link->stream_name);
                else
                        snprintf(new_name, sizeof(new_name), "%s %s-%d",
-                               rtd->dai_link->stream_name, codec_dai->name, num);
+                               rtd->dai_link->stream_name,
+                               (rtd->num_codecs > 1) ?
+                               "multicodec" : rtd->codec_dai->name, num);
 
                ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
                        capture, &pcm);
@@ -2302,8 +2521,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 
        pcm->private_free = platform->driver->pcm_free;
 out:
-       dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", codec_dai->name,
-               cpu_dai->name);
+       dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
+                (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
+                cpu_dai->name);
        return ret;
 }
 
index 02734bd4f09bd383222e88fd607066af9ec1394f..a83aff09dce2aa4dbb3755012a07f22ce1de830f 100644 (file)
@@ -41,8 +41,7 @@ static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card);
        int srate, mclk;
        int err;
@@ -105,7 +104,7 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = codec_dai->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(codec->card);
+       struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card);
 
        snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
                         &tegra_alc5632_hs_jack);
index ce73e1f62c4bd4b65686add1c14c4bae208d552e..b86cd9936ef19cad0c1ca04c60c52ed746f46b50 100644 (file)
@@ -49,8 +49,7 @@ static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
        int srate, mclk;
        int err;
@@ -127,7 +126,7 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = codec_dai->codec;
-       struct tegra_max98090 *machine = snd_soc_card_get_drvdata(codec->card);
+       struct tegra_max98090 *machine = snd_soc_card_get_drvdata(rtd->card);
 
        if (gpio_is_valid(machine->gpio_hp_det)) {
                snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
index 4feb16a99e02d28a10ba7b12149a8d73b6404266..a6898831fb9f3b0011ddd0c3341901ffe9f1d043 100644 (file)
@@ -51,8 +51,7 @@ static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
        int srate, mclk;
        int err;
@@ -110,7 +109,7 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = codec_dai->codec;
-       struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(codec->card);
+       struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
 
        snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
                         &tegra_rt5640_hp_jack);
index 8e774d1a243c624dcca5b09c25065316629d886e..769e28f6642e7f11207bf29687e1d16f96781d0b 100644 (file)
@@ -55,8 +55,7 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
        int srate, mclk;
        int err;
index 0939661df60ba2caeeba68564d9f9c8753b4df0e..86e05e938585ffed43a8ff6610ff4c162cecade3 100644 (file)
@@ -60,8 +60,7 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
        int srate, mclk;
        int err;
@@ -173,7 +172,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = codec_dai->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
 
        if (gpio_is_valid(machine->gpio_hp_det)) {
index 734bfcd211481ccccf26c86a3db23399ecf9ffb3..589d2d9b553a907d71c56acf14f12c606b3f1305 100644 (file)
@@ -50,8 +50,7 @@ static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct snd_soc_card *card = codec->card;
+       struct snd_soc_card *card = rtd->card;
        struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
        int srate, mclk;
        int err;