ASoC: wm9712: Remove driver specific version
[deliverable/linux.git] / sound / soc / soc-dapm.c
index f42e8b9fb17db7baeabaf7152f81123773cdecd0..ec58a31465694ba53747e5d8f73b7e212257b05c 100644 (file)
@@ -39,6 +39,8 @@
 #include <linux/platform_device.h>
 #include <linux/jiffies.h>
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -54,6 +56,7 @@
 static int dapm_up_seq[] = {
        [snd_soc_dapm_pre] = 0,
        [snd_soc_dapm_supply] = 1,
+       [snd_soc_dapm_regulator_supply] = 1,
        [snd_soc_dapm_micbias] = 2,
        [snd_soc_dapm_aif_in] = 3,
        [snd_soc_dapm_aif_out] = 3,
@@ -89,6 +92,7 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_value_mux] = 9,
        [snd_soc_dapm_aif_in] = 10,
        [snd_soc_dapm_aif_out] = 10,
+       [snd_soc_dapm_regulator_supply] = 11,
        [snd_soc_dapm_supply] = 11,
        [snd_soc_dapm_post] = 12,
 };
@@ -196,21 +200,28 @@ static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val)
 static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
        unsigned short reg, unsigned int mask, unsigned int value)
 {
-       int change;
+       bool change;
        unsigned int old, new;
        int ret;
 
-       ret = soc_widget_read(w, reg);
-       if (ret < 0)
-               return ret;
-
-       old = ret;
-       new = (old & ~mask) | (value & mask);
-       change = old != new;
-       if (change) {
-               ret = soc_widget_write(w, reg, new);
+       if (w->codec && w->codec->using_regmap) {
+               ret = regmap_update_bits_check(w->codec->control_data,
+                                              reg, mask, value, &change);
+               if (ret != 0)
+                       return ret;
+       } else {
+               ret = soc_widget_read(w, reg);
                if (ret < 0)
                        return ret;
+
+               old = ret;
+               new = (old & ~mask) | (value & mask);
+               change = old != new;
+               if (change) {
+                       ret = soc_widget_write(w, reg, new);
+                       if (ret < 0)
+                               return ret;
+               }
        }
 
        return change;
@@ -339,10 +350,12 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_output:
        case snd_soc_dapm_adc:
        case snd_soc_dapm_input:
+       case snd_soc_dapm_siggen:
        case snd_soc_dapm_dac:
        case snd_soc_dapm_micbias:
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
        case snd_soc_dapm_hp:
@@ -671,8 +684,13 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       if (widget->id == snd_soc_dapm_supply)
+       switch (widget->id) {
+       case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                return 0;
+       default:
+               break;
+       }
 
        switch (widget->id) {
        case snd_soc_dapm_adc:
@@ -736,8 +754,13 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       if (widget->id == snd_soc_dapm_supply)
+       switch (widget->id) {
+       case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                return 0;
+       default:
+               break;
+       }
 
        /* active stream ? */
        switch (widget->id) {
@@ -772,6 +795,11 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
                        return widget->inputs;
                }
 
+               /* signal generator */
+               if (widget->id == snd_soc_dapm_siggen) {
+                       widget->inputs = snd_soc_dapm_suspend_check(widget);
+                       return widget->inputs;
+               }
        }
 
        list_for_each_entry(path, &widget->sources, list_sink) {
@@ -814,6 +842,19 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(dapm_reg_event);
 
+/*
+ * Handler for regulator supply widget.
+ */
+int dapm_regulator_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               return regulator_enable(w->priv);
+       else
+               return regulator_disable_deferred(w->priv, w->shift);
+}
+EXPORT_SYMBOL_GPL(dapm_regulator_event);
+
 static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
 {
        if (w->power_checked)
@@ -1200,6 +1241,9 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
        /* If we're off and we're not supposed to be go into STANDBY */
        if (d->bias_level == SND_SOC_BIAS_OFF &&
            d->target_bias_level != SND_SOC_BIAS_OFF) {
+               if (d->dev)
+                       pm_runtime_get_sync(d->dev);
+
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
                if (ret != 0)
                        dev_err(d->dev,
@@ -1239,6 +1283,9 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
                ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
                if (ret != 0)
                        dev_err(d->dev, "Failed to turn off bias: %d\n", ret);
+
+               if (d->dev)
+                       pm_runtime_put(d->dev);
        }
 
        /* If we just powered up then move to active bias */
@@ -1288,6 +1335,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
        }
        switch (w->id) {
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                /* Supplies can't affect their outputs, only their inputs */
                break;
        default:
@@ -1387,10 +1435,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                        /* Supplies and micbiases only bring the
                         * context up to STANDBY as unless something
                         * else is active and passing audio they
-                        * generally don't require full power.
+                        * generally don't require full power.  Signal
+                        * generators are virtual pins and have no
+                        * power impact themselves.
                         */
                        switch (w->id) {
+                       case snd_soc_dapm_siggen:
+                               break;
                        case snd_soc_dapm_supply:
+                       case snd_soc_dapm_regulator_supply:
                        case snd_soc_dapm_micbias:
                                if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
                                        d->target_bias_level = SND_SOC_BIAS_STANDBY;
@@ -1413,7 +1466,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                        dapm->target_bias_level = SND_SOC_BIAS_ON;
                        break;
                case SND_SOC_DAPM_STREAM_STOP:
-                       if (dapm->codec->active)
+                       if (dapm->codec && dapm->codec->active)
                                dapm->target_bias_level = SND_SOC_BIAS_ON;
                        else
                                dapm->target_bias_level = SND_SOC_BIAS_STANDBY;
@@ -1725,8 +1778,7 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
 static ssize_t dapm_widget_show(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
-       struct snd_soc_pcm_runtime *rtd =
-                       container_of(dev, struct snd_soc_pcm_runtime, dev);
+       struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
        struct snd_soc_codec *codec =rtd->codec;
        struct snd_soc_dapm_widget *w;
        int count = 0;
@@ -1750,6 +1802,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                case snd_soc_dapm_mixer:
                case snd_soc_dapm_mixer_named_ctl:
                case snd_soc_dapm_supply:
+               case snd_soc_dapm_regulator_supply:
                        if (w->name)
                                count += sprintf(buf + count, "%s: %s\n",
                                        w->name, w->power ? "On":"Off");
@@ -1982,11 +2035,13 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_out_drv:
        case snd_soc_dapm_input:
        case snd_soc_dapm_output:
+       case snd_soc_dapm_siggen:
        case snd_soc_dapm_micbias:
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_pre:
        case snd_soc_dapm_post:
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
                list_add(&path->list, &dapm->card->paths);
@@ -2653,10 +2708,25 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 {
        struct snd_soc_dapm_widget *w;
        size_t name_len;
+       int ret;
 
        if ((w = dapm_cnew_widget(widget)) == NULL)
                return -ENOMEM;
 
+       switch (w->id) {
+       case snd_soc_dapm_regulator_supply:
+               w->priv = devm_regulator_get(dapm->dev, w->name);
+               if (IS_ERR(w->priv)) {
+                       ret = PTR_ERR(w->priv);
+                       dev_err(dapm->dev, "Failed to request %s: %d\n",
+                               w->name, ret);
+                       return ret;
+               }
+               break;
+       default:
+               break;
+       }
+
        name_len = strlen(widget->name) + 1;
        if (dapm->codec && dapm->codec->name_prefix)
                name_len += 1 + strlen(dapm->codec->name_prefix);
@@ -2702,6 +2772,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                w->power_check = dapm_generic_check_power;
                break;
        case snd_soc_dapm_supply:
+       case snd_soc_dapm_regulator_supply:
                w->power_check = dapm_supply_check_power;
                break;
        default:
@@ -2947,6 +3018,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(codec->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(codec->dev, "Auto NC: Checking widget %s\n",
+                               w->name);
+                       if (!snd_soc_dapm_widget_in_card_paths(card, w)) {
+                               dev_dbg(codec->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
This page took 0.038565 seconds and 5 git commands to generate.