xref: /openbmc/linux/sound/soc/sof/ipc4-control.c (revision 4e174665)
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