* [PATCH 01/10] ASoC: topology: Create kcontrols based on their type
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 02/10] ASoC: topology: Save num_channels value for mixer controls Cezary Rojewski
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
Fields ->ops.info and ->type of struct snd_soc_tplg_ctl_hdr denote
info-operation type and control type respectively. These are two
different pieces of information. The info type is represented by
SND_SOC_TPLG_CTL_xxx and SND_SOC_TPLG_DAPM_CTL_xxx on UAPI side whereas
for control type it is SND_SOC_TPLG_TYPE_xxx (mixer, bytes or enum).
The type of the kcontrol to be created is currently guessed based on the
value of the ->ops.info. Use the ->type instead to correct and simplify
the code. With this change ops.info() can be customized by sound drivers
utilizing the ASoC-topology just like ops.get() and ops.put() can be.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/soc-topology.c | 54 +++++++++++-----------------------------
1 file changed, 14 insertions(+), 40 deletions(-)
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 9f4da061eff9..921521a84e29 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -220,15 +220,6 @@ static int get_widget_id(int tplg_type)
return -EINVAL;
}
-static inline void soc_bind_err(struct soc_tplg *tplg,
- struct snd_soc_tplg_ctl_hdr *hdr, int index)
-{
- dev_err(tplg->dev,
- "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n",
- hdr->ops.get, hdr->ops.put, hdr->ops.info, index,
- soc_tplg_get_offset(tplg));
-}
-
static inline void soc_control_err(struct soc_tplg *tplg,
struct snd_soc_tplg_ctl_hdr *hdr, const char *name)
{
@@ -992,35 +983,26 @@ static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg,
return -EINVAL;
}
- switch (le32_to_cpu(control_hdr->ops.info)) {
- case SND_SOC_TPLG_CTL_VOLSW:
- case SND_SOC_TPLG_CTL_STROBE:
- case SND_SOC_TPLG_CTL_VOLSW_SX:
- case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
- case SND_SOC_TPLG_CTL_RANGE:
- case SND_SOC_TPLG_DAPM_CTL_VOLSW:
- case SND_SOC_TPLG_DAPM_CTL_PIN:
+ switch (le32_to_cpu(control_hdr->type)) {
+ case SND_SOC_TPLG_TYPE_MIXER:
ret = soc_tplg_dmixer_create(tplg, le32_to_cpu(hdr->payload_size));
break;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_CTL_ENUM_VALUE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_TYPE_ENUM:
ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size));
break;
- case SND_SOC_TPLG_CTL_BYTES:
+ case SND_SOC_TPLG_TYPE_BYTES:
ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size));
break;
default:
- soc_bind_err(tplg, control_hdr, i);
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+
if (ret < 0) {
- dev_err(tplg->dev, "ASoC: invalid control\n");
+ dev_err(tplg->dev, "ASoC: invalid control type: %d, index: %d at 0x%lx\n",
+ control_hdr->type, i, soc_tplg_get_offset(tplg));
return ret;
}
-
}
return 0;
@@ -1184,13 +1166,9 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
for (i = 0; i < le32_to_cpu(w->num_kcontrols); i++) {
control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos;
- switch (le32_to_cpu(control_hdr->ops.info)) {
- case SND_SOC_TPLG_CTL_VOLSW:
- case SND_SOC_TPLG_CTL_STROBE:
- case SND_SOC_TPLG_CTL_VOLSW_SX:
- case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
- case SND_SOC_TPLG_CTL_RANGE:
- case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+
+ switch (le32_to_cpu(control_hdr->type)) {
+ case SND_SOC_TPLG_TYPE_MIXER:
/* volume mixer */
kc[i].index = mixer_count;
kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER;
@@ -1199,11 +1177,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
if (ret < 0)
goto hdr_err;
break;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_CTL_ENUM_VALUE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
- case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+ case SND_SOC_TPLG_TYPE_ENUM:
/* enumerated mixer */
kc[i].index = enum_count;
kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM;
@@ -1212,7 +1186,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
if (ret < 0)
goto hdr_err;
break;
- case SND_SOC_TPLG_CTL_BYTES:
+ case SND_SOC_TPLG_TYPE_BYTES:
/* bytes control */
kc[i].index = bytes_count;
kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES;
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 02/10] ASoC: topology: Save num_channels value for mixer controls
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
2025-02-17 10:21 ` [PATCH 01/10] ASoC: topology: Create kcontrols based on their type Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 03/10] ASoC: Intel: avs: Make PEAKVOL configurable from topology Cezary Rojewski
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
To provide minimal support for multi-channel kcontrols i.e.: more than
stereo configuration, store the number of channels specified within the
SectionControlMixer. The field is part of the topology standard,
currently skipped by the ASoC core.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
include/sound/soc.h | 1 +
sound/soc/soc-topology.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 16e4e488521c..8931c24d9bb9 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1265,6 +1265,7 @@ struct soc_mixer_control {
int min, max, platform_max;
int reg, rreg;
unsigned int shift, rshift;
+ u32 num_channels;
unsigned int sign_bit;
unsigned int invert:1;
unsigned int autodisable:1;
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 921521a84e29..2b86cc3311f7 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -669,6 +669,7 @@ static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcon
sm->min = le32_to_cpu(mc->min);
sm->invert = le32_to_cpu(mc->invert);
sm->platform_max = le32_to_cpu(mc->platform_max);
+ sm->num_channels = le32_to_cpu(mc->num_channels);
/* map io handlers */
err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg);
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 03/10] ASoC: Intel: avs: Make PEAKVOL configurable from topology
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
2025-02-17 10:21 ` [PATCH 01/10] ASoC: topology: Create kcontrols based on their type Cezary Rojewski
2025-02-17 10:21 ` [PATCH 02/10] ASoC: topology: Save num_channels value for mixer controls Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 04/10] ASoC: Intel: avs: Add volume control for GAIN module Cezary Rojewski
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
The driver exposes volume kcontrols if PEAKVOL/GAIN module is present
in the streaming path. Currently there is no control over their default
values including the effect that may accompany the volume change event.
Add template for PEAKVOL/GAIN module which holds all the information
needed to address the limitation.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
include/uapi/sound/intel/avs/tokens.h | 4 ++++
sound/soc/intel/avs/path.c | 4 ++--
sound/soc/intel/avs/topology.c | 18 ++++++++++++++++++
sound/soc/intel/avs/topology.h | 5 +++++
4 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
index 06ff30537f47..c9f845b3c523 100644
--- a/include/uapi/sound/intel/avs/tokens.h
+++ b/include/uapi/sound/intel/avs/tokens.h
@@ -84,6 +84,10 @@ enum avs_tplg_token {
AVS_TKN_MODCFG_WHM_DMA_TYPE_U32 = 437,
AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32 = 438,
AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32 = 439,
+ AVS_TKN_MODCFG_PEAKVOL_VOLUME_U32 = 440,
+ AVS_TKN_MODCFG_PEAKVOL_CHANNEL_ID_U32 = 441, /* reserved */
+ AVS_TKN_MODCFG_PEAKVOL_CURVE_TYPE_U32 = 442,
+ AVS_TKN_MODCFG_PEAKVOL_CURVE_DURATION_U32 = 443,
/* struct avs_tplg_pplcfg */
AVS_TKN_PPLCFG_ID_U32 = 1401,
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index dfb85bd2b665..329838119015 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -350,8 +350,8 @@ static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
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;
+ cfg->vols[0].curve_type = t->cfg_ext->peakvol.curve_type;
+ cfg->vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
t->domain, cfg, cfg_size, &mod->instance_id);
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index 471b00b9a149..45952fbe9694 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -857,6 +857,24 @@ static const struct avs_tplg_token_parser modcfg_ext_parsers[] = {
.offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt),
.parse = avs_parse_audio_format_ptr,
},
+ {
+ .token = AVS_TKN_MODCFG_PEAKVOL_VOLUME_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.target_volume),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_PEAKVOL_CURVE_DURATION_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, peakvol.curve_duration),
+ .parse = avs_parse_word_token,
+ },
};
static const struct avs_tplg_token_parser pin_format_parsers[] = {
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index 23d5ccd19959..304880997717 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -113,6 +113,11 @@ struct avs_tplg_modcfg_ext {
struct {
struct avs_audio_format *out_fmt;
} micsel;
+ struct {
+ u32 target_volume;
+ u32 curve_type;
+ u32 curve_duration;
+ } peakvol;
};
};
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 04/10] ASoC: Intel: avs: Add volume control for GAIN module
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (2 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 03/10] ASoC: Intel: avs: Make PEAKVOL configurable from topology Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 05/10] ASoC: Intel: avs: Update VOLUME and add MUTE IPCs Cezary Rojewski
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
From: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
The AudioDSP firmware's GAIN module has same initialization payload as
PEAKVOL and user volume setting can be applied up-front. Update existing
code to account for PEAKVOL and GAIN both.
Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/control.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
index dc7dc45e0a0a..a55723289600 100644
--- a/sound/soc/intel/avs/control.c
+++ b/sound/soc/intel/avs/control.c
@@ -31,8 +31,11 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i
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 (guid_equal(&mod->template->cfg_ext->type, &AVS_PEAKVOL_MOD_UUID)
- && mod->template->ctl_id == id) {
+ 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;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 05/10] ASoC: Intel: avs: Update VOLUME and add MUTE IPCs
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (3 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 04/10] ASoC: Intel: avs: Add volume control for GAIN module Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 06/10] ASoC: Intel: avs: New volume control operations Cezary Rojewski
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
For mute kcontrols to have an effect add IPCs for triggering the mute
operation on the DSP side. On top of basic get/set, an aggregated
variant of the latter is provided for both MUTE and, to already present
VOLUME IPC. It allows for efficient transmission of multiple parameters
at once.
While at it, sort the functions - getters come before setters in the
AudioDSP firmware interface as well as in the kcontrol one.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/messages.c | 111 ++++++++++++++++++++++++++++++---
sound/soc/intel/avs/messages.h | 24 ++++++-
2 files changed, 126 insertions(+), 9 deletions(-)
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 242a175381c2..a5ba27983091 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -677,13 +677,6 @@ 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)
{
@@ -706,6 +699,110 @@ int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_
return 0;
}
+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_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vols, size_t num_vols)
+{
+ struct avs_tlv *tlv;
+ size_t offset;
+ size_t size;
+ u8 *payload;
+ int ret, i;
+
+ size = num_vols * sizeof(*vols);
+ size += num_vols * sizeof(*tlv);
+ if (size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ for (offset = i = 0; i < num_vols; i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+
+ tlv->type = AVS_PEAKVOL_VOLUME;
+ tlv->length = sizeof(*vols);
+ memcpy(tlv->value, &vols[i], tlv->length);
+
+ offset += sizeof(*tlv) + tlv->length;
+ }
+
+ ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload,
+ size);
+ kfree(payload);
+ return ret;
+}
+
+int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg **mutes, size_t *num_mutes)
+{
+ size_t payload_size;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ /* Non-zero payload expected for PEAKVOL_MUTE. */
+ if (!payload_size)
+ return -EREMOTEIO;
+
+ *mutes = (struct avs_mute_cfg *)payload;
+ *num_mutes = payload_size / sizeof(**mutes);
+
+ return 0;
+}
+
+int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mute)
+{
+ return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_MUTE,
+ (u8 *)mute, sizeof(*mute));
+}
+
+int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mutes, size_t num_mutes)
+{
+ struct avs_tlv *tlv;
+ size_t offset;
+ size_t size;
+ u8 *payload;
+ int ret, i;
+
+ size = num_mutes * sizeof(*mutes);
+ size += num_mutes * sizeof(*tlv);
+ if (size > AVS_MAILBOX_SIZE)
+ return -EINVAL;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ for (offset = i = 0; i < num_mutes; i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+
+ tlv->type = AVS_PEAKVOL_MUTE;
+ tlv->length = sizeof(*mutes);
+ memcpy(tlv->value, &mutes[i], tlv->length);
+
+ offset += sizeof(*tlv) + tlv->length;
+ }
+
+ ret = avs_ipc_set_large_config(adev, module_id, instance_id, AVS_VENDOR_CONFIG, payload,
+ size);
+ kfree(payload);
+ return ret;
+}
+
#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 f44fcfc81de7..2f243802ccc2 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -814,6 +814,15 @@ struct avs_volume_cfg {
} __packed;
static_assert(sizeof(struct avs_volume_cfg) == 24);
+struct avs_mute_cfg {
+ u32 channel_id;
+ u32 mute;
+ u32 curve_type;
+ u32 reserved; /* alignment */
+ u64 curve_duration;
+} __packed;
+static_assert(sizeof(struct avs_mute_cfg) == 24);
+
struct avs_peakvol_cfg {
struct avs_modcfg_base base;
struct avs_volume_cfg vols[];
@@ -896,6 +905,8 @@ static_assert(sizeof(struct avs_whm_cfg) == 108);
/* Module runtime parameters */
+#define AVS_VENDOR_CONFIG 0xFF
+
enum avs_copier_runtime_param {
AVS_COPIER_SET_SINK_FORMAT = 2,
};
@@ -914,6 +925,7 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
enum avs_peakvol_runtime_param {
AVS_PEAKVOL_VOLUME = 0,
+ AVS_PEAKVOL_MUTE = 3,
};
enum avs_audio_curve_type {
@@ -921,10 +933,18 @@ enum avs_audio_curve_type {
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);
+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_set_volumes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vols, size_t num_vols);
+int avs_ipc_peakvol_get_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg **mutes, size_t *num_mutes);
+int avs_ipc_peakvol_set_mute(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mute);
+int avs_ipc_peakvol_set_mutes(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_mute_cfg *mutes, size_t num_mutes);
#define AVS_PROBE_INST_ID 0
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 06/10] ASoC: Intel: avs: New volume control operations
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (4 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 05/10] ASoC: Intel: avs: Update VOLUME and add MUTE IPCs Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 07/10] ASoC: Intel: avs: Move to the new " Cezary Rojewski
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
To provide multi-channel - more than 2 - capability to volume controls
implement operations that honor the num_channels of a mixer control. As
mc->num_channels can be 0 and is in fact the default behavior, the new
functions decide between ALL_CHANNELS_MASK and individual channels based
on the field value.
To avoid hard-to-review delta when refactoring the code, first implement
the new behavior with follow up changes cleaning things up.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/control.c | 84 +++++++++++++++++++++++++++++++++++
sound/soc/intel/avs/control.h | 5 +++
sound/soc/intel/avs/path.c | 36 +++++++++++++++
sound/soc/intel/avs/path.h | 3 ++
4 files changed, 128 insertions(+)
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
index a55723289600..a1c7431cfe13 100644
--- a/sound/soc/intel/avs/control.c
+++ b/sound/soc/intel/avs/control.c
@@ -6,6 +6,7 @@
// Cezary Rojewski <cezary.rojewski@intel.com>
//
+#include <linux/cleanup.h>
#include <sound/soc.h>
#include "avs.h"
#include "control.h"
@@ -114,3 +115,86 @@ int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
return ret ? ret : changed;
}
+
+int avs_control_volume_get2(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_put2(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;
+}
diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h
index d9fac3569e8d..e16fa79962de 100644
--- a/sound/soc/intel/avs/control.h
+++ b/sound/soc/intel/avs/control.h
@@ -10,14 +10,19 @@
#define __SOUND_SOC_INTEL_AVS_CTRL_H
#include <sound/control.h>
+#include <uapi/sound/asoc.h>
struct avs_control_data {
u32 id;
long volume;
+ long values[SND_SOC_TPLG_MAX_CHAN];
};
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);
+int avs_control_volume_get2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_volume_put2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo);
#endif
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index 329838119015..a72ebde7d011 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -323,6 +323,42 @@ static struct avs_control_data *avs_get_module_control(struct avs_path_module *m
return NULL;
}
+int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input)
+{
+ struct avs_volume_cfg vols[SND_SOC_TPLG_MAX_CHAN] = {{0}};
+ struct avs_control_data *ctl_data;
+ struct avs_tplg_module *t;
+ int ret, i;
+
+ ctl_data = mc->dobj.private;
+ t = mod->template;
+ if (!input)
+ input = ctl_data->values;
+
+ if (mc->num_channels) {
+ for (i = 0; i < mc->num_channels; i++) {
+ vols[i].channel_id = i;
+ vols[i].target_volume = input[i];
+ vols[i].curve_type = t->cfg_ext->peakvol.curve_type;
+ vols[i].curve_duration = t->cfg_ext->peakvol.curve_duration;
+ }
+
+ ret = avs_ipc_peakvol_set_volumes(adev, mod->module_id, mod->instance_id, vols,
+ mc->num_channels);
+ return AVS_IPC_RET(ret);
+ }
+
+ /* Target all channels if no individual selected. */
+ vols[0].channel_id = AVS_ALL_CHANNELS_MASK;
+ vols[0].target_volume = input[0];
+ vols[0].curve_type = t->cfg_ext->peakvol.curve_type;
+ vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
+
+ ret = avs_ipc_peakvol_set_volume(adev, mod->module_id, mod->instance_id, &vols[0]);
+ return AVS_IPC_RET(ret);
+}
+
static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
{
struct avs_tplg_module *t = mod->template;
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
index bfd253c9fa95..e9317b64de86 100644
--- a/sound/soc/intel/avs/path.h
+++ b/sound/soc/intel/avs/path.h
@@ -69,4 +69,7 @@ int avs_path_reset(struct avs_path *path);
int avs_path_pause(struct avs_path *path);
int avs_path_run(struct avs_path *path, int trigger);
+int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input);
+
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 07/10] ASoC: Intel: avs: Move to the new control operations
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (5 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 06/10] ASoC: Intel: avs: New volume control operations Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 08/10] ASoC: Intel: avs: Add support for mute for PEAKVOL and GAIN Cezary Rojewski
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
Allow for multi-channel volume controls to be utilized by an application
by moving over to the new implementation. Drop all unused code in the
process.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/control.c | 72 +---------------------------------
sound/soc/intel/avs/control.h | 8 +---
sound/soc/intel/avs/path.c | 2 +-
sound/soc/intel/avs/topology.c | 1 +
4 files changed, 6 insertions(+), 77 deletions(-)
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
index a1c7431cfe13..64283aa35281 100644
--- a/sound/soc/intel/avs/control.c
+++ b/sound/soc/intel/avs/control.c
@@ -48,75 +48,7 @@ static struct avs_path_module *avs_get_volume_module(struct avs_dev *adev, u32 i
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_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)
- 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_volume_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;
-}
-
-int avs_control_volume_get2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
+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;
@@ -150,7 +82,7 @@ int avs_control_volume_get2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value
return 0;
}
-int avs_control_volume_put2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
+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;
diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h
index e16fa79962de..66f3fe064e1d 100644
--- a/sound/soc/intel/avs/control.h
+++ b/sound/soc/intel/avs/control.h
@@ -14,15 +14,11 @@
struct avs_control_data {
u32 id;
-
- long volume;
long values[SND_SOC_TPLG_MAX_CHAN];
};
-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);
-int avs_control_volume_get2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
-int avs_control_volume_put2(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo);
#endif
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index a72ebde7d011..56a2916eec5e 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -370,7 +370,7 @@ static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
ctl_data = avs_get_module_control(mod);
if (ctl_data)
- volume = ctl_data->volume;
+ volume = ctl_data->values[0];
/* As 2+ channels controls are unsupported, have a single block for all channels. */
cfg_size = struct_size(cfg, vols, 1);
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index 45952fbe9694..ee70e3d0e889 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1912,6 +1912,7 @@ 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,
+ .info = avs_control_volume_info,
},
};
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 08/10] ASoC: Intel: avs: Add support for mute for PEAKVOL and GAIN
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (6 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 07/10] ASoC: Intel: avs: Move to the new " Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 09/10] ASoC: Intel: avs: Honor the invert flag for mixer controls Cezary Rojewski
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
From: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
With recent updates to AudioDSP firmware, mute functionality has been
added to PEAKVOL and GAIN modules. The operation occurs over IPC
similarly to how volume is configured. Wire it up to kcontrol
infrastructure present in the avs-driver.
Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/control.c | 83 ++++++++++++++++++++++++++++++++++
sound/soc/intel/avs/control.h | 3 ++
sound/soc/intel/avs/path.c | 36 +++++++++++++++
sound/soc/intel/avs/path.h | 2 +
sound/soc/intel/avs/topology.c | 11 ++++-
5 files changed, 134 insertions(+), 1 deletion(-)
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
index 64283aa35281..2e01dc75a15a 100644
--- a/sound/soc/intel/avs/control.c
+++ b/sound/soc/intel/avs/control.c
@@ -130,3 +130,86 @@ int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info
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;
+}
diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h
index 66f3fe064e1d..08b2919e4629 100644
--- a/sound/soc/intel/avs/control.h
+++ b/sound/soc/intel/avs/control.h
@@ -20,5 +20,8 @@ struct avs_control_data {
int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
int avs_control_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
int avs_control_volume_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo);
+int avs_control_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl);
+int avs_control_mute_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo);
#endif
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index 56a2916eec5e..403510494e77 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -359,6 +359,42 @@ int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod,
return AVS_IPC_RET(ret);
}
+int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input)
+{
+ struct avs_mute_cfg mutes[SND_SOC_TPLG_MAX_CHAN] = {{0}};
+ struct avs_control_data *ctl_data;
+ struct avs_tplg_module *t;
+ int ret, i;
+
+ ctl_data = mc->dobj.private;
+ t = mod->template;
+ if (!input)
+ input = ctl_data->values;
+
+ if (mc->num_channels) {
+ for (i = 0; i < mc->num_channels; i++) {
+ mutes[i].channel_id = i;
+ mutes[i].mute = !input[i];
+ mutes[i].curve_type = t->cfg_ext->peakvol.curve_type;
+ mutes[i].curve_duration = t->cfg_ext->peakvol.curve_duration;
+ }
+
+ ret = avs_ipc_peakvol_set_mutes(adev, mod->module_id, mod->instance_id, mutes,
+ mc->num_channels);
+ return AVS_IPC_RET(ret);
+ }
+
+ /* Target all channels if no individual selected. */
+ mutes[0].channel_id = AVS_ALL_CHANNELS_MASK;
+ mutes[0].mute = !input[0];
+ mutes[0].curve_type = t->cfg_ext->peakvol.curve_type;
+ mutes[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
+
+ ret = avs_ipc_peakvol_set_mute(adev, mod->module_id, mod->instance_id, &mutes[0]);
+ return AVS_IPC_RET(ret);
+}
+
static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
{
struct avs_tplg_module *t = mod->template;
diff --git a/sound/soc/intel/avs/path.h b/sound/soc/intel/avs/path.h
index e9317b64de86..7ed7e94e0a56 100644
--- a/sound/soc/intel/avs/path.h
+++ b/sound/soc/intel/avs/path.h
@@ -71,5 +71,7 @@ int avs_path_run(struct avs_path *path, int trigger);
int avs_peakvol_set_volume(struct avs_dev *adev, struct avs_path_module *mod,
struct soc_mixer_control *mc, long *input);
+int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod,
+ struct soc_mixer_control *mc, long *input);
#endif
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index ee70e3d0e889..9ef1adb077f4 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1905,7 +1905,10 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
return 0;
}
-#define AVS_CONTROL_OPS_VOLUME 257
+enum {
+ AVS_CONTROL_OPS_VOLUME = 257,
+ AVS_CONTROL_OPS_MUTE,
+};
static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = {
{
@@ -1914,6 +1917,12 @@ static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = {
.put = avs_control_volume_put,
.info = avs_control_volume_info,
},
+ {
+ .id = AVS_CONTROL_OPS_MUTE,
+ .get = avs_control_mute_get,
+ .put = avs_control_mute_put,
+ .info = avs_control_mute_info,
+ },
};
static const struct avs_tplg_token_parser control_parsers[] = {
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 09/10] ASoC: Intel: avs: Honor the invert flag for mixer controls
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (7 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 08/10] ASoC: Intel: avs: Add support for mute for PEAKVOL and GAIN Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-17 10:21 ` [PATCH 10/10] ASoC: Intel: avs: Support multi-channel PEAKVOL instantiation Cezary Rojewski
2025-02-25 17:03 ` [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Mark Brown
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
Values for the mute flag represented on the AudioDSP side are inverted.
Check mixer control description and initialize default values
accordingly.
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/topology.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index 9ef1adb077f4..3c222c352701 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1943,18 +1943,20 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_
struct avs_control_data *ctl_data;
struct soc_mixer_control *mc;
size_t block_size;
- int ret;
+ int ret, i;
switch (le32_to_cpu(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;
}
+ mc = (struct soc_mixer_control *)ctmpl->private_value;
+ tmc = container_of(hdr, typeof(*tmc), hdr);
+ tuples = tmc->priv.array;
+ block_size = le32_to_cpu(tmc->priv.size);
+
ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL);
if (!ctl_data)
return -ENOMEM;
@@ -1965,8 +1967,13 @@ avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_
if (ret)
return ret;
- mc = (struct soc_mixer_control *)ctmpl->private_value;
mc->dobj.private = ctl_data;
+ if (tmc->invert) {
+ ctl_data->values[0] = mc->max;
+ for (i = 1; i < mc->num_channels; i++)
+ ctl_data->values[i] = mc->max;
+ }
+
return 0;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* [PATCH 10/10] ASoC: Intel: avs: Support multi-channel PEAKVOL instantiation
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (8 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 09/10] ASoC: Intel: avs: Honor the invert flag for mixer controls Cezary Rojewski
@ 2025-02-17 10:21 ` Cezary Rojewski
2025-02-25 17:03 ` [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Mark Brown
10 siblings, 0 replies; 12+ messages in thread
From: Cezary Rojewski @ 2025-02-17 10:21 UTC (permalink / raw)
To: broonie; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound, Cezary Rojewski
The PEAKVOL module initialization procedure allows for specifying
default configuration for all individual channels. To reflect that in
the code, first update avs_get_module_control() to allow for selecting
Volume or Mute control based on needs and then apply the settings with
newly added avs_peakvol_set_volume() and avs_peakvol_set_mute().
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
sound/soc/intel/avs/path.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index 403510494e77..ef0c1d125d66 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -300,7 +300,8 @@ static int avs_whm_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)
+static struct soc_mixer_control *avs_get_module_control(struct avs_path_module *mod,
+ const char *name)
{
struct avs_tplg_module *t = mod->template;
struct avs_tplg_path_template *path_tmpl;
@@ -316,8 +317,8 @@ static struct avs_control_data *avs_get_module_control(struct avs_path_module *m
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;
+ if (ctl_data->id == t->ctl_id && strstr(w->kcontrols[i]->id.name, name))
+ return mc;
}
return NULL;
@@ -398,17 +399,11 @@ int avs_peakvol_set_mute(struct avs_dev *adev, struct avs_path_module *mod,
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 soc_mixer_control *mc;
struct avs_peakvol_cfg *cfg;
- int volume = S32_MAX;
size_t cfg_size;
int ret;
- ctl_data = avs_get_module_control(mod);
- if (ctl_data)
- volume = ctl_data->values[0];
-
- /* As 2+ channels controls are unsupported, have a single block for all channels. */
cfg_size = struct_size(cfg, vols, 1);
if (cfg_size > AVS_MAILBOX_SIZE)
return -EINVAL;
@@ -420,15 +415,28 @@ static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
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].target_volume = S32_MAX;
cfg->vols[0].curve_type = t->cfg_ext->peakvol.curve_type;
cfg->vols[0].curve_duration = t->cfg_ext->peakvol.curve_duration;
ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
t->domain, cfg, cfg_size, &mod->instance_id);
+ if (ret)
+ return ret;
- return ret;
+ /* Now configure both VOLUME and MUTE parameters. */
+ mc = avs_get_module_control(mod, "Volume");
+ if (mc) {
+ ret = avs_peakvol_set_volume(adev, mod, mc, NULL);
+ if (ret)
+ return ret;
+ }
+
+ mc = avs_get_module_control(mod, "Switch");
+ if (mc)
+ return avs_peakvol_set_mute(adev, mod, mc, NULL);
+ return 0;
}
static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod)
--
2.25.1
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support
2025-02-17 10:21 [PATCH 00/10] ASoC: Intel: avs: Mute and multi-channel controls support Cezary Rojewski
` (9 preceding siblings ...)
2025-02-17 10:21 ` [PATCH 10/10] ASoC: Intel: avs: Support multi-channel PEAKVOL instantiation Cezary Rojewski
@ 2025-02-25 17:03 ` Mark Brown
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2025-02-25 17:03 UTC (permalink / raw)
To: Cezary Rojewski; +Cc: tiwai, perex, amadeuszx.slawinski, linux-sound
On Mon, 17 Feb 2025 11:21:05 +0100, Cezary Rojewski wrote:
> Note: the patchset DOES provide functional changes to the ASoC
> framework.
>
> Current kcontrols loaded with ASoC topology allow for mono or stereo
> configuration only. To expand this and provide support to up to 8
> channels, first address the limitations found within the ASoC core and
> then update the user (avs-driver) so that it can utilize these new
> functionality. The 8 channels max stems from SND_SOC_TPLG_MAX_CHAN
> constant which is part of UAPI - asoc.h.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/10] ASoC: topology: Create kcontrols based on their type
commit: 758beab0252912395efb79f34095c5ae7e3e58b1
[02/10] ASoC: topology: Save num_channels value for mixer controls
commit: 81eb3a2bd273b84fa9808e6b13b533f9c55e16eb
[03/10] ASoC: Intel: avs: Make PEAKVOL configurable from topology
commit: 28feec15fa285e561c626b3490bc5a10f5d177c8
[04/10] ASoC: Intel: avs: Add volume control for GAIN module
commit: 4c43a930e3e165ca6890147a309508ccb6768faf
[05/10] ASoC: Intel: avs: Update VOLUME and add MUTE IPCs
commit: 10188a25c9b5944c0a912482011b484b7c2e22d4
[06/10] ASoC: Intel: avs: New volume control operations
commit: c321a4d705a31a50d7580516422aaa5b853e7602
[07/10] ASoC: Intel: avs: Move to the new control operations
commit: 4c32ebcc8650ce506632a32136993c85537fb01a
[08/10] ASoC: Intel: avs: Add support for mute for PEAKVOL and GAIN
commit: a4217a03686989c4a79530fe54fa17576aff7330
[09/10] ASoC: Intel: avs: Honor the invert flag for mixer controls
commit: 76e013152891a69dfe68a28706a51a7df9ed4c42
[10/10] ASoC: Intel: avs: Support multi-channel PEAKVOL instantiation
commit: a9409fcb979eaff401837b955b234ca1ee05fdbd
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply [flat|nested] 12+ messages in thread