Commit | Line | Data |
---|---|---|
10f461d7 RS |
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 or | |
4 | // redistributing this file, you may do so under either license. | |
5 | // | |
6 | // Copyright(c) 2021 Intel Corporation. All rights reserved. | |
7 | // | |
8 | // | |
9 | ||
10 | #include "sof-priv.h" | |
11 | #include "sof-audio.h" | |
f80beaf6 | 12 | #include "ipc3-priv.h" |
10f461d7 | 13 | |
e760f102 | 14 | /* IPC set()/get() for kcontrols. */ |
f94f3915 PU |
15 | static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, |
16 | bool set, bool lock) | |
e760f102 PU |
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; | |
f94f3915 | 25 | int ret = 0; |
e760f102 PU |
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 | ||
f94f3915 PU |
40 | if (lock) |
41 | mutex_lock(&swidget->setup_mutex); | |
42 | else | |
43 | lockdep_assert_held(&swidget->setup_mutex); | |
44 | ||
e760f102 | 45 | /* |
f94f3915 PU |
46 | * Volatile controls should always be part of static pipelines and the |
47 | * 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. | |
e760f102 PU |
49 | */ |
50 | if (!swidget->use_count) | |
f94f3915 | 51 | goto unlock; |
e760f102 PU |
52 | |
53 | /* | |
54 | * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the | |
55 | * direction | |
56 | * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently | |
57 | * for ctrl_type | |
58 | */ | |
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: | |
f94f3915 PU |
91 | ret = -EINVAL; |
92 | goto unlock; | |
e760f102 PU |
93 | } |
94 | ||
95 | cdata->rhdr.hdr.size = msg_bytes; | |
96 | cdata->elems_remaining = 0; | |
97 | ||
f94f3915 | 98 | ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); |
299f6c75 PO |
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 good | |
108 | * configuration | |
109 | */ | |
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 | } | |
f94f3915 PU |
119 | |
120 | unlock: | |
121 | if (lock) | |
122 | mutex_unlock(&swidget->setup_mutex); | |
123 | ||
124 | return ret; | |
e760f102 PU |
125 | } |
126 | ||
3eac8de3 | 127 | static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol) |
838d04f3 RS |
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; | |
f94f3915 | 145 | ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); |
838d04f3 RS |
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 | ||
154 | static 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 | ||
3eac8de3 | 161 | sof_ipc3_refresh_control(scontrol); |
838d04f3 RS |
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 | ||
172 | static 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)) { | |
f94f3915 | 193 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); |
838d04f3 RS |
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 | ||
a6668746 RS |
205 | static 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 | ||
3eac8de3 | 212 | sof_ipc3_refresh_control(scontrol); |
a6668746 RS |
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 | ||
221 | static 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)) { | |
f94f3915 | 241 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); |
a6668746 RS |
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 | ||
049307aa RS |
253 | static 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 | ||
3eac8de3 | 260 | sof_ipc3_refresh_control(scontrol); |
049307aa RS |
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 | ||
269 | static 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)) { | |
f94f3915 | 289 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); |
049307aa RS |
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 | ||
544ac885 RS |
301 | static 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 | ||
3eac8de3 | 309 | sof_ipc3_refresh_control(scontrol); |
544ac885 RS |
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 | ||
333 | static 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)) | |
f94f3915 | 361 | return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); |
544ac885 RS |
362 | |
363 | return 0; | |
364 | } | |
365 | ||
67ec2a09 RS |
366 | static 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; | |
299f6c75 | 374 | int ret = -EINVAL; |
67ec2a09 RS |
375 | |
376 | /* | |
377 | * The beginning of bytes data contains a header from where | |
378 | * the length (as bytes) is needed to know the correct copy | |
379 | * 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 | ||
299f6c75 PO |
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 | } | |
67ec2a09 RS |
417 | |
418 | if (cdata->data->magic != SOF_ABI_MAGIC) { | |
419 | dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); | |
299f6c75 | 420 | goto err_restore; |
67ec2a09 RS |
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); | |
299f6c75 | 426 | goto err_restore; |
67ec2a09 RS |
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"); | |
299f6c75 | 432 | goto err_restore; |
67ec2a09 RS |
433 | } |
434 | ||
435 | /* notify DSP of byte control updates */ | |
299f6c75 PO |
436 | if (pm_runtime_active(scomp->dev)) { |
437 | /* Actually send the data to the DSP; this is an opportunity to validate the data */ | |
f94f3915 | 438 | return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); |
299f6c75 | 439 | } |
67ec2a09 RS |
440 | |
441 | return 0; | |
299f6c75 PO |
442 | |
443 | err_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; | |
67ec2a09 RS |
451 | } |
452 | ||
76fc628a PU |
453 | static 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) | |
67ec2a09 RS |
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; | |
67ec2a09 RS |
462 | |
463 | /* | |
464 | * Decrement the limit by ext bytes header size to | |
465 | * 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 */ | |
76fc628a PU |
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 | } | |
67ec2a09 RS |
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 | ||
76fc628a PU |
506 | return 0; |
507 | } | |
508 | ||
509 | static 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 | ||
515 | static 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); | |
67ec2a09 RS |
520 | } |
521 | ||
10f461d7 RS |
522 | static 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 | ||
550 | static 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 the | |
652 | * control's local cache using the received notification | |
653 | */ | |
654 | snd_sof_update_control(scontrol, cdata); | |
655 | else | |
656 | /* 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 | ||
e394ffb8 PU |
662 | static 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 */ | |
f94f3915 | 672 | ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false); |
e394ffb8 PU |
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 kcontrols | |
683 | * associated with static pipeline widgets to initialize | |
684 | * the data size to match that in the DSP. | |
685 | */ | |
686 | if (swidget->dynamic_pipeline_widget) | |
687 | continue; | |
688 | ||
f94f3915 | 689 | ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false); |
e394ffb8 PU |
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 | ||
48d2a1ce RS |
699 | static int |
700 | sof_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 | ||
10f461d7 | 716 | const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { |
838d04f3 RS |
717 | .volume_put = sof_ipc3_volume_put, |
718 | .volume_get = sof_ipc3_volume_get, | |
a6668746 RS |
719 | .switch_put = sof_ipc3_switch_put, |
720 | .switch_get = sof_ipc3_switch_get, | |
049307aa RS |
721 | .enum_put = sof_ipc3_enum_put, |
722 | .enum_get = sof_ipc3_enum_get, | |
544ac885 RS |
723 | .bytes_put = sof_ipc3_bytes_put, |
724 | .bytes_get = sof_ipc3_bytes_get, | |
67ec2a09 RS |
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, | |
10f461d7 | 728 | .update = sof_ipc3_control_update, |
e394ffb8 | 729 | .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup, |
48d2a1ce | 730 | .set_up_volume_table = sof_ipc3_set_up_volume_table, |
10f461d7 | 731 | }; |