OSDN Git Service

Merge tag 'v4.4.214' into 10
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / sound / soc / soc-pcm.c
index 81bedd9..627bab3 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/workqueue.h>
 #include <linux/export.h>
 #include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -52,6 +53,26 @@ static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
        return codec_stream->channels_min;
 }
 
+static const struct snd_pcm_hardware no_host_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                       SNDRV_PCM_INFO_MMAP_VALID |
+                                       SNDRV_PCM_INFO_INTERLEAVED |
+                                       SNDRV_PCM_INFO_PAUSE |
+                                       SNDRV_PCM_INFO_RESUME,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE |
+                                       SNDRV_PCM_FMTBIT_S32_LE,
+       .period_bytes_min       = PAGE_SIZE >> 2,
+       .period_bytes_max       = PAGE_SIZE >> 1,
+       .periods_min            = 2,
+       .periods_max            = 4,
+       /*
+        * Increase the max buffer bytes as PAGE_SIZE bytes is
+        * not enough to encompass all the scenarios sent by
+        * userspapce.
+        */
+       .buffer_bytes_max       = PAGE_SIZE * 4,
+};
+
 /**
  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
  * @rtd: ASoC PCM runtime that is activated
@@ -156,6 +177,8 @@ int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
        const struct snd_pcm_hardware *hw)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
+       if (!runtime)
+               return 0;
        runtime->hw.info = hw->info;
        runtime->hw.formats = hw->formats;
        runtime->hw.period_bytes_min = hw->period_bytes_min;
@@ -182,8 +205,10 @@ int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
                                be->dai_link->name, event, dir);
 
                if ((event == SND_SOC_DAPM_STREAM_STOP) &&
-                   (be->dpcm[dir].users >= 1))
+                   (be->dpcm[dir].users >= 1)) {
+                       pr_debug("%s Don't close BE \n", __func__);
                        continue;
+               }
 
                snd_soc_dapm_stream_event(be, dir, event);
        }
@@ -468,6 +493,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        pm_runtime_get_sync(platform->dev);
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+       if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
+               snd_soc_set_runtime_hwparams(substream, &no_host_hardware);
 
        /* startup the audio subsystem */
        if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) {
@@ -598,7 +625,7 @@ codec_dai_err:
                platform->driver->ops->close(substream);
 
 platform_err:
-       if (cpu_dai->driver->ops->shutdown)
+       if (cpu_dai->driver->ops && cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 out:
        mutex_unlock(&rtd->pcm_mutex);
@@ -674,6 +701,20 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 
        snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
 
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
+                       /* powered down playback stream now */
+                       snd_soc_dapm_stream_event(rtd,
+                                                 SNDRV_PCM_STREAM_PLAYBACK,
+                                                 SND_SOC_DAPM_STREAM_STOP);
+               } else {
+                       /* start delayed pop wq here for playback streams */
+                       rtd->pop_wait = 1;
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &rtd->delayed_work,
+                                          msecs_to_jiffies(rtd->pmdown_time));
+               }
+       }
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 
@@ -689,20 +730,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
        if (platform->driver->ops && platform->driver->ops->close)
                platform->driver->ops->close(substream);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
-                       /* powered down playback stream now */
-                       snd_soc_dapm_stream_event(rtd,
-                                                 SNDRV_PCM_STREAM_PLAYBACK,
-                                                 SND_SOC_DAPM_STREAM_STOP);
-               } else {
-                       /* start delayed pop wq here for playback streams */
-                       rtd->pop_wait = 1;
-                       queue_delayed_work(system_power_efficient_wq,
-                                          &rtd->delayed_work,
-                                          msecs_to_jiffies(rtd->pmdown_time));
-               }
-       } else {
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
                /* capture streams can be powered down now */
                snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE,
                                          SND_SOC_DAPM_STREAM_STOP);
@@ -739,6 +767,11 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               snd_soc_dapm_stream_event(rtd,
+               SNDRV_PCM_STREAM_PLAYBACK,
+               SND_SOC_DAPM_STREAM_START);
+
        if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) {
                ret = rtd->dai_link->ops->prepare(substream);
                if (ret < 0) {
@@ -787,8 +820,15 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                cancel_delayed_work(&rtd->delayed_work);
        }
 
-       snd_soc_dapm_stream_event(rtd, substream->stream,
-                       SND_SOC_DAPM_STREAM_START);
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               for (i = 0; i < rtd->num_codecs; i++) {
+                       codec_dai = rtd->codec_dais[i];
+                       if (codec_dai->capture_active == 1)
+                               snd_soc_dapm_stream_event(rtd,
+                               SNDRV_PCM_STREAM_CAPTURE,
+                               SND_SOC_DAPM_STREAM_START);
+               }
+       }
 
        for (i = 0; i < rtd->num_codecs; i++)
                snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
@@ -796,6 +836,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
 
 out:
+       if (ret < 0 && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               pr_err("%s: Issue stop stream for codec_dai due to op failure %d = ret\n",
+               __func__, ret);
+               snd_soc_dapm_stream_event(rtd,
+               SNDRV_PCM_STREAM_PLAYBACK,
+               SND_SOC_DAPM_STREAM_STOP);
+       }
        mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
@@ -844,10 +891,31 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
+       /* perform any hw_params fixups */
+       if ((rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) &&
+                               rtd->dai_link->be_hw_params_fixup) {
+               ret = rtd->dai_link->be_hw_params_fixup(rtd,
+                               params);
+               if (ret < 0)
+                       dev_err(rtd->card->dev, "ASoC: fixup failed for %s\n",
+                       rtd->dai_link->name);
+       }
+
        ret = soc_pcm_params_symmetry(substream, params);
        if (ret)
                goto out;
 
+       /* perform any hw_params fixups */
+       if ((rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) &&
+                               rtd->dai_link->be_hw_params_fixup) {
+               ret = rtd->dai_link->be_hw_params_fixup(rtd,
+                               params);
+               if (ret < 0) {
+                       dev_err(rtd->card->dev, "ASoC: fixup failed for %s\n",
+                       rtd->dai_link->name);
+               }
+       }
+
        if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) {
                ret = rtd->dai_link->ops->hw_params(substream, params);
                if (ret < 0) {
@@ -920,6 +988,22 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        cpu_dai->channels = params_channels(params);
        cpu_dai->sample_bits =
                snd_pcm_format_physical_width(params_format(params));
+       /* malloc a page for hostless IO.
+        * FIXME: rework with alsa-lib changes so that this malloc is not required.
+        */
+       if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST) {
+               substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV;
+               substream->dma_buffer.dev.dev = rtd->dev;
+               substream->dma_buffer.dev.dev->coherent_dma_mask =
+                                       DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
+               substream->dma_buffer.private_data = NULL;
+
+               arch_setup_dma_ops(substream->dma_buffer.dev.dev,
+                                  0, 0, NULL, 0);
+               ret = snd_pcm_lib_malloc_pages(substream, PAGE_SIZE);
+               if (ret < 0)
+                       goto platform_err;
+       }
 
 out:
        mutex_unlock(&rtd->pcm_mutex);
@@ -1003,6 +1087,9 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
 
+       if (rtd->dai_link->no_host_mode == SND_SOC_DAI_LINK_NO_HOST)
+               snd_pcm_lib_free_pages(substream);
+
        mutex_unlock(&rtd->pcm_mutex);
        return 0;
 }
@@ -1099,6 +1186,9 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
        if (platform->driver->ops && platform->driver->ops->pointer)
                offset = platform->driver->ops->pointer(substream);
 
+       if (platform->driver->delay_blk)
+               return offset;
+
        if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay)
                delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
 
@@ -1123,6 +1213,22 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
        return offset;
 }
 
+static int soc_pcm_delay_blk(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       snd_pcm_sframes_t delay = 0;
+
+       if (platform->driver->delay_blk)
+               delay = platform->driver->delay_blk(substream,
+                               rtd->codec_dais[0]);
+
+       runtime->delay = delay;
+
+       return 0;
+}
+
 /* connect a FE and BE */
 static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream)
@@ -1229,7 +1335,11 @@ 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)
+                       if ((be->cpu_dai->playback_widget == widget &&
+                               (be->dai_link->stream_name &&
+                               !strcmp(be->dai_link->stream_name,
+                                   be->cpu_dai->playback_widget->sname))) ||
+                               be->codec_dai->playback_widget == widget)
                                return be;
 
                        for (j = 0; j < be->num_codecs; j++) {
@@ -1246,7 +1356,11 @@ 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)
+                       if ((be->cpu_dai->capture_widget == widget &&
+                               (be->dai_link->stream_name &&
+                               !strcmp(be->dai_link->stream_name,
+                                   be->cpu_dai->capture_widget->sname))) ||
+                               be->codec_dai->capture_widget == widget)
                                return be;
 
                        for (j = 0; j < be->num_codecs; j++) {
@@ -1716,14 +1830,14 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
 
        dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
 
-       /* shutdown the BEs */
-       dpcm_be_dai_shutdown(fe, substream->stream);
-
        dev_dbg(fe->dev, "ASoC: close FE %s\n", fe->dai_link->name);
 
        /* now shutdown the frontend */
        soc_pcm_close(substream);
 
+       /* shutdown the BEs */
+       dpcm_be_dai_shutdown(fe, substream->stream);
+
        /* run the stream event for each BE */
        dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
 
@@ -1802,6 +1916,81 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+int dpcm_fe_dai_hw_params_be(struct snd_soc_pcm_runtime *fe,
+       struct snd_soc_pcm_runtime *be,
+       struct snd_pcm_hw_params *params, int stream)
+{
+       int ret;
+       struct snd_soc_dpcm *dpcm;
+       struct snd_pcm_substream *be_substream =
+               snd_soc_dpcm_get_substream(be, stream);
+
+       /* is this op for this BE ? */
+       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+               return 0;
+
+       /* only allow hw_params() if no connected FEs are running */
+       if (!snd_soc_dpcm_can_be_params(fe, be, stream))
+               return 0;
+
+       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+                       (be->dpcm[stream].state !=
+                               SND_SOC_DPCM_STATE_HW_PARAMS) &&
+                       (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
+               return 0;
+
+       dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
+                       fe->dai_link->name);
+
+       /* perform any hw_params fixups */
+       if (be->dai_link->be_hw_params_fixup) {
+               ret = be->dai_link->be_hw_params_fixup(be,
+                               params);
+               if (ret < 0) {
+                       dev_err(be->dev,
+                                       "ASoC: hw_params BE fixup failed %d\n",
+                                       ret);
+                       goto unwind;
+               }
+       }
+
+       ret = soc_pcm_hw_params(be_substream, params);
+       if (ret < 0) {
+               dev_err(be->dev, "ASoC: hw_params BE failed %d\n", ret);
+               goto unwind;
+       }
+
+       be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+       return 0;
+
+unwind:
+       /* disable any enabled and non active backends */
+       list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+               struct snd_pcm_substream *be_substream =
+                       snd_soc_dpcm_get_substream(be, stream);
+
+               if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                       continue;
+
+               /* only allow hw_free() if no connected FEs are running */
+               if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
+                       continue;
+
+               if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+                       (be->dpcm[stream].state
+                               != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+                       (be->dpcm[stream].state
+                               != SND_SOC_DPCM_STATE_HW_FREE) &&
+                       (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+                       continue;
+
+               soc_pcm_hw_free(be_substream);
+       }
+
+       return ret;
+}
+
 int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
 {
        struct snd_soc_dpcm *dpcm;
@@ -2143,6 +2332,35 @@ out:
        return ret;
 }
 
+int dpcm_fe_dai_prepare_be(struct snd_soc_pcm_runtime *fe,
+               struct snd_soc_pcm_runtime *be, int stream)
+{
+       struct snd_pcm_substream *be_substream =
+               snd_soc_dpcm_get_substream(be, stream);
+       int ret = 0;
+
+       /* is this op for this BE ? */
+       if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+               return 0;
+
+       if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+                       (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+               return 0;
+
+       dev_dbg(be->dev, "ASoC: prepare BE %s\n",
+                       fe->dai_link->name);
+
+       ret = soc_pcm_prepare(be_substream);
+       if (ret < 0) {
+               dev_err(be->dev, "ASoC: backend prepare failed %d\n",
+                               ret);
+               return ret;
+       }
+
+       be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+       return ret;
+}
+
 static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *fe = substream->private_data;
@@ -2194,13 +2412,94 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
        return ret;
 }
 
+static void dpcm_be_async_prepare(void *data, async_cookie_t cookie)
+{
+       struct snd_soc_dpcm *dpcm = data;
+       struct snd_soc_pcm_runtime *be = dpcm->be;
+       int stream = dpcm->stream;
+       struct snd_pcm_substream *be_substream =
+               snd_soc_dpcm_get_substream(be, stream);
+       int ret;
+
+       dev_dbg(be->dev, "%s ASoC: prepare BE %s\n", __func__,
+                                       dpcm->fe->dai_link->name);
+       ret = soc_pcm_prepare(be_substream);
+       if (ret < 0) {
+               be->err_ops = ret;
+               dev_err(be->dev, "ASoC: backend prepare failed %d\n",
+                               ret);
+               return;
+       }
+       be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+}
+
+void dpcm_be_dai_prepare_async(struct snd_soc_pcm_runtime *fe, int stream,
+                                           struct async_domain *domain)
+{
+       struct snd_soc_dpcm *dpcm;
+       struct snd_soc_dpcm *dpcm_async[DPCM_MAX_BE_USERS];
+       int i = 0, j;
+
+       list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+
+               be->err_ops = 0;
+               /* is this op for this BE ? */
+               if (!snd_soc_dpcm_be_can_update(fe, be, stream))
+                       continue;
+
+               if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+                       (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+                       continue;
+
+               /* does this BE support async op ?*/
+               if ((fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE) &&
+                   (be->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) {
+                       dpcm->stream = stream;
+                       async_schedule_domain(dpcm_be_async_prepare,
+                                                           dpcm, domain);
+               } else {
+                       dpcm_async[i++] = dpcm;
+                       if (i == DPCM_MAX_BE_USERS) {
+                               dev_dbg(fe->dev, "ASoC: MAX backend users!\n");
+                               break;
+                       }
+               }
+       }
+
+       for (j = 0; j < i; j++) {
+               struct snd_soc_dpcm *dpcm = dpcm_async[j];
+               struct snd_soc_pcm_runtime *be = dpcm->be;
+               struct snd_pcm_substream *be_substream =
+                       snd_soc_dpcm_get_substream(be, stream);
+               int ret;
+
+               dev_dbg(be->dev, "ASoC: prepare BE %s\n",
+                               dpcm->fe->dai_link->name);
+
+               ret = soc_pcm_prepare(be_substream);
+               if (ret < 0) {
+                       dev_err(be->dev, "ASoC: backend prepare failed %d\n",
+                                       ret);
+                       be->err_ops = ret;
+                       return;
+               }
+
+               be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
+       }
+}
+
 static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *fe = substream->private_data;
+       struct snd_soc_dpcm *dpcm;
        int stream = substream->stream, ret = 0;
+       ASYNC_DOMAIN_EXCLUSIVE(async_domain);
 
        mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
 
+       fe->err_ops = 0;
+
        dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
 
        dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
@@ -2213,16 +2512,47 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
                goto out;
        }
 
-       ret = dpcm_be_dai_prepare(fe, substream->stream);
-       if (ret < 0)
-               goto out;
+       if (!(fe->dai_link->async_ops & ASYNC_DPCM_SND_SOC_PREPARE)) {
+               ret = dpcm_be_dai_prepare(fe, substream->stream);
+               if (ret < 0)
+                       goto out;
+               /* call prepare on the frontend */
+               ret = soc_pcm_prepare(substream);
+               if (ret < 0) {
+                       dev_err(fe->dev, "ASoC: prepare FE %s failed\n",
+                                       fe->dai_link->name);
+                       goto out;
+               }
+       } else {
+               dpcm_be_dai_prepare_async(fe, substream->stream,
+                                                       &async_domain);
 
-       /* call prepare on the frontend */
-       ret = soc_pcm_prepare(substream);
-       if (ret < 0) {
-               dev_err(fe->dev,"ASoC: prepare FE %s failed\n",
-                       fe->dai_link->name);
-               goto out;
+               /* call prepare on the frontend */
+               ret = soc_pcm_prepare(substream);
+               if (ret < 0) {
+                       fe->err_ops = ret;
+                       dev_err(fe->dev, "ASoC: prepare FE %s failed\n",
+                                       fe->dai_link->name);
+               }
+
+               async_synchronize_full_domain(&async_domain);
+
+               /* check if any BE failed */
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients,
+                                                           list_be) {
+                       struct snd_soc_pcm_runtime *be = dpcm->be;
+
+                       if (be->err_ops < 0) {
+                               ret = be->err_ops;
+                               goto out;
+                       }
+               }
+
+               /* check if FE failed */
+               if (fe->err_ops < 0) {
+                       ret = fe->err_ops;
+                       goto out;
+               }
        }
 
        /* run the stream event for each BE */
@@ -2236,6 +2566,18 @@ out:
        return ret;
 }
 
+static int soc_pcm_compat_ioctl(struct snd_pcm_substream *substream,
+                    unsigned int cmd, void *arg)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+
+       if (platform->driver->ops->compat_ioctl)
+               return platform->driver->ops->compat_ioctl(substream,
+                       cmd, arg);
+       return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
 static int soc_pcm_ioctl(struct snd_pcm_substream *substream,
                     unsigned int cmd, void *arg)
 {
@@ -2664,9 +3006,27 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                        pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
                if (capture)
                        pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
+               if (platform->driver->pcm_new)
+                       rtd->platform->driver->pcm_new(rtd);
                goto out;
        }
 
+       /* setup any hostless PCMs - i.e. no host IO is performed */
+       if (rtd->dai_link->no_host_mode) {
+               if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+                       pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->hw_no_buffer = 1;
+                       snd_soc_set_runtime_hwparams(
+                               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+                               &no_host_hardware);
+               }
+               if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+                       pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->hw_no_buffer = 1;
+                       snd_soc_set_runtime_hwparams(
+                               pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+                               &no_host_hardware);
+               }
+       }
+
        /* ASoC PCM operations */
        if (rtd->dai_link->dynamic) {
                rtd->ops.open           = dpcm_fe_dai_open;
@@ -2676,7 +3036,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                rtd->ops.hw_free        = dpcm_fe_dai_hw_free;
                rtd->ops.close          = dpcm_fe_dai_close;
                rtd->ops.pointer        = soc_pcm_pointer;
+               rtd->ops.delay_blk      = soc_pcm_delay_blk;
                rtd->ops.ioctl          = soc_pcm_ioctl;
+               rtd->ops.compat_ioctl   = soc_pcm_compat_ioctl;
        } else {
                rtd->ops.open           = soc_pcm_open;
                rtd->ops.hw_params      = soc_pcm_hw_params;
@@ -2685,7 +3047,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                rtd->ops.hw_free        = soc_pcm_hw_free;
                rtd->ops.close          = soc_pcm_close;
                rtd->ops.pointer        = soc_pcm_pointer;
+               rtd->ops.delay_blk      = soc_pcm_delay_blk;
                rtd->ops.ioctl          = soc_pcm_ioctl;
+               rtd->ops.compat_ioctl   = soc_pcm_compat_ioctl;
        }
 
        if (platform->driver->ops) {
@@ -2714,7 +3078,7 @@ 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",
+       dev_dbg(rtd->card->dev, "%s <-> %s mapping ok\n",
                 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
                 cpu_dai->name);
        return ret;