Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license.  When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2022 Intel Corporation. All rights reserved.
0007 //
0008 //
0009 
0010 #include "sof-priv.h"
0011 #include "sof-audio.h"
0012 #include "ipc4-priv.h"
0013 #include "ipc4-topology.h"
0014 
0015 static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
0016 {
0017     struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
0018     struct snd_soc_component *scomp = scontrol->scomp;
0019     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0020     const struct sof_ipc_ops *iops = sdev->ipc->ops;
0021     struct sof_ipc4_msg *msg = &cdata->msg;
0022     struct snd_sof_widget *swidget;
0023     bool widget_found = false;
0024 
0025     /* find widget associated with the control */
0026     list_for_each_entry(swidget, &sdev->widget_list, list) {
0027         if (swidget->comp_id == scontrol->comp_id) {
0028             widget_found = true;
0029             break;
0030         }
0031     }
0032 
0033     if (!widget_found) {
0034         dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);
0035         return -ENOENT;
0036     }
0037 
0038     /*
0039      * Volatile controls should always be part of static pipelines and the widget use_count
0040      * would always be > 0 in this case. For the others, just return the cached value if the
0041      * widget is not set up.
0042      */
0043     if (!swidget->use_count)
0044         return 0;
0045 
0046     msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
0047     msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
0048 
0049     return iops->set_get_data(sdev, msg, msg->data_size, set);
0050 }
0051 
0052 static int
0053 sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
0054              struct snd_sof_control *scontrol)
0055 {
0056     struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
0057     struct sof_ipc4_gain *gain = swidget->private;
0058     struct sof_ipc4_msg *msg = &cdata->msg;
0059     struct sof_ipc4_gain_data data;
0060     bool all_channels_equal = true;
0061     u32 value;
0062     int ret, i;
0063 
0064     /* check if all channel values are equal */
0065     value = cdata->chanv[0].value;
0066     for (i = 1; i < scontrol->num_channels; i++) {
0067         if (cdata->chanv[i].value != value) {
0068             all_channels_equal = false;
0069             break;
0070         }
0071     }
0072 
0073     /*
0074      * notify DSP with a single IPC message if all channel values are equal. Otherwise send
0075      * a separate IPC for each channel.
0076      */
0077     for (i = 0; i < scontrol->num_channels; i++) {
0078         if (all_channels_equal) {
0079             data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK;
0080             data.init_val = cdata->chanv[0].value;
0081         } else {
0082             data.channels = cdata->chanv[i].channel;
0083             data.init_val = cdata->chanv[i].value;
0084         }
0085 
0086         /* set curve type and duration from topology */
0087         data.curve_duration = gain->data.curve_duration;
0088         data.curve_type = gain->data.curve_type;
0089 
0090         msg->data_ptr = &data;
0091         msg->data_size = sizeof(data);
0092 
0093         ret = sof_ipc4_set_get_kcontrol_data(scontrol, true);
0094         msg->data_ptr = NULL;
0095         msg->data_size = 0;
0096         if (ret < 0) {
0097             dev_err(sdev->dev, "Failed to set volume update for %s\n",
0098                 scontrol->name);
0099             return ret;
0100         }
0101 
0102         if (all_channels_equal)
0103             break;
0104     }
0105 
0106     return 0;
0107 }
0108 
0109 static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
0110                 struct snd_ctl_elem_value *ucontrol)
0111 {
0112     struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
0113     struct snd_soc_component *scomp = scontrol->scomp;
0114     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0115     unsigned int channels = scontrol->num_channels;
0116     struct snd_sof_widget *swidget;
0117     bool widget_found = false;
0118     bool change = false;
0119     unsigned int i;
0120     int ret;
0121 
0122     /* update each channel */
0123     for (i = 0; i < channels; i++) {
0124         u32 value = mixer_to_ipc(ucontrol->value.integer.value[i],
0125                      scontrol->volume_table, scontrol->max + 1);
0126 
0127         change = change || (value != cdata->chanv[i].value);
0128         cdata->chanv[i].channel = i;
0129         cdata->chanv[i].value = value;
0130     }
0131 
0132     if (!pm_runtime_active(scomp->dev))
0133         return change;
0134 
0135     /* find widget associated with the control */
0136     list_for_each_entry(swidget, &sdev->widget_list, list) {
0137         if (swidget->comp_id == scontrol->comp_id) {
0138             widget_found = true;
0139             break;
0140         }
0141     }
0142 
0143     if (!widget_found) {
0144         dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name);
0145         return false;
0146     }
0147 
0148     ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
0149     if (ret < 0)
0150         return false;
0151 
0152     return change;
0153 }
0154 
0155 static int sof_ipc4_volume_get(struct snd_sof_control *scontrol,
0156                    struct snd_ctl_elem_value *ucontrol)
0157 {
0158     struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
0159     unsigned int channels = scontrol->num_channels;
0160     unsigned int i;
0161 
0162     for (i = 0; i < channels; i++)
0163         ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value,
0164                                 scontrol->volume_table,
0165                                 scontrol->max + 1);
0166 
0167     return 0;
0168 }
0169 
0170 /* set up all controls for the widget */
0171 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
0172 {
0173     struct snd_sof_control *scontrol;
0174     int ret;
0175 
0176     list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
0177         if (scontrol->comp_id == swidget->comp_id) {
0178             ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
0179             if (ret < 0) {
0180                 dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
0181                     __func__, scontrol->comp_id, swidget->widget->name);
0182                 return ret;
0183             }
0184         }
0185 
0186     return 0;
0187 }
0188 
0189 static int
0190 sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size)
0191 {
0192     int i;
0193 
0194     /* init the volume table */
0195     scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL);
0196     if (!scontrol->volume_table)
0197         return -ENOMEM;
0198 
0199     /* populate the volume table */
0200     for (i = 0; i < size ; i++) {
0201         u32 val = vol_compute_gain(i, tlv);
0202         u64 q31val = ((u64)val) << 15; /* Can be over Q1.31, need to saturate */
0203 
0204         scontrol->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ?
0205                         SOF_IPC4_VOL_ZERO_DB : q31val;
0206     }
0207 
0208     return 0;
0209 }
0210 
0211 const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = {
0212     .volume_put = sof_ipc4_volume_put,
0213     .volume_get = sof_ipc4_volume_get,
0214     .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup,
0215     .set_up_volume_table = sof_ipc4_set_up_volume_table,
0216 };