linux/drivers/staging/greybus/audio_helper.c
<<
>>
Prefs
   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
 196        return gbaudio_remove_controls(card, component->dev, controls,
 197                                       num_controls, component->name_prefix);
 198}
 199