ASoC: snd_soc_component_driver has pmdown_time
[sfrench/cifs-2.6.git] / sound / soc / soc-pcm.c
index f11421f080650d14d9329ed968941be176fc6c07..8075856668c23b10e0f16eebb78cb59b8c7a233a 100644 (file)
@@ -133,16 +133,25 @@ 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)
 {
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
        int i;
        bool ignore = true;
 
        if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
                return true;
 
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               ignore &= !component->driver->pmdown_time;
+       }
+
+       /* this will be removed */
        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;
+       return ignore;
 }
 
 /**
@@ -459,7 +468,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        const char *codec_dai_name = "multicodec";
-       int i, ret = 0;
+       int i, ret = 0, __ret;
 
        pinctrl_pm_select_default_state(cpu_dai->dev);
        for (i = 0; i < rtd->num_codecs; i++)
@@ -474,7 +483,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
        /* startup the audio subsystem */
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
+       if (cpu_dai->driver->ops->startup) {
                ret = cpu_dai->driver->ops->startup(substream, cpu_dai);
                if (ret < 0) {
                        dev_err(cpu_dai->dev, "ASoC: can't open interface"
@@ -483,7 +492,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                }
        }
 
-       if (platform->driver->ops && platform->driver->ops->open) {
+       if (platform && platform->driver->ops && platform->driver->ops->open) {
                ret = platform->driver->ops->open(substream);
                if (ret < 0) {
                        dev_err(platform->dev, "ASoC: can't open platform"
@@ -492,9 +501,32 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                }
        }
 
+       ret = 0;
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->open)
+                       continue;
+
+               __ret = component->driver->ops->open(substream);
+               if (__ret < 0) {
+                       dev_err(component->dev,
+                               "ASoC: can't open component %s: %d\n",
+                               component->name, ret);
+                       ret = __ret;
+               }
+       }
+       if (ret < 0)
+               goto component_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) {
+               if (codec_dai->driver->ops->startup) {
                        ret = codec_dai->driver->ops->startup(substream,
                                                              codec_dai);
                        if (ret < 0) {
@@ -511,7 +543,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
                        codec_dai->rx_mask = 0;
        }
 
-       if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
+       if (rtd->dai_link->ops->startup) {
                ret = rtd->dai_link->ops->startup(substream);
                if (ret < 0) {
                        pr_err("ASoC: %s startup failed: %d\n",
@@ -585,7 +617,7 @@ dynamic:
        return 0;
 
 config_err:
-       if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+       if (rtd->dai_link->ops->shutdown)
                rtd->dai_link->ops->shutdown(substream);
 
 machine_err:
@@ -598,7 +630,22 @@ codec_dai_err:
                        codec_dai->driver->ops->shutdown(substream, codec_dai);
        }
 
-       if (platform->driver->ops && platform->driver->ops->close)
+component_err:
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->close)
+                       continue;
+
+               component->driver->ops->close(substream);
+       }
+
+       if (platform && platform->driver->ops && platform->driver->ops->close)
                platform->driver->ops->close(substream);
 
 platform_err:
@@ -692,12 +739,26 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
                        codec_dai->driver->ops->shutdown(substream, codec_dai);
        }
 
-       if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
+       if (rtd->dai_link->ops->shutdown)
                rtd->dai_link->ops->shutdown(substream);
 
-       if (platform->driver->ops && platform->driver->ops->close)
+       if (platform && platform->driver->ops && platform->driver->ops->close)
                platform->driver->ops->close(substream);
 
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->close)
+                       continue;
+
+               component->driver->ops->close(substream);
+       }
+
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
                        /* powered down playback stream now */
@@ -745,13 +806,15 @@ 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_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        int i, ret = 0;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
-       if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
+       if (rtd->dai_link->ops->prepare) {
                ret = rtd->dai_link->ops->prepare(substream);
                if (ret < 0) {
                        dev_err(rtd->card->dev, "ASoC: machine prepare error:"
@@ -760,7 +823,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
-       if (platform->driver->ops && platform->driver->ops->prepare) {
+       if (platform && platform->driver->ops && platform->driver->ops->prepare) {
                ret = platform->driver->ops->prepare(substream);
                if (ret < 0) {
                        dev_err(platform->dev, "ASoC: platform prepare error:"
@@ -769,9 +832,28 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->prepare)
+                       continue;
+
+               ret = component->driver->ops->prepare(substream);
+               if (ret < 0) {
+                       dev_err(component->dev,
+                               "ASoC: platform 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) {
+               if (codec_dai->driver->ops->prepare) {
                        ret = codec_dai->driver->ops->prepare(substream,
                                                              codec_dai);
                        if (ret < 0) {
@@ -783,7 +865,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) {
+       if (cpu_dai->driver->ops->prepare) {
                ret = cpu_dai->driver->ops->prepare(substream, cpu_dai);
                if (ret < 0) {
                        dev_err(cpu_dai->dev,
@@ -829,7 +911,7 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream,
 {
        int ret;
 
-       if (dai->driver->ops && dai->driver->ops->hw_params) {
+       if (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",
@@ -851,11 +933,13 @@ 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_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
-       int i, ret = 0;
+       int i, ret = 0, __ret;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
-       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
+       if (rtd->dai_link->ops->hw_params) {
                ret = rtd->dai_link->ops->hw_params(substream, params);
                if (ret < 0) {
                        dev_err(rtd->card->dev, "ASoC: machine hw_params"
@@ -910,7 +994,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                goto interface_err;
 
-       if (platform->driver->ops && platform->driver->ops->hw_params) {
+       if (platform && 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",
@@ -919,22 +1003,62 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                }
        }
 
+       ret = 0;
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->hw_params)
+                       continue;
+
+               __ret = component->driver->ops->hw_params(substream, params);
+               if (__ret < 0) {
+                       dev_err(component->dev,
+                               "ASoC: %s hw params failed: %d\n",
+                               component->name, ret);
+                       ret = __ret;
+               }
+       }
+       if (ret < 0)
+               goto component_err;
+
        /* store the parameters for each DAIs */
        cpu_dai->rate = params_rate(params);
        cpu_dai->channels = params_channels(params);
        cpu_dai->sample_bits =
                snd_pcm_format_physical_width(params_format(params));
 
-
        ret = soc_pcm_params_symmetry(substream, params);
         if (ret)
-                goto platform_err;
+               goto component_err;
 out:
        mutex_unlock(&rtd->pcm_mutex);
        return ret;
 
+component_err:
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->hw_free)
+                       continue;
+
+               component->driver->ops->hw_free(substream);
+       }
+
+       if (platform && platform->driver->ops && platform->driver->ops->hw_free)
+               platform->driver->ops->hw_free(substream);
+
 platform_err:
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
+       if (cpu_dai->driver->ops->hw_free)
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
 
 interface_err:
@@ -943,12 +1067,12 @@ interface_err:
 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)
+               if (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)
+       if (rtd->dai_link->ops->hw_free)
                rtd->dai_link->ops->hw_free(substream);
 
        mutex_unlock(&rtd->pcm_mutex);
@@ -962,6 +1086,8 @@ 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_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
@@ -994,21 +1120,36 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        }
 
        /* free any machine hw params */
-       if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
+       if (rtd->dai_link->ops->hw_free)
                rtd->dai_link->ops->hw_free(substream);
 
        /* free any DMA resources */
-       if (platform->driver->ops && platform->driver->ops->hw_free)
+       if (platform && platform->driver->ops && platform->driver->ops->hw_free)
                platform->driver->ops->hw_free(substream);
 
+       /* free any component resources */
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->hw_free)
+                       continue;
+
+               component->driver->ops->hw_free(substream);
+       }
+
        /* now free hw params for the DAIs  */
        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)
+               if (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)
+       if (cpu_dai->driver->ops->hw_free)
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
 
        mutex_unlock(&rtd->pcm_mutex);
@@ -1019,13 +1160,15 @@ 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_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        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) {
+               if (codec_dai->driver->ops->trigger) {
                        ret = codec_dai->driver->ops->trigger(substream,
                                                              cmd, codec_dai);
                        if (ret < 0)
@@ -1033,19 +1176,35 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                }
        }
 
-       if (platform->driver->ops && platform->driver->ops->trigger) {
+       if (platform && platform->driver->ops && platform->driver->ops->trigger) {
                ret = platform->driver->ops->trigger(substream, cmd);
                if (ret < 0)
                        return ret;
        }
 
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) {
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->trigger)
+                       continue;
+
+               ret = component->driver->ops->trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (cpu_dai->driver->ops->trigger) {
                ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
                if (ret < 0)
                        return ret;
        }
 
-       if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) {
+       if (rtd->dai_link->ops->trigger) {
                ret = rtd->dai_link->ops->trigger(substream, cmd);
                if (ret < 0)
                        return ret;
@@ -1064,8 +1223,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
 
        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) {
+               if (codec_dai->driver->ops->bespoke_trigger) {
                        ret = codec_dai->driver->ops->bespoke_trigger(substream,
                                                                cmd, codec_dai);
                        if (ret < 0)
@@ -1073,7 +1231,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                }
        }
 
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) {
+       if (cpu_dai->driver->ops->bespoke_trigger) {
                ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
                if (ret < 0)
                        return ret;
@@ -1089,6 +1247,8 @@ 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_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -1097,15 +1257,31 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
        snd_pcm_sframes_t codec_delay = 0;
        int i;
 
-       if (platform->driver->ops && platform->driver->ops->pointer)
+       if (platform && platform->driver->ops && platform->driver->ops->pointer)
                offset = platform->driver->ops->pointer(substream);
 
-       if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay)
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->pointer)
+                       continue;
+
+               /* FIXME: use 1st pointer */
+               offset = component->driver->ops->pointer(substream);
+               break;
+       }
+
+       if (cpu_dai->driver->ops->delay)
                delay += cpu_dai->driver->ops->delay(substream, cpu_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)
+               if (codec_dai->driver->ops->delay)
                        codec_delay = max(codec_delay,
                                        codec_dai->driver->ops->delay(substream,
                                                                    codec_dai));
@@ -2284,9 +2460,27 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
+       struct snd_soc_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
 
-       if (platform->driver->ops && platform->driver->ops->ioctl)
+       if (platform && platform->driver->ops && platform->driver->ops->ioctl)
                return platform->driver->ops->ioctl(substream, cmd, arg);
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               /* ignore duplication for now */
+               if (platform && (component == &platform->component))
+                       continue;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->ioctl)
+                       continue;
+
+               /* FIXME: use 1st ioctl */
+               return component->driver->ops->ioctl(substream, cmd, arg);
+       }
+
        return snd_pcm_lib_ioctl(substream, cmd, arg);
 }
 
@@ -2631,12 +2825,163 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
        return ret;
 }
 
+static void soc_pcm_private_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               /* need to sync the delayed work before releasing resources */
+
+               flush_delayed_work(&rtd->delayed_work);
+               component = rtdcom->component;
+
+               if (component->pcm_free)
+                       component->pcm_free(component, pcm);
+       }
+}
+
+static int soc_rtdcom_ack(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->ack)
+                       continue;
+
+               /* FIXME. it returns 1st ask now */
+               return component->driver->ops->ack(substream);
+       }
+
+       return -EINVAL;
+}
+
+static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel,
+                               unsigned long pos, void __user *buf,
+                               unsigned long bytes)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->copy_user)
+                       continue;
+
+               /* FIXME. it returns 1st copy now */
+               return component->driver->ops->copy_user(substream, channel,
+                                                        pos, buf, bytes);
+       }
+
+       return -EINVAL;
+}
+
+static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel,
+                                 unsigned long pos, void *buf, unsigned long bytes)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->copy_kernel)
+                       continue;
+
+               /* FIXME. it returns 1st copy now */
+               return component->driver->ops->copy_kernel(substream, channel,
+                                                          pos, buf, bytes);
+       }
+
+       return -EINVAL;
+}
+
+static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel,
+                                  unsigned long pos, unsigned long bytes)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->fill_silence)
+                       continue;
+
+               /* FIXME. it returns 1st silence now */
+               return component->driver->ops->fill_silence(substream, channel,
+                                                           pos, bytes);
+       }
+
+       return -EINVAL;
+}
+
+static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream,
+                                   unsigned long offset)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+       struct page *page;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->page)
+                       continue;
+
+               /* FIXME. it returns 1st page now */
+               page = component->driver->ops->page(substream, offset);
+               if (page)
+                       return page;
+       }
+
+       return NULL;
+}
+
+static int soc_rtdcom_mmap(struct snd_pcm_substream *substream,
+                          struct vm_area_struct *vma)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
+
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->driver->ops ||
+                   !component->driver->ops->mmap)
+                       continue;
+
+               /* FIXME. it returns 1st mmap now */
+               return component->driver->ops->mmap(substream, vma);
+       }
+
+       return -EINVAL;
+}
+
 /* create a new pcm */
 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;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_component *component;
+       struct snd_soc_rtdcom_list *rtdcom;
        struct snd_pcm *pcm;
        char new_name[64];
        int ret = 0, playback = 0, capture = 0;
@@ -2731,7 +3076,28 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                rtd->ops.ioctl          = soc_pcm_ioctl;
        }
 
-       if (platform->driver->ops) {
+       for_each_rtdcom(rtd, rtdcom) {
+               const struct snd_pcm_ops *ops = rtdcom->component->driver->ops;
+
+               if (!ops)
+                       continue;
+
+               if (ops->ack)
+                       rtd->ops.ack            = soc_rtdcom_ack;
+               if (ops->copy_user)
+                       rtd->ops.copy_user      = soc_rtdcom_copy_user;
+               if (ops->copy_kernel)
+                       rtd->ops.copy_kernel    = soc_rtdcom_copy_kernel;
+               if (ops->fill_silence)
+                       rtd->ops.fill_silence   = soc_rtdcom_fill_silence;
+               if (ops->page)
+                       rtd->ops.page           = soc_rtdcom_page;
+               if (ops->mmap)
+                       rtd->ops.mmap           = soc_rtdcom_mmap;
+       }
+
+       /* overwrite */
+       if (platform && platform->driver->ops) {
                rtd->ops.ack            = platform->driver->ops->ack;
                rtd->ops.copy_user      = platform->driver->ops->copy_user;
                rtd->ops.copy_kernel    = platform->driver->ops->copy_kernel;
@@ -2746,17 +3112,22 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        if (capture)
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
 
-       if (platform->driver->pcm_new) {
-               ret = platform->driver->pcm_new(rtd);
+       for_each_rtdcom(rtd, rtdcom) {
+               component = rtdcom->component;
+
+               if (!component->pcm_new)
+                       continue;
+
+               ret = component->pcm_new(component, rtd);
                if (ret < 0) {
-                       dev_err(platform->dev,
+                       dev_err(component->dev,
                                "ASoC: pcm constructor failed: %d\n",
                                ret);
                        return ret;
                }
        }
 
-       pcm->private_free = platform->driver->pcm_free;
+       pcm->private_free = soc_pcm_private_free;
 out:
        dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
                 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,