ASoC: Implement fully_routed card property
authorStephen Warren <swarren@nvidia.com>
Wed, 23 Nov 2011 19:42:04 +0000 (12:42 -0700)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Wed, 23 Nov 2011 21:34:54 +0000 (21:34 +0000)
A card is fully routed if the DAPM route table describes all connections on
the board.

When a card is fully routed, some operations can be automated by the ASoC
core. The first, and currently only, such operation is described below, and
implemented by this patch.

Codecs often have a large number of external pins, and not all of these pins
will be connected on all board designs. Some machine drivers therefore call
snd_soc_dapm_nc_pin() for all the unused pins, in order to tell the ASoC core
never to activate them.

However, when a card is fully routed, the information needed to derive the
set of unused pins is present in card->dapm_routes. In this case, have
the ASoC core automatically call snd_soc_dapm_nc_pin() for each unused
codec pin.

This has been tested with soc/tegra/tegra_wm8903.c and soc/tegra/trimslice.c.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
include/sound/soc-dapm.h
include/sound/soc.h
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index 17a4c17f19f5ff672030d448bb84ea5b3d8a354a..0c159a7d211e78b8d3b765dbb174ce8384cb71f8 100644 (file)
@@ -380,6 +380,7 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm,
                                  const char *pin);
 int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
                                const char *pin);
+void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec);
 
 /* Mostly internal - should not normally be used */
 void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason);
index b21b3047e91b883f3b7ef421db60c3d1690cd8f2..737a4f4b18ff725c52b1e3c6587dab3b3e90c69f 100644 (file)
@@ -815,6 +815,7 @@ struct snd_soc_card {
        int num_dapm_widgets;
        const struct snd_soc_dapm_route *dapm_routes;
        int num_dapm_routes;
+       bool fully_routed;
 
        struct work_struct deferred_resume_work;
 
index b194842c4cc3d69238b5a0d751c9e6d9030e0886..2abaf6dcdb0ac06b750231982bf3c06e665b875d 100644 (file)
@@ -1488,6 +1488,10 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
 
        snd_soc_dapm_new_widgets(&card->dapm);
 
+       if (card->fully_routed)
+               list_for_each_entry(codec, &codec_list, list)
+                       snd_soc_dapm_auto_nc_codec_pins(codec);
+
        ret = snd_card_register(card->snd_card);
        if (ret < 0) {
                printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
index f42e8b9fb17db7baeabaf7152f81123773cdecd0..1ecd1b4927f95c816b211e4e7893f25d29601e0c 100644 (file)
@@ -2947,6 +2947,79 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
 
+static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
+                                             struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_path *p;
+
+       list_for_each_entry(p, &card->paths, list) {
+               if ((p->source == w) || (p->sink == w)) {
+                       dev_dbg(card->dev,
+                           "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
+                           p->source->name, p->source->id, p->source->dapm,
+                           p->sink->name, p->sink->id, p->sink->dapm);
+
+                       /* Connected to something other than the codec */
+                       if (p->source->dapm != p->sink->dapm)
+                               return true;
+                       /*
+                        * Loopback connection from codec external pin to
+                        * codec external pin
+                        */
+                       if (p->sink->id == snd_soc_dapm_input) {
+                               switch (p->source->id) {
+                               case snd_soc_dapm_output:
+                               case snd_soc_dapm_micbias:
+                                       return true;
+                               default:
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return false;
+}
+
+/**
+ * snd_soc_dapm_auto_nc_codec_pins - call snd_soc_dapm_nc_pin for unused pins
+ * @codec: The codec whose pins should be processed
+ *
+ * Automatically call snd_soc_dapm_nc_pin() for any external pins in the codec
+ * which are unused. Pins are used if they are connected externally to the
+ * codec, whether that be to some other device, or a loop-back connection to
+ * the codec itself.
+ */
+void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec)
+{
+       struct snd_soc_card *card = codec->card;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct snd_soc_dapm_widget *w;
+
+       dev_dbg(card->dev, "Auto NC: DAPMs: card:%p codec:%p\n",
+               &card->dapm, &codec->dapm);
+
+       list_for_each_entry(w, &card->widgets, list) {
+               if (w->dapm != dapm)
+                       continue;
+               switch (w->id) {
+               case snd_soc_dapm_input:
+               case snd_soc_dapm_output:
+               case snd_soc_dapm_micbias:
+                       dev_dbg(card->dev, "Auto NC: Checking widget %s\n",
+                               w->name);
+                       if (!snd_soc_dapm_widget_in_card_paths(card, w)) {
+                               dev_dbg(card->dev,
+                                       "... Not in map; disabling\n");
+                               snd_soc_dapm_nc_pin(dapm, w->name);
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
 /**
  * snd_soc_dapm_free - free dapm resources
  * @dapm: DAPM context