File Index
Files and directories
Sign in
FILE

audio_helper.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/greybus/audio_helper.c 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
 15static 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
 59int 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
 81static 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
 90static 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
112int 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	struct snd_soc_card *card = snd_soc_dapm_to_card(dapm);
119
120	mutex_lock(&card->dapm_mutex);
121	for (i = 0; i < num; i++) {
122		/* below logic can be optimized to identify widget pointer */
123		w = NULL;
124		list_for_each_entry(tmp_w, &card->widgets, list) {
125			if (tmp_w->dapm == dapm &&
126			    !strcmp(tmp_w->name, widget->name)) {
127				w = tmp_w;
128				break;
129			}
130		}
131		if (!w) {
132			dev_err(card->dev, "%s: widget not found\n",
133				widget->name);
134			widget++;
135			continue;
136		}
137		widget++;
138		gbaudio_dapm_free_widget(w);
139	}
140	mutex_unlock(&card->dapm_mutex);
141	return 0;
142}
143
144static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
145				   const struct snd_kcontrol_new *controls,
146				   int num_controls, const char *prefix)
147{
148	int i, err;
149
150	for (i = 0; i < num_controls; i++) {
151		const struct snd_kcontrol_new *control = &controls[i];
152		struct snd_ctl_elem_id id;
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		err = snd_ctl_remove_id(card, &id);
165		if (err < 0)
166			dev_err(dev, "%d: Failed to remove %s\n", err,
167				control->name);
168	}
169	return 0;
170}
171
172int gbaudio_remove_component_controls(struct snd_soc_component *component,
173				      const struct snd_kcontrol_new *controls,
174				      unsigned int num_controls)
175{
176	struct snd_card *card = component->card->snd_card;
177
178	return gbaudio_remove_controls(card, component->dev, controls,
179				       num_controls, component->name_prefix);
180}
~/ Read-only file preview.