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 = gain->data.curve_duration; 101 data.curve_type = gain->data.curve_type; 102 103 msg->data_ptr = &data; 104 msg->data_size = sizeof(data); 105 106 ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock); 107 msg->data_ptr = NULL; 108 msg->data_size = 0; 109 if (ret < 0) { 110 dev_err(sdev->dev, "Failed to set volume update for %s\n", 111 scontrol->name); 112 return ret; 113 } 114 115 if (all_channels_equal) 116 break; 117 } 118 119 return 0; 120 } 121 122 static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, 123 struct snd_ctl_elem_value *ucontrol) 124 { 125 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 126 struct snd_soc_component *scomp = scontrol->scomp; 127 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 128 unsigned int channels = scontrol->num_channels; 129 struct snd_sof_widget *swidget; 130 bool widget_found = false; 131 bool change = false; 132 unsigned int i; 133 int ret; 134 135 /* update each channel */ 136 for (i = 0; i < channels; i++) { 137 u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], 138 scontrol->volume_table, scontrol->max + 1); 139 140 change = change || (value != cdata->chanv[i].value); 141 cdata->chanv[i].channel = i; 142 cdata->chanv[i].value = value; 143 } 144 145 if (!pm_runtime_active(scomp->dev)) 146 return change; 147 148 /* find widget associated with the control */ 149 list_for_each_entry(swidget, &sdev->widget_list, list) { 150 if (swidget->comp_id == scontrol->comp_id) { 151 widget_found = true; 152 break; 153 } 154 } 155 156 if (!widget_found) { 157 dev_err(scomp->dev, "Failed to find widget for kcontrol %s\n", scontrol->name); 158 return false; 159 } 160 161 ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true); 162 if (ret < 0) 163 return false; 164 165 return change; 166 } 167 168 static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, 169 struct snd_ctl_elem_value *ucontrol) 170 { 171 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 172 unsigned int channels = scontrol->num_channels; 173 unsigned int i; 174 175 for (i = 0; i < channels; i++) 176 ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, 177 scontrol->volume_table, 178 scontrol->max + 1); 179 180 return 0; 181 } 182 183 /* set up all controls for the widget */ 184 static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 185 { 186 struct snd_sof_control *scontrol; 187 int ret; 188 189 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 190 if (scontrol->comp_id == swidget->comp_id) { 191 ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); 192 if (ret < 0) { 193 dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n", 194 __func__, scontrol->comp_id, swidget->widget->name); 195 return ret; 196 } 197 } 198 199 return 0; 200 } 201 202 static int 203 sof_ipc4_set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) 204 { 205 int i; 206 207 /* init the volume table */ 208 scontrol->volume_table = kcalloc(size, sizeof(u32), GFP_KERNEL); 209 if (!scontrol->volume_table) 210 return -ENOMEM; 211 212 /* populate the volume table */ 213 for (i = 0; i < size ; i++) { 214 u32 val = vol_compute_gain(i, tlv); 215 u64 q31val = ((u64)val) << 15; /* Can be over Q1.31, need to saturate */ 216 217 scontrol->volume_table[i] = q31val > SOF_IPC4_VOL_ZERO_DB ? 218 SOF_IPC4_VOL_ZERO_DB : q31val; 219 } 220 221 return 0; 222 } 223 224 const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops = { 225 .volume_put = sof_ipc4_volume_put, 226 .volume_get = sof_ipc4_volume_get, 227 .widget_kcontrol_setup = sof_ipc4_widget_kcontrol_setup, 228 .set_up_volume_table = sof_ipc4_set_up_volume_table, 229 }; 230