linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec
@ 2016-10-03 11:07 Chen-Yu Tsai
  2016-10-03 11:07 ` [PATCH 01/12] ASoC: dapm: Support second register for DAPM control updates Chen-Yu Tsai
                   ` (11 more replies)
  0 siblings, 12 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

Hi everyone,

This series adds support for stereo mixer controls in DAPM and uses them
to add support for Allwinner A31's internal codec. This series is for
4.10. I'm sending them out now, so if there are any issues we can discuss
them at ELCE next week.

The A31's internal codec is similar (in terms of DMA, interface and
control layouts) to the one found in the A10/A13/A20 SoCs. However
it has more external inputs and outputs, the mixer controls are now
stereo (left/right separated), and it also has some audio processing
features (not supported).

To fully support DAPM but also provide a sane interface to userspace,
the first 4 patches attempt to implement stereo support to DAPM controls.
Otherwise we end up with a bunch of mono controls labeled "X Left" and
"X Right".

Patches 5 through 10 add support for the A31's internal codec, one
feature per patch. Hopefully this makes it easier to review. Some
features, such as PHONE inputs/outputs, audio processing, headset
jack detection and buttons, aren't supported yet.

Patch 11 adds a device node for the codec to the A31 dtsi.

Patch 12 enables the codec for the Hummingbird A31 board.


Regards
ChenYu

Chen-Yu Tsai (12):
  ASoC: dapm: Support second register for DAPM control updates
  ASoC: dapm: Implement stereo mixer control support
  ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type
  ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control
    type
  ASoC: sun4i-codec: Add support for A31 playback through headphone
    output
  ASoC: sun4i-codec: Add support for A31 Line In playback
  ASoC: sun4i-codec: Add support for A31 Line Out playback
  ASoC: sun4i-codec: Add support for A31 analog microphone inputs
  ASoC: sun4i-codec: Add support for A31 ADC capture path
  ASoC: sun4i-codec: Add support for A31 board level audio routing
  ARM: dts: sun6i: Add audio codec device node
  ARM: dts: sun6i: hummingbird: Enable internal audio codec

 .../devicetree/bindings/sound/sun4i-codec.txt      |  41 +-
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts        |  12 +
 arch/arm/boot/dts/sun6i-a31.dtsi                   |  12 +
 include/sound/soc-dapm.h                           |  14 +
 sound/soc/codecs/adau17x1.c                        |   2 +-
 sound/soc/codecs/tlv320aic3x.c                     |   2 +-
 sound/soc/codecs/wm9712.c                          |   2 +-
 sound/soc/codecs/wm9713.c                          |   2 +-
 sound/soc/soc-dapm.c                               | 116 ++--
 sound/soc/sunxi/sun4i-codec.c                      | 599 ++++++++++++++++++---
 10 files changed, 699 insertions(+), 103 deletions(-)

-- 
2.9.3

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

* [PATCH 01/12] ASoC: dapm: Support second register for DAPM control updates
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-10-03 11:07 ` [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support Chen-Yu Tsai
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

To support double channel shared controls split across 2 registers, one
for each channel, we must be able to update both registers together.

Add a second set of register fields to struct snd_soc_dapm_update, and
update the DAPM control writeback (put) callbacks to support this.

For codecs that use custom events which call into DAPM to do updates,
also clear struct snd_soc_dapm_update before using it, so the second
set of fields remains clean.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 include/sound/soc-dapm.h       |  4 ++++
 sound/soc/codecs/adau17x1.c    |  2 +-
 sound/soc/codecs/tlv320aic3x.c |  2 +-
 sound/soc/codecs/wm9712.c      |  2 +-
 sound/soc/codecs/wm9713.c      |  2 +-
 sound/soc/soc-dapm.c           | 13 +++++++++++--
 6 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index f60d755f7ac6..d5f4677776ce 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -615,6 +615,10 @@ struct snd_soc_dapm_update {
 	int reg;
 	int mask;
 	int val;
+	int reg2;
+	int mask2;
+	int val2;
+	bool has_second_set;
 };
 
 struct snd_soc_dapm_wcache {
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 439aa3ff1f99..b36511d965c8 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
 	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 	struct adau *adau = snd_soc_codec_get_drvdata(codec);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	struct snd_soc_dapm_update update;
+	struct snd_soc_dapm_update update = { 0 };
 	unsigned int stream = e->shift_l;
 	unsigned int val, change;
 	int reg;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5a8d96ec058c..8877b74b0510 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -157,7 +157,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
 	unsigned short val;
-	struct snd_soc_dapm_update update;
+	struct snd_soc_dapm_update update = { 0 };
 	int connect, change;
 
 	val = (ucontrol->value.integer.value[0] & mask);
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 557709eac698..85f7c5bb8b82 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -187,7 +187,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
 	unsigned int mixer, mask, shift, old;
-	struct snd_soc_dapm_update update;
+	struct snd_soc_dapm_update update = { 0 };
 	bool change;
 
 	mixer = mc->shift >> 8;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index e4301ddb1b84..7e4822185feb 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
 	unsigned int mixer, mask, shift, old;
-	struct snd_soc_dapm_update update;
+	struct snd_soc_dapm_update update = { 0 };
 	bool change;
 
 	mixer = mc->shift >> 8;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 5f37fe9c454b..535c20e4fac9 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1635,6 +1635,15 @@ static void dapm_widget_update(struct snd_soc_card *card)
 		dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
 			w->name, ret);
 
+	if (update->has_second_set) {
+		ret = soc_dapm_update_bits(w->dapm, update->reg2,
+					   update->mask2, update->val2);
+		if (ret < 0)
+			dev_err(w->dapm->dev,
+				"ASoC: %s DAPM update failed: %d\n",
+				w->name, ret);
+	}
+
 	for (wi = 0; wi < wlist->num_widgets; wi++) {
 		w = wlist->widgets[wi];
 
@@ -3093,7 +3102,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 	unsigned int invert = mc->invert;
 	unsigned int val;
 	int connect, change, reg_change = 0;
-	struct snd_soc_dapm_update update;
+	struct snd_soc_dapm_update update = { 0 };
 	int ret = 0;
 
 	if (snd_soc_volsw_is_stereo(mc))
@@ -3201,7 +3210,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 	unsigned int *item = ucontrol->value.enumerated.item;
 	unsigned int val, change, reg_change = 0;
 	unsigned int mask;
-	struct snd_soc_dapm_update update;
+	struct snd_soc_dapm_update update = { 0 };
 	int ret = 0;
 
 	if (item[0] >= e->items)
-- 
2.9.3

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

* [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
  2016-10-03 11:07 ` [PATCH 01/12] ASoC: dapm: Support second register for DAPM control updates Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-10-26 16:57   ` Mark Brown
  2016-10-26 17:50   ` Mark Brown
  2016-10-03 11:07 ` [PATCH 03/12] ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type Chen-Yu Tsai
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

While DAPM is mono or single channel, its controls can be shared between
widgets, such as sharing one stereo mixer control between the left and
right channel widgets.

This patch introduces support for such shared mixer controls.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 sound/soc/soc-dapm.c | 103 ++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 74 insertions(+), 29 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 535c20e4fac9..1739018ef81d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -723,7 +723,8 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 }
 
 /* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
+				       int nth_path)
 {
 	struct soc_mixer_control *mc = (struct soc_mixer_control *)
 		p->sink->kcontrol_news[i].private_value;
@@ -736,7 +737,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 
 	if (reg != SND_SOC_NOPM) {
 		soc_dapm_read(p->sink->dapm, reg, &val);
-		val = (val >> shift) & mask;
+		if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
+			if (reg != mc->rreg)
+				soc_dapm_read(p->sink->dapm, mc->rreg, &val);
+			val = (val >> mc->rshift) & mask;
+		} else {
+			val = (val >> shift) & mask;
+		}
 		if (invert)
 			val = max - val;
 		p->connect = !!val;
@@ -749,13 +756,13 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
 	struct snd_soc_dapm_path *path, const char *control_name)
 {
-	int i;
+	int i, nth_path = 0;
 
 	/* search for mixer kcontrol */
 	for (i = 0; i < path->sink->num_kcontrols; i++) {
 		if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
 			path->name = path->sink->kcontrol_news[i].name;
-			dapm_set_mixer_path_status(path, i);
+			dapm_set_mixer_path_status(path, i, nth_path++);
 			return 0;
 		}
 	}
@@ -2195,7 +2202,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
 
 /* test and update the power status of a mixer or switch widget */
 static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
-				   struct snd_kcontrol *kcontrol, int connect)
+				       struct snd_kcontrol *kcontrol,
+				       int connect, int rconnect)
 {
 	struct snd_soc_dapm_path *path;
 	int found = 0;
@@ -2204,8 +2212,16 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
 
 	/* find dapm widget path assoc with kcontrol */
 	dapm_kcontrol_for_each_path(path, kcontrol) {
+		/*
+		 * If status for the second channel was given ( >= 0 ),
+		 * consider the second and later paths as the second
+		 * channel.
+		 */
+		if (found && rconnect >= 0)
+			soc_dapm_connect_path(path, rconnect, "mixer update");
+		else
+			soc_dapm_connect_path(path, connect, "mixer update");
 		found = 1;
-		soc_dapm_connect_path(path, connect, "mixer update");
 	}
 
 	if (found)
@@ -2223,7 +2239,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
 
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 	card->update = update;
-	ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+	ret = soc_dapm_mixer_update_power(card, kcontrol, connect, -1);
 	card->update = NULL;
 	mutex_unlock(&card->dapm_mutex);
 	if (ret > 0)
@@ -3048,22 +3064,28 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
 	int reg = mc->reg;
 	unsigned int shift = mc->shift;
 	int max = mc->max;
+	unsigned int width = fls(max);
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
-	unsigned int val;
+	unsigned int reg_val, val, rval = 0;
 	int ret = 0;
 
-	if (snd_soc_volsw_is_stereo(mc))
-		dev_warn(dapm->dev,
-			 "ASoC: Control '%s' is stereo, which is not supported\n",
-			 kcontrol->id.name);
-
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 	if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
-		ret = soc_dapm_read(dapm, reg, &val);
-		val = (val >> shift) & mask;
+		ret = soc_dapm_read(dapm, reg, &reg_val);
+		val = (reg_val >> shift) & mask;
+
+		if (ret == 0 && reg != mc->rreg)
+			ret = soc_dapm_read(dapm, mc->rreg, &reg_val);
+
+		if (snd_soc_volsw_is_stereo(mc))
+			rval = (reg_val >> mc->rshift) & mask;
 	} else {
-		val = dapm_kcontrol_get_value(kcontrol);
+		reg_val = dapm_kcontrol_get_value(kcontrol);
+		val = reg_val & mask;
+
+		if (snd_soc_volsw_is_stereo(mc))
+			rval = (reg_val >> width) & mask;
 	}
 	mutex_unlock(&card->dapm_mutex);
 
@@ -3075,6 +3097,13 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
 	else
 		ucontrol->value.integer.value[0] = val;
 
+	if (snd_soc_volsw_is_stereo(mc)) {
+		if (invert)
+			ucontrol->value.integer.value[1] = max - rval;
+		else
+			ucontrol->value.integer.value[1] = rval;
+	}
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
@@ -3098,46 +3127,62 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 	int reg = mc->reg;
 	unsigned int shift = mc->shift;
 	int max = mc->max;
-	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int width = fls(max);
+	unsigned int mask = (1 << width) - 1;
 	unsigned int invert = mc->invert;
-	unsigned int val;
-	int connect, change, reg_change = 0;
+	unsigned int val, rval = 0;
+	int connect, rconnect = -1, change, reg_change = 0;
 	struct snd_soc_dapm_update update = { 0 };
 	int ret = 0;
 
-	if (snd_soc_volsw_is_stereo(mc))
-		dev_warn(dapm->dev,
-			 "ASoC: Control '%s' is stereo, which is not supported\n",
-			 kcontrol->id.name);
-
 	val = (ucontrol->value.integer.value[0] & mask);
 	connect = !!val;
 
 	if (invert)
 		val = max - val;
 
+	if (snd_soc_volsw_is_stereo(mc)) {
+		rval = (ucontrol->value.integer.value[1] & mask);
+		rconnect = !!rval;
+		if (invert)
+			rval = max - rval;
+	}
+
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-	change = dapm_kcontrol_set_value(kcontrol, val);
+	/* This assumes field width < (bits in unsigned int / 2) */
+	change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));
 
 	if (reg != SND_SOC_NOPM) {
-		mask = mask << shift;
 		val = val << shift;
+		rval = rval << mc->rshift;
 
-		reg_change = soc_dapm_test_bits(dapm, reg, mask, val);
+		reg_change = soc_dapm_test_bits(dapm, reg, mask << shift, val);
+
+		if (snd_soc_volsw_is_stereo(mc))
+			reg_change |= soc_dapm_test_bits(dapm, mc->rreg,
+							 mask << mc->rshift,
+							 rval);
 	}
 
 	if (change || reg_change) {
 		if (reg_change) {
+			if (snd_soc_volsw_is_stereo(mc)) {
+				update.has_second_set = true;
+				update.reg2 = mc->rreg;
+				update.mask2 = mask << mc->rshift;
+				update.val2 = rval;
+			}
 			update.kcontrol = kcontrol;
 			update.reg = reg;
-			update.mask = mask;
+			update.mask = mask << shift;
 			update.val = val;
 			card->update = &update;
 		}
 		change |= reg_change;
 
-		ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+		ret = soc_dapm_mixer_update_power(card, kcontrol, connect,
+						  rconnect);
 
 		card->update = NULL;
 	}
-- 
2.9.3

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

* [PATCH 03/12] ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
  2016-10-03 11:07 ` [PATCH 01/12] ASoC: dapm: Support second register for DAPM control updates Chen-Yu Tsai
  2016-10-03 11:07 ` [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-11-02 16:47   ` Applied "ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type" to the asoc tree Mark Brown
  2016-10-03 11:07 ` [PATCH 04/12] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type Chen-Yu Tsai
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

A DAPM_DOUBLE control type can be used for dual channel mixer input
selectors / mute controls in one register, possibly toggling both
channels together.

The control is meant to be shared by 2 widgets, 1 for each channel,
such that the mixer control exposed to userspace remains a combined
stereo control.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 include/sound/soc-dapm.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index d5f4677776ce..f74ec19687f8 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -272,6 +272,11 @@ struct device;
 
 
 /* dapm kcontrol types */
+#define SOC_DAPM_DOUBLE(xname, reg, lshift, rshift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+	.private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
-- 
2.9.3

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

* [PATCH 04/12] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (2 preceding siblings ...)
  2016-10-03 11:07 ` [PATCH 03/12] ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-11-02 16:46   ` Applied "ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type" to the asoc tree Mark Brown
  2016-10-03 11:07 ` [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output Chen-Yu Tsai
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

A DAPM_DOUBLE_R control type can be used for dual channel mixer input
selectors / mute controls across 2 registers, possibly toggling both
channels together.

The control is meant to be shared by 2 widgets, 1 for each channel,
such that the mixer control exposed to userspace remains a combined
stereo control.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 include/sound/soc-dapm.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index f74ec19687f8..a466f4bdc835 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -277,6 +277,11 @@ struct device;
 	.info = snd_soc_info_volsw, \
 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
 	.private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
+#define SOC_DAPM_DOUBLE_R(xname, lreg, rreg, shift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+	.private_value = SOC_DOUBLE_R_VALUE(lreg, rreg, shift, max, invert) }
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
-- 
2.9.3

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

* [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (3 preceding siblings ...)
  2016-10-03 11:07 ` [PATCH 04/12] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-10-03 11:47   ` Maxime Ripard
  2016-10-09  1:29   ` Rob Herring
  2016-10-03 11:07 ` [PATCH 06/12] ASoC: sun4i-codec: Add support for A31 Line In playback Chen-Yu Tsai
                   ` (6 subsequent siblings)
  11 siblings, 2 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31 has a similar codec to the A10/A20. The PCM parts are very
similar, with just different register offsets. The analog paths are
very different. There are more inputs and outputs.

The quirks structure is expanded to include different register offsets
and separate callbacks for creating the ASoC card. Also the DMA burst
length is increased to 8. While the A10 DMA engine supports bursts of
1, 4 and 8, the A31 engine only supports 1 and 8.

This patch adds support for the basic playback path of the A31 codec,
from the DAC to the headphones. Headphone detection, microphone,
signaling, other inputs/outputs and capture will be added later.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 .../devicetree/bindings/sound/sun4i-codec.txt      |   6 +-
 sound/soc/sunxi/sun4i-codec.c                      | 396 +++++++++++++++++----
 2 files changed, 334 insertions(+), 68 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
index 0dce690f78f5..1d2411cea98d 100644
--- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
+++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
@@ -1,8 +1,10 @@
 * Allwinner A10 Codec
 
 Required properties:
-- compatible: must be either "allwinner,sun4i-a10-codec" or
-  "allwinner,sun7i-a20-codec"
+- compatible: must be one of the following compatibles:
+		- "allwinner,sun4i-a10-codec"
+		- "allwinner,sun6i-a31-codec"
+		- "allwinner,sun7i-a20-codec"
 - reg: must contain the registers location and length
 - interrupts: must contain the codec interrupt
 - dmas: DMA channels for tx and rx dma. See the DMA client binding,
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index e047ec06d538..9916714ecb71 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -3,6 +3,7 @@
  * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
  * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
  * Copyright 2015 Adam Sampson <ats@offog.org>
+ * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
  *
  * Based on the Allwinner SDK driver, released under the GPL.
  *
@@ -24,8 +25,9 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/of_platform.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
 #include <linux/clk.h>
 #include <linux/regmap.h>
 #include <linux/gpio/consumer.h>
@@ -55,6 +57,8 @@
 #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH		(0)
 #define SUN4I_CODEC_DAC_FIFOS			(0x08)
 #define SUN4I_CODEC_DAC_TXDATA			(0x0c)
+
+/* Codec DAC side analog signal controls */
 #define SUN4I_CODEC_DAC_ACTL			(0x10)
 #define SUN4I_CODEC_DAC_ACTL_DACAENR			(31)
 #define SUN4I_CODEC_DAC_ACTL_DACAENL			(30)
@@ -81,6 +85,8 @@
 #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH		(0)
 #define SUN4I_CODEC_ADC_FIFOS			(0x20)
 #define SUN4I_CODEC_ADC_RXDATA			(0x24)
+
+/* Codec ADC side analog signal controls */
 #define SUN4I_CODEC_ADC_ACTL			(0x28)
 #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN			(31)
 #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN			(30)
@@ -93,18 +99,106 @@
 #define SUN4I_CODEC_ADC_ACTL_DDE			(3)
 #define SUN4I_CODEC_ADC_DEBUG			(0x2c)
 
-/* Other various ADC registers */
+/* FIFO counters */
 #define SUN4I_CODEC_DAC_TXCNT			(0x30)
 #define SUN4I_CODEC_ADC_RXCNT			(0x34)
+
+/* Other various ADC registers */
 #define SUN7I_CODEC_AC_DAC_CAL			(0x38)
 #define SUN7I_CODEC_AC_MIC_PHONE_CAL		(0x3c)
 
+/*** sun6i specific register offsets ***/
+#define SUN6I_CODEC_ADC_FIFOC			(0x10)
+#define SUN6I_CODEC_ADC_FIFOC_EN_AD			(28)
+#define SUN6I_CODEC_ADC_FIFOS			(0x14)
+#define SUN6I_CODEC_ADC_RXDATA			(0x18)
+#define SUN6I_CODEC_OM_DACA_CTRL		(0x20)
+#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN		(31)
+#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN		(30)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN			(29)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN			(28)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1		(23)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2		(22)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE		(21)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP		(20)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR		(19)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR		(18)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL		(17)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1		(16)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2		(15)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE		(14)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN		(13)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL		(12)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL		(11)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR		(10)
+#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS			(9)
+#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS			(8)
+#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE		(7)
+#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE		(6)
+#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL			(0)
+#define SUN6I_CODEC_OM_PA_CTRL			(0x24)
+#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN			(31)
+#define SUN6I_CODEC_OM_PA_CTRL_MIC1G			(15)
+#define SUN6I_CODEC_OM_PA_CTRL_MIC2G			(12)
+#define SUN6I_CODEC_OM_PA_CTRL_LINEING			(9)
+#define SUN6I_CODEC_OM_PA_CTRL_PHONEG			(6)
+#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG			(3)
+#define SUN6I_CODEC_OM_PA_CTRL_PHONENG			(0)
+#define SUN6I_CODEC_MIC_CTRL			(0x28)
+#define SUN6I_CODEC_MIC_CTRL_HBIASEN			(31)
+#define SUN6I_CODEC_MIC_CTRL_MBIASEN			(30)
+#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN			(28)
+#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST			(25)
+#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN			(24)
+#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST			(21)
+#define SUN6I_CODEC_MIC_CTRL_MIC2SLT			(20)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN			(19)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN			(18)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC		(17)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC		(16)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC			(11)
+#define SUN6I_CODEC_MIC_CTRL_PHONEPREG			(8)
+#define SUN6I_CODEC_ADC_ACTL			(0x2c)
+#define SUN6I_CODEC_ADC_ACTL_ADCREN			(31)
+#define SUN6I_CODEC_ADC_ACTL_ADCLEN			(30)
+#define SUN6I_CODEC_ADC_ACTL_ADCRG			(27)
+#define SUN6I_CODEC_ADC_ACTL_ADCLG			(24)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1		(13)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2		(12)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE		(11)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP		(10)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR		(9)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR		(8)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL		(7)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1		(6)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2		(5)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE		(4)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN		(3)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL		(2)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL		(1)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR		(0)
+#define SUN6I_CODEC_ADDA_TUNE			(0x30)
+#define SUN6I_CODEC_CALIBRATION			(0x34)
+#define SUN6I_CODEC_DAC_TXCNT			(0x40)
+#define SUN6I_CODEC_ADC_RXCNT			(0x44)
+#define SUN6I_CODEC_HMIC_CTL			(0x50)
+#define SUN6I_CODEC_HMIC_DATA			(0x54)
+
+/* TODO sun6i DAP (Digital Audio Processing) bits */
+
+struct sun4i_codec_regs {
+	u32	adc_fifoc;
+	u32	adc_fifos;
+	u32	adc_rxdata;
+};
+
 struct sun4i_codec {
 	struct device	*dev;
 	struct regmap	*regmap;
 	struct clk	*clk_apb;
 	struct clk	*clk_module;
 	struct gpio_desc *gpio_pa;
+	const struct sun4i_codec_regs *regs;
 
 	struct snd_dmaengine_dai_dma_data	capture_dma_data;
 	struct snd_dmaengine_dai_dma_data	playback_dma_data;
@@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
 static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
 {
 	/* Enable ADC DRQ */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
 			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
 }
@@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
 static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
 {
 	/* Disable ADC DRQ */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
 }
 
@@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
 
 
 	/* Flush RX FIFO */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 			   BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
 			   BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
 
 
 	/* Set RX FIFO trigger level */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 			   0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 			   0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
 
@@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
 	 *        Allwinner's code mentions that it is related
 	 *        related to microphone gain
 	 */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
-			   0x3 << 25,
-			   0x1 << 25);
+	if (!of_device_is_compatible(scodec->dev->of_node,
+				     "allwinner,sun6i-a31-codec")) {
+		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
+				   0x3 << 25,
+				   0x1 << 25);
+	}
 
 	if (of_device_is_compatible(scodec->dev->of_node,
 				    "allwinner,sun7i-a20-codec"))
@@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
 				   0x1 << 8);
 
 	/* Fill most significant bits with valid data MSB */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
 			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
 
@@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
 					 unsigned int hwrate)
 {
 	/* Set ADC sample rate */
-	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 			   7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
 			   hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
 
 	/* Set the number of channels we want to use */
 	if (params_channels(params) == 1)
-		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+		regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
 				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
 	else
-		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+		regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
 				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
 
 	return 0;
@@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
 				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
 				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
 
-		/* Set TX FIFO mode to padding the LSBs with 0 */
+		/* Use higher 24 bits of TX FIFO */
 		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
 				   0);
@@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
 				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
 				   0);
 
-		/* Set TX FIFO mode to repeat the MSB */
+		/* Use lower 16 bits of TX FIFO */
 		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
 				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
 				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
@@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
 	},
 };
 
-/*** Codec ***/
+/*** sun4i Codec ***/
 static const struct snd_kcontrol_new sun4i_codec_pa_mute =
 	SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
 			SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
@@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = {
 	},
 };
 
+/*** sun6i Codec ***/
+
+/* mixer controls */
+static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
+	SOC_DAPM_DOUBLE("DAC Playback Switch",
+			SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
+	SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
+			SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
+};
+
+/* headphone controls */
+static const char * const sun6i_codec_hp_src_enum_text[] = {
+	"DAC", "Mixer",
+};
+
+static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
+			    SUN6I_CODEC_OM_DACA_CTRL,
+			    SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
+			    SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
+			    sun6i_codec_hp_src_enum_text);
+
+static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
+	SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
+};
+
+/* volume / mute controls */
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
+	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
+		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
+		       sun6i_codec_dvol_scale),
+	SOC_SINGLE_TLV("Headphone Playback Volume",
+		       SUN6I_CODEC_OM_DACA_CTRL,
+		       SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
+		       sun6i_codec_hp_vol_scale),
+	SOC_DOUBLE("Headphone Playback Switch",
+		   SUN6I_CODEC_OM_DACA_CTRL,
+		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
+		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+	/* Digital parts of the DACs */
+	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
+			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+			    NULL, 0),
+
+	/* Analog parts of the DACs */
+	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
+			 SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
+			 SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
+
+	/* Mixers */
+	SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
+			sun6i_codec_mixer_controls),
+	SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
+			sun6i_codec_mixer_controls),
+
+	/* Headphone output path */
+	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
+			 SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
+	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
+			     SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("HP"),
+};
+
+static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
+	/* DAC Routes */
+	{ "Left DAC", NULL, "DAC Enable" },
+	{ "Right DAC", NULL, "DAC Enable" },
+
+	/* Left Mixer Routes */
+	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
+	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
+
+	/* Right Mixer Routes */
+	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
+	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
+
+	/* Headphone Routes */
+	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
+	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
+	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
+	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
+	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
+	{ "HP", NULL, "Headphone Amp" },
+};
+
+static struct snd_soc_codec_driver sun6i_codec_codec = {
+	.component_driver = {
+		.controls		= sun6i_codec_codec_widgets,
+		.num_controls		= ARRAY_SIZE(sun6i_codec_codec_widgets),
+		.dapm_widgets		= sun6i_codec_codec_dapm_widgets,
+		.num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
+		.dapm_routes		= sun6i_codec_codec_dapm_routes,
+		.num_dapm_routes	= ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
+	},
+};
+
 static const struct snd_soc_component_driver sun4i_codec_component = {
 	.name = "sun4i-codec",
 };
@@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
 	 },
 };
 
-static const struct regmap_config sun4i_codec_regmap_config = {
-	.reg_bits	= 32,
-	.reg_stride	= 4,
-	.val_bits	= 32,
-	.max_register	= SUN4I_CODEC_ADC_RXCNT,
-};
-
-static const struct regmap_config sun7i_codec_regmap_config = {
-	.reg_bits	= 32,
-	.reg_stride	= 4,
-	.val_bits	= 32,
-	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
-};
-
-struct sun4i_codec_quirks {
-	const struct regmap_config *regmap_config;
-};
-
-static const struct sun4i_codec_quirks sun4i_codec_quirks = {
-	.regmap_config = &sun4i_codec_regmap_config,
-};
-
-static const struct sun4i_codec_quirks sun7i_codec_quirks = {
-	.regmap_config = &sun7i_codec_regmap_config,
-};
-
-static const struct of_device_id sun4i_codec_of_match[] = {
-	{
-		.compatible = "allwinner,sun4i-a10-codec",
-		.data = &sun4i_codec_quirks,
-	},
-	{
-		.compatible = "allwinner,sun7i-a20-codec",
-		.data = &sun7i_codec_quirks,
-	},
-	{}
-};
-MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
-
 static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
 							int *num_links)
 {
@@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
 	return card;
 };
 
+static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
+{
+	struct snd_soc_card *card;
+
+	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return NULL;
+
+	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+	if (!card->dai_link)
+		return NULL;
+
+	card->dev	= dev;
+	card->name	= "sun4i-codec";
+
+	return card;
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register	= SUN4I_CODEC_ADC_RXCNT,
+};
+
+static const struct regmap_config sun6i_codec_regmap_config = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register	= SUN6I_CODEC_HMIC_DATA,
+};
+
+static const struct regmap_config sun7i_codec_regmap_config = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct sun4i_codec_regs sun4i_codec_regs = {
+	.adc_fifoc	= SUN4I_CODEC_ADC_FIFOC,
+	.adc_fifos	= SUN4I_CODEC_ADC_FIFOS,
+	.adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
+};
+
+static const struct sun4i_codec_regs sun6i_codec_regs = {
+	.adc_fifoc	= SUN6I_CODEC_ADC_FIFOC,
+	.adc_fifos	= SUN6I_CODEC_ADC_FIFOS,
+	.adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
+};
+
+struct sun4i_codec_quirks {
+	const struct regmap_config *regmap_config;
+	const struct snd_soc_codec_driver *codec;
+	const struct sun4i_codec_regs *regs;
+	struct snd_soc_card * (*create_card)(struct device *dev);
+};
+
+static const struct sun4i_codec_quirks sun4i_codec_quirks = {
+	.regmap_config	= &sun4i_codec_regmap_config,
+	.regs		= &sun4i_codec_regs,
+	.codec		= &sun4i_codec_codec,
+	.create_card	= sun4i_codec_create_card,
+};
+
+static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
+	.regmap_config	= &sun6i_codec_regmap_config,
+	.regs		= &sun6i_codec_regs,
+	.codec		= &sun6i_codec_codec,
+	.create_card	= sun6i_codec_create_card,
+};
+
+static const struct sun4i_codec_quirks sun7i_codec_quirks = {
+	.regmap_config	= &sun7i_codec_regmap_config,
+	.regs		= &sun4i_codec_regs,
+	.codec		= &sun4i_codec_codec,
+	.create_card	= sun4i_codec_create_card,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+	{
+		.compatible = "allwinner,sun4i-a10-codec",
+		.data = &sun4i_codec_quirks,
+	},
+	{
+		.compatible = "allwinner,sun6i-a31-codec",
+		.data = &sun6i_a31_codec_quirks,
+	},
+	{
+		.compatible = "allwinner,sun7i-a20-codec",
+		.data = &sun7i_codec_quirks,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
 static int sun4i_codec_probe(struct platform_device *pdev)
 {
 	struct snd_soc_card *card;
@@ -790,11 +1052,18 @@ static int sun4i_codec_probe(struct platform_device *pdev)
 	void __iomem *base;
 	int ret;
 
+	quirks = of_device_get_match_data(&pdev->dev);
+	if (quirks == NULL) {
+		dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+		return -ENODEV;
+	}
+
 	scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
 	if (!scodec)
 		return -ENOMEM;
 
 	scodec->dev = &pdev->dev;
+	scodec->regs = quirks->regs;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	base = devm_ioremap_resource(&pdev->dev, res);
@@ -803,12 +1072,6 @@ static int sun4i_codec_probe(struct platform_device *pdev)
 		return PTR_ERR(base);
 	}
 
-	quirks = of_device_get_match_data(&pdev->dev);
-	if (quirks == NULL) {
-		dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
-		return -ENODEV;
-	}
-
 	scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
 					       quirks->regmap_config);
 	if (IS_ERR(scodec->regmap)) {
@@ -846,15 +1109,15 @@ static int sun4i_codec_probe(struct platform_device *pdev)
 
 	/* DMA configuration for TX FIFO */
 	scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
-	scodec->playback_dma_data.maxburst = 4;
+	scodec->playback_dma_data.maxburst = 8;
 	scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 
 	/* DMA configuration for RX FIFO */
-	scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
-	scodec->capture_dma_data.maxburst = 4;
+	scodec->capture_dma_data.addr = res->start + quirks->regs->adc_rxdata;
+	scodec->capture_dma_data.maxburst = 8;
 	scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 
-	ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+	ret = snd_soc_register_codec(&pdev->dev, quirks->codec,
 				     &sun4i_codec_dai, 1);
 	if (ret) {
 		dev_err(&pdev->dev, "Failed to register our codec\n");
@@ -875,7 +1138,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
 		goto err_unregister_codec;
 	}
 
-	card = sun4i_codec_create_card(&pdev->dev);
+	card = quirks->create_card(&pdev->dev);
 	if (!card) {
 		dev_err(&pdev->dev, "Failed to create our card\n");
 		goto err_unregister_codec;
@@ -925,4 +1188,5 @@ MODULE_DESCRIPTION("Allwinner A10 codec driver");
 MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
 MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
 MODULE_LICENSE("GPL");
-- 
2.9.3

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

* [PATCH 06/12] ASoC: sun4i-codec: Add support for A31 Line In playback
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (4 preceding siblings ...)
  2016-10-03 11:07 ` [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-11-03 20:33   ` Applied "ASoC: sun4i-codec: Add support for A31 Line In playback" to the asoc tree Mark Brown
  2016-10-03 11:07 ` [PATCH 07/12] ASoC: sun4i-codec: Add support for A31 Line Out playback Chen-Yu Tsai
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31 integrated codec has a stereo "Line In" input. Add support for
it to the playback paths.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 sound/soc/sunxi/sun4i-codec.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 9916714ecb71..d9ec8215c1f9 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -747,6 +747,10 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
 			SUN6I_CODEC_OM_DACA_CTRL,
 			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
 			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
+	SOC_DAPM_DOUBLE("Line In Playback Switch",
+			SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
 };
 
 /* headphone controls */
@@ -767,6 +771,8 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
 /* volume / mute controls */
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
+				  -450, 150, 0);
 
 static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
@@ -780,9 +786,16 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 		   SUN6I_CODEC_OM_DACA_CTRL,
 		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
 		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+	/* Mixer pre-gains */
+	SOC_SINGLE_TLV("Line In Playback Volume",
+		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
+		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
 };
 
 static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+	/* Line In */
+	SND_SOC_DAPM_INPUT("LINEIN"),
+
 	/* Digital parts of the DACs */
 	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
 			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
@@ -818,10 +831,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	/* Left Mixer Routes */
 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
+	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
 
 	/* Right Mixer Routes */
 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
+	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
 
 	/* Headphone Routes */
 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
-- 
2.9.3

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

* [PATCH 07/12] ASoC: sun4i-codec: Add support for A31 Line Out playback
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (5 preceding siblings ...)
  2016-10-03 11:07 ` [PATCH 06/12] ASoC: sun4i-codec: Add support for A31 Line In playback Chen-Yu Tsai
@ 2016-10-03 11:07 ` Chen-Yu Tsai
  2016-10-03 11:08 ` [PATCH 08/12] ASoC: sun4i-codec: Add support for A31 analog microphone inputs Chen-Yu Tsai
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31 integrated codec has a second "Line Out" output which does not
include an integrated amplifier in its path. This path does have a
separate volume control.

This patch adds support for the playback path from the DAC to the Line
Out pins.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 sound/soc/sunxi/sun4i-codec.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index d9ec8215c1f9..5cbbab62bfd2 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -768,9 +768,26 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
 	SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
 };
 
+/* line out controls */
+static const char * const sun6i_codec_lineout_src_enum_text[] = {
+	"Stereo", "Mono Differential",
+};
+
+static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum,
+			    SUN6I_CODEC_MIC_CTRL,
+			    SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC,
+			    SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC,
+			    sun6i_codec_lineout_src_enum_text);
+
+static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = {
+	SOC_DAPM_ENUM("Line Out Source Playback Route",
+		      sun6i_codec_lineout_src_enum),
+};
+
 /* volume / mute controls */
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_lineout_vol_scale, -4800, 150, 0);
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
 				  -450, 150, 0);
 
@@ -782,10 +799,18 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 		       SUN6I_CODEC_OM_DACA_CTRL,
 		       SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
 		       sun6i_codec_hp_vol_scale),
+	SOC_SINGLE_TLV("Line Out Playback Volume",
+		       SUN6I_CODEC_MIC_CTRL,
+		       SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0,
+		       sun6i_codec_lineout_vol_scale),
 	SOC_DOUBLE("Headphone Playback Switch",
 		   SUN6I_CODEC_OM_DACA_CTRL,
 		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
 		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+	SOC_DOUBLE("Line Out Playback Switch",
+		   SUN6I_CODEC_MIC_CTRL,
+		   SUN6I_CODEC_MIC_CTRL_LINEOUTLEN,
+		   SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0),
 	/* Mixer pre-gains */
 	SOC_SINGLE_TLV("Line In Playback Volume",
 		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
@@ -821,6 +846,11 @@ static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
 	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
 			     SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
 	SND_SOC_DAPM_OUTPUT("HP"),
+
+	/* Line Out path */
+	SND_SOC_DAPM_MUX("Line Out Source Playback Route",
+			 SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src),
+	SND_SOC_DAPM_OUTPUT("LINEOUT"),
 };
 
 static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
@@ -845,6 +875,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
 	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
 	{ "HP", NULL, "Headphone Amp" },
+
+	/* Line Out Routes */
+	{ "Line Out Source Playback Route", "Stereo", "Left Mixer" },
+	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
+	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
+	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
 };
 
 static struct snd_soc_codec_driver sun6i_codec_codec = {
-- 
2.9.3

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

* [PATCH 08/12] ASoC: sun4i-codec: Add support for A31 analog microphone inputs
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (6 preceding siblings ...)
  2016-10-03 11:07 ` [PATCH 07/12] ASoC: sun4i-codec: Add support for A31 Line Out playback Chen-Yu Tsai
@ 2016-10-03 11:08 ` Chen-Yu Tsai
  2016-10-03 11:08 ` [PATCH 09/12] ASoC: sun4i-codec: Add support for A31 ADC capture path Chen-Yu Tsai
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31 internal codec has 3 microphone outputs, of which MIC2 and MIC3
are muxed internally. The resulting two microphone inputs have separate
gain controls and mixer inputs.

The codec also has 2 microphone bias pins. HBIAS is specifically for the
headphone jack, which also supports headphone detection and control
buttons. These extra functions are not supported yet. The other, MBIAS,
is for all other analog microphones.

There is also mention of digital microphone support, but documentation
is scarce, and no hardware with it is available.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 sound/soc/sunxi/sun4i-codec.c | 70 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 5cbbab62bfd2..727f54e3bd13 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -751,6 +751,14 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
 			SUN6I_CODEC_OM_DACA_CTRL,
 			SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
 			SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
+	SOC_DAPM_DOUBLE("Mic1 Playback Switch",
+			SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0),
+	SOC_DAPM_DOUBLE("Mic2 Playback Switch",
+			SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
 };
 
 /* headphone controls */
@@ -768,6 +776,21 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
 	SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
 };
 
+/* microphone controls */
+static const char * const sun6i_codec_mic2_src_enum_text[] = {
+	"Mic2", "Mic3",
+};
+
+static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum,
+			    SUN6I_CODEC_MIC_CTRL,
+			    SUN6I_CODEC_MIC_CTRL_MIC2SLT,
+			    sun6i_codec_mic2_src_enum_text);
+
+static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = {
+	SOC_DAPM_ENUM("Mic2 Amplifier Source Route",
+		      sun6i_codec_mic2_src_enum),
+};
+
 /* line out controls */
 static const char * const sun6i_codec_lineout_src_enum_text[] = {
 	"Stereo", "Mono Differential",
@@ -790,6 +813,10 @@ static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_lineout_vol_scale, -4800, 150, 0);
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
 				  -450, 150, 0);
+static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale,
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
+);
 
 static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
@@ -815,9 +842,42 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 	SOC_SINGLE_TLV("Line In Playback Volume",
 		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
 		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
+	SOC_SINGLE_TLV("Mic1 Playback Volume",
+		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G,
+		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
+	SOC_SINGLE_TLV("Mic2 Playback Volume",
+		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G,
+		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
+
+	/* Microphone Amp boost gains */
+	SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL,
+		       SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0,
+		       sun6i_codec_mic_gain_scale),
+	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
+		       SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
+		       sun6i_codec_mic_gain_scale),
 };
 
 static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+	/* Microphone inputs */
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("MIC3"),
+
+	/* Microphone Bias */
+	SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL,
+			    SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL,
+			    SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0),
+
+	/* Mic input path */
+	SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route",
+			 SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src),
+	SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL,
+			 SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL,
+			 SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0),
+
 	/* Line In */
 	SND_SOC_DAPM_INPUT("LINEIN"),
 
@@ -858,15 +918,25 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	{ "Left DAC", NULL, "DAC Enable" },
 	{ "Right DAC", NULL, "DAC Enable" },
 
+	/* Microphone Routes */
+	{ "Mic1 Amplifier", NULL, "MIC1"},
+	{ "Mic2 Amplifier Source Route", "Mic2", "MIC2" },
+	{ "Mic2 Amplifier Source Route", "Mic3", "MIC3" },
+	{ "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"},
+
 	/* Left Mixer Routes */
 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
 	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
+	{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
+	{ "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
 
 	/* Right Mixer Routes */
 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
 	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
+	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
+	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
 
 	/* Headphone Routes */
 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
-- 
2.9.3

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

* [PATCH 09/12] ASoC: sun4i-codec: Add support for A31 ADC capture path
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (7 preceding siblings ...)
  2016-10-03 11:08 ` [PATCH 08/12] ASoC: sun4i-codec: Add support for A31 analog microphone inputs Chen-Yu Tsai
@ 2016-10-03 11:08 ` Chen-Yu Tsai
  2016-11-09 14:59   ` Applied "ASoC: sun4i-codec: Add support for A31 ADC capture path" to the asoc tree Mark Brown
  2016-10-03 11:08 ` [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing Chen-Yu Tsai
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31's internal codec capture path has a mixer in front of the ADC
for each channel, capable of selecting various inputs, including
microphones, line in, phone in, and the main output mixer.

This patch adds the various controls, widgets and routes needed for
audio capture from the already supported inputs on the A31.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 sound/soc/sunxi/sun4i-codec.c | 65 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 727f54e3bd13..cd21914fd01f 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -761,6 +761,30 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
 			SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
 };
 
+/* ADC mixer controls */
+static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {
+	SOC_DAPM_DOUBLE("Mixer Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),
+	SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),
+	SOC_DAPM_DOUBLE("Line In Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),
+	SOC_DAPM_DOUBLE("Mic1 Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),
+	SOC_DAPM_DOUBLE("Mic2 Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),
+};
+
 /* headphone controls */
 static const char * const sun6i_codec_hp_src_enum_text[] = {
 	"DAC", "Mixer",
@@ -856,6 +880,10 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
 		       SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
 		       sun6i_codec_mic_gain_scale),
+	SOC_DOUBLE_TLV("ADC Capture Volume",
+		       SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,
+		       SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,
+		       sun6i_codec_out_mixer_pregain_scale),
 };
 
 static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
@@ -881,6 +909,23 @@ static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
 	/* Line In */
 	SND_SOC_DAPM_INPUT("LINEIN"),
 
+	/* Digital parts of the ADCs */
+	SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
+			    SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,
+			    NULL, 0),
+
+	/* Analog parts of the ADCs */
+	SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
+			 SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
+			 SUN6I_CODEC_ADC_ACTL_ADCREN, 0),
+
+	/* ADC Mixers */
+	SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+			sun6i_codec_adc_mixer_controls),
+	SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+			sun6i_codec_adc_mixer_controls),
+
 	/* Digital parts of the DACs */
 	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
 			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
@@ -938,6 +983,20 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
 	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
 
+	/* Left ADC Mixer Routes */
+	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
+	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
+	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
+	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
+	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
+	/* Right ADC Mixer Routes */
+	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
+	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
+	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
+	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
+	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
 	/* Headphone Routes */
 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
 	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
@@ -951,6 +1010,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
 	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
 	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
+
+	/* ADC Routes */
+	{ "Left ADC", NULL, "ADC Enable" },
+	{ "Right ADC", NULL, "ADC Enable" },
+	{ "Left ADC", NULL, "Left ADC Mixer" },
+	{ "Right ADC", NULL, "Right ADC Mixer" },
 };
 
 static struct snd_soc_codec_driver sun6i_codec_codec = {
-- 
2.9.3

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

* [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (8 preceding siblings ...)
  2016-10-03 11:08 ` [PATCH 09/12] ASoC: sun4i-codec: Add support for A31 ADC capture path Chen-Yu Tsai
@ 2016-10-03 11:08 ` Chen-Yu Tsai
  2016-10-09  1:29   ` Rob Herring
  2016-10-03 11:08 ` [PATCH 11/12] ARM: dts: sun6i: Add audio codec device node Chen-Yu Tsai
  2016-10-03 11:08 ` [PATCH 12/12] ARM: dts: sun6i: hummingbird: Enable internal audio codec Chen-Yu Tsai
  11 siblings, 1 reply; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31 SoC's codec has various inputs, outputs and microphone bias
supplies. These can be routed on the board in different ways, such as:

  - Microphones all use the MBIAS main microphone supply or one mic may
    use the HBIAS supply, which supports headset detection and buttons.

  - Line Out may be routed to an audio jack, or an onboard speaker amp
    with power controls.

Add support for specifying the audio routes in the device tree.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 .../devicetree/bindings/sound/sun4i-codec.txt      | 35 ++++++++++++++++++++++
 sound/soc/sunxi/sun4i-codec.c                      | 21 +++++++++++--
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
index 1d2411cea98d..893f37413943 100644
--- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
+++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
@@ -19,6 +19,32 @@ Required properties:
 Optional properties:
 - allwinner,pa-gpios: gpio to enable external amplifier
 
+Required properties for "allwinner,sun6i-a31-codec":
+- allwinner,audio-routing: A list of the connections between audio components.
+			   Each entry is a pair of strings, the first being the
+			   connection's sink, the second being the connection's
+			   source. Valid names include:
+
+			   Audio pins on the SoC:
+			   "HP"
+			   "LINEIN"
+			   "LINEOUT"
+			   "MIC1"
+			   "MIC2"
+			   "MIC3"
+
+			   Microphone biases from the SoC:
+			   "HBIAS"
+			   "MBIAS"
+
+			   Board connectors:
+			   "Headphone"
+			   "Headset Mic"
+			   "Line In"
+			   "Line Out"
+			   "Mic"
+			   "Speaker"
+
 Example:
 codec: codec@01c22c00 {
 	#sound-dai-cells = <0>;
@@ -29,4 +55,13 @@ codec: codec@01c22c00 {
 	clock-names = "apb", "codec";
 	dmas = <&dma 0 19>, <&dma 0 19>;
 	dma-names = "rx", "tx";
+	allwinner,pa-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
+	allwinner,audio-routing =
+		"Headphone", "HP",
+		"Speaker", "LINEOUT",
+		"LINEIN", "Line In",
+		"MIC1",	"MBIAS",
+		"MIC1", "Mic",
+		"MIC2", "HBIAS",
+		"MIC2", "Headset Mic";
 };
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index cd21914fd01f..4c364e6410dd 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -1133,9 +1133,19 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
 	return card;
 };
 
+static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_LINE("Line In", NULL),
+	SND_SOC_DAPM_LINE("Line Out", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Mic", NULL),
+	SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
+};
+
 static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
 {
 	struct snd_soc_card *card;
+	int ret;
 
 	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
 	if (!card)
@@ -1145,8 +1155,15 @@ static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
 	if (!card->dai_link)
 		return NULL;
 
-	card->dev	= dev;
-	card->name	= "sun4i-codec";
+	card->dev		= dev;
+	card->name		= "sun4i-codec";
+	card->dapm_widgets	= sun6i_codec_card_dapm_widgets;
+	card->num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
+	card->fully_routed	= true;
+
+	ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
+	if (ret)
+		dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
 
 	return card;
 };
-- 
2.9.3

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

* [PATCH 11/12] ARM: dts: sun6i: Add audio codec device node
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (9 preceding siblings ...)
  2016-10-03 11:08 ` [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing Chen-Yu Tsai
@ 2016-10-03 11:08 ` Chen-Yu Tsai
  2016-10-03 11:08 ` [PATCH 12/12] ARM: dts: sun6i: hummingbird: Enable internal audio codec Chen-Yu Tsai
  11 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The A31 SoC includes the Allwinner audio codec, capable of 24-bit
playback up to 192 kHz and 24-bit capture up to 48 kHz.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index ce1960453a0b..9024171d3aa8 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -728,6 +728,18 @@
 			reset-names = "ahb";
 		};
 
+		codec: codec@01c22c00 {
+			#sound-dai-cells = <0>;
+			compatible = "allwinner,sun6i-a31-codec";
+			reg = <0x01c22c00 0x98>;
+			interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&ccu CLK_APB1_CODEC>, <&ccu CLK_CODEC>;
+			clock-names = "apb", "codec";
+			dmas = <&dma 15>, <&dma 15>;
+			dma-names = "rx", "tx";
+			status = "disabled";
+		};
+
 		timer@01c60000 {
 			compatible = "allwinner,sun6i-a31-hstimer",
 				     "allwinner,sun7i-a20-hstimer";
-- 
2.9.3

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

* [PATCH 12/12] ARM: dts: sun6i: hummingbird: Enable internal audio codec
  2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
                   ` (10 preceding siblings ...)
  2016-10-03 11:08 ` [PATCH 11/12] ARM: dts: sun6i: Add audio codec device node Chen-Yu Tsai
@ 2016-10-03 11:08 ` Chen-Yu Tsai
  11 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
  To: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard
  Cc: Chen-Yu Tsai, alsa-devel, linux-arm-kernel, linux-kernel,
	devicetree, linux-sunxi

The Hummingbird A31 has headset and line in audio jacks and an onboard
mic routed to the pins for the SoC's internal codec. The line out pins
are routed to an onboard speaker amp, whose output is available on a
pin header.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
 arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 9a74637f677f..67f46ea279a8 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -69,6 +69,18 @@
 	};
 };
 
+&codec {
+	allwinner,audio-routing =
+		"Headphone", "HP",
+		"Speaker", "LINEOUT",
+		"LINEIN", "Line In",
+		"MIC1", "Mic",
+		"MIC2", "Headset Mic",
+		"Mic",	"MBIAS",
+		"Headset Mic", "HBIAS";
+	status = "okay";
+};
+
 &cpu0 {
 	cpu-supply = <&reg_dcdc3>;
 };
-- 
2.9.3

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

* Re: [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
  2016-10-03 11:07 ` [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output Chen-Yu Tsai
@ 2016-10-03 11:47   ` Maxime Ripard
  2016-10-04  4:26     ` Chen-Yu Tsai
  2016-10-09  1:29   ` Rob Herring
  1 sibling, 1 reply; 26+ messages in thread
From: Maxime Ripard @ 2016-10-03 11:47 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, alsa-devel,
	linux-arm-kernel, linux-kernel, devicetree, linux-sunxi

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

Hi,

On Mon, Oct 03, 2016 at 07:07:57PM +0800, Chen-Yu Tsai wrote:
> The A31 has a similar codec to the A10/A20. The PCM parts are very
> similar, with just different register offsets. The analog paths are
> very different. There are more inputs and outputs.
> 
> The quirks structure is expanded to include different register offsets
> and separate callbacks for creating the ASoC card. Also the DMA burst
> length is increased to 8. While the A10 DMA engine supports bursts of
> 1, 4 and 8, the A31 engine only supports 1 and 8.
> 
> This patch adds support for the basic playback path of the A31 codec,
> from the DAC to the headphones. Headphone detection, microphone,
> signaling, other inputs/outputs and capture will be added later.
> 
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
>  .../devicetree/bindings/sound/sun4i-codec.txt      |   6 +-
>  sound/soc/sunxi/sun4i-codec.c                      | 396 +++++++++++++++++----
>  2 files changed, 334 insertions(+), 68 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> index 0dce690f78f5..1d2411cea98d 100644
> --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> @@ -1,8 +1,10 @@
>  * Allwinner A10 Codec
>  
>  Required properties:
> -- compatible: must be either "allwinner,sun4i-a10-codec" or
> -  "allwinner,sun7i-a20-codec"
> +- compatible: must be one of the following compatibles:
> +		- "allwinner,sun4i-a10-codec"
> +		- "allwinner,sun6i-a31-codec"
> +		- "allwinner,sun7i-a20-codec"

I'm guessing it needs a reset line?

>  - reg: must contain the registers location and length
>  - interrupts: must contain the codec interrupt
>  - dmas: DMA channels for tx and rx dma. See the DMA client binding,
> diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
> index e047ec06d538..9916714ecb71 100644
> --- a/sound/soc/sunxi/sun4i-codec.c
> +++ b/sound/soc/sunxi/sun4i-codec.c
> @@ -3,6 +3,7 @@
>   * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
>   * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
>   * Copyright 2015 Adam Sampson <ats@offog.org>
> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
>   *
>   * Based on the Allwinner SDK driver, released under the GPL.
>   *
> @@ -24,8 +25,9 @@
>  #include <linux/delay.h>
>  #include <linux/slab.h>
>  #include <linux/of.h>
> -#include <linux/of_platform.h>
>  #include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
>  #include <linux/clk.h>
>  #include <linux/regmap.h>
>  #include <linux/gpio/consumer.h>
> @@ -55,6 +57,8 @@
>  #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH		(0)
>  #define SUN4I_CODEC_DAC_FIFOS			(0x08)
>  #define SUN4I_CODEC_DAC_TXDATA			(0x0c)
> +
> +/* Codec DAC side analog signal controls */
>  #define SUN4I_CODEC_DAC_ACTL			(0x10)
>  #define SUN4I_CODEC_DAC_ACTL_DACAENR			(31)
>  #define SUN4I_CODEC_DAC_ACTL_DACAENL			(30)
> @@ -81,6 +85,8 @@
>  #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH		(0)
>  #define SUN4I_CODEC_ADC_FIFOS			(0x20)
>  #define SUN4I_CODEC_ADC_RXDATA			(0x24)
> +
> +/* Codec ADC side analog signal controls */
>  #define SUN4I_CODEC_ADC_ACTL			(0x28)
>  #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN			(31)
>  #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN			(30)
> @@ -93,18 +99,106 @@
>  #define SUN4I_CODEC_ADC_ACTL_DDE			(3)
>  #define SUN4I_CODEC_ADC_DEBUG			(0x2c)
>  
> -/* Other various ADC registers */
> +/* FIFO counters */
>  #define SUN4I_CODEC_DAC_TXCNT			(0x30)
>  #define SUN4I_CODEC_ADC_RXCNT			(0x34)
> +
> +/* Other various ADC registers */
>  #define SUN7I_CODEC_AC_DAC_CAL			(0x38)
>  #define SUN7I_CODEC_AC_MIC_PHONE_CAL		(0x3c)
>  
> +/*** sun6i specific register offsets ***/
> +#define SUN6I_CODEC_ADC_FIFOC			(0x10)
> +#define SUN6I_CODEC_ADC_FIFOC_EN_AD			(28)
> +#define SUN6I_CODEC_ADC_FIFOS			(0x14)
> +#define SUN6I_CODEC_ADC_RXDATA			(0x18)
> +#define SUN6I_CODEC_OM_DACA_CTRL		(0x20)
> +#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN		(31)
> +#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN		(30)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN			(29)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN			(28)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1		(23)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2		(22)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE		(21)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP		(20)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR		(19)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR		(18)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL		(17)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1		(16)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2		(15)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE		(14)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN		(13)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL		(12)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL		(11)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR		(10)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS			(9)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS			(8)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE		(7)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE		(6)
> +#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL			(0)
> +#define SUN6I_CODEC_OM_PA_CTRL			(0x24)
> +#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN			(31)
> +#define SUN6I_CODEC_OM_PA_CTRL_MIC1G			(15)
> +#define SUN6I_CODEC_OM_PA_CTRL_MIC2G			(12)
> +#define SUN6I_CODEC_OM_PA_CTRL_LINEING			(9)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEG			(6)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG			(3)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONENG			(0)
> +#define SUN6I_CODEC_MIC_CTRL			(0x28)
> +#define SUN6I_CODEC_MIC_CTRL_HBIASEN			(31)
> +#define SUN6I_CODEC_MIC_CTRL_MBIASEN			(30)
> +#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN			(28)
> +#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST			(25)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN			(24)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST			(21)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2SLT			(20)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN			(19)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN			(18)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC		(17)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC		(16)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC			(11)
> +#define SUN6I_CODEC_MIC_CTRL_PHONEPREG			(8)
> +#define SUN6I_CODEC_ADC_ACTL			(0x2c)
> +#define SUN6I_CODEC_ADC_ACTL_ADCREN			(31)
> +#define SUN6I_CODEC_ADC_ACTL_ADCLEN			(30)
> +#define SUN6I_CODEC_ADC_ACTL_ADCRG			(27)
> +#define SUN6I_CODEC_ADC_ACTL_ADCLG			(24)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1		(13)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2		(12)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE		(11)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP		(10)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR		(9)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR		(8)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL		(7)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1		(6)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2		(5)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE		(4)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN		(3)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL		(2)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL		(1)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR		(0)
> +#define SUN6I_CODEC_ADDA_TUNE			(0x30)
> +#define SUN6I_CODEC_CALIBRATION			(0x34)
> +#define SUN6I_CODEC_DAC_TXCNT			(0x40)
> +#define SUN6I_CODEC_ADC_RXCNT			(0x44)
> +#define SUN6I_CODEC_HMIC_CTL			(0x50)
> +#define SUN6I_CODEC_HMIC_DATA			(0x54)
> +
> +/* TODO sun6i DAP (Digital Audio Processing) bits */
> +
> +struct sun4i_codec_regs {
> +	u32	adc_fifoc;
> +	u32	adc_fifos;
> +	u32	adc_rxdata;
> +};
> +
>  struct sun4i_codec {
>  	struct device	*dev;
>  	struct regmap	*regmap;
>  	struct clk	*clk_apb;
>  	struct clk	*clk_module;
>  	struct gpio_desc *gpio_pa;
> +	const struct sun4i_codec_regs *regs;

You're reimplementing reg_field here.

>  
>  	struct snd_dmaengine_dai_dma_data	capture_dma_data;
>  	struct snd_dmaengine_dai_dma_data	playback_dma_data;
> @@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
>  static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
>  {
>  	/* Enable ADC DRQ */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
>  }
> @@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
>  static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
>  {
>  	/* Disable ADC DRQ */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
>  }
>  
> @@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>  
>  
>  	/* Flush RX FIFO */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
>  
>  
>  	/* Set RX FIFO trigger level */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
>  			   0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
>  
> @@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>  	 *        Allwinner's code mentions that it is related
>  	 *        related to microphone gain
>  	 */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
> -			   0x3 << 25,
> -			   0x1 << 25);
> +	if (!of_device_is_compatible(scodec->dev->of_node,
> +				     "allwinner,sun6i-a31-codec")) {
> +		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
> +				   0x3 << 25,
> +				   0x1 << 25);
> +	}
>  
>  	if (of_device_is_compatible(scodec->dev->of_node,
>  				    "allwinner,sun7i-a20-codec"))
> @@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>  				   0x1 << 8);
>  
>  	/* Fill most significant bits with valid data MSB */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
>  			   BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
>  
> @@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
>  					 unsigned int hwrate)
>  {
>  	/* Set ADC sample rate */
> -	regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +	regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  			   7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
>  			   hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
>  
>  	/* Set the number of channels we want to use */
>  	if (params_channels(params) == 1)
> -		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +		regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
>  				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
>  	else
> -		regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> +		regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>  				   BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
>  
>  	return 0;
> @@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
>  
> -		/* Set TX FIFO mode to padding the LSBs with 0 */
> +		/* Use higher 24 bits of TX FIFO */
>  		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
>  				   0);
> @@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
>  				   0);
>  
> -		/* Set TX FIFO mode to repeat the MSB */
> +		/* Use lower 16 bits of TX FIFO */
>  		regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
>  				   BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
> @@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
>  	},
>  };
>  
> -/*** Codec ***/
> +/*** sun4i Codec ***/
>  static const struct snd_kcontrol_new sun4i_codec_pa_mute =
>  	SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
>  			SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
> @@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = {
>  	},
>  };
>  
> +/*** sun6i Codec ***/
> +
> +/* mixer controls */
> +static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
> +	SOC_DAPM_DOUBLE("DAC Playback Switch",
> +			SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
> +			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
> +	SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
> +			SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
> +			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
> +};
> +
> +/* headphone controls */
> +static const char * const sun6i_codec_hp_src_enum_text[] = {
> +	"DAC", "Mixer",
> +};
> +
> +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
> +			    SUN6I_CODEC_OM_DACA_CTRL,
> +			    SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
> +			    SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
> +			    sun6i_codec_hp_src_enum_text);
> +
> +static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
> +	SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
> +};
> +
> +/* volume / mute controls */
> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
> +
> +static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
> +	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
> +		       SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
> +		       sun6i_codec_dvol_scale),
> +	SOC_SINGLE_TLV("Headphone Playback Volume",
> +		       SUN6I_CODEC_OM_DACA_CTRL,
> +		       SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
> +		       sun6i_codec_hp_vol_scale),
> +	SOC_DOUBLE("Headphone Playback Switch",
> +		   SUN6I_CODEC_OM_DACA_CTRL,
> +		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
> +		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
> +	/* Digital parts of the DACs */
> +	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
> +			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
> +			    NULL, 0),
> +
> +	/* Analog parts of the DACs */
> +	SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
> +			 SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
> +	SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
> +			 SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
> +
> +	/* Mixers */
> +	SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
> +			sun6i_codec_mixer_controls),
> +	SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
> +			SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
> +			sun6i_codec_mixer_controls),
> +
> +	/* Headphone output path */
> +	SND_SOC_DAPM_MUX("Headphone Source Playback Route",
> +			 SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
> +	SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
> +			     SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
> +	SND_SOC_DAPM_OUTPUT("HP"),
> +};
> +
> +static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
> +	/* DAC Routes */
> +	{ "Left DAC", NULL, "DAC Enable" },
> +	{ "Right DAC", NULL, "DAC Enable" },
> +
> +	/* Left Mixer Routes */
> +	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
> +	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
> +
> +	/* Right Mixer Routes */
> +	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
> +	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
> +
> +	/* Headphone Routes */
> +	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
> +	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
> +	{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },
> +	{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
> +	{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
> +	{ "HP", NULL, "Headphone Amp" },
> +};
> +
> +static struct snd_soc_codec_driver sun6i_codec_codec = {
> +	.component_driver = {
> +		.controls		= sun6i_codec_codec_widgets,
> +		.num_controls		= ARRAY_SIZE(sun6i_codec_codec_widgets),
> +		.dapm_widgets		= sun6i_codec_codec_dapm_widgets,
> +		.num_dapm_widgets	= ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
> +		.dapm_routes		= sun6i_codec_codec_dapm_routes,
> +		.num_dapm_routes	= ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
> +	},
> +};
> +
>  static const struct snd_soc_component_driver sun4i_codec_component = {
>  	.name = "sun4i-codec",
>  };
> @@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
>  	 },
>  };
>  
> -static const struct regmap_config sun4i_codec_regmap_config = {
> -	.reg_bits	= 32,
> -	.reg_stride	= 4,
> -	.val_bits	= 32,
> -	.max_register	= SUN4I_CODEC_ADC_RXCNT,
> -};
> -
> -static const struct regmap_config sun7i_codec_regmap_config = {
> -	.reg_bits	= 32,
> -	.reg_stride	= 4,
> -	.val_bits	= 32,
> -	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
> -};
> -
> -struct sun4i_codec_quirks {
> -	const struct regmap_config *regmap_config;
> -};
> -
> -static const struct sun4i_codec_quirks sun4i_codec_quirks = {
> -	.regmap_config = &sun4i_codec_regmap_config,
> -};
> -
> -static const struct sun4i_codec_quirks sun7i_codec_quirks = {
> -	.regmap_config = &sun7i_codec_regmap_config,
> -};
> -
> -static const struct of_device_id sun4i_codec_of_match[] = {
> -	{
> -		.compatible = "allwinner,sun4i-a10-codec",
> -		.data = &sun4i_codec_quirks,
> -	},
> -	{
> -		.compatible = "allwinner,sun7i-a20-codec",
> -		.data = &sun7i_codec_quirks,
> -	},
> -	{}
> -};
> -MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> -
>  static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
>  							int *num_links)
>  {
> @@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
>  	return card;
>  };
>  
> +static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
> +{
> +	struct snd_soc_card *card;
> +
> +	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
> +	if (!card)
> +		return NULL;
> +
> +	card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
> +	if (!card->dai_link)
> +		return NULL;
> +
> +	card->dev	= dev;
> +	card->name	= "sun4i-codec";
> +
> +	return card;
> +};
> +
> +static const struct regmap_config sun4i_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN4I_CODEC_ADC_RXCNT,
> +};
> +
> +static const struct regmap_config sun6i_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN6I_CODEC_HMIC_DATA,
> +};
> +
> +static const struct regmap_config sun7i_codec_regmap_config = {
> +	.reg_bits	= 32,
> +	.reg_stride	= 4,
> +	.val_bits	= 32,
> +	.max_register	= SUN7I_CODEC_AC_MIC_PHONE_CAL,
> +};
> +
> +static const struct sun4i_codec_regs sun4i_codec_regs = {
> +	.adc_fifoc	= SUN4I_CODEC_ADC_FIFOC,
> +	.adc_fifos	= SUN4I_CODEC_ADC_FIFOS,
> +	.adc_rxdata	= SUN4I_CODEC_ADC_RXDATA,
> +};
> +
> +static const struct sun4i_codec_regs sun6i_codec_regs = {
> +	.adc_fifoc	= SUN6I_CODEC_ADC_FIFOC,
> +	.adc_fifos	= SUN6I_CODEC_ADC_FIFOS,
> +	.adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
> +};
> +
> +struct sun4i_codec_quirks {
> +	const struct regmap_config *regmap_config;
> +	const struct snd_soc_codec_driver *codec;
> +	const struct sun4i_codec_regs *regs;
> +	struct snd_soc_card * (*create_card)(struct device *dev);
> +};
> +
> +static const struct sun4i_codec_quirks sun4i_codec_quirks = {
> +	.regmap_config	= &sun4i_codec_regmap_config,
> +	.regs		= &sun4i_codec_regs,
> +	.codec		= &sun4i_codec_codec,
> +	.create_card	= sun4i_codec_create_card,
> +};
> +
> +static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
> +	.regmap_config	= &sun6i_codec_regmap_config,
> +	.regs		= &sun6i_codec_regs,
> +	.codec		= &sun6i_codec_codec,
> +	.create_card	= sun6i_codec_create_card,
> +};
> +
> +static const struct sun4i_codec_quirks sun7i_codec_quirks = {
> +	.regmap_config	= &sun7i_codec_regmap_config,
> +	.regs		= &sun4i_codec_regs,
> +	.codec		= &sun4i_codec_codec,
> +	.create_card	= sun4i_codec_create_card,
> +};
> +
> +static const struct of_device_id sun4i_codec_of_match[] = {
> +	{
> +		.compatible = "allwinner,sun4i-a10-codec",
> +		.data = &sun4i_codec_quirks,
> +	},
> +	{
> +		.compatible = "allwinner,sun6i-a31-codec",
> +		.data = &sun6i_a31_codec_quirks,
> +	},
> +	{
> +		.compatible = "allwinner,sun7i-a20-codec",
> +		.data = &sun7i_codec_quirks,
> +	},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> +

I don't really like moving blocks of code over and over again,
especially in the middle of an unrelated patch.

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
  2016-10-03 11:47   ` Maxime Ripard
@ 2016-10-04  4:26     ` Chen-Yu Tsai
  2016-10-10 10:04       ` Maxime Ripard
  0 siblings, 1 reply; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-04  4:26 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Chen-Yu Tsai, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Rob Herring, Mark Rutland, Russell King,
	Linux-ALSA, linux-arm-kernel, linux-kernel, devicetree,
	linux-sunxi

On Mon, Oct 3, 2016 at 7:47 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> Hi,
>
> On Mon, Oct 03, 2016 at 07:07:57PM +0800, Chen-Yu Tsai wrote:
>> The A31 has a similar codec to the A10/A20. The PCM parts are very
>> similar, with just different register offsets. The analog paths are
>> very different. There are more inputs and outputs.
>>
>> The quirks structure is expanded to include different register offsets
>> and separate callbacks for creating the ASoC card. Also the DMA burst
>> length is increased to 8. While the A10 DMA engine supports bursts of
>> 1, 4 and 8, the A31 engine only supports 1 and 8.
>>
>> This patch adds support for the basic playback path of the A31 codec,
>> from the DAC to the headphones. Headphone detection, microphone,
>> signaling, other inputs/outputs and capture will be added later.
>>
>> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
>> ---
>>  .../devicetree/bindings/sound/sun4i-codec.txt      |   6 +-
>>  sound/soc/sunxi/sun4i-codec.c                      | 396 +++++++++++++++++----
>>  2 files changed, 334 insertions(+), 68 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
>> index 0dce690f78f5..1d2411cea98d 100644
>> --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
>> +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
>> @@ -1,8 +1,10 @@
>>  * Allwinner A10 Codec
>>
>>  Required properties:
>> -- compatible: must be either "allwinner,sun4i-a10-codec" or
>> -  "allwinner,sun7i-a20-codec"
>> +- compatible: must be one of the following compatibles:
>> +             - "allwinner,sun4i-a10-codec"
>> +             - "allwinner,sun6i-a31-codec"
>> +             - "allwinner,sun7i-a20-codec"
>
> I'm guessing it needs a reset line?

There isn't one. I know, weird.

>>  - reg: must contain the registers location and length
>>  - interrupts: must contain the codec interrupt
>>  - dmas: DMA channels for tx and rx dma. See the DMA client binding,
>> diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
>> index e047ec06d538..9916714ecb71 100644
>> --- a/sound/soc/sunxi/sun4i-codec.c
>> +++ b/sound/soc/sunxi/sun4i-codec.c
>> @@ -3,6 +3,7 @@
>>   * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
>>   * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
>>   * Copyright 2015 Adam Sampson <ats@offog.org>
>> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
>>   *
>>   * Based on the Allwinner SDK driver, released under the GPL.
>>   *
>> @@ -24,8 +25,9 @@
>>  #include <linux/delay.h>
>>  #include <linux/slab.h>
>>  #include <linux/of.h>
>> -#include <linux/of_platform.h>
>>  #include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_platform.h>
>>  #include <linux/clk.h>
>>  #include <linux/regmap.h>
>>  #include <linux/gpio/consumer.h>
>> @@ -55,6 +57,8 @@
>>  #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH             (0)
>>  #define SUN4I_CODEC_DAC_FIFOS                        (0x08)
>>  #define SUN4I_CODEC_DAC_TXDATA                       (0x0c)
>> +
>> +/* Codec DAC side analog signal controls */
>>  #define SUN4I_CODEC_DAC_ACTL                 (0x10)
>>  #define SUN4I_CODEC_DAC_ACTL_DACAENR                 (31)
>>  #define SUN4I_CODEC_DAC_ACTL_DACAENL                 (30)
>> @@ -81,6 +85,8 @@
>>  #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH             (0)
>>  #define SUN4I_CODEC_ADC_FIFOS                        (0x20)
>>  #define SUN4I_CODEC_ADC_RXDATA                       (0x24)
>> +
>> +/* Codec ADC side analog signal controls */
>>  #define SUN4I_CODEC_ADC_ACTL                 (0x28)
>>  #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN                        (31)
>>  #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN                        (30)
>> @@ -93,18 +99,106 @@
>>  #define SUN4I_CODEC_ADC_ACTL_DDE                     (3)
>>  #define SUN4I_CODEC_ADC_DEBUG                        (0x2c)
>>
>> -/* Other various ADC registers */
>> +/* FIFO counters */
>>  #define SUN4I_CODEC_DAC_TXCNT                        (0x30)
>>  #define SUN4I_CODEC_ADC_RXCNT                        (0x34)
>> +
>> +/* Other various ADC registers */
>>  #define SUN7I_CODEC_AC_DAC_CAL                       (0x38)
>>  #define SUN7I_CODEC_AC_MIC_PHONE_CAL         (0x3c)
>>
>> +/*** sun6i specific register offsets ***/
>> +#define SUN6I_CODEC_ADC_FIFOC                        (0x10)
>> +#define SUN6I_CODEC_ADC_FIFOC_EN_AD                  (28)
>> +#define SUN6I_CODEC_ADC_FIFOS                        (0x14)
>> +#define SUN6I_CODEC_ADC_RXDATA                       (0x18)
>> +#define SUN6I_CODEC_OM_DACA_CTRL             (0x20)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN             (31)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN             (30)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN                      (29)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN                      (28)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1           (23)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2           (22)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE          (21)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP         (20)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR                (19)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR           (18)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL           (17)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1           (16)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2           (15)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE          (14)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN         (13)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL                (12)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL           (11)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR           (10)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS                       (9)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS                       (8)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE           (7)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE           (6)
>> +#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL                       (0)
>> +#define SUN6I_CODEC_OM_PA_CTRL                       (0x24)
>> +#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN                        (31)
>> +#define SUN6I_CODEC_OM_PA_CTRL_MIC1G                 (15)
>> +#define SUN6I_CODEC_OM_PA_CTRL_MIC2G                 (12)
>> +#define SUN6I_CODEC_OM_PA_CTRL_LINEING                       (9)
>> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEG                        (6)
>> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG                       (3)
>> +#define SUN6I_CODEC_OM_PA_CTRL_PHONENG                       (0)
>> +#define SUN6I_CODEC_MIC_CTRL                 (0x28)
>> +#define SUN6I_CODEC_MIC_CTRL_HBIASEN                 (31)
>> +#define SUN6I_CODEC_MIC_CTRL_MBIASEN                 (30)
>> +#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN                       (28)
>> +#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST                       (25)
>> +#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN                       (24)
>> +#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST                       (21)
>> +#define SUN6I_CODEC_MIC_CTRL_MIC2SLT                 (20)
>> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN                      (19)
>> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN                      (18)
>> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC             (17)
>> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC             (16)
>> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC                       (11)
>> +#define SUN6I_CODEC_MIC_CTRL_PHONEPREG                       (8)
>> +#define SUN6I_CODEC_ADC_ACTL                 (0x2c)
>> +#define SUN6I_CODEC_ADC_ACTL_ADCREN                  (31)
>> +#define SUN6I_CODEC_ADC_ACTL_ADCLEN                  (30)
>> +#define SUN6I_CODEC_ADC_ACTL_ADCRG                   (27)
>> +#define SUN6I_CODEC_ADC_ACTL_ADCLG                   (24)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1            (13)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2            (12)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE           (11)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP          (10)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR         (9)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR           (8)
>> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL           (7)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1            (6)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2            (5)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE           (4)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN          (3)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL         (2)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL           (1)
>> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR           (0)
>> +#define SUN6I_CODEC_ADDA_TUNE                        (0x30)
>> +#define SUN6I_CODEC_CALIBRATION                      (0x34)
>> +#define SUN6I_CODEC_DAC_TXCNT                        (0x40)
>> +#define SUN6I_CODEC_ADC_RXCNT                        (0x44)
>> +#define SUN6I_CODEC_HMIC_CTL                 (0x50)
>> +#define SUN6I_CODEC_HMIC_DATA                        (0x54)
>> +
>> +/* TODO sun6i DAP (Digital Audio Processing) bits */
>> +
>> +struct sun4i_codec_regs {
>> +     u32     adc_fifoc;
>> +     u32     adc_fifos;
>> +     u32     adc_rxdata;
>> +};
>> +
>>  struct sun4i_codec {
>>       struct device   *dev;
>>       struct regmap   *regmap;
>>       struct clk      *clk_apb;
>>       struct clk      *clk_module;
>>       struct gpio_desc *gpio_pa;
>> +     const struct sun4i_codec_regs *regs;
>
> You're reimplementing reg_field here.

Are you suggesting we do reg_fields for each register?
Or all the bit fields separately. The latter would add
quite a few pointers.

>>
>>       struct snd_dmaengine_dai_dma_data       capture_dma_data;
>>       struct snd_dmaengine_dai_dma_data       playback_dma_data;
>> @@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
>>  static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
>>  {
>>       /* Enable ADC DRQ */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +     regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
>>  }
>> @@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
>>  static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
>>  {
>>       /* Disable ADC DRQ */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +     regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
>>  }
>>
>> @@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>>
>>
>>       /* Flush RX FIFO */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +     regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
>>
>>
>>       /* Set RX FIFO trigger level */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +     regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                          0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
>>                          0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
>>
>> @@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>>        *        Allwinner's code mentions that it is related
>>        *        related to microphone gain
>>        */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
>> -                        0x3 << 25,
>> -                        0x1 << 25);
>> +     if (!of_device_is_compatible(scodec->dev->of_node,
>> +                                  "allwinner,sun6i-a31-codec")) {
>> +             regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
>> +                                0x3 << 25,
>> +                                0x1 << 25);
>> +     }
>>
>>       if (of_device_is_compatible(scodec->dev->of_node,
>>                                   "allwinner,sun7i-a20-codec"))
>> @@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>>                                  0x1 << 8);
>>
>>       /* Fill most significant bits with valid data MSB */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +     regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
>>                          BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
>>
>> @@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
>>                                        unsigned int hwrate)
>>  {
>>       /* Set ADC sample rate */
>> -     regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +     regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                          7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
>>                          hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
>>
>>       /* Set the number of channels we want to use */
>>       if (params_channels(params) == 1)
>> -             regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +             regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                                  BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
>>                                  BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
>>       else
>> -             regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
>> +             regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
>>                                  BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
>>
>>       return 0;
>> @@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
>>                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
>>                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
>>
>> -             /* Set TX FIFO mode to padding the LSBs with 0 */
>> +             /* Use higher 24 bits of TX FIFO */
>>               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
>>                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
>>                                  0);
>> @@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
>>                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
>>                                  0);
>>
>> -             /* Set TX FIFO mode to repeat the MSB */
>> +             /* Use lower 16 bits of TX FIFO */
>>               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
>>                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
>>                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
>> @@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
>>       },
>>  };
>>
>> -/*** Codec ***/
>> +/*** sun4i Codec ***/
>>  static const struct snd_kcontrol_new sun4i_codec_pa_mute =
>>       SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
>>                       SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
>> @@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = {
>>       },
>>  };
>>
>> +/*** sun6i Codec ***/
>> +
>> +/* mixer controls */
>> +static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
>> +     SOC_DAPM_DOUBLE("DAC Playback Switch",
>> +                     SUN6I_CODEC_OM_DACA_CTRL,
>> +                     SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
>> +                     SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
>> +     SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
>> +                     SUN6I_CODEC_OM_DACA_CTRL,
>> +                     SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
>> +                     SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
>> +};
>> +
>> +/* headphone controls */
>> +static const char * const sun6i_codec_hp_src_enum_text[] = {
>> +     "DAC", "Mixer",
>> +};
>> +
>> +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
>> +                         SUN6I_CODEC_OM_DACA_CTRL,
>> +                         SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
>> +                         SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
>> +                         sun6i_codec_hp_src_enum_text);
>> +
>> +static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
>> +     SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
>> +};
>> +
>> +/* volume / mute controls */
>> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
>> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
>> +
>> +static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
>> +     SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
>> +                    SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
>> +                    sun6i_codec_dvol_scale),
>> +     SOC_SINGLE_TLV("Headphone Playback Volume",
>> +                    SUN6I_CODEC_OM_DACA_CTRL,
>> +                    SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
>> +                    sun6i_codec_hp_vol_scale),
>> +     SOC_DOUBLE("Headphone Playback Switch",
>> +                SUN6I_CODEC_OM_DACA_CTRL,
>> +                SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
>> +                SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
>> +     /* Digital parts of the DACs */
>> +     SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
>> +                         SUN4I_CODEC_DAC_DPC_EN_DA, 0,
>> +                         NULL, 0),
>> +
>> +     /* Analog parts of the DACs */
>> +     SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
>> +                      SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
>> +     SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
>> +                      SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
>> +
>> +     /* Mixers */
>> +     SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
>> +                     SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
>> +                     sun6i_codec_mixer_controls),
>> +     SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
>> +                     SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
>> +                     sun6i_codec_mixer_controls),
>> +
>> +     /* Headphone output path */
>> +     SND_SOC_DAPM_MUX("Headphone Source Playback Route",
>> +                      SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
>> +     SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
>> +                          SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
>> +     SND_SOC_DAPM_OUTPUT("HP"),
>> +};
>> +
>> +static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
>> +     /* DAC Routes */
>> +     { "Left DAC", NULL, "DAC Enable" },
>> +     { "Right DAC", NULL, "DAC Enable" },
>> +
>> +     /* Left Mixer Routes */
>> +     { "Left Mixer", "DAC Playback Switch", "Left DAC" },
>> +     { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
>> +
>> +     /* Right Mixer Routes */
>> +     { "Right Mixer", "DAC Playback Switch", "Right DAC" },
>> +     { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
>> +
>> +     /* Headphone Routes */
>> +     { "Headphone Source Playback Route", "DAC", "Left DAC" },
>> +     { "Headphone Source Playback Route", "DAC", "Right DAC" },
>> +     { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
>> +     { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
>> +     { "Headphone Amp", NULL, "Headphone Source Playback Route" },
>> +     { "HP", NULL, "Headphone Amp" },
>> +};
>> +
>> +static struct snd_soc_codec_driver sun6i_codec_codec = {
>> +     .component_driver = {
>> +             .controls               = sun6i_codec_codec_widgets,
>> +             .num_controls           = ARRAY_SIZE(sun6i_codec_codec_widgets),
>> +             .dapm_widgets           = sun6i_codec_codec_dapm_widgets,
>> +             .num_dapm_widgets       = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
>> +             .dapm_routes            = sun6i_codec_codec_dapm_routes,
>> +             .num_dapm_routes        = ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
>> +     },
>> +};
>> +
>>  static const struct snd_soc_component_driver sun4i_codec_component = {
>>       .name = "sun4i-codec",
>>  };
>> @@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
>>        },
>>  };
>>
>> -static const struct regmap_config sun4i_codec_regmap_config = {
>> -     .reg_bits       = 32,
>> -     .reg_stride     = 4,
>> -     .val_bits       = 32,
>> -     .max_register   = SUN4I_CODEC_ADC_RXCNT,
>> -};
>> -
>> -static const struct regmap_config sun7i_codec_regmap_config = {
>> -     .reg_bits       = 32,
>> -     .reg_stride     = 4,
>> -     .val_bits       = 32,
>> -     .max_register   = SUN7I_CODEC_AC_MIC_PHONE_CAL,
>> -};
>> -
>> -struct sun4i_codec_quirks {
>> -     const struct regmap_config *regmap_config;
>> -};
>> -
>> -static const struct sun4i_codec_quirks sun4i_codec_quirks = {
>> -     .regmap_config = &sun4i_codec_regmap_config,
>> -};
>> -
>> -static const struct sun4i_codec_quirks sun7i_codec_quirks = {
>> -     .regmap_config = &sun7i_codec_regmap_config,
>> -};
>> -
>> -static const struct of_device_id sun4i_codec_of_match[] = {
>> -     {
>> -             .compatible = "allwinner,sun4i-a10-codec",
>> -             .data = &sun4i_codec_quirks,
>> -     },
>> -     {
>> -             .compatible = "allwinner,sun7i-a20-codec",
>> -             .data = &sun7i_codec_quirks,
>> -     },
>> -     {}
>> -};
>> -MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
>> -
>>  static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
>>                                                       int *num_links)
>>  {
>> @@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
>>       return card;
>>  };
>>
>> +static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
>> +{
>> +     struct snd_soc_card *card;
>> +
>> +     card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
>> +     if (!card)
>> +             return NULL;
>> +
>> +     card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
>> +     if (!card->dai_link)
>> +             return NULL;
>> +
>> +     card->dev       = dev;
>> +     card->name      = "sun4i-codec";
>> +
>> +     return card;
>> +};
>> +
>> +static const struct regmap_config sun4i_codec_regmap_config = {
>> +     .reg_bits       = 32,
>> +     .reg_stride     = 4,
>> +     .val_bits       = 32,
>> +     .max_register   = SUN4I_CODEC_ADC_RXCNT,
>> +};
>> +
>> +static const struct regmap_config sun6i_codec_regmap_config = {
>> +     .reg_bits       = 32,
>> +     .reg_stride     = 4,
>> +     .val_bits       = 32,
>> +     .max_register   = SUN6I_CODEC_HMIC_DATA,
>> +};
>> +
>> +static const struct regmap_config sun7i_codec_regmap_config = {
>> +     .reg_bits       = 32,
>> +     .reg_stride     = 4,
>> +     .val_bits       = 32,
>> +     .max_register   = SUN7I_CODEC_AC_MIC_PHONE_CAL,
>> +};
>> +
>> +static const struct sun4i_codec_regs sun4i_codec_regs = {
>> +     .adc_fifoc      = SUN4I_CODEC_ADC_FIFOC,
>> +     .adc_fifos      = SUN4I_CODEC_ADC_FIFOS,
>> +     .adc_rxdata     = SUN4I_CODEC_ADC_RXDATA,
>> +};
>> +
>> +static const struct sun4i_codec_regs sun6i_codec_regs = {
>> +     .adc_fifoc      = SUN6I_CODEC_ADC_FIFOC,
>> +     .adc_fifos      = SUN6I_CODEC_ADC_FIFOS,
>> +     .adc_rxdata     = SUN6I_CODEC_ADC_RXDATA,
>> +};
>> +
>> +struct sun4i_codec_quirks {
>> +     const struct regmap_config *regmap_config;
>> +     const struct snd_soc_codec_driver *codec;
>> +     const struct sun4i_codec_regs *regs;
>> +     struct snd_soc_card * (*create_card)(struct device *dev);
>> +};
>> +
>> +static const struct sun4i_codec_quirks sun4i_codec_quirks = {
>> +     .regmap_config  = &sun4i_codec_regmap_config,
>> +     .regs           = &sun4i_codec_regs,
>> +     .codec          = &sun4i_codec_codec,
>> +     .create_card    = sun4i_codec_create_card,
>> +};
>> +
>> +static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
>> +     .regmap_config  = &sun6i_codec_regmap_config,
>> +     .regs           = &sun6i_codec_regs,
>> +     .codec          = &sun6i_codec_codec,
>> +     .create_card    = sun6i_codec_create_card,
>> +};
>> +
>> +static const struct sun4i_codec_quirks sun7i_codec_quirks = {
>> +     .regmap_config  = &sun7i_codec_regmap_config,
>> +     .regs           = &sun4i_codec_regs,
>> +     .codec          = &sun4i_codec_codec,
>> +     .create_card    = sun4i_codec_create_card,
>> +};
>> +
>> +static const struct of_device_id sun4i_codec_of_match[] = {
>> +     {
>> +             .compatible = "allwinner,sun4i-a10-codec",
>> +             .data = &sun4i_codec_quirks,
>> +     },
>> +     {
>> +             .compatible = "allwinner,sun6i-a31-codec",
>> +             .data = &sun6i_a31_codec_quirks,
>> +     },
>> +     {
>> +             .compatible = "allwinner,sun7i-a20-codec",
>> +             .data = &sun7i_codec_quirks,
>> +     },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
>> +
>
> I don't really like moving blocks of code over and over again,
> especially in the middle of an unrelated patch.

It's not completely unrelated. I want different create_card
functions for the different SoCs, and that has to be part of the
quirks, so the quirks and the of_match list have to be moved
below them. I suppose I could leave the regmap parts in place,
but keeping them together is nicer.

If I split out the addition of the .create_card field and
code movement into a separate patch, would that be OK?

Thanks
ChenYu

>
> Thanks!
> Maxime
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

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

* Re: [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
  2016-10-03 11:07 ` [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output Chen-Yu Tsai
  2016-10-03 11:47   ` Maxime Ripard
@ 2016-10-09  1:29   ` Rob Herring
  1 sibling, 0 replies; 26+ messages in thread
From: Rob Herring @ 2016-10-09  1:29 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Mark Rutland, Russell King, Maxime Ripard, alsa-devel,
	linux-arm-kernel, linux-kernel, devicetree, linux-sunxi

On Mon, Oct 03, 2016 at 07:07:57PM +0800, Chen-Yu Tsai wrote:
> The A31 has a similar codec to the A10/A20. The PCM parts are very
> similar, with just different register offsets. The analog paths are
> very different. There are more inputs and outputs.
> 
> The quirks structure is expanded to include different register offsets
> and separate callbacks for creating the ASoC card. Also the DMA burst
> length is increased to 8. While the A10 DMA engine supports bursts of
> 1, 4 and 8, the A31 engine only supports 1 and 8.
> 
> This patch adds support for the basic playback path of the A31 codec,
> from the DAC to the headphones. Headphone detection, microphone,
> signaling, other inputs/outputs and capture will be added later.
> 
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
>  .../devicetree/bindings/sound/sun4i-codec.txt      |   6 +-

Acked-by: Rob Herring <robh@kernel.org>

>  sound/soc/sunxi/sun4i-codec.c                      | 396 +++++++++++++++++----
>  2 files changed, 334 insertions(+), 68 deletions(-)

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

* Re: [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing
  2016-10-03 11:08 ` [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing Chen-Yu Tsai
@ 2016-10-09  1:29   ` Rob Herring
  0 siblings, 0 replies; 26+ messages in thread
From: Rob Herring @ 2016-10-09  1:29 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Mark Rutland, Russell King, Maxime Ripard, alsa-devel,
	linux-arm-kernel, linux-kernel, devicetree, linux-sunxi

On Mon, Oct 03, 2016 at 07:08:02PM +0800, Chen-Yu Tsai wrote:
> The A31 SoC's codec has various inputs, outputs and microphone bias
> supplies. These can be routed on the board in different ways, such as:
> 
>   - Microphones all use the MBIAS main microphone supply or one mic may
>     use the HBIAS supply, which supports headset detection and buttons.
> 
>   - Line Out may be routed to an audio jack, or an onboard speaker amp
>     with power controls.
> 
> Add support for specifying the audio routes in the device tree.
> 
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
>  .../devicetree/bindings/sound/sun4i-codec.txt      | 35 ++++++++++++++++++++++

Acked-by: Rob Herring <robh@kernel.org>

>  sound/soc/sunxi/sun4i-codec.c                      | 21 +++++++++++--
>  2 files changed, 54 insertions(+), 2 deletions(-)

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

* Re: [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
  2016-10-04  4:26     ` Chen-Yu Tsai
@ 2016-10-10 10:04       ` Maxime Ripard
  0 siblings, 0 replies; 26+ messages in thread
From: Maxime Ripard @ 2016-10-10 10:04 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Liam Girdwood, Mark Brown, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Linux-ALSA,
	linux-arm-kernel, linux-kernel, devicetree, linux-sunxi

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

On Tue, Oct 04, 2016 at 12:26:08PM +0800, Chen-Yu Tsai wrote:
> >> +struct sun4i_codec_regs {
> >> +     u32     adc_fifoc;
> >> +     u32     adc_fifos;
> >> +     u32     adc_rxdata;
> >> +};
> >> +
> >>  struct sun4i_codec {
> >>       struct device   *dev;
> >>       struct regmap   *regmap;
> >>       struct clk      *clk_apb;
> >>       struct clk      *clk_module;
> >>       struct gpio_desc *gpio_pa;
> >> +     const struct sun4i_codec_regs *regs;
> >
> > You're reimplementing reg_field here.
> 
> Are you suggesting we do reg_fields for each register?
> Or all the bit fields separately. The latter would add
> quite a few pointers.

only the one that change, so judging from your structure, only the ADC
fifo control, status and data registers.
> >> +static const struct of_device_id sun4i_codec_of_match[] = {
> >> +     {
> >> +             .compatible = "allwinner,sun4i-a10-codec",
> >> +             .data = &sun4i_codec_quirks,
> >> +     },
> >> +     {
> >> +             .compatible = "allwinner,sun6i-a31-codec",
> >> +             .data = &sun6i_a31_codec_quirks,
> >> +     },
> >> +     {
> >> +             .compatible = "allwinner,sun7i-a20-codec",
> >> +             .data = &sun7i_codec_quirks,
> >> +     },
> >> +     {}
> >> +};
> >> +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> >> +
> >
> > I don't really like moving blocks of code over and over again,
> > especially in the middle of an unrelated patch.
> 
> It's not completely unrelated. I want different create_card
> functions for the different SoCs, and that has to be part of the
> quirks, so the quirks and the of_match list have to be moved
> below them. I suppose I could leave the regmap parts in place,
> but keeping them together is nicer.
> 
> If I split out the addition of the .create_card field and
> code movement into a separate patch, would that be OK?

Yep, that would work.

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support
  2016-10-03 11:07 ` [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support Chen-Yu Tsai
@ 2016-10-26 16:57   ` Mark Brown
  2016-10-27  1:20     ` Chen-Yu Tsai
  2016-10-26 17:50   ` Mark Brown
  1 sibling, 1 reply; 26+ messages in thread
From: Mark Brown @ 2016-10-26 16:57 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Rob Herring,
	Mark Rutland, Russell King, Maxime Ripard, alsa-devel,
	linux-arm-kernel, linux-kernel, devicetree, linux-sunxi

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

On Mon, Oct 03, 2016 at 07:07:54PM +0800, Chen-Yu Tsai wrote:

> While DAPM is mono or single channel, its controls can be shared between
> widgets, such as sharing one stereo mixer control between the left and
> right channel widgets.

> This patch introduces support for such shared mixer controls.

Based on this changelog I'm really not sure what the intended semantic
of this change is which makes it difficult to review.  What are you
expecting these controls to look like and how are you expecting them to
work?

> -static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
> +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
> +				       int nth_path)

It looks like the goal is to attach more than one path to a single
control somehow?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

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

* Re: [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support
  2016-10-03 11:07 ` [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support Chen-Yu Tsai
  2016-10-26 16:57   ` Mark Brown
@ 2016-10-26 17:50   ` Mark Brown
  2016-10-27 14:02     ` Chen-Yu Tsai
  1 sibling, 1 reply; 26+ messages in thread
From: Mark Brown @ 2016-10-26 17:50 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Rob Herring,
	Mark Rutland, Russell King, Maxime Ripard, alsa-devel,
	linux-arm-kernel, linux-kernel, devicetree, linux-sunxi

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

On Mon, Oct 03, 2016 at 07:07:54PM +0800, Chen-Yu Tsai wrote:

>  	/* find dapm widget path assoc with kcontrol */
>  	dapm_kcontrol_for_each_path(path, kcontrol) {
> +		/*
> +		 * If status for the second channel was given ( >= 0 ),
> +		 * consider the second and later paths as the second
> +		 * channel.
> +		 */
> +		if (found && rconnect >= 0)
> +			soc_dapm_connect_path(path, rconnect, "mixer update");
> +		else
> +			soc_dapm_connect_path(path, connect, "mixer update");
>  		found = 1;
> -		soc_dapm_connect_path(path, connect, "mixer update");

This really only works for two channels with the current inteface - the
comment makes it sound like it'll work for more but we can only pass in
two (and there's only support for specifying two everywhere).

> -	change = dapm_kcontrol_set_value(kcontrol, val);
> +	/* This assumes field width < (bits in unsigned int / 2) */
> +	change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));

That seems like a bit of an assumption in cases where we've got a single
control for both power and volume?  They're very rare though, I'm not
even sure any exist.  It'd be good to have a check in the code just in
case it does come up, it'll likely be a nightmare to debug if someone
does run into it.

Otherwise I think this makes sense.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

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

* Re: [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support
  2016-10-26 16:57   ` Mark Brown
@ 2016-10-27  1:20     ` Chen-Yu Tsai
  0 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-27  1:20 UTC (permalink / raw)
  To: Mark Brown
  Cc: Chen-Yu Tsai, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard,
	Linux-ALSA, linux-arm-kernel, linux-kernel, devicetree,
	linux-sunxi

On Thu, Oct 27, 2016 at 12:57 AM, Mark Brown <broonie@kernel.org> wrote:
> On Mon, Oct 03, 2016 at 07:07:54PM +0800, Chen-Yu Tsai wrote:
>
>> While DAPM is mono or single channel, its controls can be shared between
>> widgets, such as sharing one stereo mixer control between the left and
>> right channel widgets.
>
>> This patch introduces support for such shared mixer controls.
>
> Based on this changelog I'm really not sure what the intended semantic
> of this change is which makes it difficult to review.  What are you
> expecting these controls to look like and how are you expecting them to
> work?
>
>> -static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
>> +static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
>> +                                    int nth_path)
>
> It looks like the goal is to attach more than one path to a single
> control somehow?

Correct. I'll try to expand the commit log and add an example diagram.

ChenYu

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

* Re: [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support
  2016-10-26 17:50   ` Mark Brown
@ 2016-10-27 14:02     ` Chen-Yu Tsai
  0 siblings, 0 replies; 26+ messages in thread
From: Chen-Yu Tsai @ 2016-10-27 14:02 UTC (permalink / raw)
  To: Mark Brown
  Cc: Chen-Yu Tsai, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Rob Herring, Mark Rutland, Russell King, Maxime Ripard,
	Linux-ALSA, linux-arm-kernel, linux-kernel, devicetree,
	linux-sunxi

On Thu, Oct 27, 2016 at 1:50 AM, Mark Brown <broonie@kernel.org> wrote:
> On Mon, Oct 03, 2016 at 07:07:54PM +0800, Chen-Yu Tsai wrote:
>
>>       /* find dapm widget path assoc with kcontrol */
>>       dapm_kcontrol_for_each_path(path, kcontrol) {
>> +             /*
>> +              * If status for the second channel was given ( >= 0 ),
>> +              * consider the second and later paths as the second
>> +              * channel.
>> +              */
>> +             if (found && rconnect >= 0)
>> +                     soc_dapm_connect_path(path, rconnect, "mixer update");
>> +             else
>> +                     soc_dapm_connect_path(path, connect, "mixer update");
>>               found = 1;
>> -             soc_dapm_connect_path(path, connect, "mixer update");
>
> This really only works for two channels with the current inteface - the
> comment makes it sound like it'll work for more but we can only pass in
> two (and there's only support for specifying two everywhere).

I could rework it to pass a list of connected status' and the number
of elements instead, but it wouldn't work well for situations where
the number of channels on the kcontrol != the number of paths. I'm not
sure if that's even a valid setup, but it does work with the current
core code.

On the other hand, are there kcontrols that are multi-channel
(> 2 channels)?

I'm inclined to just fixup the comment to make it clear that the
implementation supports stereo, i.e. 2 channels, only.

>
>> -     change = dapm_kcontrol_set_value(kcontrol, val);
>> +     /* This assumes field width < (bits in unsigned int / 2) */
>> +     change = dapm_kcontrol_set_value(kcontrol, val | (rval << width));
>
> That seems like a bit of an assumption in cases where we've got a single
> control for both power and volume?  They're very rare though, I'm not
> even sure any exist.  It'd be good to have a check in the code just in
> case it does come up, it'll likely be a nightmare to debug if someone
> does run into it.

Agreed. I'll put a check and warning before it.

>
> Otherwise I think this makes sense.

Thanks for the review!

Regards
ChenYu

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

* Applied "ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type" to the asoc tree
  2016-10-03 11:07 ` [PATCH 04/12] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type Chen-Yu Tsai
@ 2016-11-02 16:46   ` Mark Brown
  0 siblings, 0 replies; 26+ messages in thread
From: Mark Brown @ 2016-11-02 16:46 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Mark Brown, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Rob Herring, Mark Rutland, Russell King,
	Maxime Ripard, devicetree, alsa-devel, linux-sunxi, linux-kernel,
	linux-arm-kernel, alsa-devel

The patch

   ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 02866eab0f0d88c4b6a68de72022c2b26f0359b5 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Wed, 2 Nov 2016 15:36:01 +0800
Subject: [PATCH] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual
 register control type

A DAPM_DOUBLE_R control type can be used for dual channel mixer input
selectors / mute controls across 2 registers, possibly toggling both
channels together.

The control is meant to be shared by 2 widgets, 1 for each channel,
such that the mixer control exposed to userspace remains a combined
stereo control.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 include/sound/soc-dapm.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index f74ec19687f8..a466f4bdc835 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -277,6 +277,11 @@ struct device;
 	.info = snd_soc_info_volsw, \
 	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
 	.private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
+#define SOC_DAPM_DOUBLE_R(xname, lreg, rreg, shift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+	.private_value = SOC_DOUBLE_R_VALUE(lreg, rreg, shift, max, invert) }
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
-- 
2.10.1

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

* Applied "ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type" to the asoc tree
  2016-10-03 11:07 ` [PATCH 03/12] ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type Chen-Yu Tsai
@ 2016-11-02 16:47   ` Mark Brown
  0 siblings, 0 replies; 26+ messages in thread
From: Mark Brown @ 2016-11-02 16:47 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Mark Brown, Liam Girdwood, Mark Brown, Jaroslav Kysela,
	Takashi Iwai, Rob Herring, Mark Rutland, Russell King,
	Maxime Ripard, devicetree, alsa-devel, linux-sunxi, linux-kernel,
	linux-arm-kernel, alsa-devel

The patch

   ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 9ee7ef31b5a07cdca88cae023c613e045af935b9 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Wed, 2 Nov 2016 15:36:00 +0800
Subject: [PATCH] ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type

A DAPM_DOUBLE control type can be used for dual channel mixer input
selectors / mute controls in one register, possibly toggling both
channels together.

The control is meant to be shared by 2 widgets, 1 for each channel,
such that the mixer control exposed to userspace remains a combined
stereo control.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 include/sound/soc-dapm.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index d5f4677776ce..f74ec19687f8 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -272,6 +272,11 @@ struct device;
 
 
 /* dapm kcontrol types */
+#define SOC_DAPM_DOUBLE(xname, reg, lshift, rshift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+	.private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, \
-- 
2.10.1

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

* Applied "ASoC: sun4i-codec: Add support for A31 Line In playback" to the asoc tree
  2016-10-03 11:07 ` [PATCH 06/12] ASoC: sun4i-codec: Add support for A31 Line In playback Chen-Yu Tsai
@ 2016-11-03 20:33   ` Mark Brown
  0 siblings, 0 replies; 26+ messages in thread
From: Mark Brown @ 2016-11-03 20:33 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Maxime Ripard, Mark Brown, Liam Girdwood, Mark Brown,
	Jaroslav Kysela, Takashi Iwai, Rob Herring, Mark Rutland,
	Russell King, Maxime Ripard, devicetree, alsa-devel, linux-sunxi,
	linux-kernel, linux-arm-kernel, alsa-devel

The patch

   ASoC: sun4i-codec: Add support for A31 Line In playback

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From dff5051250674fce575fa36c22b2f007363e42d0 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Thu, 3 Nov 2016 15:55:49 +0800
Subject: [PATCH] ASoC: sun4i-codec: Add support for A31 Line In playback

The A31 integrated codec has a stereo "Line In" input. Add support for
it to the playback paths.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/sunxi/sun4i-codec.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index d4b2186b5d84..72a84f76aa57 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -772,6 +772,10 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
 			SUN6I_CODEC_OM_DACA_CTRL,
 			SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
 			SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
+	SOC_DAPM_DOUBLE("Line In Playback Switch",
+			SUN6I_CODEC_OM_DACA_CTRL,
+			SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
+			SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
 };
 
 /* headphone controls */
@@ -793,6 +797,8 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
 /* volume / mute controls */
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
 static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
+				  -450, 150, 0);
 
 static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 	SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
@@ -806,9 +812,16 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 		   SUN6I_CODEC_OM_DACA_CTRL,
 		   SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
 		   SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+	/* Mixer pre-gains */
+	SOC_SINGLE_TLV("Line In Playback Volume",
+		       SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
+		       0x7, 0, sun6i_codec_out_mixer_pregain_scale),
 };
 
 static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+	/* Line In */
+	SND_SOC_DAPM_INPUT("LINEIN"),
+
 	/* Digital parts of the DACs */
 	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
 			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
@@ -850,10 +863,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	/* Left Mixer Routes */
 	{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
 	{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
+	{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
 
 	/* Right Mixer Routes */
 	{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
 	{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
+	{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
 
 	/* Headphone Routes */
 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
-- 
2.10.1

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

* Applied "ASoC: sun4i-codec: Add support for A31 ADC capture path" to the asoc tree
  2016-10-03 11:08 ` [PATCH 09/12] ASoC: sun4i-codec: Add support for A31 ADC capture path Chen-Yu Tsai
@ 2016-11-09 14:59   ` Mark Brown
  0 siblings, 0 replies; 26+ messages in thread
From: Mark Brown @ 2016-11-09 14:59 UTC (permalink / raw)
  To: Chen-Yu Tsai
  Cc: Maxime Ripard, Mark Brown, Liam Girdwood, Mark Brown,
	Jaroslav Kysela, Takashi Iwai, Rob Herring, Mark Rutland,
	Russell King, Maxime Ripard, devicetree, alsa-devel, linux-sunxi,
	linux-kernel, linux-arm-kernel, alsa-devel

The patch

   ASoC: sun4i-codec: Add support for A31 ADC capture path

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 24c99f843208df70ec7d1e04aa405f7e4c36f228 Mon Sep 17 00:00:00 2001
From: Chen-Yu Tsai <wens@csie.org>
Date: Mon, 7 Nov 2016 18:06:59 +0800
Subject: [PATCH] ASoC: sun4i-codec: Add support for A31 ADC capture path

The A31's internal codec capture path has a mixer in front of the ADC
for each channel, capable of selecting various inputs, including
microphones, line in, phone in, and the main output mixer.

This patch adds the various controls, widgets and routes needed for
audio capture from the already supported inputs on the A31.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/sunxi/sun4i-codec.c | 65 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 1934db29b2b5..735115244b17 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -786,6 +786,30 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
 			SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
 };
 
+/* ADC mixer controls */
+static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {
+	SOC_DAPM_DOUBLE("Mixer Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),
+	SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),
+	SOC_DAPM_DOUBLE("Line In Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),
+	SOC_DAPM_DOUBLE("Mic1 Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),
+	SOC_DAPM_DOUBLE("Mic2 Capture Switch",
+			SUN6I_CODEC_ADC_ACTL,
+			SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,
+			SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),
+};
+
 /* headphone controls */
 static const char * const sun6i_codec_hp_src_enum_text[] = {
 	"DAC", "Mixer",
@@ -885,6 +909,10 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
 	SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
 		       SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
 		       sun6i_codec_mic_gain_scale),
+	SOC_DOUBLE_TLV("ADC Capture Volume",
+		       SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,
+		       SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,
+		       sun6i_codec_out_mixer_pregain_scale),
 };
 
 static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
@@ -910,6 +938,23 @@ static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
 	/* Line In */
 	SND_SOC_DAPM_INPUT("LINEIN"),
 
+	/* Digital parts of the ADCs */
+	SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
+			    SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,
+			    NULL, 0),
+
+	/* Analog parts of the ADCs */
+	SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
+			 SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
+			 SUN6I_CODEC_ADC_ACTL_ADCREN, 0),
+
+	/* ADC Mixers */
+	SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+			sun6i_codec_adc_mixer_controls),
+	SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+			sun6i_codec_adc_mixer_controls),
+
 	/* Digital parts of the DACs */
 	SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
 			    SUN4I_CODEC_DAC_DPC_EN_DA, 0,
@@ -973,6 +1018,20 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
 	{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
 
+	/* Left ADC Mixer Routes */
+	{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
+	{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
+	{ "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
+	{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
+	{ "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
+	/* Right ADC Mixer Routes */
+	{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
+	{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
+	{ "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
+	{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
+	{ "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
 	/* Headphone Routes */
 	{ "Headphone Source Playback Route", "DAC", "Left DAC" },
 	{ "Headphone Source Playback Route", "DAC", "Right DAC" },
@@ -987,6 +1046,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
 	{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
 	{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
 	{ "LINEOUT", NULL, "Line Out Source Playback Route" },
+
+	/* ADC Routes */
+	{ "Left ADC", NULL, "ADC Enable" },
+	{ "Right ADC", NULL, "ADC Enable" },
+	{ "Left ADC", NULL, "Left ADC Mixer" },
+	{ "Right ADC", NULL, "Right ADC Mixer" },
 };
 
 static struct snd_soc_codec_driver sun6i_codec_codec = {
-- 
2.10.2

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

end of thread, other threads:[~2016-11-09 15:00 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-03 11:07 [PATCH 00/12] ASoC: sun4i-codec: Add support for A31 Codec Chen-Yu Tsai
2016-10-03 11:07 ` [PATCH 01/12] ASoC: dapm: Support second register for DAPM control updates Chen-Yu Tsai
2016-10-03 11:07 ` [PATCH 02/12] ASoC: dapm: Implement stereo mixer control support Chen-Yu Tsai
2016-10-26 16:57   ` Mark Brown
2016-10-27  1:20     ` Chen-Yu Tsai
2016-10-26 17:50   ` Mark Brown
2016-10-27 14:02     ` Chen-Yu Tsai
2016-10-03 11:07 ` [PATCH 03/12] ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type Chen-Yu Tsai
2016-11-02 16:47   ` Applied "ASoC: dapm: Introduce DAPM_DOUBLE dual channel control type" to the asoc tree Mark Brown
2016-10-03 11:07 ` [PATCH 04/12] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type Chen-Yu Tsai
2016-11-02 16:46   ` Applied "ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type" to the asoc tree Mark Brown
2016-10-03 11:07 ` [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output Chen-Yu Tsai
2016-10-03 11:47   ` Maxime Ripard
2016-10-04  4:26     ` Chen-Yu Tsai
2016-10-10 10:04       ` Maxime Ripard
2016-10-09  1:29   ` Rob Herring
2016-10-03 11:07 ` [PATCH 06/12] ASoC: sun4i-codec: Add support for A31 Line In playback Chen-Yu Tsai
2016-11-03 20:33   ` Applied "ASoC: sun4i-codec: Add support for A31 Line In playback" to the asoc tree Mark Brown
2016-10-03 11:07 ` [PATCH 07/12] ASoC: sun4i-codec: Add support for A31 Line Out playback Chen-Yu Tsai
2016-10-03 11:08 ` [PATCH 08/12] ASoC: sun4i-codec: Add support for A31 analog microphone inputs Chen-Yu Tsai
2016-10-03 11:08 ` [PATCH 09/12] ASoC: sun4i-codec: Add support for A31 ADC capture path Chen-Yu Tsai
2016-11-09 14:59   ` Applied "ASoC: sun4i-codec: Add support for A31 ADC capture path" to the asoc tree Mark Brown
2016-10-03 11:08 ` [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing Chen-Yu Tsai
2016-10-09  1:29   ` Rob Herring
2016-10-03 11:08 ` [PATCH 11/12] ARM: dts: sun6i: Add audio codec device node Chen-Yu Tsai
2016-10-03 11:08 ` [PATCH 12/12] ARM: dts: sun6i: hummingbird: Enable internal audio codec Chen-Yu Tsai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).