// SPDX-License-Identifier: GPL-2.0-only // // Copyright(c) 2021-2022 Intel Corporation // // Authors: Amadeusz Slawinski // Cezary Rojewski // #include #include #include "avs.h" #include "control.h" #include "messages.h" #include "path.h" static struct avs_dev *avs_get_kcontrol_adev(struct snd_kcontrol *kcontrol) { struct snd_soc_dapm_widget *w; w = snd_soc_dapm_kcontrol_widget(kcontrol); return to_avs_dev(w->dapm->component->dev); } static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 id) { struct avs_path *path; struct avs_path_pipeline *ppl; struct avs_path_module *mod; spin_lock(&adev->path_list_lock); list_for_each_entry(path, &adev->path_list, node) { list_for_each_entry(ppl, &path->ppl_list, node) { list_for_each_entry(mod, &ppl->mod_list, node) { guid_t *type = &mod->template->cfg_ext->type; if ((guid_equal(type, &AVS_PEAKVOL_MOD_UUID) || guid_equal(type, &AVS_GAIN_MOD_UUID)) && mod->template->ctl_id == id) { spin_unlock(&adev->path_list_lock); return mod; } } } } spin_unlock(&adev->path_list_lock); return NULL; } int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; struct avs_control_data *ctl_data = mc->dobj.private; struct avs_path_module *active_module; struct avs_volume_cfg *dspvols; struct avs_dev *adev; size_t num_dspvols; int ret, i; adev = avs_get_kcontrol_adev(kctl); /* Prevent access to modules while path is being constructed. */ guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { ret = avs_ipc_peakvol_get_volume(adev, active_module->module_id, active_module->instance_id, &dspvols, &num_dspvols); if (ret) return AVS_IPC_RET(ret); /* Do not copy more than the control can store. */ num_dspvols = min_t(u32, num_dspvols, SND_SOC_TPLG_MAX_CHAN); for (i = 0; i < num_dspvols; i++) ctl_data->values[i] = dspvols[i].target_volume; kfree(dspvols); } memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values)); return 0; } int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { struct avs_path_module *active_module; struct avs_control_data *ctl_data; struct soc_mixer_control *mc; struct avs_dev *adev; long *input; int ret, i; mc = (struct soc_mixer_control *)kctl->private_value; ctl_data = mc->dobj.private; adev = avs_get_kcontrol_adev(kctl); input = uctl->value.integer.value; i = 0; /* mc->num_channels can be 0. */ do { if (input[i] < mc->min || input[i] > mc->max) return -EINVAL; } while (++i < mc->num_channels); if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values))) return 0; /* Prevent access to modules while path is being constructed. */ guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { ret = avs_peakvol_set_volume(adev, active_module, mc, input); if (ret) return ret; } memcpy(ctl_data->values, input, sizeof(ctl_data->values)); return 1; } int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = max_t(u32, 1, mc->num_channels); uinfo->value.integer.min = 0; uinfo->value.integer.max = mc->max; return 0; } int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; struct avs_control_data *ctl_data = mc->dobj.private; struct avs_path_module *active_module; struct avs_mute_cfg *dspmutes; struct avs_dev *adev; size_t num_dspmutes; int ret, i; adev = avs_get_kcontrol_adev(kctl); /* Prevent access to modules while path is being constructed. */ guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { ret = avs_ipc_peakvol_get_mute(adev, active_module->module_id, active_module->instance_id, &dspmutes, &num_dspmutes); if (ret) return AVS_IPC_RET(ret); /* Do not copy more than the control can store. */ num_dspmutes = min_t(u32, num_dspmutes, SND_SOC_TPLG_MAX_CHAN); for (i = 0; i < num_dspmutes; i++) ctl_data->values[i] = !dspmutes[i].mute; kfree(dspmutes); } memcpy(uctl->value.integer.value, ctl_data->values, sizeof(ctl_data->values)); return 0; } int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl) { struct avs_path_module *active_module; struct avs_control_data *ctl_data; struct soc_mixer_control *mc; struct avs_dev *adev; long *input; int ret, i; mc = (struct soc_mixer_control *)kctl->private_value; ctl_data = mc->dobj.private; adev = avs_get_kcontrol_adev(kctl); input = uctl->value.integer.value; i = 0; /* mc->num_channels can be 0. */ do { if (input[i] < mc->min || input[i] > mc->max) return -EINVAL; } while (++i < mc->num_channels); if (!memcmp(ctl_data->values, input, sizeof(ctl_data->values))) return 0; /* Prevent access to modules while path is being constructed. */ guard(mutex)(&adev->path_mutex); active_module = avs_get_volume_module(adev, ctl_data->id); if (active_module) { ret = avs_peakvol_set_mute(adev, active_module, mc, input); if (ret) return ret; } memcpy(ctl_data->values, input, sizeof(ctl_data->values)); return 1; } int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value; uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = max_t(u32, 1, mc->num_channels); uinfo->value.integer.min = 0; uinfo->value.integer.max = mc->max; return 0; }