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