Skip to content

Commit 510e340

Browse files
agarwalvaibhavgregkh
authored andcommitted
staging: greybus: audio: Add helper APIs for dynamic audio modules
Greybus Codec driver allows modules to be dynamically added and removed, which further requires updating the DAPM configurations as well. With current snd_soc architecture, dynamic audio modules is not yet supported. This patch provides helper APIs to update DAPM configurations in response to modules which are dynamically added or removed. The source is primarily based on snd_dapm.c Signed-off-by: Vaibhav Agarwal <[email protected]> Reviewed-by: Dan Carpenter <[email protected]> Link: https://lore.kernel.org/r/35e1baaae10a3f2162e71be4c2f75a701584f0e6.1594290158.git.vaibhav.sr@gmail.com Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent c4bb7dc commit 510e340

File tree

4 files changed

+223
-5
lines changed

4 files changed

+223
-5
lines changed

drivers/staging/greybus/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ obj-$(CONFIG_GREYBUS_VIBRATOR) += gb-vibrator.o
2828

2929
# Greybus Audio is a bunch of modules
3030
gb-audio-module-y := audio_module.o audio_topology.o
31-
gb-audio-codec-y := audio_codec.o
31+
gb-audio-codec-y := audio_codec.o audio_helper.o
3232
gb-audio-gb-y := audio_gb.o
3333
gb-audio-apbridgea-y := audio_apbridgea.o
3434
gb-audio-manager-y := audio_manager.o audio_manager_module.o

drivers/staging/greybus/audio_codec.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "audio_codec.h"
1515
#include "audio_apbridgea.h"
1616
#include "audio_manager.h"
17+
#include "audio_helper.h"
1718

1819
static struct gbaudio_codec_info *gbcodec;
1920

@@ -865,7 +866,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module)
865866

866867
/* card already instantiated, create widgets here only */
867868
if (comp->card->instantiated) {
868-
snd_soc_dapm_link_component_dai_widgets(comp->card,
869+
gbaudio_dapm_link_component_dai_widgets(comp->card,
869870
&comp->dapm);
870871
#ifdef CONFIG_SND_JACK
871872
/*
@@ -999,13 +1000,16 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module)
9991000
if (module->controls) {
10001001
dev_dbg(comp->dev, "Removing %d controls\n",
10011002
module->num_controls);
1002-
snd_soc_remove_codec_controls(comp, module->controls,
1003-
module->num_controls);
1003+
/* release control semaphore */
1004+
up_write(&card->controls_rwsem);
1005+
gbaudio_remove_component_controls(comp, module->controls,
1006+
module->num_controls);
1007+
down_write(&card->controls_rwsem);
10041008
}
10051009
if (module->dapm_widgets) {
10061010
dev_dbg(comp->dev, "Removing %d widgets\n",
10071011
module->num_dapm_widgets);
1008-
snd_soc_dapm_free_controls(&comp->dapm, module->dapm_widgets,
1012+
gbaudio_dapm_free_controls(&comp->dapm, module->dapm_widgets,
10091013
module->num_dapm_widgets);
10101014
}
10111015

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
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+
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+
#ifdef CONFIG_DEBUG_FS
119+
struct dentry *parent = dapm->debugfs_dapm;
120+
struct dentry *debugfs_w = NULL;
121+
#endif
122+
123+
mutex_lock(&dapm->card->dapm_mutex);
124+
for (i = 0; i < num; i++) {
125+
/* below logic can be optimized to identify widget pointer */
126+
list_for_each_entry_safe(w, next_w, &dapm->card->widgets,
127+
list) {
128+
if (w->dapm != dapm)
129+
continue;
130+
if (!strcmp(w->name, widget->name))
131+
break;
132+
w = NULL;
133+
}
134+
if (!w) {
135+
dev_err(dapm->dev, "%s: widget not found\n",
136+
widget->name);
137+
return -EINVAL;
138+
}
139+
widget++;
140+
#ifdef CONFIG_DEBUG_FS
141+
if (!parent)
142+
debugfs_w = debugfs_lookup(w->name, parent);
143+
debugfs_remove(debugfs_w);
144+
debugfs_w = NULL;
145+
#endif
146+
gbaudio_dapm_free_widget(w);
147+
}
148+
mutex_unlock(&dapm->card->dapm_mutex);
149+
return 0;
150+
}
151+
152+
static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
153+
const struct snd_kcontrol_new *controls,
154+
int num_controls, const char *prefix)
155+
{
156+
int i, err;
157+
158+
for (i = 0; i < num_controls; i++) {
159+
const struct snd_kcontrol_new *control = &controls[i];
160+
struct snd_ctl_elem_id id;
161+
struct snd_kcontrol *kctl;
162+
163+
if (prefix)
164+
snprintf(id.name, sizeof(id.name), "%s %s", prefix,
165+
control->name);
166+
else
167+
strlcpy(id.name, control->name, sizeof(id.name));
168+
id.numid = 0;
169+
id.iface = control->iface;
170+
id.device = control->device;
171+
id.subdevice = control->subdevice;
172+
id.index = control->index;
173+
kctl = snd_ctl_find_id(card, &id);
174+
if (!kctl) {
175+
dev_err(dev, "%d: Failed to find %s\n", err,
176+
control->name);
177+
continue;
178+
}
179+
err = snd_ctl_remove(card, kctl);
180+
if (err < 0) {
181+
dev_err(dev, "%d: Failed to remove %s\n", err,
182+
control->name);
183+
continue;
184+
}
185+
}
186+
return 0;
187+
}
188+
189+
int gbaudio_remove_component_controls(struct snd_soc_component *component,
190+
const struct snd_kcontrol_new *controls,
191+
unsigned int num_controls)
192+
{
193+
struct snd_card *card = component->card->snd_card;
194+
195+
return gbaudio_remove_controls(card, component->dev, controls,
196+
num_controls, component->name_prefix);
197+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Greybus Audio Sound SoC helper APIs
4+
*/
5+
6+
#ifndef __LINUX_GBAUDIO_HELPER_H
7+
#define __LINUX_GBAUDIO_HELPER_H
8+
9+
int gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card *card,
10+
struct snd_soc_dapm_context *dapm);
11+
int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm,
12+
const struct snd_soc_dapm_widget *widget,
13+
int num);
14+
int gbaudio_remove_component_controls(struct snd_soc_component *component,
15+
const struct snd_kcontrol_new *controls,
16+
unsigned int num_controls);
17+
#endif

0 commit comments

Comments
 (0)