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 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 143static 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 153 if (prefix) 154 snprintf(id.name, sizeof(id.name), "%s %s", prefix, 155 control->name); 156 else 157 strscpy(id.name, control->name, sizeof(id.name)); 158 id.numid = 0; 159 id.iface = control->iface; 160 id.device = control->device; 161 id.subdevice = control->subdevice; 162 id.index = control->index; 163 err = snd_ctl_remove_id(card, &id); 164 if (err < 0) 165 dev_err(dev, "%d: Failed to remove %s\n", err, 166 control->name); 167 } 168 return 0; 169} 170 171int gbaudio_remove_component_controls(struct snd_soc_component *component, 172 const struct snd_kcontrol_new *controls, 173 unsigned int num_controls) 174{ 175 struct snd_card *card = component->card->snd_card; 176 177 return gbaudio_remove_controls(card, component->dev, controls, 178 num_controls, component->name_prefix); 179} 180