diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index 53ea94bf1c08d3..220dd092c74653 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -104,4 +104,8 @@ /* for backward compatibility */ #define SOF_TKN_EFFECT_TYPE SOF_TKN_PROCESS_TYPE +/* Led control for mute switches */ +#define SOF_TKN_MUTE_LED_USE 1300 +#define SOF_TKN_MUTE_LED_DIRECTION 1301 + #endif diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index a4983f90ff5b31..2c4abd406c4fa9 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -11,8 +11,39 @@ /* Mixer Controls */ #include +#include #include "sof-priv.h" +static void update_mute_led(struct snd_sof_control *scontrol, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int temp = 0; + unsigned int mask; + int i; + + mask = 1U << snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + for (i = 0; i < scontrol->num_channels; i++) { + if (ucontrol->value.integer.value[i]) { + temp |= mask; + break; + } + } + + if (temp == scontrol->led_ctl.led_value) + return; + + scontrol->led_ctl.led_value = temp; + +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + if (!scontrol->led_ctl.direction) + ledtrig_audio_set(LED_AUDIO_MUTE, temp ? LED_OFF : LED_ON); + else + ledtrig_audio_set(LED_AUDIO_MICMUTE, temp ? LED_OFF : LED_ON); +#endif +} + static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) { if (value >= size) @@ -112,6 +143,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, cdata->chanv[i].channel = i; } + if (scontrol->led_ctl.use_led) + update_mute_led(scontrol, kcontrol, ucontrol); + /* notify DSP of mixer updates */ if (pm_runtime_active(sdev->dev)) snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4608437bffbef6..ac87a672b78aba 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -15,6 +15,7 @@ #include #include +#include #include #include /* needs to be included before control.h */ @@ -296,10 +297,18 @@ struct snd_sof_pcm { int hw_params_upon_resume[2]; /* set up hw_params upon resume */ }; +struct snd_sof_led_control { + unsigned int use_led; + unsigned int direction; + unsigned int led_value; +}; + /* ALSA SOF Kcontrol device */ struct snd_sof_control { struct snd_sof_dev *sdev; int comp_id; + int min_volume_step; /* min volume step for volume_table */ + int max_volume_step; /* max volume step for volume_table */ int num_channels; u32 readback_offset; /* offset to mmaped data if used */ struct sof_ipc_ctrl_data *control_data; @@ -308,6 +317,8 @@ struct snd_sof_control { u32 *volume_table; /* volume table computed from tlv data*/ struct list_head list; /* list in sdev control list */ + + struct snd_sof_led_control led_ctl; }; /* ASoC SOF DAPM widget */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 38977665b950e9..b0b9e02fd507f5 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -420,163 +420,6 @@ static enum sof_comp_type find_process_comp_type(enum sof_ipc_process_type type) return SOF_COMP_NONE; } -/* - * Standard Kcontrols. - */ - -static int sof_control_load_volume(struct snd_soc_component *scomp, - struct snd_sof_control *scontrol, - struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_mixer_control *mc = - container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); - struct sof_ipc_ctrl_data *cdata; - int tlv[TLV_ITEMS]; - unsigned int i; - int ret; - - /* validate topology data */ - if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) - return -EINVAL; - - /* init the volume get/put data */ - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - sizeof(struct sof_ipc_ctrl_value_chan) * - le32_to_cpu(mc->num_channels); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) - return -ENOMEM; - - scontrol->comp_id = sdev->next_comp_id; - scontrol->num_channels = le32_to_cpu(mc->num_channels); - - /* set cmd for mixer control */ - if (le32_to_cpu(mc->max) == 1) { - scontrol->cmd = SOF_CTRL_CMD_SWITCH; - goto out; - } - - scontrol->cmd = SOF_CTRL_CMD_VOLUME; - - /* extract tlv data */ - if (get_tlv_data(kc->tlv.p, tlv) < 0) { - dev_err(sdev->dev, "error: invalid TLV data\n"); - return -EINVAL; - } - - /* set up volume table */ - ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); - if (ret < 0) { - dev_err(sdev->dev, "error: setting up volume table\n"); - return ret; - } - - /* set default volume values to 0dB in control */ - cdata = scontrol->control_data; - for (i = 0; i < scontrol->num_channels; i++) { - cdata->chanv[i].channel = i; - cdata->chanv[i].value = VOL_ZERO_DB; - } - -out: - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); - - return 0; -} - -static int sof_control_load_enum(struct snd_soc_component *scomp, - struct snd_sof_control *scontrol, - struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_soc_tplg_enum_control *ec = - container_of(hdr, struct snd_soc_tplg_enum_control, hdr); - - /* validate topology data */ - if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) - return -EINVAL; - - /* init the enum get/put data */ - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - sizeof(struct sof_ipc_ctrl_value_chan) * - le32_to_cpu(ec->num_channels); - scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); - if (!scontrol->control_data) - return -ENOMEM; - - scontrol->comp_id = sdev->next_comp_id; - scontrol->num_channels = le32_to_cpu(ec->num_channels); - - scontrol->cmd = SOF_CTRL_CMD_ENUM; - - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", - scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); - - return 0; -} - -static int sof_control_load_bytes(struct snd_soc_component *scomp, - struct snd_sof_control *scontrol, - struct snd_kcontrol_new *kc, - struct snd_soc_tplg_ctl_hdr *hdr) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc_ctrl_data *cdata; - struct snd_soc_tplg_bytes_control *control = - container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); - struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; - int max_size = sbe->max; - - if (le32_to_cpu(control->priv.size) > max_size) { - dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n", - control->priv.size, max_size); - return -EINVAL; - } - - /* init the get/put bytes data */ - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - le32_to_cpu(control->priv.size); - scontrol->control_data = kzalloc(max_size, GFP_KERNEL); - cdata = scontrol->control_data; - if (!scontrol->control_data) - return -ENOMEM; - - scontrol->comp_id = sdev->next_comp_id; - scontrol->cmd = SOF_CTRL_CMD_BINARY; - - dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); - - if (le32_to_cpu(control->priv.size) > 0) { - memcpy(cdata->data, control->priv.data, - le32_to_cpu(control->priv.size)); - - if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n", - cdata->data->magic); - return -EINVAL; - } - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, - cdata->data->abi)) { - dev_err(sdev->dev, - "error: Incompatible ABI version 0x%08x.\n", - cdata->data->abi); - return -EINVAL; - } - if (cdata->data->size + sizeof(const struct sof_abi_hdr) != - le32_to_cpu(control->priv.size)) { - dev_err(sdev->dev, - "error: Conflict in bytes vs. priv size.\n"); - return -EINVAL; - } - } - return 0; -} - /* * Topology Token Parsing. * New tokens should be added to headers and parsing tables below. @@ -820,6 +663,14 @@ static const struct sof_topology_token dmic_pdm_tokens[] = { static const struct sof_topology_token hda_tokens[] = { }; +/* Leds */ +static const struct sof_topology_token led_tokens[] = { + {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct snd_sof_led_control, use_led), 0}, + {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, + get_token_u32, offsetof(struct snd_sof_led_control, direction), 0}, +}; + static void sof_parse_uuid_tokens(struct snd_soc_component *scomp, void *object, const struct sof_topology_token *tokens, @@ -1018,6 +869,173 @@ static void sof_dbg_comp_config(struct snd_soc_component *scomp, config->frame_fmt); } +/* + * Standard Kcontrols. + */ + +static int sof_control_load_volume(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_mixer_control *mc = + container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); + struct sof_ipc_ctrl_data *cdata; + int tlv[TLV_ITEMS]; + unsigned int i; + int ret; + + /* validate topology data */ + if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; + + /* init the volume get/put data */ + scontrol->size = struct_size(scontrol->control_data, chanv, + le32_to_cpu(mc->num_channels)); + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->min_volume_step = le32_to_cpu(mc->min); + scontrol->max_volume_step = le32_to_cpu(mc->max); + scontrol->num_channels = le32_to_cpu(mc->num_channels); + + /* set cmd for mixer control */ + if (le32_to_cpu(mc->max) == 1) { + scontrol->cmd = SOF_CTRL_CMD_SWITCH; + goto out; + } + + scontrol->cmd = SOF_CTRL_CMD_VOLUME; + + /* extract tlv data */ + if (get_tlv_data(kc->tlv.p, tlv) < 0) { + dev_err(sdev->dev, "error: invalid TLV data\n"); + return -EINVAL; + } + + /* set up volume table */ + ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); + if (ret < 0) { + dev_err(sdev->dev, "error: setting up volume table\n"); + return ret; + } + + /* set default volume values to 0dB in control */ + cdata = scontrol->control_data; + for (i = 0; i < scontrol->num_channels; i++) { + cdata->chanv[i].channel = i; + cdata->chanv[i].value = VOL_ZERO_DB; + } + +out: + /* set up possible led control from mixer private data */ + ret = sof_parse_tokens(scomp, &scontrol->led_ctl, led_tokens, + ARRAY_SIZE(led_tokens), mc->priv.array, + le32_to_cpu(mc->priv.size)); + if (ret != 0) { + dev_err(sdev->dev, "error: parse led tokens failed %d\n", + le32_to_cpu(mc->priv.size)); + return ret; + } + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + return 0; +} + +static int sof_control_load_enum(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_soc_tplg_enum_control *ec = + container_of(hdr, struct snd_soc_tplg_enum_control, hdr); + + /* validate topology data */ + if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) + return -EINVAL; + + /* init the enum get/put data */ + scontrol->size = struct_size(scontrol->control_data, chanv, + le32_to_cpu(ec->num_channels)); + scontrol->control_data = kzalloc(scontrol->size, GFP_KERNEL); + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->num_channels = le32_to_cpu(ec->num_channels); + + scontrol->cmd = SOF_CTRL_CMD_ENUM; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", + scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); + + return 0; +} + +static int sof_control_load_bytes(struct snd_soc_component *scomp, + struct snd_sof_control *scontrol, + struct snd_kcontrol_new *kc, + struct snd_soc_tplg_ctl_hdr *hdr) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_ipc_ctrl_data *cdata; + struct snd_soc_tplg_bytes_control *control = + container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); + struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; + int max_size = sbe->max; + + if (le32_to_cpu(control->priv.size) > max_size) { + dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n", + control->priv.size, max_size); + return -EINVAL; + } + + /* init the get/put bytes data */ + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + + le32_to_cpu(control->priv.size); + scontrol->control_data = kzalloc(max_size, GFP_KERNEL); + cdata = scontrol->control_data; + if (!scontrol->control_data) + return -ENOMEM; + + scontrol->comp_id = sdev->next_comp_id; + scontrol->cmd = SOF_CTRL_CMD_BINARY; + + dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->comp_id, scontrol->num_channels); + + if (le32_to_cpu(control->priv.size) > 0) { + memcpy(cdata->data, control->priv.data, + le32_to_cpu(control->priv.size)); + + if (cdata->data->magic != SOF_ABI_MAGIC) { + dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n", + cdata->data->magic); + return -EINVAL; + } + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, + cdata->data->abi)) { + dev_err(sdev->dev, + "error: Incompatible ABI version 0x%08x.\n", + cdata->data->abi); + return -EINVAL; + } + if (cdata->data->size + sizeof(const struct sof_abi_hdr) != + le32_to_cpu(control->priv.size)) { + dev_err(sdev->dev, + "error: Conflict in bytes vs. priv size.\n"); + return -EINVAL; + } + } + return 0; +} + /* external kcontrol init - used for any driver specific init */ static int sof_control_load(struct snd_soc_component *scomp, int index, struct snd_kcontrol_new *kc, @@ -1555,13 +1573,16 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_volume *volume; + struct snd_sof_control *scontrol; + int min_step; + int max_step; int ret; volume = kzalloc(sizeof(*volume), GFP_KERNEL); if (!volume) return -ENOMEM; - if (le32_to_cpu(tw->num_kcontrols) != 1) { + if (!le32_to_cpu(tw->num_kcontrols)) { dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n", tw->num_kcontrols); ret = -EINVAL; @@ -1597,6 +1618,18 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, swidget->private = volume; + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + if (scontrol->comp_id == swidget->comp_id && + scontrol->volume_table) { + min_step = scontrol->min_volume_step; + max_step = scontrol->max_volume_step; + volume->min_value = scontrol->volume_table[min_step]; + volume->max_value = scontrol->volume_table[max_step]; + volume->channels = scontrol->num_channels; + break; + } + } + ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume, sizeof(*volume), r, sizeof(*r)); if (ret >= 0)