Merge remote-tracking branch 'asoc/topic/dapm' into asoc-next
authorMark Brown <broonie@kernel.org>
Mon, 12 Dec 2016 15:52:44 +0000 (15:52 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 12 Dec 2016 15:52:44 +0000 (15:52 +0000)
include/sound/soc-dapm.h
sound/soc/codecs/adau17x1.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/soc-dapm.c

index f60d755f7ac6c947ff5d6c817ae8ebce430f1224..a466f4bdc835948c21f27a5f9f57f91930db0090 100644 (file)
@@ -272,6 +272,16 @@ struct device;
 
 
 /* dapm kcontrol types */
+#define SOC_DAPM_DOUBLE(xname, reg, lshift, rshift, max, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
+#define SOC_DAPM_DOUBLE_R(xname, lreg, rreg, shift, max, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value = SOC_DOUBLE_R_VALUE(lreg, rreg, shift, max, invert) }
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
@@ -615,6 +625,10 @@ struct snd_soc_dapm_update {
        int reg;
        int mask;
        int val;
+       int reg2;
+       int mask2;
+       int val2;
+       bool has_second_set;
 };
 
 struct snd_soc_dapm_wcache {
index 439aa3ff1f99cd517f28988f80fe2b34c6489ab2..b36511d965c8202c547a9aaef2827273acc83c28 100644 (file)
@@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       struct snd_soc_dapm_update update;
+       struct snd_soc_dapm_update update = { 0 };
        unsigned int stream = e->shift_l;
        unsigned int val, change;
        int reg;
index 5a8d96ec058c5e5464a251d86edcc2920c5e14c7..8877b74b0510fedefe81adc6637c9d72cf9c722f 100644 (file)
@@ -157,7 +157,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
        unsigned short val;
-       struct snd_soc_dapm_update update;
+       struct snd_soc_dapm_update update = { 0 };
        int connect, change;
 
        val = (ucontrol->value.integer.value[0] & mask);
index 557709eac698fb60c6a66d5af9a88bf744e8256c..85f7c5bb8b825fbba0f14d6ed6f8753b454daaa7 100644 (file)
@@ -187,7 +187,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int mixer, mask, shift, old;
-       struct snd_soc_dapm_update update;
+       struct snd_soc_dapm_update update = { 0 };
        bool change;
 
        mixer = mc->shift >> 8;
index e4301ddb1b84e434ce3bbfa5997f20658af30dbd..7e4822185febeac4ae7d2d601f3a76c7659ff924 100644 (file)
@@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int mixer, mask, shift, old;
-       struct snd_soc_dapm_update update;
+       struct snd_soc_dapm_update update = { 0 };
        bool change;
 
        mixer = mc->shift >> 8;
index 3bbe32ee4630479ae55ea22f28690eef538aa75b..27dd02e57b31b0083f00a7864005a8ca867c42fb 100644 (file)
@@ -330,6 +330,11 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
        case snd_soc_dapm_mixer_named_ctl:
                mc = (struct soc_mixer_control *)kcontrol->private_value;
 
+               if (mc->autodisable && snd_soc_volsw_is_stereo(mc))
+                       dev_warn(widget->dapm->dev,
+                                "ASoC: Unsupported stereo autodisable control '%s'\n",
+                                ctrl_name);
+
                if (mc->autodisable) {
                        struct snd_soc_dapm_widget template;
 
@@ -723,7 +728,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 }
 
 /* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
+                                      int nth_path)
 {
        struct soc_mixer_control *mc = (struct soc_mixer_control *)
                p->sink->kcontrol_news[i].private_value;
@@ -736,7 +742,25 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 
        if (reg != SND_SOC_NOPM) {
                soc_dapm_read(p->sink->dapm, reg, &val);
-               val = (val >> shift) & mask;
+               /*
+                * The nth_path argument allows this function to know
+                * which path of a kcontrol it is setting the initial
+                * status for. Ideally this would support any number
+                * of paths and channels. But since kcontrols only come
+                * in mono and stereo variants, we are limited to 2
+                * channels.
+                *
+                * The following code assumes for stereo controls the
+                * first path is the left channel, and all remaining
+                * paths are the right channel.
+                */
+               if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
+                       if (reg != mc->rreg)
+                               soc_dapm_read(p->sink->dapm, mc->rreg, &val);
+                       val = (val >> mc->rshift) & mask;
+               } else {
+                       val = (val >> shift) & mask;
+               }
                if (invert)
                        val = max - val;
                p->connect = !!val;
@@ -749,13 +773,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_path *path, const char *control_name)
 {
-       int i;
+       int i, nth_path = 0;
 
        /* search for mixer kcontrol */
        for (i = 0; i < path->sink->num_kcontrols; i++) {
                if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
                        path->name = path->sink->kcontrol_news[i].name;
-                       dapm_set_mixer_path_status(path, i);
+                       dapm_set_mixer_path_status(path, i, nth_path++);
                        return 0;
                }
        }
@@ -1626,6 +1650,15 @@ static void dapm_widget_update(struct snd_soc_card *card)
                dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
                        w->name, ret);
 
+       if (update->has_second_set) {
+               ret = soc_dapm_update_bits(w->dapm, update->reg2,
+                                          update->mask2, update->val2);
+               if (ret < 0)
+                       dev_err(w->dapm->dev,
+                               "ASoC: %s DAPM update failed: %d\n",
+                               w->name, ret);
+       }
+
        for (wi = 0; wi < wlist->num_widgets; wi++) {
                w = wlist->widgets[wi];
 
@@ -2177,7 +2210,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
 
 /* test and update the power status of a mixer or switch widget */
 static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
-                                  struct snd_kcontrol *kcontrol, int connect)
+                                      struct snd_kcontrol *kcontrol,
+                                      int connect, int rconnect)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
@@ -2186,8 +2220,33 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
 
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
+               /*
+                * Ideally this function should support any number of
+                * paths and channels. But since kcontrols only come
+                * in mono and stereo variants, we are limited to 2
+                * channels.
+                *
+                * The following code assumes for stereo controls the
+                * first path (when 'found == 0') is the left channel,
+                * and all remaining paths (when 'found == 1') are the
+                * right channel.
+                *
+                * A stereo control is signified by a valid 'rconnect'
+                * value, either 0 for unconnected, or >= 0 for connected.
+                * This is chosen instead of using snd_soc_volsw_is_stereo,
+                * so that the behavior of snd_soc_dapm_mixer_update_power
+                * doesn't change even when the kcontrol passed in is
+                * stereo.
+                *
+                * It passes 'connect' as the path connect status for
+                * the left channel, and 'rconnect' for the right
+                * channel.
+                */
+               if (found && rconnect >= 0)
+                       soc_dapm_connect_path(path, rconnect, "mixer update");
+               else
+                       soc_dapm_connect_path(path, connect, "mixer update");
                found = 1;
-               soc_dapm_connect_path(path, connect, "mixer update");
        }
 
        if (found)
@@ -2205,7 +2264,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
        card->update = update;
-       ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+       ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1);
        card->update = NULL;
        mutex_unlock(&card->dapm_mutex);
        if (ret > 0)
@@ -3030,22 +3089,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        int reg = mc->reg;
        unsigned int shift = mc->shift;
        int max = mc->max;
+       unsigned int width = fls(max);
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned int val;
+       unsigned int reg_val, val, rval = 0;
        int ret = 0;
 
-       if (snd_soc_volsw_is_stereo(mc))
-               dev_warn(dapm->dev,
-                        "ASoC: Control '%s' is stereo, which is not supported\n",
-                        kcontrol->id.name);
-
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
        if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
-               ret = soc_dapm_read(dapm, reg, &val);
-               val = (val >> shift) & mask;
+               ret = soc_dapm_read(dapm, reg, &reg_val);
+               val = (reg_val >> shift) & mask;
+
+               if (ret == 0 && reg != mc->rreg)
+                       ret = soc_dapm_read(dapm, mc->rreg, &reg_val);
+
+               if (snd_soc_volsw_is_stereo(mc))
+                       rval = (reg_val >> mc->rshift) & mask;
        } else {
-               val = dapm_kcontrol_get_value(kcontrol);
+               reg_val = dapm_kcontrol_get_value(kcontrol);
+               val = reg_val & mask;
+
+               if (snd_soc_volsw_is_stereo(mc))
+                       rval = (reg_val >> width) & mask;
        }
        mutex_unlock(&card->dapm_mutex);
 
@@ -3057,6 +3122,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        else
                ucontrol->value.integer.value[0] = val;
 
+       if (snd_soc_volsw_is_stereo(mc)) {
+               if (invert)
+                       ucontrol->value.integer.value[1] = max - rval;
+               else
+                       ucontrol->value.integer.value[1] = rval;
+       }
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
@@ -3080,46 +3152,66 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        int reg = mc->reg;
        unsigned int shift = mc->shift;
        int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int width = fls(max);
+       unsigned int mask = (1 << width) - 1;
        unsigned int invert = mc->invert;
-       unsigned int val;
-       int connect, change, reg_change = 0;
-       struct snd_soc_dapm_update update;
+       unsigned int val, rval = 0;
+       int connect, rconnect = -1, change, reg_change = 0;
+       struct snd_soc_dapm_update update = { NULL };
        int ret = 0;
 
-       if (snd_soc_volsw_is_stereo(mc))
-               dev_warn(dapm->dev,
-                        "ASoC: 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;
 
+       if (snd_soc_volsw_is_stereo(mc)) {
+               rval = (ucontrol->value.integer.value[1] & mask);
+               rconnect = !!rval;
+               if (invert)
+                       rval = max - rval;
+       }
+
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-       change = dapm_kcontrol_set_value(kcontrol, val);
+       /* This assumes field width < (bits in unsigned int / 2) */
+       if (width > sizeof(unsigned int) * 8 / 2)
+               dev_warn(dapm->dev,
+                        "ASoC: control %s field width limit exceeded\n",
+                        kcontrol->id.name);
+       change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));
 
        if (reg != SND_SOC_NOPM) {
-               mask = mask << shift;
                val = val << shift;
+               rval = rval << mc->rshift;
+
+               reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
 
-               reg_change = soc_dapm_test_bits(dapm, reg, mask, val);
+               if (snd_soc_volsw_is_stereo(mc))
+                       reg_change |= soc_dapm_test_bits(dapm, mc->rreg,
+                                                        mask << mc->rshift,
+                                                        rval);
        }
 
        if (change || reg_change) {
                if (reg_change) {
+                       if (snd_soc_volsw_is_stereo(mc)) {
+                               update.has_second_set = true;
+                               update.reg2 = mc->rreg;
+                               update.mask2 = mask << mc->rshift;
+                               update.val2 = rval;
+                       }
                        update.kcontrol = kcontrol;
                        update.reg = reg;
-                       update.mask = mask;
+                       update.mask = mask << shift;
                        update.val = val;
                        card->update = &update;
                }
                change |= reg_change;
 
-               ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+               ret = soc_dapm_mixer_update_power(card, kcontrol, connect,
+                                                 rconnect);
 
                card->update = NULL;
        }
@@ -3192,7 +3284,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        unsigned int *item = ucontrol->value.enumerated.item;
        unsigned int val, change, reg_change = 0;
        unsigned int mask;
-       struct snd_soc_dapm_update update;
+       struct snd_soc_dapm_update update = { NULL };
        int ret = 0;
 
        if (item[0] >= e->items)