Unverified Commit d909b8d1 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: Intel: avs: Mute and multi-channel controls

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

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.

For the ASoC side, two changes are made:

- drop the ambiguous usage of ops.info when determining the kcontrol
  type
- save the num_channels value which is already part of the ALSA-topology
  but is currently skipped by ASoC core when loading mixer controls

For the avs-driver side, merge PEAKVOL IPCs as there is basically no
difference between the handles and then do the same with the control
operations. The merge for the latter is done is two steps: first provide
new implementation which honors the multi-channel controls and then move
to it while dropping the now-duplicated code.

Amadeusz Sławiński (2):
  ASoC: Intel: avs: Add volume control for GAIN module
  ASoC: Intel: avs: Add support for mute for PEAKVOL and GAIN

Cezary Rojewski (8):
  ASoC: topology: Create kcontrols based on their type
  ASoC: topology: Save num_channels value for mixer controls
  ASoC: Intel: avs: Make PEAKVOL configurable from topology
  ASoC: Intel: avs: Update VOLUME and add MUTE IPCs
  ASoC: Intel: avs: New volume control operations
  ASoC: Intel: avs: Move to the new control operations
  ASoC: Intel: avs: Honor the invert flag for mixer controls
  ASoC: Intel: avs: Support multi-channel PEAKVOL instantiation

 include/sound/soc.h                   |   1 +
 include/uapi/sound/intel/avs/tokens.h |   4 +
 sound/soc/intel/avs/control.c         | 180 ++++++++++++++++++++------
 sound/soc/intel/avs/control.h         |  12 +-
 sound/soc/intel/avs/messages.c        | 111 +++++++++++++++-
 sound/soc/intel/avs/messages.h        |  24 +++-
 sound/soc/intel/avs/path.c            | 108 ++++++++++++++--
 sound/soc/intel/avs/path.h            |   5 +
 sound/soc/intel/avs/topology.c        |  47 ++++++-
 sound/soc/intel/avs/topology.h        |   5 +
 sound/soc/soc-topology.c              |  55 +++-----
 11 files changed, 440 insertions(+), 112 deletions(-)

--
2.25.1
parents f9d4f699 a9409fcb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1254,6 +1254,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;
+4 −0
Original line number Diff line number Diff line
@@ -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,
+142 −40
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
//          Cezary Rojewski <cezary.rojewski@intel.com>
//

#include <linux/cleanup.h>
#include <sound/soc.h>
#include "avs.h"
#include "control.h"
@@ -31,8 +32,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;
				}
@@ -44,70 +48,168 @@ 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)
int avs_control_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
{
	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 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 = 0;
	int ret, i;

	adev = avs_get_kcontrol_adev(kctl);

	/* prevent access to modules while path is being constructed */
	mutex_lock(&adev->path_mutex);
	/* 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)
			ucontrol->value.integer.value[0] = dspvols[0].target_volume;
		if (ret)
			return AVS_IPC_RET(ret);

		ret = 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);
	} else {
		ucontrol->value.integer.value[0] = ctl_data->volume;
	}

	mutex_unlock(&adev->path_mutex);
	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;
	}

int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
	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 *)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 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 dspvol = {0};
	long ctlvol = ucontrol->value.integer.value[0];
	int ret = 0, changed = 0;
	struct avs_mute_cfg *dspmutes;
	struct avs_dev *adev;
	size_t num_dspmutes;
	int ret, i;

	if (ctlvol < 0 || ctlvol > mc->max)
		return -EINVAL;
	adev = avs_get_kcontrol_adev(kctl);

	/* Prevent access to modules while path is being constructed. */
	guard(mutex)(&adev->path_mutex);

	/* 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_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);
	}

	if (*volume != ctlvol) {
		*volume = ctlvol;
		changed = 1;
	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) {
		dspvol.channel_id = AVS_ALL_CHANNELS_MASK;
		dspvol.target_volume = *volume;
		ret = avs_peakvol_set_mute(adev, active_module, mc, input);
		if (ret)
			return ret;
	}

		ret = avs_ipc_peakvol_set_volume(adev, active_module->module_id,
						 active_module->instance_id, &dspvol);
		ret = AVS_IPC_RET(ret);
	memcpy(ctl_data->values, input, sizeof(ctl_data->values));
	return 1;
}

	mutex_unlock(&adev->path_mutex);
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;

	return ret ? ret : changed;
	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;
}
+8 −4
Original line number Diff line number Diff line
@@ -10,14 +10,18 @@
#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_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
+104 −7
Original line number Diff line number Diff line
@@ -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)
{
Loading