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" |
12 | #include "ipc3-priv.h" |
13 | |
14 | /* IPC set()/get() for kcontrols. */ |
15 | static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, |
16 | bool set, bool lock) |
17 | { |
18 | struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(c: scontrol->scomp); |
19 | struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; |
20 | const struct sof_ipc_ops *iops = sdev->ipc->ops; |
21 | enum sof_ipc_ctrl_type ctrl_type; |
22 | struct snd_sof_widget *swidget; |
23 | bool widget_found = false; |
24 | u32 ipc_cmd, msg_bytes; |
25 | int ret = 0; |
26 | |
27 | list_for_each_entry(swidget, &sdev->widget_list, list) { |
28 | if (swidget->comp_id == scontrol->comp_id) { |
29 | widget_found = true; |
30 | break; |
31 | } |
32 | } |
33 | |
34 | if (!widget_found) { |
35 | dev_err(sdev->dev, "%s: can't find widget with id %d\n" , __func__, |
36 | scontrol->comp_id); |
37 | return -EINVAL; |
38 | } |
39 | |
40 | if (lock) |
41 | mutex_lock(&swidget->setup_mutex); |
42 | else |
43 | lockdep_assert_held(&swidget->setup_mutex); |
44 | |
45 | /* |
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. |
49 | */ |
50 | if (!swidget->use_count) |
51 | goto unlock; |
52 | |
53 | /* |
54 | * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and 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: |
91 | ret = -EINVAL; |
92 | goto unlock; |
93 | } |
94 | |
95 | cdata->rhdr.hdr.size = msg_bytes; |
96 | cdata->elems_remaining = 0; |
97 | |
98 | ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); |
99 | if (!set) |
100 | goto unlock; |
101 | |
102 | /* It is a set-data operation, and we have a backup that we can restore */ |
103 | if (ret < 0) { |
104 | if (!scontrol->old_ipc_control_data) |
105 | goto unlock; |
106 | /* |
107 | * Current ipc_control_data is not valid, we use the last known good |
108 | * configuration |
109 | */ |
110 | memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, |
111 | scontrol->max_size); |
112 | kfree(objp: scontrol->old_ipc_control_data); |
113 | scontrol->old_ipc_control_data = NULL; |
114 | /* Send the last known good configuration to firmware */ |
115 | ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); |
116 | if (ret < 0) |
117 | goto unlock; |
118 | } |
119 | |
120 | unlock: |
121 | if (lock) |
122 | mutex_unlock(lock: &swidget->setup_mutex); |
123 | |
124 | return ret; |
125 | } |
126 | |
127 | static void sof_ipc3_refresh_control(struct snd_sof_control *scontrol) |
128 | { |
129 | struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; |
130 | struct snd_soc_component *scomp = scontrol->scomp; |
131 | int ret; |
132 | |
133 | if (!scontrol->comp_data_dirty) |
134 | return; |
135 | |
136 | if (!pm_runtime_active(dev: scomp->dev)) |
137 | return; |
138 | |
139 | /* set the ABI header values */ |
140 | cdata->data->magic = SOF_ABI_MAGIC; |
141 | cdata->data->abi = SOF_ABI_VERSION; |
142 | |
143 | /* refresh the component data from DSP */ |
144 | scontrol->comp_data_dirty = false; |
145 | ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: false, lock: true); |
146 | if (ret < 0) { |
147 | dev_err(scomp->dev, "Failed to get control data: %d\n" , ret); |
148 | |
149 | /* Set the flag to re-try next time to get the data */ |
150 | scontrol->comp_data_dirty = true; |
151 | } |
152 | } |
153 | |
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 | |
161 | sof_ipc3_refresh_control(scontrol); |
162 | |
163 | /* read back each channel */ |
164 | for (i = 0; i < channels; i++) |
165 | ucontrol->value.integer.value[i] = ipc_to_mixer(value: cdata->chanv[i].value, |
166 | volume_map: scontrol->volume_table, |
167 | size: 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(value: ucontrol->value.integer.value[i], |
184 | volume_map: scontrol->volume_table, size: 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(dev: scomp->dev)) { |
193 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: true, lock: true); |
194 | |
195 | if (ret < 0) { |
196 | dev_err(scomp->dev, "Failed to set mixer updates for %s\n" , |
197 | scontrol->name); |
198 | return false; |
199 | } |
200 | } |
201 | |
202 | return change; |
203 | } |
204 | |
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 | |
212 | sof_ipc3_refresh_control(scontrol); |
213 | |
214 | /* read back each channel */ |
215 | for (i = 0; i < channels; i++) |
216 | ucontrol->value.integer.value[i] = cdata->chanv[i].value; |
217 | |
218 | return 0; |
219 | } |
220 | |
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(dev: scomp->dev)) { |
241 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: true, lock: true); |
242 | |
243 | if (ret < 0) { |
244 | dev_err(scomp->dev, "Failed to set mixer updates for %s\n" , |
245 | scontrol->name); |
246 | return false; |
247 | } |
248 | } |
249 | |
250 | return change; |
251 | } |
252 | |
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 | |
260 | sof_ipc3_refresh_control(scontrol); |
261 | |
262 | /* read back each channel */ |
263 | for (i = 0; i < channels; i++) |
264 | ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; |
265 | |
266 | return 0; |
267 | } |
268 | |
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(dev: scomp->dev)) { |
289 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: true, lock: true); |
290 | |
291 | if (ret < 0) { |
292 | dev_err(scomp->dev, "Failed to set enum updates for %s\n" , |
293 | scontrol->name); |
294 | return false; |
295 | } |
296 | } |
297 | |
298 | return change; |
299 | } |
300 | |
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 | |
309 | sof_ipc3_refresh_control(scontrol); |
310 | |
311 | if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { |
312 | dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n" , |
313 | scontrol->max_size); |
314 | return -EINVAL; |
315 | } |
316 | |
317 | /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ |
318 | if (data->size > scontrol->max_size - sizeof(*data)) { |
319 | dev_err_ratelimited(scomp->dev, |
320 | "%u bytes of control data is invalid, max is %zu\n" , |
321 | data->size, scontrol->max_size - sizeof(*data)); |
322 | return -EINVAL; |
323 | } |
324 | |
325 | size = data->size + sizeof(*data); |
326 | |
327 | /* copy back to kcontrol */ |
328 | memcpy(ucontrol->value.bytes.data, data, size); |
329 | |
330 | return 0; |
331 | } |
332 | |
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(dev: scomp->dev)) |
361 | return sof_ipc3_set_get_kcontrol_data(scontrol, set: true, lock: true); |
362 | |
363 | return 0; |
364 | } |
365 | |
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 ; |
374 | int ret = -EINVAL; |
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(to: &header, from: tlvd, n: sizeof(struct snd_ctl_tlv))) |
382 | return -EFAULT; |
383 | |
384 | /* make sure TLV info is consistent */ |
385 | if (header.length + sizeof(struct snd_ctl_tlv) > size) { |
386 | dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n" , |
387 | header.length, sizeof(struct snd_ctl_tlv), size); |
388 | return -EINVAL; |
389 | } |
390 | |
391 | /* be->max is coming from topology */ |
392 | if (header.length > scontrol->max_size) { |
393 | dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n" , |
394 | header.length, scontrol->max_size); |
395 | return -EINVAL; |
396 | } |
397 | |
398 | /* Check that header id matches the command */ |
399 | if (header.numid != cdata->cmd) { |
400 | dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n" , |
401 | header.numid); |
402 | return -EINVAL; |
403 | } |
404 | |
405 | if (!scontrol->old_ipc_control_data) { |
406 | /* Create a backup of the current, valid bytes control */ |
407 | scontrol->old_ipc_control_data = kmemdup(p: scontrol->ipc_control_data, |
408 | size: scontrol->max_size, GFP_KERNEL); |
409 | if (!scontrol->old_ipc_control_data) |
410 | return -ENOMEM; |
411 | } |
412 | |
413 | if (copy_from_user(to: cdata->data, from: tlvd->tlv, n: header.length)) { |
414 | ret = -EFAULT; |
415 | goto err_restore; |
416 | } |
417 | |
418 | if (cdata->data->magic != SOF_ABI_MAGIC) { |
419 | dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n" , cdata->data->magic); |
420 | goto err_restore; |
421 | } |
422 | |
423 | if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { |
424 | dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n" , |
425 | cdata->data->abi); |
426 | goto err_restore; |
427 | } |
428 | |
429 | /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ |
430 | if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { |
431 | dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n" ); |
432 | goto err_restore; |
433 | } |
434 | |
435 | /* notify DSP of byte control updates */ |
436 | if (pm_runtime_active(dev: scomp->dev)) { |
437 | /* Actually send the data to the DSP; this is an opportunity to validate the data */ |
438 | return sof_ipc3_set_get_kcontrol_data(scontrol, set: true, lock: true); |
439 | } |
440 | |
441 | return 0; |
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(objp: scontrol->old_ipc_control_data); |
448 | scontrol->old_ipc_control_data = NULL; |
449 | } |
450 | return ret; |
451 | } |
452 | |
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) |
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 ; |
461 | size_t data_size; |
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 */ |
477 | if (from_dsp) { |
478 | int ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: false, lock: true); |
479 | |
480 | if (ret < 0) |
481 | return ret; |
482 | } |
483 | |
484 | /* check data size doesn't exceed max coming from topology */ |
485 | if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { |
486 | dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n" , |
487 | cdata->data->size, |
488 | scontrol->max_size - sizeof(struct sof_abi_hdr)); |
489 | return -EINVAL; |
490 | } |
491 | |
492 | data_size = cdata->data->size + sizeof(struct sof_abi_hdr); |
493 | |
494 | /* make sure we don't exceed size provided by user space for data */ |
495 | if (data_size > size) |
496 | return -ENOSPC; |
497 | |
498 | header.numid = cdata->cmd; |
499 | header.length = data_size; |
500 | if (copy_to_user(to: tlvd, from: &header, n: sizeof(struct snd_ctl_tlv))) |
501 | return -EFAULT; |
502 | |
503 | if (copy_to_user(to: tlvd->tlv, from: cdata->data, n: data_size)) |
504 | return -EFAULT; |
505 | |
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, from_dsp: 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, from_dsp: true); |
520 | } |
521 | |
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(card: swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kctl: kc, ioff: 0); |
660 | } |
661 | |
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 */ |
672 | ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: true, lock: false); |
673 | if (ret < 0) { |
674 | dev_err(sdev->dev, |
675 | "kcontrol %d set up failed for widget %s\n" , |
676 | scontrol->comp_id, swidget->widget->name); |
677 | return ret; |
678 | } |
679 | |
680 | /* |
681 | * Read back the data from the DSP for static widgets. |
682 | * This is particularly useful for binary 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 | |
689 | ret = sof_ipc3_set_get_kcontrol_data(scontrol, set: false, lock: false); |
690 | if (ret < 0) |
691 | dev_warn(sdev->dev, |
692 | "kcontrol %d read failed for widget %s\n" , |
693 | scontrol->comp_id, swidget->widget->name); |
694 | } |
695 | |
696 | return 0; |
697 | } |
698 | |
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(n: size, 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(value: i, tlv); |
712 | |
713 | return 0; |
714 | } |
715 | |
716 | const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { |
717 | .volume_put = sof_ipc3_volume_put, |
718 | .volume_get = sof_ipc3_volume_get, |
719 | .switch_put = sof_ipc3_switch_put, |
720 | .switch_get = sof_ipc3_switch_get, |
721 | .enum_put = sof_ipc3_enum_put, |
722 | .enum_get = sof_ipc3_enum_get, |
723 | .bytes_put = sof_ipc3_bytes_put, |
724 | .bytes_get = sof_ipc3_bytes_get, |
725 | .bytes_ext_put = sof_ipc3_bytes_ext_put, |
726 | .bytes_ext_get = sof_ipc3_bytes_ext_get, |
727 | .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get, |
728 | .update = sof_ipc3_control_update, |
729 | .widget_kcontrol_setup = sof_ipc3_widget_kcontrol_setup, |
730 | .set_up_volume_table = sof_ipc3_set_up_volume_table, |
731 | }; |
732 | |