ASoC: Intel: catpt: New volume and mute control operations

The catpt-driver's volume and mute control operations always return '0'
regardless if a change occurred or not. To conform to ALSA's interface,
value '1' shall be returned when a change occurred.

The second major point is power consumption. Existing control operations
always wake the DSP even if no streams are running. In such case waking
the DSP just for the sake of updating the volume (or mute) settings on
the firmware side is a waste of power. The provided implementation
caches the values and updates the settings only when streams are being
opened for streaming or are already running.

As changing existing code is non-trivial, provide new operations
instead. The put() operation, which interests us the most, takes the
following shape:

	// two values provided to put():
	// pin_id - which stream given control relates to
	// value_to_apply - the value from user

	if (control->existing_val == value_to_apply)
		return 0;

	runtime_stream = get_running_stream(pin_id);
	if (runtime_stream != NULL) {
		ret = send_ipc();
		if (ret)
			return ret;
	}

	control->existing_val = value_to_apply;
	return 1;

Adheres to ALSA's expectation and avoids sending IPCs if there is no
change to be made.

Two helpers which are part of the patch, catpt_stream_hw_id() and
catpt_stream_volume_regs(), help differentiate between individual
streams and the general MIXER stream. Translates to one pair of
get()/put() instead of two pairs as done currently.

PIN_ID_INVALID is returned if given stream is not currently running -
the constant is part of the firmware's API but remained unused by the
driver.

Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Link: https://patch.msgid.link/20260309091605.896307-3-cezary.rojewski@intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Cezary Rojewski 2026-03-09 10:16:02 +01:00 committed by Mark Brown
parent b0b49c77bd
commit d16b942aa7
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
2 changed files with 152 additions and 0 deletions

View File

@ -69,6 +69,7 @@ struct catpt_fw_version {
int catpt_ipc_get_fw_version(struct catpt_dev *cdev,
struct catpt_fw_version *version);
/* PIN_IDs represent both, individual streams and the general mixer. */
enum catpt_pin_id {
CATPT_PIN_ID_SYSTEM = 0,
CATPT_PIN_ID_REFERENCE = 1,
@ -79,6 +80,8 @@ enum catpt_pin_id {
CATPT_PIN_ID_MIXER = 7,
CATPT_PIN_ID_BLUETOOTH_CAPTURE = 8,
CATPT_PIN_ID_BLUETOOTH_RENDER = 9,
/* 10 is reserved */
CATPT_PIN_ID_INVALID = 11,
};
enum catpt_path_id {

View File

@ -114,6 +114,46 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id)
return result;
}
/* Caller responsible for holding ->stream_mutex. */
static u8 catpt_stream_hw_id(struct catpt_dev *cdev, enum catpt_pin_id pin_id)
{
struct catpt_stream_runtime *stream;
switch (pin_id) {
default:
stream = catpt_stream_find(cdev, pin_id);
if (stream)
return stream->info.stream_hw_id;
break;
case CATPT_PIN_ID_MIXER:
if (!list_empty(&cdev->stream_list))
return cdev->mixer.mixer_hw_id;
break;
}
return CATPT_PIN_ID_INVALID;
}
/* Caller responsible for holding ->stream_mutex. */
static u32 *catpt_stream_volume_regs(struct catpt_dev *cdev, enum catpt_pin_id pin_id)
{
struct catpt_stream_runtime *stream;
switch (pin_id) {
case CATPT_PIN_ID_MIXER:
if (!list_empty(&cdev->stream_list))
return &cdev->mixer.volume_regaddr[0];
break;
default:
stream = catpt_stream_find(cdev, pin_id);
if (stream)
return &stream->info.volume_regaddr[0];
break;
}
return NULL;
}
static void catpt_stream_read_position(struct catpt_dev *cdev,
struct catpt_stream_runtime *stream, u32 *pos)
{
@ -314,6 +354,11 @@ static void catpt_dai_shutdown(struct snd_pcm_substream *substream,
static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol);
struct catpt_control_data {
enum catpt_pin_id pin_id;
long volumes[CATPT_CHANNELS_MAX];
};
static int catpt_dai_apply_usettings(struct snd_soc_dai *dai,
struct catpt_stream_runtime *stream)
{
@ -855,6 +900,97 @@ static int catpt_volume_info(struct snd_kcontrol *kcontrol,
return 0;
}
__maybe_unused
static int catpt_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
{
struct snd_soc_component *component = snd_kcontrol_chip(kctl);
struct catpt_dev *cdev = dev_get_drvdata(component->dev);
struct catpt_control_data *data;
u32 dspvol, *regs;
long *uvolumes;
int i;
data = (struct catpt_control_data *)kctl->private_value;
uvolumes = &uctl->value.integer.value[0];
guard(mutex)(&cdev->stream_mutex);
regs = catpt_stream_volume_regs(cdev, data->pin_id);
if (regs) {
for (i = 0; i < CATPT_CHANNELS_MAX; i++) {
dspvol = readl(cdev->lpe_ba + regs[i]);
data->volumes[i] = dspvol_to_ctlvol(dspvol);
}
}
memcpy(uvolumes, data->volumes, sizeof(data->volumes));
return 0;
}
__maybe_unused
static int catpt_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
{
struct snd_soc_component *component = snd_kcontrol_chip(kctl);
struct catpt_dev *cdev = dev_get_drvdata(component->dev);
struct catpt_control_data *data;
u8 stream_hw_id;
long *uvolumes;
int ret;
data = (struct catpt_control_data *)kctl->private_value;
uvolumes = &uctl->value.integer.value[0];
if (!memcmp(data->volumes, uvolumes, sizeof(data->volumes)))
return 0;
guard(mutex)(&cdev->stream_mutex);
stream_hw_id = catpt_stream_hw_id(cdev, data->pin_id);
if (stream_hw_id != CATPT_PIN_ID_INVALID) {
ret = catpt_set_dspvol(cdev, stream_hw_id, uvolumes);
if (ret)
return ret;
}
memcpy(data->volumes, uvolumes, sizeof(data->volumes));
return 1;
}
__maybe_unused
static int catpt_loopback_mute_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
{
uctl->value.integer.value[0] = *(bool *)kctl->private_value;
return 0;
}
__maybe_unused
static int catpt_loopback_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uctl)
{
struct snd_soc_component *component = snd_kcontrol_chip(kctl);
struct catpt_dev *cdev = dev_get_drvdata(component->dev);
bool *kmute, cmute;
u8 stream_hw_id;
int ret;
kmute = (bool *)kctl->private_value;
cmute = (bool)uctl->value.integer.value[0];
if (*kmute == cmute)
return 0;
guard(mutex)(&cdev->stream_mutex);
stream_hw_id = catpt_stream_hw_id(cdev, CATPT_PIN_ID_REFERENCE);
if (stream_hw_id != CATPT_PIN_ID_INVALID) {
ret = catpt_ipc_mute_loopback(cdev, stream_hw_id, cmute);
if (ret)
return CATPT_IPC_RET(ret);
}
*kmute = cmute;
return 1;
}
static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -1072,6 +1208,19 @@ static int catpt_waves_param_put(struct snd_kcontrol *kcontrol,
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1);
#define CATPT_VOLUME_CTL2(kname, pname) { \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = kname, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
.info = catpt_volume_info, \
.get = catpt_volume_get, \
.put = catpt_volume_put, \
.tlv.p = catpt_volume_tlv, \
.private_value = (unsigned long) \
&(struct catpt_control_data) { CATPT_PIN_ID_##pname } \
}
#define CATPT_VOLUME_CTL(kname, sname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = (kname), \