ASoC: core: Init pcm runtime work early to avoid warnings
[linux-2.6-microblaze.git] / sound / soc / soc-pcm.c
index e163dde..b78f6ff 100644 (file)
@@ -118,11 +118,8 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
        if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
                return true;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component)
                ignore &= !component->driver->use_pmdown_time;
-       }
 
        return ignore;
 }
@@ -435,8 +432,7 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream,
        struct snd_soc_component *component;
        int ret = 0;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
+       for_each_rtd_components(rtd, rtdcom, component) {
                *last = component;
 
                ret = snd_soc_component_module_get_when_open(component);
@@ -467,9 +463,7 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream,
        struct snd_soc_component *component;
        int ret = 0;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                if (component == last)
                        break;
 
@@ -500,9 +494,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        for_each_rtd_codec_dai(rtd, i, codec_dai)
                pinctrl_pm_select_default_state(codec_dai->dev);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                pm_runtime_get_sync(component->dev);
        }
 
@@ -625,9 +617,7 @@ component_err:
 out:
        mutex_unlock(&rtd->card->pcm_mutex);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                pm_runtime_mark_last_busy(component->dev);
                pm_runtime_put_autosuspend(component->dev);
        }
@@ -647,10 +637,8 @@ out:
  * This is to ensure there are no pops or clicks in between any music tracks
  * due to DAPM power cycling.
  */
-static void close_delayed_work(struct work_struct *work)
+static void close_delayed_work(struct snd_soc_pcm_runtime *rtd)
 {
-       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_dais[0];
 
        mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
@@ -670,7 +658,7 @@ static void close_delayed_work(struct work_struct *work)
        mutex_unlock(&rtd->card->pcm_mutex);
 }
 
-static void codec2codec_close_delayed_work(struct work_struct *work)
+static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
 {
        /*
         * Currently nothing to do for c2c links
@@ -740,9 +728,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
 
        mutex_unlock(&rtd->card->pcm_mutex);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                pm_runtime_mark_last_busy(component->dev);
                pm_runtime_put_autosuspend(component->dev);
        }
@@ -782,9 +768,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
                }
        }
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                ret = snd_soc_component_prepare(component, substream);
                if (ret < 0) {
                        dev_err(component->dev,
@@ -849,9 +833,7 @@ static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
        struct snd_soc_component *component;
        int ret = 0;
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                if (component == last)
                        break;
 
@@ -877,6 +859,11 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        int i, ret = 0;
 
        mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
+
+       ret = soc_pcm_params_symmetry(substream, params);
+       if (ret)
+               goto out;
+
        if (rtd->dai_link->ops->hw_params) {
                ret = rtd->dai_link->ops->hw_params(substream, params);
                if (ret < 0) {
@@ -945,9 +932,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
 
        snd_soc_dapm_update_dai(substream, params, cpu_dai);
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
-
+       for_each_rtd_components(rtd, rtdcom, component) {
                ret = snd_soc_component_hw_params(component, substream, params);
                if (ret < 0) {
                        dev_err(component->dev,
@@ -958,9 +943,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
        }
        component = NULL;
 
-       ret = soc_pcm_params_symmetry(substream, params);
-        if (ret)
-               goto component_err;
 out:
        mutex_unlock(&rtd->card->pcm_mutex);
        return ret;
@@ -1047,7 +1029,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int soc_pcm_trigger_start(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_component *component;
@@ -1056,24 +1038,56 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        struct snd_soc_dai *codec_dai;
        int i, ret;
 
+       if (rtd->dai_link->ops->trigger) {
+               ret = rtd->dai_link->ops->trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       for_each_rtd_components(rtd, rtdcom, component) {
+               ret = snd_soc_component_trigger(component, substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
+       if (ret < 0)
+               return ret;
+
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
                ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
                if (ret < 0)
                        return ret;
        }
 
-       for_each_rtdcom(rtd, rtdcom) {
-               component = rtdcom->component;
+       return 0;
+}
 
-               ret = snd_soc_component_trigger(component, substream, cmd);
+static int soc_pcm_trigger_stop(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       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_each_rtd_codec_dai(rtd, i, codec_dai) {
+               ret = snd_soc_dai_trigger(codec_dai, substream, cmd);
                if (ret < 0)
                        return ret;
        }
 
-       snd_soc_dai_trigger(cpu_dai, substream, cmd);
+       ret = snd_soc_dai_trigger(cpu_dai, substream, cmd);
        if (ret < 0)
                return ret;
 
+       for_each_rtd_components(rtd, rtdcom, component) {
+               ret = snd_soc_component_trigger(component, substream, cmd);
+               if (ret < 0)
+                       return ret;
+       }
+
        if (rtd->dai_link->ops->trigger) {
                ret = rtd->dai_link->ops->trigger(substream, cmd);
                if (ret < 0)
@@ -1083,6 +1097,28 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        return 0;
 }
 
+static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       int ret;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = soc_pcm_trigger_start(substream, cmd);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = soc_pcm_trigger_stop(substream, cmd);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
 static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                                   int cmd)
 {
@@ -1097,7 +1133,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                        return ret;
        }
 
-       snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
+       ret = snd_soc_dai_bespoke_trigger(cpu_dai, substream, cmd);
        if (ret < 0)
                return ret;
 
@@ -1146,6 +1182,9 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
 {
        struct snd_soc_dpcm *dpcm;
        unsigned long flags;
+#ifdef CONFIG_DEBUG_FS
+       char *name;
+#endif
 
        /* only add new dpcms */
        for_each_dpcm_be(fe, stream, dpcm) {
@@ -1171,9 +1210,15 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
                        stream ? "<-" : "->", be->dai_link->name);
 
 #ifdef CONFIG_DEBUG_FS
-       dpcm->debugfs_state = debugfs_create_dir(be->dai_link->name,
-                                                fe->debugfs_dpcm_root);
-       debugfs_create_u32("state", 0644, dpcm->debugfs_state, &dpcm->state);
+       name = kasprintf(GFP_KERNEL, "%s:%s", be->dai_link->name,
+                        stream ? "capture" : "playback");
+       if (name) {
+               dpcm->debugfs_state = debugfs_create_dir(name,
+                                                        fe->debugfs_dpcm_root);
+               debugfs_create_u32("state", 0644, dpcm->debugfs_state,
+                                  &dpcm->state);
+               kfree(name);
+       }
 #endif
        return 1;
 }
@@ -1378,6 +1423,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
        struct snd_soc_dapm_widget *widget;
        struct snd_soc_dai *dai;
        int prune = 0;
+       int do_prune;
 
        /* Destroy any old FE <--> BE connections */
        for_each_dpcm_be(fe, stream, dpcm) {
@@ -1391,13 +1437,16 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
                        continue;
 
                /* is there a valid CODEC DAI widget for this BE */
+               do_prune = 1;
                for_each_rtd_codec_dai(dpcm->be, i, dai) {
                        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;
+                               do_prune = 0;
                }
+               if (!do_prune)
+                       continue;
 
                dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
                        stream ? "capture" : "playback",
@@ -2282,42 +2331,81 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
 }
 EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
 
+static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
+                                 int cmd, bool fe_first)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       int ret;
+
+       /* call trigger on the frontend before the backend. */
+       if (fe_first) {
+               dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
+                       fe->dai_link->name, cmd);
+
+               ret = soc_pcm_trigger(substream, cmd);
+               if (ret < 0)
+                       return ret;
+
+               ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+               return ret;
+       }
+
+       /* call trigger on the frontend after the backend. */
+       ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
+               fe->dai_link->name, cmd);
+
+       ret = soc_pcm_trigger(substream, cmd);
+
+       return ret;
+}
+
 static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *fe = substream->private_data;
-       int stream = substream->stream, ret;
+       int stream = substream->stream;
+       int ret = 0;
        enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream];
 
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
 
        switch (trigger) {
        case SND_SOC_DPCM_TRIGGER_PRE:
-               /* call trigger on the frontend before the backend. */
-
-               dev_dbg(fe->dev, "ASoC: pre trigger FE %s cmd %d\n",
-                               fe->dai_link->name, cmd);
-
-               ret = soc_pcm_trigger(substream, cmd);
-               if (ret < 0) {
-                       dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
-                       goto out;
+               switch (cmd) {
+               case SNDRV_PCM_TRIGGER_START:
+               case SNDRV_PCM_TRIGGER_RESUME:
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+                       break;
+               case SNDRV_PCM_TRIGGER_STOP:
+               case SNDRV_PCM_TRIGGER_SUSPEND:
+               case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
                }
-
-               ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
                break;
        case SND_SOC_DPCM_TRIGGER_POST:
-               /* call trigger on the frontend after the backend. */
-
-               ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
-               if (ret < 0) {
-                       dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
-                       goto out;
+               switch (cmd) {
+               case SNDRV_PCM_TRIGGER_START:
+               case SNDRV_PCM_TRIGGER_RESUME:
+               case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, false);
+                       break;
+               case SNDRV_PCM_TRIGGER_STOP:
+               case SNDRV_PCM_TRIGGER_SUSPEND:
+               case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+                       ret = dpcm_dai_trigger_fe_be(substream, cmd, true);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;
                }
-
-               dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
-                               fe->dai_link->name, cmd);
-
-               ret = soc_pcm_trigger(substream, cmd);
                break;
        case SND_SOC_DPCM_TRIGGER_BESPOKE:
                /* bespoke trigger() - handles both FE and BEs */
@@ -2326,10 +2414,6 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
                                fe->dai_link->name, cmd);
 
                ret = soc_pcm_bespoke_trigger(substream, cmd);
-               if (ret < 0) {
-                       dev_err(fe->dev,"ASoC: trigger FE failed %d\n", ret);
-                       goto out;
-               }
                break;
        default:
                dev_err(fe->dev, "ASoC: invalid trigger cmd %d for %s\n", cmd,
@@ -2338,6 +2422,12 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
                goto out;
        }
 
+       if (ret < 0) {
+               dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
+                       cmd, ret);
+               goto out;
+       }
+
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
@@ -2802,21 +2892,13 @@ 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;
-
-       /* need to sync the delayed work before releasing resources */
-       flush_delayed_work(&rtd->delayed_work);
-       snd_soc_pcm_component_free(pcm);
-}
-
 /* create a new pcm */
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 {
        struct snd_soc_dai *codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_rtdcom_list *rtdcom;
+       struct snd_soc_component *component;
        struct snd_pcm *pcm;
        char new_name[64];
        int ret = 0, playback = 0, capture = 0;
@@ -2890,10 +2972,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 
        /* DAPM dai link stream work */
        if (rtd->dai_link->params)
-               INIT_DELAYED_WORK(&rtd->delayed_work,
-                                 codec2codec_close_delayed_work);
+               rtd->close_delayed_work_func = codec2codec_close_delayed_work;
        else
-               INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+               rtd->close_delayed_work_func = close_delayed_work;
 
        pcm->nonatomic = rtd->dai_link->nonatomic;
        rtd->pcm = pcm;
@@ -2928,17 +3009,14 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
                rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
        }
 
-       for_each_rtdcom(rtd, rtdcom) {
-               const struct snd_pcm_ops *ops = rtdcom->component->driver->ops;
-
-               if (!ops)
-                       continue;
+       for_each_rtd_components(rtd, rtdcom, component) {
+               const struct snd_soc_component_driver *drv = component->driver;
 
-               if (ops->copy_user)
+               if (drv->copy_user)
                        rtd->ops.copy_user      = snd_soc_pcm_component_copy_user;
-               if (ops->page)
+               if (drv->page)
                        rtd->ops.page           = snd_soc_pcm_component_page;
-               if (ops->mmap)
+               if (drv->mmap)
                        rtd->ops.mmap           = snd_soc_pcm_component_mmap;
        }
 
@@ -2948,13 +3026,12 @@ 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);
 
-       ret = snd_soc_pcm_component_new(pcm);
+       ret = snd_soc_pcm_component_new(rtd);
        if (ret < 0) {
                dev_err(rtd->dev, "ASoC: pcm constructor failed: %d\n", ret);
                return ret;
        }
 
-       pcm->private_free = soc_pcm_private_free;
        pcm->no_device_suspend = true;
 out:
        dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",