diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 0c0cf47946b1df..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 { @@ -567,6 +568,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 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"); diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 576f407cd456af..3150364e6c3def 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -649,6 +649,157 @@ 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 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, + 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) { + 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__); + } + } + + 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.kctl) + return; + + kctl = priv->capture_hw_mute.kctl; + + 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; + + kctl->private_value = status; + 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) +{ + 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 +824,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 +842,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 +860,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 +1014,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..147c76aecc6577 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); @@ -117,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 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; }