Commit | Line | Data |
---|---|---|
eb50fd3a | 1 | // SPDX-License-Identifier: GPL-2.0 |
6339d232 VA |
2 | /* |
3 | * Greybus audio driver | |
4 | * Copyright 2015-2016 Google Inc. | |
5 | * Copyright 2015-2016 Linaro Ltd. | |
6339d232 VA |
6 | */ |
7 | ||
8 | #include "audio_codec.h" | |
9 | #include "greybus_protocols.h" | |
10 | ||
11 | #define GBAUDIO_INVALID_ID 0xFF | |
12 | ||
13 | /* mixer control */ | |
14 | struct gb_mixer_control { | |
15 | int min, max; | |
16 | unsigned int reg, rreg, shift, rshift, invert; | |
17 | }; | |
18 | ||
19 | struct gbaudio_ctl_pvt { | |
20 | unsigned int ctl_id; | |
21 | unsigned int data_cport; | |
22 | unsigned int access; | |
23 | unsigned int vcount; | |
24 | struct gb_audio_ctl_elem_info *info; | |
25 | }; | |
26 | ||
6dd67645 VA |
27 | static struct gbaudio_module_info *find_gb_module( |
28 | struct gbaudio_codec_info *codec, | |
29 | char const *name) | |
30 | { | |
31 | int dev_id, ret; | |
32 | char begin[NAME_SIZE]; | |
33 | struct gbaudio_module_info *module; | |
34 | ||
35 | if (!name) | |
36 | return NULL; | |
37 | ||
38 | ret = sscanf(name, "%s %d", begin, &dev_id); | |
39 | dev_dbg(codec->dev, "%s:Find module#%d\n", __func__, dev_id); | |
40 | ||
41 | mutex_lock(&codec->lock); | |
42 | list_for_each_entry(module, &codec->module_list, list) { | |
43 | if (module->dev_id == dev_id) { | |
44 | mutex_unlock(&codec->lock); | |
45 | return module; | |
46 | } | |
47 | } | |
48 | mutex_unlock(&codec->lock); | |
02f1c12c VA |
49 | dev_warn(codec->dev, "%s: module#%d missing in codec list\n", name, |
50 | dev_id); | |
6dd67645 VA |
51 | return NULL; |
52 | } | |
53 | ||
54 | static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, | |
e65579e3 | 55 | __u8 control_id, __u8 index) |
6339d232 VA |
56 | { |
57 | struct gbaudio_control *control; | |
58 | ||
59 | if (control_id == GBAUDIO_INVALID_ID) | |
60 | return NULL; | |
61 | ||
6dd67645 | 62 | list_for_each_entry(control, &module->ctl_list, list) { |
6339d232 VA |
63 | if (control->id == control_id) { |
64 | if (index == GBAUDIO_INVALID_ID) | |
65 | return control->name; | |
15c726ea VA |
66 | if (index >= control->items) |
67 | return NULL; | |
6339d232 VA |
68 | return control->texts[index]; |
69 | } | |
70 | } | |
6dd67645 | 71 | list_for_each_entry(control, &module->widget_ctl_list, list) { |
6339d232 VA |
72 | if (control->id == control_id) { |
73 | if (index == GBAUDIO_INVALID_ID) | |
74 | return control->name; | |
15c726ea VA |
75 | if (index >= control->items) |
76 | return NULL; | |
6339d232 VA |
77 | return control->texts[index]; |
78 | } | |
79 | } | |
80 | return NULL; | |
81 | } | |
82 | ||
e65579e3 VA |
83 | static int gbaudio_map_controlname(struct gbaudio_module_info *module, |
84 | const char *name) | |
85 | { | |
86 | struct gbaudio_control *control; | |
87 | ||
88 | list_for_each_entry(control, &module->ctl_list, list) { | |
89 | if (!strncmp(control->name, name, NAME_SIZE)) | |
90 | return control->id; | |
91 | } | |
92 | ||
93 | dev_warn(module->dev, "%s: missing in modules controls list\n", name); | |
94 | ||
95 | return -EINVAL; | |
96 | } | |
97 | ||
0c15a9e0 | 98 | static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, |
e65579e3 | 99 | const char *name) |
0c15a9e0 VA |
100 | { |
101 | struct gbaudio_control *control; | |
102 | ||
103 | list_for_each_entry(control, &module->widget_ctl_list, list) { | |
104 | if (!strncmp(control->wname, name, NAME_SIZE)) | |
105 | return control->id; | |
106 | } | |
107 | dev_warn(module->dev, "%s: missing in modules controls list\n", name); | |
108 | ||
109 | return -EINVAL; | |
110 | } | |
111 | ||
6dd67645 | 112 | static int gbaudio_map_widgetname(struct gbaudio_module_info *module, |
e65579e3 | 113 | const char *name) |
6339d232 VA |
114 | { |
115 | struct gbaudio_widget *widget; | |
a2a87b22 | 116 | |
6dd67645 VA |
117 | list_for_each_entry(widget, &module->widget_list, list) { |
118 | if (!strncmp(widget->name, name, NAME_SIZE)) | |
6339d232 VA |
119 | return widget->id; |
120 | } | |
bb296b48 VA |
121 | dev_warn(module->dev, "%s: missing in modules widgets list\n", name); |
122 | ||
6339d232 VA |
123 | return -EINVAL; |
124 | } | |
125 | ||
6dd67645 | 126 | static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, |
e65579e3 | 127 | __u8 widget_id) |
6339d232 VA |
128 | { |
129 | struct gbaudio_widget *widget; | |
130 | ||
6dd67645 | 131 | list_for_each_entry(widget, &module->widget_list, list) { |
6339d232 VA |
132 | if (widget->id == widget_id) |
133 | return widget->name; | |
134 | } | |
135 | return NULL; | |
136 | } | |
137 | ||
a0de502e VK |
138 | static const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, |
139 | struct gb_audio_enumerated *gbenum) | |
e65579e3 VA |
140 | { |
141 | const char **strings; | |
142 | int i; | |
055fb9ce | 143 | unsigned int items; |
e65579e3 VA |
144 | __u8 *data; |
145 | ||
055fb9ce | 146 | items = le32_to_cpu(gbenum->items); |
a86854d0 | 147 | strings = devm_kcalloc(gb->dev, items, sizeof(char *), GFP_KERNEL); |
e65579e3 VA |
148 | data = gbenum->names; |
149 | ||
055fb9ce | 150 | for (i = 0; i < items; i++) { |
e65579e3 VA |
151 | strings[i] = (const char *)data; |
152 | while (*data != '\0') | |
153 | data++; | |
154 | data++; | |
155 | } | |
156 | ||
157 | return strings; | |
158 | } | |
159 | ||
6339d232 VA |
160 | static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, |
161 | struct snd_ctl_elem_info *uinfo) | |
162 | { | |
163 | unsigned int max; | |
164 | const char *name; | |
165 | struct gbaudio_ctl_pvt *data; | |
166 | struct gb_audio_ctl_elem_info *info; | |
6dd67645 | 167 | struct gbaudio_module_info *module; |
6339d232 | 168 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
796fad44 | 169 | struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); |
6339d232 | 170 | |
6dd67645 | 171 | dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); |
6339d232 VA |
172 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; |
173 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
174 | ||
175 | if (!info) { | |
7224a2a5 | 176 | dev_err(codec->dev, "NULL info for %s\n", uinfo->id.name); |
6339d232 VA |
177 | return -EINVAL; |
178 | } | |
179 | ||
180 | /* update uinfo */ | |
181 | uinfo->access = data->access; | |
182 | uinfo->count = data->vcount; | |
183 | uinfo->type = (snd_ctl_elem_type_t)info->type; | |
184 | ||
185 | switch (info->type) { | |
186 | case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: | |
187 | case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: | |
055fb9ce VA |
188 | uinfo->value.integer.min = le32_to_cpu(info->value.integer.min); |
189 | uinfo->value.integer.max = le32_to_cpu(info->value.integer.max); | |
6339d232 VA |
190 | break; |
191 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
055fb9ce | 192 | max = le32_to_cpu(info->value.enumerated.items); |
6339d232 VA |
193 | uinfo->value.enumerated.items = max; |
194 | if (uinfo->value.enumerated.item > max - 1) | |
195 | uinfo->value.enumerated.item = max - 1; | |
6dd67645 VA |
196 | module = find_gb_module(gbcodec, kcontrol->id.name); |
197 | if (!module) | |
198 | return -EINVAL; | |
199 | name = gbaudio_map_controlid(module, data->ctl_id, | |
6339d232 VA |
200 | uinfo->value.enumerated.item); |
201 | strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); | |
202 | break; | |
203 | default: | |
204 | dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", | |
205 | info->type, kcontrol->id.name); | |
206 | break; | |
207 | } | |
208 | return 0; | |
209 | } | |
210 | ||
211 | static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, | |
212 | struct snd_ctl_elem_value *ucontrol) | |
213 | { | |
214 | int ret; | |
215 | struct gb_audio_ctl_elem_info *info; | |
216 | struct gbaudio_ctl_pvt *data; | |
217 | struct gb_audio_ctl_elem_value gbvalue; | |
6dd67645 | 218 | struct gbaudio_module_info *module; |
6339d232 | 219 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
796fad44 | 220 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6ba7fad4 | 221 | struct gb_bundle *bundle; |
6339d232 | 222 | |
6dd67645 VA |
223 | dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); |
224 | module = find_gb_module(gb, kcontrol->id.name); | |
225 | if (!module) | |
226 | return -EINVAL; | |
3994e0b1 | 227 | |
6339d232 VA |
228 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; |
229 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
6ba7fad4 DL |
230 | bundle = to_gb_bundle(module->dev); |
231 | ||
232 | ret = gb_pm_runtime_get_sync(bundle); | |
233 | if (ret) | |
234 | return ret; | |
6339d232 | 235 | |
6dd67645 | 236 | ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, |
6339d232 | 237 | GB_AUDIO_INVALID_INDEX, &gbvalue); |
6ba7fad4 DL |
238 | |
239 | gb_pm_runtime_put_autosuspend(bundle); | |
240 | ||
6339d232 | 241 | if (ret) { |
c6722ab5 VA |
242 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, |
243 | __func__, kcontrol->id.name); | |
6339d232 VA |
244 | return ret; |
245 | } | |
246 | ||
247 | /* update ucontrol */ | |
248 | switch (info->type) { | |
249 | case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: | |
250 | case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: | |
251 | ucontrol->value.integer.value[0] = | |
055fb9ce | 252 | le32_to_cpu(gbvalue.value.integer_value[0]); |
6339d232 VA |
253 | if (data->vcount == 2) |
254 | ucontrol->value.integer.value[1] = | |
055fb9ce | 255 | le32_to_cpu(gbvalue.value.integer_value[1]); |
6339d232 VA |
256 | break; |
257 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
258 | ucontrol->value.enumerated.item[0] = | |
055fb9ce | 259 | le32_to_cpu(gbvalue.value.enumerated_item[0]); |
6339d232 VA |
260 | if (data->vcount == 2) |
261 | ucontrol->value.enumerated.item[1] = | |
055fb9ce | 262 | le32_to_cpu(gbvalue.value.enumerated_item[1]); |
6339d232 VA |
263 | break; |
264 | default: | |
265 | dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", | |
266 | info->type, kcontrol->id.name); | |
267 | ret = -EINVAL; | |
268 | break; | |
269 | } | |
270 | return ret; | |
271 | } | |
272 | ||
273 | static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, | |
274 | struct snd_ctl_elem_value *ucontrol) | |
275 | { | |
276 | int ret = 0; | |
277 | struct gb_audio_ctl_elem_info *info; | |
278 | struct gbaudio_ctl_pvt *data; | |
279 | struct gb_audio_ctl_elem_value gbvalue; | |
6dd67645 | 280 | struct gbaudio_module_info *module; |
6339d232 | 281 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); |
796fad44 | 282 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6ba7fad4 | 283 | struct gb_bundle *bundle; |
6339d232 | 284 | |
6dd67645 VA |
285 | dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); |
286 | module = find_gb_module(gb, kcontrol->id.name); | |
287 | if (!module) | |
288 | return -EINVAL; | |
3994e0b1 | 289 | |
6339d232 VA |
290 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; |
291 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
6ba7fad4 | 292 | bundle = to_gb_bundle(module->dev); |
6339d232 VA |
293 | |
294 | /* update ucontrol */ | |
295 | switch (info->type) { | |
296 | case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: | |
297 | case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: | |
298 | gbvalue.value.integer_value[0] = | |
055fb9ce | 299 | cpu_to_le32(ucontrol->value.integer.value[0]); |
6339d232 VA |
300 | if (data->vcount == 2) |
301 | gbvalue.value.integer_value[1] = | |
055fb9ce | 302 | cpu_to_le32(ucontrol->value.integer.value[1]); |
6339d232 VA |
303 | break; |
304 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
305 | gbvalue.value.enumerated_item[0] = | |
055fb9ce | 306 | cpu_to_le32(ucontrol->value.enumerated.item[0]); |
6339d232 VA |
307 | if (data->vcount == 2) |
308 | gbvalue.value.enumerated_item[1] = | |
055fb9ce | 309 | cpu_to_le32(ucontrol->value.enumerated.item[1]); |
6339d232 VA |
310 | break; |
311 | default: | |
312 | dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", | |
313 | info->type, kcontrol->id.name); | |
314 | ret = -EINVAL; | |
315 | break; | |
316 | } | |
317 | ||
6ba7fad4 DL |
318 | if (ret) |
319 | return ret; | |
320 | ||
321 | ret = gb_pm_runtime_get_sync(bundle); | |
6339d232 VA |
322 | if (ret) |
323 | return ret; | |
324 | ||
6dd67645 | 325 | ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, |
6339d232 | 326 | GB_AUDIO_INVALID_INDEX, &gbvalue); |
6ba7fad4 DL |
327 | |
328 | gb_pm_runtime_put_autosuspend(bundle); | |
329 | ||
6339d232 | 330 | if (ret) { |
c6722ab5 VA |
331 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, |
332 | __func__, kcontrol->id.name); | |
6339d232 VA |
333 | } |
334 | ||
335 | return ret; | |
336 | } | |
337 | ||
338 | #define SOC_MIXER_GB(xname, kcount, data) \ | |
339 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | |
340 | .count = kcount, .info = gbcodec_mixer_ctl_info, \ | |
341 | .get = gbcodec_mixer_ctl_get, .put = gbcodec_mixer_ctl_put, \ | |
342 | .private_value = (unsigned long)data } | |
343 | ||
344 | /* | |
345 | * although below callback functions seems redundant to above functions. | |
346 | * same are kept to allow provision for different handling in case | |
347 | * of DAPM related sequencing, etc. | |
348 | */ | |
349 | static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, | |
350 | struct snd_ctl_elem_info *uinfo) | |
351 | { | |
352 | int platform_max, platform_min; | |
353 | struct gbaudio_ctl_pvt *data; | |
354 | struct gb_audio_ctl_elem_info *info; | |
6dd67645 VA |
355 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); |
356 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
357 | struct snd_soc_codec *codec = widget->codec; | |
6339d232 | 358 | |
6dd67645 | 359 | dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); |
6339d232 VA |
360 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; |
361 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
362 | ||
363 | /* update uinfo */ | |
055fb9ce VA |
364 | platform_max = le32_to_cpu(info->value.integer.max); |
365 | platform_min = le32_to_cpu(info->value.integer.min); | |
6339d232 VA |
366 | |
367 | if (platform_max == 1 && | |
368 | !strnstr(kcontrol->id.name, " Volume", NAME_SIZE)) | |
369 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
370 | else | |
371 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
372 | ||
373 | uinfo->count = data->vcount; | |
84510052 VA |
374 | uinfo->value.integer.min = platform_min; |
375 | uinfo->value.integer.max = platform_max; | |
6339d232 VA |
376 | |
377 | return 0; | |
378 | } | |
379 | ||
380 | static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, | |
381 | struct snd_ctl_elem_value *ucontrol) | |
382 | { | |
383 | int ret; | |
384 | struct gb_audio_ctl_elem_info *info; | |
385 | struct gbaudio_ctl_pvt *data; | |
386 | struct gb_audio_ctl_elem_value gbvalue; | |
6dd67645 | 387 | struct gbaudio_module_info *module; |
6339d232 VA |
388 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); |
389 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
390 | struct snd_soc_codec *codec = widget->codec; | |
796fad44 | 391 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6ba7fad4 | 392 | struct gb_bundle *bundle; |
6339d232 | 393 | |
6dd67645 VA |
394 | dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); |
395 | module = find_gb_module(gb, kcontrol->id.name); | |
396 | if (!module) | |
397 | return -EINVAL; | |
3994e0b1 | 398 | |
6339d232 VA |
399 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; |
400 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
6ba7fad4 | 401 | bundle = to_gb_bundle(module->dev); |
6339d232 VA |
402 | |
403 | if (data->vcount == 2) | |
404 | dev_warn(widget->dapm->dev, | |
405 | "GB: Control '%s' is stereo, which is not supported\n", | |
406 | kcontrol->id.name); | |
407 | ||
6ba7fad4 DL |
408 | ret = gb_pm_runtime_get_sync(bundle); |
409 | if (ret) | |
410 | return ret; | |
411 | ||
6dd67645 | 412 | ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, |
6339d232 | 413 | GB_AUDIO_INVALID_INDEX, &gbvalue); |
6ba7fad4 DL |
414 | |
415 | gb_pm_runtime_put_autosuspend(bundle); | |
416 | ||
6339d232 | 417 | if (ret) { |
c6722ab5 VA |
418 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, |
419 | __func__, kcontrol->id.name); | |
6339d232 VA |
420 | return ret; |
421 | } | |
422 | /* update ucontrol */ | |
055fb9ce VA |
423 | ucontrol->value.integer.value[0] = |
424 | le32_to_cpu(gbvalue.value.integer_value[0]); | |
6339d232 VA |
425 | |
426 | return ret; | |
427 | } | |
428 | ||
429 | static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, | |
430 | struct snd_ctl_elem_value *ucontrol) | |
431 | { | |
432 | int ret, wi, max, connect; | |
433 | unsigned int mask, val; | |
434 | struct gb_audio_ctl_elem_info *info; | |
435 | struct gbaudio_ctl_pvt *data; | |
436 | struct gb_audio_ctl_elem_value gbvalue; | |
6dd67645 | 437 | struct gbaudio_module_info *module; |
6339d232 VA |
438 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); |
439 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
440 | struct snd_soc_codec *codec = widget->codec; | |
796fad44 | 441 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6ba7fad4 | 442 | struct gb_bundle *bundle; |
6339d232 | 443 | |
6dd67645 VA |
444 | dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); |
445 | module = find_gb_module(gb, kcontrol->id.name); | |
446 | if (!module) | |
447 | return -EINVAL; | |
3994e0b1 | 448 | |
6339d232 VA |
449 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; |
450 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
6ba7fad4 | 451 | bundle = to_gb_bundle(module->dev); |
6339d232 VA |
452 | |
453 | if (data->vcount == 2) | |
454 | dev_warn(widget->dapm->dev, | |
455 | "GB: Control '%s' is stereo, which is not supported\n", | |
456 | kcontrol->id.name); | |
457 | ||
055fb9ce | 458 | max = le32_to_cpu(info->value.integer.max); |
6339d232 | 459 | mask = (1 << fls(max)) - 1; |
4f1cbe2a | 460 | val = ucontrol->value.integer.value[0] & mask; |
6339d232 VA |
461 | connect = !!val; |
462 | ||
463 | /* update ucontrol */ | |
464 | if (gbvalue.value.integer_value[0] != val) { | |
465 | for (wi = 0; wi < wlist->num_widgets; wi++) { | |
466 | widget = wlist->widgets[wi]; | |
467 | ||
468 | widget->value = val; | |
469 | widget->dapm->update = NULL; | |
470 | snd_soc_dapm_mixer_update_power(widget, kcontrol, | |
471 | connect); | |
472 | } | |
473 | gbvalue.value.integer_value[0] = | |
055fb9ce | 474 | cpu_to_le32(ucontrol->value.integer.value[0]); |
6ba7fad4 DL |
475 | |
476 | ret = gb_pm_runtime_get_sync(bundle); | |
477 | if (ret) | |
478 | return ret; | |
479 | ||
6dd67645 | 480 | ret = gb_audio_gb_set_control(module->mgmt_connection, |
6339d232 VA |
481 | data->ctl_id, |
482 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
6ba7fad4 DL |
483 | |
484 | gb_pm_runtime_put_autosuspend(bundle); | |
485 | ||
6339d232 | 486 | if (ret) { |
c6722ab5 VA |
487 | dev_err_ratelimited(codec->dev, |
488 | "%d:Error in %s for %s\n", ret, | |
489 | __func__, kcontrol->id.name); | |
7224a2a5 | 490 | return ret; |
6339d232 VA |
491 | } |
492 | } | |
493 | ||
7224a2a5 | 494 | return 0; |
6339d232 VA |
495 | } |
496 | ||
497 | #define SOC_DAPM_MIXER_GB(xname, kcount, data) \ | |
498 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | |
499 | .count = kcount, .info = gbcodec_mixer_dapm_ctl_info, \ | |
500 | .get = gbcodec_mixer_dapm_ctl_get, .put = gbcodec_mixer_dapm_ctl_put, \ | |
501 | .private_value = (unsigned long)data} | |
502 | ||
503 | static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, | |
504 | struct snd_kcontrol *k, int event) | |
505 | { | |
506 | /* Ensure GB speaker is connected */ | |
507 | ||
508 | return 0; | |
509 | } | |
510 | ||
511 | static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, | |
512 | struct snd_kcontrol *k, int event) | |
513 | { | |
514 | /* Ensure GB module supports jack slot */ | |
515 | ||
516 | return 0; | |
517 | } | |
518 | ||
519 | static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, | |
520 | struct snd_kcontrol *k, int event) | |
521 | { | |
522 | /* Ensure GB module supports jack slot */ | |
523 | ||
524 | return 0; | |
525 | } | |
526 | ||
527 | static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) | |
528 | { | |
529 | int ret = 0; | |
530 | ||
531 | switch (w->type) { | |
532 | case snd_soc_dapm_spk: | |
533 | case snd_soc_dapm_hp: | |
534 | case snd_soc_dapm_mic: | |
535 | case snd_soc_dapm_output: | |
536 | case snd_soc_dapm_input: | |
537 | if (w->ncontrols) | |
538 | ret = -EINVAL; | |
539 | break; | |
540 | case snd_soc_dapm_switch: | |
541 | case snd_soc_dapm_mux: | |
542 | if (w->ncontrols != 1) | |
543 | ret = -EINVAL; | |
544 | break; | |
545 | default: | |
546 | break; | |
547 | } | |
548 | ||
549 | return ret; | |
550 | } | |
551 | ||
e65579e3 VA |
552 | static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol, |
553 | struct snd_ctl_elem_value *ucontrol) | |
554 | { | |
555 | int ret, ctl_id; | |
556 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
557 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | |
558 | struct gb_audio_ctl_elem_value gbvalue; | |
559 | struct gbaudio_module_info *module; | |
560 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); | |
9d3717f7 | 561 | struct gb_bundle *bundle; |
e65579e3 VA |
562 | |
563 | module = find_gb_module(gb, kcontrol->id.name); | |
564 | if (!module) | |
565 | return -EINVAL; | |
566 | ||
567 | ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); | |
568 | if (ctl_id < 0) | |
569 | return -EINVAL; | |
570 | ||
9d3717f7 DL |
571 | bundle = to_gb_bundle(module->dev); |
572 | ||
573 | ret = gb_pm_runtime_get_sync(bundle); | |
574 | if (ret) | |
575 | return ret; | |
576 | ||
e65579e3 VA |
577 | ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, |
578 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
9d3717f7 DL |
579 | |
580 | gb_pm_runtime_put_autosuspend(bundle); | |
581 | ||
e65579e3 VA |
582 | if (ret) { |
583 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, | |
584 | __func__, kcontrol->id.name); | |
585 | return ret; | |
586 | } | |
587 | ||
055fb9ce VA |
588 | ucontrol->value.enumerated.item[0] = |
589 | le32_to_cpu(gbvalue.value.enumerated_item[0]); | |
e65579e3 VA |
590 | if (e->shift_l != e->shift_r) |
591 | ucontrol->value.enumerated.item[1] = | |
055fb9ce | 592 | le32_to_cpu(gbvalue.value.enumerated_item[1]); |
e65579e3 VA |
593 | |
594 | return 0; | |
595 | } | |
596 | ||
597 | static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol, | |
598 | struct snd_ctl_elem_value *ucontrol) | |
599 | { | |
600 | int ret, ctl_id; | |
601 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
602 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | |
603 | struct gb_audio_ctl_elem_value gbvalue; | |
604 | struct gbaudio_module_info *module; | |
605 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); | |
9d3717f7 | 606 | struct gb_bundle *bundle; |
e65579e3 VA |
607 | |
608 | module = find_gb_module(gb, kcontrol->id.name); | |
609 | if (!module) | |
610 | return -EINVAL; | |
611 | ||
612 | ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); | |
613 | if (ctl_id < 0) | |
614 | return -EINVAL; | |
615 | ||
616 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | |
617 | return -EINVAL; | |
055fb9ce VA |
618 | gbvalue.value.enumerated_item[0] = |
619 | cpu_to_le32(ucontrol->value.enumerated.item[0]); | |
e65579e3 VA |
620 | |
621 | if (e->shift_l != e->shift_r) { | |
622 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | |
623 | return -EINVAL; | |
624 | gbvalue.value.enumerated_item[1] = | |
055fb9ce | 625 | cpu_to_le32(ucontrol->value.enumerated.item[1]); |
e65579e3 VA |
626 | } |
627 | ||
9d3717f7 DL |
628 | bundle = to_gb_bundle(module->dev); |
629 | ||
630 | ret = gb_pm_runtime_get_sync(bundle); | |
631 | if (ret) | |
632 | return ret; | |
633 | ||
e65579e3 VA |
634 | ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, |
635 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
9d3717f7 DL |
636 | |
637 | gb_pm_runtime_put_autosuspend(bundle); | |
638 | ||
e65579e3 VA |
639 | if (ret) { |
640 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, | |
641 | __func__, kcontrol->id.name); | |
642 | } | |
643 | ||
644 | return ret; | |
645 | } | |
646 | ||
647 | static int gbaudio_tplg_create_enum_kctl(struct gbaudio_module_info *gb, | |
648 | struct snd_kcontrol_new *kctl, | |
649 | struct gb_audio_control *ctl) | |
650 | { | |
651 | struct soc_enum *gbe; | |
652 | struct gb_audio_enumerated *gb_enum; | |
653 | int i; | |
654 | ||
655 | gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); | |
656 | if (!gbe) | |
657 | return -ENOMEM; | |
658 | ||
659 | gb_enum = &ctl->info.value.enumerated; | |
660 | ||
661 | /* since count=1, and reg is dummy */ | |
055fb9ce | 662 | gbe->max = le32_to_cpu(gb_enum->items); |
e65579e3 VA |
663 | gbe->texts = gb_generate_enum_strings(gb, gb_enum); |
664 | ||
665 | /* debug enum info */ | |
055fb9ce VA |
666 | dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gbe->max, |
667 | le16_to_cpu(gb_enum->names_length)); | |
668 | for (i = 0; i < gbe->max; i++) | |
e65579e3 VA |
669 | dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); |
670 | ||
671 | *kctl = (struct snd_kcontrol_new) | |
672 | SOC_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_ctl_get, | |
673 | gbcodec_enum_ctl_put); | |
674 | return 0; | |
675 | } | |
676 | ||
6dd67645 | 677 | static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb, |
6339d232 VA |
678 | struct snd_kcontrol_new *kctl, |
679 | struct gb_audio_control *ctl) | |
680 | { | |
e65579e3 | 681 | int ret = 0; |
6339d232 VA |
682 | struct gbaudio_ctl_pvt *ctldata; |
683 | ||
684 | switch (ctl->iface) { | |
685 | case SNDRV_CTL_ELEM_IFACE_MIXER: | |
e65579e3 VA |
686 | switch (ctl->info.type) { |
687 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
688 | ret = gbaudio_tplg_create_enum_kctl(gb, kctl, ctl); | |
689 | break; | |
690 | default: | |
691 | ctldata = devm_kzalloc(gb->dev, | |
692 | sizeof(struct gbaudio_ctl_pvt), | |
693 | GFP_KERNEL); | |
694 | if (!ctldata) | |
695 | return -ENOMEM; | |
696 | ctldata->ctl_id = ctl->id; | |
055fb9ce | 697 | ctldata->data_cport = le16_to_cpu(ctl->data_cport); |
e65579e3 VA |
698 | ctldata->access = ctl->access; |
699 | ctldata->vcount = ctl->count_values; | |
700 | ctldata->info = &ctl->info; | |
701 | *kctl = (struct snd_kcontrol_new) | |
702 | SOC_MIXER_GB(ctl->name, ctl->count, ctldata); | |
703 | ctldata = NULL; | |
704 | break; | |
705 | } | |
6339d232 VA |
706 | break; |
707 | default: | |
708 | return -EINVAL; | |
709 | } | |
710 | ||
711 | dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id); | |
e65579e3 VA |
712 | return ret; |
713 | } | |
714 | ||
715 | static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol, | |
716 | struct snd_ctl_elem_value *ucontrol) | |
717 | { | |
718 | int ret, ctl_id; | |
719 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | |
720 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
721 | struct gbaudio_module_info *module; | |
722 | struct gb_audio_ctl_elem_value gbvalue; | |
723 | struct snd_soc_codec *codec = widget->codec; | |
724 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); | |
725 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | |
9d3717f7 | 726 | struct gb_bundle *bundle; |
e65579e3 VA |
727 | |
728 | module = find_gb_module(gb, kcontrol->id.name); | |
729 | if (!module) | |
730 | return -EINVAL; | |
731 | ||
732 | ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); | |
733 | if (ctl_id < 0) | |
734 | return -EINVAL; | |
735 | ||
9d3717f7 DL |
736 | bundle = to_gb_bundle(module->dev); |
737 | ||
738 | ret = gb_pm_runtime_get_sync(bundle); | |
739 | if (ret) | |
740 | return ret; | |
741 | ||
e65579e3 VA |
742 | ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, |
743 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
9d3717f7 DL |
744 | |
745 | gb_pm_runtime_put_autosuspend(bundle); | |
746 | ||
e65579e3 VA |
747 | if (ret) { |
748 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, | |
749 | __func__, kcontrol->id.name); | |
750 | return ret; | |
751 | } | |
752 | ||
753 | ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; | |
754 | if (e->shift_l != e->shift_r) | |
755 | ucontrol->value.enumerated.item[1] = | |
756 | gbvalue.value.enumerated_item[1]; | |
757 | ||
6339d232 VA |
758 | return 0; |
759 | } | |
760 | ||
e65579e3 VA |
761 | static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, |
762 | struct snd_ctl_elem_value *ucontrol) | |
763 | { | |
764 | int ret, wi, ctl_id; | |
765 | unsigned int val, mux, change; | |
766 | unsigned int mask; | |
767 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | |
768 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
769 | struct gb_audio_ctl_elem_value gbvalue; | |
770 | struct gbaudio_module_info *module; | |
771 | struct snd_soc_codec *codec = widget->codec; | |
772 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); | |
773 | struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | |
9d3717f7 | 774 | struct gb_bundle *bundle; |
e65579e3 VA |
775 | |
776 | if (ucontrol->value.enumerated.item[0] > e->max - 1) | |
777 | return -EINVAL; | |
778 | ||
779 | module = find_gb_module(gb, kcontrol->id.name); | |
780 | if (!module) | |
781 | return -EINVAL; | |
782 | ||
783 | ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); | |
784 | if (ctl_id < 0) | |
785 | return -EINVAL; | |
786 | ||
787 | change = 0; | |
9d3717f7 DL |
788 | bundle = to_gb_bundle(module->dev); |
789 | ||
790 | ret = gb_pm_runtime_get_sync(bundle); | |
791 | if (ret) | |
792 | return ret; | |
793 | ||
e65579e3 VA |
794 | ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, |
795 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
9d3717f7 DL |
796 | |
797 | gb_pm_runtime_put_autosuspend(bundle); | |
798 | ||
e65579e3 VA |
799 | if (ret) { |
800 | dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, | |
801 | __func__, kcontrol->id.name); | |
802 | return ret; | |
803 | } | |
6339d232 | 804 | |
e65579e3 VA |
805 | mux = ucontrol->value.enumerated.item[0]; |
806 | val = mux << e->shift_l; | |
807 | mask = e->mask << e->shift_l; | |
6339d232 | 808 | |
e65579e3 VA |
809 | if (gbvalue.value.enumerated_item[0] != |
810 | ucontrol->value.enumerated.item[0]) { | |
811 | change = 1; | |
812 | gbvalue.value.enumerated_item[0] = | |
813 | ucontrol->value.enumerated.item[0]; | |
814 | } | |
815 | ||
816 | if (e->shift_l != e->shift_r) { | |
817 | if (ucontrol->value.enumerated.item[1] > e->max - 1) | |
818 | return -EINVAL; | |
819 | val |= ucontrol->value.enumerated.item[1] << e->shift_r; | |
820 | mask |= e->mask << e->shift_r; | |
821 | if (gbvalue.value.enumerated_item[1] != | |
822 | ucontrol->value.enumerated.item[1]) { | |
823 | change = 1; | |
824 | gbvalue.value.enumerated_item[1] = | |
825 | ucontrol->value.enumerated.item[1]; | |
826 | } | |
827 | } | |
828 | ||
829 | if (change) { | |
9d3717f7 DL |
830 | ret = gb_pm_runtime_get_sync(bundle); |
831 | if (ret) | |
832 | return ret; | |
833 | ||
e65579e3 VA |
834 | ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, |
835 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
9d3717f7 DL |
836 | |
837 | gb_pm_runtime_put_autosuspend(bundle); | |
838 | ||
e65579e3 VA |
839 | if (ret) { |
840 | dev_err_ratelimited(codec->dev, | |
841 | "%d:Error in %s for %s\n", ret, | |
842 | __func__, kcontrol->id.name); | |
843 | } | |
844 | for (wi = 0; wi < wlist->num_widgets; wi++) { | |
845 | widget = wlist->widgets[wi]; | |
846 | ||
847 | widget->value = val; | |
848 | widget->dapm->update = NULL; | |
849 | snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); | |
850 | } | |
851 | } | |
852 | ||
853 | return change; | |
854 | } | |
6339d232 | 855 | |
6dd67645 | 856 | static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb, |
6339d232 VA |
857 | struct snd_kcontrol_new *kctl, |
858 | struct gb_audio_control *ctl) | |
859 | { | |
e65579e3 VA |
860 | struct soc_enum *gbe; |
861 | struct gb_audio_enumerated *gb_enum; | |
862 | int i; | |
863 | ||
864 | gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); | |
865 | if (!gbe) | |
866 | return -ENOMEM; | |
6339d232 | 867 | |
e65579e3 VA |
868 | gb_enum = &ctl->info.value.enumerated; |
869 | ||
870 | /* since count=1, and reg is dummy */ | |
055fb9ce | 871 | gbe->max = le32_to_cpu(gb_enum->items); |
e65579e3 VA |
872 | gbe->texts = gb_generate_enum_strings(gb, gb_enum); |
873 | ||
874 | /* debug enum info */ | |
055fb9ce VA |
875 | dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gbe->max, |
876 | le16_to_cpu(gb_enum->names_length)); | |
877 | for (i = 0; i < gbe->max; i++) | |
e65579e3 VA |
878 | dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); |
879 | ||
880 | *kctl = (struct snd_kcontrol_new) | |
881 | SOC_DAPM_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_dapm_ctl_get, | |
882 | gbcodec_enum_dapm_ctl_put); | |
6339d232 VA |
883 | return 0; |
884 | } | |
885 | ||
6dd67645 | 886 | static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_module_info *gb, |
6339d232 VA |
887 | struct snd_kcontrol_new *kctl, |
888 | struct gb_audio_control *ctl) | |
889 | { | |
890 | struct gbaudio_ctl_pvt *ctldata; | |
891 | ||
892 | ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), | |
893 | GFP_KERNEL); | |
894 | if (!ctldata) | |
895 | return -ENOMEM; | |
896 | ctldata->ctl_id = ctl->id; | |
055fb9ce | 897 | ctldata->data_cport = le16_to_cpu(ctl->data_cport); |
6339d232 VA |
898 | ctldata->access = ctl->access; |
899 | ctldata->vcount = ctl->count_values; | |
900 | ctldata->info = &ctl->info; | |
901 | *kctl = (struct snd_kcontrol_new) | |
902 | SOC_DAPM_MIXER_GB(ctl->name, ctl->count, ctldata); | |
903 | ||
904 | return 0; | |
905 | } | |
906 | ||
6dd67645 | 907 | static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb, |
6339d232 VA |
908 | struct snd_kcontrol_new *kctl, |
909 | struct gb_audio_control *ctl) | |
910 | { | |
911 | int ret; | |
912 | ||
913 | switch (ctl->iface) { | |
914 | case SNDRV_CTL_ELEM_IFACE_MIXER: | |
915 | switch (ctl->info.type) { | |
916 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
917 | ret = gbaudio_tplg_create_enum_ctl(gb, kctl, ctl); | |
918 | break; | |
919 | default: | |
920 | ret = gbaudio_tplg_create_mixer_ctl(gb, kctl, ctl); | |
921 | break; | |
922 | } | |
923 | break; | |
924 | default: | |
925 | return -EINVAL; | |
926 | ||
927 | } | |
928 | ||
929 | dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name, | |
930 | ctl->id, ret); | |
931 | return ret; | |
932 | } | |
933 | ||
934 | static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, | |
935 | struct snd_kcontrol *kcontrol, int event) | |
936 | { | |
937 | int wid; | |
938 | int ret; | |
939 | struct snd_soc_codec *codec = w->codec; | |
796fad44 | 940 | struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); |
6dd67645 | 941 | struct gbaudio_module_info *module; |
6ba7fad4 | 942 | struct gb_bundle *bundle; |
6339d232 VA |
943 | |
944 | dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); | |
945 | ||
6dd67645 VA |
946 | /* Find relevant module */ |
947 | module = find_gb_module(gbcodec, w->name); | |
948 | if (!module) | |
949 | return -EINVAL; | |
950 | ||
6339d232 | 951 | /* map name to widget id */ |
6dd67645 | 952 | wid = gbaudio_map_widgetname(module, w->name); |
6339d232 VA |
953 | if (wid < 0) { |
954 | dev_err(codec->dev, "Invalid widget name:%s\n", w->name); | |
955 | return -EINVAL; | |
956 | } | |
957 | ||
6ba7fad4 DL |
958 | bundle = to_gb_bundle(module->dev); |
959 | ||
960 | ret = gb_pm_runtime_get_sync(bundle); | |
961 | if (ret) | |
962 | return ret; | |
963 | ||
6339d232 VA |
964 | switch (event) { |
965 | case SND_SOC_DAPM_PRE_PMU: | |
6dd67645 VA |
966 | ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid); |
967 | if (!ret) | |
4ffca62a | 968 | ret = gbaudio_module_update(gbcodec, w, module, 1); |
6339d232 VA |
969 | break; |
970 | case SND_SOC_DAPM_POST_PMD: | |
6dd67645 VA |
971 | ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid); |
972 | if (!ret) | |
4ffca62a | 973 | ret = gbaudio_module_update(gbcodec, w, module, 0); |
6339d232 VA |
974 | break; |
975 | } | |
976 | if (ret) | |
c6722ab5 VA |
977 | dev_err_ratelimited(codec->dev, |
978 | "%d: widget, event:%d failed:%d\n", wid, | |
979 | event, ret); | |
6ba7fad4 DL |
980 | |
981 | gb_pm_runtime_put_autosuspend(bundle); | |
982 | ||
6339d232 VA |
983 | return ret; |
984 | } | |
985 | ||
6dd67645 | 986 | static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, |
6339d232 | 987 | struct snd_soc_dapm_widget *dw, |
d4cd9daa | 988 | struct gb_audio_widget *w, int *w_size) |
6339d232 | 989 | { |
d4cd9daa | 990 | int i, ret, csize; |
6339d232 VA |
991 | struct snd_kcontrol_new *widget_kctls; |
992 | struct gb_audio_control *curr; | |
993 | struct gbaudio_control *control, *_control; | |
994 | size_t size; | |
6dd67645 | 995 | char temp_name[NAME_SIZE]; |
6339d232 VA |
996 | |
997 | ret = gbaudio_validate_kcontrol_count(w); | |
998 | if (ret) { | |
ac5d6d86 | 999 | dev_err(module->dev, "Invalid kcontrol count=%d for %s\n", |
6339d232 VA |
1000 | w->ncontrols, w->name); |
1001 | return ret; | |
1002 | } | |
1003 | ||
1004 | /* allocate memory for kcontrol */ | |
1005 | if (w->ncontrols) { | |
1006 | size = sizeof(struct snd_kcontrol_new) * w->ncontrols; | |
6dd67645 | 1007 | widget_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL); |
6339d232 VA |
1008 | if (!widget_kctls) |
1009 | return -ENOMEM; | |
1010 | } | |
1011 | ||
d4cd9daa VA |
1012 | *w_size = sizeof(struct gb_audio_widget); |
1013 | ||
6339d232 | 1014 | /* create relevant kcontrols */ |
d4cd9daa | 1015 | curr = w->ctl; |
6339d232 | 1016 | for (i = 0; i < w->ncontrols; i++) { |
6dd67645 | 1017 | ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i], |
6339d232 VA |
1018 | curr); |
1019 | if (ret) { | |
6dd67645 | 1020 | dev_err(module->dev, |
6339d232 VA |
1021 | "%s:%d type widget_ctl not supported\n", |
1022 | curr->name, curr->iface); | |
1023 | goto error; | |
1024 | } | |
6dd67645 | 1025 | control = devm_kzalloc(module->dev, |
6339d232 VA |
1026 | sizeof(struct gbaudio_control), |
1027 | GFP_KERNEL); | |
1028 | if (!control) { | |
1029 | ret = -ENOMEM; | |
1030 | goto error; | |
1031 | } | |
1032 | control->id = curr->id; | |
1033 | control->name = curr->name; | |
0c15a9e0 VA |
1034 | control->wname = w->name; |
1035 | ||
e65579e3 VA |
1036 | if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { |
1037 | struct gb_audio_enumerated *gbenum = | |
1038 | &curr->info.value.enumerated; | |
1039 | ||
1040 | csize = offsetof(struct gb_audio_control, info); | |
1041 | csize += offsetof(struct gb_audio_ctl_elem_info, value); | |
1042 | csize += offsetof(struct gb_audio_enumerated, names); | |
055fb9ce | 1043 | csize += le16_to_cpu(gbenum->names_length); |
6339d232 | 1044 | control->texts = (const char * const *) |
e65579e3 | 1045 | gb_generate_enum_strings(module, gbenum); |
055fb9ce | 1046 | control->items = le32_to_cpu(gbenum->items); |
02ae32f7 | 1047 | } else { |
e65579e3 | 1048 | csize = sizeof(struct gb_audio_control); |
02ae32f7 CM |
1049 | } |
1050 | ||
d4cd9daa VA |
1051 | *w_size += csize; |
1052 | curr = (void *)curr + csize; | |
6dd67645 VA |
1053 | list_add(&control->list, &module->widget_ctl_list); |
1054 | dev_dbg(module->dev, "%s: control of type %d created\n", | |
6339d232 VA |
1055 | widget_kctls[i].name, widget_kctls[i].iface); |
1056 | } | |
1057 | ||
6dd67645 VA |
1058 | /* Prefix dev_id to widget control_name */ |
1059 | strlcpy(temp_name, w->name, NAME_SIZE); | |
1060 | snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); | |
1061 | ||
6339d232 VA |
1062 | switch (w->type) { |
1063 | case snd_soc_dapm_spk: | |
1064 | *dw = (struct snd_soc_dapm_widget) | |
1065 | SND_SOC_DAPM_SPK(w->name, gbcodec_event_spk); | |
89de9a06 | 1066 | module->op_devices |= GBAUDIO_DEVICE_OUT_SPEAKER; |
6339d232 VA |
1067 | break; |
1068 | case snd_soc_dapm_hp: | |
1069 | *dw = (struct snd_soc_dapm_widget) | |
1070 | SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); | |
89de9a06 VA |
1071 | module->op_devices |= (GBAUDIO_DEVICE_OUT_WIRED_HEADSET |
1072 | | GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE); | |
1073 | module->ip_devices |= GBAUDIO_DEVICE_IN_WIRED_HEADSET; | |
6339d232 VA |
1074 | break; |
1075 | case snd_soc_dapm_mic: | |
1076 | *dw = (struct snd_soc_dapm_widget) | |
1077 | SND_SOC_DAPM_MIC(w->name, gbcodec_event_int_mic); | |
89de9a06 | 1078 | module->ip_devices |= GBAUDIO_DEVICE_IN_BUILTIN_MIC; |
6339d232 VA |
1079 | break; |
1080 | case snd_soc_dapm_output: | |
1081 | *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_OUTPUT(w->name); | |
1082 | break; | |
1083 | case snd_soc_dapm_input: | |
1084 | *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_INPUT(w->name); | |
1085 | break; | |
1086 | case snd_soc_dapm_switch: | |
1087 | *dw = (struct snd_soc_dapm_widget) | |
1088 | SND_SOC_DAPM_SWITCH_E(w->name, SND_SOC_NOPM, 0, 0, | |
1089 | widget_kctls, gbaudio_widget_event, | |
1090 | SND_SOC_DAPM_PRE_PMU | | |
1091 | SND_SOC_DAPM_POST_PMD); | |
1092 | break; | |
1093 | case snd_soc_dapm_pga: | |
1094 | *dw = (struct snd_soc_dapm_widget) | |
1095 | SND_SOC_DAPM_PGA_E(w->name, SND_SOC_NOPM, 0, 0, NULL, 0, | |
1096 | gbaudio_widget_event, | |
1097 | SND_SOC_DAPM_PRE_PMU | | |
1098 | SND_SOC_DAPM_POST_PMD); | |
1099 | break; | |
1100 | case snd_soc_dapm_mixer: | |
1101 | *dw = (struct snd_soc_dapm_widget) | |
1102 | SND_SOC_DAPM_MIXER_E(w->name, SND_SOC_NOPM, 0, 0, NULL, | |
1103 | 0, gbaudio_widget_event, | |
1104 | SND_SOC_DAPM_PRE_PMU | | |
1105 | SND_SOC_DAPM_POST_PMD); | |
1106 | break; | |
1107 | case snd_soc_dapm_mux: | |
1108 | *dw = (struct snd_soc_dapm_widget) | |
1109 | SND_SOC_DAPM_MUX_E(w->name, SND_SOC_NOPM, 0, 0, | |
1110 | widget_kctls, gbaudio_widget_event, | |
1111 | SND_SOC_DAPM_PRE_PMU | | |
1112 | SND_SOC_DAPM_POST_PMD); | |
1113 | break; | |
1114 | case snd_soc_dapm_aif_in: | |
1115 | *dw = (struct snd_soc_dapm_widget) | |
1116 | SND_SOC_DAPM_AIF_IN_E(w->name, w->sname, 0, | |
1117 | SND_SOC_NOPM, | |
1118 | 0, 0, gbaudio_widget_event, | |
1119 | SND_SOC_DAPM_PRE_PMU | | |
1120 | SND_SOC_DAPM_POST_PMD); | |
1121 | break; | |
1122 | case snd_soc_dapm_aif_out: | |
1123 | *dw = (struct snd_soc_dapm_widget) | |
1124 | SND_SOC_DAPM_AIF_OUT_E(w->name, w->sname, 0, | |
1125 | SND_SOC_NOPM, | |
1126 | 0, 0, gbaudio_widget_event, | |
1127 | SND_SOC_DAPM_PRE_PMU | | |
1128 | SND_SOC_DAPM_POST_PMD); | |
1129 | break; | |
1130 | default: | |
1131 | ret = -EINVAL; | |
1132 | goto error; | |
1133 | } | |
1134 | ||
6dd67645 | 1135 | dev_dbg(module->dev, "%s: widget of type %d created\n", dw->name, |
6339d232 VA |
1136 | dw->id); |
1137 | return 0; | |
1138 | error: | |
6dd67645 | 1139 | list_for_each_entry_safe(control, _control, &module->widget_ctl_list, |
6339d232 VA |
1140 | list) { |
1141 | list_del(&control->list); | |
6dd67645 | 1142 | devm_kfree(module->dev, control); |
6339d232 VA |
1143 | } |
1144 | return ret; | |
1145 | } | |
1146 | ||
6dd67645 | 1147 | static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, |
6339d232 VA |
1148 | struct gb_audio_control *controls) |
1149 | { | |
d4cd9daa | 1150 | int i, csize, ret; |
6339d232 VA |
1151 | struct snd_kcontrol_new *dapm_kctls; |
1152 | struct gb_audio_control *curr; | |
1153 | struct gbaudio_control *control, *_control; | |
1154 | size_t size; | |
6dd67645 | 1155 | char temp_name[NAME_SIZE]; |
6339d232 | 1156 | |
6dd67645 VA |
1157 | size = sizeof(struct snd_kcontrol_new) * module->num_controls; |
1158 | dapm_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL); | |
6339d232 VA |
1159 | if (!dapm_kctls) |
1160 | return -ENOMEM; | |
1161 | ||
1162 | curr = controls; | |
6dd67645 VA |
1163 | for (i = 0; i < module->num_controls; i++) { |
1164 | ret = gbaudio_tplg_create_kcontrol(module, &dapm_kctls[i], | |
6339d232 VA |
1165 | curr); |
1166 | if (ret) { | |
6dd67645 | 1167 | dev_err(module->dev, "%s:%d type not supported\n", |
6339d232 VA |
1168 | curr->name, curr->iface); |
1169 | goto error; | |
1170 | } | |
6dd67645 | 1171 | control = devm_kzalloc(module->dev, sizeof(struct |
6339d232 VA |
1172 | gbaudio_control), |
1173 | GFP_KERNEL); | |
1174 | if (!control) { | |
1175 | ret = -ENOMEM; | |
1176 | goto error; | |
1177 | } | |
1178 | control->id = curr->id; | |
6dd67645 VA |
1179 | /* Prefix dev_id to widget_name */ |
1180 | strlcpy(temp_name, curr->name, NAME_SIZE); | |
1181 | snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id, | |
1182 | temp_name); | |
6339d232 | 1183 | control->name = curr->name; |
e65579e3 VA |
1184 | if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { |
1185 | struct gb_audio_enumerated *gbenum = | |
1186 | &curr->info.value.enumerated; | |
1187 | ||
1188 | csize = offsetof(struct gb_audio_control, info); | |
1189 | csize += offsetof(struct gb_audio_ctl_elem_info, value); | |
1190 | csize += offsetof(struct gb_audio_enumerated, names); | |
055fb9ce | 1191 | csize += le16_to_cpu(gbenum->names_length); |
6339d232 | 1192 | control->texts = (const char * const *) |
e65579e3 | 1193 | gb_generate_enum_strings(module, gbenum); |
055fb9ce | 1194 | control->items = le32_to_cpu(gbenum->items); |
02ae32f7 | 1195 | } else { |
e65579e3 | 1196 | csize = sizeof(struct gb_audio_control); |
02ae32f7 | 1197 | } |
d4cd9daa | 1198 | |
6dd67645 VA |
1199 | list_add(&control->list, &module->ctl_list); |
1200 | dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id, | |
6339d232 | 1201 | curr->name, curr->info.type); |
d4cd9daa | 1202 | curr = (void *)curr + csize; |
6339d232 | 1203 | } |
6dd67645 | 1204 | module->controls = dapm_kctls; |
6339d232 VA |
1205 | |
1206 | return 0; | |
1207 | error: | |
6dd67645 | 1208 | list_for_each_entry_safe(control, _control, &module->ctl_list, |
6339d232 VA |
1209 | list) { |
1210 | list_del(&control->list); | |
6dd67645 | 1211 | devm_kfree(module->dev, control); |
6339d232 | 1212 | } |
6dd67645 | 1213 | devm_kfree(module->dev, dapm_kctls); |
6339d232 VA |
1214 | return ret; |
1215 | } | |
1216 | ||
6dd67645 | 1217 | static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, |
6339d232 VA |
1218 | struct gb_audio_widget *widgets) |
1219 | { | |
d4cd9daa | 1220 | int i, ret, w_size; |
6339d232 VA |
1221 | struct snd_soc_dapm_widget *dapm_widgets; |
1222 | struct gb_audio_widget *curr; | |
1223 | struct gbaudio_widget *widget, *_widget; | |
1224 | size_t size; | |
1225 | ||
6dd67645 VA |
1226 | size = sizeof(struct snd_soc_dapm_widget) * module->num_dapm_widgets; |
1227 | dapm_widgets = devm_kzalloc(module->dev, size, GFP_KERNEL); | |
6339d232 VA |
1228 | if (!dapm_widgets) |
1229 | return -ENOMEM; | |
1230 | ||
1231 | curr = widgets; | |
6dd67645 VA |
1232 | for (i = 0; i < module->num_dapm_widgets; i++) { |
1233 | ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i], | |
d4cd9daa | 1234 | curr, &w_size); |
6339d232 | 1235 | if (ret) { |
6dd67645 | 1236 | dev_err(module->dev, "%s:%d type not supported\n", |
6339d232 VA |
1237 | curr->name, curr->type); |
1238 | goto error; | |
1239 | } | |
6dd67645 | 1240 | widget = devm_kzalloc(module->dev, sizeof(struct |
6339d232 VA |
1241 | gbaudio_widget), |
1242 | GFP_KERNEL); | |
1243 | if (!widget) { | |
1244 | ret = -ENOMEM; | |
1245 | goto error; | |
1246 | } | |
1247 | widget->id = curr->id; | |
1248 | widget->name = curr->name; | |
6dd67645 | 1249 | list_add(&widget->list, &module->widget_list); |
d4cd9daa | 1250 | curr = (void *)curr + w_size; |
6339d232 | 1251 | } |
6dd67645 | 1252 | module->dapm_widgets = dapm_widgets; |
6339d232 VA |
1253 | |
1254 | return 0; | |
1255 | ||
1256 | error: | |
6dd67645 | 1257 | list_for_each_entry_safe(widget, _widget, &module->widget_list, |
6339d232 VA |
1258 | list) { |
1259 | list_del(&widget->list); | |
6dd67645 | 1260 | devm_kfree(module->dev, widget); |
6339d232 | 1261 | } |
6dd67645 | 1262 | devm_kfree(module->dev, dapm_widgets); |
6339d232 VA |
1263 | return ret; |
1264 | } | |
1265 | ||
6dd67645 | 1266 | static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module, |
6339d232 VA |
1267 | struct gb_audio_route *routes) |
1268 | { | |
1269 | int i, ret; | |
1270 | struct snd_soc_dapm_route *dapm_routes; | |
1271 | struct gb_audio_route *curr; | |
1272 | size_t size; | |
1273 | ||
6dd67645 VA |
1274 | size = sizeof(struct snd_soc_dapm_route) * module->num_dapm_routes; |
1275 | dapm_routes = devm_kzalloc(module->dev, size, GFP_KERNEL); | |
6339d232 VA |
1276 | if (!dapm_routes) |
1277 | return -ENOMEM; | |
1278 | ||
6dd67645 | 1279 | module->dapm_routes = dapm_routes; |
6339d232 VA |
1280 | curr = routes; |
1281 | ||
6dd67645 | 1282 | for (i = 0; i < module->num_dapm_routes; i++) { |
6339d232 | 1283 | dapm_routes->sink = |
6dd67645 | 1284 | gbaudio_map_widgetid(module, curr->destination_id); |
6339d232 | 1285 | if (!dapm_routes->sink) { |
6dd67645 | 1286 | dev_err(module->dev, "%d:%d:%d:%d - Invalid sink\n", |
6339d232 VA |
1287 | curr->source_id, curr->destination_id, |
1288 | curr->control_id, curr->index); | |
1289 | ret = -EINVAL; | |
1290 | goto error; | |
1291 | } | |
1292 | dapm_routes->source = | |
6dd67645 | 1293 | gbaudio_map_widgetid(module, curr->source_id); |
6339d232 | 1294 | if (!dapm_routes->source) { |
6dd67645 | 1295 | dev_err(module->dev, "%d:%d:%d:%d - Invalid source\n", |
6339d232 VA |
1296 | curr->source_id, curr->destination_id, |
1297 | curr->control_id, curr->index); | |
1298 | ret = -EINVAL; | |
1299 | goto error; | |
1300 | } | |
1301 | dapm_routes->control = | |
6dd67645 | 1302 | gbaudio_map_controlid(module, |
6339d232 VA |
1303 | curr->control_id, |
1304 | curr->index); | |
1305 | if ((curr->control_id != GBAUDIO_INVALID_ID) && | |
1306 | !dapm_routes->control) { | |
6dd67645 | 1307 | dev_err(module->dev, "%d:%d:%d:%d - Invalid control\n", |
6339d232 VA |
1308 | curr->source_id, curr->destination_id, |
1309 | curr->control_id, curr->index); | |
1310 | ret = -EINVAL; | |
1311 | goto error; | |
1312 | } | |
6dd67645 | 1313 | dev_dbg(module->dev, "Route {%s, %s, %s}\n", dapm_routes->sink, |
8a983614 | 1314 | (dapm_routes->control) ? dapm_routes->control : "NULL", |
6339d232 VA |
1315 | dapm_routes->source); |
1316 | dapm_routes++; | |
1317 | curr++; | |
1318 | } | |
1319 | ||
1320 | return 0; | |
1321 | ||
1322 | error: | |
6198f892 | 1323 | devm_kfree(module->dev, module->dapm_routes); |
6339d232 VA |
1324 | return ret; |
1325 | } | |
1326 | ||
6dd67645 | 1327 | static int gbaudio_tplg_process_header(struct gbaudio_module_info *module, |
6339d232 VA |
1328 | struct gb_audio_topology *tplg_data) |
1329 | { | |
1330 | /* fetch no. of kcontrols, widgets & routes */ | |
6dd67645 VA |
1331 | module->num_controls = tplg_data->num_controls; |
1332 | module->num_dapm_widgets = tplg_data->num_widgets; | |
1333 | module->num_dapm_routes = tplg_data->num_routes; | |
6339d232 VA |
1334 | |
1335 | /* update block offset */ | |
6dd67645 | 1336 | module->dai_offset = (unsigned long)&tplg_data->data; |
055fb9ce VA |
1337 | module->control_offset = module->dai_offset + |
1338 | le32_to_cpu(tplg_data->size_dais); | |
6dd67645 | 1339 | module->widget_offset = module->control_offset + |
055fb9ce | 1340 | le32_to_cpu(tplg_data->size_controls); |
6dd67645 | 1341 | module->route_offset = module->widget_offset + |
055fb9ce | 1342 | le32_to_cpu(tplg_data->size_widgets); |
6339d232 | 1343 | |
6dd67645 VA |
1344 | dev_dbg(module->dev, "DAI offset is 0x%lx\n", module->dai_offset); |
1345 | dev_dbg(module->dev, "control offset is %lx\n", | |
1346 | module->control_offset); | |
1347 | dev_dbg(module->dev, "widget offset is %lx\n", module->widget_offset); | |
1348 | dev_dbg(module->dev, "route offset is %lx\n", module->route_offset); | |
6339d232 VA |
1349 | |
1350 | return 0; | |
1351 | } | |
1352 | ||
6dd67645 | 1353 | int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, |
6339d232 VA |
1354 | struct gb_audio_topology *tplg_data) |
1355 | { | |
1356 | int ret; | |
6339d232 VA |
1357 | struct gb_audio_control *controls; |
1358 | struct gb_audio_widget *widgets; | |
1359 | struct gb_audio_route *routes; | |
055fb9ce | 1360 | unsigned int jack_type; |
6339d232 VA |
1361 | |
1362 | if (!tplg_data) | |
1363 | return -EINVAL; | |
1364 | ||
6dd67645 | 1365 | ret = gbaudio_tplg_process_header(module, tplg_data); |
6339d232 | 1366 | if (ret) { |
6dd67645 | 1367 | dev_err(module->dev, "%d: Error in parsing topology header\n", |
6339d232 VA |
1368 | ret); |
1369 | return ret; | |
1370 | } | |
1371 | ||
1372 | /* process control */ | |
6dd67645 VA |
1373 | controls = (struct gb_audio_control *)module->control_offset; |
1374 | ret = gbaudio_tplg_process_kcontrols(module, controls); | |
6339d232 | 1375 | if (ret) { |
6dd67645 | 1376 | dev_err(module->dev, |
6339d232 VA |
1377 | "%d: Error in parsing controls data\n", ret); |
1378 | return ret; | |
1379 | } | |
6dd67645 | 1380 | dev_dbg(module->dev, "Control parsing finished\n"); |
6339d232 VA |
1381 | |
1382 | /* process widgets */ | |
6dd67645 VA |
1383 | widgets = (struct gb_audio_widget *)module->widget_offset; |
1384 | ret = gbaudio_tplg_process_widgets(module, widgets); | |
6339d232 | 1385 | if (ret) { |
6dd67645 | 1386 | dev_err(module->dev, |
6339d232 VA |
1387 | "%d: Error in parsing widgets data\n", ret); |
1388 | return ret; | |
1389 | } | |
6dd67645 | 1390 | dev_dbg(module->dev, "Widget parsing finished\n"); |
6339d232 VA |
1391 | |
1392 | /* process route */ | |
6dd67645 VA |
1393 | routes = (struct gb_audio_route *)module->route_offset; |
1394 | ret = gbaudio_tplg_process_routes(module, routes); | |
6339d232 | 1395 | if (ret) { |
6dd67645 | 1396 | dev_err(module->dev, |
6339d232 VA |
1397 | "%d: Error in parsing routes data\n", ret); |
1398 | return ret; | |
1399 | } | |
6dd67645 | 1400 | dev_dbg(module->dev, "Route parsing finished\n"); |
6339d232 | 1401 | |
847175e8 | 1402 | /* parse jack capabilities */ |
055fb9ce VA |
1403 | jack_type = le32_to_cpu(tplg_data->jack_type); |
1404 | if (jack_type) { | |
1405 | module->jack_mask = jack_type & GBCODEC_JACK_MASK; | |
1406 | module->button_mask = jack_type & GBCODEC_JACK_BUTTON_MASK; | |
847175e8 VA |
1407 | } |
1408 | ||
6339d232 VA |
1409 | return ret; |
1410 | } | |
1411 | ||
6dd67645 | 1412 | void gbaudio_tplg_release(struct gbaudio_module_info *module) |
6339d232 | 1413 | { |
6339d232 VA |
1414 | struct gbaudio_control *control, *_control; |
1415 | struct gbaudio_widget *widget, *_widget; | |
1416 | ||
6dd67645 | 1417 | if (!module->topology) |
6339d232 VA |
1418 | return; |
1419 | ||
1420 | /* release kcontrols */ | |
6dd67645 | 1421 | list_for_each_entry_safe(control, _control, &module->ctl_list, |
6339d232 VA |
1422 | list) { |
1423 | list_del(&control->list); | |
6dd67645 | 1424 | devm_kfree(module->dev, control); |
6339d232 | 1425 | } |
6dd67645 VA |
1426 | if (module->controls) |
1427 | devm_kfree(module->dev, module->controls); | |
6339d232 VA |
1428 | |
1429 | /* release widget controls */ | |
6dd67645 | 1430 | list_for_each_entry_safe(control, _control, &module->widget_ctl_list, |
6339d232 VA |
1431 | list) { |
1432 | list_del(&control->list); | |
6dd67645 | 1433 | devm_kfree(module->dev, control); |
6339d232 VA |
1434 | } |
1435 | ||
1436 | /* release widgets */ | |
6dd67645 | 1437 | list_for_each_entry_safe(widget, _widget, &module->widget_list, |
6339d232 VA |
1438 | list) { |
1439 | list_del(&widget->list); | |
6dd67645 | 1440 | devm_kfree(module->dev, widget); |
6339d232 | 1441 | } |
6dd67645 VA |
1442 | if (module->dapm_widgets) |
1443 | devm_kfree(module->dev, module->dapm_widgets); | |
6339d232 VA |
1444 | |
1445 | /* release routes */ | |
6dd67645 VA |
1446 | if (module->dapm_routes) |
1447 | devm_kfree(module->dev, module->dapm_routes); | |
6339d232 | 1448 | } |