audio_helper.c (4878B)
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 16static 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 60int 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 82static 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 91static 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 113int 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 154static 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 190int 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}