]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - sound/soc/soc-dapm.c
Merge branch 'dmi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvar...
[linux.git] / sound / soc / soc-dapm.c
index 2790c00735f3abaa4582fd95854b7134780ae869..b6378f025836c6b7c91cd6c1f476afbecaa5e81e 100644 (file)
@@ -684,8 +684,8 @@ int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
 {
        int ret = 0;
 
-       if (dapm->set_bias_level)
-               ret = dapm->set_bias_level(dapm, level);
+       if (dapm->component)
+               ret = snd_soc_component_set_bias_level(dapm->component, level);
 
        if (ret == 0)
                dapm->bias_level = level;
@@ -1128,6 +1128,34 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
        return 0;
 }
 
+/*
+ * Recursively reset the cached number of inputs or outputs for the specified
+ * widget and all widgets that can be reached via incoming or outcoming paths
+ * from the widget.
+ */
+static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
+       enum snd_soc_dapm_direction dir)
+{
+       enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
+       struct snd_soc_dapm_path *path;
+
+       widget->endpoints[dir] = -1;
+
+       snd_soc_dapm_widget_for_each_path(widget, rdir, path) {
+               if (path->weak || path->is_supply)
+                       continue;
+
+               if (path->walking)
+                       return;
+
+               if (path->connect) {
+                       path->walking = 1;
+                       invalidate_paths_ep(path->node[dir], dir);
+                       path->walking = 0;
+               }
+       }
+}
+
 /*
  * Common implementation for is_connected_output_ep() and
  * is_connected_input_ep(). The function is inlined since the combined size of
@@ -1257,21 +1285,17 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-       /*
-        * For is_connected_{output,input}_ep fully discover the graph we need
-        * to reset the cached number of inputs and outputs.
-        */
-       list_for_each_entry(w, &card->widgets, list) {
-               w->endpoints[SND_SOC_DAPM_DIR_IN] = -1;
-               w->endpoints[SND_SOC_DAPM_DIR_OUT] = -1;
-       }
-
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               paths = is_connected_output_ep(dai->playback_widget, &widgets,
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w = dai->playback_widget;
+               invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
+               paths = is_connected_output_ep(w, &widgets,
                                custom_stop_condition);
-       else
-               paths = is_connected_input_ep(dai->capture_widget, &widgets,
+       } else {
+               w = dai->capture_widget;
+               invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);
+               paths = is_connected_input_ep(w, &widgets,
                                custom_stop_condition);
+       }
 
        /* Drop starting point */
        list_del(widgets.next);
@@ -1611,12 +1635,12 @@ static void dapm_seq_run(struct snd_soc_card *card,
                        if (!list_empty(&pending))
                                dapm_seq_run_coalesced(card, &pending);
 
-                       if (cur_dapm && cur_dapm->seq_notifier) {
+                       if (cur_dapm && cur_dapm->component) {
                                for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
                                        if (sort[i] == cur_sort)
-                                               cur_dapm->seq_notifier(cur_dapm,
-                                                                      i,
-                                                                      cur_subseq);
+                                               snd_soc_component_seq_notifier(
+                                                       cur_dapm->component,
+                                                       i, cur_subseq);
                        }
 
                        if (cur_dapm && w->dapm != cur_dapm)
@@ -1674,11 +1698,12 @@ static void dapm_seq_run(struct snd_soc_card *card,
        if (!list_empty(&pending))
                dapm_seq_run_coalesced(card, &pending);
 
-       if (cur_dapm && cur_dapm->seq_notifier) {
+       if (cur_dapm && cur_dapm->component) {
                for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
                        if (sort[i] == cur_sort)
-                               cur_dapm->seq_notifier(cur_dapm,
-                                                      i, cur_subseq);
+                               snd_soc_component_seq_notifier(
+                                       cur_dapm->component,
+                                       i, cur_subseq);
        }
 
        list_for_each_entry(d, &card->dapm_list, list) {
@@ -1912,6 +1937,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
        LIST_HEAD(down_list);
        ASYNC_DOMAIN_EXCLUSIVE(async_domain);
        enum snd_soc_bias_level bias;
+       int ret;
 
        lockdep_assert_held(&card->dapm_mutex);
 
@@ -2028,8 +2054,12 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
 
        /* do we need to notify any clients that DAPM event is complete */
        list_for_each_entry(d, &card->dapm_list, list) {
-               if (d->stream_event)
-                       d->stream_event(d, event);
+               if (!d->component)
+                       continue;
+
+               ret = snd_soc_component_stream_event(d->component, event);
+               if (ret < 0)
+                       return ret;
        }
 
        pop_dbg(card->dev, card->pop_time,
@@ -2154,50 +2184,28 @@ static const struct file_operations dapm_bias_fops = {
 void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
        struct dentry *parent)
 {
-       struct dentry *d;
-
        if (!parent || IS_ERR(parent))
                return;
 
        dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
 
-       if (IS_ERR(dapm->debugfs_dapm)) {
-               dev_warn(dapm->dev,
-                        "ASoC: Failed to create DAPM debugfs directory %ld\n",
-                        PTR_ERR(dapm->debugfs_dapm));
-               return;
-       }
-
-       d = debugfs_create_file("bias_level", 0444,
-                               dapm->debugfs_dapm, dapm,
-                               &dapm_bias_fops);
-       if (IS_ERR(d))
-               dev_warn(dapm->dev,
-                        "ASoC: Failed to create bias level debugfs file: %ld\n",
-                        PTR_ERR(d));
+       debugfs_create_file("bias_level", 0444, dapm->debugfs_dapm, dapm,
+                           &dapm_bias_fops);
 }
 
 static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w)
 {
        struct snd_soc_dapm_context *dapm = w->dapm;
-       struct dentry *d;
 
        if (!dapm->debugfs_dapm || !w->name)
                return;
 
-       d = debugfs_create_file(w->name, 0444,
-                               dapm->debugfs_dapm, w,
-                               &dapm_widget_power_fops);
-       if (IS_ERR(d))
-               dev_warn(w->dapm->dev,
-                        "ASoC: Failed to create %s debugfs file: %ld\n",
-                        w->name, PTR_ERR(d));
+       debugfs_create_file(w->name, 0444, dapm->debugfs_dapm, w,
+                           &dapm_widget_power_fops);
 }
 
 static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 {
-       if (!dapm->debugfs_dapm)
-               return;
        debugfs_remove_recursive(dapm->debugfs_dapm);
        dapm->debugfs_dapm = NULL;
 }
@@ -3766,25 +3774,70 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
 
-static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
-                                 struct snd_kcontrol *kcontrol, int event)
+static int
+snd_soc_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
+                              struct snd_pcm_substream *substream)
 {
        struct snd_soc_dapm_path *path;
        struct snd_soc_dai *source, *sink;
-       struct snd_soc_pcm_runtime *rtd = w->priv;
-       const struct snd_soc_pcm_stream *config;
-       struct snd_pcm_substream substream;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_pcm_hw_params *params = NULL;
+       const struct snd_soc_pcm_stream *config = NULL;
        struct snd_pcm_runtime *runtime = NULL;
        unsigned int fmt;
        int ret = 0;
 
-       config = rtd->dai_link->params + rtd->params_select;
+       params = kzalloc(sizeof(*params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
 
-       if (WARN_ON(!config) ||
-           WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
-                   list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])))
-               return -EINVAL;
+       runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+       if (!runtime) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       substream->runtime = runtime;
+
+       substream->stream = SNDRV_PCM_STREAM_CAPTURE;
+       snd_soc_dapm_widget_for_each_source_path(w, path) {
+               source = path->source->priv;
+
+               ret = snd_soc_dai_startup(source, substream);
+               if (ret < 0) {
+                       dev_err(source->dev,
+                               "ASoC: startup() failed: %d\n", ret);
+                       goto out;
+               }
+               source->active++;
+       }
+
+       substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
+       snd_soc_dapm_widget_for_each_sink_path(w, path) {
+               sink = path->sink->priv;
+
+               ret = snd_soc_dai_startup(sink, substream);
+               if (ret < 0) {
+                       dev_err(sink->dev,
+                               "ASoC: startup() failed: %d\n", ret);
+                       goto out;
+               }
+               sink->active++;
+       }
+
+       substream->hw_opened = 1;
+
+       /*
+        * Note: getting the config after .startup() gives a chance to
+        * either party on the link to alter the configuration if
+        * necessary
+        */
+       config = rtd->dai_link->params + rtd->params_select;
+       if (WARN_ON(!config)) {
+               dev_err(w->dapm->dev, "ASoC: link config missing\n");
+               ret = -EINVAL;
+               goto out;
+       }
 
        /* Be a little careful as we don't want to overflow the mask array */
        if (config->formats) {
@@ -3792,83 +3845,74 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
        } else {
                dev_warn(w->dapm->dev, "ASoC: Invalid format %llx specified\n",
                         config->formats);
-               fmt = 0;
-       }
 
-       /* Currently very limited parameter selection */
-       params = kzalloc(sizeof(*params), GFP_KERNEL);
-       if (!params) {
-               ret = -ENOMEM;
+               ret = -EINVAL;
                goto out;
        }
-       snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
 
+       snd_mask_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), fmt);
        hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min =
                config->rate_min;
        hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max =
                config->rate_max;
-
        hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min
                = config->channels_min;
        hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->max
                = config->channels_max;
 
-       memset(&substream, 0, sizeof(substream));
+       substream->stream = SNDRV_PCM_STREAM_CAPTURE;
+       snd_soc_dapm_widget_for_each_source_path(w, path) {
+               source = path->source->priv;
 
-       /* Allocate a dummy snd_pcm_runtime for startup() and other ops() */
-       runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
-       if (!runtime) {
-               ret = -ENOMEM;
-               goto out;
+               ret = snd_soc_dai_hw_params(source, substream, params);
+               if (ret < 0)
+                       goto out;
+
+               dapm_update_dai_unlocked(substream, params, source);
        }
-       substream.runtime = runtime;
-       substream.private_data = rtd;
 
-       switch (event) {
-       case SND_SOC_DAPM_PRE_PMU:
-               substream.stream = SNDRV_PCM_STREAM_CAPTURE;
-               snd_soc_dapm_widget_for_each_source_path(w, path) {
-                       source = path->source->priv;
+       substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
+       snd_soc_dapm_widget_for_each_sink_path(w, path) {
+               sink = path->sink->priv;
 
-                       if (source->driver->ops->startup) {
-                               ret = source->driver->ops->startup(&substream,
-                                                                  source);
-                               if (ret < 0) {
-                                       dev_err(source->dev,
-                                               "ASoC: startup() failed: %d\n",
-                                               ret);
-                                       goto out;
-                               }
-                       }
-                       source->active++;
-                       ret = soc_dai_hw_params(&substream, params, source);
-                       if (ret < 0)
-                               goto out;
+               ret = snd_soc_dai_hw_params(sink, substream, params);
+               if (ret < 0)
+                       goto out;
 
-                       dapm_update_dai_unlocked(&substream, params, source);
-               }
+               dapm_update_dai_unlocked(substream, params, sink);
+       }
 
-               substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
-               snd_soc_dapm_widget_for_each_sink_path(w, path) {
-                       sink = path->sink->priv;
+       runtime->format = params_format(params);
+       runtime->subformat = params_subformat(params);
+       runtime->channels = params_channels(params);
+       runtime->rate = params_rate(params);
 
-                       if (sink->driver->ops->startup) {
-                               ret = sink->driver->ops->startup(&substream,
-                                                                sink);
-                               if (ret < 0) {
-                                       dev_err(sink->dev,
-                                               "ASoC: startup() failed: %d\n",
-                                               ret);
-                                       goto out;
-                               }
-                       }
-                       sink->active++;
-                       ret = soc_dai_hw_params(&substream, params, sink);
-                       if (ret < 0)
-                               goto out;
+out:
+       if (ret < 0)
+               kfree(runtime);
+
+       kfree(params);
+       return ret;
+}
+
+static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_dapm_path *path;
+       struct snd_soc_dai *source, *sink;
+       struct snd_pcm_substream *substream = w->priv;
+       int ret = 0, saved_stream = substream->stream;
+
+       if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) ||
+                   list_empty(&w->edges[SND_SOC_DAPM_DIR_IN])))
+               return -EINVAL;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               ret = snd_soc_dai_link_event_pre_pmu(w, substream);
+               if (ret < 0)
+                       goto out;
 
-                       dapm_update_dai_unlocked(&substream, params, sink);
-               }
                break;
 
        case SND_SOC_DAPM_POST_PMU:
@@ -3896,41 +3940,45 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
                        ret = 0;
                }
 
-               substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+               substream->stream = SNDRV_PCM_STREAM_CAPTURE;
                snd_soc_dapm_widget_for_each_source_path(w, path) {
                        source = path->source->priv;
+                       snd_soc_dai_hw_free(source, substream);
+               }
 
-                       if (source->driver->ops->hw_free)
-                               source->driver->ops->hw_free(&substream,
-                                                            source);
+               substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
+               snd_soc_dapm_widget_for_each_sink_path(w, path) {
+                       sink = path->sink->priv;
+                       snd_soc_dai_hw_free(sink, substream);
+               }
 
+               substream->stream = SNDRV_PCM_STREAM_CAPTURE;
+               snd_soc_dapm_widget_for_each_source_path(w, path) {
+                       source = path->source->priv;
                        source->active--;
-                       if (source->driver->ops->shutdown)
-                               source->driver->ops->shutdown(&substream,
-                                                             source);
+                       snd_soc_dai_shutdown(source, substream);
                }
 
-               substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+               substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
                snd_soc_dapm_widget_for_each_sink_path(w, path) {
                        sink = path->sink->priv;
-
-                       if (sink->driver->ops->hw_free)
-                               sink->driver->ops->hw_free(&substream, sink);
-
                        sink->active--;
-                       if (sink->driver->ops->shutdown)
-                               sink->driver->ops->shutdown(&substream, sink);
+                       snd_soc_dai_shutdown(sink, substream);
                }
                break;
 
+       case SND_SOC_DAPM_POST_PMD:
+               kfree(substream->runtime);
+               break;
+
        default:
                WARN(1, "Unknown event %d\n", event);
                ret = -EINVAL;
        }
 
 out:
-       kfree(runtime);
-       kfree(params);
+       /* Restore the substream direction */
+       substream->stream = saved_stream;
        return ret;
 }
 
@@ -4053,10 +4101,11 @@ snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card,
 }
 
 static struct snd_soc_dapm_widget *
-snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd,
-                    struct snd_soc_dapm_widget *source,
-                    struct snd_soc_dapm_widget *sink)
+snd_soc_dapm_new_dai(struct snd_soc_card *card,
+                    struct snd_pcm_substream *substream,
+                    char *id)
 {
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dapm_widget template;
        struct snd_soc_dapm_widget *w;
        const char **w_param_text;
@@ -4065,7 +4114,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd,
        int ret;
 
        link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s",
-                                  source->name, sink->name);
+                                  rtd->dai_link->name, id);
        if (!link_name)
                return ERR_PTR(-ENOMEM);
 
@@ -4075,7 +4124,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd,
        template.name = link_name;
        template.event = snd_soc_dai_link_event;
        template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-               SND_SOC_DAPM_PRE_PMD;
+               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD;
        template.kcontrol_news = NULL;
 
        /* allocate memory for control, only in case of multiple configs */
@@ -4110,7 +4159,7 @@ snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd,
                goto outfree_kcontrol_news;
        }
 
-       w->priv = rtd;
+       w->priv = substream;
 
        return w;
 
@@ -4232,6 +4281,8 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
        struct snd_soc_dai *codec_dai;
        struct snd_soc_dapm_widget *playback = NULL, *capture = NULL;
        struct snd_soc_dapm_widget *codec, *playback_cpu, *capture_cpu;
+       struct snd_pcm_substream *substream;
+       struct snd_pcm_str *streams = rtd->pcm->streams;
        int i;
 
        if (rtd->dai_link->params) {
@@ -4245,15 +4296,14 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
        }
 
        for_each_rtd_codec_dai(rtd, i, codec_dai) {
-
                /* connect BE DAI playback if widgets are valid */
                codec = codec_dai->playback_widget;
 
                if (playback_cpu && codec) {
                        if (!playback) {
-                               playback = snd_soc_dapm_new_dai(card, rtd,
-                                                               playback_cpu,
-                                                               codec);
+                               substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+                               playback = snd_soc_dapm_new_dai(card, substream,
+                                                               "playback");
                                if (IS_ERR(playback)) {
                                        dev_err(rtd->dev,
                                                "ASoC: Failed to create DAI %s: %ld\n",
@@ -4281,9 +4331,9 @@ static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
 
                if (codec && capture_cpu) {
                        if (!capture) {
-                               capture = snd_soc_dapm_new_dai(card, rtd,
-                                                              codec,
-                                                              capture_cpu);
+                               substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+                               capture = snd_soc_dapm_new_dai(card, substream,
+                                                              "capture");
                                if (IS_ERR(capture)) {
                                        dev_err(rtd->dev,
                                                "ASoC: Failed to create DAI %s: %ld\n",
@@ -4667,6 +4717,27 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
+void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
+                      struct snd_soc_card *card,
+                      struct snd_soc_component *component)
+{
+       dapm->card              = card;
+       dapm->component         = component;
+       dapm->bias_level        = SND_SOC_BIAS_OFF;
+
+       if (component) {
+               dapm->dev               = component->dev;
+               dapm->idle_bias_off     = !component->driver->idle_bias_on,
+               dapm->suspend_bias_off  = component->driver->suspend_bias_off;
+       } else {
+               dapm->dev               = card->dev;
+       }
+
+       INIT_LIST_HEAD(&dapm->list);
+       list_add(&dapm->list, &card->dapm_list);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_init);
+
 static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
 {
        struct snd_soc_card *card = dapm->card;