xref: /linux/sound/soc/sof/ipc3-control.c (revision 66da65005aa819e0b8d3a08f5ec1491b7690cb67)
110f461d7SRanjani Sridharan // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
210f461d7SRanjani Sridharan //
310f461d7SRanjani Sridharan // This file is provided under a dual BSD/GPLv2 license.  When using or
410f461d7SRanjani Sridharan // redistributing this file, you may do so under either license.
510f461d7SRanjani Sridharan //
610f461d7SRanjani Sridharan // Copyright(c) 2021 Intel Corporation. All rights reserved.
710f461d7SRanjani Sridharan //
810f461d7SRanjani Sridharan //
910f461d7SRanjani Sridharan 
1010f461d7SRanjani Sridharan #include "sof-priv.h"
1110f461d7SRanjani Sridharan #include "sof-audio.h"
12f80beaf6SPeter Ujfalusi #include "ipc3-priv.h"
1310f461d7SRanjani Sridharan 
14e760f102SPeter Ujfalusi /* IPC set()/get() for kcontrols. */
15e760f102SPeter Ujfalusi static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
16e760f102SPeter Ujfalusi {
17e760f102SPeter Ujfalusi 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
18e760f102SPeter Ujfalusi 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
19e760f102SPeter Ujfalusi 	const struct sof_ipc_ops *iops = sdev->ipc->ops;
20e760f102SPeter Ujfalusi 	enum sof_ipc_ctrl_type ctrl_type;
21e760f102SPeter Ujfalusi 	struct snd_sof_widget *swidget;
22e760f102SPeter Ujfalusi 	bool widget_found = false;
23e760f102SPeter Ujfalusi 	u32 ipc_cmd, msg_bytes;
24e760f102SPeter Ujfalusi 
25e760f102SPeter Ujfalusi 	list_for_each_entry(swidget, &sdev->widget_list, list) {
26e760f102SPeter Ujfalusi 		if (swidget->comp_id == scontrol->comp_id) {
27e760f102SPeter Ujfalusi 			widget_found = true;
28e760f102SPeter Ujfalusi 			break;
29e760f102SPeter Ujfalusi 		}
30e760f102SPeter Ujfalusi 	}
31e760f102SPeter Ujfalusi 
32e760f102SPeter Ujfalusi 	if (!widget_found) {
33e760f102SPeter Ujfalusi 		dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__,
34e760f102SPeter Ujfalusi 			scontrol->comp_id);
35e760f102SPeter Ujfalusi 		return -EINVAL;
36e760f102SPeter Ujfalusi 	}
37e760f102SPeter Ujfalusi 
38e760f102SPeter Ujfalusi 	/*
39e760f102SPeter Ujfalusi 	 * Volatile controls should always be part of static pipelines and the widget use_count
40e760f102SPeter Ujfalusi 	 * would always be > 0 in this case. For the others, just return the cached value if the
41e760f102SPeter Ujfalusi 	 * widget is not set up.
42e760f102SPeter Ujfalusi 	 */
43e760f102SPeter Ujfalusi 	if (!swidget->use_count)
44e760f102SPeter Ujfalusi 		return 0;
45e760f102SPeter Ujfalusi 
46e760f102SPeter Ujfalusi 	/*
47e760f102SPeter Ujfalusi 	 * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
48e760f102SPeter Ujfalusi 	 * direction
49e760f102SPeter Ujfalusi 	 * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently
50e760f102SPeter Ujfalusi 	 *	 for ctrl_type
51e760f102SPeter Ujfalusi 	 */
52e760f102SPeter Ujfalusi 	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
53e760f102SPeter Ujfalusi 		ipc_cmd = set ? SOF_IPC_COMP_SET_DATA : SOF_IPC_COMP_GET_DATA;
54e760f102SPeter Ujfalusi 		ctrl_type = set ? SOF_CTRL_TYPE_DATA_SET : SOF_CTRL_TYPE_DATA_GET;
55e760f102SPeter Ujfalusi 	} else {
56e760f102SPeter Ujfalusi 		ipc_cmd = set ? SOF_IPC_COMP_SET_VALUE : SOF_IPC_COMP_GET_VALUE;
57e760f102SPeter Ujfalusi 		ctrl_type = set ? SOF_CTRL_TYPE_VALUE_CHAN_SET : SOF_CTRL_TYPE_VALUE_CHAN_GET;
58e760f102SPeter Ujfalusi 	}
59e760f102SPeter Ujfalusi 
60e760f102SPeter Ujfalusi 	cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
61e760f102SPeter Ujfalusi 	cdata->type = ctrl_type;
62e760f102SPeter Ujfalusi 	cdata->comp_id = scontrol->comp_id;
63e760f102SPeter Ujfalusi 	cdata->msg_index = 0;
64e760f102SPeter Ujfalusi 
65e760f102SPeter Ujfalusi 	/* calculate header and data size */
66e760f102SPeter Ujfalusi 	switch (cdata->type) {
67e760f102SPeter Ujfalusi 	case SOF_CTRL_TYPE_VALUE_CHAN_GET:
68e760f102SPeter Ujfalusi 	case SOF_CTRL_TYPE_VALUE_CHAN_SET:
69e760f102SPeter Ujfalusi 		cdata->num_elems = scontrol->num_channels;
70e760f102SPeter Ujfalusi 
71e760f102SPeter Ujfalusi 		msg_bytes = scontrol->num_channels *
72e760f102SPeter Ujfalusi 			    sizeof(struct sof_ipc_ctrl_value_chan);
73e760f102SPeter Ujfalusi 		msg_bytes += sizeof(struct sof_ipc_ctrl_data);
74e760f102SPeter Ujfalusi 		break;
75e760f102SPeter Ujfalusi 	case SOF_CTRL_TYPE_DATA_GET:
76e760f102SPeter Ujfalusi 	case SOF_CTRL_TYPE_DATA_SET:
77e760f102SPeter Ujfalusi 		cdata->num_elems = cdata->data->size;
78e760f102SPeter Ujfalusi 
79e760f102SPeter Ujfalusi 		msg_bytes = cdata->data->size;
80e760f102SPeter Ujfalusi 		msg_bytes += sizeof(struct sof_ipc_ctrl_data) +
81e760f102SPeter Ujfalusi 			     sizeof(struct sof_abi_hdr);
82e760f102SPeter Ujfalusi 		break;
83e760f102SPeter Ujfalusi 	default:
84e760f102SPeter Ujfalusi 		return -EINVAL;
85e760f102SPeter Ujfalusi 	}
86e760f102SPeter Ujfalusi 
87e760f102SPeter Ujfalusi 	cdata->rhdr.hdr.size = msg_bytes;
88e760f102SPeter Ujfalusi 	cdata->elems_remaining = 0;
89e760f102SPeter Ujfalusi 
90e760f102SPeter Ujfalusi 	return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
91e760f102SPeter Ujfalusi }
92e760f102SPeter Ujfalusi 
93838d04f3SRanjani Sridharan static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
94838d04f3SRanjani Sridharan {
95838d04f3SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
96838d04f3SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
97838d04f3SRanjani Sridharan 	int ret;
98838d04f3SRanjani Sridharan 
99838d04f3SRanjani Sridharan 	if (!scontrol->comp_data_dirty)
100838d04f3SRanjani Sridharan 		return;
101838d04f3SRanjani Sridharan 
102838d04f3SRanjani Sridharan 	if (!pm_runtime_active(scomp->dev))
103838d04f3SRanjani Sridharan 		return;
104838d04f3SRanjani Sridharan 
105838d04f3SRanjani Sridharan 	/* set the ABI header values */
106838d04f3SRanjani Sridharan 	cdata->data->magic = SOF_ABI_MAGIC;
107838d04f3SRanjani Sridharan 	cdata->data->abi = SOF_ABI_VERSION;
108838d04f3SRanjani Sridharan 
109838d04f3SRanjani Sridharan 	/* refresh the component data from DSP */
110838d04f3SRanjani Sridharan 	scontrol->comp_data_dirty = false;
111e760f102SPeter Ujfalusi 	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
112838d04f3SRanjani Sridharan 	if (ret < 0) {
113838d04f3SRanjani Sridharan 		dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
114838d04f3SRanjani Sridharan 
115838d04f3SRanjani Sridharan 		/* Set the flag to re-try next time to get the data */
116838d04f3SRanjani Sridharan 		scontrol->comp_data_dirty = true;
117838d04f3SRanjani Sridharan 	}
118838d04f3SRanjani Sridharan }
119838d04f3SRanjani Sridharan 
120838d04f3SRanjani Sridharan static int sof_ipc3_volume_get(struct snd_sof_control *scontrol,
121838d04f3SRanjani Sridharan 			       struct snd_ctl_elem_value *ucontrol)
122838d04f3SRanjani Sridharan {
123838d04f3SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
124838d04f3SRanjani Sridharan 	unsigned int channels = scontrol->num_channels;
125838d04f3SRanjani Sridharan 	unsigned int i;
126838d04f3SRanjani Sridharan 
127838d04f3SRanjani Sridharan 	snd_sof_refresh_control(scontrol);
128838d04f3SRanjani Sridharan 
129838d04f3SRanjani Sridharan 	/* read back each channel */
130838d04f3SRanjani Sridharan 	for (i = 0; i < channels; i++)
131838d04f3SRanjani Sridharan 		ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,
132838d04f3SRanjani Sridharan 								scontrol->volume_table,
133838d04f3SRanjani Sridharan 								scontrol->max + 1);
134838d04f3SRanjani Sridharan 
135838d04f3SRanjani Sridharan 	return 0;
136838d04f3SRanjani Sridharan }
137838d04f3SRanjani Sridharan 
138838d04f3SRanjani Sridharan static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
139838d04f3SRanjani Sridharan 				struct snd_ctl_elem_value *ucontrol)
140838d04f3SRanjani Sridharan {
141838d04f3SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
142838d04f3SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
143838d04f3SRanjani Sridharan 	unsigned int channels = scontrol->num_channels;
144838d04f3SRanjani Sridharan 	unsigned int i;
145838d04f3SRanjani Sridharan 	bool change = false;
146838d04f3SRanjani Sridharan 
147838d04f3SRanjani Sridharan 	/* update each channel */
148838d04f3SRanjani Sridharan 	for (i = 0; i < channels; i++) {
149838d04f3SRanjani Sridharan 		u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],
150838d04f3SRanjani Sridharan 					 scontrol->volume_table, scontrol->max + 1);
151838d04f3SRanjani Sridharan 
152838d04f3SRanjani Sridharan 		change = change || (value != cdata->chanv[i].value);
153838d04f3SRanjani Sridharan 		cdata->chanv[i].channel = i;
154838d04f3SRanjani Sridharan 		cdata->chanv[i].value = value;
155838d04f3SRanjani Sridharan 	}
156838d04f3SRanjani Sridharan 
157838d04f3SRanjani Sridharan 	/* notify DSP of mixer updates */
158838d04f3SRanjani Sridharan 	if (pm_runtime_active(scomp->dev)) {
159e760f102SPeter Ujfalusi 		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
160838d04f3SRanjani Sridharan 
161838d04f3SRanjani Sridharan 		if (ret < 0) {
162838d04f3SRanjani Sridharan 			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
163838d04f3SRanjani Sridharan 				scontrol->name);
164838d04f3SRanjani Sridharan 			return false;
165838d04f3SRanjani Sridharan 		}
166838d04f3SRanjani Sridharan 	}
167838d04f3SRanjani Sridharan 
168838d04f3SRanjani Sridharan 	return change;
169838d04f3SRanjani Sridharan }
170838d04f3SRanjani Sridharan 
171a6668746SRanjani Sridharan static int sof_ipc3_switch_get(struct snd_sof_control *scontrol,
172a6668746SRanjani Sridharan 			       struct snd_ctl_elem_value *ucontrol)
173a6668746SRanjani Sridharan {
174a6668746SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
175a6668746SRanjani Sridharan 	unsigned int channels = scontrol->num_channels;
176a6668746SRanjani Sridharan 	unsigned int i;
177a6668746SRanjani Sridharan 
178a6668746SRanjani Sridharan 	snd_sof_refresh_control(scontrol);
179a6668746SRanjani Sridharan 
180a6668746SRanjani Sridharan 	/* read back each channel */
181a6668746SRanjani Sridharan 	for (i = 0; i < channels; i++)
182a6668746SRanjani Sridharan 		ucontrol->value.integer.value[i] = cdata->chanv[i].value;
183a6668746SRanjani Sridharan 
184a6668746SRanjani Sridharan 	return 0;
185a6668746SRanjani Sridharan }
186a6668746SRanjani Sridharan 
187a6668746SRanjani Sridharan static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
188a6668746SRanjani Sridharan 				struct snd_ctl_elem_value *ucontrol)
189a6668746SRanjani Sridharan {
190a6668746SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
191a6668746SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
192a6668746SRanjani Sridharan 	unsigned int channels = scontrol->num_channels;
193a6668746SRanjani Sridharan 	unsigned int i;
194a6668746SRanjani Sridharan 	bool change = false;
195a6668746SRanjani Sridharan 	u32 value;
196a6668746SRanjani Sridharan 
197a6668746SRanjani Sridharan 	/* update each channel */
198a6668746SRanjani Sridharan 	for (i = 0; i < channels; i++) {
199a6668746SRanjani Sridharan 		value = ucontrol->value.integer.value[i];
200a6668746SRanjani Sridharan 		change = change || (value != cdata->chanv[i].value);
201a6668746SRanjani Sridharan 		cdata->chanv[i].channel = i;
202a6668746SRanjani Sridharan 		cdata->chanv[i].value = value;
203a6668746SRanjani Sridharan 	}
204a6668746SRanjani Sridharan 
205a6668746SRanjani Sridharan 	/* notify DSP of mixer updates */
206a6668746SRanjani Sridharan 	if (pm_runtime_active(scomp->dev)) {
207e760f102SPeter Ujfalusi 		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
208a6668746SRanjani Sridharan 
209a6668746SRanjani Sridharan 		if (ret < 0) {
210a6668746SRanjani Sridharan 			dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
211a6668746SRanjani Sridharan 				scontrol->name);
212a6668746SRanjani Sridharan 			return false;
213a6668746SRanjani Sridharan 		}
214a6668746SRanjani Sridharan 	}
215a6668746SRanjani Sridharan 
216a6668746SRanjani Sridharan 	return change;
217a6668746SRanjani Sridharan }
218a6668746SRanjani Sridharan 
219049307aaSRanjani Sridharan static int sof_ipc3_enum_get(struct snd_sof_control *scontrol,
220049307aaSRanjani Sridharan 			     struct snd_ctl_elem_value *ucontrol)
221049307aaSRanjani Sridharan {
222049307aaSRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
223049307aaSRanjani Sridharan 	unsigned int channels = scontrol->num_channels;
224049307aaSRanjani Sridharan 	unsigned int i;
225049307aaSRanjani Sridharan 
226049307aaSRanjani Sridharan 	snd_sof_refresh_control(scontrol);
227049307aaSRanjani Sridharan 
228049307aaSRanjani Sridharan 	/* read back each channel */
229049307aaSRanjani Sridharan 	for (i = 0; i < channels; i++)
230049307aaSRanjani Sridharan 		ucontrol->value.enumerated.item[i] = cdata->chanv[i].value;
231049307aaSRanjani Sridharan 
232049307aaSRanjani Sridharan 	return 0;
233049307aaSRanjani Sridharan }
234049307aaSRanjani Sridharan 
235049307aaSRanjani Sridharan static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
236049307aaSRanjani Sridharan 			      struct snd_ctl_elem_value *ucontrol)
237049307aaSRanjani Sridharan {
238049307aaSRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
239049307aaSRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
240049307aaSRanjani Sridharan 	unsigned int channels = scontrol->num_channels;
241049307aaSRanjani Sridharan 	unsigned int i;
242049307aaSRanjani Sridharan 	bool change = false;
243049307aaSRanjani Sridharan 	u32 value;
244049307aaSRanjani Sridharan 
245049307aaSRanjani Sridharan 	/* update each channel */
246049307aaSRanjani Sridharan 	for (i = 0; i < channels; i++) {
247049307aaSRanjani Sridharan 		value = ucontrol->value.enumerated.item[i];
248049307aaSRanjani Sridharan 		change = change || (value != cdata->chanv[i].value);
249049307aaSRanjani Sridharan 		cdata->chanv[i].channel = i;
250049307aaSRanjani Sridharan 		cdata->chanv[i].value = value;
251049307aaSRanjani Sridharan 	}
252049307aaSRanjani Sridharan 
253049307aaSRanjani Sridharan 	/* notify DSP of enum updates */
254049307aaSRanjani Sridharan 	if (pm_runtime_active(scomp->dev)) {
255e760f102SPeter Ujfalusi 		int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
256049307aaSRanjani Sridharan 
257049307aaSRanjani Sridharan 		if (ret < 0) {
258049307aaSRanjani Sridharan 			dev_err(scomp->dev, "Failed to set enum updates for %s\n",
259049307aaSRanjani Sridharan 				scontrol->name);
260049307aaSRanjani Sridharan 			return false;
261049307aaSRanjani Sridharan 		}
262049307aaSRanjani Sridharan 	}
263049307aaSRanjani Sridharan 
264049307aaSRanjani Sridharan 	return change;
265049307aaSRanjani Sridharan }
266049307aaSRanjani Sridharan 
267544ac885SRanjani Sridharan static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol,
268544ac885SRanjani Sridharan 			      struct snd_ctl_elem_value *ucontrol)
269544ac885SRanjani Sridharan {
270544ac885SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
271544ac885SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
272544ac885SRanjani Sridharan 	struct sof_abi_hdr *data = cdata->data;
273544ac885SRanjani Sridharan 	size_t size;
274544ac885SRanjani Sridharan 
275544ac885SRanjani Sridharan 	snd_sof_refresh_control(scontrol);
276544ac885SRanjani Sridharan 
277544ac885SRanjani Sridharan 	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
278544ac885SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
279544ac885SRanjani Sridharan 				    scontrol->max_size);
280544ac885SRanjani Sridharan 		return -EINVAL;
281544ac885SRanjani Sridharan 	}
282544ac885SRanjani Sridharan 
283544ac885SRanjani Sridharan 	/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
284544ac885SRanjani Sridharan 	if (data->size > scontrol->max_size - sizeof(*data)) {
285544ac885SRanjani Sridharan 		dev_err_ratelimited(scomp->dev,
286544ac885SRanjani Sridharan 				    "%u bytes of control data is invalid, max is %zu\n",
287544ac885SRanjani Sridharan 				    data->size, scontrol->max_size - sizeof(*data));
288544ac885SRanjani Sridharan 		return -EINVAL;
289544ac885SRanjani Sridharan 	}
290544ac885SRanjani Sridharan 
291544ac885SRanjani Sridharan 	size = data->size + sizeof(*data);
292544ac885SRanjani Sridharan 
293544ac885SRanjani Sridharan 	/* copy back to kcontrol */
294544ac885SRanjani Sridharan 	memcpy(ucontrol->value.bytes.data, data, size);
295544ac885SRanjani Sridharan 
296544ac885SRanjani Sridharan 	return 0;
297544ac885SRanjani Sridharan }
298544ac885SRanjani Sridharan 
299544ac885SRanjani Sridharan static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
300544ac885SRanjani Sridharan 			      struct snd_ctl_elem_value *ucontrol)
301544ac885SRanjani Sridharan {
302544ac885SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
303544ac885SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
304544ac885SRanjani Sridharan 	struct sof_abi_hdr *data = cdata->data;
305544ac885SRanjani Sridharan 	size_t size;
306544ac885SRanjani Sridharan 
307544ac885SRanjani Sridharan 	if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) {
308544ac885SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n",
309544ac885SRanjani Sridharan 				    scontrol->max_size);
310544ac885SRanjani Sridharan 		return -EINVAL;
311544ac885SRanjani Sridharan 	}
312544ac885SRanjani Sridharan 
313544ac885SRanjani Sridharan 	/* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */
314544ac885SRanjani Sridharan 	if (data->size > scontrol->max_size - sizeof(*data)) {
315544ac885SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n",
316544ac885SRanjani Sridharan 				    data->size, scontrol->max_size - sizeof(*data));
317544ac885SRanjani Sridharan 		return -EINVAL;
318544ac885SRanjani Sridharan 	}
319544ac885SRanjani Sridharan 
320544ac885SRanjani Sridharan 	size = data->size + sizeof(*data);
321544ac885SRanjani Sridharan 
322544ac885SRanjani Sridharan 	/* copy from kcontrol */
323544ac885SRanjani Sridharan 	memcpy(data, ucontrol->value.bytes.data, size);
324544ac885SRanjani Sridharan 
325544ac885SRanjani Sridharan 	/* notify DSP of byte control updates */
326544ac885SRanjani Sridharan 	if (pm_runtime_active(scomp->dev))
327e760f102SPeter Ujfalusi 		return sof_ipc3_set_get_kcontrol_data(scontrol, true);
328544ac885SRanjani Sridharan 
329544ac885SRanjani Sridharan 	return 0;
330544ac885SRanjani Sridharan }
331544ac885SRanjani Sridharan 
33267ec2a09SRanjani Sridharan static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol,
33367ec2a09SRanjani Sridharan 				  const unsigned int __user *binary_data, unsigned int size)
33467ec2a09SRanjani Sridharan {
33567ec2a09SRanjani Sridharan 	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
33667ec2a09SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
33767ec2a09SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
33867ec2a09SRanjani Sridharan 	struct snd_ctl_tlv header;
33967ec2a09SRanjani Sridharan 	size_t data_size;
34067ec2a09SRanjani Sridharan 
34167ec2a09SRanjani Sridharan 	snd_sof_refresh_control(scontrol);
34267ec2a09SRanjani Sridharan 
34367ec2a09SRanjani Sridharan 	/*
34467ec2a09SRanjani Sridharan 	 * Decrement the limit by ext bytes header size to
34567ec2a09SRanjani Sridharan 	 * ensure the user space buffer is not exceeded.
34667ec2a09SRanjani Sridharan 	 */
34767ec2a09SRanjani Sridharan 	if (size < sizeof(struct snd_ctl_tlv))
34867ec2a09SRanjani Sridharan 		return -ENOSPC;
34967ec2a09SRanjani Sridharan 
35067ec2a09SRanjani Sridharan 	size -= sizeof(struct snd_ctl_tlv);
35167ec2a09SRanjani Sridharan 
35267ec2a09SRanjani Sridharan 	/* set the ABI header values */
35367ec2a09SRanjani Sridharan 	cdata->data->magic = SOF_ABI_MAGIC;
35467ec2a09SRanjani Sridharan 	cdata->data->abi = SOF_ABI_VERSION;
35567ec2a09SRanjani Sridharan 
35667ec2a09SRanjani Sridharan 	/* check data size doesn't exceed max coming from topology */
35767ec2a09SRanjani Sridharan 	if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
35867ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
35967ec2a09SRanjani Sridharan 				    cdata->data->size,
36067ec2a09SRanjani Sridharan 				    scontrol->max_size - sizeof(struct sof_abi_hdr));
36167ec2a09SRanjani Sridharan 		return -EINVAL;
36267ec2a09SRanjani Sridharan 	}
36367ec2a09SRanjani Sridharan 
36467ec2a09SRanjani Sridharan 	data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
36567ec2a09SRanjani Sridharan 
36667ec2a09SRanjani Sridharan 	/* make sure we don't exceed size provided by user space for data */
36767ec2a09SRanjani Sridharan 	if (data_size > size)
36867ec2a09SRanjani Sridharan 		return -ENOSPC;
36967ec2a09SRanjani Sridharan 
37067ec2a09SRanjani Sridharan 	header.numid = cdata->cmd;
37167ec2a09SRanjani Sridharan 	header.length = data_size;
37267ec2a09SRanjani Sridharan 	if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
37367ec2a09SRanjani Sridharan 		return -EFAULT;
37467ec2a09SRanjani Sridharan 
37567ec2a09SRanjani Sridharan 	if (copy_to_user(tlvd->tlv, cdata->data, data_size))
37667ec2a09SRanjani Sridharan 		return -EFAULT;
37767ec2a09SRanjani Sridharan 
37867ec2a09SRanjani Sridharan 	return 0;
37967ec2a09SRanjani Sridharan }
38067ec2a09SRanjani Sridharan 
38167ec2a09SRanjani Sridharan static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
38267ec2a09SRanjani Sridharan 				  const unsigned int __user *binary_data,
38367ec2a09SRanjani Sridharan 				  unsigned int size)
38467ec2a09SRanjani Sridharan {
38567ec2a09SRanjani Sridharan 	const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data;
38667ec2a09SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
38767ec2a09SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
38867ec2a09SRanjani Sridharan 	struct snd_ctl_tlv header;
38967ec2a09SRanjani Sridharan 
39067ec2a09SRanjani Sridharan 	/*
39167ec2a09SRanjani Sridharan 	 * The beginning of bytes data contains a header from where
39267ec2a09SRanjani Sridharan 	 * the length (as bytes) is needed to know the correct copy
39367ec2a09SRanjani Sridharan 	 * length of data from tlvd->tlv.
39467ec2a09SRanjani Sridharan 	 */
39567ec2a09SRanjani Sridharan 	if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv)))
39667ec2a09SRanjani Sridharan 		return -EFAULT;
39767ec2a09SRanjani Sridharan 
39867ec2a09SRanjani Sridharan 	/* make sure TLV info is consistent */
39967ec2a09SRanjani Sridharan 	if (header.length + sizeof(struct snd_ctl_tlv) > size) {
40067ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n",
40167ec2a09SRanjani Sridharan 				    header.length, sizeof(struct snd_ctl_tlv), size);
40267ec2a09SRanjani Sridharan 		return -EINVAL;
40367ec2a09SRanjani Sridharan 	}
40467ec2a09SRanjani Sridharan 
40567ec2a09SRanjani Sridharan 	/* be->max is coming from topology */
40667ec2a09SRanjani Sridharan 	if (header.length > scontrol->max_size) {
40767ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n",
40867ec2a09SRanjani Sridharan 				    header.length, scontrol->max_size);
40967ec2a09SRanjani Sridharan 		return -EINVAL;
41067ec2a09SRanjani Sridharan 	}
41167ec2a09SRanjani Sridharan 
41267ec2a09SRanjani Sridharan 	/* Check that header id matches the command */
41367ec2a09SRanjani Sridharan 	if (header.numid != cdata->cmd) {
41467ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n",
41567ec2a09SRanjani Sridharan 				    header.numid);
41667ec2a09SRanjani Sridharan 		return -EINVAL;
41767ec2a09SRanjani Sridharan 	}
41867ec2a09SRanjani Sridharan 
41967ec2a09SRanjani Sridharan 	if (copy_from_user(cdata->data, tlvd->tlv, header.length))
42067ec2a09SRanjani Sridharan 		return -EFAULT;
42167ec2a09SRanjani Sridharan 
42267ec2a09SRanjani Sridharan 	if (cdata->data->magic != SOF_ABI_MAGIC) {
42367ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic);
42467ec2a09SRanjani Sridharan 		return -EINVAL;
42567ec2a09SRanjani Sridharan 	}
42667ec2a09SRanjani Sridharan 
42767ec2a09SRanjani Sridharan 	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) {
42867ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n",
42967ec2a09SRanjani Sridharan 				    cdata->data->abi);
43067ec2a09SRanjani Sridharan 		return -EINVAL;
43167ec2a09SRanjani Sridharan 	}
43267ec2a09SRanjani Sridharan 
43367ec2a09SRanjani Sridharan 	/* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
43467ec2a09SRanjani Sridharan 	if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
43567ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n");
43667ec2a09SRanjani Sridharan 		return -EINVAL;
43767ec2a09SRanjani Sridharan 	}
43867ec2a09SRanjani Sridharan 
43967ec2a09SRanjani Sridharan 	/* notify DSP of byte control updates */
44067ec2a09SRanjani Sridharan 	if (pm_runtime_active(scomp->dev))
441e760f102SPeter Ujfalusi 		return sof_ipc3_set_get_kcontrol_data(scontrol, true);
44267ec2a09SRanjani Sridharan 
44367ec2a09SRanjani Sridharan 	return 0;
44467ec2a09SRanjani Sridharan }
44567ec2a09SRanjani Sridharan 
44667ec2a09SRanjani Sridharan static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
44767ec2a09SRanjani Sridharan 					   const unsigned int __user *binary_data,
44867ec2a09SRanjani Sridharan 					   unsigned int size)
44967ec2a09SRanjani Sridharan {
45067ec2a09SRanjani Sridharan 	struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data;
45167ec2a09SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
45267ec2a09SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
45367ec2a09SRanjani Sridharan 	struct snd_ctl_tlv header;
45467ec2a09SRanjani Sridharan 	size_t data_size;
45567ec2a09SRanjani Sridharan 	int ret;
45667ec2a09SRanjani Sridharan 
45767ec2a09SRanjani Sridharan 	/*
45867ec2a09SRanjani Sridharan 	 * Decrement the limit by ext bytes header size to
45967ec2a09SRanjani Sridharan 	 * ensure the user space buffer is not exceeded.
46067ec2a09SRanjani Sridharan 	 */
46167ec2a09SRanjani Sridharan 	if (size < sizeof(struct snd_ctl_tlv))
46267ec2a09SRanjani Sridharan 		return -ENOSPC;
46367ec2a09SRanjani Sridharan 
46467ec2a09SRanjani Sridharan 	size -= sizeof(struct snd_ctl_tlv);
46567ec2a09SRanjani Sridharan 
46667ec2a09SRanjani Sridharan 	/* set the ABI header values */
46767ec2a09SRanjani Sridharan 	cdata->data->magic = SOF_ABI_MAGIC;
46867ec2a09SRanjani Sridharan 	cdata->data->abi = SOF_ABI_VERSION;
46967ec2a09SRanjani Sridharan 
47067ec2a09SRanjani Sridharan 	/* get all the component data from DSP */
471e760f102SPeter Ujfalusi 	ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
47267ec2a09SRanjani Sridharan 	if (ret < 0)
47367ec2a09SRanjani Sridharan 		return ret;
47467ec2a09SRanjani Sridharan 
47567ec2a09SRanjani Sridharan 	/* check data size doesn't exceed max coming from topology */
47667ec2a09SRanjani Sridharan 	if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) {
47767ec2a09SRanjani Sridharan 		dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n",
47867ec2a09SRanjani Sridharan 				    cdata->data->size,
47967ec2a09SRanjani Sridharan 				    scontrol->max_size - sizeof(struct sof_abi_hdr));
48067ec2a09SRanjani Sridharan 		return -EINVAL;
48167ec2a09SRanjani Sridharan 	}
48267ec2a09SRanjani Sridharan 
48367ec2a09SRanjani Sridharan 	data_size = cdata->data->size + sizeof(struct sof_abi_hdr);
48467ec2a09SRanjani Sridharan 
48567ec2a09SRanjani Sridharan 	/* make sure we don't exceed size provided by user space for data */
48667ec2a09SRanjani Sridharan 	if (data_size > size)
48767ec2a09SRanjani Sridharan 		return -ENOSPC;
48867ec2a09SRanjani Sridharan 
48967ec2a09SRanjani Sridharan 	header.numid = cdata->cmd;
49067ec2a09SRanjani Sridharan 	header.length = data_size;
49167ec2a09SRanjani Sridharan 	if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv)))
49267ec2a09SRanjani Sridharan 		return -EFAULT;
49367ec2a09SRanjani Sridharan 
49467ec2a09SRanjani Sridharan 	if (copy_to_user(tlvd->tlv, cdata->data, data_size))
49567ec2a09SRanjani Sridharan 		return -EFAULT;
49667ec2a09SRanjani Sridharan 
49767ec2a09SRanjani Sridharan 	return ret;
49867ec2a09SRanjani Sridharan }
49967ec2a09SRanjani Sridharan 
50010f461d7SRanjani Sridharan static void snd_sof_update_control(struct snd_sof_control *scontrol,
50110f461d7SRanjani Sridharan 				   struct sof_ipc_ctrl_data *cdata)
50210f461d7SRanjani Sridharan {
50310f461d7SRanjani Sridharan 	struct snd_soc_component *scomp = scontrol->scomp;
50410f461d7SRanjani Sridharan 	struct sof_ipc_ctrl_data *local_cdata;
50510f461d7SRanjani Sridharan 	int i;
50610f461d7SRanjani Sridharan 
50710f461d7SRanjani Sridharan 	local_cdata = scontrol->ipc_control_data;
50810f461d7SRanjani Sridharan 
50910f461d7SRanjani Sridharan 	if (cdata->cmd == SOF_CTRL_CMD_BINARY) {
51010f461d7SRanjani Sridharan 		if (cdata->num_elems != local_cdata->data->size) {
51110f461d7SRanjani Sridharan 			dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n",
51210f461d7SRanjani Sridharan 				cdata->num_elems, local_cdata->data->size);
51310f461d7SRanjani Sridharan 			return;
51410f461d7SRanjani Sridharan 		}
51510f461d7SRanjani Sridharan 
51610f461d7SRanjani Sridharan 		/* copy the new binary data */
51710f461d7SRanjani Sridharan 		memcpy(local_cdata->data, cdata->data, cdata->num_elems);
51810f461d7SRanjani Sridharan 	} else if (cdata->num_elems != scontrol->num_channels) {
51910f461d7SRanjani Sridharan 		dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n",
52010f461d7SRanjani Sridharan 			cdata->num_elems, scontrol->num_channels);
52110f461d7SRanjani Sridharan 	} else {
52210f461d7SRanjani Sridharan 		/* copy the new values */
52310f461d7SRanjani Sridharan 		for (i = 0; i < cdata->num_elems; i++)
52410f461d7SRanjani Sridharan 			local_cdata->chanv[i].value = cdata->chanv[i].value;
52510f461d7SRanjani Sridharan 	}
52610f461d7SRanjani Sridharan }
52710f461d7SRanjani Sridharan 
52810f461d7SRanjani Sridharan static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message)
52910f461d7SRanjani Sridharan {
53010f461d7SRanjani Sridharan 	struct sof_ipc_ctrl_data *cdata = ipc_control_message;
53110f461d7SRanjani Sridharan 	struct snd_soc_dapm_widget *widget;
53210f461d7SRanjani Sridharan 	struct snd_sof_control *scontrol;
53310f461d7SRanjani Sridharan 	struct snd_sof_widget *swidget;
53410f461d7SRanjani Sridharan 	struct snd_kcontrol *kc = NULL;
53510f461d7SRanjani Sridharan 	struct soc_mixer_control *sm;
53610f461d7SRanjani Sridharan 	struct soc_bytes_ext *be;
53710f461d7SRanjani Sridharan 	size_t expected_size;
53810f461d7SRanjani Sridharan 	struct soc_enum *se;
53910f461d7SRanjani Sridharan 	bool found = false;
54010f461d7SRanjani Sridharan 	int i, type;
54110f461d7SRanjani Sridharan 
54210f461d7SRanjani Sridharan 	if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET ||
54310f461d7SRanjani Sridharan 	    cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) {
54410f461d7SRanjani Sridharan 		dev_err(sdev->dev, "Component data is not supported in control notification\n");
54510f461d7SRanjani Sridharan 		return;
54610f461d7SRanjani Sridharan 	}
54710f461d7SRanjani Sridharan 
54810f461d7SRanjani Sridharan 	/* Find the swidget first */
54910f461d7SRanjani Sridharan 	list_for_each_entry(swidget, &sdev->widget_list, list) {
55010f461d7SRanjani Sridharan 		if (swidget->comp_id == cdata->comp_id) {
55110f461d7SRanjani Sridharan 			found = true;
55210f461d7SRanjani Sridharan 			break;
55310f461d7SRanjani Sridharan 		}
55410f461d7SRanjani Sridharan 	}
55510f461d7SRanjani Sridharan 
55610f461d7SRanjani Sridharan 	if (!found)
55710f461d7SRanjani Sridharan 		return;
55810f461d7SRanjani Sridharan 
55910f461d7SRanjani Sridharan 	/* Translate SOF cmd to TPLG type */
56010f461d7SRanjani Sridharan 	switch (cdata->cmd) {
56110f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_VOLUME:
56210f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_SWITCH:
56310f461d7SRanjani Sridharan 		type = SND_SOC_TPLG_TYPE_MIXER;
56410f461d7SRanjani Sridharan 		break;
56510f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_BINARY:
56610f461d7SRanjani Sridharan 		type = SND_SOC_TPLG_TYPE_BYTES;
56710f461d7SRanjani Sridharan 		break;
56810f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_ENUM:
56910f461d7SRanjani Sridharan 		type = SND_SOC_TPLG_TYPE_ENUM;
57010f461d7SRanjani Sridharan 		break;
57110f461d7SRanjani Sridharan 	default:
57210f461d7SRanjani Sridharan 		dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__);
57310f461d7SRanjani Sridharan 		return;
57410f461d7SRanjani Sridharan 	}
57510f461d7SRanjani Sridharan 
57610f461d7SRanjani Sridharan 	widget = swidget->widget;
57710f461d7SRanjani Sridharan 	for (i = 0; i < widget->num_kcontrols; i++) {
57810f461d7SRanjani Sridharan 		/* skip non matching types or non matching indexes within type */
57910f461d7SRanjani Sridharan 		if (widget->dobj.widget.kcontrol_type[i] == type &&
58010f461d7SRanjani Sridharan 		    widget->kcontrol_news[i].index == cdata->index) {
58110f461d7SRanjani Sridharan 			kc = widget->kcontrols[i];
58210f461d7SRanjani Sridharan 			break;
58310f461d7SRanjani Sridharan 		}
58410f461d7SRanjani Sridharan 	}
58510f461d7SRanjani Sridharan 
58610f461d7SRanjani Sridharan 	if (!kc)
58710f461d7SRanjani Sridharan 		return;
58810f461d7SRanjani Sridharan 
58910f461d7SRanjani Sridharan 	switch (cdata->cmd) {
59010f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_VOLUME:
59110f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_SWITCH:
59210f461d7SRanjani Sridharan 		sm = (struct soc_mixer_control *)kc->private_value;
59310f461d7SRanjani Sridharan 		scontrol = sm->dobj.private;
59410f461d7SRanjani Sridharan 		break;
59510f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_BINARY:
59610f461d7SRanjani Sridharan 		be = (struct soc_bytes_ext *)kc->private_value;
59710f461d7SRanjani Sridharan 		scontrol = be->dobj.private;
59810f461d7SRanjani Sridharan 		break;
59910f461d7SRanjani Sridharan 	case SOF_CTRL_CMD_ENUM:
60010f461d7SRanjani Sridharan 		se = (struct soc_enum *)kc->private_value;
60110f461d7SRanjani Sridharan 		scontrol = se->dobj.private;
60210f461d7SRanjani Sridharan 		break;
60310f461d7SRanjani Sridharan 	default:
60410f461d7SRanjani Sridharan 		return;
60510f461d7SRanjani Sridharan 	}
60610f461d7SRanjani Sridharan 
60710f461d7SRanjani Sridharan 	expected_size = sizeof(struct sof_ipc_ctrl_data);
60810f461d7SRanjani Sridharan 	switch (cdata->type) {
60910f461d7SRanjani Sridharan 	case SOF_CTRL_TYPE_VALUE_CHAN_GET:
61010f461d7SRanjani Sridharan 	case SOF_CTRL_TYPE_VALUE_CHAN_SET:
61110f461d7SRanjani Sridharan 		expected_size += cdata->num_elems *
61210f461d7SRanjani Sridharan 				 sizeof(struct sof_ipc_ctrl_value_chan);
61310f461d7SRanjani Sridharan 		break;
61410f461d7SRanjani Sridharan 	case SOF_CTRL_TYPE_DATA_GET:
61510f461d7SRanjani Sridharan 	case SOF_CTRL_TYPE_DATA_SET:
61610f461d7SRanjani Sridharan 		expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr);
61710f461d7SRanjani Sridharan 		break;
61810f461d7SRanjani Sridharan 	default:
61910f461d7SRanjani Sridharan 		return;
62010f461d7SRanjani Sridharan 	}
62110f461d7SRanjani Sridharan 
62210f461d7SRanjani Sridharan 	if (cdata->rhdr.hdr.size != expected_size) {
62310f461d7SRanjani Sridharan 		dev_err(sdev->dev, "Component notification size mismatch\n");
62410f461d7SRanjani Sridharan 		return;
62510f461d7SRanjani Sridharan 	}
62610f461d7SRanjani Sridharan 
62710f461d7SRanjani Sridharan 	if (cdata->num_elems)
62810f461d7SRanjani Sridharan 		/*
62910f461d7SRanjani Sridharan 		 * The message includes the updated value/data, update the
63010f461d7SRanjani Sridharan 		 * control's local cache using the received notification
63110f461d7SRanjani Sridharan 		 */
63210f461d7SRanjani Sridharan 		snd_sof_update_control(scontrol, cdata);
63310f461d7SRanjani Sridharan 	else
63410f461d7SRanjani Sridharan 		/* Mark the scontrol that the value/data is changed in SOF */
63510f461d7SRanjani Sridharan 		scontrol->comp_data_dirty = true;
63610f461d7SRanjani Sridharan 
63710f461d7SRanjani Sridharan 	snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0);
63810f461d7SRanjani Sridharan }
63910f461d7SRanjani Sridharan 
640e394ffb8SPeter Ujfalusi static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
641e394ffb8SPeter Ujfalusi 					  struct snd_sof_widget *swidget)
642e394ffb8SPeter Ujfalusi {
643e394ffb8SPeter Ujfalusi 	struct snd_sof_control *scontrol;
644e394ffb8SPeter Ujfalusi 	int ret;
645e394ffb8SPeter Ujfalusi 
646e394ffb8SPeter Ujfalusi 	/* set up all controls for the widget */
647e394ffb8SPeter Ujfalusi 	list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
648e394ffb8SPeter Ujfalusi 		if (scontrol->comp_id == swidget->comp_id) {
649e394ffb8SPeter Ujfalusi 			/* set kcontrol data in DSP */
650e760f102SPeter Ujfalusi 			ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
651e394ffb8SPeter Ujfalusi 			if (ret < 0) {
652e394ffb8SPeter Ujfalusi 				dev_err(sdev->dev,
653e394ffb8SPeter Ujfalusi 					"kcontrol %d set up failed for widget %s\n",
654e394ffb8SPeter Ujfalusi 					scontrol->comp_id, swidget->widget->name);
655e394ffb8SPeter Ujfalusi 				return ret;
656e394ffb8SPeter Ujfalusi 			}
657e394ffb8SPeter Ujfalusi 
658e394ffb8SPeter Ujfalusi 			/*
659e394ffb8SPeter Ujfalusi 			 * Read back the data from the DSP for static widgets.
660e394ffb8SPeter Ujfalusi 			 * This is particularly useful for binary kcontrols
661e394ffb8SPeter Ujfalusi 			 * associated with static pipeline widgets to initialize
662e394ffb8SPeter Ujfalusi 			 * the data size to match that in the DSP.
663e394ffb8SPeter Ujfalusi 			 */
664e394ffb8SPeter Ujfalusi 			if (swidget->dynamic_pipeline_widget)
665e394ffb8SPeter Ujfalusi 				continue;
666e394ffb8SPeter Ujfalusi 
667e760f102SPeter Ujfalusi 			ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
668e394ffb8SPeter Ujfalusi 			if (ret < 0)
669e394ffb8SPeter Ujfalusi 				dev_warn(sdev->dev,
670e394ffb8SPeter Ujfalusi 					 "kcontrol %d read failed for widget %s\n",
671e394ffb8SPeter Ujfalusi 					 scontrol->comp_id, swidget->widget->name);
672e394ffb8SPeter Ujfalusi 		}
673e394ffb8SPeter Ujfalusi 
674e394ffb8SPeter Ujfalusi 	return 0;
675e394ffb8SPeter Ujfalusi }
676e394ffb8SPeter Ujfalusi 
67748d2a1ceSRanjani Sridharan static int
67848d2a1ceSRanjani Sridharan sof_ipc3_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)
67948d2a1ceSRanjani Sridharan {
68048d2a1ceSRanjani Sridharan 	int i;
68148d2a1ceSRanjani Sridharan 
68248d2a1ceSRanjani Sridharan 	/* init the volume table */
68348d2a1ceSRanjani Sridharan 	scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
68448d2a1ceSRanjani Sridharan 	if (!scontrol->volume_table)
68548d2a1ceSRanjani Sridharan 		return -ENOMEM;
68648d2a1ceSRanjani Sridharan 
68748d2a1ceSRanjani Sridharan 	/* populate the volume table */
68848d2a1ceSRanjani Sridharan 	for (i = 0; i < size ; i++)
68948d2a1ceSRanjani Sridharan 		scontrol->volume_table[i] = vol_compute_gain(i, tlv);
69048d2a1ceSRanjani Sridharan 
69148d2a1ceSRanjani Sridharan 	return 0;
69248d2a1ceSRanjani Sridharan }
69348d2a1ceSRanjani Sridharan 
69410f461d7SRanjani Sridharan const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = {
695838d04f3SRanjani Sridharan 	.volume_put = sof_ipc3_volume_put,
696838d04f3SRanjani Sridharan 	.volume_get = sof_ipc3_volume_get,
697a6668746SRanjani Sridharan 	.switch_put = sof_ipc3_switch_put,
698a6668746SRanjani Sridharan 	.switch_get = sof_ipc3_switch_get,
699049307aaSRanjani Sridharan 	.enum_put = sof_ipc3_enum_put,
700049307aaSRanjani Sridharan 	.enum_get = sof_ipc3_enum_get,
701544ac885SRanjani Sridharan 	.bytes_put = sof_ipc3_bytes_put,
702544ac885SRanjani Sridharan 	.bytes_get = sof_ipc3_bytes_get,
70367ec2a09SRanjani Sridharan 	.bytes_ext_put = sof_ipc3_bytes_ext_put,
70467ec2a09SRanjani Sridharan 	.bytes_ext_get = sof_ipc3_bytes_ext_get,
70567ec2a09SRanjani Sridharan 	.bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get,
70610f461d7SRanjani Sridharan 	.update = sof_ipc3_control_update,
707e394ffb8SPeter Ujfalusi 	.widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup,
70848d2a1ceSRanjani Sridharan 	.set_up_volume_table = sof_ipc3_set_up_volume_table,
70910f461d7SRanjani Sridharan };
710