732 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) 2021 Intel Corporation7//8//9 10#include "sof-priv.h"11#include "sof-audio.h"12#include "ipc3-priv.h"13 14/* IPC set()/get() for kcontrols. */15static 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(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 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 /*54 * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the55 * direction56 * Note: SOF_CTRL_TYPE_VALUE_COMP_* is not used and supported currently57 * for ctrl_type58 */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 good108 * configuration109 */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 }119 120unlock:121 if (lock)122 mutex_unlock(&swidget->setup_mutex);123 124 return ret;125}126 127static 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(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, false, 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 154static 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(cdata->chanv[i].value,166 scontrol->volume_table,167 scontrol->max + 1);168 169 return 0;170}171 172static 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)) {193 int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, 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 205static 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 221static 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)) {241 int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, 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 253static 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 269static 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)) {289 int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, 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 301static 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 333static 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))361 return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);362 363 return 0;364}365 366static 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;374 int ret = -EINVAL;375 376 /*377 * The beginning of bytes data contains a header from where378 * the length (as bytes) is needed to know the correct copy379 * 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 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 }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(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, true, true);439 }440 441 return 0;442 443err_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;451}452 453static 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 header;461 size_t data_size;462 463 /*464 * Decrement the limit by ext bytes header size to465 * 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, false, 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(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 506 return 0;507}508 509static 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 515static 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);520}521 522static 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 550static 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 the652 * control's local cache using the received notification653 */654 snd_sof_update_control(scontrol, cdata);655 else656 /* 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 662static 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, true, 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 kcontrols683 * associated with static pipeline widgets to initialize684 * 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, false, 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 699static int700sof_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 716const 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