brintos

brintos / linux-shallow public Read only

0
0
Text · 34.7 KiB · 83319a9 Raw
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