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
gbaudio_dapm_link_dai_widget(struct snd_soc_dapm_widget * dai_w,struct snd_soc_card * card)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
gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card * card,struct snd_soc_dapm_context * dapm)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
gbaudio_dapm_free_path(struct snd_soc_dapm_path * path)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
gbaudio_dapm_free_widget(struct snd_soc_dapm_widget * w)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
gbaudio_dapm_free_controls(struct snd_soc_dapm_context * dapm,const struct snd_soc_dapm_widget * widget,int num)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
gbaudio_remove_controls(struct snd_card * card,struct device * dev,const struct snd_kcontrol_new * controls,int num_controls,const char * prefix)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
gbaudio_remove_component_controls(struct snd_soc_component * component,const struct snd_kcontrol_new * controls,unsigned int num_controls)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 int err;
196
197 down_write(&card->controls_rwsem);
198 err = gbaudio_remove_controls(card, component->dev, controls,
199 num_controls, component->name_prefix);
200 up_write(&card->controls_rwsem);
201 return err;
202 }
203