1318 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2020, Linaro Limited3 4#include <linux/cleanup.h>5#include <sound/soc.h>6#include <sound/soc-dapm.h>7#include <sound/pcm.h>8#include <sound/control.h>9#include <sound/asound.h>10#include <linux/firmware.h>11#include <sound/soc-topology.h>12#include <sound/soc-dpcm.h>13#include <uapi/sound/snd_ar_tokens.h>14#include <linux/kernel.h>15#include <linux/wait.h>16#include "q6apm.h"17#include "audioreach.h"18 19struct snd_ar_control {20 u32 graph_id; /* Graph ID */21 u32 sgid; /* Sub Graph ID */22 u32 module_instance_id; /* Connected Module Instance ID */23 struct snd_soc_dapm_widget *w;24 struct list_head node;25 struct snd_soc_component *scomp;26};27 28static struct audioreach_graph_info *audioreach_tplg_alloc_graph_info(struct q6apm *apm,29 uint32_t graph_id,30 bool *found)31{32 struct audioreach_graph_info *info;33 int ret;34 35 mutex_lock(&apm->lock);36 info = idr_find(&apm->graph_info_idr, graph_id);37 mutex_unlock(&apm->lock);38 39 if (info) {40 *found = true;41 return info;42 }43 44 *found = false;45 info = kzalloc(sizeof(*info), GFP_KERNEL);46 if (!info)47 return ERR_PTR(-ENOMEM);48 49 INIT_LIST_HEAD(&info->sg_list);50 51 mutex_lock(&apm->lock);52 ret = idr_alloc_u32(&apm->graph_info_idr, info, &graph_id, graph_id, GFP_KERNEL);53 mutex_unlock(&apm->lock);54 55 if (ret < 0) {56 dev_err(apm->dev, "Failed to allocate Graph ID (%x)\n", graph_id);57 kfree(info);58 return ERR_PTR(ret);59 }60 61 info->id = graph_id;62 63 return info;64}65 66static void audioreach_tplg_add_sub_graph(struct audioreach_sub_graph *sg,67 struct audioreach_graph_info *info)68{69 list_add_tail(&sg->node, &info->sg_list);70 sg->info = info;71 info->num_sub_graphs++;72}73 74static struct audioreach_sub_graph *audioreach_tplg_alloc_sub_graph(struct q6apm *apm,75 uint32_t sub_graph_id,76 bool *found)77{78 struct audioreach_sub_graph *sg;79 int ret;80 81 if (!sub_graph_id)82 return ERR_PTR(-EINVAL);83 84 /* Find if there is already a matching sub-graph */85 mutex_lock(&apm->lock);86 sg = idr_find(&apm->sub_graphs_idr, sub_graph_id);87 mutex_unlock(&apm->lock);88 89 if (sg) {90 *found = true;91 return sg;92 }93 94 *found = false;95 sg = kzalloc(sizeof(*sg), GFP_KERNEL);96 if (!sg)97 return ERR_PTR(-ENOMEM);98 99 INIT_LIST_HEAD(&sg->container_list);100 101 mutex_lock(&apm->lock);102 ret = idr_alloc_u32(&apm->sub_graphs_idr, sg, &sub_graph_id, sub_graph_id, GFP_KERNEL);103 mutex_unlock(&apm->lock);104 105 if (ret < 0) {106 dev_err(apm->dev, "Failed to allocate Sub-Graph Instance ID (%x)\n", sub_graph_id);107 kfree(sg);108 return ERR_PTR(ret);109 }110 111 sg->sub_graph_id = sub_graph_id;112 113 return sg;114}115 116static struct audioreach_container *audioreach_tplg_alloc_container(struct q6apm *apm,117 struct audioreach_sub_graph *sg,118 uint32_t container_id,119 bool *found)120{121 struct audioreach_container *cont;122 int ret;123 124 if (!container_id)125 return ERR_PTR(-EINVAL);126 127 mutex_lock(&apm->lock);128 cont = idr_find(&apm->containers_idr, container_id);129 mutex_unlock(&apm->lock);130 131 if (cont) {132 *found = true;133 return cont;134 }135 *found = false;136 137 cont = kzalloc(sizeof(*cont), GFP_KERNEL);138 if (!cont)139 return ERR_PTR(-ENOMEM);140 141 INIT_LIST_HEAD(&cont->modules_list);142 143 mutex_lock(&apm->lock);144 ret = idr_alloc_u32(&apm->containers_idr, cont, &container_id, container_id, GFP_KERNEL);145 mutex_unlock(&apm->lock);146 147 if (ret < 0) {148 dev_err(apm->dev, "Failed to allocate Container Instance ID (%x)\n", container_id);149 kfree(cont);150 return ERR_PTR(ret);151 }152 153 cont->container_id = container_id;154 cont->sub_graph = sg;155 /* add to container list */156 list_add_tail(&cont->node, &sg->container_list);157 sg->num_containers++;158 159 return cont;160}161 162static struct audioreach_module *audioreach_tplg_alloc_module(struct q6apm *apm,163 struct audioreach_container *cont,164 struct snd_soc_dapm_widget *w,165 uint32_t module_id, bool *found)166{167 struct audioreach_module *mod;168 int ret;169 170 mutex_lock(&apm->lock);171 mod = idr_find(&apm->modules_idr, module_id);172 mutex_unlock(&apm->lock);173 174 if (mod) {175 *found = true;176 return mod;177 }178 *found = false;179 mod = kzalloc(sizeof(*mod), GFP_KERNEL);180 if (!mod)181 return ERR_PTR(-ENOMEM);182 183 mutex_lock(&apm->lock);184 if (!module_id) { /* alloc module id dynamically */185 ret = idr_alloc_cyclic(&apm->modules_idr, mod,186 AR_MODULE_DYNAMIC_INSTANCE_ID_START,187 AR_MODULE_DYNAMIC_INSTANCE_ID_END, GFP_KERNEL);188 } else {189 ret = idr_alloc_u32(&apm->modules_idr, mod, &module_id, module_id, GFP_KERNEL);190 }191 mutex_unlock(&apm->lock);192 193 if (ret < 0) {194 dev_err(apm->dev, "Failed to allocate Module Instance ID (%x)\n", module_id);195 kfree(mod);196 return ERR_PTR(ret);197 }198 199 mod->instance_id = module_id;200 /* add to module list */201 list_add_tail(&mod->node, &cont->modules_list);202 mod->container = cont;203 mod->widget = w;204 cont->num_modules++;205 206 return mod;207}208 209static struct snd_soc_tplg_vendor_array *audioreach_get_sg_array(210 struct snd_soc_tplg_private *private)211{212 struct snd_soc_tplg_vendor_array *sg_array = NULL;213 bool found = false;214 int sz;215 216 for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {217 struct snd_soc_tplg_vendor_value_elem *sg_elem;218 int tkn_count = 0;219 220 sg_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);221 sg_elem = sg_array->value;222 sz = sz + le32_to_cpu(sg_array->size);223 while (!found && tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) {224 switch (le32_to_cpu(sg_elem->token)) {225 case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:226 found = true;227 break;228 default:229 break;230 }231 tkn_count++;232 sg_elem++;233 }234 }235 236 if (found)237 return sg_array;238 239 return NULL;240}241 242static struct snd_soc_tplg_vendor_array *audioreach_get_cont_array(243 struct snd_soc_tplg_private *private)244{245 struct snd_soc_tplg_vendor_array *cont_array = NULL;246 bool found = false;247 int sz;248 249 for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {250 struct snd_soc_tplg_vendor_value_elem *cont_elem;251 int tkn_count = 0;252 253 cont_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);254 cont_elem = cont_array->value;255 sz = sz + le32_to_cpu(cont_array->size);256 while (!found && tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) {257 switch (le32_to_cpu(cont_elem->token)) {258 case AR_TKN_U32_CONTAINER_INSTANCE_ID:259 found = true;260 break;261 default:262 break;263 }264 tkn_count++;265 cont_elem++;266 }267 }268 269 if (found)270 return cont_array;271 272 return NULL;273}274 275static struct snd_soc_tplg_vendor_array *audioreach_get_module_array(276 struct snd_soc_tplg_private *private)277{278 struct snd_soc_tplg_vendor_array *mod_array = NULL;279 bool found = false;280 int sz = 0;281 282 for (sz = 0; !found && (sz < le32_to_cpu(private->size)); ) {283 struct snd_soc_tplg_vendor_value_elem *mod_elem;284 int tkn_count = 0;285 286 mod_array = (struct snd_soc_tplg_vendor_array *)((u8 *)private->array + sz);287 mod_elem = mod_array->value;288 sz = sz + le32_to_cpu(mod_array->size);289 while (!found && tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {290 switch (le32_to_cpu(mod_elem->token)) {291 case AR_TKN_U32_MODULE_INSTANCE_ID:292 found = true;293 break;294 default:295 break;296 }297 tkn_count++;298 mod_elem++;299 }300 }301 302 if (found)303 return mod_array;304 305 return NULL;306}307 308static struct audioreach_sub_graph *audioreach_parse_sg_tokens(struct q6apm *apm,309 struct snd_soc_tplg_private *private)310{311 struct snd_soc_tplg_vendor_value_elem *sg_elem;312 struct snd_soc_tplg_vendor_array *sg_array;313 struct audioreach_graph_info *info = NULL;314 int graph_id, sub_graph_id, tkn_count = 0;315 struct audioreach_sub_graph *sg;316 bool found;317 318 sg_array = audioreach_get_sg_array(private);319 sg_elem = sg_array->value;320 321 while (tkn_count <= (le32_to_cpu(sg_array->num_elems) - 1)) {322 switch (le32_to_cpu(sg_elem->token)) {323 case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:324 sub_graph_id = le32_to_cpu(sg_elem->value);325 sg = audioreach_tplg_alloc_sub_graph(apm, sub_graph_id, &found);326 if (IS_ERR(sg)) {327 return sg;328 } else if (found) {329 /* Already parsed data for this sub-graph */330 return sg;331 }332 break;333 case AR_TKN_DAI_INDEX:334 /* Sub graph is associated with predefined graph */335 graph_id = le32_to_cpu(sg_elem->value);336 info = audioreach_tplg_alloc_graph_info(apm, graph_id, &found);337 if (IS_ERR(info))338 return ERR_CAST(info);339 break;340 case AR_TKN_U32_SUB_GRAPH_PERF_MODE:341 sg->perf_mode = le32_to_cpu(sg_elem->value);342 break;343 case AR_TKN_U32_SUB_GRAPH_DIRECTION:344 sg->direction = le32_to_cpu(sg_elem->value);345 break;346 case AR_TKN_U32_SUB_GRAPH_SCENARIO_ID:347 sg->scenario_id = le32_to_cpu(sg_elem->value);348 break;349 default:350 dev_err(apm->dev, "Not a valid token %d for graph\n", sg_elem->token);351 break;352 353 }354 tkn_count++;355 sg_elem++;356 }357 358 /* Sub graph is associated with predefined graph */359 if (info)360 audioreach_tplg_add_sub_graph(sg, info);361 362 return sg;363}364 365static struct audioreach_container *audioreach_parse_cont_tokens(struct q6apm *apm,366 struct audioreach_sub_graph *sg,367 struct snd_soc_tplg_private *private)368{369 struct snd_soc_tplg_vendor_value_elem *cont_elem;370 struct snd_soc_tplg_vendor_array *cont_array;371 struct audioreach_container *cont;372 int container_id, tkn_count = 0;373 bool found = false;374 375 cont_array = audioreach_get_cont_array(private);376 cont_elem = cont_array->value;377 378 while (tkn_count <= (le32_to_cpu(cont_array->num_elems) - 1)) {379 switch (le32_to_cpu(cont_elem->token)) {380 case AR_TKN_U32_CONTAINER_INSTANCE_ID:381 container_id = le32_to_cpu(cont_elem->value);382 cont = audioreach_tplg_alloc_container(apm, sg, container_id, &found);383 if (IS_ERR(cont) || found)/* Error or Already parsed container data */384 return cont;385 break;386 case AR_TKN_U32_CONTAINER_CAPABILITY_ID:387 cont->capability_id = le32_to_cpu(cont_elem->value);388 break;389 case AR_TKN_U32_CONTAINER_STACK_SIZE:390 cont->stack_size = le32_to_cpu(cont_elem->value);391 break;392 case AR_TKN_U32_CONTAINER_GRAPH_POS:393 cont->graph_pos = le32_to_cpu(cont_elem->value);394 break;395 case AR_TKN_U32_CONTAINER_PROC_DOMAIN:396 cont->proc_domain = le32_to_cpu(cont_elem->value);397 break;398 default:399 dev_err(apm->dev, "Not a valid token %d for graph\n", cont_elem->token);400 break;401 402 }403 tkn_count++;404 cont_elem++;405 }406 407 return cont;408}409 410static struct audioreach_module *audioreach_parse_common_tokens(struct q6apm *apm,411 struct audioreach_container *cont,412 struct snd_soc_tplg_private *private,413 struct snd_soc_dapm_widget *w)414{415 uint32_t max_ip_port = 0, max_op_port = 0, in_port = 0, out_port = 0;416 uint32_t src_mod_op_port_id[AR_MAX_MOD_LINKS] = { 0, };417 uint32_t dst_mod_inst_id[AR_MAX_MOD_LINKS] = { 0, };418 uint32_t dst_mod_ip_port_id[AR_MAX_MOD_LINKS] = { 0, };419 uint32_t src_mod_inst_id = 0;420 421 int module_id = 0, instance_id = 0, tkn_count = 0;422 struct snd_soc_tplg_vendor_value_elem *mod_elem;423 struct snd_soc_tplg_vendor_array *mod_array;424 struct audioreach_module *mod = NULL;425 uint32_t token;426 bool found;427 int max_tokens;428 429 mod_array = audioreach_get_module_array(private);430 mod_elem = mod_array->value;431 max_tokens = le32_to_cpu(mod_array->num_elems);432 while (tkn_count <= (max_tokens - 1)) {433 token = le32_to_cpu(mod_elem->token);434 switch (token) {435 /* common module info */436 case AR_TKN_U32_MODULE_ID:437 module_id = le32_to_cpu(mod_elem->value);438 break;439 case AR_TKN_U32_MODULE_INSTANCE_ID:440 instance_id = le32_to_cpu(mod_elem->value);441 mod = audioreach_tplg_alloc_module(apm, cont, w,442 instance_id, &found);443 if (IS_ERR(mod)) {444 return mod;445 } else if (found) {446 dev_err(apm->dev, "Duplicate Module Instance ID 0x%08x found\n",447 instance_id);448 return ERR_PTR(-EINVAL);449 }450 451 break;452 case AR_TKN_U32_MODULE_MAX_IP_PORTS:453 max_ip_port = le32_to_cpu(mod_elem->value);454 break;455 case AR_TKN_U32_MODULE_MAX_OP_PORTS:456 max_op_port = le32_to_cpu(mod_elem->value);457 break;458 case AR_TKN_U32_MODULE_IN_PORTS:459 in_port = le32_to_cpu(mod_elem->value);460 break;461 case AR_TKN_U32_MODULE_OUT_PORTS:462 out_port = le32_to_cpu(mod_elem->value);463 break;464 case AR_TKN_U32_MODULE_SRC_INSTANCE_ID:465 src_mod_inst_id = le32_to_cpu(mod_elem->value);466 break;467 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID:468 src_mod_op_port_id[0] = le32_to_cpu(mod_elem->value);469 break;470 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID1:471 src_mod_op_port_id[1] = le32_to_cpu(mod_elem->value);472 break;473 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID2:474 src_mod_op_port_id[2] = le32_to_cpu(mod_elem->value);475 break;476 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID3:477 src_mod_op_port_id[3] = le32_to_cpu(mod_elem->value);478 break;479 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID4:480 src_mod_op_port_id[4] = le32_to_cpu(mod_elem->value);481 break;482 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID5:483 src_mod_op_port_id[5] = le32_to_cpu(mod_elem->value);484 break;485 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID6:486 src_mod_op_port_id[6] = le32_to_cpu(mod_elem->value);487 break;488 case AR_TKN_U32_MODULE_SRC_OP_PORT_ID7:489 src_mod_op_port_id[7] = le32_to_cpu(mod_elem->value);490 break;491 case AR_TKN_U32_MODULE_DST_INSTANCE_ID:492 dst_mod_inst_id[0] = le32_to_cpu(mod_elem->value);493 break;494 case AR_TKN_U32_MODULE_DST_INSTANCE_ID1:495 dst_mod_inst_id[1] = le32_to_cpu(mod_elem->value);496 break;497 case AR_TKN_U32_MODULE_DST_INSTANCE_ID2:498 dst_mod_inst_id[2] = le32_to_cpu(mod_elem->value);499 break;500 case AR_TKN_U32_MODULE_DST_INSTANCE_ID3:501 dst_mod_inst_id[3] = le32_to_cpu(mod_elem->value);502 break;503 case AR_TKN_U32_MODULE_DST_INSTANCE_ID4:504 dst_mod_inst_id[4] = le32_to_cpu(mod_elem->value);505 break;506 case AR_TKN_U32_MODULE_DST_INSTANCE_ID5:507 dst_mod_inst_id[5] = le32_to_cpu(mod_elem->value);508 break;509 case AR_TKN_U32_MODULE_DST_INSTANCE_ID6:510 dst_mod_inst_id[6] = le32_to_cpu(mod_elem->value);511 break;512 case AR_TKN_U32_MODULE_DST_INSTANCE_ID7:513 dst_mod_inst_id[7] = le32_to_cpu(mod_elem->value);514 break;515 case AR_TKN_U32_MODULE_DST_IN_PORT_ID:516 dst_mod_ip_port_id[0] = le32_to_cpu(mod_elem->value);517 break;518 case AR_TKN_U32_MODULE_DST_IN_PORT_ID1:519 dst_mod_ip_port_id[1] = le32_to_cpu(mod_elem->value);520 break;521 case AR_TKN_U32_MODULE_DST_IN_PORT_ID2:522 dst_mod_ip_port_id[2] = le32_to_cpu(mod_elem->value);523 break;524 case AR_TKN_U32_MODULE_DST_IN_PORT_ID3:525 dst_mod_ip_port_id[3] = le32_to_cpu(mod_elem->value);526 break;527 case AR_TKN_U32_MODULE_DST_IN_PORT_ID4:528 dst_mod_ip_port_id[4] = le32_to_cpu(mod_elem->value);529 break;530 case AR_TKN_U32_MODULE_DST_IN_PORT_ID5:531 dst_mod_ip_port_id[5] = le32_to_cpu(mod_elem->value);532 break;533 case AR_TKN_U32_MODULE_DST_IN_PORT_ID6:534 dst_mod_ip_port_id[6] = le32_to_cpu(mod_elem->value);535 break;536 case AR_TKN_U32_MODULE_DST_IN_PORT_ID7:537 dst_mod_ip_port_id[7] = le32_to_cpu(mod_elem->value);538 break;539 default:540 break;541 542 }543 tkn_count++;544 mod_elem++;545 }546 547 if (mod) {548 int pn, id = 0;549 550 mod->module_id = module_id;551 mod->max_ip_port = max_ip_port;552 mod->max_op_port = max_op_port;553 mod->in_port = in_port;554 mod->out_port = out_port;555 mod->src_mod_inst_id = src_mod_inst_id;556 for (pn = 0; pn < mod->max_op_port; pn++) {557 if (src_mod_op_port_id[pn] && dst_mod_inst_id[pn] &&558 dst_mod_ip_port_id[pn]) {559 mod->src_mod_op_port_id[id] = src_mod_op_port_id[pn];560 mod->dst_mod_inst_id[id] = dst_mod_inst_id[pn];561 mod->dst_mod_ip_port_id[id] = dst_mod_ip_port_id[pn];562 id++;563 mod->num_connections = id;564 }565 }566 }567 568 return mod;569}570 571static int audioreach_widget_load_module_common(struct snd_soc_component *component,572 int index, struct snd_soc_dapm_widget *w,573 struct snd_soc_tplg_dapm_widget *tplg_w)574{575 struct q6apm *apm = dev_get_drvdata(component->dev);576 struct audioreach_container *cont;577 struct audioreach_sub_graph *sg;578 struct audioreach_module *mod;579 struct snd_soc_dobj *dobj;580 581 sg = audioreach_parse_sg_tokens(apm, &tplg_w->priv);582 if (IS_ERR(sg))583 return PTR_ERR(sg);584 585 cont = audioreach_parse_cont_tokens(apm, sg, &tplg_w->priv);586 if (IS_ERR(cont))587 return PTR_ERR(cont);588 589 mod = audioreach_parse_common_tokens(apm, cont, &tplg_w->priv, w);590 if (IS_ERR(mod))591 return PTR_ERR(mod);592 593 dobj = &w->dobj;594 dobj->private = mod;595 596 return 0;597}598 599static int audioreach_widget_load_enc_dec_cnv(struct snd_soc_component *component,600 int index, struct snd_soc_dapm_widget *w,601 struct snd_soc_tplg_dapm_widget *tplg_w)602{603 struct snd_soc_tplg_vendor_value_elem *mod_elem;604 struct snd_soc_tplg_vendor_array *mod_array;605 struct audioreach_module *mod;606 struct snd_soc_dobj *dobj;607 int tkn_count = 0;608 int ret;609 610 ret = audioreach_widget_load_module_common(component, index, w, tplg_w);611 if (ret)612 return ret;613 614 dobj = &w->dobj;615 mod = dobj->private;616 mod_array = audioreach_get_module_array(&tplg_w->priv);617 mod_elem = mod_array->value;618 619 while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {620 switch (le32_to_cpu(mod_elem->token)) {621 case AR_TKN_U32_MODULE_FMT_INTERLEAVE:622 mod->interleave_type = le32_to_cpu(mod_elem->value);623 break;624 case AR_TKN_U32_MODULE_FMT_SAMPLE_RATE:625 mod->rate = le32_to_cpu(mod_elem->value);626 break;627 case AR_TKN_U32_MODULE_FMT_BIT_DEPTH:628 mod->bit_depth = le32_to_cpu(mod_elem->value);629 break;630 default:631 break;632 }633 tkn_count++;634 mod_elem++;635 }636 637 return 0;638}639 640static int audioreach_widget_log_module_load(struct audioreach_module *mod,641 struct snd_soc_tplg_vendor_array *mod_array)642{643 struct snd_soc_tplg_vendor_value_elem *mod_elem;644 int tkn_count = 0;645 646 mod_elem = mod_array->value;647 648 while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {649 switch (le32_to_cpu(mod_elem->token)) {650 651 case AR_TKN_U32_MODULE_LOG_CODE:652 mod->log_code = le32_to_cpu(mod_elem->value);653 break;654 case AR_TKN_U32_MODULE_LOG_TAP_POINT_ID:655 mod->log_tap_point_id = le32_to_cpu(mod_elem->value);656 break;657 case AR_TKN_U32_MODULE_LOG_MODE:658 mod->log_mode = le32_to_cpu(mod_elem->value);659 break;660 default:661 break;662 }663 tkn_count++;664 mod_elem++;665 }666 667 return 0;668}669 670static int audioreach_widget_dma_module_load(struct audioreach_module *mod,671 struct snd_soc_tplg_vendor_array *mod_array)672{673 struct snd_soc_tplg_vendor_value_elem *mod_elem;674 int tkn_count = 0;675 676 mod_elem = mod_array->value;677 678 while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {679 switch (le32_to_cpu(mod_elem->token)) {680 case AR_TKN_U32_MODULE_HW_IF_IDX:681 mod->hw_interface_idx = le32_to_cpu(mod_elem->value);682 break;683 case AR_TKN_U32_MODULE_FMT_DATA:684 mod->data_format = le32_to_cpu(mod_elem->value);685 break;686 case AR_TKN_U32_MODULE_HW_IF_TYPE:687 mod->hw_interface_type = le32_to_cpu(mod_elem->value);688 break;689 default:690 break;691 }692 tkn_count++;693 mod_elem++;694 }695 696 return 0;697}698 699static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,700 struct snd_soc_tplg_vendor_array *mod_array)701{702 struct snd_soc_tplg_vendor_value_elem *mod_elem;703 int tkn_count = 0;704 705 mod_elem = mod_array->value;706 707 while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {708 switch (le32_to_cpu(mod_elem->token)) {709 case AR_TKN_U32_MODULE_HW_IF_IDX:710 mod->hw_interface_idx = le32_to_cpu(mod_elem->value);711 break;712 case AR_TKN_U32_MODULE_FMT_DATA:713 mod->data_format = le32_to_cpu(mod_elem->value);714 break;715 case AR_TKN_U32_MODULE_HW_IF_TYPE:716 mod->hw_interface_type = le32_to_cpu(mod_elem->value);717 break;718 case AR_TKN_U32_MODULE_SD_LINE_IDX:719 mod->sd_line_idx = le32_to_cpu(mod_elem->value);720 break;721 case AR_TKN_U32_MODULE_WS_SRC:722 mod->ws_src = le32_to_cpu(mod_elem->value);723 break;724 default:725 break;726 }727 tkn_count++;728 mod_elem++;729 }730 731 return 0;732}733 734static int audioreach_widget_dp_module_load(struct audioreach_module *mod,735 struct snd_soc_tplg_vendor_array *mod_array)736{737 struct snd_soc_tplg_vendor_value_elem *mod_elem;738 int tkn_count = 0;739 740 mod_elem = mod_array->value;741 742 while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) {743 switch (le32_to_cpu(mod_elem->token)) {744 case AR_TKN_U32_MODULE_FMT_DATA:745 mod->data_format = le32_to_cpu(mod_elem->value);746 break;747 default:748 break;749 }750 tkn_count++;751 mod_elem++;752 }753 754 return 0;755}756 757static int audioreach_widget_load_buffer(struct snd_soc_component *component,758 int index, struct snd_soc_dapm_widget *w,759 struct snd_soc_tplg_dapm_widget *tplg_w)760{761 struct snd_soc_tplg_vendor_array *mod_array;762 struct audioreach_module *mod;763 struct snd_soc_dobj *dobj;764 int ret;765 766 ret = audioreach_widget_load_module_common(component, index, w, tplg_w);767 if (ret)768 return ret;769 770 dobj = &w->dobj;771 mod = dobj->private;772 773 mod_array = audioreach_get_module_array(&tplg_w->priv);774 775 switch (mod->module_id) {776 case MODULE_ID_CODEC_DMA_SINK:777 case MODULE_ID_CODEC_DMA_SOURCE:778 audioreach_widget_dma_module_load(mod, mod_array);779 break;780 case MODULE_ID_DATA_LOGGING:781 audioreach_widget_log_module_load(mod, mod_array);782 break;783 case MODULE_ID_I2S_SINK:784 case MODULE_ID_I2S_SOURCE:785 audioreach_widget_i2s_module_load(mod, mod_array);786 break;787 case MODULE_ID_DISPLAY_PORT_SINK:788 audioreach_widget_dp_module_load(mod, mod_array);789 break;790 default:791 return -EINVAL;792 }793 794 return 0;795}796 797static int audioreach_widget_load_mixer(struct snd_soc_component *component,798 int index, struct snd_soc_dapm_widget *w,799 struct snd_soc_tplg_dapm_widget *tplg_w)800{801 struct snd_soc_tplg_vendor_value_elem *w_elem;802 struct snd_soc_tplg_vendor_array *w_array;803 struct snd_ar_control *scontrol;804 struct q6apm *data = dev_get_drvdata(component->dev);805 struct snd_soc_dobj *dobj;806 int tkn_count = 0;807 808 w_array = &tplg_w->priv.array[0];809 810 scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);811 if (!scontrol)812 return -ENOMEM;813 814 scontrol->scomp = component;815 dobj = &w->dobj;816 dobj->private = scontrol;817 818 w_elem = w_array->value;819 while (tkn_count <= (le32_to_cpu(w_array->num_elems) - 1)) {820 switch (le32_to_cpu(w_elem->token)) {821 case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:822 scontrol->sgid = le32_to_cpu(w_elem->value);823 break;824 case AR_TKN_DAI_INDEX:825 scontrol->graph_id = le32_to_cpu(w_elem->value);826 break;827 default: /* ignore other tokens */828 break;829 }830 tkn_count++;831 w_elem++;832 }833 834 scontrol->w = w;835 list_add_tail(&scontrol->node, &data->widget_list);836 837 return 0;838}839 840static int audioreach_pga_event(struct snd_soc_dapm_widget *w,841 struct snd_kcontrol *kcontrol, int event)842 843{844 struct snd_soc_dapm_context *dapm = w->dapm;845 struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);846 struct audioreach_module *mod = w->dobj.private;847 struct q6apm *apm = dev_get_drvdata(c->dev);848 849 switch (event) {850 case SND_SOC_DAPM_POST_PMU:851 /* apply gain after power up of widget */852 audioreach_gain_set_vol_ctrl(apm, mod, mod->gain);853 break;854 default:855 break;856 }857 858 return 0;859}860 861static const struct snd_soc_tplg_widget_events audioreach_widget_ops[] = {862 { AR_PGA_DAPM_EVENT, audioreach_pga_event },863};864 865static int audioreach_widget_load_pga(struct snd_soc_component *component,866 int index, struct snd_soc_dapm_widget *w,867 struct snd_soc_tplg_dapm_widget *tplg_w)868{869 struct audioreach_module *mod;870 struct snd_soc_dobj *dobj;871 int ret;872 873 ret = audioreach_widget_load_module_common(component, index, w, tplg_w);874 if (ret)875 return ret;876 877 dobj = &w->dobj;878 mod = dobj->private;879 mod->gain = VOL_CTRL_DEFAULT_GAIN;880 881 ret = snd_soc_tplg_widget_bind_event(w, audioreach_widget_ops,882 ARRAY_SIZE(audioreach_widget_ops),883 le16_to_cpu(tplg_w->event_type));884 if (ret) {885 dev_err(component->dev, "matching event handlers NOT found for %d\n",886 le16_to_cpu(tplg_w->event_type));887 return -EINVAL;888 }889 890 return 0;891}892 893static int audioreach_widget_ready(struct snd_soc_component *component,894 int index, struct snd_soc_dapm_widget *w,895 struct snd_soc_tplg_dapm_widget *tplg_w)896{897 switch (w->id) {898 case snd_soc_dapm_aif_in:899 case snd_soc_dapm_aif_out:900 audioreach_widget_load_buffer(component, index, w, tplg_w);901 break;902 case snd_soc_dapm_decoder:903 case snd_soc_dapm_encoder:904 case snd_soc_dapm_src:905 audioreach_widget_load_enc_dec_cnv(component, index, w, tplg_w);906 break;907 case snd_soc_dapm_buffer:908 audioreach_widget_load_buffer(component, index, w, tplg_w);909 break;910 case snd_soc_dapm_mixer:911 return audioreach_widget_load_mixer(component, index, w, tplg_w);912 case snd_soc_dapm_pga:913 return audioreach_widget_load_pga(component, index, w, tplg_w);914 case snd_soc_dapm_dai_link:915 case snd_soc_dapm_scheduler:916 case snd_soc_dapm_out_drv:917 default:918 dev_err(component->dev, "Widget type (0x%x) not yet supported\n", w->id);919 break;920 }921 922 return 0;923}924 925static int audioreach_widget_unload(struct snd_soc_component *scomp,926 struct snd_soc_dobj *dobj)927{928 struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj);929 struct q6apm *apm = dev_get_drvdata(scomp->dev);930 struct audioreach_container *cont;931 struct audioreach_module *mod;932 933 mod = dobj->private;934 cont = mod->container;935 936 if (w->id == snd_soc_dapm_mixer) {937 /* virtual widget */938 struct snd_ar_control *scontrol = dobj->private;939 940 list_del(&scontrol->node);941 kfree(scontrol);942 return 0;943 }944 945 mutex_lock(&apm->lock);946 idr_remove(&apm->modules_idr, mod->instance_id);947 cont->num_modules--;948 949 list_del(&mod->node);950 kfree(mod);951 /* Graph Info has N sub-graphs, sub-graph has N containers, Container has N Modules */952 if (list_empty(&cont->modules_list)) { /* if no modules in the container then remove it */953 struct audioreach_sub_graph *sg = cont->sub_graph;954 955 idr_remove(&apm->containers_idr, cont->container_id);956 list_del(&cont->node);957 sg->num_containers--;958 kfree(cont);959 /* check if there are no more containers in the sub graph and remove it */960 if (list_empty(&sg->container_list)) {961 struct audioreach_graph_info *info = sg->info;962 963 idr_remove(&apm->sub_graphs_idr, sg->sub_graph_id);964 list_del(&sg->node);965 info->num_sub_graphs--;966 kfree(sg);967 /* Check if there are no more sub-graphs left then remove graph info */968 if (list_empty(&info->sg_list)) {969 idr_remove(&apm->graph_info_idr, info->id);970 kfree(info);971 }972 }973 }974 975 mutex_unlock(&apm->lock);976 977 return 0;978}979 980static struct snd_ar_control *audioreach_find_widget(struct snd_soc_component *comp,981 const char *name)982{983 struct q6apm *apm = dev_get_drvdata(comp->dev);984 struct snd_ar_control *control;985 986 list_for_each_entry(control, &apm->widget_list, node) {987 if (control->w && !strcmp(name, control->w->name))988 return control;989 }990 991 return NULL;992}993 994static struct audioreach_module *audioreach_find_module(struct snd_soc_component *comp,995 const char *name)996{997 struct q6apm *apm = dev_get_drvdata(comp->dev);998 struct audioreach_module *module;999 int id;1000 1001 idr_for_each_entry(&apm->modules_idr, module, id) {1002 if (!strcmp(name, module->widget->name))1003 return module;1004 }1005 1006 return NULL;1007}1008 1009static int audioreach_route_load(struct snd_soc_component *scomp, int index,1010 struct snd_soc_dapm_route *route)1011{1012 struct audioreach_module *src_module, *sink_module;1013 struct snd_ar_control *control;1014 struct snd_soc_dapm_widget *w;1015 int i;1016 1017 /* check if these are actual modules */1018 src_module = audioreach_find_module(scomp, route->source);1019 sink_module = audioreach_find_module(scomp, route->sink);1020 1021 if (sink_module && !src_module) {1022 control = audioreach_find_widget(scomp, route->source);1023 if (control)1024 control->module_instance_id = sink_module->instance_id;1025 1026 } else if (!sink_module && src_module && route->control) {1027 /* check if this is a virtual mixer */1028 control = audioreach_find_widget(scomp, route->sink);1029 if (!control || !control->w)1030 return 0;1031 1032 w = control->w;1033 1034 for (i = 0; i < w->num_kcontrols; i++) {1035 if (!strcmp(route->control, w->kcontrol_news[i].name)) {1036 struct soc_mixer_control *sm;1037 struct snd_soc_dobj *dobj;1038 struct snd_ar_control *scontrol;1039 1040 sm = (struct soc_mixer_control *)w->kcontrol_news[i].private_value;1041 dobj = &sm->dobj;1042 scontrol = dobj->private;1043 scontrol->module_instance_id = src_module->instance_id;1044 }1045 }1046 1047 }1048 1049 return 0;1050}1051 1052static int audioreach_route_unload(struct snd_soc_component *scomp,1053 struct snd_soc_dobj *dobj)1054{1055 return 0;1056}1057 1058static int audioreach_tplg_complete(struct snd_soc_component *component)1059{1060 /* TBD */1061 return 0;1062}1063 1064/* DAI link - used for any driver specific init */1065static int audioreach_link_load(struct snd_soc_component *component, int index,1066 struct snd_soc_dai_link *link,1067 struct snd_soc_tplg_link_config *cfg)1068{1069 link->nonatomic = true;1070 link->dynamic = true;1071 link->platforms->name = NULL;1072 link->platforms->of_node = of_get_compatible_child(component->dev->of_node,1073 "qcom,q6apm-dais");1074 return 0;1075}1076 1077static void audioreach_connect_sub_graphs(struct q6apm *apm,1078 struct snd_ar_control *m1,1079 struct snd_ar_control *m2,1080 bool connect)1081{1082 struct audioreach_graph_info *info;1083 1084 mutex_lock(&apm->lock);1085 info = idr_find(&apm->graph_info_idr, m2->graph_id);1086 mutex_unlock(&apm->lock);1087 1088 if (connect) {1089 info->src_mod_inst_id = m1->module_instance_id;1090 info->src_mod_op_port_id = 1;1091 info->dst_mod_inst_id = m2->module_instance_id;1092 info->dst_mod_ip_port_id = 2;1093 1094 } else {1095 info->src_mod_inst_id = 0;1096 info->src_mod_op_port_id = 0;1097 info->dst_mod_inst_id = 0;1098 info->dst_mod_ip_port_id = 0;1099 }1100}1101 1102static bool audioreach_is_vmixer_connected(struct q6apm *apm,1103 struct snd_ar_control *m1,1104 struct snd_ar_control *m2)1105{1106 struct audioreach_graph_info *info;1107 1108 mutex_lock(&apm->lock);1109 info = idr_find(&apm->graph_info_idr, m2->graph_id);1110 mutex_unlock(&apm->lock);1111 1112 if (info->dst_mod_inst_id == m2->module_instance_id &&1113 info->src_mod_inst_id == m1->module_instance_id)1114 return true;1115 1116 return false;1117}1118 1119static int audioreach_get_audio_mixer(struct snd_kcontrol *kcontrol,1120 struct snd_ctl_elem_value *ucontrol)1121{1122 struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;1123 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);1124 struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);1125 struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);1126 struct snd_ar_control *dapm_scontrol = dw->dobj.private;1127 struct snd_ar_control *scontrol = mc->dobj.private;1128 struct q6apm *data = dev_get_drvdata(c->dev);1129 bool connected;1130 1131 connected = audioreach_is_vmixer_connected(data, scontrol, dapm_scontrol);1132 if (connected)1133 ucontrol->value.integer.value[0] = 1;1134 else1135 ucontrol->value.integer.value[0] = 0;1136 1137 return 0;1138}1139 1140static int audioreach_put_audio_mixer(struct snd_kcontrol *kcontrol,1141 struct snd_ctl_elem_value *ucontrol)1142{1143 struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;1144 struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);1145 struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);1146 struct snd_soc_component *c = snd_soc_dapm_to_component(dapm);1147 struct snd_ar_control *dapm_scontrol = dw->dobj.private;1148 struct snd_ar_control *scontrol = mc->dobj.private;1149 struct q6apm *data = dev_get_drvdata(c->dev);1150 1151 if (ucontrol->value.integer.value[0]) {1152 audioreach_connect_sub_graphs(data, scontrol, dapm_scontrol, true);1153 snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, NULL);1154 } else {1155 audioreach_connect_sub_graphs(data, scontrol, dapm_scontrol, false);1156 snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, NULL);1157 }1158 return 0;1159}1160 1161static int audioreach_get_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,1162 struct snd_ctl_elem_value *ucontrol)1163{1164 struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);1165 struct audioreach_module *mod = dw->dobj.private;1166 1167 ucontrol->value.integer.value[0] = mod->gain;1168 1169 return 0;1170}1171 1172static int audioreach_put_vol_ctrl_audio_mixer(struct snd_kcontrol *kcontrol,1173 struct snd_ctl_elem_value *ucontrol)1174{1175 struct snd_soc_dapm_widget *dw = snd_soc_dapm_kcontrol_widget(kcontrol);1176 struct audioreach_module *mod = dw->dobj.private;1177 1178 mod->gain = ucontrol->value.integer.value[0];1179 1180 return 1;1181}1182 1183static int audioreach_control_load_mix(struct snd_soc_component *scomp,1184 struct snd_ar_control *scontrol,1185 struct snd_kcontrol_new *kc,1186 struct snd_soc_tplg_ctl_hdr *hdr)1187{1188 struct snd_soc_tplg_vendor_value_elem *c_elem;1189 struct snd_soc_tplg_vendor_array *c_array;1190 struct snd_soc_tplg_mixer_control *mc;1191 int tkn_count = 0;1192 1193 mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);1194 c_array = (struct snd_soc_tplg_vendor_array *)mc->priv.data;1195 1196 c_elem = c_array->value;1197 1198 while (tkn_count <= (le32_to_cpu(c_array->num_elems) - 1)) {1199 switch (le32_to_cpu(c_elem->token)) {1200 case AR_TKN_U32_SUB_GRAPH_INSTANCE_ID:1201 scontrol->sgid = le32_to_cpu(c_elem->value);1202 break;1203 case AR_TKN_DAI_INDEX:1204 scontrol->graph_id = le32_to_cpu(c_elem->value);1205 break;1206 default:1207 /* Ignore other tokens */1208 break;1209 }1210 c_elem++;1211 tkn_count++;1212 }1213 1214 return 0;1215}1216 1217static int audioreach_control_load(struct snd_soc_component *scomp, int index,1218 struct snd_kcontrol_new *kc,1219 struct snd_soc_tplg_ctl_hdr *hdr)1220{1221 struct snd_ar_control *scontrol;1222 struct soc_mixer_control *sm;1223 struct snd_soc_dobj *dobj;1224 int ret = 0;1225 1226 scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);1227 if (!scontrol)1228 return -ENOMEM;1229 1230 scontrol->scomp = scomp;1231 1232 switch (le32_to_cpu(hdr->ops.get)) {1233 case SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX:1234 sm = (struct soc_mixer_control *)kc->private_value;1235 dobj = &sm->dobj;1236 ret = audioreach_control_load_mix(scomp, scontrol, kc, hdr);1237 break;1238 case SND_SOC_AR_TPLG_VOL_CTL:1239 sm = (struct soc_mixer_control *)kc->private_value;1240 dobj = &sm->dobj;1241 break;1242 default:1243 dev_warn(scomp->dev, "control type not supported %d:%d:%d\n",1244 hdr->ops.get, hdr->ops.put, hdr->ops.info);1245 kfree(scontrol);1246 return -EINVAL;1247 }1248 1249 dobj->private = scontrol;1250 return ret;1251}1252 1253static int audioreach_control_unload(struct snd_soc_component *scomp,1254 struct snd_soc_dobj *dobj)1255{1256 struct snd_ar_control *scontrol = dobj->private;1257 1258 kfree(scontrol);1259 1260 return 0;1261}1262 1263static const struct snd_soc_tplg_kcontrol_ops audioreach_io_ops[] = {1264 {SND_SOC_AR_TPLG_FE_BE_GRAPH_CTL_MIX, audioreach_get_audio_mixer,1265 audioreach_put_audio_mixer, snd_soc_info_volsw},1266 {SND_SOC_AR_TPLG_VOL_CTL, audioreach_get_vol_ctrl_audio_mixer,1267 audioreach_put_vol_ctrl_audio_mixer, snd_soc_info_volsw},1268};1269 1270static const struct snd_soc_tplg_ops audioreach_tplg_ops = {1271 .io_ops = audioreach_io_ops,1272 .io_ops_count = ARRAY_SIZE(audioreach_io_ops),1273 1274 .control_load = audioreach_control_load,1275 .control_unload = audioreach_control_unload,1276 1277 .widget_ready = audioreach_widget_ready,1278 .widget_unload = audioreach_widget_unload,1279 1280 .complete = audioreach_tplg_complete,1281 .link_load = audioreach_link_load,1282 1283 .dapm_route_load = audioreach_route_load,1284 .dapm_route_unload = audioreach_route_unload,1285};1286 1287int audioreach_tplg_init(struct snd_soc_component *component)1288{1289 struct snd_soc_card *card = component->card;1290 struct device *dev = component->dev;1291 const struct firmware *fw;1292 int ret;1293 1294 /* Inline with Qualcomm UCM configs and linux-firmware path */1295 char *tplg_fw_name __free(kfree) = kasprintf(GFP_KERNEL, "qcom/%s/%s-tplg.bin",1296 card->driver_name,1297 card->name);1298 if (!tplg_fw_name)1299 return -ENOMEM;1300 1301 ret = request_firmware(&fw, tplg_fw_name, dev);1302 if (ret < 0) {1303 dev_err(dev, "tplg firmware loading %s failed %d\n", tplg_fw_name, ret);1304 return ret;1305 }1306 1307 ret = snd_soc_tplg_component_load(component, &audioreach_tplg_ops, fw);1308 if (ret < 0) {1309 if (ret != -EPROBE_DEFER)1310 dev_err(dev, "tplg component load failed: %d\n", ret);1311 }1312 1313 release_firmware(fw);1314 1315 return ret;1316}1317EXPORT_SYMBOL_GPL(audioreach_tplg_init);1318