From d010ca901a4347e4ffb2c4bf863c8b5ae093246e Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 24 Jan 2025 22:29:26 +0200 Subject: [PATCH 1/5] ASoC: SOF: ipc4: Add enum for global kcontrol IDs Add enum for global kcontrol IDs and SOF_IPC4_KCONTROL_GLOBAL_CAPTURE_HW_MUTE in it. Signed-off-by: Jyri Sarha --- include/sound/sof/ipc4/header.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 0c0cf47946b1df..0ee9292b88970c 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -567,6 +567,14 @@ struct sof_ipc4_notify_module_data { #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL 0xA15A0000 #define SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK GENMASK(15, 0) +/* + * Global non topology kcontrol ids + */ + +enum sof_ipc4_kcontrol_global_id { + SOF_IPC4_KCONTROL_GLOBAL_CAPTURE_HW_MUTE = 1, +}; + /** @}*/ #endif From 137ee123cb8fd9e194aacd37bf62f87f4fa13608 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Sat, 25 Jan 2025 00:08:32 +0200 Subject: [PATCH 2/5] ASoC: SOF: IPC4: Add SOF_IPC4_FW_GLOBAL_KCONTROL_MASK to fw_config_params Add SOF_IPC4_FW_GLOBAL_KCONTROL_MASK to fw_config_params. The purpose is to inform the driver which global kcontrols are supported by the FW. Signed-off-by: Jyri Sarha --- include/sound/sof/ipc4/header.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 0ee9292b88970c..b5950ee68dc61f 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -428,7 +428,8 @@ enum sof_ipc4_fw_config_params { SOF_IPC4_FW_RESERVED3, SOF_IPC4_FW_RESERVED4, SOF_IPC4_FW_RESERVED5, - SOF_IPC4_FW_CONTEXT_SAVE + SOF_IPC4_FW_CONTEXT_SAVE, + SOF_IPC4_FW_GLOBAL_KCONTROL_MASK, }; struct sof_ipc4_fw_version { From a6f05dc6d49e7b5a250eb9d67c70e8db3d94dcd5 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 20 Jan 2025 22:02:28 +0200 Subject: [PATCH 3/5] ASoC: sof: ipc4-control: Global mixers and capture hw mute indicator Implement global kcontrol concept and add capture hw mute indicator implementation in it. Signed-off-by: Jyri Sarha --- sound/soc/sof/ipc4-control.c | 135 ++++++++++++++++++++++++++++++++++- sound/soc/sof/ipc4-loader.c | 3 + sound/soc/sof/ipc4-priv.h | 2 + sound/soc/sof/sof-audio.h | 2 + sound/soc/sof/topology.c | 11 ++- 5 files changed, 150 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 576f407cd456af..d52cb9d01ec104 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -649,6 +649,130 @@ sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); } +struct sof_ipc4_kcontrol_global_priv { + struct snd_card *snd_card; + struct snd_kcontrol *capture_hw_mute; +}; + +static int sof_ipc4_capture_hw_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = kcontrol->private_value; + return 0; +} + +static const struct snd_kcontrol_new sof_ipc4_capture_hw_mute = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .name = "Global capture HW mute indicator", + .info = snd_ctl_boolean_mono_info, + .get = sof_ipc4_capture_hw_mute_get, +}; + +static struct snd_card *sof_scomp_get_card(struct snd_soc_component *scomp) +{ + struct device *dev = scomp->dev; + + if (!scomp->card) { + dev_err(dev, "%s: No snd_soc_card", __func__); + return NULL; + } + + return scomp->card->snd_card; +} + +static int sof_ipc4_create_non_topology_controls(struct snd_sof_dev *sdev, + struct snd_soc_component *scomp) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct snd_card *snd_card = sof_scomp_get_card(scomp); + struct device *dev = sdev->dev; + struct sof_ipc4_kcontrol_global_priv *priv; + int ret; + + if (!snd_card) { + dev_err(dev, "%s: Card not found!", __func__); + return -ENODEV; + } + + if (!ipc4_data->global_kcontrol_mask) + return 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->snd_card = snd_card; + + if (ipc4_data->global_kcontrol_mask & + BIT(SOF_IPC4_KCONTROL_GLOBAL_CAPTURE_HW_MUTE)) { + struct snd_kcontrol *kctl; + + kctl = snd_ctl_new1(&sof_ipc4_capture_hw_mute, NULL); + if (!kctl) { + dev_err(dev, "%s: snd_ctl_new1(): failed", __func__); + return -EINVAL; + } + + kctl->private_value = 0; + + ret = snd_ctl_add(snd_card, kctl); + if (ret < 0) + dev_err(dev, "%s: snd_ctl_add(): failed", __func__); + else + priv->capture_hw_mute = kctl; + } + + ipc4_data->global_kcontrol_priv = priv; + + return 0; +} + +static void snd_ipc4_global_capture_hw_mute_report(struct snd_sof_dev *sdev, + bool status) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_kcontrol_global_priv *priv = ipc4_data->global_kcontrol_priv; + struct device *dev = sdev->dev; + struct snd_kcontrol *kctl; + + if (!priv || !priv->capture_hw_mute) + return; + + kctl = priv->capture_hw_mute; + + dev_dbg(dev, "%s: reporting %u, old %lu", __func__, + status, kctl->private_value); + + if (kctl->private_value == status) + return; + + kctl->private_value = status; + snd_ctl_notify(priv->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); +} + +static void snd_ipc4_handle_global_event(struct snd_sof_dev *sdev, + struct sof_ipc4_control_msg_payload *msg_data) +{ + struct device *dev = sdev->dev; + + dev_dbg(dev, "%s: got msg id %u num_elems %u", __func__, + msg_data->id, msg_data->num_elems); + + switch (msg_data->id) { + case SOF_IPC4_KCONTROL_GLOBAL_CAPTURE_HW_MUTE: + if (msg_data->num_elems == 1 || msg_data->chanv[0].channel == 0) + snd_ipc4_global_capture_hw_mute_report(sdev, msg_data->chanv[0].value); + else + dev_warn(dev, "%s: bad data for id %u chan 0 %u", __func__, + msg_data->id, msg_data->chanv[0].channel); + break; + default: + dev_warn(dev, "%s: unknown global control elem id %u", __func__, + msg_data->id); + } +} + #define PARAM_ID_FROM_EXTENSION(_ext) (((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) \ >> SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT) @@ -673,6 +797,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) ndata->event_data_size); return; } + msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data; event_param_id = ndata->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_PARAMID_MASK; switch (event_param_id) { @@ -690,6 +815,14 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) return; } + if (ndata->module_id == SOF_IPC4_MOD_INIT_BASEFW_MOD_ID && + ndata->instance_id == SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID) { + dev_dbg(sdev->dev, "Received global notifier event_id %#x", + event_param_id); + snd_ipc4_handle_global_event(sdev, msg_data); + return; + } + /* Find the swidget based on ndata->module_id and ndata->instance_id */ swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id, ndata->instance_id); @@ -700,7 +833,6 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) } /* Find the scontrol which is the source of the notification */ - msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data; list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { u32 local_param_id; @@ -855,4 +987,5 @@ const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { .update = sof_ipc4_control_update, .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, .set_up_volume_table = sof_ipc4_set_up_volume_table, + .create_non_topology_controls = sof_ipc4_create_non_topology_controls, }; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 456b9885d06ca1..339f6032d11103 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -411,6 +411,9 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) case SOF_IPC4_FW_CONTEXT_SAVE: ipc4_data->fw_context_save = *tuple->value; break; + case SOF_IPC4_FW_GLOBAL_KCONTROL_MASK: + ipc4_data->global_kcontrol_mask = *tuple->value; + break; default: break; } diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index ea3323b90343df..1ca96a36792209 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -86,6 +86,8 @@ struct sof_ipc4_fw_data { int max_num_pipelines; u32 max_libs_count; bool fw_context_save; + u32 global_kcontrol_mask; + void *global_kcontrol_priv; int (*load_library)(struct snd_sof_dev *sdev, struct sof_ipc4_fw_library *fw_lib, bool reload); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 36ab75e11779d2..045074364de981 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -166,6 +166,8 @@ struct sof_ipc_tplg_control_ops { /* mandatory callback to set up volume table for volume kcontrols */ int (*set_up_volume_table)(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size); + int (*create_non_topology_controls)(struct snd_sof_dev *sdev, + struct snd_soc_component *scomp); }; /** diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 688cc7ac17148a..ea6e417dc9346b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2251,8 +2251,15 @@ static int sof_complete(struct snd_soc_component *scomp) } /* set up static pipelines */ - if (tplg_ops && tplg_ops->set_up_all_pipelines) - return tplg_ops->set_up_all_pipelines(sdev, false); + if (tplg_ops && tplg_ops->set_up_all_pipelines) { + ret = tplg_ops->set_up_all_pipelines(sdev, false); + if (ret < 0) + return ret; + } + + /* set up non topology mixers */ + if (tplg_ops && tplg_ops->control && tplg_ops->control->create_non_topology_controls) + return tplg_ops->control->create_non_topology_controls(sdev, scomp); return 0; } From ac691ee3198d20930eb9c380ea1d4b8e007d45f0 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 27 Jan 2025 22:30:11 +0200 Subject: [PATCH 4/5] ASoC: sof: ipc4-control: Force method for global capture hw mute indicator This is inteded for driving the mute indicator following dsp power state. If dsp is powerred down the capture muted state should be indicated. Signed-off-by: Jyri Sarha --- sound/soc/sof/ipc4-control.c | 43 +++++++++++++++++++++++++++++------- sound/soc/sof/ipc4-priv.h | 2 ++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index d52cb9d01ec104..3150364e6c3def 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -651,7 +651,11 @@ sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, struct sof_ipc4_kcontrol_global_priv { struct snd_card *snd_card; - struct snd_kcontrol *capture_hw_mute; + struct sof_ipc4_kcontrol_global_capture_hw_mute { + struct snd_kcontrol *kctl; + bool force_muted; /* If true mute state indicator forced to muted */ + bool last_reported; /* The latest mute state received from FW */ + } capture_hw_mute; }; static int sof_ipc4_capture_hw_mute_get(struct snd_kcontrol *kcontrol, @@ -717,10 +721,13 @@ static int sof_ipc4_create_non_topology_controls(struct snd_sof_dev *sdev, kctl->private_value = 0; ret = snd_ctl_add(snd_card, kctl); - if (ret < 0) + if (ret >= 0) { + priv->capture_hw_mute.kctl = kctl; + priv->capture_hw_mute.force_muted = false; + priv->capture_hw_mute.last_reported = false; + } else { dev_err(dev, "%s: snd_ctl_add(): failed", __func__); - else - priv->capture_hw_mute = kctl; + } } ipc4_data->global_kcontrol_priv = priv; @@ -736,13 +743,19 @@ static void snd_ipc4_global_capture_hw_mute_report(struct snd_sof_dev *sdev, struct device *dev = sdev->dev; struct snd_kcontrol *kctl; - if (!priv || !priv->capture_hw_mute) + if (!priv || !priv->capture_hw_mute.kctl) return; - kctl = priv->capture_hw_mute; + kctl = priv->capture_hw_mute.kctl; - dev_dbg(dev, "%s: reporting %u, old %lu", __func__, - status, kctl->private_value); + dev_dbg(dev, "%s: new %u, old %lu, last %u force %u", __func__, + status, kctl->private_value, priv->capture_hw_mute.last_reported, + priv->capture_hw_mute.force_muted); + + priv->capture_hw_mute.last_reported = status; + + if (priv->capture_hw_mute.force_muted) + status = false; if (kctl->private_value == status) return; @@ -751,6 +764,20 @@ static void snd_ipc4_global_capture_hw_mute_report(struct snd_sof_dev *sdev, snd_ctl_notify(priv->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); } +void snd_ipc4_global_capture_hw_mute_force(struct snd_sof_dev *sdev, bool force) +{ + struct sof_ipc4_fw_data *ipc4_data = sdev->private; + struct sof_ipc4_kcontrol_global_priv *priv = ipc4_data->global_kcontrol_priv; + + if (!priv) + return; + + priv->capture_hw_mute.force_muted = force; + + snd_ipc4_global_capture_hw_mute_report(sdev, priv->capture_hw_mute.last_reported); +} +EXPORT_SYMBOL(snd_ipc4_global_capture_hw_mute_force); + static void snd_ipc4_handle_global_event(struct snd_sof_dev *sdev, struct sof_ipc4_control_msg_payload *msg_data) { diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 1ca96a36792209..147c76aecc6577 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -119,4 +119,6 @@ void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev, u32 slot_type); +void snd_ipc4_global_capture_hw_mute_force(struct snd_sof_dev *sdev, bool force); + #endif From d65892b2d00033df560cd9d7a4047866b64db20c Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 5 Feb 2025 15:53:37 +0200 Subject: [PATCH 5/5] ASoC: SOF: Intel: Force global capture hw mute according to DSP power Force global capture hw mute to muted state when DSP is powered down. This is done by calling snd_ipc4_global_capture_hw_mute_force() from hda_dsp_set_power_state_ipc4(). Signed-off-by: Jyri Sarha --- sound/soc/sof/intel/hda-dsp.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index ec6453eddfd2c8..57139cbeb28053 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -26,6 +26,9 @@ #include "hda.h" #include "mtl.h" #include "hda-ipc.h" +#ifdef CONFIG_SND_SOC_SOF_IPC4 +#include "../ipc4-priv.h" +#endif #define EXCEPT_MAX_HDR_SIZE 0x400 #define HDA_EXT_ROM_STATUS_SIZE 8 @@ -752,11 +755,22 @@ int hda_dsp_set_power_state_ipc4(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { /* Return without doing anything if the DSP is already in the target state */ + int ret; + if (target_state->state == sdev->dsp_power_state.state && target_state->substate == sdev->dsp_power_state.substate) return 0; - return hda_dsp_set_power_state(sdev, target_state); + ret = hda_dsp_set_power_state(sdev, target_state); + +#ifdef CONFIG_SND_SOC_SOF_IPC4 + if (ret >= 0) + snd_ipc4_global_capture_hw_mute_force(sdev, + target_state->state == SOF_DSP_PM_D3 ? + true : false); +#endif + + return ret; } EXPORT_SYMBOL_NS(hda_dsp_set_power_state_ipc4, "SND_SOC_SOF_INTEL_HDA_COMMON");