Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Greybus Audio Sound SoC helper APIs
0004  */
0005 
0006 #include <linux/debugfs.h>
0007 #include <sound/core.h>
0008 #include <sound/soc.h>
0009 #include <sound/soc-dapm.h>
0010 #include "audio_helper.h"
0011 
0012 #define gbaudio_dapm_for_each_direction(dir) \
0013     for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
0014         (dir)++)
0015 
0016 static void gbaudio_dapm_link_dai_widget(struct snd_soc_dapm_widget *dai_w,
0017                      struct snd_soc_card *card)
0018 {
0019     struct snd_soc_dapm_widget *w;
0020     struct snd_soc_dapm_widget *src, *sink;
0021     struct snd_soc_dai *dai = dai_w->priv;
0022 
0023     /* ...find all widgets with the same stream and link them */
0024     list_for_each_entry(w, &card->widgets, list) {
0025         if (w->dapm != dai_w->dapm)
0026             continue;
0027 
0028         switch (w->id) {
0029         case snd_soc_dapm_dai_in:
0030         case snd_soc_dapm_dai_out:
0031             continue;
0032         default:
0033             break;
0034         }
0035 
0036         if (!w->sname || !strstr(w->sname, dai_w->sname))
0037             continue;
0038 
0039         /*
0040          * check if widget is already linked,
0041          * if (w->linked)
0042          *  return;
0043          */
0044 
0045         if (dai_w->id == snd_soc_dapm_dai_in) {
0046             src = dai_w;
0047             sink = w;
0048         } else {
0049             src = w;
0050             sink = dai_w;
0051         }
0052         dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);
0053         /* Add the DAPM path and set widget's linked status
0054          * snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);
0055          * w->linked = 1;
0056          */
0057     }
0058 }
0059 
0060 int gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card *card,
0061                         struct snd_soc_dapm_context *dapm)
0062 {
0063     struct snd_soc_dapm_widget *dai_w;
0064 
0065     /* For each DAI widget... */
0066     list_for_each_entry(dai_w, &card->widgets, list) {
0067         if (dai_w->dapm != dapm)
0068             continue;
0069         switch (dai_w->id) {
0070         case snd_soc_dapm_dai_in:
0071         case snd_soc_dapm_dai_out:
0072             break;
0073         default:
0074             continue;
0075         }
0076         gbaudio_dapm_link_dai_widget(dai_w, card);
0077     }
0078 
0079     return 0;
0080 }
0081 
0082 static void gbaudio_dapm_free_path(struct snd_soc_dapm_path *path)
0083 {
0084     list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]);
0085     list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]);
0086     list_del(&path->list_kcontrol);
0087     list_del(&path->list);
0088     kfree(path);
0089 }
0090 
0091 static void gbaudio_dapm_free_widget(struct snd_soc_dapm_widget *w)
0092 {
0093     struct snd_soc_dapm_path *p, *next_p;
0094     enum snd_soc_dapm_direction dir;
0095 
0096     list_del(&w->list);
0097     /*
0098      * remove source and sink paths associated to this widget.
0099      * While removing the path, remove reference to it from both
0100      * source and sink widgets so that path is removed only once.
0101      */
0102     gbaudio_dapm_for_each_direction(dir) {
0103         snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p)
0104             gbaudio_dapm_free_path(p);
0105     }
0106 
0107     kfree(w->kcontrols);
0108     kfree_const(w->name);
0109     kfree_const(w->sname);
0110     kfree(w);
0111 }
0112 
0113 int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm,
0114                    const struct snd_soc_dapm_widget *widget,
0115                    int num)
0116 {
0117     int i;
0118     struct snd_soc_dapm_widget *w, *tmp_w;
0119 #ifdef CONFIG_DEBUG_FS
0120     struct dentry *parent = dapm->debugfs_dapm;
0121     struct dentry *debugfs_w = NULL;
0122 #endif
0123 
0124     mutex_lock(&dapm->card->dapm_mutex);
0125     for (i = 0; i < num; i++) {
0126         /* below logic can be optimized to identify widget pointer */
0127         w = NULL;
0128         list_for_each_entry(tmp_w, &dapm->card->widgets, list) {
0129             if (tmp_w->dapm == dapm &&
0130                 !strcmp(tmp_w->name, widget->name)) {
0131                 w = tmp_w;
0132                 break;
0133             }
0134         }
0135         if (!w) {
0136             dev_err(dapm->dev, "%s: widget not found\n",
0137                 widget->name);
0138             widget++;
0139             continue;
0140         }
0141         widget++;
0142 #ifdef CONFIG_DEBUG_FS
0143         if (!parent)
0144             debugfs_w = debugfs_lookup(w->name, parent);
0145         debugfs_remove(debugfs_w);
0146         debugfs_w = NULL;
0147 #endif
0148         gbaudio_dapm_free_widget(w);
0149     }
0150     mutex_unlock(&dapm->card->dapm_mutex);
0151     return 0;
0152 }
0153 
0154 static int gbaudio_remove_controls(struct snd_card *card, struct device *dev,
0155                    const struct snd_kcontrol_new *controls,
0156                    int num_controls, const char *prefix)
0157 {
0158     int i, err;
0159 
0160     for (i = 0; i < num_controls; i++) {
0161         const struct snd_kcontrol_new *control = &controls[i];
0162         struct snd_ctl_elem_id id;
0163         struct snd_kcontrol *kctl;
0164 
0165         if (prefix)
0166             snprintf(id.name, sizeof(id.name), "%s %s", prefix,
0167                  control->name);
0168         else
0169             strscpy(id.name, control->name, sizeof(id.name));
0170         id.numid = 0;
0171         id.iface = control->iface;
0172         id.device = control->device;
0173         id.subdevice = control->subdevice;
0174         id.index = control->index;
0175         kctl = snd_ctl_find_id(card, &id);
0176         if (!kctl) {
0177             dev_err(dev, "Failed to find %s\n", control->name);
0178             continue;
0179         }
0180         err = snd_ctl_remove(card, kctl);
0181         if (err < 0) {
0182             dev_err(dev, "%d: Failed to remove %s\n", err,
0183                 control->name);
0184             continue;
0185         }
0186     }
0187     return 0;
0188 }
0189 
0190 int gbaudio_remove_component_controls(struct snd_soc_component *component,
0191                       const struct snd_kcontrol_new *controls,
0192                       unsigned int num_controls)
0193 {
0194     struct snd_card *card = component->card->snd_card;
0195     int err;
0196 
0197     down_write(&card->controls_rwsem);
0198     err = gbaudio_remove_controls(card, component->dev, controls,
0199                       num_controls, component->name_prefix);
0200     up_write(&card->controls_rwsem);
0201     return err;
0202 }