]> Pileus Git - ~andy/linux/blobdiff - sound/soc/soc-dapm.c
Merge branch 'for-3.5' into for-3.6
[~andy/linux] / sound / soc / soc-dapm.c
index 90ee77d2409da8402ea58026b788a624f850a25a..4d181df95dc3069ef58efcff1cd50de0d522fa7e 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/debugfs.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
+#include <linux/clk.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -51,6 +52,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_clock_supply] = 1,
        [snd_soc_dapm_micbias] = 2,
        [snd_soc_dapm_dai_link] = 2,
        [snd_soc_dapm_dai] = 3,
@@ -92,6 +94,7 @@ static int dapm_down_seq[] = {
        [snd_soc_dapm_aif_out] = 10,
        [snd_soc_dapm_dai] = 10,
        [snd_soc_dapm_dai_link] = 11,
+       [snd_soc_dapm_clock_supply] = 12,
        [snd_soc_dapm_regulator_supply] = 12,
        [snd_soc_dapm_supply] = 12,
        [snd_soc_dapm_post] = 13,
@@ -288,9 +291,9 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
                if (dapm->codec->driver->set_bias_level)
                        ret = dapm->codec->driver->set_bias_level(dapm->codec,
                                                                  level);
-               else
-                       dapm->bias_level = level;
-       }
+       } else
+               dapm->bias_level = level;
+
        if (ret != 0)
                goto out;
 
@@ -321,11 +324,10 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 
                val = soc_widget_read(w, reg);
                val = (val >> shift) & mask;
+               if (invert)
+                       val = max - val;
 
-               if ((invert && !val) || (!invert && val))
-                       p->connect = 1;
-               else
-                       p->connect = 0;
+               p->connect = !!val;
        }
        break;
        case snd_soc_dapm_mux: {
@@ -391,6 +393,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
+       case snd_soc_dapm_clock_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dai:
@@ -764,6 +767,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
        switch (widget->id) {
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
+       case snd_soc_dapm_clock_supply:
                return 0;
        default:
                break;
@@ -850,6 +854,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
        switch (widget->id) {
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
+       case snd_soc_dapm_clock_supply:
                return 0;
        default:
                break;
@@ -913,7 +918,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
                        /* do we need to add this widget to the list ? */
                        if (list) {
                                int err;
-                               err = dapm_list_add_widget(list, path->sink);
+                               err = dapm_list_add_widget(list, path->source);
                                if (err < 0) {
                                        dev_err(widget->dapm->dev, "could not add widget %s\n",
                                                widget->name);
@@ -954,7 +959,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                paths = is_connected_output_ep(dai->playback_widget, list);
        else
-               paths = is_connected_input_ep(dai->playback_widget, list);
+               paths = is_connected_input_ep(dai->capture_widget, list);
 
        trace_snd_soc_dapm_connected(paths, stream);
        dapm_clear_walk(&card->dapm);
@@ -996,6 +1001,27 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(dapm_regulator_event);
 
+/*
+ * Handler for clock supply widget.
+ */
+int dapm_clock_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event)
+{
+       if (!w->clk)
+               return -EIO;
+
+#ifdef CONFIG_HAVE_CLK
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               return clk_enable(w->clk);
+       } else {
+               clk_disable(w->clk);
+               return 0;
+       }
+#endif
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dapm_clock_event);
+
 static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
 {
        if (w->power_checked)
@@ -1487,6 +1513,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:
+       case snd_soc_dapm_clock_supply:
                /* Supplies can't affect their outputs, only their inputs */
                break;
        default:
@@ -1570,7 +1597,15 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        }
 
        list_for_each_entry(w, &card->widgets, list) {
-               list_del_init(&w->dirty);
+               switch (w->id) {
+               case snd_soc_dapm_pre:
+               case snd_soc_dapm_post:
+                       /* These widgets always need to be powered */
+                       break;
+               default:
+                       list_del_init(&w->dirty);
+                       break;
+               }
 
                if (w->power) {
                        d = w->dapm;
@@ -1587,6 +1622,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                                break;
                        case snd_soc_dapm_supply:
                        case snd_soc_dapm_regulator_supply:
+                       case snd_soc_dapm_clock_supply:
                        case snd_soc_dapm_micbias:
                                if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
                                        d->target_bias_level = SND_SOC_BIAS_STANDBY;
@@ -1941,6 +1977,7 @@ static ssize_t dapm_widget_show(struct device *dev,
                case snd_soc_dapm_mixer_named_ctl:
                case snd_soc_dapm_supply:
                case snd_soc_dapm_regulator_supply:
+               case snd_soc_dapm_clock_supply:
                        if (w->name)
                                count += sprintf(buf + count, "%s: %s\n",
                                        w->name, w->power ? "On":"Off");
@@ -2187,6 +2224,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_post:
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
+       case snd_soc_dapm_clock_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dai:
@@ -2221,6 +2259,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
                path->connect = 0;
                return 0;
        }
+
+       dapm_mark_dirty(wsource, "Route added");
+       dapm_mark_dirty(wsink, "Route added");
+
        return 0;
 
 err:
@@ -2230,6 +2272,59 @@ err:
        return ret;
 }
 
+static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
+                                 const struct snd_soc_dapm_route *route)
+{
+       struct snd_soc_dapm_path *path, *p;
+       const char *sink;
+       const char *source;
+       char prefixed_sink[80];
+       char prefixed_source[80];
+
+       if (route->control) {
+               dev_err(dapm->dev,
+                       "Removal of routes with controls not supported\n");
+               return -EINVAL;
+       }
+
+       if (dapm->codec && dapm->codec->name_prefix) {
+               snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+                        dapm->codec->name_prefix, route->sink);
+               sink = prefixed_sink;
+               snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+                        dapm->codec->name_prefix, route->source);
+               source = prefixed_source;
+       } else {
+               sink = route->sink;
+               source = route->source;
+       }
+
+       path = NULL;
+       list_for_each_entry(p, &dapm->card->paths, list) {
+               if (strcmp(p->source->name, source) != 0)
+                       continue;
+               if (strcmp(p->sink->name, sink) != 0)
+                       continue;
+               path = p;
+               break;
+       }
+
+       if (path) {
+               dapm_mark_dirty(path->source, "Route removed");
+               dapm_mark_dirty(path->sink, "Route removed");
+
+               list_del(&path->list);
+               list_del(&path->list_sink);
+               list_del(&path->list_source);
+               kfree(path);
+       } else {
+               dev_warn(dapm->dev, "Route %s->%s does not exist\n",
+                        source, sink);
+       }
+
+       return 0;
+}
+
 /**
  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
  * @dapm: DAPM context
@@ -2246,15 +2341,15 @@ err:
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
                            const struct snd_soc_dapm_route *route, int num)
 {
-       int i, ret = 0;
+       int i, r, ret = 0;
 
        mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
-               ret = snd_soc_dapm_add_route(dapm, route);
-               if (ret < 0) {
+               r = snd_soc_dapm_add_route(dapm, route);
+               if (r < 0) {
                        dev_err(dapm->dev, "Failed to add route %s->%s\n",
                                route->source, route->sink);
-                       break;
+                       ret = r;
                }
                route++;
        }
@@ -2264,6 +2359,30 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 
+/**
+ * snd_soc_dapm_del_routes - Remove routes between DAPM widgets
+ * @dapm: DAPM context
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Removes routes from the DAPM context.
+ */
+int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
+                           const struct snd_soc_dapm_route *route, int num)
+{
+       int i, ret = 0;
+
+       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+       for (i = 0; i < num; i++) {
+               snd_soc_dapm_del_route(dapm, route);
+               route++;
+       }
+       mutex_unlock(&dapm->card->dapm_mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
+
 static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
                                   const struct snd_soc_dapm_route *route)
 {
@@ -2434,23 +2553,20 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
        int max = mc->max;
-       unsigned int invert = mc->invert;
        unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+
+       if (snd_soc_volsw_is_stereo(mc))
+               dev_warn(widget->dapm->dev,
+                        "Control '%s' is stereo, which is not supported\n",
+                        kcontrol->id.name);
 
        ucontrol->value.integer.value[0] =
                (snd_soc_read(widget->codec, reg) >> shift) & mask;
-       if (shift != rshift)
-               ucontrol->value.integer.value[1] =
-                       (snd_soc_read(widget->codec, reg) >> rshift) & mask;
-       if (invert) {
+       if (invert)
                ucontrol->value.integer.value[0] =
                        max - ucontrol->value.integer.value[0];
-               if (shift != rshift)
-                       ucontrol->value.integer.value[1] =
-                               max - ucontrol->value.integer.value[1];
-       }
 
        return 0;
 }
@@ -2484,20 +2600,19 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        struct snd_soc_dapm_update update;
        int wi;
 
+       if (snd_soc_volsw_is_stereo(mc))
+               dev_warn(widget->dapm->dev,
+                        "Control '%s' is stereo, which is not supported\n",
+                        kcontrol->id.name);
+
        val = (ucontrol->value.integer.value[0] & mask);
+       connect = !!val;
 
        if (invert)
                val = max - val;
        mask = mask << shift;
        val = val << shift;
 
-       if (val)
-               /* new connection */
-               connect = invert ? 0 : 1;
-       else
-               /* old connection must be powered down */
-               connect = invert ? 1 : 0;
-
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
        change = snd_soc_test_bits(widget->codec, reg, mask, val);
@@ -2873,6 +2988,19 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                        return NULL;
                }
                break;
+       case snd_soc_dapm_clock_supply:
+#ifdef CONFIG_CLKDEV_LOOKUP
+               w->clk = devm_clk_get(dapm->dev, w->name);
+               if (IS_ERR(w->clk)) {
+                       ret = PTR_ERR(w->clk);
+                       dev_err(dapm->dev, "Failed to request %s: %d\n",
+                               w->name, ret);
+                       return NULL;
+               }
+#else
+               return NULL;
+#endif
+               break;
        default:
                break;
        }
@@ -2924,6 +3052,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                break;
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
+       case snd_soc_dapm_clock_supply:
                w->power_check = dapm_supply_check_power;
                break;
        case snd_soc_dapm_dai:
@@ -3538,10 +3667,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
 static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
 {
+       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
        LIST_HEAD(down_list);
        int powerdown = 0;
 
+       mutex_lock(&card->dapm_mutex);
+
        list_for_each_entry(w, &dapm->card->widgets, list) {
                if (w->dapm != dapm)
                        continue;
@@ -3564,6 +3696,8 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
                        snd_soc_dapm_set_bias_level(dapm,
                                                    SND_SOC_BIAS_STANDBY);
        }
+
+       mutex_unlock(&card->dapm_mutex);
 }
 
 /*