|
@@ -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)
|
|
@@ -129,17 +131,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 +149,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 +187,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 +226,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 +315,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
|
|
@@ -313,12 +414,25 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
|
|
|
if (!skl_tplg_alloc_pipe_mcps(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;
|
|
|
}
|
|
@@ -326,6 +440,24 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
|
|
|
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:
|
|
@@ -397,41 +529,24 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
|
|
|
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 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;
|
|
|
/*
|
|
|
* here we will check widgets in sink pipelines, so that
|
|
|
* can be any widgets type and we are only interested if
|
|
@@ -441,7 +556,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 +565,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_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, 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 +661,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 +674,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 +693,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 +709,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,10 +762,12 @@ 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;
|
|
@@ -639,9 +781,8 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
|
|
|
}
|
|
|
|
|
|
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 +794,34 @@ 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_stop_pipe(ctx, sink_mconfig->pipe);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
@@ -774,6 +902,67 @@ 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);
|
|
|
+
|
|
|
+ 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 +979,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 +998,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 +1036,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 +1049,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 +1110,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 +1143,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 +1168,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 +1213,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 +1233,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 +1281,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 +1337,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 +1357,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 +1392,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 +1415,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 */
|