aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2021-03-16 15:42:42 +0000
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2021-03-16 16:00:31 +0000
commit1c165473dc3972bfeaffd9e33bab65303a229dcc (patch)
tree1cc1930fce54f5fbaac837f0a670d7f88eb60ad0
parent4a38c0c44468469cb24542515d36a7ee1efd2960 (diff)
ASoC: qcom: audio-reach: add toplogy support
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-rw-r--r--sound/soc/qcom/Kconfig1
-rw-r--r--sound/soc/qcom/audio-reach/Makefile2
-rw-r--r--sound/soc/qcom/audio-reach/topology.c545
-rw-r--r--sound/soc/qcom/audio-reach/topology.h7
4 files changed, 554 insertions, 1 deletions
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 0c325f37ed89e..d24857f08257e 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -39,6 +39,7 @@ config SND_SOC_LPASS_SC7180
config SND_SOC_AUDIO_REACH_CARD
tristate "ASoC support for AUDIO REACH CARD"
depends on SND_SOC_AUDIO_REACH && HAS_DMA
+ select SND_SOC_TOPOLOGY
config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards"
diff --git a/sound/soc/qcom/audio-reach/Makefile b/sound/soc/qcom/audio-reach/Makefile
index abfb132e8344c..a482b93730be8 100644
--- a/sound/soc/qcom/audio-reach/Makefile
+++ b/sound/soc/qcom/audio-reach/Makefile
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += q6apm.o q6prm.o q6apm-static-graphs.o util.o module.o q6apm-dai.o q6prm-clocks.o debug.o
+obj-y += q6apm.o q6prm.o q6apm-static-graphs.o util.o module.o q6apm-dai.o q6prm-clocks.o debug.o topology.o
diff --git a/sound/soc/qcom/audio-reach/topology.c b/sound/soc/qcom/audio-reach/topology.c
new file mode 100644
index 0000000000000..9f34f06ee7708
--- /dev/null
+++ b/sound/soc/qcom/audio-reach/topology.c
@@ -0,0 +1,545 @@
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asound.h>
+#include <linux/firmware.h>
+#include <sound/soc-topology.h>
+#include <uapi/sound/snd_ar_tokens.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include "q6apm.h"
+#include "graph.h"
+#include "common.h"
+#include "module.h"
+#include "util.h"
+#include "debug.h"
+
+static struct audio_reach_graph_info *audio_reach_tplg_alloc_graph_info(struct q6apm *apm,
+ uint32_t graph_id,
+ bool *found)
+{
+ struct audio_reach_graph_info *info;
+
+ spin_lock(&apm->graph_lock);
+ info = idr_find(&apm->graph_info_idr, graph_id);
+ spin_unlock(&apm->graph_lock);
+
+ if (info) {
+ *found = true;
+ return info;
+ }
+
+ *found = false;
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock(&apm->graph_lock);
+ idr_alloc(&apm->graph_info_idr, info, graph_id,
+ graph_id + 1, GFP_ATOMIC);
+ spin_unlock(&apm->graph_lock);
+
+ return info;
+}
+
+static struct audio_reach_sub_graph *audio_reach_tplg_alloc_sub_graph(
+ struct q6apm *apm,
+ struct audio_reach_graph_info *info,
+ uint32_t sub_graph_id,
+ bool *found)
+{
+ struct audio_reach_sub_graph *sg = NULL;
+ int i;
+
+ /* Find if there is already a matching sub-graph */
+ spin_lock(&apm->graph_lock);
+ sg = idr_find(&apm->sub_graphs_idr, sub_graph_id);
+ spin_unlock(&apm->graph_lock);
+
+
+ if (sg) {
+ *found = true;
+ return sg;
+ }
+
+ *found = false;
+
+ for (i = 0; i < info->num_sub_graphs; i++) {
+ if (!info->sub_graphs[i].sub_graph_id) {
+ sg = &info->sub_graphs[i];
+ break;
+ }
+ }
+
+ if (!sg)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock(&apm->graph_lock);
+ idr_alloc(&apm->sub_graphs_idr, sg, sub_graph_id,
+ sub_graph_id + 1, GFP_ATOMIC);
+ spin_unlock(&apm->graph_lock);
+
+ return sg;
+}
+
+static struct audio_reach_container *audio_reach_tplg_alloc_container(struct q6apm *apm,
+ struct audio_reach_sub_graph *sg,
+ uint32_t container_id,
+ bool *found)
+{
+ struct audio_reach_container *cont = NULL;
+ int i;
+
+ spin_lock(&apm->graph_lock);
+ cont = idr_find(&apm->containers_idr, container_id);
+ spin_unlock(&apm->graph_lock);
+
+ if (cont)
+ return cont;
+
+ for (i = 0; i < sg->num_containers; i++) {
+ if (!sg->containers[i].container_id) {
+ cont = &sg->containers[i];
+ break;
+ }
+ }
+
+ if (!cont)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock(&apm->graph_lock);
+ idr_alloc(&apm->containers_idr, cont, container_id,
+ container_id + 1, GFP_ATOMIC);
+ spin_unlock(&apm->graph_lock);
+
+ return cont;
+}
+
+static struct audio_reach_module *audio_reach_tplg_alloc_module(struct q6apm *apm,
+ struct audio_reach_container *cont,
+ uint32_t module_id,
+ bool *found)
+{
+ struct audio_reach_module *mod = NULL;
+ int i;
+
+ spin_lock(&apm->graph_lock);
+ mod = idr_find(&apm->modules_idr, module_id);
+ spin_unlock(&apm->graph_lock);
+
+ if (mod)
+ return mod;
+
+ for (i = 0; i < cont->num_modules; i++) {
+ if (!cont->modules[i].instance_id) {
+ mod = &cont->modules[i];
+ break;
+ }
+ }
+
+ if (!mod)
+ return ERR_PTR(-ENOMEM);
+
+ spin_lock(&apm->graph_lock);
+ idr_alloc(&apm->modules_idr, mod, module_id,
+ module_id + 1, GFP_ATOMIC);
+ spin_unlock(&apm->graph_lock);
+
+ return mod;
+}
+
+static int audio_reach_tplg_get_module_pvt_data(struct q6apm *apm,
+ struct audio_reach_container *cont,
+ struct snd_soc_tplg_vendor_array *mod_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ struct audio_reach_module *mod;
+ int module_iid, tkn_count = 0;
+ bool found;
+
+ if (!cont->modules) {
+ cont->modules = kcalloc(cont->num_modules, sizeof(*mod),
+ GFP_KERNEL);
+ if (!cont->modules)
+ return -ENOMEM;
+ }
+
+ mod_elem = mod_array->value;
+ module_iid = mod_elem->value;
+
+ mod = audio_reach_tplg_alloc_module(apm, cont, module_iid, &found);
+ if (IS_ERR(mod)) {
+ return -ENOMEM;
+ } else if (found) {
+ return 0;
+ }
+
+ while (tkn_count <= (mod_array->num_elems - 1)) {
+ switch (mod_elem->token) {
+ case AUD_REACH_TKN_U32_MODULE_ID:
+ mod->module_id = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_MODULE_INSTANCE_ID:
+ mod->instance_id = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_MAX_IP_PORTS:
+ mod->max_ip_port = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_MAX_OP_PORTS:
+ mod->max_op_port = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_IN_PORTS:
+ mod->in_port = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_OUT_PORTS:
+ mod->out_port = mod_elem->value;
+ break;
+
+ /* Connections */
+ case AUD_REACH_TKN_U32_SRC_MODULE_INSTANCE_ID:
+ mod->src_mod_inst_id = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_SRC_MODULE_OP_PORT_ID:
+ mod->src_mod_op_port_id = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_DST_MODULE_INSTANCE_ID:
+ mod->dst_mod_inst_id = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_DST_MODULE_IN_PORT_ID:
+ mod->dst_mod_ip_port_id = mod_elem->value;
+ break;
+
+ /* Format specifics */
+ /* I2S module Specfic */
+ case AUD_REACH_TKN_U32_HW_IF_IDX:
+ mod->hw_interface_idx = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_SD_LINE_IDX:
+ mod->sd_line_idx = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_WS_SRC:
+ mod->ws_src = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_FRAME_SZ_FACTOR:
+ mod->frame_size_factor = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_FMT_DATA:
+ mod->data_format = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_HW_IF_TYPE:
+ mod->hw_interface_type = mod_elem->value;
+ break;
+
+ /* PCM module specific */
+ case AUD_REACH_TKN_U32_FMT_INTERLEAVE:
+ mod->interleave_type = mod_elem->value;
+ break;
+
+ /* GAIN Module */
+ case AUD_REACH_TKN_U16_GAIN:
+ mod->gain = mod_elem->value;
+ break;
+ /* Logging */
+ case AUD_REACH_TKN_U32_LOG_CODE:
+ mod->log_code = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_LOG_TAP_POINT_ID:
+ mod->log_tap_point_id = mod_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_LOG_MODE:
+ mod->mode = mod_elem->value;
+ break;
+
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n",
+ mod_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
+static int audio_reach_tplg_get_container_pvt_data(struct q6apm *apm,
+ struct audio_reach_sub_graph *sg,
+ struct snd_soc_tplg_vendor_array *cont_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *cont_elem;
+ struct snd_soc_tplg_vendor_array *mod_array;
+ struct audio_reach_container *cont;
+ int container_id, tkn_count = 0;
+ void *data = cont_array;
+ bool found;
+
+ if (!sg->containers) {
+ sg->containers = kcalloc(sg->num_containers, sizeof(*cont),
+ GFP_KERNEL);
+ if (!sg->containers)
+ return -ENOMEM;
+ }
+
+ cont_elem = cont_array->value;
+ container_id = cont_elem->value;
+ mod_array = (struct snd_soc_tplg_vendor_array *)(data + cont_array->size);
+
+ cont = audio_reach_tplg_alloc_container(apm, sg, container_id, &found);
+ if (IS_ERR(cont)) {
+ return -ENOMEM;
+ } else if (found) {
+ /*
+ * Already allocated so we just need to add new modules if any
+ * that are not already added
+ */
+ while (tkn_count <= (cont_array->num_elems - 1)) {
+ switch (cont_elem->token) {
+ case AUD_REACH_TKN_U32_NUM_MODULES:
+ audio_reach_tplg_get_module_pvt_data(apm, cont,
+ mod_array);
+ break;
+ default: /* ignore other tokens */
+ break;
+ }
+ tkn_count++;
+ cont_elem++;
+ }
+
+ return 0;
+ }
+
+ while (tkn_count <= (cont_array->num_elems - 1)) {
+ switch (cont_elem->token) {
+ case AUD_REACH_TKN_U32_CONAINER_ID:
+ cont->container_id = cont_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_CAPABILITY_ID:
+ cont->capability_id = cont_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_STACK_SIZE:
+ cont->stack_size = cont_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_GRAPH_POS:
+ cont->graph_pos = cont_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_PROC_DOMAIN:
+ cont->proc_domain = cont_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_NUM_MODULES:
+ cont->num_modules = cont_elem->value;
+ audio_reach_tplg_get_module_pvt_data(apm, cont,
+ mod_array);
+ break;
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n",
+ cont_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ cont_elem++;
+ }
+
+ return 0;
+}
+
+static int audio_reach_tplg_get_subgraph_pvt_data(struct q6apm *apm,
+ struct audio_reach_graph_info *info,
+ struct snd_soc_tplg_vendor_array *sg_array)
+{
+ struct snd_soc_tplg_vendor_value_elem *sg_elem;
+ struct snd_soc_tplg_vendor_array *cont_array;
+ struct audio_reach_sub_graph *sg;
+ int sub_graph_id, tkn_count = 0;
+ void *data = sg_array;
+ bool found;
+
+ if (!info->sub_graphs) {
+ info->sub_graphs = kcalloc(info->num_sub_graphs, sizeof(*sg),
+ GFP_KERNEL);
+ if (!info->sub_graphs)
+ return -ENOMEM;
+ }
+
+ sg_elem = sg_array->value;
+ sub_graph_id = sg_elem->value;
+ cont_array = (struct snd_soc_tplg_vendor_array *)(data + sg_array->size);
+
+ sg = audio_reach_tplg_alloc_sub_graph(apm, info, sub_graph_id, &found);
+ if (IS_ERR(sg)) {
+ return -ENOMEM;
+ } else if (found) {
+ /*
+ * Already allocated so we just need to add new containers if any
+ * that are not already added
+ */
+ while (tkn_count <= (sg_array->num_elems - 1)) {
+ switch (sg_elem->token) {
+ case AUD_REACH_TKN_U32_NUM_CONTAINERS:
+ audio_reach_tplg_get_container_pvt_data(apm, sg,
+ cont_array);
+ break;
+ default: /* ignore other tokens */
+ break;
+ }
+ tkn_count++;
+ sg_elem++;
+ }
+
+ return 0;
+ }
+
+
+ while (tkn_count <= (sg_array->num_elems - 1)) {
+ switch (sg_elem->token) {
+ case AUD_REACH_TKN_U32_SUB_GRAPH_ID:
+ sg->sub_graph_id = sg_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_PERF_MODE:
+ sg->perf_mode = sg_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_SG_DIRECTION:
+ sg->direction = sg_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_SCENARIO_ID:
+ sg->scenario_id = sg_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_NUM_CONTAINERS:
+ sg->num_containers = sg_elem->value;
+ audio_reach_tplg_get_container_pvt_data(apm, sg,
+ cont_array);
+ break;
+ default:
+ dev_err(apm->dev, "Not a valid token %d for graph\n",
+ sg_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ sg_elem++;
+ }
+
+ return 0;
+}
+
+static int audio_reach_tplg_get_graph_pvt_data(struct q6apm *apm,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct snd_soc_tplg_vendor_value_elem *graph_elem;
+ struct audio_reach_graph_info *info;
+ struct snd_soc_tplg_vendor_array *g_array;
+ struct snd_soc_tplg_vendor_array *sg_array;
+ int tkn_count = 0, i;
+ int graph_id;
+ bool found;
+
+ g_array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data);
+ sg_array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data +
+ g_array->size);
+
+ graph_elem = g_array->value;
+ graph_id = graph_elem->value;
+
+ info = audio_reach_tplg_alloc_graph_info(apm, graph_id, &found);
+ if (IS_ERR(info)) {
+ return -ENOMEM;
+ } else if (found) {
+ /*
+ * Already allocated so we just need to add new subgraphs if any
+ * that are not already added
+ */
+ while (tkn_count <= (g_array->num_elems - 1)) {
+ switch (graph_elem->token) {
+ case AUD_REACH_TKN_U32_NUM_SUB_GRAPHS:
+ audio_reach_tplg_get_subgraph_pvt_data(apm, info,
+ sg_array);
+ break;
+ default: /* ignore other tokens */
+ break;
+ }
+ tkn_count++;
+ graph_elem++;
+ }
+
+ return 0;
+ }
+
+
+ while (tkn_count <= (g_array->num_elems - 1)) {
+ switch (graph_elem->token) {
+ case AUD_REACH_TKN_U32_GRAPH_ID:
+ info->id = graph_elem->value;
+ break;
+ case AUD_REACH_TKN_U32_NUM_SUB_GRAPHS:
+ info->num_sub_graphs = graph_elem->value;
+ audio_reach_tplg_get_subgraph_pvt_data(apm, info,
+ sg_array);
+ break;
+ default:
+ dev_err(apm->dev, "%s Not a valid token %d for graph\n",
+ __func__, graph_elem->token);
+ break;
+
+ }
+ tkn_count++;
+ graph_elem++;
+ }
+
+ return 0;
+}
+
+static int audio_reach_tplg_widget_load(struct snd_soc_component *component,
+ int index, struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+ struct q6apm *apm = dev_get_drvdata(component->dev->parent);
+
+ audio_reach_tplg_get_graph_pvt_data(apm, tplg_w);
+
+ return 0;
+}
+
+static void audio_reach_tplg_complete(struct snd_soc_component *component)
+{
+ dev_err(component->dev, "DEBUG: %s: %d \n", __func__, __LINE__);
+}
+
+static struct snd_soc_tplg_ops audio_reach_tplg_ops = {
+ .widget_load = audio_reach_tplg_widget_load,
+// .manifest = audio_reach_manifest_load,
+// .dai_load = audio_reach_dai_load,
+ .complete = audio_reach_tplg_complete,
+};
+
+int audio_reach_tplg_init(struct snd_soc_component *component)
+{
+ struct device *dev = component->dev;
+ struct q6apm *apm = dev_get_drvdata(dev->parent);
+ const struct firmware *fw;
+ int ret;
+ struct audio_reach_graph_info *info;
+
+ ret = request_firmware(&fw, "audio-reach.bin", dev);
+ if (ret < 0) {
+ dev_err(dev, "tplg fw audio-reach.bin load failed with %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_tplg_component_load(component, &audio_reach_tplg_ops, fw);
+ if (ret < 0) {
+ dev_err(dev, "tplg component load failed%d\n", ret);
+ release_firmware(fw);
+ return -EINVAL;
+ }
+ audio_reach_dump_dot_graph_info(apm);
+#if 0
+ info = idr_find(&apm->graph_info_idr, 0x2);
+ audio_reach_dump_dot_graph_info(info);
+ info = idr_find(&apm->graph_info_idr, 0x3);
+ audio_reach_dump_dot_graph_info(info);
+#endif
+
+ return 0;
+}
diff --git a/sound/soc/qcom/audio-reach/topology.h b/sound/soc/qcom/audio-reach/topology.h
new file mode 100644
index 0000000000000..87e4ab86f29df
--- /dev/null
+++ b/sound/soc/qcom/audio-reach/topology.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __AR_TOPOLOGY__
+#define __AR_TOPOLOGY__
+
+int audio_reach_tplg_init(struct snd_soc_component *component);
+
+#endif /*__AR_TOPOLOGY__ */