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