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