brintos

brintos / linux-shallow public Read only

0
0
Text · 20.4 KiB · 2b1befa Raw
732 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)2//3// This file is provided under a dual BSD/GPLv2 license.  When using or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2021 Intel Corporation7//8//9 10#include "sof-priv.h"11#include "sof-audio.h"12#include "ipc3-priv.h"13 14/* IPC set()/get() for kcontrols. */15static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,16					  bool set, bool lock)17{18	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);19	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;20	const struct sof_ipc_ops *iops = sdev->ipc->ops;21	enum sof_ipc_ctrl_type ctrl_type;22	struct snd_sof_widget *swidget;23	bool widget_found = false;24	u32 ipc_cmd, msg_bytes;25	int ret = 0;26 27	list_for_each_entry(swidget, &sdev->widget_list, list) {28		if (swidget->comp_id == scontrol->comp_id) {29			widget_found = true;30			break;31		}32	}33 34	if (!widget_found) {35		dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__,36			scontrol->comp_id);37		return -EINVAL;38	}39 40	if (lock)41		mutex_lock(&swidget->setup_mutex);42	else43		lockdep_assert_held(&swidget->setup_mutex);44 45	/*46	 * Volatile controls should always be part of static pipelines and the47	 * widget use_count would always be > 0 in this case. For the others,48	 * just return the cached value if the widget is not set up.49	 */50	if (!swidget->use_count)51		goto unlock;52 53	/*54	 * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the55	 * direction56	 * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently57	 *	 for ctrl_type58	 */59	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {60		ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA;61		ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET;62	} else {63		ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE;64		ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET;65	}66 67	cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;68	cdata->type = ctrl_type;69	cdata->comp_id = scontrol->comp_id;70	cdata->msg_index = 0;71 72	/* calculate header and data size */73	switch (cdata->type) {74	case SOF_CTRL_TYPE_VALUE_CHAN_GET:75	case SOF_CTRL_TYPE_VALUE_CHAN_SET:76		cdata->num_elems = scontrol->num_channels;77 78		msg_bytes = scontrol->num_channels *79			    sizeof(struct sof_ipc_ctrl_value_chan);80		msg_bytes += sizeof(struct sof_ipc_ctrl_data);81		break;82	case SOF_CTRL_TYPE_DATA_GET:83	case SOF_CTRL_TYPE_DATA_SET:84		cdata->num_elems = cdata->data->size;85 86		msg_bytes = cdata->data->size;87		msg_bytes += sizeof(struct sof_ipc_ctrl_data) +88			     sizeof(struct sof_abi_hdr);89		break;90	default:91		ret = -EINVAL;92		goto unlock;93	}94 95	cdata->rhdr.hdr.size = msg_bytes;96	cdata->elems_remaining = 0;97 98	ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);99	if (!set)100		goto unlock;101 102	/* It is a set-data operation, and we have a backup that we can restore */103	if (ret < 0) {104		if (!scontrol->old_ipc_control_data)105			goto unlock;106		/*107		 * Current ipc_control_data is not valid, we use the last known good108		 * configuration109		 */110		memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,111		       scontrol->max_size);112		kfree(scontrol->old_ipc_control_data);113		scontrol->old_ipc_control_data = NULL;114		/* Send the last known good configuration to firmware */115		ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);116		if (ret < 0)117			goto unlock;118	}119 120unlock:121	if (lock)122		mutex_unlock(&swidget->setup_mutex);123 124	return ret;125}126 127static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol)128{129	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;130	struct snd_soc_component *scomp = scontrol->scomp;131	int ret;132 133	if (!scontrol->comp_data_dirty)134		return;135 136	if (!pm_runtime_active(scomp->dev))137		return;138 139	/* set the ABI header values */140	cdata->data->magic = SOF_ABI_MAGIC;141	cdata->data->abi = SOF_ABI_VERSION;142 143	/* refresh the component data from DSP */144	scontrol->comp_data_dirty = false;145	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);146	if (ret < 0) {147		dev_err(scomp->dev, "Failed to get control data: %d\n", ret);148 149		/* Set the flag to re-try next time to get the data */150		scontrol->comp_data_dirty = true;151	}152}153 154static int sof_ipc3_volume_get(struct snd_sof_control *scontrol,155			       struct snd_ctl_elem_value *ucontrol)156{157	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;158	unsigned int channels = scontrol->num_channels;159	unsigned int i;160 161	sof_ipc3_refresh_control(scontrol);162 163	/* read back each channel */164	for (i = 0; i < channels; i++)165		ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,166								scontrol->volume_table,167								scontrol->max + 1);168 169	return 0;170}171 172static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,173				struct snd_ctl_elem_value *ucontrol)174{175	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;176	struct snd_soc_component *scomp = scontrol->scomp;177	unsigned int channels = scontrol->num_channels;178	unsigned int i;179	bool change = false;180 181	/* update each channel */182	for (i = 0; i < channels; i++) {183		u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],184					 scontrol->volume_table, scontrol->max + 1);185 186		change = change || (value != cdata->chanv[i].value);187		cdata->chanv[i].channel = i;188		cdata->chanv[i].value = value;189	}190 191	/* notify DSP of mixer updates */192	if (pm_runtime_active(scomp->dev)) {193		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);194 195		if (ret < 0) {196			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",197				scontrol->name);198			return false;199		}200	}201 202	return change;203}204 205static int sof_ipc3_switch_get(struct snd_sof_control *scontrol,206			       struct snd_ctl_elem_value *ucontrol)207{208	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;209	unsigned int channels = scontrol->num_channels;210	unsigned int i;211 212	sof_ipc3_refresh_control(scontrol);213 214	/* read back each channel */215	for (i = 0; i < channels; i++)216		ucontrol->value.integer.value[i] = cdata->chanv[i].value;217 218	return 0;219}220 221static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,222				struct snd_ctl_elem_value *ucontrol)223{224	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;225	struct snd_soc_component *scomp = scontrol->scomp;226	unsigned int channels = scontrol->num_channels;227	unsigned int i;228	bool change = false;229	u32 value;230 231	/* update each channel */232	for (i = 0; i < channels; i++) {233		value = ucontrol->value.integer.value[i];234		change = change || (value != cdata->chanv[i].value);235		cdata->chanv[i].channel = i;236		cdata->chanv[i].value = value;237	}238 239	/* notify DSP of mixer updates */240	if (pm_runtime_active(scomp->dev)) {241		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);242 243		if (ret < 0) {244			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",245				scontrol->name);246			return false;247		}248	}249 250	return change;251}252 253static int sof_ipc3_enum_get(struct snd_sof_control *scontrol,254			     struct snd_ctl_elem_value *ucontrol)255{256	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;257	unsigned int channels = scontrol->num_channels;258	unsigned int i;259 260	sof_ipc3_refresh_control(scontrol);261 262	/* read back each channel */263	for (i = 0; i < channels; i++)264		ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;265 266	return 0;267}268 269static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,270			      struct snd_ctl_elem_value *ucontrol)271{272	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;273	struct snd_soc_component *scomp = scontrol->scomp;274	unsigned int channels = scontrol->num_channels;275	unsigned int i;276	bool change = false;277	u32 value;278 279	/* update each channel */280	for (i = 0; i < channels; i++) {281		value = ucontrol->value.enumerated.item[i];282		change = change || (value != cdata->chanv[i].value);283		cdata->chanv[i].channel = i;284		cdata->chanv[i].value = value;285	}286 287	/* notify DSP of enum updates */288	if (pm_runtime_active(scomp->dev)) {289		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);290 291		if (ret < 0) {292			dev_err(scomp->dev, "Failed to set enum updates for %s\n",293				scontrol->name);294			return false;295		}296	}297 298	return change;299}300 301static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,302			      struct snd_ctl_elem_value *ucontrol)303{304	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;305	struct snd_soc_component *scomp = scontrol->scomp;306	struct sof_abi_hdr *data = cdata->data;307	size_t size;308 309	sof_ipc3_refresh_control(scontrol);310 311	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {312		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",313				    scontrol->max_size);314		return -EINVAL;315	}316 317	/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */318	if (data->size > scontrol->max_size - sizeof(*data)) {319		dev_err_ratelimited(scomp->dev,320				    "%u bytes of control data is invalid, max is %zu\n",321				    data->size, scontrol->max_size - sizeof(*data));322		return -EINVAL;323	}324 325	size = data->size + sizeof(*data);326 327	/* copy back to kcontrol */328	memcpy(ucontrol->value.bytes.data, data, size);329 330	return 0;331}332 333static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,334			      struct snd_ctl_elem_value *ucontrol)335{336	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;337	struct snd_soc_component *scomp = scontrol->scomp;338	struct sof_abi_hdr *data = cdata->data;339	size_t size;340 341	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {342		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",343				    scontrol->max_size);344		return -EINVAL;345	}346 347	/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */348	if (data->size > scontrol->max_size - sizeof(*data)) {349		dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n",350				    data->size, scontrol->max_size - sizeof(*data));351		return -EINVAL;352	}353 354	size = data->size + sizeof(*data);355 356	/* copy from kcontrol */357	memcpy(data, ucontrol->value.bytes.data, size);358 359	/* notify DSP of byte control updates */360	if (pm_runtime_active(scomp->dev))361		return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);362 363	return 0;364}365 366static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,367				  const unsigned int __user *binary_data,368				  unsigned int size)369{370	const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data;371	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;372	struct snd_soc_component *scomp = scontrol->scomp;373	struct snd_ctl_tlv header;374	int ret = -EINVAL;375 376	/*377	 * The beginning of bytes data contains a header from where378	 * the length (as bytes) is needed to know the correct copy379	 * length of data from tlvd->tlv.380	 */381	if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))382		return -EFAULT;383 384	/* make sure TLV info is consistent */385	if (header.length + sizeof(struct snd_ctl_tlv) > size) {386		dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n",387				    header.length, sizeof(struct snd_ctl_tlv), size);388		return -EINVAL;389	}390 391	/* be->max is coming from topology */392	if (header.length > scontrol->max_size) {393		dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n",394				    header.length, scontrol->max_size);395		return -EINVAL;396	}397 398	/* Check that header id matches the command */399	if (header.numid != cdata->cmd) {400		dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n",401				    header.numid);402		return -EINVAL;403	}404 405	if (!scontrol->old_ipc_control_data) {406		/* Create a backup of the current, valid bytes control */407		scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,408							 scontrol->max_size, GFP_KERNEL);409		if (!scontrol->old_ipc_control_data)410			return -ENOMEM;411	}412 413	if (copy_from_user(cdata->data, tlvd->tlv, header.length)) {414		ret = -EFAULT;415		goto err_restore;416	}417 418	if (cdata->data->magic != SOF_ABI_MAGIC) {419		dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic);420		goto err_restore;421	}422 423	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {424		dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n",425				    cdata->data->abi);426		goto err_restore;427	}428 429	/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */430	if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {431		dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");432		goto err_restore;433	}434 435	/* notify DSP of byte control updates */436	if (pm_runtime_active(scomp->dev)) {437		/* Actually send the data to the DSP; this is an opportunity to validate the data */438		return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);439	}440 441	return 0;442 443err_restore:444	/* If we have an issue, we restore the old, valid bytes control data */445	if (scontrol->old_ipc_control_data) {446		memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size);447		kfree(scontrol->old_ipc_control_data);448		scontrol->old_ipc_control_data = NULL;449	}450	return ret;451}452 453static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,454				   const unsigned int __user *binary_data,455				   unsigned int size, bool from_dsp)456{457	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;458	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;459	struct snd_soc_component *scomp = scontrol->scomp;460	struct snd_ctl_tlv header;461	size_t data_size;462 463	/*464	 * Decrement the limit by ext bytes header size to465	 * ensure the user space buffer is not exceeded.466	 */467	if (size < sizeof(struct snd_ctl_tlv))468		return -ENOSPC;469 470	size -= sizeof(struct snd_ctl_tlv);471 472	/* set the ABI header values */473	cdata->data->magic = SOF_ABI_MAGIC;474	cdata->data->abi = SOF_ABI_VERSION;475 476	/* get all the component data from DSP */477	if (from_dsp) {478		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);479 480		if (ret < 0)481			return ret;482	}483 484	/* check data size doesn't exceed max coming from topology */485	if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {486		dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",487				    cdata->data->size,488				    scontrol->max_size - sizeof(struct sof_abi_hdr));489		return -EINVAL;490	}491 492	data_size = cdata->data->size + sizeof(struct sof_abi_hdr);493 494	/* make sure we don't exceed size provided by user space for data */495	if (data_size > size)496		return -ENOSPC;497 498	header.numid = cdata->cmd;499	header.length = data_size;500	if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))501		return -EFAULT;502 503	if (copy_to_user(tlvd->tlv, cdata->data, data_size))504		return -EFAULT;505 506	return 0;507}508 509static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,510				  const unsigned int __user *binary_data, unsigned int size)511{512	return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, false);513}514 515static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,516					   const unsigned int __user *binary_data,517					   unsigned int size)518{519	return _sof_ipc3_bytes_ext_get(scontrol, binary_data, size, true);520}521 522static void snd_sof_update_control(struct snd_sof_control *scontrol,523				   struct sof_ipc_ctrl_data *cdata)524{525	struct snd_soc_component *scomp = scontrol->scomp;526	struct sof_ipc_ctrl_data *local_cdata;527	int i;528 529	local_cdata = scontrol->ipc_control_data;530 531	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {532		if (cdata->num_elems != local_cdata->data->size) {533			dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n",534				cdata->num_elems, local_cdata->data->size);535			return;536		}537 538		/* copy the new binary data */539		memcpy(local_cdata->data, cdata->data, cdata->num_elems);540	} else if (cdata->num_elems != scontrol->num_channels) {541		dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n",542			cdata->num_elems, scontrol->num_channels);543	} else {544		/* copy the new values */545		for (i = 0; i < cdata->num_elems; i++)546			local_cdata->chanv[i].value = cdata->chanv[i].value;547	}548}549 550static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message)551{552	struct sof_ipc_ctrl_data *cdata = ipc_control_message;553	struct snd_soc_dapm_widget *widget;554	struct snd_sof_control *scontrol;555	struct snd_sof_widget *swidget;556	struct snd_kcontrol *kc = NULL;557	struct soc_mixer_control *sm;558	struct soc_bytes_ext *be;559	size_t expected_size;560	struct soc_enum *se;561	bool found = false;562	int i, type;563 564	if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET ||565	    cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) {566		dev_err(sdev->dev, "Component data is not supported in control notification\n");567		return;568	}569 570	/* Find the swidget first */571	list_for_each_entry(swidget, &sdev->widget_list, list) {572		if (swidget->comp_id == cdata->comp_id) {573			found = true;574			break;575		}576	}577 578	if (!found)579		return;580 581	/* Translate SOF cmd to TPLG type */582	switch (cdata->cmd) {583	case SOF_CTRL_CMD_VOLUME:584	case SOF_CTRL_CMD_SWITCH:585		type = SND_SOC_TPLG_TYPE_MIXER;586		break;587	case SOF_CTRL_CMD_BINARY:588		type = SND_SOC_TPLG_TYPE_BYTES;589		break;590	case SOF_CTRL_CMD_ENUM:591		type = SND_SOC_TPLG_TYPE_ENUM;592		break;593	default:594		dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__);595		return;596	}597 598	widget = swidget->widget;599	for (i = 0; i < widget->num_kcontrols; i++) {600		/* skip non matching types or non matching indexes within type */601		if (widget->dobj.widget.kcontrol_type[i] == type &&602		    widget->kcontrol_news[i].index == cdata->index) {603			kc = widget->kcontrols[i];604			break;605		}606	}607 608	if (!kc)609		return;610 611	switch (cdata->cmd) {612	case SOF_CTRL_CMD_VOLUME:613	case SOF_CTRL_CMD_SWITCH:614		sm = (struct soc_mixer_control *)kc->private_value;615		scontrol = sm->dobj.private;616		break;617	case SOF_CTRL_CMD_BINARY:618		be = (struct soc_bytes_ext *)kc->private_value;619		scontrol = be->dobj.private;620		break;621	case SOF_CTRL_CMD_ENUM:622		se = (struct soc_enum *)kc->private_value;623		scontrol = se->dobj.private;624		break;625	default:626		return;627	}628 629	expected_size = sizeof(struct sof_ipc_ctrl_data);630	switch (cdata->type) {631	case SOF_CTRL_TYPE_VALUE_CHAN_GET:632	case SOF_CTRL_TYPE_VALUE_CHAN_SET:633		expected_size += cdata->num_elems *634				 sizeof(struct sof_ipc_ctrl_value_chan);635		break;636	case SOF_CTRL_TYPE_DATA_GET:637	case SOF_CTRL_TYPE_DATA_SET:638		expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);639		break;640	default:641		return;642	}643 644	if (cdata->rhdr.hdr.size != expected_size) {645		dev_err(sdev->dev, "Component notification size mismatch\n");646		return;647	}648 649	if (cdata->num_elems)650		/*651		 * The message includes the updated value/data, update the652		 * control's local cache using the received notification653		 */654		snd_sof_update_control(scontrol, cdata);655	else656		/* Mark the scontrol that the value/data is changed in SOF */657		scontrol->comp_data_dirty = true;658 659	snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);660}661 662static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,663					  struct snd_sof_widget *swidget)664{665	struct snd_sof_control *scontrol;666	int ret;667 668	/* set up all controls for the widget */669	list_for_each_entry(scontrol, &sdev->kcontrol_list, list)670		if (scontrol->comp_id == swidget->comp_id) {671			/* set kcontrol data in DSP */672			ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false);673			if (ret < 0) {674				dev_err(sdev->dev,675					"kcontrol %d set up failed for widget %s\n",676					scontrol->comp_id, swidget->widget->name);677				return ret;678			}679 680			/*681			 * Read back the data from the DSP for static widgets.682			 * This is particularly useful for binary kcontrols683			 * associated with static pipeline widgets to initialize684			 * the data size to match that in the DSP.685			 */686			if (swidget->dynamic_pipeline_widget)687				continue;688 689			ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false);690			if (ret < 0)691				dev_warn(sdev->dev,692					 "kcontrol %d read failed for widget %s\n",693					 scontrol->comp_id, swidget->widget->name);694		}695 696	return 0;697}698 699static int700sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)701{702	int i;703 704	/* init the volume table */705	scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);706	if (!scontrol->volume_table)707		return -ENOMEM;708 709	/* populate the volume table */710	for (i = 0; i < size ; i++)711		scontrol->volume_table[i] = vol_compute_gain(i, tlv);712 713	return 0;714}715 716const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {717	.volume_put = sof_ipc3_volume_put,718	.volume_get = sof_ipc3_volume_get,719	.switch_put = sof_ipc3_switch_put,720	.switch_get = sof_ipc3_switch_get,721	.enum_put = sof_ipc3_enum_put,722	.enum_get = sof_ipc3_enum_get,723	.bytes_put = sof_ipc3_bytes_put,724	.bytes_get = sof_ipc3_bytes_get,725	.bytes_ext_put = sof_ipc3_bytes_ext_put,726	.bytes_ext_get = sof_ipc3_bytes_ext_get,727	.bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get,728	.update = sof_ipc3_control_update,729	.widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup,730	.set_up_volume_table = sof_ipc3_set_up_volume_table,731};732