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 
gbaudio_dapm_link_dai_widget(struct snd_soc_dapm_widget * dai_w,struct snd_soc_card * card)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 
gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card * card,struct snd_soc_dapm_context * dapm)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 
gbaudio_dapm_free_path(struct snd_soc_dapm_path * path)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 
gbaudio_dapm_free_widget(struct snd_soc_dapm_widget * w)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 
gbaudio_dapm_free_controls(struct snd_soc_dapm_context * dapm,const struct snd_soc_dapm_widget * widget,int num)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, *tmp_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 		w = NULL;
123 		list_for_each_entry(tmp_w, &dapm->card->widgets, list) {
124 			if (tmp_w->dapm == dapm &&
125 			    !strcmp(tmp_w->name, widget->name)) {
126 				w = tmp_w;
127 				break;
128 			}
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 
gbaudio_remove_controls(struct snd_card * card,struct device * dev,const struct snd_kcontrol_new * controls,int num_controls,const char * prefix)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 			strscpy(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 
gbaudio_remove_component_controls(struct snd_soc_component * component,const struct snd_kcontrol_new * controls,unsigned int num_controls)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 }
192