Message ID | 1396494710-20944-1-git-send-email-aruns@nvidia.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 04/03/2014 05:11 AM, Arun Shamanna Lakshmi wrote: This looks essentially good to me. A few minor issues, once those are fixed things should be good to go. [...] > struct snd_soc_dapm_update { > struct snd_kcontrol *kcontrol; > - int reg; > - int mask; > - int val; > + int reg[3]; > + int mask[3]; > + int val[3]; Make the 3 a define and check against it in the put handler. > + int num_regs; unsigned int > }; [...] > +/* > + * Soc Enum Type > + * > + * @NONE: soc_enum type for SINGLE, DOUBLE or VIRTUAL mux > + * @ONEHOT: soc_enum type for one hot encoding mux > + */ This should be kernel doc style so /** * enum snd_soc_enum_type - Type of the ASoC enum control * @SND_SOC_ENUM_NONE: ... * ... */ > +enum snd_soc_enum_type { > + SND_SOC_ENUM_NONE = 0, I'm not sure if NONE is the right term. Maybe BINARY is better. > + SND_SOC_ENUM_ONEHOT = 1, > +}; > + [...] > diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c > index c8a780d..19b004a 100644 > --- a/sound/soc/soc-dapm.c > +++ b/sound/soc/soc-dapm.c > @@ -511,13 +511,26 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, > const struct snd_kcontrol_new *kcontrol) > { > struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > - unsigned int val, item; > + unsigned int val, item, bit_pos = -1; default for bit_pos should probably be 0. [...] > @@ -1575,8 +1588,12 @@ static void dapm_widget_update(struct snd_soc_card *card) > if (!w) > return; > > - ret = soc_widget_update_bits_locked(w, update->reg, update->mask, > - update->val); > + /* dapm update for multiple registers */ > + for (i = 0; i < update->num_regs; i++) { > + ret |= soc_widget_update_bits_locked(w, update->reg[i], > + update->mask[i], update->val[i]); I'd prefer ret = soc_widget_update_bits_locked(...); if (ret < 0) break; > + } > + > if (ret < 0) > dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", > w->name, ret); [...] > @@ -2984,6 +3002,112 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, > EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); > > /** > + * snd_soc_dapm_get_enum_onehot - dapm enumerated onehot mixer get callback > + * @kcontrol: mixer control > + * @ucontrol: control element information > + * > + * Callback to get the value of a dapm enumerated onehot encoded mixer control > + * > + * Returns 0 for success. > + */ > +int snd_soc_dapm_get_enum_onehot(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); > + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > + unsigned int reg_val, val, bit_pos = -1, reg_idx; Here as well, default for bit_pos should be 0. > + > + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { > + reg_val = snd_soc_read(codec, e->reg[reg_idx]); > + val = reg_val & e->mask[reg_idx]; > + if (val != 0) { > + bit_pos = __ffs(val) + (8 * codec->val_bytes * reg_idx); > + break; > + } > + } > + > + ucontrol->value.enumerated.item[0] = > + snd_soc_enum_val_to_item(e, bit_pos); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_onehot); > + > +/** > + * snd_soc_dapm_put_enum_onehot - dapm enumerated onehot mixer put callback > + * @kcontrol: mixer control > + * @ucontrol: control element information > + * > + * Callback to put the value of a dapm enumerated onehot encoded mixer control > + * > + * Returns 0 for success. > + */ > +int snd_soc_dapm_put_enum_onehot(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); > + struct snd_soc_card *card = codec->card; > + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > + unsigned int *item = ucontrol->value.enumerated.item; > + unsigned int change = 0, reg_idx = 0, value, bit_pos; > + struct snd_soc_dapm_update update; > + int ret = 0, reg_val = 0, i, update_idx = 0; > + > + if (item[0] >= e->items) > + return -EINVAL; > + > + value = snd_soc_enum_item_to_val(e, item[0]); > + > + if (value >= 0) { value is unsigned int, so this is never false. > + /* get the register index and value to set */ > + reg_idx = value / (8 * codec->val_bytes); > + bit_pos = value % (8 * codec->val_bytes); > + reg_val = BIT(bit_pos); > + } > + > + for (i = 0; i < e->num_regs; i++) { > + if (i == reg_idx) { > + change = snd_soc_test_bits(codec, e->reg[i], > + e->mask[i], reg_val); change |= > + /* set the selected register */ > + update.reg[e->num_regs - 1] = e->reg[reg_idx]; > + update.mask[e->num_regs - 1] = e->mask[reg_idx]; > + update.val[e->num_regs - 1] = reg_val; > + } else { > + /* accumulate the change to update the DAPM path > + when none is selected */ > + change |= snd_soc_test_bits(codec, e->reg[i], > + e->mask[i], 0); > + > + /* clear the register when not selected */ > + update.reg[update_idx] = e->reg[i]; > + update.mask[update_idx] = e->mask[i]; > + update.val[update_idx++] = 0; > + } > + } > + > + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); > + > + if (change) { > + update.kcontrol = kcontrol; > + update.num_regs = 3; Should be e->num_regs; > + card->update = &update; > + > + ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); > + > + card->update = NULL; > + } > +
On Thu, Apr 03, 2014 at 10:27:17AM +0200, Lars-Peter Clausen wrote: > On 04/03/2014 05:11 AM, Arun Shamanna Lakshmi wrote: > >+enum snd_soc_enum_type { > >+ SND_SOC_ENUM_NONE = 0, > I'm not sure if NONE is the right term. Maybe BINARY is better. Yes, it's definitely not none.
At Wed, 2 Apr 2014 20:11:50 -0700, Arun Shamanna Lakshmi wrote: > > - Modify soc_enum struct to handle pointers for reg and mask > - Add dapm get and put APIs for multi register mux with one hot encoding > - Update snd_soc_dapm_update struct to support multiple reg update > > Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com> > Signed-off-by: Songhee Baek <sbaek@nvidia.com> I'm a bit late in the game, but I feel a bit uneasy through looking at the whole changes. My primary question is, whether do we really need to share the same struct soc_enum for the onehot type? What makes hard to use a struct soc_enum_onehot for them? You need different individual get/put for each type. We may still need to change soc_dapm_update stuff, but it's different from sharing soc_enum. thanks, Takashi > --- > include/sound/soc-dapm.h | 17 ++++- > include/sound/soc.h | 34 +++++++-- > sound/soc/soc-core.c | 12 ++-- > sound/soc/soc-dapm.c | 174 +++++++++++++++++++++++++++++++++++++++------- > 4 files changed, 197 insertions(+), 40 deletions(-) > > diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h > index ef78f56..ded46732 100644 > --- a/include/sound/soc-dapm.h > +++ b/include/sound/soc-dapm.h > @@ -305,6 +305,12 @@ struct device; > .get = snd_soc_dapm_get_enum_double, \ > .put = snd_soc_dapm_put_enum_double, \ > .private_value = (unsigned long)&xenum } > +#define SOC_DAPM_ENUM_ONEHOT(xname, xenum) \ > +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ > + .info = snd_soc_info_enum_double, \ > + .get = snd_soc_dapm_get_enum_onehot, \ > + .put = snd_soc_dapm_put_enum_onehot, \ > + .private_value = (unsigned long)&xenum } > #define SOC_DAPM_ENUM_VIRT(xname, xenum) \ > SOC_DAPM_ENUM(xname, xenum) > #define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \ > @@ -378,6 +384,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, > struct snd_ctl_elem_value *ucontrol); > int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, > struct snd_ctl_elem_value *ucontrol); > +int snd_soc_dapm_get_enum_onehot(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol); > +int snd_soc_dapm_put_enum_onehot(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol); > int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, > struct snd_ctl_elem_info *uinfo); > int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, > @@ -590,9 +600,10 @@ struct snd_soc_dapm_widget { > > struct snd_soc_dapm_update { > struct snd_kcontrol *kcontrol; > - int reg; > - int mask; > - int val; > + int reg[3]; > + int mask[3]; > + int val[3]; > + int num_regs; > }; > > /* DAPM context */ > diff --git a/include/sound/soc.h b/include/sound/soc.h > index 0b83168..add326a 100644 > --- a/include/sound/soc.h > +++ b/include/sound/soc.h > @@ -177,18 +177,24 @@ > {.reg = xreg, .min = xmin, .max = xmax, \ > .platform_max = xmax} } > #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \ > -{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ > +{ .reg = &(int){(xreg)}, .shift_l = xshift_l, .shift_r = xshift_r, \ > .items = xitems, .texts = xtexts, \ > - .mask = xitems ? roundup_pow_of_two(xitems) - 1 : 0} > + .mask = &(unsigned int){(xitems ? roundup_pow_of_two(xitems) - 1 : 0)}, \ > + .num_regs = 1, .type = SND_SOC_ENUM_NONE } > #define SOC_ENUM_SINGLE(xreg, xshift, xitems, xtexts) \ > SOC_ENUM_DOUBLE(xreg, xshift, xshift, xitems, xtexts) > #define SOC_ENUM_SINGLE_EXT(xitems, xtexts) \ > { .items = xitems, .texts = xtexts } > #define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xitems, xtexts, xvalues) \ > -{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ > - .mask = xmask, .items = xitems, .texts = xtexts, .values = xvalues} > +{ .reg = &(int){(xreg)}, .shift_l = xshift_l, .shift_r = xshift_r, \ > + .mask = &(unsigned int){(xmask)}, .items = xitems, .texts = xtexts, \ > + .values = xvalues, .num_regs = 1, .type = SND_SOC_ENUM_NONE } > #define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xnitmes, xtexts, xvalues) \ > SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xnitmes, xtexts, xvalues) > +#define SOC_VALUE_ENUM_ONEHOT(xregs, xmasks, xnum_regs, xitems, xtexts, xvalues) \ > +{ .reg = xregs, .mask = xmasks, .num_regs = xnum_regs, \ > + .items = xitems, .texts = xtexts, .values = xvalues, \ > + .type = SND_SOC_ENUM_ONEHOT } > #define SOC_ENUM_SINGLE_VIRT(xitems, xtexts) \ > SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, xitems, xtexts) > #define SOC_ENUM(xname, xenum) \ > @@ -293,6 +299,9 @@ > #define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \ > const struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \ > ARRAY_SIZE(xtexts), xtexts, xvalues) > +#define SOC_VALUE_ENUM_ONEHOT_DECL(name, xregs, xmasks, xnum_regs, xtexts, xvalues) \ > + const struct soc_enum name = SOC_VALUE_ENUM_ONEHOT(xregs, xmasks, xnum_regs, \ > + ARRAY_SIZE(xtexts), xtexts, xvalues) > #define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \ > SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) > #define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \ > @@ -326,6 +335,17 @@ enum snd_soc_bias_level { > SND_SOC_BIAS_ON = 3, > }; > > +/* > + * Soc Enum Type > + * > + * @NONE: soc_enum type for SINGLE, DOUBLE or VIRTUAL mux > + * @ONEHOT: soc_enum type for one hot encoding mux > + */ > +enum snd_soc_enum_type { > + SND_SOC_ENUM_NONE = 0, > + SND_SOC_ENUM_ONEHOT = 1, > +}; > + > struct device_node; > struct snd_jack; > struct snd_soc_card; > @@ -1098,13 +1118,15 @@ struct soc_mreg_control { > > /* enumerated kcontrol */ > struct soc_enum { > - int reg; > + int *reg; > unsigned char shift_l; > unsigned char shift_r; > unsigned int items; > - unsigned int mask; > + unsigned int *mask; > const char * const *texts; > const unsigned int *values; > + enum snd_soc_enum_type type; > + unsigned int num_regs; > }; > > /** > diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c > index caebd63..cf29722 100644 > --- a/sound/soc/soc-core.c > +++ b/sound/soc/soc-core.c > @@ -2601,12 +2601,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, > unsigned int val, item; > unsigned int reg_val; > > - reg_val = snd_soc_read(codec, e->reg); > - val = (reg_val >> e->shift_l) & e->mask; > + reg_val = snd_soc_read(codec, e->reg[0]); > + val = (reg_val >> e->shift_l) & e->mask[0]; > item = snd_soc_enum_val_to_item(e, val); > ucontrol->value.enumerated.item[0] = item; > if (e->shift_l != e->shift_r) { > - val = (reg_val >> e->shift_l) & e->mask; > + val = (reg_val >> e->shift_l) & e->mask[0]; > item = snd_soc_enum_val_to_item(e, val); > ucontrol->value.enumerated.item[1] = item; > } > @@ -2636,15 +2636,15 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, > if (item[0] >= e->items) > return -EINVAL; > val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; > - mask = e->mask << e->shift_l; > + mask = e->mask[0] << e->shift_l; > if (e->shift_l != e->shift_r) { > if (item[1] >= e->items) > return -EINVAL; > val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; > - mask |= e->mask << e->shift_r; > + mask |= e->mask[0] << e->shift_r; > } > > - return snd_soc_update_bits_locked(codec, e->reg, mask, val); > + return snd_soc_update_bits_locked(codec, e->reg[0], mask, val); > } > EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); > > diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c > index c8a780d..19b004a 100644 > --- a/sound/soc/soc-dapm.c > +++ b/sound/soc/soc-dapm.c > @@ -511,13 +511,26 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, > const struct snd_kcontrol_new *kcontrol) > { > struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > - unsigned int val, item; > + unsigned int val, item, bit_pos = -1; > int i; > > - if (e->reg != SND_SOC_NOPM) { > - soc_widget_read(dest, e->reg, &val); > - val = (val >> e->shift_l) & e->mask; > - item = snd_soc_enum_val_to_item(e, val); > + if (e->reg[0] != SND_SOC_NOPM) { > + if (e->type == SND_SOC_ENUM_ONEHOT) { > + for (i = 0; i < e->num_regs; i++) { > + soc_widget_read(dest, e->reg[i], &val); > + val = val & e->mask[i]; > + if (val != 0) { > + bit_pos = __ffs(val) + > + (8 * dest->codec->val_bytes * i); > + break; > + } > + } > + item = snd_soc_enum_val_to_item(e, bit_pos); > + } else { > + soc_widget_read(dest, e->reg[0], &val); > + val = (val >> e->shift_l) & e->mask[0]; > + item = snd_soc_enum_val_to_item(e, val); > + } > } else { > /* since a virtual mux has no backing registers to > * decide which path to connect, it will try to match > @@ -1553,8 +1566,8 @@ static void dapm_widget_update(struct snd_soc_card *card) > struct snd_soc_dapm_update *update = card->update; > struct snd_soc_dapm_widget_list *wlist; > struct snd_soc_dapm_widget *w = NULL; > - unsigned int wi; > - int ret; > + unsigned int wi, i; > + int ret = 0; > > if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) > return; > @@ -1575,8 +1588,12 @@ static void dapm_widget_update(struct snd_soc_card *card) > if (!w) > return; > > - ret = soc_widget_update_bits_locked(w, update->reg, update->mask, > - update->val); > + /* dapm update for multiple registers */ > + for (i = 0; i < update->num_regs; i++) { > + ret |= soc_widget_update_bits_locked(w, update->reg[i], > + update->mask[i], update->val[i]); > + } > + > if (ret < 0) > dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", > w->name, ret); > @@ -2866,10 +2883,10 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, > if (change) { > if (reg != SND_SOC_NOPM) { > update.kcontrol = kcontrol; > - update.reg = reg; > - update.mask = mask; > - update.val = val; > - > + update.reg[0] = reg; > + update.mask[0] = mask; > + update.val[0] = val; > + update.num_regs = 1; > card->update = &update; > } > > @@ -2903,15 +2920,15 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, > struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > unsigned int reg_val, val; > > - if (e->reg != SND_SOC_NOPM) > - reg_val = snd_soc_read(codec, e->reg); > + if (e->reg[0] != SND_SOC_NOPM) > + reg_val = snd_soc_read(codec, e->reg[0]); > else > reg_val = dapm_kcontrol_get_value(kcontrol); > > - val = (reg_val >> e->shift_l) & e->mask; > + val = (reg_val >> e->shift_l) & e->mask[0]; > ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); > if (e->shift_l != e->shift_r) { > - val = (reg_val >> e->shift_r) & e->mask; > + val = (reg_val >> e->shift_r) & e->mask[0]; > val = snd_soc_enum_val_to_item(e, val); > ucontrol->value.enumerated.item[1] = val; > } > @@ -2945,27 +2962,28 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, > return -EINVAL; > > val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; > - mask = e->mask << e->shift_l; > + mask = e->mask[0] << e->shift_l; > if (e->shift_l != e->shift_r) { > if (item[1] > e->items) > return -EINVAL; > val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l; > - mask |= e->mask << e->shift_r; > + mask |= e->mask[0] << e->shift_r; > } > > mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); > > - if (e->reg != SND_SOC_NOPM) > - change = snd_soc_test_bits(codec, e->reg, mask, val); > + if (e->reg[0] != SND_SOC_NOPM) > + change = snd_soc_test_bits(codec, e->reg[0], mask, val); > else > change = dapm_kcontrol_set_value(kcontrol, val); > > if (change) { > - if (e->reg != SND_SOC_NOPM) { > + if (e->reg[0] != SND_SOC_NOPM) { > update.kcontrol = kcontrol; > - update.reg = e->reg; > - update.mask = mask; > - update.val = val; > + update.reg[0] = e->reg[0]; > + update.mask[0] = mask; > + update.val[0] = val; > + update.num_regs = 1; > card->update = &update; > } > > @@ -2984,6 +3002,112 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, > EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); > > /** > + * snd_soc_dapm_get_enum_onehot - dapm enumerated onehot mixer get callback > + * @kcontrol: mixer control > + * @ucontrol: control element information > + * > + * Callback to get the value of a dapm enumerated onehot encoded mixer control > + * > + * Returns 0 for success. > + */ > +int snd_soc_dapm_get_enum_onehot(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); > + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > + unsigned int reg_val, val, bit_pos = -1, reg_idx; > + > + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { > + reg_val = snd_soc_read(codec, e->reg[reg_idx]); > + val = reg_val & e->mask[reg_idx]; > + if (val != 0) { > + bit_pos = __ffs(val) + (8 * codec->val_bytes * reg_idx); > + break; > + } > + } > + > + ucontrol->value.enumerated.item[0] = > + snd_soc_enum_val_to_item(e, bit_pos); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_onehot); > + > +/** > + * snd_soc_dapm_put_enum_onehot - dapm enumerated onehot mixer put callback > + * @kcontrol: mixer control > + * @ucontrol: control element information > + * > + * Callback to put the value of a dapm enumerated onehot encoded mixer control > + * > + * Returns 0 for success. > + */ > +int snd_soc_dapm_put_enum_onehot(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); > + struct snd_soc_card *card = codec->card; > + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; > + unsigned int *item = ucontrol->value.enumerated.item; > + unsigned int change = 0, reg_idx = 0, value, bit_pos; > + struct snd_soc_dapm_update update; > + int ret = 0, reg_val = 0, i, update_idx = 0; > + > + if (item[0] >= e->items) > + return -EINVAL; > + > + value = snd_soc_enum_item_to_val(e, item[0]); > + > + if (value >= 0) { > + /* get the register index and value to set */ > + reg_idx = value / (8 * codec->val_bytes); > + bit_pos = value % (8 * codec->val_bytes); > + reg_val = BIT(bit_pos); > + } > + > + for (i = 0; i < e->num_regs; i++) { > + if (i == reg_idx) { > + change = snd_soc_test_bits(codec, e->reg[i], > + e->mask[i], reg_val); > + /* set the selected register */ > + update.reg[e->num_regs - 1] = e->reg[reg_idx]; > + update.mask[e->num_regs - 1] = e->mask[reg_idx]; > + update.val[e->num_regs - 1] = reg_val; > + } else { > + /* accumulate the change to update the DAPM path > + when none is selected */ > + change |= snd_soc_test_bits(codec, e->reg[i], > + e->mask[i], 0); > + > + /* clear the register when not selected */ > + update.reg[update_idx] = e->reg[i]; > + update.mask[update_idx] = e->mask[i]; > + update.val[update_idx++] = 0; > + } > + } > + > + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); > + > + if (change) { > + update.kcontrol = kcontrol; > + update.num_regs = 3; > + card->update = &update; > + > + ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); > + > + card->update = NULL; > + } > + > + mutex_unlock(&card->dapm_mutex); > + > + if (ret > 0) > + soc_dpcm_runtime_update(card); > + > + return change; > +} > +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_onehot); > + > +/** > * snd_soc_dapm_info_pin_switch - Info for a pin switch > * > * @kcontrol: mixer control > -- > 1.7.9.5 >
On Thu, Apr 03, 2014 at 11:47:15AM +0200, Takashi Iwai wrote: > I'm a bit late in the game, but I feel a bit uneasy through looking > at the whole changes. My primary question is, whether do we really > need to share the same struct soc_enum for the onehot type? What > makes hard to use a struct soc_enum_onehot for them? You need > different individual get/put for each type. We may still need to > change soc_dapm_update stuff, but it's different from sharing > soc_enum. Indeed, I had thought this was where the discussion was heading - not looked at this version of the patch yet.
On 04/03/2014 11:53 AM, Mark Brown wrote: > On Thu, Apr 03, 2014 at 11:47:15AM +0200, Takashi Iwai wrote: > >> I'm a bit late in the game, but I feel a bit uneasy through looking >> at the whole changes. My primary question is, whether do we really >> need to share the same struct soc_enum for the onehot type? What >> makes hard to use a struct soc_enum_onehot for them? You need >> different individual get/put for each type. We may still need to >> change soc_dapm_update stuff, but it's different from sharing >> soc_enum. > > Indeed, I had thought this was where the discussion was heading - not > looked at this version of the patch yet. > It would be nice, but it also requires some slight restructuring. The issue we have right now is that there is strictly speaking a bit of a layering violation. The DAPM widgets should not need to know how the kcontrols that are attached to the widget do their IO. What we essentially do in dapm_connect_mux() (and also dapm_connect_mixer) is an open-coded version of the controls get handler. Replacing that by calling the get handler instead should allow us to use different structs for enums and onehot enums. - Lars
> -----Original Message----- > From: Lars-Peter Clausen [mailto:lars@metafoo.de] > Sent: Thursday, April 03, 2014 1:27 AM > To: Arun Shamanna Lakshmi > Cc: lgirdwood@gmail.com; broonie@kernel.org; swarren@wwwdotorg.org; > perex@perex.cz; tiwai@suse.de; alsa- devel@alsa-project.org; > linux-kernel@vger.kernel.org; Songhee Baek > Subject: Re: [PATCH] ASoC: dapm: Add support for multi register mux > > On 04/03/2014 05:11 AM, Arun Shamanna Lakshmi wrote: > > This looks essentially good to me. A few minor issues, once those are > fixed things should be good to go. > > [...] > > @@ -2984,6 +3002,112 @@ int > snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, > > EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); > > > > /** > > + * snd_soc_dapm_get_enum_onehot - dapm enumerated onehot > mixer get > > +callback > > + * @kcontrol: mixer control > > + * @ucontrol: control element information > > + * > > + * Callback to get the value of a dapm enumerated onehot encoded > > +mixer control > > + * > > + * Returns 0 for success. > > + */ > > +int snd_soc_dapm_get_enum_onehot(struct snd_kcontrol *kcontrol, > > + struct snd_ctl_elem_value *ucontrol) { > > + struct snd_soc_codec *codec = > snd_soc_dapm_kcontrol_codec(kcontrol); > > + struct soc_enum *e = (struct soc_enum *)kcontrol- > >private_value; > > + unsigned int reg_val, val, bit_pos = -1, reg_idx; > > Here as well, default for bit_pos should be 0. This means when 'None' of the options are selected, by default, it enumerates to 0. Since we are using __ffs, BIT(0) of Register-0 also enumerates to 0. That's the reason why I used just ffs in the first place. Let me know your opinion. My value table looks like below. #define MUX_VALUE(npart, nbit) (nbit + 32 * npart) static const int mux_values[] = { 0, MUX_VALUE(0, 0), . . . MUX_VALUE(0, 31), /* above inputs are for part0 mux */ MUX_VALUE(1, 0), . . . MUX_VALUE(1, 31), /* above inputs are for part1 mux */ MUX_VALUE(2, 0), . . . MUX_VALUE(2, 31), /* above inputs are for part2 mux */ }; > > > + > > + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { > > + reg_val = snd_soc_read(codec, e->reg[reg_idx]); > > + val = reg_val & e->mask[reg_idx]; > > + if (val != 0) { > > + bit_pos = __ffs(val) + (8 * codec->val_bytes * > reg_idx); > > + break; > > + } > > + } > > + > > + ucontrol->value.enumerated.item[0] = > > + snd_soc_enum_val_to_item(e, bit_pos); > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_onehot); > > + > > +/** > > +
On 04/03/2014 10:11 PM, Arun Shamanna Lakshmi wrote: [...] >> Here as well, default for bit_pos should be 0. > > This means when 'None' of the options are selected, by default, it > enumerates to 0. Since we are using __ffs, BIT(0) of Register-0 also > enumerates to 0. That's the reason why I used just ffs in the first place. > Let me know your opinion. My value table looks like below. > > #define MUX_VALUE(npart, nbit) (nbit + 32 * npart) > static const int mux_values[] = { > 0, > MUX_VALUE(0, 0), > . > . > . > MUX_VALUE(0, 31), > /* above inputs are for part0 mux */ > MUX_VALUE(1, 0), > . > . > . > MUX_VALUE(1, 31), > /* above inputs are for part1 mux */ > MUX_VALUE(2, 0), > . > . > . > MUX_VALUE(2, 31), > /* above inputs are for part2 mux */ > }; Ok, so having none of the input selected should be a valid user selectable option?
> -----Original Message----- > From: Lars-Peter Clausen [mailto:lars@metafoo.de] > Sent: Friday, April 04, 2014 12:32 AM > To: Arun Shamanna Lakshmi > Cc: lgirdwood@gmail.com; broonie@kernel.org; > swarren@wwwdotorg.org; perex@perex.cz; tiwai@suse.de; alsa- > devel@alsa-project.org; linux-kernel@vger.kernel.org; Songhee Baek > Subject: Re: [PATCH] ASoC: dapm: Add support for multi register mux > > On 04/03/2014 10:11 PM, Arun Shamanna Lakshmi wrote: > [...] > >> Here as well, default for bit_pos should be 0. > > > > This means when 'None' of the options are selected, by default, it > > enumerates to 0. Since we are using __ffs, BIT(0) of Register-0 also > > enumerates to 0. That's the reason why I used just ffs in the first place. > > Let me know your opinion. My value table looks like below. > > > > #define MUX_VALUE(npart, nbit) (nbit + 32 * npart) > > static const int mux_values[] = { > > 0, > > MUX_VALUE(0, 0), > > . > > . > > . > > MUX_VALUE(0, 31), > > /* above inputs are for part0 mux */ > > MUX_VALUE(1, 0), > > . > > . > > . > > MUX_VALUE(1, 31), > > /* above inputs are for part1 mux */ > > MUX_VALUE(2, 0), > > . > > . > > . > > MUX_VALUE(2, 31), > > /* above inputs are for part2 mux */ > > }; > > Ok, so having none of the input selected should be a valid user selectable > option? Yes. If 'None' is selected, it goes and clears the register. So, can we have ffs( ) instead of __ffs( ) ? It would fix this case. - Arun
On 04/04/2014 09:34 AM, Arun Shamanna Lakshmi wrote: > >> -----Original Message----- >> From: Lars-Peter Clausen [mailto:lars@metafoo.de] >> Sent: Friday, April 04, 2014 12:32 AM >> To: Arun Shamanna Lakshmi >> Cc: lgirdwood@gmail.com; broonie@kernel.org; >> swarren@wwwdotorg.org; perex@perex.cz; tiwai@suse.de; alsa- >> devel@alsa-project.org; linux-kernel@vger.kernel.org; Songhee Baek >> Subject: Re: [PATCH] ASoC: dapm: Add support for multi register mux >> >> On 04/03/2014 10:11 PM, Arun Shamanna Lakshmi wrote: >> [...] >>>> Here as well, default for bit_pos should be 0. >>> >>> This means when 'None' of the options are selected, by default, it >>> enumerates to 0. Since we are using __ffs, BIT(0) of Register-0 also >>> enumerates to 0. That's the reason why I used just ffs in the first place. >>> Let me know your opinion. My value table looks like below. >>> >>> #define MUX_VALUE(npart, nbit) (nbit + 32 * npart) >>> static const int mux_values[] = { >>> 0, >>> MUX_VALUE(0, 0), >>> . >>> . >>> . >>> MUX_VALUE(0, 31), >>> /* above inputs are for part0 mux */ >>> MUX_VALUE(1, 0), >>> . >>> . >>> . >>> MUX_VALUE(1, 31), >>> /* above inputs are for part1 mux */ >>> MUX_VALUE(2, 0), >>> . >>> . >>> . >>> MUX_VALUE(2, 31), >>> /* above inputs are for part2 mux */ >>> }; >> >> Ok, so having none of the input selected should be a valid user selectable >> option? > > Yes. If 'None' is selected, it goes and clears the register. So, can we have ffs( ) > instead of __ffs( ) ? It would fix this case. Yes, but you need to make sure to handle it also correctly in the put handler, since all of the registers need to be written to 0 in that case.
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ef78f56..ded46732 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -305,6 +305,12 @@ struct device; .get = snd_soc_dapm_get_enum_double, \ .put = snd_soc_dapm_put_enum_double, \ .private_value = (unsigned long)&xenum } +#define SOC_DAPM_ENUM_ONEHOT(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_onehot, \ + .put = snd_soc_dapm_put_enum_onehot, \ + .private_value = (unsigned long)&xenum } #define SOC_DAPM_ENUM_VIRT(xname, xenum) \ SOC_DAPM_ENUM(xname, xenum) #define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \ @@ -378,6 +384,10 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_get_enum_onehot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_enum_onehot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, @@ -590,9 +600,10 @@ struct snd_soc_dapm_widget { struct snd_soc_dapm_update { struct snd_kcontrol *kcontrol; - int reg; - int mask; - int val; + int reg[3]; + int mask[3]; + int val[3]; + int num_regs; }; /* DAPM context */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168..add326a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -177,18 +177,24 @@ {.reg = xreg, .min = xmin, .max = xmax, \ .platform_max = xmax} } #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \ -{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ +{ .reg = &(int){(xreg)}, .shift_l = xshift_l, .shift_r = xshift_r, \ .items = xitems, .texts = xtexts, \ - .mask = xitems ? roundup_pow_of_two(xitems) - 1 : 0} + .mask = &(unsigned int){(xitems ? roundup_pow_of_two(xitems) - 1 : 0)}, \ + .num_regs = 1, .type = SND_SOC_ENUM_NONE } #define SOC_ENUM_SINGLE(xreg, xshift, xitems, xtexts) \ SOC_ENUM_DOUBLE(xreg, xshift, xshift, xitems, xtexts) #define SOC_ENUM_SINGLE_EXT(xitems, xtexts) \ { .items = xitems, .texts = xtexts } #define SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xitems, xtexts, xvalues) \ -{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ - .mask = xmask, .items = xitems, .texts = xtexts, .values = xvalues} +{ .reg = &(int){(xreg)}, .shift_l = xshift_l, .shift_r = xshift_r, \ + .mask = &(unsigned int){(xmask)}, .items = xitems, .texts = xtexts, \ + .values = xvalues, .num_regs = 1, .type = SND_SOC_ENUM_NONE } #define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xnitmes, xtexts, xvalues) \ SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xnitmes, xtexts, xvalues) +#define SOC_VALUE_ENUM_ONEHOT(xregs, xmasks, xnum_regs, xitems, xtexts, xvalues) \ +{ .reg = xregs, .mask = xmasks, .num_regs = xnum_regs, \ + .items = xitems, .texts = xtexts, .values = xvalues, \ + .type = SND_SOC_ENUM_ONEHOT } #define SOC_ENUM_SINGLE_VIRT(xitems, xtexts) \ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, xitems, xtexts) #define SOC_ENUM(xname, xenum) \ @@ -293,6 +299,9 @@ #define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \ const struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \ ARRAY_SIZE(xtexts), xtexts, xvalues) +#define SOC_VALUE_ENUM_ONEHOT_DECL(name, xregs, xmasks, xnum_regs, xtexts, xvalues) \ + const struct soc_enum name = SOC_VALUE_ENUM_ONEHOT(xregs, xmasks, xnum_regs, \ + ARRAY_SIZE(xtexts), xtexts, xvalues) #define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \ SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) #define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \ @@ -326,6 +335,17 @@ enum snd_soc_bias_level { SND_SOC_BIAS_ON = 3, }; +/* + * Soc Enum Type + * + * @NONE: soc_enum type for SINGLE, DOUBLE or VIRTUAL mux + * @ONEHOT: soc_enum type for one hot encoding mux + */ +enum snd_soc_enum_type { + SND_SOC_ENUM_NONE = 0, + SND_SOC_ENUM_ONEHOT = 1, +}; + struct device_node; struct snd_jack; struct snd_soc_card; @@ -1098,13 +1118,15 @@ struct soc_mreg_control { /* enumerated kcontrol */ struct soc_enum { - int reg; + int *reg; unsigned char shift_l; unsigned char shift_r; unsigned int items; - unsigned int mask; + unsigned int *mask; const char * const *texts; const unsigned int *values; + enum snd_soc_enum_type type; + unsigned int num_regs; }; /** diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index caebd63..cf29722 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2601,12 +2601,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, item; unsigned int reg_val; - reg_val = snd_soc_read(codec, e->reg); - val = (reg_val >> e->shift_l) & e->mask; + reg_val = snd_soc_read(codec, e->reg[0]); + val = (reg_val >> e->shift_l) & e->mask[0]; item = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[0] = item; if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_l) & e->mask; + val = (reg_val >> e->shift_l) & e->mask[0]; item = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[1] = item; } @@ -2636,15 +2636,15 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, if (item[0] >= e->items) return -EINVAL; val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - mask = e->mask << e->shift_l; + mask = e->mask[0] << e->shift_l; if (e->shift_l != e->shift_r) { if (item[1] >= e->items) return -EINVAL; val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r; - mask |= e->mask << e->shift_r; + mask |= e->mask[0] << e->shift_r; } - return snd_soc_update_bits_locked(codec, e->reg, mask, val); + return snd_soc_update_bits_locked(codec, e->reg[0], mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_enum_double); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c8a780d..19b004a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -511,13 +511,26 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, const struct snd_kcontrol_new *kcontrol) { struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int val, item; + unsigned int val, item, bit_pos = -1; int i; - if (e->reg != SND_SOC_NOPM) { - soc_widget_read(dest, e->reg, &val); - val = (val >> e->shift_l) & e->mask; - item = snd_soc_enum_val_to_item(e, val); + if (e->reg[0] != SND_SOC_NOPM) { + if (e->type == SND_SOC_ENUM_ONEHOT) { + for (i = 0; i < e->num_regs; i++) { + soc_widget_read(dest, e->reg[i], &val); + val = val & e->mask[i]; + if (val != 0) { + bit_pos = __ffs(val) + + (8 * dest->codec->val_bytes * i); + break; + } + } + item = snd_soc_enum_val_to_item(e, bit_pos); + } else { + soc_widget_read(dest, e->reg[0], &val); + val = (val >> e->shift_l) & e->mask[0]; + item = snd_soc_enum_val_to_item(e, val); + } } else { /* since a virtual mux has no backing registers to * decide which path to connect, it will try to match @@ -1553,8 +1566,8 @@ static void dapm_widget_update(struct snd_soc_card *card) struct snd_soc_dapm_update *update = card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; - unsigned int wi; - int ret; + unsigned int wi, i; + int ret = 0; if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) return; @@ -1575,8 +1588,12 @@ static void dapm_widget_update(struct snd_soc_card *card) if (!w) return; - ret = soc_widget_update_bits_locked(w, update->reg, update->mask, - update->val); + /* dapm update for multiple registers */ + for (i = 0; i < update->num_regs; i++) { + ret |= soc_widget_update_bits_locked(w, update->reg[i], + update->mask[i], update->val[i]); + } + if (ret < 0) dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); @@ -2866,10 +2883,10 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, if (change) { if (reg != SND_SOC_NOPM) { update.kcontrol = kcontrol; - update.reg = reg; - update.mask = mask; - update.val = val; - + update.reg[0] = reg; + update.mask[0] = mask; + update.val[0] = val; + update.num_regs = 1; card->update = &update; } @@ -2903,15 +2920,15 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val; - if (e->reg != SND_SOC_NOPM) - reg_val = snd_soc_read(codec, e->reg); + if (e->reg[0] != SND_SOC_NOPM) + reg_val = snd_soc_read(codec, e->reg[0]); else reg_val = dapm_kcontrol_get_value(kcontrol); - val = (reg_val >> e->shift_l) & e->mask; + val = (reg_val >> e->shift_l) & e->mask[0]; ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(e, val); if (e->shift_l != e->shift_r) { - val = (reg_val >> e->shift_r) & e->mask; + val = (reg_val >> e->shift_r) & e->mask[0]; val = snd_soc_enum_val_to_item(e, val); ucontrol->value.enumerated.item[1] = val; } @@ -2945,27 +2962,28 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, return -EINVAL; val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - mask = e->mask << e->shift_l; + mask = e->mask[0] << e->shift_l; if (e->shift_l != e->shift_r) { if (item[1] > e->items) return -EINVAL; val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l; - mask |= e->mask << e->shift_r; + mask |= e->mask[0] << e->shift_r; } mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - if (e->reg != SND_SOC_NOPM) - change = snd_soc_test_bits(codec, e->reg, mask, val); + if (e->reg[0] != SND_SOC_NOPM) + change = snd_soc_test_bits(codec, e->reg[0], mask, val); else change = dapm_kcontrol_set_value(kcontrol, val); if (change) { - if (e->reg != SND_SOC_NOPM) { + if (e->reg[0] != SND_SOC_NOPM) { update.kcontrol = kcontrol; - update.reg = e->reg; - update.mask = mask; - update.val = val; + update.reg[0] = e->reg[0]; + update.mask[0] = mask; + update.val[0] = val; + update.num_regs = 1; card->update = &update; } @@ -2984,6 +3002,112 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); /** + * snd_soc_dapm_get_enum_onehot - dapm enumerated onehot mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a dapm enumerated onehot encoded mixer control + * + * Returns 0 for success. + */ +int snd_soc_dapm_get_enum_onehot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int reg_val, val, bit_pos = -1, reg_idx; + + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { + reg_val = snd_soc_read(codec, e->reg[reg_idx]); + val = reg_val & e->mask[reg_idx]; + if (val != 0) { + bit_pos = __ffs(val) + (8 * codec->val_bytes * reg_idx); + break; + } + } + + ucontrol->value.enumerated.item[0] = + snd_soc_enum_val_to_item(e, bit_pos); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_onehot); + +/** + * snd_soc_dapm_put_enum_onehot - dapm enumerated onehot mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to put the value of a dapm enumerated onehot encoded mixer control + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_enum_onehot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->card; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int change = 0, reg_idx = 0, value, bit_pos; + struct snd_soc_dapm_update update; + int ret = 0, reg_val = 0, i, update_idx = 0; + + if (item[0] >= e->items) + return -EINVAL; + + value = snd_soc_enum_item_to_val(e, item[0]); + + if (value >= 0) { + /* get the register index and value to set */ + reg_idx = value / (8 * codec->val_bytes); + bit_pos = value % (8 * codec->val_bytes); + reg_val = BIT(bit_pos); + } + + for (i = 0; i < e->num_regs; i++) { + if (i == reg_idx) { + change = snd_soc_test_bits(codec, e->reg[i], + e->mask[i], reg_val); + /* set the selected register */ + update.reg[e->num_regs - 1] = e->reg[reg_idx]; + update.mask[e->num_regs - 1] = e->mask[reg_idx]; + update.val[e->num_regs - 1] = reg_val; + } else { + /* accumulate the change to update the DAPM path + when none is selected */ + change |= snd_soc_test_bits(codec, e->reg[i], + e->mask[i], 0); + + /* clear the register when not selected */ + update.reg[update_idx] = e->reg[i]; + update.mask[update_idx] = e->mask[i]; + update.val[update_idx++] = 0; + } + } + + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + if (change) { + update.kcontrol = kcontrol; + update.num_regs = 3; + card->update = &update; + + ret = soc_dapm_mux_update_power(card, kcontrol, item[0], e); + + card->update = NULL; + } + + mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + + return change; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_onehot); + +/** * snd_soc_dapm_info_pin_switch - Info for a pin switch * * @kcontrol: mixer control