]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
ASoC: SOF: ipc4-control: Add support for ALSA switch control
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Tue, 19 Sep 2023 10:31:14 +0000 (13:31 +0300)
committerMark Brown <broonie@kernel.org>
Tue, 19 Sep 2023 12:48:56 +0000 (13:48 +0100)
Volume controls with a max value of 1 are switches.
Switch controls use generic param_id and a generic struct where the data
is passed to the firmware.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20230919103115.30783-3-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/ipc4-control.c
sound/soc/sof/ipc4-topology.c

index c6d404d440970b4eebd707ed7bb3b422d3233efe..cabdd891c644b193329fba9e4658ef59c803ba8b 100644 (file)
@@ -201,6 +201,102 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,
        return 0;
 }
 
+static int
+sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev,
+                                 struct snd_sof_widget *swidget,
+                                 struct snd_sof_control *scontrol, bool lock)
+{
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct sof_ipc4_control_msg_payload *data;
+       struct sof_ipc4_msg *msg = &cdata->msg;
+       size_t data_size;
+       unsigned int i;
+       int ret;
+
+       data_size = struct_size(data, chanv, scontrol->num_channels);
+       data = kzalloc(data_size, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->id = cdata->index;
+       data->num_elems = scontrol->num_channels;
+       for (i = 0; i < scontrol->num_channels; i++) {
+               data->chanv[i].channel = cdata->chanv[i].channel;
+               data->chanv[i].value = cdata->chanv[i].value;
+       }
+
+       msg->data_ptr = data;
+       msg->data_size = data_size;
+
+       ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
+       msg->data_ptr = NULL;
+       msg->data_size = 0;
+       if (ret < 0)
+               dev_err(sdev->dev, "Failed to set control update for %s\n",
+                       scontrol->name);
+
+       kfree(data);
+
+       return ret;
+}
+
+static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       struct snd_soc_component *scomp = scontrol->scomp;
+       struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+       struct snd_sof_widget *swidget;
+       bool widget_found = false;
+       bool change = false;
+       unsigned int i;
+       u32 value;
+       int ret;
+
+       /* update each channel */
+       for (i = 0; i < scontrol->num_channels; i++) {
+               value = ucontrol->value.integer.value[i];
+               change = change || (value != cdata->chanv[i].value);
+               cdata->chanv[i].channel = i;
+               cdata->chanv[i].value = value;
+       }
+
+       if (!pm_runtime_active(scomp->dev))
+               return change;
+
+       /* find widget associated with the control */
+       list_for_each_entry(swidget, &sdev->widget_list, list) {
+               if (swidget->comp_id == scontrol->comp_id) {
+                       widget_found = true;
+                       break;
+               }
+       }
+
+       if (!widget_found) {
+               dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);
+               return false;
+       }
+
+       ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true);
+       if (ret < 0)
+               return false;
+
+       return change;
+}
+
+static int sof_ipc4_switch_get(struct snd_sof_control *scontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
+       unsigned int i;
+
+       /* read back each channel */
+       for (i = 0; i < scontrol->num_channels; i++)
+               ucontrol->value.integer.value[i] = cdata->chanv[i].value;
+
+       return 0;
+}
+
 static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev,
                                       struct snd_sof_control *scontrol,
                                       bool set, bool lock)
@@ -438,6 +534,16 @@ static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
        return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true);
 }
 
+static int
+sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
+                    struct snd_sof_control *scontrol)
+{
+       if (scontrol->max == 1)
+               return sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, false);
+
+       return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
+}
+
 /* set up all controls for the widget */
 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
 {
@@ -450,8 +556,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
                        case SND_SOC_TPLG_CTL_VOLSW:
                        case SND_SOC_TPLG_CTL_VOLSW_SX:
                        case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
-                               ret = sof_ipc4_set_volume_data(sdev, swidget,
-                                                              scontrol, false);
+                               ret = sof_ipc4_volsw_setup(sdev, swidget, scontrol);
                                break;
                        case SND_SOC_TPLG_CTL_BYTES:
                                ret = sof_ipc4_set_get_bytes_data(sdev, scontrol,
@@ -498,6 +603,8 @@ sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_I
 const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
        .volume_put = sof_ipc4_volume_put,
        .volume_get = sof_ipc4_volume_get,
+       .switch_put = sof_ipc4_switch_put,
+       .switch_get = sof_ipc4_switch_get,
        .bytes_put = sof_ipc4_bytes_put,
        .bytes_get = sof_ipc4_bytes_get,
        .bytes_ext_put = sof_ipc4_bytes_ext_put,
index 2462feceda5d97bd51447d91b0e40677ae5c43d4..093dfa53a0e7d37441774ed1af894055ccd59ffc 100644 (file)
@@ -2107,12 +2107,22 @@ static int sof_ipc4_control_load_volume(struct snd_sof_dev *sdev, struct snd_sof
        msg->primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
        msg->primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
 
-       msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);
+       /* volume controls with range 0-1 (off/on) are switch controls */
+       if (scontrol->max == 1)
+               msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_SWITCH_CONTROL_PARAM_ID);
+       else
+               msg->extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_GAIN_PARAM_ID);
 
-       /* set default volume values to 0dB in control */
        for (i = 0; i < scontrol->num_channels; i++) {
                control_data->chanv[i].channel = i;
-               control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB;
+               /*
+                * Default, initial values:
+                * - 0dB for volume controls
+                * - off (0) for switch controls - value already zero after
+                *                                 memory allocation
+                */
+               if (scontrol->max > 1)
+                       control_data->chanv[i].value = SOF_IPC4_VOL_ZERO_DB;
        }
 
        return 0;