Merge branch 'fix/intel' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[cascardo/linux.git] / sound / soc / intel / skylake / skl-topology.c
index ad4d0f8..a294fee 100644 (file)
@@ -26,6 +26,8 @@
 #include "skl-topology.h"
 #include "skl.h"
 #include "skl-tplg-interface.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
 
 #define SKL_CH_FIXUP_MASK              (1 << 0)
 #define SKL_RATE_FIXUP_MASK            (1 << 1)
@@ -52,12 +54,9 @@ static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
 
 /*
  * Each pipelines needs memory to be allocated. Check if we have free memory
- * from available pool. Then only add this to pool
- * This is freed when pipe is deleted
- * Note: DSP does actual memory management we only keep track for complete
- * pool
+ * from available pool.
  */
-static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+static bool skl_is_pipe_mem_avail(struct skl *skl,
                                struct skl_module_cfg *mconfig)
 {
        struct skl_sst *ctx = skl->skl_sst;
@@ -72,10 +71,20 @@ static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
                                "exceeds ppl memory available %d mem %d\n",
                                skl->resource.max_mem, skl->resource.mem);
                return false;
+       } else {
+               return true;
        }
+}
 
+/*
+ * Add the mem to the mem pool. This is freed when pipe is deleted.
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+static void skl_tplg_alloc_pipe_mem(struct skl *skl,
+                               struct skl_module_cfg *mconfig)
+{
        skl->resource.mem += mconfig->pipe->memory_pages;
-       return true;
 }
 
 /*
@@ -83,10 +92,10 @@ static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
  * quantified in MCPS (Million Clocks Per Second) required for module/pipe
  *
  * Each pipelines needs mcps to be allocated. Check if we have mcps for this
- * pipe. This adds the mcps to driver counter
- * This is removed on pipeline delete
+ * pipe.
  */
-static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+
+static bool skl_is_pipe_mcps_avail(struct skl *skl,
                                struct skl_module_cfg *mconfig)
 {
        struct skl_sst *ctx = skl->skl_sst;
@@ -96,13 +105,18 @@ static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
                        "%s: module_id %d instance %d\n", __func__,
                        mconfig->id.module_id, mconfig->id.instance_id);
                dev_err(ctx->dev,
-                       "exceeds ppl memory available %d > mem %d\n",
+                       "exceeds ppl mcps available %d > mem %d\n",
                        skl->resource.max_mcps, skl->resource.mcps);
                return false;
+       } else {
+               return true;
        }
+}
 
+static void skl_tplg_alloc_pipe_mcps(struct skl *skl,
+                               struct skl_module_cfg *mconfig)
+{
        skl->resource.mcps += mconfig->mcps;
-       return true;
 }
 
 /*
@@ -129,17 +143,15 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
 {
        dev_dbg(ctx->dev, "Dumping config\n");
        dev_dbg(ctx->dev, "Input Format:\n");
-       dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
-       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
-       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
-       dev_dbg(ctx->dev, "valid bit depth = %d\n",
-                       mcfg->in_fmt.valid_bit_depth);
+       dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt[0].channels);
+       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt[0].s_freq);
+       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt[0].ch_cfg);
+       dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt[0].valid_bit_depth);
        dev_dbg(ctx->dev, "Output Format:\n");
-       dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
-       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
-       dev_dbg(ctx->dev, "valid bit depth = %d\n",
-                       mcfg->out_fmt.valid_bit_depth);
-       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
+       dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt[0].channels);
+       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt[0].s_freq);
+       dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt[0].valid_bit_depth);
+       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
 }
 
 static void skl_tplg_update_params(struct skl_module_fmt *fmt,
@@ -149,8 +161,24 @@ static void skl_tplg_update_params(struct skl_module_fmt *fmt,
                fmt->s_freq = params->s_freq;
        if (fixup & SKL_CH_FIXUP_MASK)
                fmt->channels = params->ch;
-       if (fixup & SKL_FMT_FIXUP_MASK)
-               fmt->valid_bit_depth = params->s_fmt;
+       if (fixup & SKL_FMT_FIXUP_MASK) {
+               fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
+
+               /*
+                * 16 bit is 16 bit container whereas 24 bit is in 32 bit
+                * container so update bit depth accordingly
+                */
+               switch (fmt->valid_bit_depth) {
+               case SKL_DEPTH_16BIT:
+                       fmt->bit_depth = fmt->valid_bit_depth;
+                       break;
+
+               default:
+                       fmt->bit_depth = SKL_DEPTH_32BIT;
+                       break;
+               }
+       }
+
 }
 
 /*
@@ -171,8 +199,9 @@ static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
        int in_fixup, out_fixup;
        struct skl_module_fmt *in_fmt, *out_fmt;
 
-       in_fmt = &m_cfg->in_fmt;
-       out_fmt = &m_cfg->out_fmt;
+       /* Fixups will be applied to pin 0 only */
+       in_fmt = &m_cfg->in_fmt[0];
+       out_fmt = &m_cfg->out_fmt[0];
 
        if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (is_fe) {
@@ -209,18 +238,25 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
                                struct skl_module_cfg *mcfg)
 {
        int multiplier = 1;
+       struct skl_module_fmt *in_fmt, *out_fmt;
+
+
+       /* Since fixups is applied to pin 0 only, ibs, obs needs
+        * change for pin 0 only
+        */
+       in_fmt = &mcfg->in_fmt[0];
+       out_fmt = &mcfg->out_fmt[0];
 
        if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
                multiplier = 5;
-
-       mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
-                               (mcfg->in_fmt.channels) *
-                               (mcfg->in_fmt.bit_depth >> 3) *
+       mcfg->ibs = (in_fmt->s_freq / 1000) *
+                               (mcfg->in_fmt->channels) *
+                               (mcfg->in_fmt->bit_depth >> 3) *
                                multiplier;
 
-       mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
-                               (mcfg->out_fmt.channels) *
-                               (mcfg->out_fmt.bit_depth >> 3) *
+       mcfg->obs = (mcfg->out_fmt->s_freq / 1000) *
+                               (mcfg->out_fmt->channels) *
+                               (mcfg->out_fmt->bit_depth >> 3) *
                                multiplier;
 }
 
@@ -291,6 +327,83 @@ static int skl_tplg_alloc_pipe_widget(struct device *dev,
        return 0;
 }
 
+/*
+ * some modules can have multiple params set from user control and
+ * need to be set after module is initialized. If set_param flag is
+ * set module params will be done after module is initialised.
+ */
+static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
+                                               struct skl_sst *ctx)
+{
+       int i, ret;
+       struct skl_module_cfg *mconfig = w->priv;
+       const struct snd_kcontrol_new *k;
+       struct soc_bytes_ext *sb;
+       struct skl_algo_data *bc;
+       struct skl_specific_cfg *sp_cfg;
+
+       if (mconfig->formats_config.caps_size > 0 &&
+               mconfig->formats_config.set_params == SKL_PARAM_SET) {
+               sp_cfg = &mconfig->formats_config;
+               ret = skl_set_module_params(ctx, sp_cfg->caps,
+                                       sp_cfg->caps_size,
+                                       sp_cfg->param_id, mconfig);
+               if (ret < 0)
+                       return ret;
+       }
+
+       for (i = 0; i < w->num_kcontrols; i++) {
+               k = &w->kcontrol_news[i];
+               if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+                       sb = (void *) k->private_value;
+                       bc = (struct skl_algo_data *)sb->dobj.private;
+
+                       if (bc->set_params == SKL_PARAM_SET) {
+                               ret = skl_set_module_params(ctx,
+                                               (u32 *)bc->params, bc->max,
+                                               bc->param_id, mconfig);
+                               if (ret < 0)
+                                       return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * some module param can set from user control and this is required as
+ * when module is initailzed. if module param is required in init it is
+ * identifed by set_param flag. if set_param flag is not set, then this
+ * parameter needs to set as part of module init.
+ */
+static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
+{
+       const struct snd_kcontrol_new *k;
+       struct soc_bytes_ext *sb;
+       struct skl_algo_data *bc;
+       struct skl_module_cfg *mconfig = w->priv;
+       int i;
+
+       for (i = 0; i < w->num_kcontrols; i++) {
+               k = &w->kcontrol_news[i];
+               if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+                       sb = (struct soc_bytes_ext *)k->private_value;
+                       bc = (struct skl_algo_data *)sb->dobj.private;
+
+                       if (bc->set_params != SKL_PARAM_INIT)
+                               continue;
+
+                       mconfig->formats_config.caps = (u32 *)&bc->params;
+                       mconfig->formats_config.caps_size = bc->max;
+
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * Inside a pipe instance, we can have various modules. These modules need
  * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
@@ -310,22 +423,54 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                mconfig = w->priv;
 
                /* check resource available */
-               if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+               if (!skl_is_pipe_mcps_avail(skl, mconfig))
                        return -ENOMEM;
 
+               if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
+                       ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
+                               mconfig->id.module_id, mconfig->guid);
+                       if (ret < 0)
+                               return ret;
+               }
+
                /*
                 * apply fix/conversion to module params based on
                 * FE/BE params
                 */
                skl_tplg_update_module_params(w, ctx);
-               ret = skl_init_module(ctx, mconfig, NULL);
+
+               skl_tplg_set_module_init_data(w);
+               ret = skl_init_module(ctx, mconfig);
+               if (ret < 0)
+                       return ret;
+
+               ret = skl_tplg_set_module_params(w, ctx);
                if (ret < 0)
                        return ret;
+               skl_tplg_alloc_pipe_mcps(skl, mconfig);
        }
 
        return 0;
 }
 
+static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
+        struct skl_pipe *pipe)
+{
+       struct skl_pipe_module *w_module = NULL;
+       struct skl_module_cfg *mconfig = NULL;
+
+       list_for_each_entry(w_module, &pipe->w_list, node) {
+               mconfig  = w_module->w->priv;
+
+               if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod)
+                       return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
+                                               mconfig->id.module_id);
+       }
+
+       /* no modules to unload in this path, so return */
+       return 0;
+}
+
 /*
  * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
  * need create the pipeline. So we do following:
@@ -345,10 +490,10 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
        struct skl_sst *ctx = skl->skl_sst;
 
        /* check resource available */
-       if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+       if (!skl_is_pipe_mcps_avail(skl, mconfig))
                return -EBUSY;
 
-       if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+       if (!skl_is_pipe_mem_avail(skl, mconfig))
                return -ENOMEM;
 
        /*
@@ -394,44 +539,35 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
                src_module = dst_module;
        }
 
+       skl_tplg_alloc_pipe_mem(skl, mconfig);
+       skl_tplg_alloc_pipe_mcps(skl, mconfig);
+
        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)
+static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
+                               struct skl *skl,
+                               struct snd_soc_dapm_widget *src_w,
+                               struct skl_module_cfg *src_mconfig)
 {
        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 snd_soc_dapm_widget *sink = NULL, *next_sink = NULL;
+       struct skl_module_cfg *sink_mconfig;
        struct skl_sst *ctx = skl->skl_sst;
-       int ret = 0;
-
-       source = w;
-       src_mconfig = source->priv;
+       int ret;
 
-       /*
-        * 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) {
+       snd_soc_dapm_widget_for_each_sink_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);
 
+               next_sink = p->sink;
+
+               if (!is_skl_dsp_widget_type(p->sink))
+                       return skl_tplg_bind_sinks(p->sink, skl, src_w, src_mconfig);
+
                /*
                 * here we will check widgets in sink pipelines, so that
                 * can be any widgets type and we are only interested if
@@ -441,7 +577,6 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
                                        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 */
@@ -451,32 +586,89 @@ static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
 
                        /* Start sinks pipe first */
                        if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
-                               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+                               if (sink_mconfig->pipe->conn_type !=
+                                                       SKL_PIPE_CONN_TYPE_FE)
+                                       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 (!sink)
+               return skl_tplg_bind_sinks(next_sink, skl, src_w, src_mconfig);
+
+       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 skl_module_cfg *src_mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int ret = 0;
+
+       src_mconfig = w->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
+        */
+       ret = skl_tplg_bind_sinks(w, skl, w, src_mconfig);
        if (ret)
                return ret;
 
+       /* Start source pipe last after starting all sinks */
+       if (src_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
+               return skl_run_pipe(ctx, src_mconfig->pipe);
+
        return 0;
 }
 
+static struct snd_soc_dapm_widget *skl_get_src_dsp_widget(
+               struct snd_soc_dapm_widget *w, struct skl *skl)
+{
+       struct snd_soc_dapm_path *p;
+       struct snd_soc_dapm_widget *src_w = NULL;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       snd_soc_dapm_widget_for_each_source_path(w, p) {
+               src_w = p->source;
+               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)) {
+                       return p->source;
+               }
+       }
+
+       if (src_w != NULL)
+               return skl_get_src_dsp_widget(src_w, skl);
+
+       return NULL;
+}
+
 /*
  * in the Post-PMU event of mixer we need to do following:
  *   - Check if this pipe is running
@@ -490,7 +682,6 @@ 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;
@@ -504,32 +695,18 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
         * 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);
+       source = skl_get_src_dsp_widget(w, skl);
+       if (source != NULL) {
+               src_mconfig = source->priv;
+               sink_mconfig = sink->priv;
+               src_pipe_started = 1;
 
                /*
-                * 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
+                * check pipe state, then no need to bind or start the
+                * pipe
                 */
-               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_mconfig->pipe->state != SKL_PIPE_STARTED)
+                       src_pipe_started = 0;
        }
 
        if (src_pipe_started) {
@@ -537,7 +714,8 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
                if (ret)
                        return ret;
 
-               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+               if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
+                       ret = skl_run_pipe(ctx, sink_mconfig->pipe);
        }
 
        return ret;
@@ -552,52 +730,35 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
 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;
+       int ret = 0, i;
        struct skl_sst *ctx = skl->skl_sst;
 
-       sink = w;
-       sink_mconfig = sink->priv;
+       sink_mconfig = w->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;
+       for (i = 0; i < sink_mconfig->max_in_queue; i++) {
+               if (sink_mconfig->m_in_pin[i].pin_state == SKL_PIN_BIND_DONE) {
+                       src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg;
+                       if (!src_mconfig)
+                               continue;
+                       /*
+                        * 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.
+                        */
+                       ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+                       if (ret < 0)
+                               return ret;
 
-               ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+                       ret = skl_unbind_modules(ctx,
+                                               src_mconfig, sink_mconfig);
+               }
        }
 
        return ret;
@@ -622,26 +783,24 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
        int ret = 0;
 
        skl_tplg_free_pipe_mcps(skl, mconfig);
+       skl_tplg_free_pipe_mem(skl, mconfig);
 
        list_for_each_entry(w_module, &s_pipe->w_list, node) {
                dst_module = w_module->w->priv;
 
+               skl_tplg_free_pipe_mcps(skl, dst_module);
                if (src_module == NULL) {
                        src_module = dst_module;
                        continue;
                }
 
-               ret = skl_unbind_modules(ctx, src_module, dst_module);
-               if (ret < 0)
-                       return ret;
-
+               skl_unbind_modules(ctx, src_module, dst_module);
                src_module = dst_module;
        }
 
        ret = skl_delete_pipe(ctx, mconfig->pipe);
-       skl_tplg_free_pipe_mem(skl, mconfig);
 
-       return ret;
+       return skl_tplg_unload_pipe_modules(ctx, s_pipe);
 }
 
 /*
@@ -653,47 +812,31 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
 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;
+       int ret = 0, i;
        struct skl_sst *ctx = skl->skl_sst;
 
-       source = w;
-       src_mconfig = source->priv;
+       src_mconfig = w->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;
+       for (i = 0; i < src_mconfig->max_out_queue; i++) {
+               if (src_mconfig->m_out_pin[i].pin_state == SKL_PIN_BIND_DONE) {
+                       sink_mconfig = src_mconfig->m_out_pin[i].tgt_mcfg;
+                       if (!sink_mconfig)
+                               continue;
+                       /*
+                        * This is a connecter and if path is found that means
+                        * unbind between source and sink has not happened yet
+                        */
+                       ret = skl_unbind_modules(ctx, src_mconfig,
+                                                       sink_mconfig);
                }
        }
 
-       /*
-        * 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;
 }
 
@@ -714,6 +857,12 @@ static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
        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);
        }
@@ -774,6 +923,74 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
+                       unsigned int __user *data, unsigned int size)
+{
+       struct soc_bytes_ext *sb =
+                       (struct soc_bytes_ext *)kcontrol->private_value;
+       struct skl_algo_data *bc = (struct skl_algo_data *)sb->dobj.private;
+       struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+       struct skl_module_cfg *mconfig = w->priv;
+       struct skl *skl = get_skl_ctx(w->dapm->dev);
+
+       if (w->power)
+               skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
+                                     bc->max, bc->param_id, mconfig);
+
+       /* decrement size for TLV header */
+       size -= 2 * sizeof(u32);
+
+       /* check size as we don't want to send kernel data */
+       if (size > bc->max)
+               size = bc->max;
+
+       if (bc->params) {
+               if (copy_to_user(data, &bc->param_id, sizeof(u32)))
+                       return -EFAULT;
+               if (copy_to_user(data + 1, &size, sizeof(u32)))
+                       return -EFAULT;
+               if (copy_to_user(data + 2, bc->params, size))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+#define SKL_PARAM_VENDOR_ID 0xff
+
+static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
+                       const unsigned int __user *data, unsigned int size)
+{
+       struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+       struct skl_module_cfg *mconfig = w->priv;
+       struct soc_bytes_ext *sb =
+                       (struct soc_bytes_ext *)kcontrol->private_value;
+       struct skl_algo_data *ac = (struct skl_algo_data *)sb->dobj.private;
+       struct skl *skl = get_skl_ctx(w->dapm->dev);
+
+       if (ac->params) {
+               /*
+                * if the param_is is of type Vendor, firmware expects actual
+                * parameter id and size from the control.
+                */
+               if (ac->param_id == SKL_PARAM_VENDOR_ID) {
+                       if (copy_from_user(ac->params, data, size))
+                               return -EFAULT;
+               } else {
+                       if (copy_from_user(ac->params,
+                                          data + 2 * sizeof(u32), size))
+                               return -EFAULT;
+               }
+
+               if (w->power)
+                       return skl_set_module_params(skl->skl_sst,
+                                               (u32 *)ac->params, ac->max,
+                                               ac->param_id, mconfig);
+       }
+
+       return 0;
+}
+
 /*
  * The FE params are passed by hw_params of the DAI.
  * On hw_params, the params are stored in Gateway module of the FE and we
@@ -790,9 +1007,9 @@ int skl_tplg_update_pipe_params(struct device *dev,
        memcpy(pipe->p_params, params, sizeof(*params));
 
        if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               format = &mconfig->in_fmt;
+               format = &mconfig->in_fmt[0];
        else
-               format = &mconfig->out_fmt;
+               format = &mconfig->out_fmt[0];
 
        /* set the hw_params */
        format->s_freq = params->s_freq;
@@ -809,6 +1026,7 @@ int skl_tplg_update_pipe_params(struct device *dev,
                break;
 
        case SKL_DEPTH_24BIT:
+       case SKL_DEPTH_32BIT:
                format->bit_depth = SKL_DEPTH_32BIT;
                break;
 
@@ -846,7 +1064,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
                w = dai->playback_widget;
                snd_soc_dapm_widget_for_each_sink_path(w, p) {
                        if (p->connect && p->sink->power &&
-                                       is_skl_dsp_widget_type(p->sink))
+                                       !is_skl_dsp_widget_type(p->sink))
                                continue;
 
                        if (p->sink->priv) {
@@ -859,7 +1077,7 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
                w = dai->capture_widget;
                snd_soc_dapm_widget_for_each_source_path(w, p) {
                        if (p->connect && p->source->power &&
-                                       is_skl_dsp_widget_type(p->source))
+                                       !is_skl_dsp_widget_type(p->source))
                                continue;
 
                        if (p->source->priv) {
@@ -920,6 +1138,9 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
 
        memcpy(pipe->p_params, params, sizeof(*params));
 
+       if (link_type == NHLT_LINK_HDA)
+               return 0;
+
        /* update the blob based on virtual bus_id*/
        cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
                                        params->s_fmt, params->ch,
@@ -950,18 +1171,13 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
                if (p->connect && is_skl_dsp_widget_type(p->source) &&
                                                p->source->priv) {
 
-                       if (!p->source->power) {
-                               ret = skl_tplg_be_fill_pipe_params(
-                                               dai, p->source->priv,
-                                               params);
-                               if (ret < 0)
-                                       return ret;
-                       } else {
-                               return -EBUSY;
-                       }
+                       ret = skl_tplg_be_fill_pipe_params(dai,
+                                               p->source->priv, params);
+                       if (ret < 0)
+                               return ret;
                } else {
-                       ret = skl_tplg_be_set_src_pipe_params(
-                                               dai, p->source, params);
+                       ret = skl_tplg_be_set_src_pipe_params(dai,
+                                               p->source, params);
                        if (ret < 0)
                                return ret;
                }
@@ -980,15 +1196,10 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
                if (p->connect && is_skl_dsp_widget_type(p->sink) &&
                                                p->sink->priv) {
 
-                       if (!p->sink->power) {
-                               ret = skl_tplg_be_fill_pipe_params(
-                                               dai, p->sink->priv, params);
-                               if (ret < 0)
-                                       return ret;
-                       } else {
-                               return -EBUSY;
-                       }
-
+                       ret = skl_tplg_be_fill_pipe_params(dai,
+                                               p->sink->priv, params);
+                       if (ret < 0)
+                               return ret;
                } else {
                        ret = skl_tplg_be_set_sink_pipe_params(
                                                dai, p->sink, params);
@@ -1030,6 +1241,11 @@ static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
        {SKL_PGA_EVENT, skl_tplg_pga_event},
 };
 
+static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
+       {SKL_CONTROL_TYPE_BYTE_TLV, skl_tplg_tlv_control_get,
+                                       skl_tplg_tlv_control_set},
+};
+
 /*
  * The topology binary passes the pin info for a module so initialize the pin
  * info passed into module instance
@@ -1045,6 +1261,7 @@ static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
                m_pin[i].id.instance_id = dfw_pin[i].instance_id;
                m_pin[i].in_use = false;
                m_pin[i].is_dynamic = is_dynamic;
+               m_pin[i].pin_state = SKL_PIN_UNBIND;
        }
 }
 
@@ -1092,6 +1309,24 @@ static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
        return ppl->pipe;
 }
 
+static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
+                               struct skl_dfw_module_fmt *src_fmt,
+                               int pins)
+{
+       int i;
+
+       for (i = 0; i < pins; i++) {
+               dst_fmt[i].channels  = src_fmt[i].channels;
+               dst_fmt[i].s_freq = src_fmt[i].freq;
+               dst_fmt[i].bit_depth = src_fmt[i].bit_depth;
+               dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth;
+               dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg;
+               dst_fmt[i].ch_map = src_fmt[i].ch_map;
+               dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style;
+               dst_fmt[i].sample_type = src_fmt[i].sample_type;
+       }
+}
+
 /*
  * Topology core widget load callback
  *
@@ -1130,22 +1365,16 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
        mconfig->max_in_queue = dfw_config->max_in_queue;
        mconfig->max_out_queue = dfw_config->max_out_queue;
        mconfig->is_loadable = dfw_config->is_loadable;
-       mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
-       mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
-       mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
-       mconfig->in_fmt.valid_bit_depth =
-                               dfw_config->in_fmt.valid_bit_depth;
-       mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
-       mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
-       mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
-       mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
-       mconfig->out_fmt.valid_bit_depth =
-                               dfw_config->out_fmt.valid_bit_depth;
-       mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
+       skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt,
+                                               MODULE_MAX_IN_PINS);
+       skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt,
+                                               MODULE_MAX_OUT_PINS);
+
        mconfig->params_fixup = dfw_config->params_fixup;
        mconfig->converter = dfw_config->converter;
        mconfig->m_type = dfw_config->module_type;
        mconfig->vbus_id = dfw_config->vbus_id;
+       mconfig->mem_pages = dfw_config->mem_pages;
 
        pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
        if (pipe)
@@ -1156,10 +1385,13 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
        mconfig->time_slot = dfw_config->time_slot;
        mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
 
-       mconfig->m_in_pin = devm_kzalloc(bus->dev,
-                               (mconfig->max_in_queue) *
-                                       sizeof(*mconfig->m_in_pin),
-                               GFP_KERNEL);
+       if (dfw_config->is_loadable)
+               memcpy(mconfig->guid, dfw_config->uuid,
+                                       ARRAY_SIZE(dfw_config->uuid));
+
+       mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
+                                               sizeof(*mconfig->m_in_pin),
+                                               GFP_KERNEL);
        if (!mconfig->m_in_pin)
                return -ENOMEM;
 
@@ -1188,7 +1420,9 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
                return -ENOMEM;
 
        memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
-                                        dfw_config->caps.caps_size);
+                                                dfw_config->caps.caps_size);
+       mconfig->formats_config.param_id = dfw_config->caps.param_id;
+       mconfig->formats_config.set_params = dfw_config->caps.set_params;
 
 bind_event:
        if (tplg_w->event_type == 0) {
@@ -1209,8 +1443,70 @@ bind_event:
        return 0;
 }
 
+static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
+                                       struct snd_soc_tplg_bytes_control *bc)
+{
+       struct skl_algo_data *ac;
+       struct skl_dfw_algo_data *dfw_ac =
+                               (struct skl_dfw_algo_data *)bc->priv.data;
+
+       ac = devm_kzalloc(dev, sizeof(*ac), GFP_KERNEL);
+       if (!ac)
+               return -ENOMEM;
+
+       /* Fill private data */
+       ac->max = dfw_ac->max;
+       ac->param_id = dfw_ac->param_id;
+       ac->set_params = dfw_ac->set_params;
+
+       if (ac->max) {
+               ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
+               if (!ac->params)
+                       return -ENOMEM;
+
+               if (dfw_ac->params)
+                       memcpy(ac->params, dfw_ac->params, ac->max);
+       }
+
+       be->dobj.private  = ac;
+       return 0;
+}
+
+static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
+                               struct snd_kcontrol_new *kctl,
+                               struct snd_soc_tplg_ctl_hdr *hdr)
+{
+       struct soc_bytes_ext *sb;
+       struct snd_soc_tplg_bytes_control *tplg_bc;
+       struct hdac_ext_bus *ebus  = snd_soc_component_get_drvdata(cmpnt);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+       switch (hdr->ops.info) {
+       case SND_SOC_TPLG_CTL_BYTES:
+               tplg_bc = container_of(hdr,
+                               struct snd_soc_tplg_bytes_control, hdr);
+               if (kctl->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+                       sb = (struct soc_bytes_ext *)kctl->private_value;
+                       if (tplg_bc->priv.size)
+                               return skl_init_algo_data(
+                                               bus->dev, sb, tplg_bc);
+               }
+               break;
+
+       default:
+               dev_warn(bus->dev, "Control load not supported %d:%d:%d\n",
+                       hdr->ops.get, hdr->ops.put, hdr->ops.info);
+               break;
+       }
+
+       return 0;
+}
+
 static struct snd_soc_tplg_ops skl_tplg_ops  = {
        .widget_load = skl_tplg_widget_load,
+       .control_load = skl_tplg_control_load,
+       .bytes_ext_ops = skl_tlv_ops,
+       .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
 };
 
 /* This will be read from topology manifest, currently defined here */
@@ -1242,6 +1538,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
                                        &skl_tplg_ops, fw, 0);
        if (ret < 0) {
                dev_err(bus->dev, "tplg component load failed%d\n", ret);
+               release_firmware(fw);
                return -EINVAL;
        }