diff options
author | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2021-03-16 15:42:42 +0000 |
---|---|---|
committer | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2021-03-16 16:00:31 +0000 |
commit | 1c165473dc3972bfeaffd9e33bab65303a229dcc (patch) | |
tree | 1cc1930fce54f5fbaac837f0a670d7f88eb60ad0 | |
parent | 4a38c0c44468469cb24542515d36a7ee1efd2960 (diff) |
ASoC: qcom: audio-reach: add toplogy support
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-rw-r--r-- | sound/soc/qcom/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/qcom/audio-reach/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/qcom/audio-reach/topology.c | 545 | ||||
-rw-r--r-- | sound/soc/qcom/audio-reach/topology.h | 7 |
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__ */ |