[alsa-devel] [PATCH] ASoC: Add support for multi register mux
If the mux uses 1 bit position per input, and requires to set one single bit at a time, then an N bit register can support up to N inputs. In more recent Tegra chips, we have at least greater than 64 inputs which requires at least 2 .reg fields in struct soc_enum.
Signed-off-by: Arun Shamanna Lakshmi aruns@nvidia.com Signed-off-by: Songhee Baek sbaek@nvidia.com --- include/sound/soc-dapm.h | 20 +++++- include/sound/soc.h | 44 ++++++++++--- sound/soc/soc-core.c | 118 +++++++++++++++++++++++++++++++++-- sound/soc/soc-dapm.c | 155 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 300 insertions(+), 37 deletions(-)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ef78f56..324de75 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -315,6 +315,12 @@ struct device; .private_value = (unsigned long)&xenum } #define SOC_DAPM_VALUE_ENUM(xname, xenum) \ SOC_DAPM_ENUM(xname, xenum) +#define SOC_DAPM_VALUE_ENUM_WIDE(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_wide, \ + .get = snd_soc_dapm_get_value_enum_wide, \ + .put = snd_soc_dapm_put_value_enum_wide, \ + .private_value = (unsigned long)&xenum } #define SOC_DAPM_PIN_SWITCH(xname) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \ .info = snd_soc_dapm_info_pin_switch, \ @@ -352,6 +358,9 @@ struct device; /* regulator widget flags */ #define SND_SOC_DAPM_REGULATOR_BYPASS 0x1 /* bypass when disabled */
+/* maximum number of registers to update */ +#define SOC_DAPM_UPDATE_MAX_REGS 3 + struct snd_soc_dapm_widget; enum snd_soc_dapm_type; struct snd_soc_dapm_path; @@ -378,6 +387,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_value_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_dapm_put_value_enum_wide(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 +603,10 @@ struct snd_soc_dapm_widget {
struct snd_soc_dapm_update { struct snd_kcontrol *kcontrol; - int reg; - int mask; - int val; + int reg[SOC_DAPM_UPDATE_MAX_REGS]; + int mask[SOC_DAPM_UPDATE_MAX_REGS]; + int val[SOC_DAPM_UPDATE_MAX_REGS]; + int num_regs; };
/* DAPM context */ diff --git a/include/sound/soc.h b/include/sound/soc.h index 0b83168..67097c6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -177,16 +177,22 @@ {.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[0] = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ .items = xitems, .texts = xtexts, \ - .mask = xitems ? roundup_pow_of_two(xitems) - 1 : 0} + .mask[0] = xitems ? roundup_pow_of_two(xitems) - 1 : 0, .num_regs = 1 } #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_TRIPLE(reg1, reg2, reg3, mask1, mask2, mask3, \ + nreg, nmax, xtexts, xvalues) \ +{ .reg[0] = reg1, .reg[1] = reg2, .reg[2] = reg3, \ + .mask[0] = mask1, .mask[1] = mask2, .mask[2] = mask3, \ + .num_regs = nreg, .items = nmax, .texts = xtexts, .values = xvalues } #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[0] = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ + .mask[0] = xmask, .items = xitems, .texts = xtexts, .values = xvalues,\ + .num_regs = 1 } #define SOC_VALUE_ENUM_SINGLE(xreg, xshift, xmask, xnitmes, xtexts, xvalues) \ SOC_VALUE_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xnitmes, xtexts, xvalues) #define SOC_ENUM_SINGLE_VIRT(xitems, xtexts) \ @@ -198,6 +204,12 @@ .private_value = (unsigned long)&xenum } #define SOC_VALUE_ENUM(xname, xenum) \ SOC_ENUM(xname, xenum) +#define SOC_VALUE_ENUM_WIDE(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\ + .info = snd_soc_info_enum_wide, \ + .get = snd_soc_get_value_enum_wide, \ + .put = snd_soc_put_value_enum_wide, \ + .private_value = (unsigned long)&xenum } #define SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -297,7 +309,13 @@ SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) #define SOC_ENUM_SINGLE_VIRT_DECL(name, xtexts) \ const struct soc_enum name = SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(xtexts), xtexts) - +#define SOC_VALUE_ENUM_TRIPLE_DECL(name, reg1, reg2, reg3, \ + mask1, mask2, mask3, \ + xtexts, xvalues) \ + const struct soc_enum name = SOC_VALUE_ENUM_TRIPLE(reg1, reg2, reg3, \ + mask1, mask2, mask3, 3, \ + ARRAY_SIZE(xtexts), \ + xtexts, xvalues) /* * Component probe and remove ordering levels for components with runtime * dependencies. @@ -309,6 +327,11 @@ #define SND_SOC_COMP_ORDER_LAST 2
/* + * The maximum number of registers in soc_enum + */ +#define SOC_ENUM_MAX_REGS 3 + +/* * Bias levels * * @ON: Bias is fully on for audio playback and capture operations. @@ -507,6 +530,12 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_value_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_value_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info @@ -1098,13 +1127,14 @@ struct soc_mreg_control {
/* enumerated kcontrol */ struct soc_enum { - int reg; + int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items; - unsigned int mask; + unsigned int mask[SOC_ENUM_MAX_REGS]; const char * const *texts; const unsigned int *values; + unsigned int num_regs; };
/** diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index caebd63..e47479d 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,19 +2636,125 @@ 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);
/** + * snd_soc_info_enum_wide - enumerated mulitple register mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a enumerated multiple register + * mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->items; + + if (uinfo->value.enumerated.item > e->items - 1) + uinfo->value.enumerated.item = e->items - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_enum_wide); + +/** + * snd_soc_get_value_enum_wide - semi enumerated multiple registers mixer + * get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a semi enumerated mutiple registers mixer. + * + * Mutiple semi enumerated mixer: the mixer has multiple registers to set the + * values. The enumerated items are referred as values. Can be + * used for handling bitfield coded enumeration for example. + * + * Returns 0 for success. + */ +int snd_soc_get_value_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, i, reg_idx; + bool match = true; + + for (i = 0; i < e->items; i++) { + match = true; + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { + val = snd_soc_read(codec, e->reg[reg_idx]); + if (val != e->values[i * e->num_regs + reg_idx]) { + match = false; + break; + } + } + if (match) + break; + } + if (!match) { + dev_err(codec->dev, "ASoC: Failed to find matched enum value\n"); + return -EINVAL; + } else + ucontrol->value.enumerated.item[0] = i; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_wide); + +/** + * snd_soc_get_value_enum_wide - semi enumerated multiple mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to put the value of a multiple semi enumerated mixer. + * + * Mutiple semi enumerated mixer: the mixer has multiple registers to set the + * values. The enumerated items are referred as values. Can be + * used for handling bitfield coded enumeration for example. + * + * Returns 0 for success. + */ +int snd_soc_put_value_enum_wide(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val, reg_idx, item; + int ret; + + if (ucontrol->value.enumerated.item[0] > e->items - 1) + return -EINVAL; + item = ucontrol->value.enumerated.item[0]; + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { + val = e->values[item * e->num_regs + reg_idx]; + ret = snd_soc_update_bits_locked(codec, e->reg[reg_idx], + e->mask[reg_idx], val); + if (ret) + return ret; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_wide); + +/** * snd_soc_read_signed - Read a codec register and interprete as signed value * @codec: codec * @reg: Register to read diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c8a780d..22ca178 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -514,9 +514,9 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, unsigned int val, item; int i;
- if (e->reg != SND_SOC_NOPM) { - soc_widget_read(dest, e->reg, &val); - val = (val >> e->shift_l) & e->mask; + if (e->reg[0] != SND_SOC_NOPM) { + 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 @@ -1553,7 +1553,7 @@ 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; + unsigned int wi, update_idx; int ret;
if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) @@ -1575,8 +1575,10 @@ 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); + for (update_idx = 0; update_idx < update->num_regs; update_idx++) + ret = soc_widget_update_bits_locked(w, update->reg[update_idx], + update->mask[update_idx], + update->val[update_idx]); if (ret < 0) dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); @@ -2866,9 +2868,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 +2906,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 +2948,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 +2988,115 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
/** + * snd_soc_dapm_get_value_enum_wide - dapm semi enumerated multiple registers + * mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to get the value of a dapm semi enumerated multiple register mixer + * control. + * + * semi enumerated multiple registers mixer: + * the mixer has multiple regsters to set the enumerated items. The enumerated + * iteams are referred as values. + * Can be used for handling bitfield coded enumeration for example. + * + * Returns 0 for success. + */ +int snd_soc_dapm_get_value_enum_wide(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, i, reg_idx; + bool match = true; + + for (i = 0; i < e->items; i++) { + match = true; + for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) { + reg_val = snd_soc_read(codec, e->reg[reg_idx]); + if (reg_val != e->values[i * e->num_regs + reg_idx]) { + match = false; + break; + } + } + if (match) + break; + } + if (!match) { + dev_err(codec->dev, "ASoC: Failed to find matched enum value\n"); + return -EINVAL; + } else + ucontrol->value.enumerated.item[0] = i; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_wide); + +/** + * snd_soc_dapm_put_value_enum_wide - dapm semi enumerated multiple registers + * mixer put callback + * @kcontrol: mixer control + * @ucontrol: control element information + * + * Callback to put the value of a dapm semi enumerated multiple register mixer + * control. + * + * semi enumerated multiple registers mixer: + * the mixer has multiple regsters to set the enumerated items. The enumerated + * iteams are referred as values. + * Can be used for handling bitfield coded enumeration for example. + * + * Returns 0 for success. + */ +int snd_soc_dapm_put_value_enum_wide(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 value, item, old, reg_idx; + struct snd_soc_dapm_update update; + int wi, update_idx; + int ret = 0; + + if (ucontrol->value.enumerated.item[0] > e->items - 1) + return -EINVAL; + + item = ucontrol->value.enumerated.item[0]; + + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + for (reg_idx = 0, update_idx = 0; reg_idx < e->num_regs; reg_idx++) { + value = e->values[item * e->num_regs + reg_idx]; + old = snd_soc_read(codec, e->reg[reg_idx]); + if (value != old) { + update.reg[update_idx] = e->reg[reg_idx]; + update.mask[update_idx] = e->mask[reg_idx]; + update.val[update_idx] = value; + update.num_regs = ++update_idx; + } + } + + if (update_idx) { + update.kcontrol = kcontrol; + card->update = &update; + + ret = soc_dapm_mux_update_power(card, kcontrol, item, e); + + card->update = NULL; + } + + mutex_unlock(&card->dapm_mutex); + + if (ret > 0) + soc_dpcm_runtime_update(card); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_wide); + +/** * snd_soc_dapm_info_pin_switch - Info for a pin switch * * @kcontrol: mixer control
On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
If the mux uses 1 bit position per input, and requires to set one single bit at a time, then an N bit register can support up to N inputs. In more recent Tegra chips, we have at least greater than 64 inputs which requires at least 2 .reg fields in struct soc_enum.
Signed-off-by: Arun Shamanna Lakshmi aruns@nvidia.com Signed-off-by: Songhee Baek sbaek@nvidia.com
The way you describe this it seems to me that a value array for this kind of mux would look like.
0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000, 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000, 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008 ...
That seems to be extremely tedious. If the MUX uses a one hot encoding how about storing the index of the bit in the values array and use (1 << value) when writing the value to the register?
[...]
/* enumerated kcontrol */ struct soc_enum {
There doesn't actually be any code that is shared between normal enums and wide enums. This patch doubles the size of the soc_enum struct, how about having a separate struct for wide enums?
- int reg;
- int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items;
- unsigned int mask;
- unsigned int mask[SOC_ENUM_MAX_REGS];
If you make mask and reg pointers instead of arrays this should be much more flexible and not be limited to 3 registers.
const char * const *texts; const unsigned int *values;
- unsigned int num_regs; };
-----Original Message----- From: Lars-Peter Clausen [mailto:lars@metafoo.de] Sent: Wednesday, March 26, 2014 12:39 PM To: Arun Shamanna Lakshmi Cc: lgirdwood@gmail.com; broonie@kernel.org; swarren@wwwdotorg.org; Songhee Baek; alsa-devel@alsa-project.org; tiwai@suse.de; linux- kernel@vger.kernel.org Subject: Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
If the mux uses 1 bit position per input, and requires to set one single bit at a time, then an N bit register can support up to N inputs. In more recent Tegra chips, we have at least greater than 64 inputs which requires at least 2 .reg fields in struct soc_enum.
Signed-off-by: Arun Shamanna Lakshmi aruns@nvidia.com Signed-off-by: Songhee Baek sbaek@nvidia.com
The way you describe this it seems to me that a value array for this kind of mux would look like.
0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000, 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000, 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008 ...
That seems to be extremely tedious. If the MUX uses a one hot encoding how about storing the index of the bit in the values array and use (1 << value) when writing the value to the register?
If we store the index of the bit, the value will be duplicated for each registers inputs since register has 0 to 31bits to shift, then we need to decode the index to interpret value for which registers to set. If we need to interpret the decoded value of index, it is better to have custom put/get function in our driver, isn't it?
[...]
/* enumerated kcontrol */ struct soc_enum {
There doesn't actually be any code that is shared between normal enums and wide enums. This patch doubles the size of the soc_enum struct, how about having a separate struct for wide enums?
We are using DAPM widgets for these muxes and DAPM uses soc_enum struct inside APIs, so we couldn't use additional struct for that. If there is the way to use additional struc in dapm, please guide me.
- int reg;
- int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items;
- unsigned int mask;
- unsigned int mask[SOC_ENUM_MAX_REGS];
If you make mask and reg pointers instead of arrays this should be much more flexible and not be limited to 3 registers.
To use pointers instead of arrays, it will be flexible but I need to update SOC_ENUM SINGLE/DOUBLE macros. It will changes a lot in current soc-core.c and soc-dapm.c.
const char * const *texts; const unsigned int *values;
- unsigned int num_regs; };
On 03/26/2014 11:41 PM, Songhee Baek wrote:
-----Original Message----- From: Lars-Peter Clausen [mailto:lars@metafoo.de] Sent: Wednesday, March 26, 2014 12:39 PM To: Arun Shamanna Lakshmi Cc: lgirdwood@gmail.com; broonie@kernel.org; swarren@wwwdotorg.org; Songhee Baek; alsa-devel@alsa-project.org; tiwai@suse.de; linux- kernel@vger.kernel.org Subject: Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
If the mux uses 1 bit position per input, and requires to set one single bit at a time, then an N bit register can support up to N inputs. In more recent Tegra chips, we have at least greater than 64 inputs which requires at least 2 .reg fields in struct soc_enum.
Signed-off-by: Arun Shamanna Lakshmi aruns@nvidia.com Signed-off-by: Songhee Baek sbaek@nvidia.com
The way you describe this it seems to me that a value array for this kind of mux would look like.
0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000, 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000, 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008 ...
That seems to be extremely tedious. If the MUX uses a one hot encoding how about storing the index of the bit in the values array and use (1 << value) when writing the value to the register?
If we store the index of the bit, the value will be duplicated for each registers inputs since register has 0 to 31bits to shift, then we need to decode the index to interpret value for which registers to set. If we need to interpret the decoded value of index, it is better to have custom put/get function in our driver, isn't it?
I'm not sure I understand. If you use (val / 32) to pick the register and (val % 32) to pick the bit in the register this should work just fine. Maybe I'm missing something. Do you have a real world code example of of the this type of enum is used?
- int reg;
- int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items;
- unsigned int mask;
- unsigned int mask[SOC_ENUM_MAX_REGS];
If you make mask and reg pointers instead of arrays this should be much more flexible and not be limited to 3 registers.
To use pointers instead of arrays, it will be flexible but I need to update SOC_ENUM SINGLE/DOUBLE macros. It will changes a lot in current soc-core.c and soc-dapm.c.
In the existing macros you can do something like this: ... .reg = &(unsigned int){(xreg)}, ...
const char * const *texts; const unsigned int *values;
- unsigned int num_regs; };
-----Original Message----- From: Lars-Peter Clausen [mailto:lars@metafoo.de] Sent: Thursday, March 27, 2014 2:20 AM To: Songhee Baek Cc: Arun Shamanna Lakshmi; lgirdwood@gmail.com; broonie@kernel.org; swarren@wwwdotorg.org; alsa-devel@alsa-project.org; tiwai@suse.de; linux-kernel@vger.kernel.org Subject: Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
On 03/26/2014 11:41 PM, Songhee Baek wrote:
-----Original Message----- From: Lars-Peter Clausen [mailto:lars@metafoo.de] Sent: Wednesday, March 26, 2014 12:39 PM To: Arun Shamanna Lakshmi Cc: lgirdwood@gmail.com; broonie@kernel.org;
swarren@wwwdotorg.org;
Songhee Baek; alsa-devel@alsa-project.org; tiwai@suse.de; linux- kernel@vger.kernel.org Subject: Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
If the mux uses 1 bit position per input, and requires to set one single bit at a time, then an N bit register can support up to N inputs. In more recent Tegra chips, we have at least greater than 64 inputs which requires at least 2 .reg fields in struct soc_enum.
Signed-off-by: Arun Shamanna Lakshmi aruns@nvidia.com Signed-off-by: Songhee Baek sbaek@nvidia.com
The way you describe this it seems to me that a value array for this kind of mux would look like.
0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000, 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000, 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008 ...
That seems to be extremely tedious. If the MUX uses a one hot encoding how about storing the index of the bit in the values array and use (1 << value) when writing the value to the register?
If we store the index of the bit, the value will be duplicated for each
registers inputs since register has 0 to 31bits to shift, then we need to decode the index to interpret value for which registers to set. If we need to interpret the decoded value of index, it is better to have custom put/get function in our driver, isn't it?
I'm not sure I understand. If you use (val / 32) to pick the register and (val % 32) to pick the bit in the register this should work just fine. Maybe I'm missing something. Do you have a real world code example of of the this type of enum is used?
I can use val/32 and val%32 for this multi register mux.
- int reg;
- int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items;
- unsigned int mask;
- unsigned int mask[SOC_ENUM_MAX_REGS];
If you make mask and reg pointers instead of arrays this should be much more flexible and not be limited to 3 registers.
To use pointers instead of arrays, it will be flexible but I need to update
SOC_ENUM SINGLE/DOUBLE macros.
It will changes a lot in current soc-core.c and soc-dapm.c.
In the existing macros you can do something like this: ... .reg = &(unsigned int){(xreg)}, ...
Ok, I will update SINGLE/DOUBLE macro with this way.
const char * const *texts; const unsigned int *values;
- unsigned int num_regs; };
On Wed, Mar 26, 2014 at 08:38:47PM +0100, Lars-Peter Clausen wrote:
On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
The way you describe this it seems to me that a value array for this kind of mux would look like.
0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000, 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000, 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008
That seems to be extremely tedious. If the MUX uses a one hot encoding how about storing the index of the bit in the values array and use (1 << value) when writing the value to the register?
Or hide it behind utility macros at any rate; I've got this horrible feeling that as soon as we have this people will notice that they have more standard enums that are splatted over multiple registers (I think from memory I've seen them but they got fudged).
[...]
/* enumerated kcontrol */ struct soc_enum {
There doesn't actually be any code that is shared between normal enums and wide enums. This patch doubles the size of the soc_enum struct, how about having a separate struct for wide enums?
Or if they are going to share the same struct then they shouldn't be duplicating the code and instead keying off num_regs (which was my first thought earlier on when I saw the separate functions). We definitely shouldn't be sharing the data without also sharing the code I think.
- int reg;
- int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items;
- unsigned int mask;
- unsigned int mask[SOC_ENUM_MAX_REGS];
If you make mask and reg pointers instead of arrays this should be much more flexible and not be limited to 3 registers.
Right, which pushes towards not sharing. Though with an arrayified mask the specification by shift syntax would get to be slightly obscure (is it relative to the enums or the registers?) so perhaps we don't want to do that at all if we've got specification by shift. If we do that then we could get away with a variable length array at the end of the struct though I think that may be painful for the static declarations. Someone would need to look to see what works
-----Original Message----- From: alsa-devel-bounces@alsa-project.org [mailto:alsa-devel-bounces@alsa- project.org] On Behalf Of Mark Brown Sent: Wednesday, March 26, 2014 6:09 PM To: Lars-Peter Clausen Cc: Songhee Baek; Arun Shamanna Lakshmi; alsa-devel@alsa-project.org; swarren@wwwdotorg.org; tiwai@suse.de; lgirdwood@gmail.com; linux- kernel@vger.kernel.org Subject: Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
- PGP Signed by an unknown key
On Wed, Mar 26, 2014 at 08:38:47PM +0100, Lars-Peter Clausen wrote:
On 03/26/2014 01:02 AM, Arun Shamanna Lakshmi wrote:
The way you describe this it seems to me that a value array for this kind of mux would look like.
0x00000000, 0x00000000, 0x00000001 0x00000000, 0x00000000, 0x00000002 0x00000000, 0x00000000, 0x00000003 0x00000000, 0x00000000, 0x00000004 0x00000000, 0x00000000, 0x00000008
That seems to be extremely tedious. If the MUX uses a one hot encoding how about storing the index of the bit in the values array and use (1 << value) when writing the value to the register?
Or hide it behind utility macros at any rate; I've got this horrible feeling that as soon as we have this people will notice that they have more standard enums that are splatted over multiple registers (I think from memory I've seen them but they got fudged).
[...]
/* enumerated kcontrol */ struct soc_enum {
There doesn't actually be any code that is shared between normal enums and wide enums. This patch doubles the size of the soc_enum struct, how about having a separate struct for wide enums?
Or if they are going to share the same struct then they shouldn't be duplicating the code and instead keying off num_regs (which was my first thought earlier on when I saw the separate functions). We definitely shouldn't be sharing the data without also sharing the code I think.
- int reg;
- int reg[SOC_ENUM_MAX_REGS]; unsigned char shift_l; unsigned char shift_r; unsigned int items;
- unsigned int mask;
- unsigned int mask[SOC_ENUM_MAX_REGS];
If you make mask and reg pointers instead of arrays this should be much more flexible and not be limited to 3 registers.
Right, which pushes towards not sharing. Though with an arrayified mask the specification by shift syntax would get to be slightly obscure (is it relative to the enums or the registers?) so perhaps we don't want to do that at all if we've got specification by shift. If we do that then we could get away with a variable length array at the end of the struct though I think that may be painful for the static declarations. Someone would need to look to see what works
Making a separate soc_enum_wide is a better way compared to using soc_enum for this use case. If we add a separate soc_enum_wide, we need to update the following functions : dapm_connect_mux, soc_dapm_mux_update_power snd_soc_dapm_mux_update_power These functions are using texts field in soc_enum struct, I think that we can pass texts pointer instead of soc_enum struct pointer. I want to know whether it is Ok to do this.
- Unknown Key
- 0x7EA229BD
On Tue, Mar 25, 2014 at 05:02:35PM -0700, Arun Shamanna Lakshmi wrote:
- }
- if (!match) {
dev_err(codec->dev, "ASoC: Failed to find matched enum value\n");
return -EINVAL;
- } else
ucontrol->value.enumerated.item[0] = i;
Coding style nit: if one side of the if has braces both should. Most of this code could also use more blank lines.
- for (reg_idx = 0; reg_idx < e->num_regs; reg_idx++) {
val = e->values[item * e->num_regs + reg_idx];
ret = snd_soc_update_bits_locked(codec, e->reg[reg_idx],
e->mask[reg_idx], val);
if (ret)
return ret;
- }
So, this is a bit interesting. It will update one register at a time which means that we are likely to transiently set an invalid value sometimes which might not make the hardware happy or may cause us to write a valid value with undesirable consequences. I'd expect to see some handling of this, some combination of providing a safe value that the hardware could be reset to prior to change and doing a bulk write to all the registers simultaneously if we can (I know sometimes hardware has special handling for atomic updates of multi-register values in a single block transfer).
participants (4)
-
Arun Shamanna Lakshmi
-
Lars-Peter Clausen
-
Mark Brown
-
Songhee Baek