All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arun Shamanna Lakshmi <aruns@nvidia.com>
To: <lgirdwood@gmail.com>, <broonie@kernel.org>, <swarren@wwwdotorg.org>
Cc: <perex@perex.cz>, <tiwai@suse.de>, <alsa-devel@alsa-project.org>,
	<linux-kernel@vger.kernel.org>,
	Arun Shamanna Lakshmi <aruns@nvidia.com>,
	Songhee Baek <sbaek@nvidia.com>
Subject: [PATCH] ASoC: Add support for multi register mux
Date: Tue, 25 Mar 2014 17:02:35 -0700	[thread overview]
Message-ID: <1395792156-4178-1-git-send-email-aruns@nvidia.com> (raw)

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


WARNING: multiple messages have this Message-ID (diff)
From: Arun Shamanna Lakshmi <aruns@nvidia.com>
To: lgirdwood@gmail.com, broonie@kernel.org, swarren@wwwdotorg.org
Cc: perex@perex.cz, tiwai@suse.de, alsa-devel@alsa-project.org,
	linux-kernel@vger.kernel.org,
	Arun Shamanna Lakshmi <aruns@nvidia.com>,
	Songhee Baek <sbaek@nvidia.com>
Subject: [PATCH] ASoC: Add support for multi register mux
Date: Tue, 25 Mar 2014 17:02:35 -0700	[thread overview]
Message-ID: <1395792156-4178-1-git-send-email-aruns@nvidia.com> (raw)

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

             reply	other threads:[~2014-03-26  0:03 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-03-26  0:02 Arun Shamanna Lakshmi [this message]
2014-03-26  0:02 ` [PATCH] ASoC: Add support for multi register mux 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
2014-03-27  9:19       ` Lars-Peter Clausen
2014-03-27 18:24       ` [alsa-devel] " Songhee Baek
     [not found]       ` <5571431004A69147BCABABE4E097D66BA3EFF70CFC@HQMAIL02.nvidia.com>
2014-03-28 18:10         ` Songhee Baek
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-31 12:07                     ` Mark Brown
2014-04-01  6:08                     ` [alsa-devel] " Arun Shamanna Lakshmi
2014-03-27  1:08   ` Mark Brown
2014-03-27  4:33     ` Songhee Baek
2014-03-27  1:29 ` Mark Brown
2014-03-27  1:29   ` Mark Brown
  -- strict thread matches above, loose matches on Subject: below --
2014-03-18 23:51 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1395792156-4178-1-git-send-email-aruns@nvidia.com \
    --to=aruns@nvidia.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=sbaek@nvidia.com \
    --cc=swarren@wwwdotorg.org \
    --cc=tiwai@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.