ASoC: Add support for multi register mux
diff mbox

Message ID 1395792156-4178-1-git-send-email-aruns@nvidia.com
State New, archived
Headers show

Commit Message

Arun Shamanna Lakshmi March 26, 2014, 12:02 a.m. UTC
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(-)

Comments

Lars-Peter Clausen March 26, 2014, 7:38 p.m. UTC | #1
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;
>   };
>
Songhee Baek March 26, 2014, 10:41 p.m. UTC | #2
> -----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;
> >   };
> >
Mark Brown March 27, 2014, 1:08 a.m. UTC | #3
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
Mark Brown March 27, 2014, 1:29 a.m. UTC | #4
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).
Songhee Baek March 27, 2014, 4:33 a.m. UTC | #5
> -----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
Lars-Peter Clausen March 27, 2014, 9:19 a.m. UTC | #6
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;
>>>    };
>>>
>
Songhee Baek March 27, 2014, 6:24 p.m. UTC | #7
> -----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;
> >>>    };
> >>>
> >
Songhee Baek March 28, 2014, 6:10 p.m. UTC | #8
> > On 03/26/2014 11:41 PM, Songhee Baek 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?
> > >
> > > 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.

With this logic, put function would be easy however get function becomes tedious due to looping on each bit per register for 3 of them in our case. Rather, it would be easy to identify a unique MUX_OFFSET to distinguish between the 3 registers as shown in the code below.

#define MULTI_MUX_INPUT_OFFSET(n)	(5 * n)

int snd_soc_dapm_get_enum_double(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, reg_idx;

	if (e->reg[0] != SND_SOC_NOPM) {
		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->shift_l) & e->mask[reg_idx];
			if (val)
				val += MULTI_MUX_INPUT_OFFSET(reg_idx);
		}
	} else {
		reg_val = dapm_kcontrol_get_value(kcontrol);
		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[0];
   		val = snd_soc_enum_val_to_item(e, val);
     		ucontrol->value.enumerated.item[1] = val;
       	} 

	return 0;
}

int snd_soc_dapm_put_enum_double(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, i, value, update_idx = 0;
	unsigned int mask;
	struct snd_soc_dapm_update update;
	int ret = 0, reg_val;

	if (item[0] >= e->items)
		return -EINVAL;

	value = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
        	mask = e->mask[0] << e->shift_l;
	if (e->shift_l != e->shift_r) {
    		if (item[1] > e->items)
	    		return -EINVAL;
    	    	value |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l;
        		mask |= e->mask[0] << e->shift_r;
	}

	if (e->num_regs < 2) {
		goto update_reg;
	}

   	for (i = 0; i < e->num_regs; i++) {
		reg_val = value - MULTI_MUX_INPUT_OFFSET(i);

		/* checking reg_val is power of 2 : one-hot code */
		/* if reg_val after subtract MULTI_MUX_INPUT_OFFSET is not power of 2, reg[i] should be zero */ 
		if (reg_val & (reg_val - 1)) {
			/* clear the current input register */
			snd_soc_write(codec, e->reg[i], 0);
		} else {
			/* reg_val is power of 2, store updated info */
			value = reg_val;
			mask = e->mask[i];
			update_idx = i;
		}
	}

update_reg:
	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);

	if (e->reg[update_idx] != SND_SOC_NOPM)
        		change = snd_soc_test_bits(codec, e->reg[update_idx], mask, value);
	else
        		change = dapm_kcontrol_set_value(kcontrol, value);

    	if (change) {
    		if (e->reg[update_idx] != SND_SOC_NOPM) {
	    	    update.kcontrol = kcontrol;
				update.reg = e->reg[update_idx];
        		update.mask= mask;
	        	update.val = value;
				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;
}

> > >>> -	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.

We will make reg* and mask* instead of arrays and since we use the same structure, the plan is to share the get and put function code.

Thanks.
Songhee.
Songhee Baek March 29, 2014, 2:30 a.m. UTC | #9
> -----Original Message-----
> From: Songhee Baek
> Sent: Friday, March 28, 2014 11:10 AM
> To: 'Lars-Peter Clausen'
> 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:
> > > >> 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?
> > > >
> > > > 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.
> 
With this logic, put function would be easy however get function becomes tedious due to looping on each bit per register for 3 of them in our case. Rather, it would be easy to identify a unique MUX_OFFSET to distinguish between the 3
registers as shown in the code below. 

These get/put functions are updated from previous mail, now it works for multi register mux, please review these function whether I can add in current put/get function.

#define MULTI_MUX_INPUT_OFFSET(n)	(5 * n)

int snd_soc_dapm_get_enum_double(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, reg_idx;

	if (e->reg[0] != SND_SOC_NOPM) {
		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->shift_l) & e->mask[reg_idx];
			if (val) {
				val += MULTI_MUX_INPUT_OFFSET(reg_idx);
				break;
			}
		}
	} else {
		reg_val = dapm_kcontrol_get_value(kcontrol);
		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[0];
   		val = snd_soc_enum_val_to_item(e, val);
     		ucontrol->value.enumerated.item[1] = val;
       	}

	return 0;
}

int snd_soc_dapm_put_enum_double(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, i, value, val, update_idx = 0;
	unsigned int mask;
	struct snd_soc_dapm_update update;
	int ret = 0, reg_val;

	if (item[0] >= e->items)
		return -EINVAL;

	val = snd_soc_enum_item_to_val(e, item[0]) << 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[0] << e->shift_r;
	}

	if (e->num_regs < 2) {
		value = val;
		goto update_reg;
	}

   	for (i = 0; i < e->num_regs; i++) {
		reg_val = val - MULTI_MUX_INPUT_OFFSET(i);

		/* checking reg_val is power of 2 : one-hot code */
		/* if reg_val after subtract MULTI_MUX_INPUT_OFFSET is not power of 2, reg[i] should be zero */
		if (reg_val & (reg_val - 1)) {
			/* clear the current input register */
			snd_soc_write(codec, e->reg[i], 0);
		} else {
			/* reg_val is power of 2, store updated info */
			value = reg_val;
			mask = e->mask[i];
			update_idx = i;
		}
	}

update_reg:
	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);

	if (e->reg[update_idx] != SND_SOC_NOPM)
        change = snd_soc_test_bits(codec, e->reg[update_idx], mask, value);
	else
        change = dapm_kcontrol_set_value(kcontrol, value);

    if (change) {
    	if (e->reg[update_idx] != SND_SOC_NOPM) {
	   	    update.kcontrol = kcontrol;
			update.reg = e->reg[update_idx];
       		update.mask= mask;
	       	update.val = value;
			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;
}
> > > >>> -	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.
> 
We will make reg* and mask* instead of arrays and since we use the same structure, the plan is to share the get and put function code.

Thanks.
Songhee.
Lars-Peter Clausen March 29, 2014, 10:53 a.m. UTC | #10
On 03/29/2014 03:30 AM, Songhee Baek wrote:
>> -----Original Message-----
>> From: Songhee Baek
>> Sent: Friday, March 28, 2014 11:10 AM
>> To: 'Lars-Peter Clausen'
>> 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:
>>>>>> 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?
>>>>>
>>>>> 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.
>>
> With this logic, put function would be easy however get function becomes tedious due to looping on each bit per register for 3 of them in our case. Rather, it would be easy to identify a unique MUX_OFFSET to distinguish between the 3
> registers as shown in the code below.

I'm not sure I understand how that MUX_OFFSET would work. To get the 
selected mux output you can use the ffs instruction.

foreach(reg) {
	reg_val = read(reg) & mask;
	if (reg_val != 0) {
		val = __ffs(reg_val);
		break;
	}
}

- Lars
Arun Shamanna Lakshmi March 30, 2014, 6:12 a.m. UTC | #11
> -----Original Message-----
> From: Lars-Peter Clausen [mailto:lars@metafoo.de]
> Sent: Saturday, March 29, 2014 3:53 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/29/2014 03:30 AM, Songhee Baek wrote:
> >> -----Original Message-----
> >> From: Songhee Baek
> >> Sent: Friday, March 28, 2014 11:10 AM
> >> To: 'Lars-Peter Clausen'
> >> 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:
> >>>>>> 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?
> >>>>>
> >>>>> 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.
> >>
> > With this logic, put function would be easy however get function
> > becomes tedious due to looping on each bit per register for 3 of them in our
> case. Rather, it would be easy to identify a unique MUX_OFFSET to distinguish
> between the 3 registers as shown in the code below.
> 
> I'm not sure I understand how that MUX_OFFSET would work. To get the
> selected mux output you can use the ffs instruction.
> 
> foreach(reg) {
> 	reg_val = read(reg) & mask;
> 	if (reg_val != 0) {
> 		val = __ffs(reg_val);
> 		break;
> 	}
> }
> 

There are 2 options to do this. The first option is what you specified above, in which case I think we cannot share get and put functions as they use the reg_val directly inside snd_soc_enum_val_to_item API (not the bit position being set). If we change to bit position like above, then the current users of these APIs should also change their soc_enum value table. And, the second option being the one that we proposed.

That being said, MUX_OFFSET which is the second option works in the following way. We know that reg_val is a power of 2 (2^0  to 2^31) which is one hot code. This method adds a unique offset for this reg_val for each incremental register that we want to set (say 2^n + MUX_OFFSET(reg_id)) inside get function and does the reverse of it in put function. For current users of only one register, it doesn't change anything as we use reg_val.

> 	if (e->reg[0] != SND_SOC_NOPM) {
> 		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->shift_l) & e->mask[reg_idx];
> 			if (val) {
> 				val += MULTI_MUX_INPUT_OFFSET(reg_idx);
> 				break;
> 			}
> 		}
> 	} else {
> 		reg_val = dapm_kcontrol_get_value(kcontrol);
> 		val = (reg_val >> e->shift_l) & e->mask[0];
> 	}

Both the options are the same. The first one adds 32 or 64 to the bit position to identify uniquely for each register and the other adds a MUX_OFFSET to reg_val to achieve the same. The only advantage of the latter being that we can share the code with get_enum_double and put_enum_double which has been our goal since the beginning of this discussion (as we share the soc_enum struct)

We have tested the second option to work well for our use case. Please let us know what works for you and we will make the necessary changes quickly.

- Arun
Mark Brown March 31, 2014, 11:21 a.m. UTC | #12
On Sat, Mar 29, 2014 at 11:12:30PM -0700, Arun Shamanna Lakshmi wrote:

Fix your mailer to word wrap within paragraphs, your mails are
excessively hard to read.

> > I'm not sure I understand how that MUX_OFFSET would work. To get the
> > selected mux output you can use the ffs instruction.

> > foreach(reg) {
> > 	reg_val = read(reg) & mask;
> > 	if (reg_val != 0) {
> > 		val = __ffs(reg_val);
> > 		break;
> > 	}
> > }

> There are 2 options to do this. The first option is what you specified
> above, in which case I think we cannot share get and put functions as
> they use the reg_val directly inside snd_soc_enum_val_to_item API (not
> the bit position being set). If we change to bit position like above,
> then the current users of these APIs should also change their soc_enum
> value table. And, the second option being the one that we proposed.

Sharing the functions isn't the goal, coming up with a usable API is.

> That being said, MUX_OFFSET which is the second option works in the
> following way. We know that reg_val is a power of 2 (2^0  to 2^31)
> which is one hot code. This method adds a unique offset for this
> reg_val for each incremental register that we want to set (say 2^n +
> MUX_OFFSET(reg_id)) inside get function and does the reverse of it in
> put function. For current users of only one register, it doesn't
> change anything as we use reg_val.

I'm afraid I can't understand the above at all, sorry.  The code below
is quoted like Lars wrote it but I think it's actually written by you,
please check your quoting when replying:

> > 	if (e->reg[0] != SND_SOC_NOPM) {
> > 		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->shift_l) & e->mask[reg_idx];
> > 			if (val) {
> > 				val += MULTI_MUX_INPUT_OFFSET(reg_idx);
> > 				break;
> > 			}
> > 		}
> > 	} else {
> > 		reg_val = dapm_kcontrol_get_value(kcontrol);
> > 		val = (reg_val >> e->shift_l) & e->mask[0];
> > 	}

The above is a bit confusing...  partly this is because of a lack of
context (what is MULTI_MUX_INPUT_OFFSET?) and partly because it isn't
entirely obvious that stopping as soon as we see any value set is the
right choice, especially given the addition to rather than setting of
val.
Lars-Peter Clausen March 31, 2014, 11:55 a.m. UTC | #13
On 03/31/2014 01:21 PM, Mark Brown wrote:
> On Sat, Mar 29, 2014 at 11:12:30PM -0700, Arun Shamanna Lakshmi wrote:
>
> Fix your mailer to word wrap within paragraphs, your mails are
> excessively hard to read.
>
>>> I'm not sure I understand how that MUX_OFFSET would work. To get the
>>> selected mux output you can use the ffs instruction.
>
>>> foreach(reg) {
>>> 	reg_val = read(reg) & mask;
>>> 	if (reg_val != 0) {
>>> 		val = __ffs(reg_val);
>>> 		break;
>>> 	}
>>> }
>
>> There are 2 options to do this. The first option is what you specified
>> above, in which case I think we cannot share get and put functions as
>> they use the reg_val directly inside snd_soc_enum_val_to_item API (not
>> the bit position being set). If we change to bit position like above,
>> then the current users of these APIs should also change their soc_enum
>> value table. And, the second option being the one that we proposed.
>
> Sharing the functions isn't the goal, coming up with a usable API is.
>
>> That being said, MUX_OFFSET which is the second option works in the
>> following way. We know that reg_val is a power of 2 (2^0  to 2^31)
>> which is one hot code. This method adds a unique offset for this
>> reg_val for each incremental register that we want to set (say 2^n +
>> MUX_OFFSET(reg_id)) inside get function and does the reverse of it in
>> put function. For current users of only one register, it doesn't
>> change anything as we use reg_val.
>
> I'm afraid I can't understand the above at all, sorry.  The code below
> is quoted like Lars wrote it but I think it's actually written by you,
> please check your quoting when replying:\
>
>>> 	if (e->reg[0] != SND_SOC_NOPM) {
>>> 		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->shift_l) & e->mask[reg_idx];
>>> 			if (val) {
>>> 				val += MULTI_MUX_INPUT_OFFSET(reg_idx);
>>> 				break;
>>> 			}
>>> 		}
>>> 	} else {
>>> 		reg_val = dapm_kcontrol_get_value(kcontrol);
>>> 		val = (reg_val >> e->shift_l) & e->mask[0];
>>> 	}
>
> The above is a bit confusing...  partly this is because of a lack of
> context (what is MULTI_MUX_INPUT_OFFSET?) and partly because it isn't
> entirely obvious that stopping as soon as we see any value set is the
> right choice, especially given the addition to rather than setting of
> val.

I think the idea is that since we know that for one-hot encodings only 
powers of two are valid values the other bits are used to encode the 
register number. E.g 0x4 means bit 3 in register 0, 0x5 means bit 3 in 
register 1, 0x6 means bit 3 in register 2 and so on. I guess it is possible 
to make it work. But this seems to be quite hack-ish to me. You'd have to be 
careful that MULTI_MUX_INPUT_OFFSET(reg_idx) never evaluates to a power of 
two and there are probably some more pitfalls.

- Lars
Mark Brown March 31, 2014, 12:07 p.m. UTC | #14
On Mon, Mar 31, 2014 at 01:55:52PM +0200, Lars-Peter Clausen wrote:
> On 03/31/2014 01:21 PM, Mark Brown wrote:

> >The above is a bit confusing...  partly this is because of a lack of
> >context (what is MULTI_MUX_INPUT_OFFSET?) and partly because it isn't
> >entirely obvious that stopping as soon as we see any value set is the
> >right choice, especially given the addition to rather than setting of
> >val.

> I think the idea is that since we know that for one-hot encodings only
> powers of two are valid values the other bits are used to encode the
> register number. E.g 0x4 means bit 3 in register 0, 0x5 means bit 3 in
> register 1, 0x6 means bit 3 in register 2 and so on. I guess it is possible
> to make it work. But this seems to be quite hack-ish to me. You'd have to be
> careful that MULTI_MUX_INPUT_OFFSET(reg_idx) never evaluates to a power of
> two and there are probably some more pitfalls.

Ugh, right.  The fact that I couldn't tell that this was what the code
was trying to do from looking at it is not a good sign here.
Arun Shamanna Lakshmi April 1, 2014, 6:08 a.m. UTC | #15
> -----Original Message-----
> From: Mark Brown [mailto:broonie@kernel.org]
> Sent: Monday, March 31, 2014 5:08 AM
> To: Lars-Peter Clausen
> Cc: Arun Shamanna Lakshmi; Songhee Baek; '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 Mon, Mar 31, 2014 at 01:55:52PM +0200, Lars-Peter Clausen wrote:
> > On 03/31/2014 01:21 PM, Mark Brown wrote:
> 
> > >The above is a bit confusing...  partly this is because of a lack of
> > >context (what is MULTI_MUX_INPUT_OFFSET?) and partly because it isn't
> > >entirely obvious that stopping as soon as we see any value set is the
> > >right choice, especially given the addition to rather than setting of
> > >val.
> 
> > I think the idea is that since we know that for one-hot encodings only
> > powers of two are valid values the other bits are used to encode the
> > register number. E.g 0x4 means bit 3 in register 0, 0x5 means bit 3 in
> > register 1, 0x6 means bit 3 in register 2 and so on. I guess it is
> > possible to make it work. But this seems to be quite hack-ish to me.
> > You'd have to be careful that MULTI_MUX_INPUT_OFFSET(reg_idx) never
> > evaluates to a power of two and there are probably some more pitfalls.
> 
> Ugh, right.  The fact that I couldn't tell that this was what the code was trying to
> do from looking at it is not a good sign here.

I will work on this and submit another patch. Thanks for all your feedback.

> 
> * Unknown Key
> * 0x7EA229BD

Patch
diff mbox

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