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