ASoC: Intel: Skylake: Add support for deferred DSP module bind
authorJeeja KP <jeeja.kp@intel.com>
Fri, 24 Mar 2017 17:40:34 +0000 (23:10 +0530)
committerMark Brown <broonie@kernel.org>
Wed, 12 Apr 2017 15:57:52 +0000 (16:57 +0100)
Module at the end of DSP pipeline that needs to be connected to a module
in another pipeline are represented as a PGA(leaf node) and in PGA event
handler these modules are bound/unbounded. Modules other than PGA leaf
can be connected directly or via switch to a module in another pipeline.
Example: reference path.

To support the deferred DSP module bind, following changes are done:
o When the path is enabled, the destination module that needs to be
bound may not be initialized. If the module is not initialized, add
these modules in a deferred bind list.
o When the destination module is initialized, check for these modules
in deferred bind list. If found, bind them.
o When the destination module is deleted, Unbind the modules.
o When the source module is deleted, remove the entry from the deferred
bind list.

Signed-off-by: Jeeja KP <jeeja.kp@intel.com>
Acked-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl.h

index 1823197c15c88e9610ae321b05b0af0809a3676c..600faad19bd4f9e98ef9e13e9c62af08978dbc32 100644 (file)
@@ -1300,6 +1300,7 @@ int skl_platform_register(struct device *dev)
        struct skl *skl = ebus_to_skl(ebus);
 
        INIT_LIST_HEAD(&skl->ppl_list);
+       INIT_LIST_HEAD(&skl->bind_list);
 
        ret = snd_soc_register_platform(dev, &skl_platform_drv);
        if (ret) {
@@ -1320,6 +1321,17 @@ int skl_platform_register(struct device *dev)
 
 int skl_platform_unregister(struct device *dev)
 {
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct skl *skl = ebus_to_skl(ebus);
+       struct skl_module_deferred_bind *modules;
+
+       if (!list_empty(&skl->bind_list)) {
+               list_for_each_entry(modules, &skl->bind_list, node) {
+                       list_del(&modules->node);
+                       kfree(modules);
+               }
+       }
+
        snd_soc_unregister_component(dev);
        snd_soc_unregister_platform(dev);
        return 0;
index e960d9f761b9354edf9af39f7d14b667e3c1d8b5..7f285176a0744301c2e76af9180df036c7bed101 100644 (file)
@@ -638,8 +638,9 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
        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_module_cfg *src_module = NULL, *dst_module, *module;
        struct skl_sst *ctx = skl->skl_sst;
+       struct skl_module_deferred_bind *modules;
 
        /* check resource available */
        if (!skl_is_pipe_mcps_avail(skl, mconfig))
@@ -680,6 +681,22 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
                src_module = dst_module;
        }
 
+       /*
+        * When the destination module is initialized, check for these modules
+        * in deferred bind list. If found, bind them.
+        */
+       list_for_each_entry(w_module, &s_pipe->w_list, node) {
+               if (list_empty(&skl->bind_list))
+                       break;
+
+               list_for_each_entry(modules, &skl->bind_list, node) {
+                       module = w_module->w->priv;
+                       if (modules->dst == module)
+                               skl_bind_modules(ctx, modules->src,
+                                                       modules->dst);
+               }
+       }
+
        return 0;
 }
 
@@ -776,6 +793,44 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+
+static int skl_tplg_module_add_deferred_bind(struct skl *skl,
+       struct skl_module_cfg *src, struct skl_module_cfg *dst)
+{
+       struct skl_module_deferred_bind *m_list, *modules;
+       int i;
+
+       /* only supported for module with static pin connection */
+       for (i = 0; i < dst->max_in_queue; i++) {
+               struct skl_module_pin *pin = &dst->m_in_pin[i];
+
+               if (pin->is_dynamic)
+                       continue;
+
+               if ((pin->id.module_id  == src->id.module_id) &&
+                       (pin->id.instance_id  == src->id.instance_id)) {
+
+                       if (!list_empty(&skl->bind_list)) {
+                               list_for_each_entry(modules, &skl->bind_list, node) {
+                                       if (modules->src == src && modules->dst == dst)
+                                               return 0;
+                               }
+                       }
+
+                       m_list = kzalloc(sizeof(*m_list), GFP_KERNEL);
+                       if (!m_list)
+                               return -ENOMEM;
+
+                       m_list->src = src;
+                       m_list->dst = dst;
+
+                       list_add(&m_list->node, &skl->bind_list);
+               }
+       }
+
+       return 0;
+}
+
 static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
                                struct skl *skl,
                                struct snd_soc_dapm_widget *src_w,
@@ -810,6 +865,28 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
                        sink = p->sink;
                        sink_mconfig = sink->priv;
 
+                       /*
+                        * Modules other than PGA leaf can be connected
+                        * directly or via switch to a module in another
+                        * pipeline. EX: reference path
+                        * when the path is enabled, the dst module that needs
+                        * to be bound may not be initialized. if the module is
+                        * not initialized, add these modules in the deferred
+                        * bind list and when the dst module is initialised,
+                        * bind this module to the dst_module in deferred list.
+                        */
+                       if (((src_mconfig->m_state == SKL_MODULE_INIT_DONE)
+                               && (sink_mconfig->m_state == SKL_MODULE_UNINIT))) {
+
+                               ret = skl_tplg_module_add_deferred_bind(skl,
+                                               src_mconfig, sink_mconfig);
+
+                               if (ret < 0)
+                                       return ret;
+
+                       }
+
+
                        if (src_mconfig->m_state == SKL_MODULE_UNINIT ||
                                sink_mconfig->m_state == SKL_MODULE_UNINIT)
                                continue;
@@ -1014,6 +1091,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
        struct skl_module_cfg *src_module = NULL, *dst_module;
        struct skl_sst *ctx = skl->skl_sst;
        struct skl_pipe *s_pipe = mconfig->pipe;
+       struct skl_module_deferred_bind *modules;
 
        if (s_pipe->state == SKL_PIPE_INVALID)
                return -EINVAL;
@@ -1021,6 +1099,35 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
        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) {
+               if (list_empty(&skl->bind_list))
+                       break;
+
+               src_module = w_module->w->priv;
+
+               list_for_each_entry(modules, &skl->bind_list, node) {
+                       /*
+                        * When the destination module is deleted, Unbind the
+                        * modules from deferred bind list.
+                        */
+                       if (modules->dst == src_module) {
+                               skl_unbind_modules(ctx, modules->src,
+                                               modules->dst);
+                       }
+
+                       /*
+                        * When the source module is deleted, remove this entry
+                        * from the deferred bind list.
+                        */
+                       if (modules->src == src_module) {
+                               list_del(&modules->node);
+                               modules->src = NULL;
+                               modules->dst = NULL;
+                               kfree(modules);
+                       }
+               }
+       }
+
        list_for_each_entry(w_module, &s_pipe->w_list, node) {
                dst_module = w_module->w->priv;
 
index 8536d03a7778b19d327fd3f4e862ad375fe3ae73..cc64d6bdb4f6c4859621619daa3a9077a96a8dc7 100644 (file)
@@ -336,6 +336,12 @@ struct skl_pipeline {
        struct list_head node;
 };
 
+struct skl_module_deferred_bind {
+       struct skl_module_cfg *src;
+       struct skl_module_cfg *dst;
+       struct list_head node;
+};
+
 static inline struct skl *get_skl_ctx(struct device *dev)
 {
        struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
index bbef77d2b917a9e080e0cd67b99fbaa796b79812..5b3fa4b916911688b06f4e5d3516709383a01bc6 100644 (file)
@@ -77,6 +77,7 @@ struct skl {
 
        struct skl_dsp_resource resource;
        struct list_head ppl_list;
+       struct list_head bind_list;
 
        const char *fw_name;
        char tplg_name[64];