ASoC: multi-component - ASoC Multi-Component Support
[cascardo/linux.git] / sound / soc / soc-dapm.c
index 7c28f40..035cab8 100644 (file)
@@ -98,7 +98,6 @@ static void pop_dbg(u32 pop_time, const char *fmt, ...)
 
        if (pop_time) {
                vprintk(fmt, args);
-               pop_wait(pop_time);
        }
 
        va_end(args);
@@ -113,43 +112,41 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
 
 /**
  * snd_soc_dapm_set_bias_level - set the bias level for the system
- * @socdev: audio device
+ * @card: audio device
  * @level: level to configure
  *
  * Configure the bias (power) levels for the SoC audio device.
  *
  * Returns 0 for success else error.
  */
-static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
-                                      enum snd_soc_bias_level level)
+static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card,
+               struct snd_soc_codec *codec, enum snd_soc_bias_level level)
 {
-       struct snd_soc_card *card = socdev->card;
-       struct snd_soc_codec *codec = socdev->card->codec;
        int ret = 0;
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               dev_dbg(socdev->dev, "Setting full bias\n");
+               dev_dbg(codec->dev, "Setting full bias\n");
                break;
        case SND_SOC_BIAS_PREPARE:
-               dev_dbg(socdev->dev, "Setting bias prepare\n");
+               dev_dbg(codec->dev, "Setting bias prepare\n");
                break;
        case SND_SOC_BIAS_STANDBY:
-               dev_dbg(socdev->dev, "Setting standby bias\n");
+               dev_dbg(codec->dev, "Setting standby bias\n");
                break;
        case SND_SOC_BIAS_OFF:
-               dev_dbg(socdev->dev, "Setting bias off\n");
+               dev_dbg(codec->dev, "Setting bias off\n");
                break;
        default:
-               dev_err(socdev->dev, "Setting invalid bias %d\n", level);
+               dev_err(codec->dev, "Setting invalid bias %d\n", level);
                return -EINVAL;
        }
 
-       if (card->set_bias_level)
+       if (card && card->set_bias_level)
                ret = card->set_bias_level(card, level);
        if (ret == 0) {
-               if (codec->set_bias_level)
-                       ret = codec->set_bias_level(codec, level);
+               if (codec->driver->set_bias_level)
+                       ret = codec->driver->set_bias_level(codec, level);
                else
                        codec->bias_level = level;
        }
@@ -315,62 +312,14 @@ static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
                pop_dbg(codec->pop_time, "pop test %s : %s in %d ms\n",
                        widget->name, widget->power ? "on" : "off",
                        codec->pop_time);
-               snd_soc_write(codec, widget->reg, new);
                pop_wait(codec->pop_time);
+               snd_soc_write(codec, widget->reg, new);
        }
        pr_debug("reg %x old %x new %x change %d\n", widget->reg,
                 old, new, change);
        return change;
 }
 
-/* ramps the volume up or down to minimise pops before or after a
- * DAPM power event */
-static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)
-{
-       const struct snd_kcontrol_new *k = widget->kcontrols;
-
-       if (widget->muted && !power)
-               return 0;
-       if (!widget->muted && power)
-               return 0;
-
-       if (widget->num_kcontrols && k) {
-               struct soc_mixer_control *mc =
-                       (struct soc_mixer_control *)k->private_value;
-               unsigned int reg = mc->reg;
-               unsigned int shift = mc->shift;
-               int max = mc->max;
-               unsigned int mask = (1 << fls(max)) - 1;
-               unsigned int invert = mc->invert;
-
-               if (power) {
-                       int i;
-                       /* power up has happended, increase volume to last level */
-                       if (invert) {
-                               for (i = max; i > widget->saved_value; i--)
-                                       snd_soc_update_bits(widget->codec, reg, mask, i);
-                       } else {
-                               for (i = 0; i < widget->saved_value; i++)
-                                       snd_soc_update_bits(widget->codec, reg, mask, i);
-                       }
-                       widget->muted = 0;
-               } else {
-                       /* power down is about to occur, decrease volume to mute */
-                       int val = snd_soc_read(widget->codec, reg);
-                       int i = widget->saved_value = (val >> shift) & mask;
-                       if (invert) {
-                               for (; i < mask; i++)
-                                       snd_soc_update_bits(widget->codec, reg, mask, i);
-                       } else {
-                               for (; i > 0; i--)
-                                       snd_soc_update_bits(widget->codec, reg, mask, i);
-                       }
-                       widget->muted = 1;
-               }
-       }
-       return 0;
-}
-
 /* create new dapm mixer control */
 static int dapm_new_mixer(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *w)
@@ -419,7 +368,7 @@ static int dapm_new_mixer(struct snd_soc_codec *codec,
 
                        path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w,
                                path->long_name);
-                       ret = snd_ctl_add(codec->card, path->kcontrol);
+                       ret = snd_ctl_add(codec->card->snd_card, path->kcontrol);
                        if (ret < 0) {
                                printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
                                       path->long_name,
@@ -447,7 +396,7 @@ static int dapm_new_mux(struct snd_soc_codec *codec,
        }
 
        kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
-       ret = snd_ctl_add(codec->card, kcontrol);
+       ret = snd_ctl_add(codec->card->snd_card, kcontrol);
        if (ret < 0)
                goto err;
 
@@ -465,20 +414,10 @@ err:
 static int dapm_new_pga(struct snd_soc_codec *codec,
        struct snd_soc_dapm_widget *w)
 {
-       struct snd_kcontrol *kcontrol;
-       int ret = 0;
-
-       if (!w->num_kcontrols)
-               return -EINVAL;
+       if (w->num_kcontrols)
+               pr_err("asoc: PGA controls not supported: '%s'\n", w->name);
 
-       kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
-       ret = snd_ctl_add(codec->card, kcontrol);
-       if (ret < 0) {
-               printk(KERN_ERR "asoc: failed to add kcontrol %s\n", w->name);
-               return ret;
-       }
-
-       return ret;
+       return 0;
 }
 
 /* reset 'walked' bit for each dapm path */
@@ -490,6 +429,25 @@ static inline void dapm_clear_walk(struct snd_soc_codec *codec)
                p->walked = 0;
 }
 
+/* We implement power down on suspend by checking the power state of
+ * the ALSA card - when we are suspending the ALSA state for the card
+ * is set to D3.
+ */
+static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget)
+{
+       int level = snd_power_get_state(widget->codec->card->snd_card);
+
+       switch (level) {
+       case SNDRV_CTL_POWER_D3hot:
+       case SNDRV_CTL_POWER_D3cold:
+               if (widget->ignore_suspend)
+                       pr_debug("%s ignoring suspend\n", widget->name);
+               return widget->ignore_suspend;
+       default:
+               return 1;
+       }
+}
+
 /*
  * Recursively check for a completed path to an active or physically connected
  * output widget. Returns number of complete paths.
@@ -506,7 +464,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
        case snd_soc_dapm_adc:
        case snd_soc_dapm_aif_out:
                if (widget->active)
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
        default:
                break;
        }
@@ -514,12 +472,12 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget)
        if (widget->connected) {
                /* connected pin ? */
                if (widget->id == snd_soc_dapm_output && !widget->ext)
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
 
                /* connected jack or spk ? */
                if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk ||
                    (widget->id == snd_soc_dapm_line && !list_empty(&widget->sources)))
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
        }
 
        list_for_each_entry(path, &widget->sinks, list_source) {
@@ -552,7 +510,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        case snd_soc_dapm_dac:
        case snd_soc_dapm_aif_in:
                if (widget->active)
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
        default:
                break;
        }
@@ -560,16 +518,16 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget)
        if (widget->connected) {
                /* connected pin ? */
                if (widget->id == snd_soc_dapm_input && !widget->ext)
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
 
                /* connected VMID/Bias for lower pops */
                if (widget->id == snd_soc_dapm_vmid)
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
 
                /* connected jack ? */
                if (widget->id == snd_soc_dapm_mic ||
                    (widget->id == snd_soc_dapm_line && !list_empty(&widget->sinks)))
-                       return 1;
+                       return snd_soc_dapm_suspend_check(widget);
        }
 
        list_for_each_entry(path, &widget->sources, list_sink) {
@@ -634,16 +592,8 @@ static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
                        return ret;
        }
 
-       /* Lower PGA volume to reduce pops */
-       if (w->id == snd_soc_dapm_pga && !w->power)
-               dapm_set_pga(w, w->power);
-
        dapm_update_bits(w);
 
-       /* Raise PGA volume to reduce pops */
-       if (w->id == snd_soc_dapm_pga && w->power)
-               dapm_set_pga(w, w->power);
-
        /* power up post event */
        if (w->power && w->event &&
            (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
@@ -810,10 +760,6 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
                                pr_err("%s: pre event failed: %d\n",
                                       w->name, ret);
                }
-
-               /* Lower PGA volume to reduce pops */
-               if (w->id == snd_soc_dapm_pga && !w->power)
-                       dapm_set_pga(w, w->power);
        }
 
        if (reg >= 0) {
@@ -825,10 +771,6 @@ static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
        }
 
        list_for_each_entry(w, pending, power_list) {
-               /* Raise PGA volume to reduce pops */
-               if (w->id == snd_soc_dapm_pga && w->power)
-                       dapm_set_pga(w, w->power);
-
                /* power up post event */
                if (w->power && w->event &&
                    (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
@@ -949,7 +891,7 @@ static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
  */
 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 {
-       struct snd_soc_device *socdev = codec->socdev;
+       struct snd_soc_card *card = codec->card;
        struct snd_soc_dapm_widget *w;
        LIST_HEAD(up_list);
        LIST_HEAD(down_list);
@@ -973,19 +915,12 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
                        if (!w->power_check)
                                continue;
 
-                       /* If we're suspending then pull down all the 
-                        * power. */
-                       switch (event) {
-                       case SND_SOC_DAPM_STREAM_SUSPEND:
-                               power = 0;
-                               break;
-
-                       default:
+                       if (!w->force)
                                power = w->power_check(w);
-                               if (power)
-                                       sys_power = 1;
-                               break;
-                       }
+                       else
+                               power = 1;
+                       if (power)
+                               sys_power = 1;
 
                        if (w->power == power)
                                continue;
@@ -1029,7 +964,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
        }
 
        if (sys_power && codec->bias_level == SND_SOC_BIAS_OFF) {
-               ret = snd_soc_dapm_set_bias_level(socdev,
+               ret = snd_soc_dapm_set_bias_level(card, codec,
                                                  SND_SOC_BIAS_STANDBY);
                if (ret != 0)
                        pr_err("Failed to turn on bias: %d\n", ret);
@@ -1038,8 +973,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
        /* If we're changing to all on or all off then prepare */
        if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
            (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
-               ret = snd_soc_dapm_set_bias_level(socdev,
-                                                 SND_SOC_BIAS_PREPARE);
+               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_PREPARE);
                if (ret != 0)
                        pr_err("Failed to prepare bias: %d\n", ret);
        }
@@ -1052,8 +986,7 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 
        /* If we just powered the last thing off drop to standby bias */
        if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
-               ret = snd_soc_dapm_set_bias_level(socdev,
-                                                 SND_SOC_BIAS_STANDBY);
+               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_STANDBY);
                if (ret != 0)
                        pr_err("Failed to apply standby bias: %d\n", ret);
        }
@@ -1061,21 +994,21 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
        /* If we're in standby and can support bias off then do that */
        if (codec->bias_level == SND_SOC_BIAS_STANDBY &&
            codec->idle_bias_off) {
-               ret = snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF);
                if (ret != 0)
                        pr_err("Failed to turn off bias: %d\n", ret);
        }
 
        /* If we just powered up then move to active bias */
        if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
-               ret = snd_soc_dapm_set_bias_level(socdev,
-                                                 SND_SOC_BIAS_ON);
+               ret = snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_ON);
                if (ret != 0)
                        pr_err("Failed to apply active bias: %d\n", ret);
        }
 
        pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
                codec->pop_time);
+       pop_wait(codec->pop_time);
 
        return 0;
 }
@@ -1250,8 +1183,9 @@ 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_device *devdata = dev_get_drvdata(dev);
-       struct snd_soc_codec *codec = devdata->card->codec;
+       struct snd_soc_pcm_runtime *rtd =
+                       container_of(dev, struct snd_soc_pcm_runtime, dev);
+       struct snd_soc_codec *codec =rtd->codec;
        struct snd_soc_dapm_widget *w;
        int count = 0;
        char *state = "not set";
@@ -1338,6 +1272,9 @@ static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec,
                if (!strcmp(w->name, pin)) {
                        pr_debug("dapm: %s: pin %s\n", codec->name, pin);
                        w->connected = status;
+                       /* Allow disabling of forced pins */
+                       if (status == 0)
+                               w->force = 0;
                        return 0;
                }
        }
@@ -1594,12 +1531,6 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        unsigned int invert = mc->invert;
        unsigned int mask = (1 << fls(max)) - 1;
 
-       /* return the saved value if we are powered down */
-       if (widget->id == snd_soc_dapm_pga && !widget->power) {
-               ucontrol->value.integer.value[0] = widget->saved_value;
-               return 0;
-       }
-
        ucontrol->value.integer.value[0] =
                (snd_soc_read(widget->codec, reg) >> shift) & mask;
        if (shift != rshift)
@@ -1659,13 +1590,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        mutex_lock(&widget->codec->mutex);
        widget->value = val;
 
-       /* save volume value if the widget is powered down */
-       if (widget->id == snd_soc_dapm_pga && !widget->power) {
-               widget->saved_value = val;
-               mutex_unlock(&widget->codec->mutex);
-               return 1;
-       }
-
        if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
                if (val)
                        /* new connection */
@@ -2070,9 +1994,10 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls);
  *
  * Returns 0 for success else error.
  */
-int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
-       char *stream, int event)
+int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd,
+       const char *stream, int event)
 {
+       struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_dapm_widget *w;
 
        if (stream == NULL)
@@ -2094,18 +2019,8 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec,
                                w->active = 0;
                                break;
                        case SND_SOC_DAPM_STREAM_SUSPEND:
-                               if (w->active)
-                                       w->suspend = 1;
-                               w->active = 0;
-                               break;
                        case SND_SOC_DAPM_STREAM_RESUME:
-                               if (w->suspend) {
-                                       w->active = 1;
-                                       w->suspend = 0;
-                               }
-                               break;
                        case SND_SOC_DAPM_STREAM_PAUSE_PUSH:
-                               break;
                        case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
                                break;
                        }
@@ -2134,6 +2049,36 @@ int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin);
 
+/**
+ * snd_soc_dapm_force_enable_pin - force a pin to be enabled
+ * @codec: SoC codec
+ * @pin: pin name
+ *
+ * Enables input/output pin regardless of any other state.  This is
+ * intended for use with microphone bias supplies used in microphone
+ * jack detection.
+ *
+ * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
+ * do any widget power switching.
+ */
+int snd_soc_dapm_force_enable_pin(struct snd_soc_codec *codec, const char *pin)
+{
+       struct snd_soc_dapm_widget *w;
+
+       list_for_each_entry(w, &codec->dapm_widgets, list) {
+               if (!strcmp(w->name, pin)) {
+                       pr_debug("dapm: %s: pin %s\n", codec->name, pin);
+                       w->connected = 1;
+                       w->force = 1;
+                       return 0;
+               }
+       }
+
+       pr_err("dapm: %s: configuring unknown pin %s\n", codec->name, pin);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin);
+
 /**
  * snd_soc_dapm_disable_pin - disable pin.
  * @codec: SoC codec
@@ -2191,27 +2136,48 @@ int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status);
 
+/**
+ * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint
+ * @codec: audio codec
+ * @pin: audio signal pin endpoint (or start point)
+ *
+ * Mark the given endpoint or pin as ignoring suspend.  When the
+ * system is disabled a path between two endpoints flagged as ignoring
+ * suspend will not be disabled.  The path must already be enabled via
+ * normal means at suspend time, it will not be turned on if it was not
+ * already enabled.
+ */
+int snd_soc_dapm_ignore_suspend(struct snd_soc_codec *codec, const char *pin)
+{
+       struct snd_soc_dapm_widget *w;
+
+       list_for_each_entry(w, &codec->dapm_widgets, list) {
+               if (!strcmp(w->name, pin)) {
+                       w->ignore_suspend = 1;
+                       return 0;
+               }
+       }
+
+       pr_err("Unknown DAPM pin: %s\n", pin);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
+
 /**
  * snd_soc_dapm_free - free dapm resources
- * @socdev: SoC device
+ * @card: SoC device
  *
  * Free all dapm widgets and resources.
  */
-void snd_soc_dapm_free(struct snd_soc_device *socdev)
+void snd_soc_dapm_free(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec = socdev->card->codec;
-
-       snd_soc_dapm_sys_remove(socdev->dev);
+       snd_soc_dapm_sys_remove(codec->dev);
        dapm_free_widgets(codec);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
-/*
- * snd_soc_dapm_shutdown - callback for system shutdown
- */
-void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
+static void soc_dapm_shutdown_codec(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec = socdev->card->codec;
        struct snd_soc_dapm_widget *w;
        LIST_HEAD(down_list);
        int powerdown = 0;
@@ -2228,12 +2194,23 @@ void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
         * standby.
         */
        if (powerdown) {
-               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE);
+               snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_PREPARE);
                dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
-               snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY);
+               snd_soc_dapm_set_bias_level(NULL, codec, SND_SOC_BIAS_STANDBY);
        }
+}
+
+/*
+ * snd_soc_dapm_shutdown - callback for system shutdown
+ */
+void snd_soc_dapm_shutdown(struct snd_soc_card *card)
+{
+       struct snd_soc_codec *codec;
+
+       list_for_each_entry(codec, &card->codec_dev_list, list)
+               soc_dapm_shutdown_codec(codec);
 
-       snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+       snd_soc_dapm_set_bias_level(card, codec, SND_SOC_BIAS_OFF);
 }
 
 /* Module information */