diff options
-rw-r--r-- | include/uapi/sound/intel/avs/tokens.h | 4 | ||||
-rw-r--r-- | sound/soc/intel/avs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/intel/avs/control.c | 105 | ||||
-rw-r--r-- | sound/soc/intel/avs/control.h | 23 | ||||
-rw-r--r-- | sound/soc/intel/avs/messages.c | 29 | ||||
-rw-r--r-- | sound/soc/intel/avs/messages.h | 33 | ||||
-rw-r--r-- | sound/soc/intel/avs/path.c | 62 | ||||
-rw-r--r-- | sound/soc/intel/avs/topology.c | 76 | ||||
-rw-r--r-- | sound/soc/intel/avs/topology.h | 3 |
9 files changed, 336 insertions, 1 deletions
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h index 754f02b2f444..4ffe546aa409 100644 --- a/include/uapi/sound/intel/avs/tokens.h +++ b/include/uapi/sound/intel/avs/tokens.h @@ -108,6 +108,7 @@ enum avs_tplg_token { AVS_TKN_MOD_CORE_ID_U8 = 1704, AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705, AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706, + AVS_TKN_MOD_KCONTROL_ID_U32 = 1707, /* struct avs_tplg_path_template */ AVS_TKN_PATH_TMPL_ID_U32 = 1801, @@ -121,6 +122,9 @@ enum avs_tplg_token { AVS_TKN_PIN_FMT_INDEX_U32 = 2201, AVS_TKN_PIN_FMT_IOBS_U32 = 2202, AVS_TKN_PIN_FMT_AFMT_ID_U32 = 2203, + + /* struct avs_tplg_kcontrol */ + AVS_TKN_KCONTROL_ID_U32 = 2301, }; #endif diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 1c6924a1ebca..460ee6599daf 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \ - topology.o path.o pcm.o board_selection.o + topology.o path.o pcm.o board_selection.o control.o snd-soc-avs-objs += cldma.o snd-soc-avs-objs += skl.o apl.o diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c new file mode 100644 index 000000000000..a8b14b784f8a --- /dev/null +++ b/sound/soc/intel/avs/control.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> +// Cezary Rojewski <cezary.rojewski@intel.com> +// + +#include <sound/soc.h> +#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_kcontrol_module(struct avs_dev *adev, u32 id) +{ + struct avs_path *path; + struct avs_path_pipeline *ppl; + struct avs_path_module *mod; + + 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) + if (mod->template->ctl_id && mod->template->ctl_id == id) + return mod; + + return NULL; +} + +int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; + struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); + struct avs_volume_cfg *dspvols = NULL; + struct avs_path_module *active_module; + size_t num_dspvols; + int ret = 0; + + /* prevent access to modules while path is being constructed */ + mutex_lock(&adev->path_mutex); + + active_module = avs_get_kcontrol_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) + ucontrol->value.integer.value[0] = dspvols[0].target_volume; + + ret = AVS_IPC_RET(ret); + kfree(dspvols); + } else { + ucontrol->value.integer.value[0] = ctl_data->volume; + } + + mutex_unlock(&adev->path_mutex); + return ret; +} + +int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private; + struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol); + long *volume = &ctl_data->volume; + struct avs_path_module *active_module; + struct avs_volume_cfg dspvol = {0}; + long ctlvol = ucontrol->value.integer.value[0]; + int ret = 0, changed = 0; + + if (ctlvol < 0 || ctlvol > mc->max) + return -EINVAL; + + /* prevent access to modules while path is being constructed */ + mutex_lock(&adev->path_mutex); + + if (*volume != ctlvol) { + *volume = ctlvol; + changed = 1; + } + + active_module = avs_get_kcontrol_module(adev, ctl_data->id); + if (active_module) { + dspvol.channel_id = AVS_ALL_CHANNELS_MASK; + dspvol.target_volume = *volume; + + ret = avs_ipc_peakvol_set_volume(adev, active_module->module_id, + active_module->instance_id, &dspvol); + ret = AVS_IPC_RET(ret); + } + + mutex_unlock(&adev->path_mutex); + + return ret ? ret : changed; +} diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h new file mode 100644 index 000000000000..08631bde13c3 --- /dev/null +++ b/sound/soc/intel/avs/control.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2021-2022 Intel Corporation. All rights reserved. + * + * Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> + * Cezary Rojewski <cezary.rojewski@intel.com> + */ + +#ifndef __SOUND_SOC_INTEL_AVS_CTRL_H +#define __SOUND_SOC_INTEL_AVS_CTRL_H + +#include <sound/control.h> + +struct avs_control_data { + u32 id; + + long volume; +}; + +int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); + +#endif diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index e11ae4246416..f887ab5b0311 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -702,6 +702,35 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, (u8 *)&cpr_fmt, sizeof(cpr_fmt)); } +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol) +{ + return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, (u8 *)vol, + sizeof(*vol)); +} + +int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg **vols, size_t *num_vols) +{ + size_t payload_size; + u8 *payload; + int ret; + + ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, NULL, 0, + &payload, &payload_size); + if (ret) + return ret; + + /* Non-zero payload expected for PEAKVOL_VOLUME. */ + if (!payload_size) + return -EREMOTEIO; + + *vols = (struct avs_volume_cfg *)payload; + *num_vols = payload_size / sizeof(**vols); + + return 0; +} + #ifdef CONFIG_DEBUG_FS int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 9dd835527e02..d3b60ae7d743 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -561,6 +561,12 @@ int avs_ipc_set_system_time(struct avs_dev *adev); #define AVS_COPIER_MOD_UUID \ GUID_INIT(0x9BA00C83, 0xCA12, 0x4A83, 0x94, 0x3C, 0x1F, 0xA2, 0xE8, 0x2F, 0x9D, 0xDA) +#define AVS_PEAKVOL_MOD_UUID \ + GUID_INIT(0x8A171323, 0x94A3, 0x4E1D, 0xAF, 0xE9, 0xFE, 0x5D, 0xBA, 0xa4, 0xC3, 0x93) + +#define AVS_GAIN_MOD_UUID \ + GUID_INIT(0x61BCA9A8, 0x18D0, 0x4A18, 0x8E, 0x7B, 0x26, 0x39, 0x21, 0x98, 0x04, 0xB7) + #define AVS_KPBUFF_MOD_UUID \ GUID_INIT(0xA8A0CB32, 0x4A77, 0x4DB1, 0x85, 0xC7, 0x53, 0xD7, 0xEE, 0x07, 0xBC, 0xE6) @@ -729,6 +735,19 @@ struct avs_copier_cfg { struct avs_copier_gtw_cfg gtw_cfg; } __packed; +struct avs_volume_cfg { + u32 channel_id; + u32 target_volume; + u32 curve_type; + u32 reserved; /* alignment */ + u64 curve_duration; +} __packed; + +struct avs_peakvol_cfg { + struct avs_modcfg_base base; + struct avs_volume_cfg vols[]; +} __packed; + struct avs_micsel_cfg { struct avs_modcfg_base base; struct avs_audio_format out_fmt; @@ -802,6 +821,20 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, const struct avs_audio_format *src_fmt, const struct avs_audio_format *sink_fmt); +enum avs_peakvol_runtime_param { + AVS_PEAKVOL_VOLUME = 0, +}; + +enum avs_audio_curve_type { + AVS_AUDIO_CURVE_NONE = 0, + AVS_AUDIO_CURVE_WINDOWS_FADE = 1, +}; + +int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg *vol); +int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id, + struct avs_volume_cfg **vols, size_t *num_vols); + #define AVS_PROBE_INST_ID 0 enum avs_probe_runtime_param { diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c index ce157a8d6552..05302ab705ae 100644 --- a/sound/soc/intel/avs/path.c +++ b/sound/soc/intel/avs/path.c @@ -10,6 +10,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "avs.h" +#include "control.h" #include "path.h" #include "topology.h" @@ -264,6 +265,65 @@ static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) return ret; } +static struct avs_control_data *avs_get_module_control(struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_tplg_path_template *path_tmpl; + struct snd_soc_dapm_widget *w; + int i; + + path_tmpl = t->owner->owner->owner; + w = path_tmpl->w; + + for (i = 0; i < w->num_kcontrols; i++) { + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + + mc = (struct soc_mixer_control *)w->kcontrols[i]->private_value; + ctl_data = (struct avs_control_data *)mc->dobj.private; + if (ctl_data->id == t->ctl_id) + return ctl_data; + } + + return NULL; +} + +static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod) +{ + struct avs_tplg_module *t = mod->template; + struct avs_control_data *ctl_data; + struct avs_peakvol_cfg *cfg; + int volume = S32_MAX; + size_t size; + int ret; + + ctl_data = avs_get_module_control(mod); + if (ctl_data) + volume = ctl_data->volume; + + /* As 2+ channels controls are unsupported, have a single block for all channels. */ + size = struct_size(cfg, vols, 1); + cfg = kzalloc(size, GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->base.cpc = t->cfg_base->cpc; + cfg->base.ibs = t->cfg_base->ibs; + cfg->base.obs = t->cfg_base->obs; + cfg->base.is_pages = t->cfg_base->is_pages; + cfg->base.audio_fmt = *t->in_fmt; + cfg->vols[0].target_volume = volume; + cfg->vols[0].channel_id = AVS_ALL_CHANNELS_MASK; + cfg->vols[0].curve_type = AVS_AUDIO_CURVE_NONE; + cfg->vols[0].curve_duration = 0; + + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, + t->domain, cfg, size, &mod->instance_id); + + kfree(cfg); + return ret; +} + static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod) { struct avs_tplg_module *t = mod->template; @@ -465,6 +525,8 @@ static struct avs_module_create avs_module_create[] = { { &AVS_MIXOUT_MOD_UUID, avs_modbase_create }, { &AVS_KPBUFF_MOD_UUID, avs_modbase_create }, { &AVS_COPIER_MOD_UUID, avs_copier_create }, + { &AVS_PEAKVOL_MOD_UUID, avs_peakvol_create }, + { &AVS_GAIN_MOD_UUID, avs_peakvol_create }, { &AVS_MICSEL_MOD_UUID, avs_micsel_create }, { &AVS_MUX_MOD_UUID, avs_mux_create }, { &AVS_UPDWMIX_MOD_UUID, avs_updown_mix_create }, diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c index e845eaf0a1e7..5fee7a8ec06a 100644 --- a/sound/soc/intel/avs/topology.c +++ b/sound/soc/intel/avs/topology.c @@ -13,6 +13,7 @@ #include <sound/soc-topology.h> #include <uapi/sound/intel/avs/tokens.h> #include "avs.h" +#include "control.h" #include "topology.h" /* Get pointer to vendor array at the specified offset. */ @@ -1070,6 +1071,12 @@ static const struct avs_tplg_token_parser module_parsers[] = { .offset = offsetof(struct avs_tplg_module, cfg_ext), .parse = avs_parse_modcfg_ext_ptr, }, + { + .token = AVS_TKN_MOD_KCONTROL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_tplg_module, ctl_id), + .parse = avs_parse_byte_token, + }, }; static struct avs_tplg_module * @@ -1435,6 +1442,16 @@ static int avs_widget_load(struct snd_soc_component *comp, int index, return 0; } +static int avs_widget_ready(struct snd_soc_component *comp, int index, + struct snd_soc_dapm_widget *w, + struct snd_soc_tplg_dapm_widget *dw) +{ + struct avs_tplg_path_template *template = w->priv; + + template->w = w; + return 0; +} + static int avs_dai_load(struct snd_soc_component *comp, int index, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) @@ -1586,9 +1603,68 @@ static int avs_manifest(struct snd_soc_component *comp, int index, return avs_tplg_parse_bindings(comp, tuples, remaining); } +#define AVS_CONTROL_OPS_VOLUME 257 + +static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = { + { + .id = AVS_CONTROL_OPS_VOLUME, + .get = avs_control_volume_get, + .put = avs_control_volume_put, + }, +}; + +static const struct avs_tplg_token_parser control_parsers[] = { + { + .token = AVS_TKN_KCONTROL_ID_U32, + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, + .offset = offsetof(struct avs_control_data, id), + .parse = avs_parse_word_token, + }, +}; + +static int +avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_new *ctmpl, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_soc_tplg_vendor_array *tuples; + struct snd_soc_tplg_mixer_control *tmc; + struct avs_control_data *ctl_data; + struct soc_mixer_control *mc; + size_t block_size; + int ret; + + switch (hdr->type) { + case SND_SOC_TPLG_TYPE_MIXER: + tmc = container_of(hdr, typeof(*tmc), hdr); + tuples = tmc->priv.array; + block_size = le32_to_cpu(tmc->priv.size); + break; + default: + return -EINVAL; + } + + ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL); + if (!ctl_data) + return -ENOMEM; + + ret = parse_dictionary_entries(comp, tuples, block_size, ctl_data, 1, sizeof(*ctl_data), + AVS_TKN_KCONTROL_ID_U32, control_parsers, + ARRAY_SIZE(control_parsers)); + if (ret) + return ret; + + mc = (struct soc_mixer_control *)ctmpl->private_value; + mc->dobj.private = ctl_data; + return 0; +} + static struct snd_soc_tplg_ops avs_tplg_ops = { + .io_ops = avs_control_ops, + .io_ops_count = ARRAY_SIZE(avs_control_ops), + .control_load = avs_control_load, .dapm_route_load = avs_route_load, .widget_load = avs_widget_load, + .widget_ready = avs_widget_ready, .dai_load = avs_dai_load, .link_load = avs_link_load, .manifest = avs_manifest, diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h index 68e5f6312353..6e1c8e9b2496 100644 --- a/sound/soc/intel/avs/topology.h +++ b/sound/soc/intel/avs/topology.h @@ -138,6 +138,8 @@ struct avs_tplg_path_template_id { struct avs_tplg_path_template { u32 id; + struct snd_soc_dapm_widget *w; + struct list_head path_list; struct avs_tplg *owner; @@ -180,6 +182,7 @@ struct avs_tplg_module { u8 core_id; u8 domain; struct avs_tplg_modcfg_ext *cfg_ext; + u32 ctl_id; struct avs_tplg_pipeline *owner; /* Pipeline modules management. */ |