@@ -325,3 +325,451 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
return 0;
}
+
+/*
+ * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
+ * need create the pipeline. So we do following:
+ * - check the resources
+ * - Create the pipeline
+ * - Initialize the modules in pipeline
+ * - finally bind all modules together
+ */
+static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret;
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ /* check resource available */
+ if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+ return -EBUSY;
+
+ if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+ return -ENOMEM;
+
+ /*
+ * Create a list of modules for pipe.
+ * This list contains modules from source to sink
+ */
+ ret = skl_create_pipeline(ctx, mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * we create a w_list of all widgets in that pipe. This list is not
+ * freed on PMD event as widgets within a pipe are static. This
+ * saves us cycles to get widgets in pipe every time.
+ *
+ * So if we have already initialized all the widgets of a pipeline
+ * we skip, so check for list_empty and create the list if empty
+ */
+ if (list_empty(&s_pipe->w_list)) {
+ ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Init all pipe modules from source to sink */
+ ret = skl_tplg_init_pipe_modules(skl, s_pipe);
+ if (ret < 0)
+ return ret;
+
+ /* Bind modules from source to sink */
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_bind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ return 0;
+}
+
+/*
+ * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
+ * we need to do following:
+ * - Bind to sink pipeline
+ * Since the sink pipes can be running and we don't get mixer event on
+ * connect for already running mixer, we need to find the sink pipes
+ * here and bind to them. This way dynamic connect works.
+ * - Start sink pipeline, if not running
+ * - Then run current pipe
+ */
+static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_path *p;
+ struct skl_dapm_path_list *path_list;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int ret = 0;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ /*
+ * find which sink it is connected to, bind with the sink,
+ * if sink is not started, start sink pipe first, then start
+ * this pipe
+ */
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
+ dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->sink->priv != NULL) &&
+ is_skl_dsp_widget_type(p->sink)) {
+
+ sink = p->sink;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+
+ /* Bind source to sink, mixin is always source */
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ /* Start sinks pipe first */
+ if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+ }
+
+ path_list = kzalloc(
+ sizeof(struct skl_dapm_path_list),
+ GFP_KERNEL);
+ if (path_list == NULL)
+ return -ENOMEM;
+
+ /* Add connected path to one global list */
+ path_list->dapm_path = p;
+ list_add_tail(&path_list->node, &skl->dapm_path_list);
+ break;
+ }
+ }
+
+ /* Start source pipe last after starting all sinks */
+ ret = skl_run_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * in the Post-PMU event of mixer we need to do following:
+ * - Check if this pipe is running
+ * - if not, then
+ * - bind this pipeline to its source pipeline
+ * if source pipe is already running, this means it is a dynamic
+ * connection and we need to bind only to that pipe
+ * - start this pipeline
+ */
+static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ int ret = 0;
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ struct skl_sst *ctx = skl->skl_sst;
+ int src_pipe_started = 0;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /*
+ * If source pipe is already started, that means source is driving
+ * one more sink before this sink got connected, Since source is
+ * started, bind this sink to source and start this pipe.
+ */
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (!p->connect)
+ continue;
+
+ dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
+ dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
+
+ /*
+ * here we will check widgets in sink pipelines, so that
+ * can be any widgets type and we are only interested if
+ * they are ones used for SKL so check that first
+ */
+ if ((p->source->priv != NULL) &&
+ is_skl_dsp_widget_type(p->source)) {
+ source = p->source;
+ src_mconfig = source->priv;
+ sink_mconfig = sink->priv;
+ src_pipe_started = 1;
+
+ /*
+ * check pipe state, then no need to bind or start
+ * the pipe
+ */
+ if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
+ src_pipe_started = 0;
+ }
+ }
+
+ if (src_pipe_started) {
+ ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+ if (ret)
+ return ret;
+
+ ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Pre-PMD event of mixer we need to do following:
+ * - Stop the pipe
+ * - find the source connections and remove that from dapm_path_list
+ * - unbind with source pipelines if still connected
+ */
+static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ sink = w;
+ sink_mconfig = sink->priv;
+
+ /* Stop the pipe */
+ ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ /*
+ * This list, dapm_path_list handling here does not need any locks
+ * as we are under dapm lock while handling widget events.
+ * List can be manipulated safely only under dapm widgets handler
+ * routines
+ */
+ list_for_each_entry_safe(path_list, tmp_list,
+ &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->sink == sink) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ source = path_list->dapm_path->source;
+ src_mconfig = source->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * If path_found == 1, that means pmd for source pipe has
+ * not occurred, source is connected to some other sink.
+ * so its responsibility of sink to unbind itself from source.
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of mixer we need to do following:
+ * - Free the mcps used
+ * - Free the mem used
+ * - Unbind the modules within the pipeline
+ * - Delete the pipeline (modules are not required to be explicitly
+ * deleted, pipeline delete is enough here
+ */
+static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct skl_module_cfg *mconfig = w->priv;
+ struct skl_pipe_module *w_module;
+ struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_pipe *s_pipe = mconfig->pipe;
+ int ret = 0;
+
+ skl_tplg_free_pipe_mcps(skl, mconfig);
+
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ dst_module = w_module->w->priv;
+
+ if (src_module == NULL) {
+ src_module = dst_module;
+ continue;
+ }
+
+ ret = skl_unbind_modules(ctx, src_module, dst_module);
+ if (ret < 0)
+ return ret;
+
+ src_module = dst_module;
+ }
+
+ ret = skl_delete_pipe(ctx, mconfig->pipe);
+ skl_tplg_free_pipe_mem(skl, mconfig);
+
+ return ret;
+}
+
+/*
+ * in the Post-PMD event of PGA we need to do following:
+ * - Free the mcps used
+ * - Stop the pipeline
+ * - In source pipe is connected, unbind with source pipelines
+ */
+static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+ struct skl *skl)
+{
+ struct snd_soc_dapm_widget *source, *sink;
+ struct skl_module_cfg *src_mconfig, *sink_mconfig;
+ int ret = 0, path_found = 0;
+ struct skl_dapm_path_list *path_list, *tmp_path_list;
+ struct skl_sst *ctx = skl->skl_sst;
+
+ source = w;
+ src_mconfig = source->priv;
+
+ skl_tplg_free_pipe_mcps(skl, src_mconfig);
+ /* Stop the pipe since this is a mixin module */
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret)
+ return ret;
+
+ list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
+ if (path_list->dapm_path->source == source) {
+ dev_dbg(ctx->dev, "Path found = %s\n",
+ path_list->dapm_path->name);
+ sink = path_list->dapm_path->sink;
+ sink_mconfig = sink->priv;
+ path_found = 1;
+
+ list_del(&path_list->node);
+ kfree(path_list);
+ break;
+ }
+ }
+
+ /*
+ * This is a connector and if path is found that means
+ * unbind between source and sink has not happened yet
+ */
+ if (path_found) {
+ ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+ }
+
+ return ret;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If
+ * mixer is not required then it is treated as static mixer aka vmixer with
+ * a hard path to source module
+ * So we don't need to check if source is started or not as hard path puts
+ * dependency on each other
+ */
+static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
+ * second one is required that is created as another pipe entity.
+ * The mixer is responsible for pipe management and represent a pipeline
+ * instance
+ */
+static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMU:
+ return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_PRE_PMD:
+ return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
+
+/*
+ * In modelling, we assumed rest of the modules in pipeline are PGA. But we
+ * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
+ * the sink when it is running (two FE to one BE or one FE to two BE)
+ * scenarios
+ */
+static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct skl *skl = get_skl_ctx(dapm->dev);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
+
+ case SND_SOC_DAPM_POST_PMD:
+ return skl_tplg_pga_dapm_post_pmd_event(w, skl);
+ }
+
+ return 0;
+}
@@ -273,6 +273,13 @@ struct skl_dapm_path_list {
struct list_head node;
};
+static inline struct skl *get_skl_ctx(struct device *dev)
+{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+
+ return ebus_to_skl(ebus);
+}
+
int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);