GNU Linux-libre 5.10.153-gnu1
[releases.git] / drivers / staging / greybus / audio_helper.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Greybus Audio Sound SoC helper APIs
4  */
5
6 #include <sound/core.h>
7 #include <sound/soc.h>
8 #include <sound/soc-dapm.h>
9 #include "audio_helper.h"
10
11 #define gbaudio_dapm_for_each_direction(dir) \
12         for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
13                 (dir)++)
14
15 static void gbaudio_dapm_link_dai_widget(struct snd_soc_dapm_widget *dai_w,
16                                          struct snd_soc_card *card)
17 {
18         struct snd_soc_dapm_widget *w;
19         struct snd_soc_dapm_widget *src, *sink;
20         struct snd_soc_dai *dai = dai_w->priv;
21
22         /* ...find all widgets with the same stream and link them */
23         list_for_each_entry(w, &card->widgets, list) {
24                 if (w->dapm != dai_w->dapm)
25                         continue;
26
27                 switch (w->id) {
28                 case snd_soc_dapm_dai_in:
29                 case snd_soc_dapm_dai_out:
30                         continue;
31                 default:
32                         break;
33                 }
34
35                 if (!w->sname || !strstr(w->sname, dai_w->sname))
36                         continue;
37
38                 /*
39                  * check if widget is already linked,
40                  * if (w->linked)
41                  *      return;
42                  */
43
44                 if (dai_w->id == snd_soc_dapm_dai_in) {
45                         src = dai_w;
46                         sink = w;
47                 } else {
48                         src = w;
49                         sink = dai_w;
50                 }
51                 dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);
52                 /* Add the DAPM path and set widget's linked status
53                  * snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);
54                  * w->linked = 1;
55                  */
56         }
57 }
58
59 int gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card *card,
60                                             struct snd_soc_dapm_context *dapm)
61 {
62         struct snd_soc_dapm_widget *dai_w;
63
64         /* For each DAI widget... */
65         list_for_each_entry(dai_w, &card->widgets, list) {
66                 if (dai_w->dapm != dapm)
67                         continue;
68                 switch (dai_w->id) {
69                 case snd_soc_dapm_dai_in:
70                 case snd_soc_dapm_dai_out:
71                         break;
72                 default:
73                         continue;
74                 }
75                 gbaudio_dapm_link_dai_widget(dai_w, card);
76         }
77
78         return 0;
79 }
80
81 static void gbaudio_dapm_free_path(struct snd_soc_dapm_path *path)
82 {
83         list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]);
84         list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]);
85         list_del(&path->list_kcontrol);
86         list_del(&path->list);
87         kfree(path);
88 }
89
90 static void gbaudio_dapm_free_widget(struct snd_soc_dapm_widget *w)
91 {
92         struct snd_soc_dapm_path *p, *next_p;
93         enum snd_soc_dapm_direction dir;
94
95         list_del(&w->list);
96         /*
97          * remove source and sink paths associated to this widget.
98          * While removing the path, remove reference to it from both
99          * source and sink widgets so that path is removed only once.
100          */
101         gbaudio_dapm_for_each_direction(dir) {
102                 snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)
103                         gbaudio_dapm_free_path(p);
104         }
105
106         kfree(w->kcontrols);
107         kfree_const(w->name);
108         kfree_const(w->sname);
109         kfree(w);
110 }
111
112 int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm,
113                                const struct snd_soc_dapm_widget *widget,
114                                int num)
115 {
116         int i;
117         struct snd_soc_dapm_widget *w, *next_w;
118
119         mutex_lock(&dapm->card->dapm_mutex);
120         for (i = 0; i < num; i++) {
121                 /* below logic can be optimized to identify widget pointer */
122                 list_for_each_entry_safe(w, next_w, &dapm->card->widgets,
123                                          list) {
124                         if (w->dapm != dapm)
125                                 continue;
126                         if (!strcmp(w->name, widget->name))
127                                 break;
128                         w = NULL;
129                 }
130                 if (!w) {
131                         dev_err(dapm->dev, "%s: widget not found\n",
132                                 widget->name);
133                         widget++;
134                         continue;
135                 }
136                 widget++;
137                 gbaudio_dapm_free_widget(w);
138         }
139         mutex_unlock(&dapm->card->dapm_mutex);
140         return 0;
141 }
142
143 static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
144                                    const struct snd_kcontrol_new *controls,
145                                    int num_controls, const char *prefix)
146 {
147         int i, err;
148
149         for (i = 0; i < num_controls; i++) {
150                 const struct snd_kcontrol_new *control = &controls[i];
151                 struct snd_ctl_elem_id id;
152                 struct snd_kcontrol *kctl;
153
154                 if (prefix)
155                         snprintf(id.name, sizeof(id.name), "%s %s", prefix,
156                                  control->name);
157                 else
158                         strlcpy(id.name, control->name, sizeof(id.name));
159                 id.numid = 0;
160                 id.iface = control->iface;
161                 id.device = control->device;
162                 id.subdevice = control->subdevice;
163                 id.index = control->index;
164                 kctl = snd_ctl_find_id(card, &id);
165                 if (!kctl) {
166                         dev_err(dev, "Failed to find %s\n", control->name);
167                         continue;
168                 }
169                 err = snd_ctl_remove(card, kctl);
170                 if (err < 0) {
171                         dev_err(dev, "%d: Failed to remove %s\n", err,
172                                 control->name);
173                         continue;
174                 }
175         }
176         return 0;
177 }
178
179 int gbaudio_remove_component_controls(struct snd_soc_component *component,
180                                       const struct snd_kcontrol_new *controls,
181                                       unsigned int num_controls)
182 {
183         struct snd_card *card = component->card->snd_card;
184         int err;
185
186         down_write(&card->controls_rwsem);
187         err = gbaudio_remove_controls(card, component->dev, controls,
188                                       num_controls, component->name_prefix);
189         up_write(&card->controls_rwsem);
190         return err;
191 }