diff mbox series

[2/4] ASoC: Intel: avs: Add control volume operations

Message ID 20221214185500.3896902-3-amadeuszx.slawinski@linux.intel.com (mailing list archive)
State Accepted
Commit 585b9427edd65ad124e23affb80fca3d15a6375d
Headers show
Series Add support for DSP volume controls | expand

Commit Message

Amadeusz Sławiński Dec. 14, 2022, 6:54 p.m. UTC
To make introduced peakvol module useful from userspace perspective,
expose ALSA controls allowing DSP volume modification. These provide
even more granular control over volume but are also the only way to
modify volume for devices devoid of codec kcontrols e.g.: DMIC.

Co-authored-by: Cezary Rojewski <cezary.rojewski@intel.com>
Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
---
 sound/soc/intel/avs/Makefile  |  2 +-
 sound/soc/intel/avs/control.c | 95 +++++++++++++++++++++++++++++++++++
 sound/soc/intel/avs/control.h | 23 +++++++++
 3 files changed, 119 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/intel/avs/control.c
 create mode 100644 sound/soc/intel/avs/control.h
diff mbox series

Patch

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..92b3aad0baca
--- /dev/null
+++ b/sound/soc/intel/avs/control.c
@@ -0,0 +1,95 @@ 
+// 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)
+{
+	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