Skip to content

Capture hw mute indicator #5310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: topic/sof-dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion include/sound/sof/ipc4/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
16 changes: 15 additions & 1 deletion sound/soc/sof/intel/hda-dsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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");

Expand Down
162 changes: 161 additions & 1 deletion sound/soc/sof/ipc4-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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,
};
3 changes: 3 additions & 0 deletions sound/soc/sof/ipc4-loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 4 additions & 0 deletions sound/soc/sof/ipc4-priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions sound/soc/sof/sof-audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};

/**
Expand Down
11 changes: 9 additions & 2 deletions sound/soc/sof/topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Loading