1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2022 Intel Corporation. All rights reserved. 7 // 8 // 9 10 #include "sof-priv.h" 11 #include "sof-audio.h" 12 #include "ipc4-priv.h" 13 #include "ipc4-topology.h" 14 15 static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) 16 { 17 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 18 struct snd_soc_component *scomp = scontrol->scomp; 19 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 20 const struct sof_ipc_ops *iops = sdev->ipc->ops; 21 struct sof_ipc4_msg *msg = &cdata->msg; 22 struct snd_sof_widget *swidget; 23 bool widget_found = false; 24 25 /* find widget associated with the control */ 26 list_for_each_entry(swidget, &sdev->widget_list, list) { 27 if (swidget->comp_id == scontrol->comp_id) { 28 widget_found = true; 29 break; 30 } 31 } 32 33 if (!widget_found) { 34 dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); 35 return -ENOENT; 36 } 37 38 /* 39 * Volatile controls should always be part of static pipelines and the widget use_count 40 * would always be > 0 in this case. For the others, just return the cached value if the 41 * widget is not set up. 42 */ 43 if (!swidget->use_count) 44 return 0; 45 46 msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK; 47 msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); 48 49 return iops->set_get_data(sdev, msg, msg->data_size, set); 50 } 51 52 static int 53 sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 54 struct snd_sof_control *scontrol) 55 { 56 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 57 struct sof_ipc4_gain *gain = swidget->private; 58 struct sof_ipc4_msg *msg = &cdata->msg; 59 struct sof_ipc4_gain_data data; 60 bool all_channels_equal = true; 61 u32 value; 62 int ret, i; 63 64 /* check if all channel values are equal */ 65 value = cdata->chanv[0].value; 66 for (i = 1; i < scontrol->num_channels; i++) { 67 if (cdata->chanv[i].value != value) { 68 all_channels_equal = false; 69 break; 70 } 71 } 72 73 /* 74 * notify DSP with a single IPC message if all channel values are equal. Otherwise send 75 * a separate IPC for each channel. 76 */ 77 for (i = 0; i < scontrol->num_channels; i++) { 78 if (all_channels_equal) { 79 data.channels = SOF_IPC4_GAIN_ALL_CHANNELS_MASK; 80 data.init_val = cdata->chanv[0].value; 81 } else { 82 data.channels = cdata->chanv[i].channel; 83 data.init_val = cdata->chanv[i].value; 84 } 85 86 /* set curve type and duration from topology */ 87 data.curve_duration = gain->data.curve_duration; 88 data.curve_type = gain->data.curve_type; 89 90 msg->data_ptr = &data; 91 msg->data_size = sizeof(data); 92 93 ret = sof_ipc4_set_get_kcontrol_data(scontrol, true); 94 msg->data_ptr = NULL; 95 msg->data_size = 0; 96 if (ret < 0) { 97 dev_err(sdev->dev, "Failed to set volume update for %s\n", 98 scontrol->name); 99 return ret; 100 } 101 102 if (all_channels_equal) 103 break; 104 } 105 106 return 0; 107 } 108 109 static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, 110 struct snd_ctl_elem_value *ucontrol) 111 { 112 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 113 struct snd_soc_component *scomp = scontrol->scomp; 114 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 115 unsigned int channels = scontrol->num_channels; 116 struct snd_sof_widget *swidget; 117 bool widget_found = false; 118 bool change = false; 119 unsigned int i; 120 int ret; 121 122 /* update each channel */ 123 for (i = 0; i < channels; i++) { 124 u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], 125 scontrol->volume_table, scontrol->max + 1); 126 127 change = change || (value != cdata->chanv[i].value); 128 cdata->chanv[i].channel = i; 129 cdata->chanv[i].value = value; 130 } 131 132 if (!pm_runtime_active(scomp->dev)) 133 return change; 134 135 /* find widget associated with the control */ 136 list_for_each_entry(swidget, &sdev->widget_list, list) { 137 if (swidget->comp_id == scontrol->comp_id) { 138 widget_found = true; 139 break; 140 } 141 } 142 143 if (!widget_found) { 144 dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); 145 return false; 146 } 147 148 ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); 149 if (ret < 0) 150 return false; 151 152 return change; 153 } 154 155 static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, 156 struct snd_ctl_elem_value *ucontrol) 157 { 158 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 159 unsigned int channels = scontrol->num_channels; 160 unsigned int i; 161 162 for (i = 0; i < channels; i++) 163 ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, 164 scontrol->volume_table, 165 scontrol->max + 1); 166 167 return 0; 168 } 169 170 /* set up all controls for the widget */ 171 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 172 { 173 struct snd_sof_control *scontrol; 174 int ret; 175 176 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 177 if (scontrol->comp_id == swidget->comp_id) { 178 ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); 179 if (ret < 0) { 180 dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n", 181 __func__, scontrol->comp_id, swidget->widget->name); 182 return ret; 183 } 184 } 185 186 return 0; 187 } 188 189 static int 190 sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) 191 { 192 int i; 193 194 /* init the volume table */ 195 scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); 196 if (!scontrol->volume_table) 197 return -ENOMEM; 198 199 /* populate the volume table */ 200 for (i = 0; i < size ; i++) { 201 u32 val = vol_compute_gain(i, tlv); 202 u64 q31val = ((u64)val) << 15; /* Can be over Q1.31, need to saturate */ 203 204 scontrol->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ? 205 SOF_IPC4_VOL_ZERO_DB : q31val; 206 } 207 208 return 0; 209 } 210 211 const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { 212 .volume_put = sof_ipc4_volume_put, 213 .volume_get = sof_ipc4_volume_get, 214 .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, 215 .set_up_volume_table = sof_ipc4_set_up_volume_table, 216 }; 217