All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ASoC: Add support for multi register mux
@ 2014-03-18 23:51 ` Arun Shamanna Lakshmi
  0 siblings, 0 replies; 20+ messages in thread
From: Arun Shamanna Lakshmi @ 2014-03-18 23:51 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: perex, tiwai, alsa-devel, linux-kernel, Arun Shamanna Lakshmi,
	Songhee Baek

Currently soc_enum structure supports only 2 registers (reg, reg2)
for kcontrol. However, it is possible to have multiple registers
per mux. This change allows us to control these multiple registers.

Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com>
Signed-off-by: Songhee Baek <sbaek@nvidia.com>
---
 include/sound/soc.h |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 9a00147..ddedfb4 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1093,6 +1093,9 @@ struct soc_enum {
 	unsigned int mask;
 	const char * const *texts;
 	const unsigned int *values;
+	unsigned int *regs;
+	unsigned int *masks;
+	unsigned int num_regs;
 };
 
 /* codec IO */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH] ASoC: Add support for multi register mux
@ 2014-03-18 23:51 ` Arun Shamanna Lakshmi
  0 siblings, 0 replies; 20+ messages in thread
From: Arun Shamanna Lakshmi @ 2014-03-18 23:51 UTC (permalink / raw)
  To: lgirdwood, broonie
  Cc: perex, tiwai, alsa-devel, linux-kernel, Arun Shamanna Lakshmi,
	Songhee Baek

Currently soc_enum structure supports only 2 registers (reg, reg2)
for kcontrol. However, it is possible to have multiple registers
per mux. This change allows us to control these multiple registers.

Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com>
Signed-off-by: Songhee Baek <sbaek@nvidia.com>
---
 include/sound/soc.h |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 9a00147..ddedfb4 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1093,6 +1093,9 @@ struct soc_enum {
 	unsigned int mask;
 	const char * const *texts;
 	const unsigned int *values;
+	unsigned int *regs;
+	unsigned int *masks;
+	unsigned int num_regs;
 };
 
 /* codec IO */
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-18 23:51 ` Arun Shamanna Lakshmi
@ 2014-03-18 23:59   ` Mark Brown
  -1 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-18 23:59 UTC (permalink / raw)
  To: Arun Shamanna Lakshmi
  Cc: lgirdwood, perex, tiwai, alsa-devel, linux-kernel, Songhee Baek

[-- Attachment #1: Type: text/plain, Size: 952 bytes --]

On Tue, Mar 18, 2014 at 04:51:32PM -0700, Arun Shamanna Lakshmi wrote:

> Currently soc_enum structure supports only 2 registers (reg, reg2)
> for kcontrol. However, it is possible to have multiple registers
> per mux. This change allows us to control these multiple registers.

I'd want to see a user along with this and...

> @@ -1093,6 +1093,9 @@ struct soc_enum {
>  	unsigned int mask;
>  	const char * const *texts;
>  	const unsigned int *values;
> +	unsigned int *regs;
> +	unsigned int *masks;
> +	unsigned int num_regs;

...it duplicates and generally isn't joined up with the existing members
of the structure, and has no support in the helpers (for example,
converting the existing stereo controls to be two element arrays which
I'd expect to see).  Helpers would count as users here.

Note that we don't support double register enums or muxes - only
numerical controls are supported.  It's not clear what a multi-register
enum would mean.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
@ 2014-03-18 23:59   ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-18 23:59 UTC (permalink / raw)
  To: Arun Shamanna Lakshmi
  Cc: Songhee Baek, alsa-devel, tiwai, linux-kernel, lgirdwood


[-- Attachment #1.1: Type: text/plain, Size: 952 bytes --]

On Tue, Mar 18, 2014 at 04:51:32PM -0700, Arun Shamanna Lakshmi wrote:

> Currently soc_enum structure supports only 2 registers (reg, reg2)
> for kcontrol. However, it is possible to have multiple registers
> per mux. This change allows us to control these multiple registers.

I'd want to see a user along with this and...

> @@ -1093,6 +1093,9 @@ struct soc_enum {
>  	unsigned int mask;
>  	const char * const *texts;
>  	const unsigned int *values;
> +	unsigned int *regs;
> +	unsigned int *masks;
> +	unsigned int num_regs;

...it duplicates and generally isn't joined up with the existing members
of the structure, and has no support in the helpers (for example,
converting the existing stereo controls to be two element arrays which
I'd expect to see).  Helpers would count as users here.

Note that we don't support double register enums or muxes - only
numerical controls are supported.  It's not clear what a multi-register
enum would mean.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* RE: [PATCH] ASoC: Add support for multi register mux
  2014-03-18 23:59   ` Mark Brown
  (?)
@ 2014-03-19 23:44   ` Arun Shamanna Lakshmi
  2014-03-20 11:48     ` Mark Brown
  -1 siblings, 1 reply; 20+ messages in thread
From: Arun Shamanna Lakshmi @ 2014-03-19 23:44 UTC (permalink / raw)
  To: Mark Brown
  Cc: lgirdwood, perex, tiwai, alsa-devel, linux-kernel, Songhee Baek

If each bit of a 32 bit register maps to an input of a mux, then with the current 'soc_enum' structure we cannot have more than 64 inputs for the mux (because of reg and reg2 only).
In such cases, we need more than 2 registers to select the input of the mux. This is referred to as 'multi register mux' 

For instance, the audio xbar (AXBAR) module acts as a mux selecting various inputs (reference: Tegra K1 manual).

The number of such inputs increases with future Tegra chips and so will be the need to control multiple registers per mux in DAPM.  We have 2 options to achieve that.

Option 1: Using custom get and put functions something similar to below inside AXBAR tegra driver.

	int tegra_xbar_get_value_enum(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
	{
		struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
		struct snd_soc_dapm_widget *widget = wlist->widgets[0];
		struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
		unsigned int reg_val, mux, find, reg_idx;
		unsigned int num_regs = 3, regs[3];

		/* control 3 registers that has a common STRIDE */
		regs[0] = e-> reg;
		regs[1] = regs[0] + MUX_REG_STRIDE;
		regs[2] = regs[1] + MUX_REG_STRIDE;

		for (mux = 0; mux < e->max; mux++) {
			find = 0;
			for (reg_idx = 0; reg_idx < num_regs; reg_idx++) {
				regmap_read(widget->codec->control_data,
						regs[reg_idx], &reg_val);
				if (reg_val ==  e->values[mux * num_regs + reg_idx])
					find++;
			}
			if (find == num_regs)
				break;
		}
		ucontrol->value.enumerated.item[0] = mux;
		return 0;
	}

	int tegra_xbar_put_value_enum(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
	{
		struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
		struct snd_soc_dapm_widget *widget = wlist->widgets[0];
		struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
		unsigned int value, mux, old, reg_idx;
		struct snd_soc_dapm_update update;
		   unsigned int num_regs = 3, regs[3], masks[3] = { 0xf1f03ff, 0x3f30031f, 0xff1cf313};
		int wi;

		regs[0] = e-> reg;
		regs[1] = regs[0] + MUX_REG_STRIDE;
		regs[2] = regs[1] + MUX_REG_STRIDE;

		if (ucontrol->value.enumerated.item[0] > e->max - 1)
			return -EINVAL;

		mux = ucontrol->value.enumerated.item[0];

		for (reg_idx = 0; reg_idx < num_regs; reg_idx++) {
			value = e->values[ucontrol->value.enumerated.item[0] * num_regs + reg_idx];
			regmap_read(widget->codec->control_data, regs[reg_idx], &old);

			if (value != old) {
				for (wi = 0; wi < wlist->num_widgets; wi++) {
					widget = wlist->widgets[wi];
					widget->value = value;
					update.kcontrol = kcontrol;
					update.widget = widget;
					update.reg = regs[reg_idx];
					update.mask = masks[reg_idx];
					update.val = value;
					widget->dapm->update = &update;
					snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
					widget->dapm->update = NULL;
				}
			}
		}
		return 0;
	}

Option 2: Modify soc_enum structure and make 'reg' variable as reg[MAX_REG]

This would also mean that we should edit all the macros in soc.h, soc-dapm.c and soc-core.c to use 'reg[0]' instead of 'reg'
Our goal is to eventually add support to do multi register mux in get and put handlers inside soc-dapm.c upstream.

With Option1, we don't need to change any code in upstream as each register among the multiple registers has a common STRIDE (address offset). Thus, option1 is not generic enough.
If you suggest Option1, we wanted to check if upstream will be okay with such a structure. (it will be tegra specific though).

With Option2, it  becomes easy to add new macros for multi register mux in soc.h and then, add new get and put handlers in dapm.c

Thanks,
Arun

-----Original Message-----
From: Mark Brown [mailto:broonie@kernel.org] 
Sent: Tuesday, March 18, 2014 5:00 PM
To: Arun Shamanna Lakshmi
Cc: lgirdwood@gmail.com; perex@perex.cz; tiwai@suse.de; alsa-devel@alsa-project.org; linux-kernel@vger.kernel.org; Songhee Baek
Subject: Re: [PATCH] ASoC: Add support for multi register mux

* PGP Signed by an unknown key

On Tue, Mar 18, 2014 at 04:51:32PM -0700, Arun Shamanna Lakshmi wrote:

> Currently soc_enum structure supports only 2 registers (reg, reg2) for 
> kcontrol. However, it is possible to have multiple registers per mux. 
> This change allows us to control these multiple registers.

I'd want to see a user along with this and...

> @@ -1093,6 +1093,9 @@ struct soc_enum {
>  	unsigned int mask;
>  	const char * const *texts;
>  	const unsigned int *values;
> +	unsigned int *regs;
> +	unsigned int *masks;
> +	unsigned int num_regs;

...it duplicates and generally isn't joined up with the existing members of the structure, and has no support in the helpers (for example, converting the existing stereo controls to be two element arrays which I'd expect to see).  Helpers would count as users here.

Note that we don't support double register enums or muxes - only numerical controls are supported.  It's not clear what a multi-register enum would mean.

* Unknown Key
* 0x7EA229BD

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-19 23:44   ` Arun Shamanna Lakshmi
@ 2014-03-20 11:48     ` Mark Brown
  2014-03-20 18:20       ` Stephen Warren
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Brown @ 2014-03-20 11:48 UTC (permalink / raw)
  To: Arun Shamanna Lakshmi
  Cc: lgirdwood, perex, tiwai, alsa-devel, linux-kernel, Songhee Baek,
	srwarren

[-- Attachment #1: Type: text/plain, Size: 1012 bytes --]

On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:

Don't top post and fix your mailer to word wrap within paragraphs, your
mail is very hard to read.

> If each bit of a 32 bit register maps to an input of a mux, then with
> the current 'soc_enum' structure we cannot have more than 64 inputs
> for the mux (because of reg and reg2 only).

What makes you say that?  We currently have devices in mainline which
have well over 32 inputs to muxes.

> For instance, the audio xbar (AXBAR) module acts as a mux selecting
> various inputs (reference: Tegra K1 manual).

I don't have access to non-public nVidia documents...

> The number of such inputs increases with future Tegra chips and so
> will be the need to control multiple registers per mux in DAPM.  We
> have 2 options to achieve that.

Like I said in my previous reply I would expect to see some users along
with the code, extending the standard helpers to support this would be a
much better idea than doing something driver custom.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-20 11:48     ` Mark Brown
@ 2014-03-20 18:20       ` Stephen Warren
  2014-03-20 18:36           ` Mark Brown
  0 siblings, 1 reply; 20+ messages in thread
From: Stephen Warren @ 2014-03-20 18:20 UTC (permalink / raw)
  To: Mark Brown, Arun Shamanna Lakshmi
  Cc: lgirdwood, perex, tiwai, alsa-devel, linux-kernel, Songhee Baek,
	Stephen Warren

On 03/20/2014 05:48 AM, Mark Brown wrote:
> On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:
> 
> Don't top post and fix your mailer to word wrap within paragraphs, your
> mail is very hard to read.
> 
>> If each bit of a 32 bit register maps to an input of a mux, then with
>> the current 'soc_enum' structure we cannot have more than 64 inputs
>> for the mux (because of reg and reg2 only).
> 
> What makes you say that?  We currently have devices in mainline which
> have well over 32 inputs to muxes.

I think their register layout is different.

I found a number of large muxes where the register stores a 'integer'
indicating which mux input to select, e.g. Arizona, WM2200, etc. In this
case, an N-bit register could support up to 2^N inputs.

However, the registers in the Tegra AHUB use 1 bit position per input,
and require you to set one single bit at a time. Hence, an N bit
register (or string of registers) can support up to N inputs. In more
recent Tegra chips, we have at least >32 inputs and I think Arun was
saying even >64 inputs. That requires 2 or 3 or more .reg fields in
struct soc_enum.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-20 18:20       ` Stephen Warren
@ 2014-03-20 18:36           ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-20 18:36 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Arun Shamanna Lakshmi, lgirdwood, perex, tiwai, alsa-devel,
	linux-kernel, Songhee Baek

[-- Attachment #1: Type: text/plain, Size: 1457 bytes --]

On Thu, Mar 20, 2014 at 12:20:17PM -0600, Stephen Warren wrote:
> On 03/20/2014 05:48 AM, Mark Brown wrote:
> > On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:

> >> If each bit of a 32 bit register maps to an input of a mux, then with
> >> the current 'soc_enum' structure we cannot have more than 64 inputs
> >> for the mux (because of reg and reg2 only).

> > What makes you say that?  We currently have devices in mainline which
> > have well over 32 inputs to muxes.

> I think their register layout is different.

> I found a number of large muxes where the register stores a 'integer'
> indicating which mux input to select, e.g. Arizona, WM2200, etc. In this
> case, an N-bit register could support up to 2^N inputs.

> However, the registers in the Tegra AHUB use 1 bit position per input,
> and require you to set one single bit at a time. Hence, an N bit
> register (or string of registers) can support up to N inputs. In more
> recent Tegra chips, we have at least >32 inputs and I think Arun was
> saying even >64 inputs. That requires 2 or 3 or more .reg fields in
> struct soc_enum.

Right, that was my guess too (the mail wasn't terribly clear with the
formatting, references to unpublished documents and so on) but that's
not a straight mux, it's a value mux, and the limit with the current
code is much lower on 32 bit systems (like at least some of the K1s)
since muxes only use one of the current register fields.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
@ 2014-03-20 18:36           ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-20 18:36 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Songhee Baek, Arun Shamanna Lakshmi, alsa-devel, tiwai,
	lgirdwood, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1457 bytes --]

On Thu, Mar 20, 2014 at 12:20:17PM -0600, Stephen Warren wrote:
> On 03/20/2014 05:48 AM, Mark Brown wrote:
> > On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:

> >> If each bit of a 32 bit register maps to an input of a mux, then with
> >> the current 'soc_enum' structure we cannot have more than 64 inputs
> >> for the mux (because of reg and reg2 only).

> > What makes you say that?  We currently have devices in mainline which
> > have well over 32 inputs to muxes.

> I think their register layout is different.

> I found a number of large muxes where the register stores a 'integer'
> indicating which mux input to select, e.g. Arizona, WM2200, etc. In this
> case, an N-bit register could support up to 2^N inputs.

> However, the registers in the Tegra AHUB use 1 bit position per input,
> and require you to set one single bit at a time. Hence, an N bit
> register (or string of registers) can support up to N inputs. In more
> recent Tegra chips, we have at least >32 inputs and I think Arun was
> saying even >64 inputs. That requires 2 or 3 or more .reg fields in
> struct soc_enum.

Right, that was my guess too (the mail wasn't terribly clear with the
formatting, references to unpublished documents and so on) but that's
not a straight mux, it's a value mux, and the limit with the current
code is much lower on 32 bit systems (like at least some of the K1s)
since muxes only use one of the current register fields.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
  2014-03-20 18:36           ` Mark Brown
  (?)
@ 2014-03-20 19:05           ` Lars-Peter Clausen
  2014-03-20 19:40               ` Lars-Peter Clausen
  -1 siblings, 1 reply; 20+ messages in thread
From: Lars-Peter Clausen @ 2014-03-20 19:05 UTC (permalink / raw)
  To: Mark Brown
  Cc: Stephen Warren, Songhee Baek, Arun Shamanna Lakshmi, alsa-devel,
	tiwai, lgirdwood, linux-kernel

On 03/20/2014 07:36 PM, Mark Brown wrote:
> On Thu, Mar 20, 2014 at 12:20:17PM -0600, Stephen Warren wrote:
>> On 03/20/2014 05:48 AM, Mark Brown wrote:
>>> On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:
>
>>>> If each bit of a 32 bit register maps to an input of a mux, then with
>>>> the current 'soc_enum' structure we cannot have more than 64 inputs
>>>> for the mux (because of reg and reg2 only).
>
>>> What makes you say that?  We currently have devices in mainline which
>>> have well over 32 inputs to muxes.
>
>> I think their register layout is different.
>
>> I found a number of large muxes where the register stores a 'integer'
>> indicating which mux input to select, e.g. Arizona, WM2200, etc. In this
>> case, an N-bit register could support up to 2^N inputs.
>
>> However, the registers in the Tegra AHUB use 1 bit position per input,
>> and require you to set one single bit at a time. Hence, an N bit
>> register (or string of registers) can support up to N inputs. In more
>> recent Tegra chips, we have at least >32 inputs and I think Arun was
>> saying even >64 inputs. That requires 2 or 3 or more .reg fields in
>> struct soc_enum.
>
> Right, that was my guess too (the mail wasn't terribly clear with the
> formatting, references to unpublished documents and so on) but that's
> not a straight mux, it's a value mux, and the limit with the current
> code is much lower on 32 bit systems (like at least some of the K1s)
> since muxes only use one of the current register fields.

It might make sense to add special code for supported muxes with a one-hot 
encoding instead of using a value mux. Having an large array where each 
entry is just 1<<n is a bit ugly in my opinion, especially if the value 
needs to be able to be larger than 2**64. But anyway the patch that modifies 
the soc_enum struct should also add the code that makes use of the new 
struct layout.

- Lars


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
  2014-03-20 19:05           ` [alsa-devel] " Lars-Peter Clausen
@ 2014-03-20 19:40               ` Lars-Peter Clausen
  0 siblings, 0 replies; 20+ messages in thread
From: Lars-Peter Clausen @ 2014-03-20 19:40 UTC (permalink / raw)
  To: Mark Brown
  Cc: Songhee Baek, Arun Shamanna Lakshmi, alsa-devel, Stephen Warren,
	tiwai, lgirdwood, linux-kernel

On 03/20/2014 08:05 PM, Lars-Peter Clausen wrote:
> On 03/20/2014 07:36 PM, Mark Brown wrote:
>> On Thu, Mar 20, 2014 at 12:20:17PM -0600, Stephen Warren wrote:
>>> On 03/20/2014 05:48 AM, Mark Brown wrote:
>>>> On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:
>>
>>>>> If each bit of a 32 bit register maps to an input of a mux, then with
>>>>> the current 'soc_enum' structure we cannot have more than 64 inputs
>>>>> for the mux (because of reg and reg2 only).
>>
>>>> What makes you say that?  We currently have devices in mainline which
>>>> have well over 32 inputs to muxes.
>>
>>> I think their register layout is different.
>>
>>> I found a number of large muxes where the register stores a 'integer'
>>> indicating which mux input to select, e.g. Arizona, WM2200, etc. In this
>>> case, an N-bit register could support up to 2^N inputs.
>>
>>> However, the registers in the Tegra AHUB use 1 bit position per input,
>>> and require you to set one single bit at a time. Hence, an N bit
>>> register (or string of registers) can support up to N inputs. In more
>>> recent Tegra chips, we have at least >32 inputs and I think Arun was
>>> saying even >64 inputs. That requires 2 or 3 or more .reg fields in
>>> struct soc_enum.
>>
>> Right, that was my guess too (the mail wasn't terribly clear with the
>> formatting, references to unpublished documents and so on) but that's
>> not a straight mux, it's a value mux, and the limit with the current
>> code is much lower on 32 bit systems (like at least some of the K1s)
>> since muxes only use one of the current register fields.
>
> It might make sense to add special code for supported muxes with a one-hot
> encoding instead of using a value mux. Having an large array where each
> entry is just 1<<n is a bit ugly in my opinion, especially if the value
> needs to be able to be larger than 2**64. But anyway the patch that modifies
> the soc_enum struct should also add the code that makes use of the new
> struct layout.

On the other hand this can actually already be implemented without any core 
changes by using a virtual mux and connecting a supply widget to each input 
which sets the bit for that input. DAPM will take care that only one of the 
supply widgets is enabled at a time.

- Lars


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
@ 2014-03-20 19:40               ` Lars-Peter Clausen
  0 siblings, 0 replies; 20+ messages in thread
From: Lars-Peter Clausen @ 2014-03-20 19:40 UTC (permalink / raw)
  To: Mark Brown
  Cc: Songhee Baek, Arun Shamanna Lakshmi, alsa-devel, Stephen Warren,
	tiwai, lgirdwood, linux-kernel

On 03/20/2014 08:05 PM, Lars-Peter Clausen wrote:
> On 03/20/2014 07:36 PM, Mark Brown wrote:
>> On Thu, Mar 20, 2014 at 12:20:17PM -0600, Stephen Warren wrote:
>>> On 03/20/2014 05:48 AM, Mark Brown wrote:
>>>> On Wed, Mar 19, 2014 at 04:44:00PM -0700, Arun Shamanna Lakshmi wrote:
>>
>>>>> If each bit of a 32 bit register maps to an input of a mux, then with
>>>>> the current 'soc_enum' structure we cannot have more than 64 inputs
>>>>> for the mux (because of reg and reg2 only).
>>
>>>> What makes you say that?  We currently have devices in mainline which
>>>> have well over 32 inputs to muxes.
>>
>>> I think their register layout is different.
>>
>>> I found a number of large muxes where the register stores a 'integer'
>>> indicating which mux input to select, e.g. Arizona, WM2200, etc. In this
>>> case, an N-bit register could support up to 2^N inputs.
>>
>>> However, the registers in the Tegra AHUB use 1 bit position per input,
>>> and require you to set one single bit at a time. Hence, an N bit
>>> register (or string of registers) can support up to N inputs. In more
>>> recent Tegra chips, we have at least >32 inputs and I think Arun was
>>> saying even >64 inputs. That requires 2 or 3 or more .reg fields in
>>> struct soc_enum.
>>
>> Right, that was my guess too (the mail wasn't terribly clear with the
>> formatting, references to unpublished documents and so on) but that's
>> not a straight mux, it's a value mux, and the limit with the current
>> code is much lower on 32 bit systems (like at least some of the K1s)
>> since muxes only use one of the current register fields.
>
> It might make sense to add special code for supported muxes with a one-hot
> encoding instead of using a value mux. Having an large array where each
> entry is just 1<<n is a bit ugly in my opinion, especially if the value
> needs to be able to be larger than 2**64. But anyway the patch that modifies
> the soc_enum struct should also add the code that makes use of the new
> struct layout.

On the other hand this can actually already be implemented without any core 
changes by using a virtual mux and connecting a supply widget to each input 
which sets the bit for that input. DAPM will take care that only one of the 
supply widgets is enabled at a time.

- Lars

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [alsa-devel] [PATCH] ASoC: Add support for multi register mux
  2014-03-20 19:40               ` Lars-Peter Clausen
  (?)
@ 2014-03-21 11:37               ` Mark Brown
  -1 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-21 11:37 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Songhee Baek, Arun Shamanna Lakshmi, alsa-devel, Stephen Warren,
	tiwai, lgirdwood, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1143 bytes --]

On Thu, Mar 20, 2014 at 08:40:54PM +0100, Lars-Peter Clausen wrote:
> On 03/20/2014 08:05 PM, Lars-Peter Clausen wrote:

> >It might make sense to add special code for supported muxes with a one-hot
> >encoding instead of using a value mux. Having an large array where each
> >entry is just 1<<n is a bit ugly in my opinion, especially if the value
> >needs to be able to be larger than 2**64. But anyway the patch that modifies
> >the soc_enum struct should also add the code that makes use of the new
> >struct layout.

> On the other hand this can actually already be implemented without any core
> changes by using a virtual mux and connecting a supply widget to each input
> which sets the bit for that input. DAPM will take care that only one of the
> supply widgets is enabled at a time.

Yes, each works - either way I think we can probably come up with some
helpers in the header which hide the actual implementation from the
drivers or at least makes it obvious that this is the way to do things.
The supply widgets approach wouldn't need any changes in the core but
then generalising the two register code isn't a bad thing anyway.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-31 11:55                 ` Lars-Peter Clausen
@ 2014-03-31 12:07                   ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-31 12:07 UTC (permalink / raw)
  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'


[-- Attachment #1.1: Type: text/plain, Size: 1046 bytes --]

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.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
       [not found]       ` <5571431004A69147BCABABE4E097D66BA3EFF70CFC@HQMAIL02.nvidia.com>
@ 2014-03-28 18:10         ` Songhee Baek
  2014-03-29  2:30         ` [alsa-devel] " Songhee Baek
  1 sibling, 0 replies; 20+ messages in thread
From: Songhee Baek @ 2014-03-28 18:10 UTC (permalink / raw)
  To: 'Lars-Peter Clausen'
  Cc: Arun Shamanna Lakshmi, 'alsa-devel@alsa-project.org',
	'swarren@wwwdotorg.org', 'tiwai@suse.de',
	'lgirdwood@gmail.com',
	'linux-kernel@vger.kernel.org',
	'broonie@kernel.org'


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

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-26 22:41   ` Songhee Baek
@ 2014-03-27  9:19     ` Lars-Peter Clausen
       [not found]       ` <5571431004A69147BCABABE4E097D66BA3EFF70CFC@HQMAIL02.nvidia.com>
  0 siblings, 1 reply; 20+ messages in thread
From: Lars-Peter Clausen @ 2014-03-27  9:19 UTC (permalink / raw)
  To: Songhee Baek
  Cc: Arun Shamanna Lakshmi, alsa-devel, swarren, tiwai, lgirdwood,
	linux-kernel, broonie

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;
>>>    };
>>>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
  2014-03-26  0:02 ` Arun Shamanna Lakshmi
@ 2014-03-27  1:29   ` Mark Brown
  -1 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-27  1:29 UTC (permalink / raw)
  To: Arun Shamanna Lakshmi
  Cc: lgirdwood, swarren, perex, tiwai, alsa-devel, linux-kernel, Songhee Baek

[-- Attachment #1: Type: text/plain, Size: 1190 bytes --]

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

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH] ASoC: Add support for multi register mux
@ 2014-03-27  1:29   ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-03-27  1:29 UTC (permalink / raw)
  To: Arun Shamanna Lakshmi
  Cc: Songhee Baek, alsa-devel, lgirdwood, tiwai, swarren, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1190 bytes --]

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

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH] ASoC: Add support for multi register mux
@ 2014-03-26  0:02 ` Arun Shamanna Lakshmi
  0 siblings, 0 replies; 20+ messages in thread
From: Arun Shamanna Lakshmi @ 2014-03-26  0:02 UTC (permalink / raw)
  To: lgirdwood, broonie, swarren
  Cc: perex, tiwai, alsa-devel, linux-kernel, Arun Shamanna Lakshmi,
	Songhee Baek

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


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH] ASoC: Add support for multi register mux
@ 2014-03-26  0:02 ` Arun Shamanna Lakshmi
  0 siblings, 0 replies; 20+ messages in thread
From: Arun Shamanna Lakshmi @ 2014-03-26  0:02 UTC (permalink / raw)
  To: lgirdwood, broonie, swarren
  Cc: perex, tiwai, alsa-devel, linux-kernel, Arun Shamanna Lakshmi,
	Songhee Baek

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

^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2014-03-31 12:08 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-18 23:51 [PATCH] ASoC: Add support for multi register mux Arun Shamanna Lakshmi
2014-03-18 23:51 ` Arun Shamanna Lakshmi
2014-03-18 23:59 ` Mark Brown
2014-03-18 23:59   ` Mark Brown
2014-03-19 23:44   ` Arun Shamanna Lakshmi
2014-03-20 11:48     ` Mark Brown
2014-03-20 18:20       ` Stephen Warren
2014-03-20 18:36         ` Mark Brown
2014-03-20 18:36           ` Mark Brown
2014-03-20 19:05           ` [alsa-devel] " Lars-Peter Clausen
2014-03-20 19:40             ` Lars-Peter Clausen
2014-03-20 19:40               ` Lars-Peter Clausen
2014-03-21 11:37               ` [alsa-devel] " Mark Brown
2014-03-26  0:02 Arun Shamanna Lakshmi
2014-03-26  0:02 ` Arun Shamanna Lakshmi
2014-03-26 19:38 ` [alsa-devel] " Lars-Peter Clausen
2014-03-26 22:41   ` Songhee Baek
2014-03-27  9:19     ` Lars-Peter Clausen
     [not found]       ` <5571431004A69147BCABABE4E097D66BA3EFF70CFC@HQMAIL02.nvidia.com>
2014-03-28 18:10         ` Songhee Baek
2014-03-29  2:30         ` [alsa-devel] " Songhee Baek
2014-03-29 10:53           ` Lars-Peter Clausen
2014-03-30  6:12             ` Arun Shamanna Lakshmi
2014-03-31 11:21               ` Mark Brown
2014-03-31 11:55                 ` Lars-Peter Clausen
2014-03-31 12:07                   ` Mark Brown
2014-03-27  1:29 ` Mark Brown
2014-03-27  1:29   ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.