brintos

brintos / linux-shallow public Read only

0
0
Text · 23.7 KiB · 576f407 Raw
859 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) 2022 Intel Corporation7//8//9 10#include "sof-priv.h"11#include "sof-audio.h"12#include "ipc4-priv.h"13#include "ipc4-topology.h"14 15static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,16					  bool set, bool lock)17{18	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;19	struct snd_soc_component *scomp = scontrol->scomp;20	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);21	const struct sof_ipc_ops *iops = sdev->ipc->ops;22	struct sof_ipc4_msg *msg = &cdata->msg;23	struct snd_sof_widget *swidget;24	bool widget_found = false;25	int ret = 0;26 27	/* find widget associated with the control */28	list_for_each_entry(swidget, &sdev->widget_list, list) {29		if (swidget->comp_id == scontrol->comp_id) {30			widget_found = true;31			break;32		}33	}34 35	if (!widget_found) {36		dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);37		return -ENOENT;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	msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;54	msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);55 56	ret = iops->set_get_data(sdev, msg, msg->data_size, set);57	if (!set)58		goto unlock;59 60	/* It is a set-data operation, and we have a valid backup that we can restore */61	if (ret < 0) {62		if (!scontrol->old_ipc_control_data)63			goto unlock;64		/*65		 * Current ipc_control_data is not valid, we use the last known good66		 * configuration67		 */68		memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,69		       scontrol->max_size);70		kfree(scontrol->old_ipc_control_data);71		scontrol->old_ipc_control_data = NULL;72		/* Send the last known good configuration to firmware */73		ret = iops->set_get_data(sdev, msg, msg->data_size, set);74		if (ret < 0)75			goto unlock;76	}77 78unlock:79	if (lock)80		mutex_unlock(&swidget->setup_mutex);81 82	return ret;83}84 85static int86sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,87			 struct snd_sof_control *scontrol, bool lock)88{89	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;90	struct sof_ipc4_gain *gain = swidget->private;91	struct sof_ipc4_msg *msg = &cdata->msg;92	struct sof_ipc4_gain_params params;93	bool all_channels_equal = true;94	u32 value;95	int ret, i;96 97	/* check if all channel values are equal */98	value = cdata->chanv[0].value;99	for (i = 1; i < scontrol->num_channels; i++) {100		if (cdata->chanv[i].value != value) {101			all_channels_equal = false;102			break;103		}104	}105 106	/*107	 * notify DSP with a single IPC message if all channel values are equal. Otherwise send108	 * a separate IPC for each channel.109	 */110	for (i = 0; i < scontrol->num_channels; i++) {111		if (all_channels_equal) {112			params.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;113			params.init_val = cdata->chanv[0].value;114		} else {115			params.channels = cdata->chanv[i].channel;116			params.init_val = cdata->chanv[i].value;117		}118 119		/* set curve type and duration from topology */120		params.curve_duration_l = gain->data.params.curve_duration_l;121		params.curve_duration_h = gain->data.params.curve_duration_h;122		params.curve_type = gain->data.params.curve_type;123 124		msg->data_ptr = &params;125		msg->data_size = sizeof(params);126 127		ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);128		msg->data_ptr = NULL;129		msg->data_size = 0;130		if (ret < 0) {131			dev_err(sdev->dev, "Failed to set volume update for %s\n",132				scontrol->name);133			return ret;134		}135 136		if (all_channels_equal)137			break;138	}139 140	return 0;141}142 143static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,144				struct snd_ctl_elem_value *ucontrol)145{146	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;147	struct snd_soc_component *scomp = scontrol->scomp;148	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);149	unsigned int channels = scontrol->num_channels;150	struct snd_sof_widget *swidget;151	bool widget_found = false;152	bool change = false;153	unsigned int i;154	int ret;155 156	/* update each channel */157	for (i = 0; i < channels; i++) {158		u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],159					 scontrol->volume_table, scontrol->max + 1);160 161		change = change || (value != cdata->chanv[i].value);162		cdata->chanv[i].channel = i;163		cdata->chanv[i].value = value;164	}165 166	if (!pm_runtime_active(scomp->dev))167		return change;168 169	/* find widget associated with the control */170	list_for_each_entry(swidget, &sdev->widget_list, list) {171		if (swidget->comp_id == scontrol->comp_id) {172			widget_found = true;173			break;174		}175	}176 177	if (!widget_found) {178		dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);179		return false;180	}181 182	ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);183	if (ret < 0)184		return false;185 186	return change;187}188 189static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,190			       struct snd_ctl_elem_value *ucontrol)191{192	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;193	unsigned int channels = scontrol->num_channels;194	unsigned int i;195 196	for (i = 0; i < channels; i++)197		ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,198								scontrol->volume_table,199								scontrol->max + 1);200 201	return 0;202}203 204static int205sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev,206				  struct snd_sof_widget *swidget,207				  struct snd_sof_control *scontrol, bool lock)208{209	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;210	struct sof_ipc4_control_msg_payload *data;211	struct sof_ipc4_msg *msg = &cdata->msg;212	size_t data_size;213	unsigned int i;214	int ret;215 216	data_size = struct_size(data, chanv, scontrol->num_channels);217	data = kzalloc(data_size, GFP_KERNEL);218	if (!data)219		return -ENOMEM;220 221	data->id = cdata->index;222	data->num_elems = scontrol->num_channels;223	for (i = 0; i < scontrol->num_channels; i++) {224		data->chanv[i].channel = cdata->chanv[i].channel;225		data->chanv[i].value = cdata->chanv[i].value;226	}227 228	msg->data_ptr = data;229	msg->data_size = data_size;230 231	ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);232	msg->data_ptr = NULL;233	msg->data_size = 0;234	if (ret < 0)235		dev_err(sdev->dev, "Failed to set control update for %s\n",236			scontrol->name);237 238	kfree(data);239 240	return ret;241}242 243static void sof_ipc4_refresh_generic_control(struct snd_sof_control *scontrol)244{245	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;246	struct snd_soc_component *scomp = scontrol->scomp;247	struct sof_ipc4_control_msg_payload *data;248	struct sof_ipc4_msg *msg = &cdata->msg;249	size_t data_size;250	unsigned int i;251	int ret;252 253	if (!scontrol->comp_data_dirty)254		return;255 256	if (!pm_runtime_active(scomp->dev))257		return;258 259	data_size = struct_size(data, chanv, scontrol->num_channels);260	data = kmalloc(data_size, GFP_KERNEL);261	if (!data)262		return;263 264	data->id = cdata->index;265	data->num_elems = scontrol->num_channels;266	msg->data_ptr = data;267	msg->data_size = data_size;268 269	scontrol->comp_data_dirty = false;270	ret = sof_ipc4_set_get_kcontrol_data(scontrol, false, true);271	msg->data_ptr = NULL;272	msg->data_size = 0;273	if (!ret) {274		for (i = 0; i < scontrol->num_channels; i++) {275			cdata->chanv[i].channel = data->chanv[i].channel;276			cdata->chanv[i].value = data->chanv[i].value;277		}278	} else {279		dev_err(scomp->dev, "Failed to read control data for %s\n",280			scontrol->name);281		scontrol->comp_data_dirty = true;282	}283 284	kfree(data);285}286 287static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,288				struct snd_ctl_elem_value *ucontrol)289{290	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;291	struct snd_soc_component *scomp = scontrol->scomp;292	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);293	struct snd_sof_widget *swidget;294	bool widget_found = false;295	bool change = false;296	unsigned int i;297	u32 value;298	int ret;299 300	/* update each channel */301	for (i = 0; i < scontrol->num_channels; i++) {302		value = ucontrol->value.integer.value[i];303		change = change || (value != cdata->chanv[i].value);304		cdata->chanv[i].channel = i;305		cdata->chanv[i].value = value;306	}307 308	if (!pm_runtime_active(scomp->dev))309		return change;310 311	/* find widget associated with the control */312	list_for_each_entry(swidget, &sdev->widget_list, list) {313		if (swidget->comp_id == scontrol->comp_id) {314			widget_found = true;315			break;316		}317	}318 319	if (!widget_found) {320		dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);321		return false;322	}323 324	ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true);325	if (ret < 0)326		return false;327 328	return change;329}330 331static int sof_ipc4_switch_get(struct snd_sof_control *scontrol,332			       struct snd_ctl_elem_value *ucontrol)333{334	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;335	unsigned int i;336 337	sof_ipc4_refresh_generic_control(scontrol);338 339	/* read back each channel */340	for (i = 0; i < scontrol->num_channels; i++)341		ucontrol->value.integer.value[i] = cdata->chanv[i].value;342 343	return 0;344}345 346static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol,347			      struct snd_ctl_elem_value *ucontrol)348{349	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;350	struct snd_soc_component *scomp = scontrol->scomp;351	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);352	struct snd_sof_widget *swidget;353	bool widget_found = false;354	bool change = false;355	unsigned int i;356	u32 value;357	int ret;358 359	/* update each channel */360	for (i = 0; i < scontrol->num_channels; i++) {361		value = ucontrol->value.enumerated.item[i];362		change = change || (value != cdata->chanv[i].value);363		cdata->chanv[i].channel = i;364		cdata->chanv[i].value = value;365	}366 367	if (!pm_runtime_active(scomp->dev))368		return change;369 370	/* find widget associated with the control */371	list_for_each_entry(swidget, &sdev->widget_list, list) {372		if (swidget->comp_id == scontrol->comp_id) {373			widget_found = true;374			break;375		}376	}377 378	if (!widget_found) {379		dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);380		return false;381	}382 383	ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true);384	if (ret < 0)385		return false;386 387	return change;388}389 390static int sof_ipc4_enum_get(struct snd_sof_control *scontrol,391			     struct snd_ctl_elem_value *ucontrol)392{393	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;394	unsigned int i;395 396	sof_ipc4_refresh_generic_control(scontrol);397 398	/* read back each channel */399	for (i = 0; i < scontrol->num_channels; i++)400		ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;401 402	return 0;403}404 405static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,406				       struct snd_sof_control *scontrol,407				       bool set, bool lock)408{409	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;410	struct sof_abi_hdr *data = cdata->data;411	struct sof_ipc4_msg *msg = &cdata->msg;412	int ret = 0;413 414	/* Send the new data to the firmware only if it is powered up */415	if (set && !pm_runtime_active(sdev->dev))416		return 0;417 418	msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(data->type);419 420	msg->data_ptr = data->data;421	msg->data_size = data->size;422 423	ret = sof_ipc4_set_get_kcontrol_data(scontrol, set, lock);424	if (ret < 0)425		dev_err(sdev->dev, "Failed to %s for %s\n",426			set ? "set bytes update" : "get bytes",427			scontrol->name);428 429	msg->data_ptr = NULL;430	msg->data_size = 0;431 432	return ret;433}434 435static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol,436			      struct snd_ctl_elem_value *ucontrol)437{438	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;439	struct snd_soc_component *scomp = scontrol->scomp;440	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);441	struct sof_abi_hdr *data = cdata->data;442	size_t size;443 444	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {445		dev_err_ratelimited(scomp->dev,446				    "data max %zu exceeds ucontrol data array size\n",447				    scontrol->max_size);448		return -EINVAL;449	}450 451	/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */452	if (data->size > scontrol->max_size - sizeof(*data)) {453		dev_err_ratelimited(scomp->dev,454				    "data size too big %u bytes max is %zu\n",455				    data->size, scontrol->max_size - sizeof(*data));456		return -EINVAL;457	}458 459	size = data->size + sizeof(*data);460 461	/* copy from kcontrol */462	memcpy(data, ucontrol->value.bytes.data, size);463 464	sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);465 466	return 0;467}468 469static int sof_ipc4_bytes_get(struct snd_sof_control *scontrol,470			      struct snd_ctl_elem_value *ucontrol)471{472	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;473	struct snd_soc_component *scomp = scontrol->scomp;474	struct sof_abi_hdr *data = cdata->data;475	size_t size;476 477	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {478		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",479				    scontrol->max_size);480		return -EINVAL;481	}482 483	if (data->size > scontrol->max_size - sizeof(*data)) {484		dev_err_ratelimited(scomp->dev,485				    "%u bytes of control data is invalid, max is %zu\n",486				    data->size, scontrol->max_size - sizeof(*data));487		return -EINVAL;488	}489 490	size = data->size + sizeof(*data);491 492	/* copy back to kcontrol */493	memcpy(ucontrol->value.bytes.data, data, size);494 495	return 0;496}497 498static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol,499				  const unsigned int __user *binary_data,500				  unsigned int size)501{502	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;503	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;504	struct snd_soc_component *scomp = scontrol->scomp;505	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);506	struct sof_abi_hdr *data = cdata->data;507	struct sof_abi_hdr abi_hdr;508	struct snd_ctl_tlv header;509 510	/*511	 * The beginning of bytes data contains a header from where512	 * the length (as bytes) is needed to know the correct copy513	 * length of data from tlvd->tlv.514	 */515	if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))516		return -EFAULT;517 518	/* make sure TLV info is consistent */519	if (header.length + sizeof(struct snd_ctl_tlv) > size) {520		dev_err_ratelimited(scomp->dev,521				    "Inconsistent TLV, data %d + header %zu > %d\n",522				    header.length, sizeof(struct snd_ctl_tlv), size);523		return -EINVAL;524	}525 526	/* be->max is coming from topology */527	if (header.length > scontrol->max_size) {528		dev_err_ratelimited(scomp->dev,529				    "Bytes data size %d exceeds max %zu\n",530				    header.length, scontrol->max_size);531		return -EINVAL;532	}533 534	/* Verify the ABI header first */535	if (copy_from_user(&abi_hdr, tlvd->tlv, sizeof(abi_hdr)))536		return -EFAULT;537 538	if (abi_hdr.magic != SOF_IPC4_ABI_MAGIC) {539		dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n",540				    abi_hdr.magic);541		return -EINVAL;542	}543 544	if (abi_hdr.size > scontrol->max_size - sizeof(abi_hdr)) {545		dev_err_ratelimited(scomp->dev,546				    "%u bytes of control data is invalid, max is %zu\n",547				    abi_hdr.size, scontrol->max_size - sizeof(abi_hdr));548		return -EINVAL;549	}550 551	if (!scontrol->old_ipc_control_data) {552		/* Create a backup of the current, valid bytes control */553		scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data,554							 scontrol->max_size, GFP_KERNEL);555		if (!scontrol->old_ipc_control_data)556			return -ENOMEM;557	}558 559	/* Copy the whole binary data which includes the ABI header and the payload */560	if (copy_from_user(data, tlvd->tlv, header.length)) {561		memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data,562		       scontrol->max_size);563		kfree(scontrol->old_ipc_control_data);564		scontrol->old_ipc_control_data = NULL;565		return -EFAULT;566	}567 568	return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true);569}570 571static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,572				   const unsigned int __user *binary_data,573				   unsigned int size, bool from_dsp)574{575	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;576	struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;577	struct snd_soc_component *scomp = scontrol->scomp;578	struct sof_abi_hdr *data = cdata->data;579	struct snd_ctl_tlv header;580	size_t data_size;581 582	/*583	 * Decrement the limit by ext bytes header size to ensure the user space584	 * buffer is not exceeded.585	 */586	if (size < sizeof(struct snd_ctl_tlv))587		return -ENOSPC;588 589	size -= sizeof(struct snd_ctl_tlv);590 591	/* get all the component data from DSP */592	if (from_dsp) {593		struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);594		int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true);595 596		if (ret < 0)597			return ret;598 599		/* Set the ABI magic (if the control is not initialized) */600		data->magic = SOF_IPC4_ABI_MAGIC;601	}602 603	if (data->size > scontrol->max_size - sizeof(*data)) {604		dev_err_ratelimited(scomp->dev,605				    "%u bytes of control data is invalid, max is %zu\n",606				    data->size, scontrol->max_size - sizeof(*data));607		return -EINVAL;608	}609 610	data_size = data->size + sizeof(struct sof_abi_hdr);611 612	/* make sure we don't exceed size provided by user space for data */613	if (data_size > size)614		return -ENOSPC;615 616	header.numid = scontrol->comp_id;617	header.length = data_size;618 619	if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))620		return -EFAULT;621 622	if (copy_to_user(tlvd->tlv, data, data_size))623		return -EFAULT;624 625	return 0;626}627 628static int sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol,629				  const unsigned int __user *binary_data,630				  unsigned int size)631{632	return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, false);633}634 635static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol,636					   const unsigned int __user *binary_data,637					   unsigned int size)638{639	return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true);640}641 642static int643sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,644		     struct snd_sof_control *scontrol)645{646	if (scontrol->max == 1)647		return sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, false);648 649	return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);650}651 652#define PARAM_ID_FROM_EXTENSION(_ext)	(((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK)	\653					 >> SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT)654 655static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message)656{657	struct sof_ipc4_msg *ipc4_msg = ipc_message;658	struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr;659	struct sof_ipc4_control_msg_payload *msg_data;660	struct sof_ipc4_control_data *cdata;661	struct snd_soc_dapm_widget *widget;662	struct snd_sof_control *scontrol;663	struct snd_sof_widget *swidget;664	struct snd_kcontrol *kc = NULL;665	bool scontrol_found = false;666	u32 event_param_id;667	int i, type;668 669	if (ndata->event_data_size < sizeof(*msg_data)) {670		dev_err(sdev->dev,671			"%s: Invalid event data size for module %u.%u: %u\n",672			__func__, ndata->module_id, ndata->instance_id,673			ndata->event_data_size);674		return;675	}676 677	event_param_id = ndata->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK;678	switch (event_param_id) {679	case SOF_IPC4_SWITCH_CONTROL_PARAM_ID:680		type = SND_SOC_TPLG_TYPE_MIXER;681		break;682	case SOF_IPC4_ENUM_CONTROL_PARAM_ID:683		type = SND_SOC_TPLG_TYPE_ENUM;684		break;685	default:686		dev_err(sdev->dev,687			"%s: Invalid control type for module %u.%u: %u\n",688			__func__, ndata->module_id, ndata->instance_id,689			event_param_id);690		return;691	}692 693	/* Find the swidget based on ndata->module_id and ndata->instance_id */694	swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id,695					       ndata->instance_id);696	if (!swidget) {697		dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n",698			__func__, ndata->module_id, ndata->instance_id);699		return;700	}701 702	/* Find the scontrol which is the source of the notification */703	msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data;704	list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {705		if (scontrol->comp_id == swidget->comp_id) {706			u32 local_param_id;707 708			cdata = scontrol->ipc_control_data;709			/*710			 * The scontrol's param_id is stored in the IPC message711			 * template's extension712			 */713			local_param_id = PARAM_ID_FROM_EXTENSION(cdata->msg.extension);714			if (local_param_id == event_param_id &&715			    msg_data->id == cdata->index) {716				scontrol_found = true;717				break;718			}719		}720	}721 722	if (!scontrol_found) {723		dev_err(sdev->dev,724			"%s: Failed to find control on widget %s: %u:%u\n",725			__func__, swidget->widget->name, ndata->event_id & 0xffff,726			msg_data->id);727		return;728	}729 730	if (msg_data->num_elems) {731		/*732		 * The message includes the updated value/data, update the733		 * control's local cache using the received notification734		 */735		for (i = 0; i < msg_data->num_elems; i++) {736			u32 channel = msg_data->chanv[i].channel;737 738			if (channel >= scontrol->num_channels) {739				dev_warn(sdev->dev,740					 "Invalid channel index for %s: %u\n",741					 scontrol->name, i);742 743				/*744				 * Mark the scontrol as dirty to force a refresh745				 * on next read746				 */747				scontrol->comp_data_dirty = true;748				break;749			}750 751			cdata->chanv[channel].value = msg_data->chanv[i].value;752		}753	} else {754		/*755		 * Mark the scontrol as dirty because the value/data is changed756		 * in firmware, forcing a refresh on next read access757		 */758		scontrol->comp_data_dirty = true;759	}760 761	/*762	 * Look up the ALSA kcontrol of the scontrol to be able to send a763	 * notification to user space764	 */765	widget = swidget->widget;766	for (i = 0; i < widget->num_kcontrols; i++) {767		/* skip non matching types or non matching indexes within type */768		if (widget->dobj.widget.kcontrol_type[i] == type &&769		    widget->kcontrol_news[i].index == cdata->index) {770			kc = widget->kcontrols[i];771			break;772		}773	}774 775	if (!kc)776		return;777 778	snd_ctl_notify_one(swidget->scomp->card->snd_card,779			   SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);780}781 782/* set up all controls for the widget */783static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)784{785	struct snd_sof_control *scontrol;786	int ret = 0;787 788	list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {789		if (scontrol->comp_id == swidget->comp_id) {790			switch (scontrol->info_type) {791			case SND_SOC_TPLG_CTL_VOLSW:792			case SND_SOC_TPLG_CTL_VOLSW_SX:793			case SND_SOC_TPLG_CTL_VOLSW_XR_SX:794				ret = sof_ipc4_volsw_setup(sdev, swidget, scontrol);795				break;796			case SND_SOC_TPLG_CTL_BYTES:797				ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,798								  true, false);799				break;800			case SND_SOC_TPLG_CTL_ENUM:801			case SND_SOC_TPLG_CTL_ENUM_VALUE:802				ret = sof_ipc4_set_generic_control_data(sdev, swidget,803									scontrol, false);804				break;805			default:806				break;807			}808 809			if (ret < 0) {810				dev_err(sdev->dev,811					"kcontrol %d set up failed for widget %s\n",812					scontrol->comp_id, swidget->widget->name);813				return ret;814			}815		}816	}817 818	return 0;819}820 821static int822sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)823{824	int i;825 826	/* init the volume table */827	scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);828	if (!scontrol->volume_table)829		return -ENOMEM;830 831	/* populate the volume table */832	for (i = 0; i < size ; i++) {833		u32 val = vol_compute_gain(i, tlv);834		u64 q31val = ((u64)val) << 15; /* Can be over Q1.31, need to saturate */835 836		scontrol->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ?837						SOF_IPC4_VOL_ZERO_DB : q31val;838	}839 840	return 0;841}842 843const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {844	.volume_put = sof_ipc4_volume_put,845	.volume_get = sof_ipc4_volume_get,846	.switch_put = sof_ipc4_switch_put,847	.switch_get = sof_ipc4_switch_get,848	.enum_put = sof_ipc4_enum_put,849	.enum_get = sof_ipc4_enum_get,850	.bytes_put = sof_ipc4_bytes_put,851	.bytes_get = sof_ipc4_bytes_get,852	.bytes_ext_put = sof_ipc4_bytes_ext_put,853	.bytes_ext_get = sof_ipc4_bytes_ext_get,854	.bytes_ext_volatile_get = sof_ipc4_bytes_ext_volatile_get,855	.update = sof_ipc4_control_update,856	.widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,857	.set_up_volume_table = sof_ipc4_set_up_volume_table,858};859