Commit | Line | Data |
---|---|---|
6339d232 VA |
1 | /* |
2 | * Greybus audio driver | |
3 | * Copyright 2015-2016 Google Inc. | |
4 | * Copyright 2015-2016 Linaro Ltd. | |
5 | * | |
6 | * Released under the GPLv2 only. | |
7 | */ | |
8 | ||
9 | #include "audio_codec.h" | |
10 | #include "greybus_protocols.h" | |
11 | ||
12 | #define GBAUDIO_INVALID_ID 0xFF | |
13 | ||
14 | /* mixer control */ | |
15 | struct gb_mixer_control { | |
16 | int min, max; | |
17 | unsigned int reg, rreg, shift, rshift, invert; | |
18 | }; | |
19 | ||
20 | struct gbaudio_ctl_pvt { | |
21 | unsigned int ctl_id; | |
22 | unsigned int data_cport; | |
23 | unsigned int access; | |
24 | unsigned int vcount; | |
25 | struct gb_audio_ctl_elem_info *info; | |
26 | }; | |
27 | ||
28 | static const char *gbaudio_map_controlid(struct gbaudio_codec_info *gbcodec, | |
29 | __u8 control_id, __u8 index) | |
30 | { | |
31 | struct gbaudio_control *control; | |
32 | ||
33 | if (control_id == GBAUDIO_INVALID_ID) | |
34 | return NULL; | |
35 | ||
36 | list_for_each_entry(control, &gbcodec->codec_ctl_list, list) { | |
37 | if (control->id == control_id) { | |
38 | if (index == GBAUDIO_INVALID_ID) | |
39 | return control->name; | |
40 | return control->texts[index]; | |
41 | } | |
42 | } | |
43 | list_for_each_entry(control, &gbcodec->widget_ctl_list, list) { | |
44 | if (control->id == control_id) { | |
45 | if (index == GBAUDIO_INVALID_ID) | |
46 | return control->name; | |
47 | return control->texts[index]; | |
48 | } | |
49 | } | |
50 | return NULL; | |
51 | } | |
52 | ||
53 | static int gbaudio_map_widgetname(struct gbaudio_codec_info *gbcodec, | |
54 | const char *name) | |
55 | { | |
56 | struct gbaudio_widget *widget; | |
57 | char widget_name[NAME_SIZE]; | |
58 | char prefix_name[NAME_SIZE]; | |
59 | ||
60 | snprintf(prefix_name, NAME_SIZE, "GB %d ", gbcodec->dev_id); | |
61 | if (strncmp(name, prefix_name, strlen(prefix_name))) | |
62 | return -EINVAL; | |
63 | ||
64 | strlcpy(widget_name, name+strlen(prefix_name), NAME_SIZE); | |
65 | dev_dbg(gbcodec->dev, "widget_name:%s, truncated widget_name:%s\n", | |
66 | name, widget_name); | |
67 | ||
68 | list_for_each_entry(widget, &gbcodec->widget_list, list) { | |
69 | if (!strncmp(widget->name, widget_name, NAME_SIZE)) | |
70 | return widget->id; | |
71 | } | |
72 | return -EINVAL; | |
73 | } | |
74 | ||
75 | static const char *gbaudio_map_widgetid(struct gbaudio_codec_info *gbcodec, | |
76 | __u8 widget_id) | |
77 | { | |
78 | struct gbaudio_widget *widget; | |
79 | ||
80 | list_for_each_entry(widget, &gbcodec->widget_list, list) { | |
81 | if (widget->id == widget_id) | |
82 | return widget->name; | |
83 | } | |
84 | return NULL; | |
85 | } | |
86 | ||
87 | static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, | |
88 | struct snd_ctl_elem_info *uinfo) | |
89 | { | |
90 | unsigned int max; | |
91 | const char *name; | |
92 | struct gbaudio_ctl_pvt *data; | |
93 | struct gb_audio_ctl_elem_info *info; | |
94 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
796fad44 | 95 | struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); |
6339d232 VA |
96 | |
97 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; | |
98 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
99 | ||
100 | if (!info) { | |
101 | dev_err(gbcodec->dev, "NULL info for %s\n", uinfo->id.name); | |
102 | return -EINVAL; | |
103 | } | |
104 | ||
105 | /* update uinfo */ | |
106 | uinfo->access = data->access; | |
107 | uinfo->count = data->vcount; | |
108 | uinfo->type = (snd_ctl_elem_type_t)info->type; | |
109 | ||
110 | switch (info->type) { | |
111 | case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: | |
112 | case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: | |
113 | uinfo->value.integer.min = info->value.integer.min; | |
114 | uinfo->value.integer.max = info->value.integer.max; | |
115 | break; | |
116 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
117 | max = info->value.enumerated.items; | |
118 | uinfo->value.enumerated.items = max; | |
119 | if (uinfo->value.enumerated.item > max - 1) | |
120 | uinfo->value.enumerated.item = max - 1; | |
121 | name = gbaudio_map_controlid(gbcodec, data->ctl_id, | |
122 | uinfo->value.enumerated.item); | |
123 | strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); | |
124 | break; | |
125 | default: | |
126 | dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", | |
127 | info->type, kcontrol->id.name); | |
128 | break; | |
129 | } | |
130 | return 0; | |
131 | } | |
132 | ||
133 | static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, | |
134 | struct snd_ctl_elem_value *ucontrol) | |
135 | { | |
136 | int ret; | |
137 | struct gb_audio_ctl_elem_info *info; | |
138 | struct gbaudio_ctl_pvt *data; | |
139 | struct gb_audio_ctl_elem_value gbvalue; | |
140 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
796fad44 | 141 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6339d232 VA |
142 | |
143 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; | |
144 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
145 | ||
146 | ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id, | |
147 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
148 | if (ret) { | |
149 | dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, | |
150 | kcontrol->id.name); | |
151 | return ret; | |
152 | } | |
153 | ||
154 | /* update ucontrol */ | |
155 | switch (info->type) { | |
156 | case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: | |
157 | case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: | |
158 | ucontrol->value.integer.value[0] = | |
159 | gbvalue.value.integer_value[0]; | |
160 | if (data->vcount == 2) | |
161 | ucontrol->value.integer.value[1] = | |
162 | gbvalue.value.integer_value[1]; | |
163 | break; | |
164 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
165 | ucontrol->value.enumerated.item[0] = | |
166 | gbvalue.value.enumerated_item[0]; | |
167 | if (data->vcount == 2) | |
168 | ucontrol->value.enumerated.item[1] = | |
169 | gbvalue.value.enumerated_item[1]; | |
170 | break; | |
171 | default: | |
172 | dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", | |
173 | info->type, kcontrol->id.name); | |
174 | ret = -EINVAL; | |
175 | break; | |
176 | } | |
177 | return ret; | |
178 | } | |
179 | ||
180 | static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, | |
181 | struct snd_ctl_elem_value *ucontrol) | |
182 | { | |
183 | int ret = 0; | |
184 | struct gb_audio_ctl_elem_info *info; | |
185 | struct gbaudio_ctl_pvt *data; | |
186 | struct gb_audio_ctl_elem_value gbvalue; | |
187 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | |
796fad44 | 188 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6339d232 VA |
189 | |
190 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; | |
191 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
192 | ||
193 | /* update ucontrol */ | |
194 | switch (info->type) { | |
195 | case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: | |
196 | case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: | |
197 | gbvalue.value.integer_value[0] = | |
198 | ucontrol->value.integer.value[0]; | |
199 | if (data->vcount == 2) | |
200 | gbvalue.value.integer_value[1] = | |
201 | ucontrol->value.integer.value[1]; | |
202 | break; | |
203 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
204 | gbvalue.value.enumerated_item[0] = | |
205 | ucontrol->value.enumerated.item[0]; | |
206 | if (data->vcount == 2) | |
207 | gbvalue.value.enumerated_item[1] = | |
208 | ucontrol->value.enumerated.item[1]; | |
209 | break; | |
210 | default: | |
211 | dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", | |
212 | info->type, kcontrol->id.name); | |
213 | ret = -EINVAL; | |
214 | break; | |
215 | } | |
216 | ||
217 | if (ret) | |
218 | return ret; | |
219 | ||
220 | ret = gb_audio_gb_set_control(gb->mgmt_connection, data->ctl_id, | |
221 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
222 | if (ret) { | |
223 | dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, | |
224 | kcontrol->id.name); | |
225 | } | |
226 | ||
227 | return ret; | |
228 | } | |
229 | ||
230 | #define SOC_MIXER_GB(xname, kcount, data) \ | |
231 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | |
232 | .count = kcount, .info = gbcodec_mixer_ctl_info, \ | |
233 | .get = gbcodec_mixer_ctl_get, .put = gbcodec_mixer_ctl_put, \ | |
234 | .private_value = (unsigned long)data } | |
235 | ||
236 | /* | |
237 | * although below callback functions seems redundant to above functions. | |
238 | * same are kept to allow provision for different handling in case | |
239 | * of DAPM related sequencing, etc. | |
240 | */ | |
241 | static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, | |
242 | struct snd_ctl_elem_info *uinfo) | |
243 | { | |
244 | int platform_max, platform_min; | |
245 | struct gbaudio_ctl_pvt *data; | |
246 | struct gb_audio_ctl_elem_info *info; | |
247 | ||
248 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; | |
249 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
250 | ||
251 | /* update uinfo */ | |
252 | platform_max = info->value.integer.max; | |
253 | platform_min = info->value.integer.min; | |
254 | ||
255 | if (platform_max == 1 && | |
256 | !strnstr(kcontrol->id.name, " Volume", NAME_SIZE)) | |
257 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | |
258 | else | |
259 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
260 | ||
261 | uinfo->count = data->vcount; | |
262 | uinfo->value.integer.min = 0; | |
263 | if (info->value.integer.min < 0 && | |
264 | (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER)) | |
265 | uinfo->value.integer.max = platform_max - platform_min; | |
266 | else | |
267 | uinfo->value.integer.max = platform_max; | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
272 | static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, | |
273 | struct snd_ctl_elem_value *ucontrol) | |
274 | { | |
275 | int ret; | |
276 | struct gb_audio_ctl_elem_info *info; | |
277 | struct gbaudio_ctl_pvt *data; | |
278 | struct gb_audio_ctl_elem_value gbvalue; | |
279 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | |
280 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
281 | struct snd_soc_codec *codec = widget->codec; | |
796fad44 | 282 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6339d232 VA |
283 | |
284 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; | |
285 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
286 | ||
287 | if (data->vcount == 2) | |
288 | dev_warn(widget->dapm->dev, | |
289 | "GB: Control '%s' is stereo, which is not supported\n", | |
290 | kcontrol->id.name); | |
291 | ||
292 | ret = gb_audio_gb_get_control(gb->mgmt_connection, data->ctl_id, | |
293 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
294 | if (ret) { | |
295 | dev_err(codec->dev, "%d:Error in %s for %s\n", ret, __func__, | |
296 | kcontrol->id.name); | |
297 | return ret; | |
298 | } | |
299 | /* update ucontrol */ | |
300 | ucontrol->value.integer.value[0] = gbvalue.value.integer_value[0]; | |
301 | ||
302 | return ret; | |
303 | } | |
304 | ||
305 | static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, | |
306 | struct snd_ctl_elem_value *ucontrol) | |
307 | { | |
308 | int ret, wi, max, connect; | |
309 | unsigned int mask, val; | |
310 | struct gb_audio_ctl_elem_info *info; | |
311 | struct gbaudio_ctl_pvt *data; | |
312 | struct gb_audio_ctl_elem_value gbvalue; | |
313 | struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | |
314 | struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | |
315 | struct snd_soc_codec *codec = widget->codec; | |
796fad44 | 316 | struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); |
6339d232 VA |
317 | |
318 | data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; | |
319 | info = (struct gb_audio_ctl_elem_info *)data->info; | |
320 | ||
321 | if (data->vcount == 2) | |
322 | dev_warn(widget->dapm->dev, | |
323 | "GB: Control '%s' is stereo, which is not supported\n", | |
324 | kcontrol->id.name); | |
325 | ||
326 | max = info->value.integer.max; | |
327 | mask = (1 << fls(max)) - 1; | |
328 | val = (ucontrol->value.integer.value[0] & mask); | |
329 | connect = !!val; | |
330 | ||
331 | /* update ucontrol */ | |
332 | if (gbvalue.value.integer_value[0] != val) { | |
333 | for (wi = 0; wi < wlist->num_widgets; wi++) { | |
334 | widget = wlist->widgets[wi]; | |
335 | ||
336 | widget->value = val; | |
337 | widget->dapm->update = NULL; | |
338 | snd_soc_dapm_mixer_update_power(widget, kcontrol, | |
339 | connect); | |
340 | } | |
341 | gbvalue.value.integer_value[0] = | |
342 | ucontrol->value.integer.value[0]; | |
343 | ret = gb_audio_gb_set_control(gb->mgmt_connection, | |
344 | data->ctl_id, | |
345 | GB_AUDIO_INVALID_INDEX, &gbvalue); | |
346 | if (ret) { | |
347 | dev_err(codec->dev, | |
348 | "%d:Error in %s for %s\n", ret, __func__, | |
349 | kcontrol->id.name); | |
350 | } | |
351 | } | |
352 | ||
353 | return ret; | |
354 | } | |
355 | ||
356 | #define SOC_DAPM_MIXER_GB(xname, kcount, data) \ | |
357 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | |
358 | .count = kcount, .info = gbcodec_mixer_dapm_ctl_info, \ | |
359 | .get = gbcodec_mixer_dapm_ctl_get, .put = gbcodec_mixer_dapm_ctl_put, \ | |
360 | .private_value = (unsigned long)data} | |
361 | ||
362 | static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, | |
363 | struct snd_kcontrol *k, int event) | |
364 | { | |
365 | /* Ensure GB speaker is connected */ | |
366 | ||
367 | return 0; | |
368 | } | |
369 | ||
370 | static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, | |
371 | struct snd_kcontrol *k, int event) | |
372 | { | |
373 | /* Ensure GB module supports jack slot */ | |
374 | ||
375 | return 0; | |
376 | } | |
377 | ||
378 | static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, | |
379 | struct snd_kcontrol *k, int event) | |
380 | { | |
381 | /* Ensure GB module supports jack slot */ | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
386 | static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) | |
387 | { | |
388 | int ret = 0; | |
389 | ||
390 | switch (w->type) { | |
391 | case snd_soc_dapm_spk: | |
392 | case snd_soc_dapm_hp: | |
393 | case snd_soc_dapm_mic: | |
394 | case snd_soc_dapm_output: | |
395 | case snd_soc_dapm_input: | |
396 | if (w->ncontrols) | |
397 | ret = -EINVAL; | |
398 | break; | |
399 | case snd_soc_dapm_switch: | |
400 | case snd_soc_dapm_mux: | |
401 | if (w->ncontrols != 1) | |
402 | ret = -EINVAL; | |
403 | break; | |
404 | default: | |
405 | break; | |
406 | } | |
407 | ||
408 | return ret; | |
409 | } | |
410 | ||
411 | static int gbaudio_tplg_create_kcontrol(struct gbaudio_codec_info *gb, | |
412 | struct snd_kcontrol_new *kctl, | |
413 | struct gb_audio_control *ctl) | |
414 | { | |
415 | struct gbaudio_ctl_pvt *ctldata; | |
416 | ||
417 | switch (ctl->iface) { | |
418 | case SNDRV_CTL_ELEM_IFACE_MIXER: | |
419 | ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), | |
420 | GFP_KERNEL); | |
421 | if (!ctldata) | |
422 | return -ENOMEM; | |
423 | ctldata->ctl_id = ctl->id; | |
424 | ctldata->data_cport = ctl->data_cport; | |
425 | ctldata->access = ctl->access; | |
426 | ctldata->vcount = ctl->count_values; | |
427 | ctldata->info = &ctl->info; | |
428 | *kctl = (struct snd_kcontrol_new) | |
429 | SOC_MIXER_GB(ctl->name, ctl->count, ctldata); | |
430 | ctldata = NULL; | |
431 | break; | |
432 | default: | |
433 | return -EINVAL; | |
434 | } | |
435 | ||
436 | dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id); | |
437 | return 0; | |
438 | } | |
439 | ||
440 | static const char * const gbtexts[] = {"Stereo", "Left", "Right"}; | |
441 | ||
442 | static const SOC_ENUM_SINGLE_DECL( | |
443 | gbcodec_apb1_rx_enum, GBCODEC_APB1_MUX_REG, 0, gbtexts); | |
444 | ||
445 | static const SOC_ENUM_SINGLE_DECL( | |
446 | gbcodec_mic_enum, GBCODEC_APB1_MUX_REG, 4, gbtexts); | |
447 | ||
448 | static int gbaudio_tplg_create_enum_ctl(struct gbaudio_codec_info *gb, | |
449 | struct snd_kcontrol_new *kctl, | |
450 | struct gb_audio_control *ctl) | |
451 | { | |
452 | switch (ctl->id) { | |
453 | case 8: | |
454 | *kctl = (struct snd_kcontrol_new) | |
455 | SOC_DAPM_ENUM(ctl->name, gbcodec_apb1_rx_enum); | |
456 | break; | |
457 | case 9: | |
458 | *kctl = (struct snd_kcontrol_new) | |
459 | SOC_DAPM_ENUM(ctl->name, gbcodec_mic_enum); | |
460 | break; | |
461 | default: | |
462 | return -EINVAL; | |
463 | } | |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
468 | static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_codec_info *gb, | |
469 | struct snd_kcontrol_new *kctl, | |
470 | struct gb_audio_control *ctl) | |
471 | { | |
472 | struct gbaudio_ctl_pvt *ctldata; | |
473 | ||
474 | ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), | |
475 | GFP_KERNEL); | |
476 | if (!ctldata) | |
477 | return -ENOMEM; | |
478 | ctldata->ctl_id = ctl->id; | |
479 | ctldata->data_cport = ctl->data_cport; | |
480 | ctldata->access = ctl->access; | |
481 | ctldata->vcount = ctl->count_values; | |
482 | ctldata->info = &ctl->info; | |
483 | *kctl = (struct snd_kcontrol_new) | |
484 | SOC_DAPM_MIXER_GB(ctl->name, ctl->count, ctldata); | |
485 | ||
486 | return 0; | |
487 | } | |
488 | ||
489 | static int gbaudio_tplg_create_wcontrol(struct gbaudio_codec_info *gb, | |
490 | struct snd_kcontrol_new *kctl, | |
491 | struct gb_audio_control *ctl) | |
492 | { | |
493 | int ret; | |
494 | ||
495 | switch (ctl->iface) { | |
496 | case SNDRV_CTL_ELEM_IFACE_MIXER: | |
497 | switch (ctl->info.type) { | |
498 | case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: | |
499 | ret = gbaudio_tplg_create_enum_ctl(gb, kctl, ctl); | |
500 | break; | |
501 | default: | |
502 | ret = gbaudio_tplg_create_mixer_ctl(gb, kctl, ctl); | |
503 | break; | |
504 | } | |
505 | break; | |
506 | default: | |
507 | return -EINVAL; | |
508 | ||
509 | } | |
510 | ||
511 | dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name, | |
512 | ctl->id, ret); | |
513 | return ret; | |
514 | } | |
515 | ||
516 | static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, | |
517 | struct snd_kcontrol *kcontrol, int event) | |
518 | { | |
519 | int wid; | |
520 | int ret; | |
521 | struct snd_soc_codec *codec = w->codec; | |
796fad44 | 522 | struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); |
6339d232 VA |
523 | |
524 | dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); | |
525 | ||
526 | /* map name to widget id */ | |
527 | wid = gbaudio_map_widgetname(gbcodec, w->name); | |
528 | if (wid < 0) { | |
529 | dev_err(codec->dev, "Invalid widget name:%s\n", w->name); | |
530 | return -EINVAL; | |
531 | } | |
532 | ||
533 | switch (event) { | |
534 | case SND_SOC_DAPM_PRE_PMU: | |
535 | ret = gb_audio_gb_enable_widget(gbcodec->mgmt_connection, wid); | |
536 | break; | |
537 | case SND_SOC_DAPM_POST_PMD: | |
538 | ret = gb_audio_gb_disable_widget(gbcodec->mgmt_connection, wid); | |
539 | break; | |
540 | } | |
541 | if (ret) | |
542 | dev_err(codec->dev, "%d: widget, event:%d failed:%d\n", wid, | |
543 | event, ret); | |
544 | return ret; | |
545 | } | |
546 | ||
547 | static int gbaudio_tplg_create_widget(struct gbaudio_codec_info *gbcodec, | |
548 | struct snd_soc_dapm_widget *dw, | |
549 | struct gb_audio_widget *w) | |
550 | { | |
551 | int i, ret; | |
552 | struct snd_kcontrol_new *widget_kctls; | |
553 | struct gb_audio_control *curr; | |
554 | struct gbaudio_control *control, *_control; | |
555 | size_t size; | |
556 | ||
557 | ret = gbaudio_validate_kcontrol_count(w); | |
558 | if (ret) { | |
559 | dev_err(gbcodec->dev, "Inavlid kcontrol count=%d for %s\n", | |
560 | w->ncontrols, w->name); | |
561 | return ret; | |
562 | } | |
563 | ||
564 | /* allocate memory for kcontrol */ | |
565 | if (w->ncontrols) { | |
566 | size = sizeof(struct snd_kcontrol_new) * w->ncontrols; | |
567 | widget_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); | |
568 | if (!widget_kctls) | |
569 | return -ENOMEM; | |
570 | } | |
571 | ||
572 | /* create relevant kcontrols */ | |
573 | for (i = 0; i < w->ncontrols; i++) { | |
574 | curr = &w->ctl[i]; | |
575 | ret = gbaudio_tplg_create_wcontrol(gbcodec, &widget_kctls[i], | |
576 | curr); | |
577 | if (ret) { | |
578 | dev_err(gbcodec->dev, | |
579 | "%s:%d type widget_ctl not supported\n", | |
580 | curr->name, curr->iface); | |
581 | goto error; | |
582 | } | |
583 | control = devm_kzalloc(gbcodec->dev, | |
584 | sizeof(struct gbaudio_control), | |
585 | GFP_KERNEL); | |
586 | if (!control) { | |
587 | ret = -ENOMEM; | |
588 | goto error; | |
589 | } | |
590 | control->id = curr->id; | |
591 | control->name = curr->name; | |
592 | if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) | |
593 | control->texts = (const char * const *) | |
594 | curr->info.value.enumerated.names; | |
595 | list_add(&control->list, &gbcodec->widget_ctl_list); | |
596 | dev_dbg(gbcodec->dev, "%s: control of type %d created\n", | |
597 | widget_kctls[i].name, widget_kctls[i].iface); | |
598 | } | |
599 | ||
600 | switch (w->type) { | |
601 | case snd_soc_dapm_spk: | |
602 | *dw = (struct snd_soc_dapm_widget) | |
603 | SND_SOC_DAPM_SPK(w->name, gbcodec_event_spk); | |
604 | break; | |
605 | case snd_soc_dapm_hp: | |
606 | *dw = (struct snd_soc_dapm_widget) | |
607 | SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); | |
608 | break; | |
609 | case snd_soc_dapm_mic: | |
610 | *dw = (struct snd_soc_dapm_widget) | |
611 | SND_SOC_DAPM_MIC(w->name, gbcodec_event_int_mic); | |
612 | break; | |
613 | case snd_soc_dapm_output: | |
614 | *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_OUTPUT(w->name); | |
615 | break; | |
616 | case snd_soc_dapm_input: | |
617 | *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_INPUT(w->name); | |
618 | break; | |
619 | case snd_soc_dapm_switch: | |
620 | *dw = (struct snd_soc_dapm_widget) | |
621 | SND_SOC_DAPM_SWITCH_E(w->name, SND_SOC_NOPM, 0, 0, | |
622 | widget_kctls, gbaudio_widget_event, | |
623 | SND_SOC_DAPM_PRE_PMU | | |
624 | SND_SOC_DAPM_POST_PMD); | |
625 | break; | |
626 | case snd_soc_dapm_pga: | |
627 | *dw = (struct snd_soc_dapm_widget) | |
628 | SND_SOC_DAPM_PGA_E(w->name, SND_SOC_NOPM, 0, 0, NULL, 0, | |
629 | gbaudio_widget_event, | |
630 | SND_SOC_DAPM_PRE_PMU | | |
631 | SND_SOC_DAPM_POST_PMD); | |
632 | break; | |
633 | case snd_soc_dapm_mixer: | |
634 | *dw = (struct snd_soc_dapm_widget) | |
635 | SND_SOC_DAPM_MIXER_E(w->name, SND_SOC_NOPM, 0, 0, NULL, | |
636 | 0, gbaudio_widget_event, | |
637 | SND_SOC_DAPM_PRE_PMU | | |
638 | SND_SOC_DAPM_POST_PMD); | |
639 | break; | |
640 | case snd_soc_dapm_mux: | |
641 | *dw = (struct snd_soc_dapm_widget) | |
642 | SND_SOC_DAPM_MUX_E(w->name, SND_SOC_NOPM, 0, 0, | |
643 | widget_kctls, gbaudio_widget_event, | |
644 | SND_SOC_DAPM_PRE_PMU | | |
645 | SND_SOC_DAPM_POST_PMD); | |
646 | break; | |
647 | case snd_soc_dapm_aif_in: | |
648 | *dw = (struct snd_soc_dapm_widget) | |
649 | SND_SOC_DAPM_AIF_IN_E(w->name, w->sname, 0, | |
650 | SND_SOC_NOPM, | |
651 | 0, 0, gbaudio_widget_event, | |
652 | SND_SOC_DAPM_PRE_PMU | | |
653 | SND_SOC_DAPM_POST_PMD); | |
654 | break; | |
655 | case snd_soc_dapm_aif_out: | |
656 | *dw = (struct snd_soc_dapm_widget) | |
657 | SND_SOC_DAPM_AIF_OUT_E(w->name, w->sname, 0, | |
658 | SND_SOC_NOPM, | |
659 | 0, 0, gbaudio_widget_event, | |
660 | SND_SOC_DAPM_PRE_PMU | | |
661 | SND_SOC_DAPM_POST_PMD); | |
662 | break; | |
663 | default: | |
664 | ret = -EINVAL; | |
665 | goto error; | |
666 | } | |
667 | ||
668 | dev_dbg(gbcodec->dev, "%s: widget of type %d created\n", dw->name, | |
669 | dw->id); | |
670 | return 0; | |
671 | error: | |
672 | list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list, | |
673 | list) { | |
674 | list_del(&control->list); | |
675 | devm_kfree(gbcodec->dev, control); | |
676 | } | |
677 | return ret; | |
678 | } | |
679 | ||
680 | static int gbaudio_tplg_create_dai(struct gbaudio_codec_info *gbcodec, | |
681 | struct snd_soc_dai_driver *gb_dai, | |
682 | struct gb_audio_dai *dai) | |
683 | { | |
684 | /* | |
685 | * do not update name here, | |
686 | * append dev_id before assigning it here | |
687 | */ | |
688 | ||
689 | gb_dai->playback.stream_name = dai->playback.stream_name; | |
690 | gb_dai->playback.channels_min = dai->playback.chan_min; | |
691 | gb_dai->playback.channels_max = dai->playback.chan_max; | |
692 | gb_dai->playback.formats = dai->playback.formats; | |
693 | gb_dai->playback.rates = dai->playback.rates; | |
694 | gb_dai->playback.sig_bits = dai->playback.sig_bits; | |
695 | ||
696 | gb_dai->capture.stream_name = dai->capture.stream_name; | |
697 | gb_dai->capture.channels_min = dai->capture.chan_min; | |
698 | gb_dai->capture.channels_max = dai->capture.chan_max; | |
699 | gb_dai->capture.formats = dai->capture.formats; | |
700 | gb_dai->capture.rates = dai->capture.rates; | |
701 | gb_dai->capture.sig_bits = dai->capture.sig_bits; | |
702 | ||
703 | return 0; | |
704 | } | |
705 | ||
706 | static int gbaudio_tplg_process_kcontrols(struct gbaudio_codec_info *gbcodec, | |
707 | struct gb_audio_control *controls) | |
708 | { | |
709 | int i, ret; | |
710 | struct snd_kcontrol_new *dapm_kctls; | |
711 | struct gb_audio_control *curr; | |
712 | struct gbaudio_control *control, *_control; | |
713 | size_t size; | |
714 | ||
715 | size = sizeof(struct snd_kcontrol_new) * gbcodec->num_kcontrols; | |
716 | dapm_kctls = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); | |
717 | if (!dapm_kctls) | |
718 | return -ENOMEM; | |
719 | ||
720 | curr = controls; | |
721 | for (i = 0; i < gbcodec->num_kcontrols; i++) { | |
722 | ret = gbaudio_tplg_create_kcontrol(gbcodec, &dapm_kctls[i], | |
723 | curr); | |
724 | if (ret) { | |
725 | dev_err(gbcodec->dev, "%s:%d type not supported\n", | |
726 | curr->name, curr->iface); | |
727 | goto error; | |
728 | } | |
729 | control = devm_kzalloc(gbcodec->dev, sizeof(struct | |
730 | gbaudio_control), | |
731 | GFP_KERNEL); | |
732 | if (!control) { | |
733 | ret = -ENOMEM; | |
734 | goto error; | |
735 | } | |
736 | control->id = curr->id; | |
737 | control->name = curr->name; | |
738 | if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) | |
739 | control->texts = (const char * const *) | |
740 | curr->info.value.enumerated.names; | |
741 | list_add(&control->list, &gbcodec->codec_ctl_list); | |
742 | dev_dbg(gbcodec->dev, "%d:%s created of type %d\n", curr->id, | |
743 | curr->name, curr->info.type); | |
744 | curr++; | |
745 | } | |
746 | gbcodec->kctls = dapm_kctls; | |
747 | ||
748 | return 0; | |
749 | error: | |
750 | list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, | |
751 | list) { | |
752 | list_del(&control->list); | |
753 | devm_kfree(gbcodec->dev, control); | |
754 | } | |
755 | devm_kfree(gbcodec->dev, dapm_kctls); | |
756 | return ret; | |
757 | } | |
758 | ||
759 | static int gbaudio_tplg_process_widgets(struct gbaudio_codec_info *gbcodec, | |
760 | struct gb_audio_widget *widgets) | |
761 | { | |
762 | int i, ret, ncontrols; | |
763 | struct snd_soc_dapm_widget *dapm_widgets; | |
764 | struct gb_audio_widget *curr; | |
765 | struct gbaudio_widget *widget, *_widget; | |
766 | size_t size; | |
767 | ||
768 | size = sizeof(struct snd_soc_dapm_widget) * gbcodec->num_dapm_widgets; | |
769 | dapm_widgets = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); | |
770 | if (!dapm_widgets) | |
771 | return -ENOMEM; | |
772 | ||
773 | curr = widgets; | |
774 | for (i = 0; i < gbcodec->num_dapm_widgets; i++) { | |
775 | ret = gbaudio_tplg_create_widget(gbcodec, &dapm_widgets[i], | |
776 | curr); | |
777 | if (ret) { | |
778 | dev_err(gbcodec->dev, "%s:%d type not supported\n", | |
779 | curr->name, curr->type); | |
780 | goto error; | |
781 | } | |
782 | widget = devm_kzalloc(gbcodec->dev, sizeof(struct | |
783 | gbaudio_widget), | |
784 | GFP_KERNEL); | |
785 | if (!widget) { | |
786 | ret = -ENOMEM; | |
787 | goto error; | |
788 | } | |
789 | widget->id = curr->id; | |
790 | widget->name = curr->name; | |
791 | list_add(&widget->list, &gbcodec->widget_list); | |
792 | ncontrols = curr->ncontrols; | |
793 | curr++; | |
794 | curr += ncontrols * sizeof(struct gb_audio_control); | |
795 | } | |
796 | gbcodec->widgets = dapm_widgets; | |
797 | ||
798 | return 0; | |
799 | ||
800 | error: | |
801 | list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, | |
802 | list) { | |
803 | list_del(&widget->list); | |
804 | devm_kfree(gbcodec->dev, widget); | |
805 | } | |
806 | devm_kfree(gbcodec->dev, dapm_widgets); | |
807 | return ret; | |
808 | } | |
809 | ||
810 | static int gbaudio_tplg_process_dais(struct gbaudio_codec_info *gbcodec, | |
811 | struct gb_audio_dai *dais) | |
812 | { | |
813 | int i, ret; | |
814 | struct snd_soc_dai_driver *gb_dais; | |
815 | struct gb_audio_dai *curr; | |
6339d232 VA |
816 | size_t size; |
817 | char dai_name[NAME_SIZE]; | |
796fad44 | 818 | struct gbaudio_dai *dai; |
6339d232 VA |
819 | |
820 | size = sizeof(struct snd_soc_dai_driver) * gbcodec->num_dais; | |
821 | gb_dais = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); | |
822 | if (!gb_dais) | |
823 | return -ENOMEM; | |
824 | ||
825 | curr = dais; | |
826 | for (i = 0; i < gbcodec->num_dais; i++) { | |
827 | ret = gbaudio_tplg_create_dai(gbcodec, &gb_dais[i], curr); | |
828 | if (ret) { | |
829 | dev_err(gbcodec->dev, "%s failed to create\n", | |
830 | curr->name); | |
831 | goto error; | |
832 | } | |
833 | /* append dev_id to dai_name */ | |
834 | snprintf(dai_name, NAME_SIZE, "%s.%d", curr->name, | |
835 | gbcodec->dev_id); | |
796fad44 | 836 | dai = gbaudio_find_dai(gbcodec, curr->data_cport, NULL); |
6339d232 VA |
837 | if (!dai) |
838 | goto error; | |
796fad44 | 839 | strlcpy(dai->name, dai_name, NAME_SIZE); |
538ecb5a | 840 | dev_dbg(gbcodec->dev, "%s:DAI added\n", dai->name); |
6339d232 VA |
841 | gb_dais[i].name = dai->name; |
842 | curr++; | |
843 | } | |
844 | gbcodec->dais = gb_dais; | |
845 | ||
846 | return 0; | |
847 | ||
848 | error: | |
6339d232 VA |
849 | devm_kfree(gbcodec->dev, gb_dais); |
850 | return ret; | |
851 | } | |
852 | ||
853 | static int gbaudio_tplg_process_routes(struct gbaudio_codec_info *gbcodec, | |
854 | struct gb_audio_route *routes) | |
855 | { | |
856 | int i, ret; | |
857 | struct snd_soc_dapm_route *dapm_routes; | |
858 | struct gb_audio_route *curr; | |
859 | size_t size; | |
860 | ||
861 | size = sizeof(struct snd_soc_dapm_route) * gbcodec->num_dapm_routes; | |
862 | dapm_routes = devm_kzalloc(gbcodec->dev, size, GFP_KERNEL); | |
863 | if (!dapm_routes) | |
864 | return -ENOMEM; | |
865 | ||
866 | ||
867 | gbcodec->routes = dapm_routes; | |
868 | curr = routes; | |
869 | ||
870 | for (i = 0; i < gbcodec->num_dapm_routes; i++) { | |
871 | dapm_routes->sink = | |
872 | gbaudio_map_widgetid(gbcodec, curr->destination_id); | |
873 | if (!dapm_routes->sink) { | |
874 | dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid sink\n", | |
875 | curr->source_id, curr->destination_id, | |
876 | curr->control_id, curr->index); | |
877 | ret = -EINVAL; | |
878 | goto error; | |
879 | } | |
880 | dapm_routes->source = | |
881 | gbaudio_map_widgetid(gbcodec, curr->source_id); | |
882 | if (!dapm_routes->source) { | |
883 | dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid source\n", | |
884 | curr->source_id, curr->destination_id, | |
885 | curr->control_id, curr->index); | |
886 | ret = -EINVAL; | |
887 | goto error; | |
888 | } | |
889 | dapm_routes->control = | |
890 | gbaudio_map_controlid(gbcodec, | |
891 | curr->control_id, | |
892 | curr->index); | |
893 | if ((curr->control_id != GBAUDIO_INVALID_ID) && | |
894 | !dapm_routes->control) { | |
895 | dev_err(gbcodec->dev, "%d:%d:%d:%d - Invalid control\n", | |
896 | curr->source_id, curr->destination_id, | |
897 | curr->control_id, curr->index); | |
898 | ret = -EINVAL; | |
899 | goto error; | |
900 | } | |
901 | dev_dbg(gbcodec->dev, "Route {%s, %s, %s}\n", dapm_routes->sink, | |
902 | (dapm_routes->control) ? dapm_routes->control:"NULL", | |
903 | dapm_routes->source); | |
904 | dapm_routes++; | |
905 | curr++; | |
906 | } | |
907 | ||
908 | return 0; | |
909 | ||
910 | error: | |
911 | devm_kfree(gbcodec->dev, dapm_routes); | |
912 | return ret; | |
913 | } | |
914 | ||
915 | static int gbaudio_tplg_process_header(struct gbaudio_codec_info *gbcodec, | |
916 | struct gb_audio_topology *tplg_data) | |
917 | { | |
918 | /* fetch no. of kcontrols, widgets & routes */ | |
919 | gbcodec->num_dais = tplg_data->num_dais; | |
920 | gbcodec->num_kcontrols = tplg_data->num_controls; | |
921 | gbcodec->num_dapm_widgets = tplg_data->num_widgets; | |
922 | gbcodec->num_dapm_routes = tplg_data->num_routes; | |
923 | ||
924 | /* update block offset */ | |
925 | gbcodec->dai_offset = (unsigned long)&tplg_data->data; | |
926 | gbcodec->control_offset = gbcodec->dai_offset + tplg_data->size_dais; | |
927 | gbcodec->widget_offset = gbcodec->control_offset + | |
928 | tplg_data->size_controls; | |
929 | gbcodec->route_offset = gbcodec->widget_offset + | |
930 | tplg_data->size_widgets; | |
931 | ||
932 | dev_dbg(gbcodec->dev, "DAI offset is 0x%lx\n", gbcodec->dai_offset); | |
933 | dev_dbg(gbcodec->dev, "control offset is %lx\n", | |
934 | gbcodec->control_offset); | |
935 | dev_dbg(gbcodec->dev, "widget offset is %lx\n", gbcodec->widget_offset); | |
936 | dev_dbg(gbcodec->dev, "route offset is %lx\n", gbcodec->route_offset); | |
937 | ||
938 | return 0; | |
939 | } | |
940 | ||
6339d232 VA |
941 | int gbaudio_tplg_parse_data(struct gbaudio_codec_info *gbcodec, |
942 | struct gb_audio_topology *tplg_data) | |
943 | { | |
944 | int ret; | |
945 | struct gb_audio_dai *dais; | |
946 | struct gb_audio_control *controls; | |
947 | struct gb_audio_widget *widgets; | |
948 | struct gb_audio_route *routes; | |
949 | ||
950 | if (!tplg_data) | |
951 | return -EINVAL; | |
952 | ||
953 | ret = gbaudio_tplg_process_header(gbcodec, tplg_data); | |
954 | if (ret) { | |
955 | dev_err(gbcodec->dev, "%d: Error in parsing topology header\n", | |
956 | ret); | |
957 | return ret; | |
958 | } | |
959 | ||
960 | /* process control */ | |
961 | controls = (struct gb_audio_control *)gbcodec->control_offset; | |
962 | ret = gbaudio_tplg_process_kcontrols(gbcodec, controls); | |
963 | if (ret) { | |
964 | dev_err(gbcodec->dev, | |
965 | "%d: Error in parsing controls data\n", ret); | |
966 | return ret; | |
967 | } | |
538ecb5a | 968 | dev_dbg(gbcodec->dev, "Control parsing finished\n"); |
6339d232 VA |
969 | |
970 | /* process DAI */ | |
971 | dais = (struct gb_audio_dai *)gbcodec->dai_offset; | |
972 | ret = gbaudio_tplg_process_dais(gbcodec, dais); | |
973 | if (ret) { | |
974 | dev_err(gbcodec->dev, | |
975 | "%d: Error in parsing DAIs data\n", ret); | |
976 | return ret; | |
977 | } | |
538ecb5a | 978 | dev_dbg(gbcodec->dev, "DAI parsing finished\n"); |
6339d232 VA |
979 | |
980 | /* process widgets */ | |
981 | widgets = (struct gb_audio_widget *)gbcodec->widget_offset; | |
982 | ret = gbaudio_tplg_process_widgets(gbcodec, widgets); | |
983 | if (ret) { | |
984 | dev_err(gbcodec->dev, | |
985 | "%d: Error in parsing widgets data\n", ret); | |
986 | return ret; | |
987 | } | |
538ecb5a | 988 | dev_dbg(gbcodec->dev, "Widget parsing finished\n"); |
6339d232 VA |
989 | |
990 | /* process route */ | |
991 | routes = (struct gb_audio_route *)gbcodec->route_offset; | |
992 | ret = gbaudio_tplg_process_routes(gbcodec, routes); | |
993 | if (ret) { | |
994 | dev_err(gbcodec->dev, | |
995 | "%d: Error in parsing routes data\n", ret); | |
996 | return ret; | |
997 | } | |
538ecb5a | 998 | dev_dbg(gbcodec->dev, "Route parsing finished\n"); |
6339d232 VA |
999 | |
1000 | return ret; | |
1001 | } | |
1002 | ||
1003 | void gbaudio_tplg_release(struct gbaudio_codec_info *gbcodec) | |
1004 | { | |
6339d232 VA |
1005 | struct gbaudio_control *control, *_control; |
1006 | struct gbaudio_widget *widget, *_widget; | |
1007 | ||
1008 | if (!gbcodec->topology) | |
1009 | return; | |
1010 | ||
1011 | /* release kcontrols */ | |
1012 | list_for_each_entry_safe(control, _control, &gbcodec->codec_ctl_list, | |
1013 | list) { | |
1014 | list_del(&control->list); | |
1015 | devm_kfree(gbcodec->dev, control); | |
1016 | } | |
1017 | if (gbcodec->kctls) | |
1018 | devm_kfree(gbcodec->dev, gbcodec->kctls); | |
1019 | ||
1020 | /* release widget controls */ | |
1021 | list_for_each_entry_safe(control, _control, &gbcodec->widget_ctl_list, | |
1022 | list) { | |
1023 | list_del(&control->list); | |
1024 | devm_kfree(gbcodec->dev, control); | |
1025 | } | |
1026 | ||
1027 | /* release widgets */ | |
1028 | list_for_each_entry_safe(widget, _widget, &gbcodec->widget_list, | |
1029 | list) { | |
1030 | list_del(&widget->list); | |
1031 | devm_kfree(gbcodec->dev, widget); | |
1032 | } | |
1033 | if (gbcodec->widgets) | |
1034 | devm_kfree(gbcodec->dev, gbcodec->widgets); | |
1035 | ||
1036 | /* release routes */ | |
1037 | if (gbcodec->routes) | |
1038 | devm_kfree(gbcodec->dev, gbcodec->routes); | |
6339d232 | 1039 | } |