From b0b49c77bddac75db79f7c2c6ec0b07d61864f2f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 9 Mar 2026 10:16:01 +0100 Subject: [PATCH 1/5] ASoC: Intel: catpt: Synchronize stream access Streams may have individual controls assigned to them e.g.: volume control in case of offload streams. If such a stream is running and simultaneously its controls are being manipulated, both processes are touching the exact same descriptors - access to these must be synchronized. Replace spinlock with mutex as IPCs are non-atomic operations and add proper locking for all ->stream_list users. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260309091605.896307-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 2 +- sound/soc/intel/catpt/device.c | 2 +- sound/soc/intel/catpt/ipc.c | 3 +++ sound/soc/intel/catpt/loader.c | 2 ++ sound/soc/intel/catpt/pcm.c | 25 +++++++++++++++---------- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index df8a5fd95e13..7e479ef89ad0 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -96,7 +96,7 @@ struct catpt_dev { struct catpt_module_type modules[CATPT_MODULE_COUNT]; struct catpt_ssp_device_format devfmt[CATPT_SSP_COUNT]; struct list_head stream_list; - spinlock_t list_lock; + struct mutex stream_mutex; struct mutex clk_mutex; struct catpt_dx_context dx_ctx; diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index 0638aecba40d..b5f4361d4465 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -226,7 +226,7 @@ static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev, cdev->spec = spec; init_completion(&cdev->fw_ready); INIT_LIST_HEAD(&cdev->stream_list); - spin_lock_init(&cdev->list_lock); + mutex_init(&cdev->stream_mutex); mutex_init(&cdev->clk_mutex); /* diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c index 5a01a9afb26e..2e3b7a5cbb9b 100644 --- a/sound/soc/intel/catpt/ipc.c +++ b/sound/soc/intel/catpt/ipc.c @@ -5,6 +5,7 @@ // Author: Cezary Rojewski // +#include #include #include "core.h" #include "messages.h" @@ -151,6 +152,8 @@ catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg) struct catpt_notify_position pos; struct catpt_notify_glitch glitch; + guard(mutex)(&cdev->stream_mutex); + stream = catpt_stream_find(cdev, msg.stream_hw_id); if (!stream) { dev_warn(cdev->dev, "notify %d for non-existent stream %d\n", diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index dc7afe587e6f..432cb1f0ab4e 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -90,6 +90,7 @@ int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { struct catpt_stream_runtime *stream; + /* Lockless as no streams can be added or removed during D3 -> D0 transition. */ list_for_each_entry(stream, &cdev->stream_list, node) { u32 off, size; int ret; @@ -180,6 +181,7 @@ catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) { struct catpt_stream_runtime *stream; + /* Lockless as no streams can be added or removed during D3 -> D0 transition. */ list_for_each_entry(stream, &cdev->stream_list, node) { u32 off, size; int ret; diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 2c5ea4e0ff3d..fbe4821755bd 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -5,6 +5,7 @@ // Author: Cezary Rojewski // +#include #include #include #include @@ -97,12 +98,12 @@ catpt_get_stream_template(struct snd_pcm_substream *substream) return catpt_topology[type]; } +/* Caller responsible for holding ->stream_mutex. */ struct catpt_stream_runtime * catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id) { struct catpt_stream_runtime *pos, *result = NULL; - spin_lock(&cdev->list_lock); list_for_each_entry(pos, &cdev->stream_list, node) { if (pos->info.stream_hw_id == stream_hw_id) { result = pos; @@ -110,7 +111,6 @@ catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id) } } - spin_unlock(&cdev->list_lock); return result; } @@ -286,10 +286,6 @@ static int catpt_dai_startup(struct snd_pcm_substream *substream, INIT_LIST_HEAD(&stream->node); snd_soc_dai_set_dma_data(dai, substream, stream); - spin_lock(&cdev->list_lock); - list_add_tail(&stream->node, &cdev->stream_list); - spin_unlock(&cdev->list_lock); - return 0; err_request: @@ -307,10 +303,6 @@ static void catpt_dai_shutdown(struct snd_pcm_substream *substream, stream = snd_soc_dai_get_dma_data(dai, substream); - spin_lock(&cdev->list_lock); - list_del(&stream->node); - spin_unlock(&cdev->list_lock); - release_resource(stream->persistent); kfree(stream->persistent); catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); @@ -410,12 +402,15 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, if (ret) return CATPT_IPC_RET(ret); + guard(mutex)(&cdev->stream_mutex); + ret = catpt_dai_apply_usettings(dai, stream); if (ret) { catpt_ipc_free_stream(cdev, stream->info.stream_hw_id); return ret; } + list_add_tail(&stream->node, &cdev->stream_list); stream->allocated = true; return 0; } @@ -430,6 +425,10 @@ static int catpt_dai_hw_free(struct snd_pcm_substream *substream, if (!stream->allocated) return 0; + mutex_lock(&cdev->stream_mutex); + list_del(&stream->node); + mutex_unlock(&cdev->stream_mutex); + catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id); catpt_ipc_free_stream(cdev, stream->info.stream_hw_id); @@ -910,6 +909,8 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, int ret; int i; + guard(mutex)(&cdev->stream_mutex); + stream = catpt_stream_find(cdev, pin_id); if (!stream) { for (i = 0; i < CATPT_CHANNELS_MAX; i++) @@ -941,6 +942,8 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, long *ctlvol = (long *)kcontrol->private_value; int ret, i; + guard(mutex)(&cdev->stream_mutex); + stream = catpt_stream_find(cdev, pin_id); if (!stream) { for (i = 0; i < CATPT_CHANNELS_MAX; i++) @@ -1017,6 +1020,8 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, bool mute; int ret; + guard(mutex)(&cdev->stream_mutex); + mute = (bool)ucontrol->value.integer.value[0]; stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE); if (!stream) { From d16b942aa7fa6135a44d156246d98e50b5a0aad3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 9 Mar 2026 10:16:02 +0100 Subject: [PATCH 2/5] 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 Link: https://patch.msgid.link/20260309091605.896307-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/messages.h | 3 + sound/soc/intel/catpt/pcm.c | 149 +++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h index a634943eb669..fcc9f1b46bc2 100644 --- a/sound/soc/intel/catpt/messages.h +++ b/sound/soc/intel/catpt/messages.h @@ -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 { diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index fbe4821755bd..08f1e36a136f 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -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), \ From 2464febd81e4c98f78466462da938ed2f8c37e17 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 9 Mar 2026 10:16:03 +0100 Subject: [PATCH 3/5] ASoC: Intel: catpt: Simplify procedure of applying user settings Existing catpt_dai_apply_usettings() applies all the individual control settings but why-what is covered behind if-statements. Refactor the operation into: catpt_apply_controls() |__ catpt_apply_volume() |__ catpt_apply_mute() to make it easy to understand why and what is going on. The update also enlists snd_ctl_find_id_mixer() for the query purpose, removing code duplication. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260309091605.896307-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 66 +++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 08f1e36a136f..3074b6c958fa 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -359,46 +359,48 @@ struct catpt_control_data { long volumes[CATPT_CHANNELS_MAX]; }; -static int catpt_dai_apply_usettings(struct snd_soc_dai *dai, - struct catpt_stream_runtime *stream) +static int catpt_apply_volume(struct catpt_dev *cdev, struct snd_soc_card *card, const char *name) { - struct snd_soc_component *component = dai->component; - struct snd_kcontrol *pos; - struct catpt_dev *cdev = dev_get_drvdata(dai->dev); - const char *name; - int ret; - u32 id = stream->info.stream_hw_id; + struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card->snd_card, name); + struct catpt_control_data *data; - /* only selected streams have individual controls */ - switch (id) { + if (!kctl) + return -ENOENT; + data = (struct catpt_control_data *)kctl->private_value; + + return catpt_set_dspvol(cdev, data->pin_id, data->volumes); +} + +static int catpt_apply_mute(struct catpt_dev *cdev, struct snd_soc_card *card) +{ + struct snd_kcontrol *kctl = snd_ctl_find_id_mixer(card->snd_card, "Loopback Mute"); + bool mute; + int ret; + + if (!kctl) + return -ENOENT; + mute = *(bool *)kctl->private_value; + + ret = catpt_ipc_mute_loopback(cdev, CATPT_PIN_ID_REFERENCE, mute); + return CATPT_IPC_RET(ret); +} + +static int catpt_apply_controls(struct catpt_dev *cdev, struct snd_soc_card *card, + struct catpt_stream_runtime *stream) +{ + /* Only selected streams have individual controls. */ + switch (stream->info.stream_hw_id) { case CATPT_PIN_ID_OFFLOAD1: - name = "Media0 Playback Volume"; - break; + return catpt_apply_volume(cdev, card, "Media0 Playback Volume"); case CATPT_PIN_ID_OFFLOAD2: - name = "Media1 Playback Volume"; - break; + return catpt_apply_volume(cdev, card, "Media1 Playback Volume"); case CATPT_PIN_ID_CAPTURE1: - name = "Mic Capture Volume"; - break; + return catpt_apply_volume(cdev, card, "Mic Capture Volume"); case CATPT_PIN_ID_REFERENCE: - name = "Loopback Mute"; - break; + return catpt_apply_mute(cdev, card); default: return 0; } - - list_for_each_entry(pos, &component->card->snd_card->controls, list) { - if (pos->private_data == component && - !strncmp(name, pos->id.name, sizeof(pos->id.name))) - break; - } - if (list_entry_is_head(pos, &component->card->snd_card->controls, list)) - return -ENOENT; - - if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK) - return catpt_set_dspvol(cdev, id, (long *)pos->private_value); - ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)pos->private_value); - return CATPT_IPC_RET(ret); } static int catpt_dai_hw_params(struct snd_pcm_substream *substream, @@ -449,7 +451,7 @@ static int catpt_dai_hw_params(struct snd_pcm_substream *substream, guard(mutex)(&cdev->stream_mutex); - ret = catpt_dai_apply_usettings(dai, stream); + ret = catpt_apply_controls(cdev, dai->component->card, stream); if (ret) { catpt_ipc_free_stream(cdev, stream->info.stream_hw_id); return ret; From 150badf73e9a10cf646d07353b41117ea90f67ae Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 9 Mar 2026 10:16:04 +0100 Subject: [PATCH 4/5] ASoC: Intel: catpt: Do not wake DSP just for volume setup With the new control-operations in place, the controls no longer wake the DSP just for the sake of updating volume (or mute) settings on the firmware side. The values are cached and actual update occurs only when streams are being opened for streaming or are already running. In those cases the DSP must already be woken up and we avoid unnecessary power consumption. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260309091605.896307-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 3074b6c958fa..2ed9df7bc434 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -388,6 +388,15 @@ static int catpt_apply_mute(struct catpt_dev *cdev, struct snd_soc_card *card) static int catpt_apply_controls(struct catpt_dev *cdev, struct snd_soc_card *card, struct catpt_stream_runtime *stream) { + int ret; + + /* Update the master volume when the first stream is opened. */ + if (list_empty(&cdev->stream_list)) { + ret = catpt_apply_volume(cdev, card, "Master Playback Volume"); + if (ret) + return ret; + } + /* Only selected streams have individual controls. */ switch (stream->info.stream_hw_id) { case CATPT_PIN_ID_OFFLOAD1: From 8a99ccb032d670eae2b1ea89174e06a60d2d3fc2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 9 Mar 2026 10:16:05 +0100 Subject: [PATCH 5/5] ASoC: Intel: catpt: Migrate to the new control operations Switch to the new implementation and remove all unused code. The change effectively causes the control put() operations to return '1' if a change occurred, '0' if no change was made or error otherwise. The second effect of the update is reducing the power consumption. With the new control-operations in place, the controls no longer wake the DSP just for the sake of updating volume (or mute) settings on the firmware side. The values are cached and actual update occurs only when streams are being opened for streaming or are already running. Signed-off-by: Cezary Rojewski Link: https://patch.msgid.link/20260309091605.896307-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 243 +----------------------------------- 1 file changed, 6 insertions(+), 237 deletions(-) diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 2ed9df7bc434..8356dbdb2809 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -160,32 +160,6 @@ static void catpt_stream_read_position(struct catpt_dev *cdev, memcpy_fromio(pos, cdev->lpe_ba + stream->info.read_pos_regaddr, sizeof(*pos)); } -static u32 catpt_stream_volume(struct catpt_dev *cdev, - struct catpt_stream_runtime *stream, u32 channel) -{ - u32 volume, offset; - - if (channel >= CATPT_CHANNELS_MAX) - channel = 0; - - offset = stream->info.volume_regaddr[channel]; - memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume)); - return volume; -} - -static u32 catpt_mixer_volume(struct catpt_dev *cdev, - struct catpt_mixer_stream_info *info, u32 channel) -{ - u32 volume, offset; - - if (channel >= CATPT_CHANNELS_MAX) - channel = 0; - - offset = info->volume_regaddr[channel]; - memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume)); - return volume; -} - static void catpt_arrange_page_table(struct snd_pcm_substream *substream, struct snd_dma_buffer *pgtbl) { @@ -911,7 +885,6 @@ 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); @@ -938,7 +911,6 @@ static int catpt_volume_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value 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); @@ -967,14 +939,12 @@ static int catpt_volume_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value 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); @@ -1002,195 +972,6 @@ static int catpt_loopback_mute_put(struct snd_kcontrol *kctl, struct snd_ctl_ele return 1; } -static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - u32 dspvol; - int ret; - int i; - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; - - for (i = 0; i < CATPT_CHANNELS_MAX; i++) { - dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i); - ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); - } - - pm_runtime_put_autosuspend(cdev->dev); - - return 0; -} - -static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - int ret; - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; - - ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id, - ucontrol->value.integer.value); - - pm_runtime_put_autosuspend(cdev->dev); - - return ret; -} - -static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - enum catpt_pin_id pin_id) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - long *ctlvol = (long *)kcontrol->private_value; - u32 dspvol; - int ret; - int i; - - guard(mutex)(&cdev->stream_mutex); - - stream = catpt_stream_find(cdev, pin_id); - if (!stream) { - for (i = 0; i < CATPT_CHANNELS_MAX; i++) - ucontrol->value.integer.value[i] = ctlvol[i]; - return 0; - } - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; - - for (i = 0; i < CATPT_CHANNELS_MAX; i++) { - dspvol = catpt_stream_volume(cdev, stream, i); - ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); - } - - pm_runtime_put_autosuspend(cdev->dev); - - return 0; -} - -static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - enum catpt_pin_id pin_id) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - long *ctlvol = (long *)kcontrol->private_value; - int ret, i; - - guard(mutex)(&cdev->stream_mutex); - - stream = catpt_stream_find(cdev, pin_id); - if (!stream) { - for (i = 0; i < CATPT_CHANNELS_MAX; i++) - ctlvol[i] = ucontrol->value.integer.value[i]; - return 0; - } - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; - - ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id, - ucontrol->value.integer.value); - - pm_runtime_put_autosuspend(cdev->dev); - - if (ret) - return ret; - - for (i = 0; i < CATPT_CHANNELS_MAX; i++) - ctlvol[i] = ucontrol->value.integer.value[i]; - return 0; -} - -static int catpt_offload1_volume_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1); -} - -static int catpt_offload1_volume_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1); -} - -static int catpt_offload2_volume_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2); -} - -static int catpt_offload2_volume_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2); -} - -static int catpt_capture_volume_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1); -} - -static int catpt_capture_volume_put(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *uctl) -{ - return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1); -} - -static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value; - return 0; -} - -static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct catpt_stream_runtime *stream; - struct catpt_dev *cdev = dev_get_drvdata(component->dev); - bool mute; - int ret; - - guard(mutex)(&cdev->stream_mutex); - - mute = (bool)ucontrol->value.integer.value[0]; - stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE); - if (!stream) { - *(bool *)kcontrol->private_value = mute; - return 0; - } - - ret = pm_runtime_resume_and_get(cdev->dev); - if (ret) - return ret; - - ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute); - - pm_runtime_put_autosuspend(cdev->dev); - - if (ret) - return CATPT_IPC_RET(ret); - - *(bool *)kcontrol->private_value = mute; - return 0; -} - static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1219,7 +1000,7 @@ 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) { \ +#define CATPT_VOLUME_CTL(kname, pname) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = kname, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ @@ -1232,27 +1013,15 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1); &(struct catpt_control_data) { CATPT_PIN_ID_##pname } \ } -#define CATPT_VOLUME_CTL(kname, sname) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = (kname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .info = catpt_volume_info, \ - .get = catpt_##sname##_volume_get, \ - .put = catpt_##sname##_volume_put, \ - .tlv.p = catpt_volume_tlv, \ - .private_value = (unsigned long) \ - &(long[CATPT_CHANNELS_MAX]) {0} } - static const struct snd_kcontrol_new component_kcontrols[] = { /* Master volume (mixer stream) */ -CATPT_VOLUME_CTL("Master Playback Volume", mixer), +CATPT_VOLUME_CTL("Master Playback Volume", MIXER), /* Individual volume controls for offload and capture */ -CATPT_VOLUME_CTL("Media0 Playback Volume", offload1), -CATPT_VOLUME_CTL("Media1 Playback Volume", offload2), -CATPT_VOLUME_CTL("Mic Capture Volume", capture), +CATPT_VOLUME_CTL("Media0 Playback Volume", OFFLOAD1), +CATPT_VOLUME_CTL("Media1 Playback Volume", OFFLOAD2), +CATPT_VOLUME_CTL("Mic Capture Volume", CAPTURE1), SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0}, - catpt_loopback_switch_get, catpt_loopback_switch_put), + catpt_loopback_mute_get, catpt_loopback_mute_put), /* Enable or disable WAVES module */ SOC_SINGLE_BOOL_EXT("Waves Switch", 0, catpt_waves_switch_get, catpt_waves_switch_put),