|
@@ -1011,3 +1011,221 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
|
|
|
+ {SKL_MIXER_EVENT, skl_tplg_mixer_event},
|
|
|
+ {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
|
|
|
+ {SKL_PGA_EVENT, skl_tplg_pga_event},
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * The topology binary passes the pin info for a module so initialize the pin
|
|
|
+ * info passed into module instance
|
|
|
+ */
|
|
|
+static void skl_fill_module_pin_info(struct device *dev,
|
|
|
+ struct skl_module_pin *m_pin,
|
|
|
+ int max_pin)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < max_pin; i++) {
|
|
|
+ m_pin[i].id.module_id = 0;
|
|
|
+ m_pin[i].id.instance_id = 0;
|
|
|
+ m_pin[i].in_use = false;
|
|
|
+ m_pin[i].is_dynamic = true;
|
|
|
+ m_pin[i].pin_index = i;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Add pipeline from topology binary into driver pipeline list
|
|
|
+ *
|
|
|
+ * If already added we return that instance
|
|
|
+ * Otherwise we create a new instance and add into driver list
|
|
|
+ */
|
|
|
+static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
|
|
|
+ struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
|
|
|
+{
|
|
|
+ struct skl_pipeline *ppl;
|
|
|
+ struct skl_pipe *pipe;
|
|
|
+ struct skl_pipe_params *params;
|
|
|
+
|
|
|
+ list_for_each_entry(ppl, &skl->ppl_list, node) {
|
|
|
+ if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
|
|
|
+ return ppl->pipe;
|
|
|
+ }
|
|
|
+
|
|
|
+ ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
|
|
|
+ if (!ppl)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
|
|
|
+ if (!pipe)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
|
|
|
+ if (!params)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ pipe->ppl_id = dfw_pipe->pipe_id;
|
|
|
+ pipe->memory_pages = dfw_pipe->memory_pages;
|
|
|
+ pipe->pipe_priority = dfw_pipe->pipe_priority;
|
|
|
+ pipe->conn_type = dfw_pipe->conn_type;
|
|
|
+ pipe->state = SKL_PIPE_INVALID;
|
|
|
+ pipe->p_params = params;
|
|
|
+ INIT_LIST_HEAD(&pipe->w_list);
|
|
|
+
|
|
|
+ ppl->pipe = pipe;
|
|
|
+ list_add(&ppl->node, &skl->ppl_list);
|
|
|
+
|
|
|
+ return ppl->pipe;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Topology core widget load callback
|
|
|
+ *
|
|
|
+ * This is used to save the private data for each widget which gives
|
|
|
+ * information to the driver about module and pipeline parameters which DSP
|
|
|
+ * FW expects like ids, resource values, formats etc
|
|
|
+ */
|
|
|
+static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
|
|
|
+ struct snd_soc_dapm_widget *w,
|
|
|
+ struct snd_soc_tplg_dapm_widget *tplg_w)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
|
|
|
+ struct skl *skl = ebus_to_skl(ebus);
|
|
|
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
|
|
|
+ struct skl_module_cfg *mconfig;
|
|
|
+ struct skl_pipe *pipe;
|
|
|
+ struct skl_dfw_module *dfw_config = (struct skl_dfw_module *)tplg_w->priv.data;
|
|
|
+
|
|
|
+ if (!tplg_w->priv.size)
|
|
|
+ goto bind_event;
|
|
|
+
|
|
|
+ mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!mconfig)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ w->priv = mconfig;
|
|
|
+ mconfig->id.module_id = dfw_config->module_id;
|
|
|
+ mconfig->id.instance_id = dfw_config->instance_id;
|
|
|
+ mconfig->mcps = dfw_config->max_mcps;
|
|
|
+ mconfig->ibs = dfw_config->ibs;
|
|
|
+ mconfig->obs = dfw_config->obs;
|
|
|
+ mconfig->core_id = dfw_config->core_id;
|
|
|
+ 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;
|
|
|
+ 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;
|
|
|
+
|
|
|
+ pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
|
|
|
+ if (pipe)
|
|
|
+ mconfig->pipe = pipe;
|
|
|
+
|
|
|
+ mconfig->dev_type = dfw_config->dev_type;
|
|
|
+ mconfig->hw_conn_type = dfw_config->hw_conn_type;
|
|
|
+ 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 (!mconfig->m_in_pin)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
|
|
|
+ sizeof(*mconfig->m_out_pin),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!mconfig->m_out_pin)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ skl_fill_module_pin_info(bus->dev, mconfig->m_in_pin,
|
|
|
+ mconfig->max_in_queue);
|
|
|
+ skl_fill_module_pin_info(bus->dev, mconfig->m_out_pin,
|
|
|
+ mconfig->max_out_queue);
|
|
|
+
|
|
|
+ if (mconfig->formats_config.caps_size == 0)
|
|
|
+ goto bind_event;
|
|
|
+
|
|
|
+ mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
|
|
|
+ mconfig->formats_config.caps_size, GFP_KERNEL);
|
|
|
+
|
|
|
+ if (mconfig->formats_config.caps == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
|
|
|
+ dfw_config->caps.caps_size);
|
|
|
+
|
|
|
+bind_event:
|
|
|
+ if (tplg_w->event_type == 0) {
|
|
|
+ dev_info(bus->dev, "ASoC: No event handler required\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
|
|
|
+ ARRAY_SIZE(skl_tplg_widget_ops), tplg_w->event_type);
|
|
|
+
|
|
|
+ if (ret) {
|
|
|
+ dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
|
|
|
+ __func__, tplg_w->event_type);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snd_soc_tplg_ops skl_tplg_ops = {
|
|
|
+ .widget_load = skl_tplg_widget_load,
|
|
|
+};
|
|
|
+
|
|
|
+/* This will be read from topology manifest, currently defined here */
|
|
|
+#define SKL_MAX_MCPS 30000000
|
|
|
+#define SKL_FW_MAX_MEM 1000000
|
|
|
+
|
|
|
+/*
|
|
|
+ * SKL topology init routine
|
|
|
+ */
|
|
|
+int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ const struct firmware *fw;
|
|
|
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
|
|
|
+ struct skl *skl = ebus_to_skl(ebus);
|
|
|
+
|
|
|
+ ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "config firmware %s request failed with %d\n",
|
|
|
+ "dfw_sst.bin", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The complete tplg for SKL is loaded as index 0, we don't use
|
|
|
+ * any other index
|
|
|
+ */
|
|
|
+ ret = snd_soc_tplg_component_load(&platform->component, &skl_tplg_ops, fw, 0);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(bus->dev, "tplg component load failed%d\n", ret);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ skl->resource.max_mcps = SKL_MAX_MCPS;
|
|
|
+ skl->resource.max_mem = SKL_FW_MAX_MEM;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|