[alsa-devel] [PATCH v2] ASoC: MAX9860: new driver

Peter Rosin peda at axentia.se
Thu May 12 23:52:06 CEST 2016


[Dropping the DT crowd]

On 2016-05-12 17:35, Peter Rosin wrote:
> This is a driver for the MAX9860 Mono Audio Voice Codec.
> 
> https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf
> 
> This driver does not support sidetone since the DVST register field is
> backwards with the mute near the maximum level instead of the minimum.

This is what I have (on top of v2). It kind of works, but as you can see,
the mute_value is needed in dapm_set_mixer_path_status which operates on
entries in path->sink->kcontrol_news[] which I believe is constant. That
kind of defeats the runtime TLV lookup thing storing the value, no?

So, I allow setting the mute_value manually instead. I could have created
new macros to help create the mixer control, but didn't. Perhaps I should
have?

I also noted that with this, an "on" sidetone will keep the system up.
Ideally the sidetone volsw should only have the power to turn on the DAC
and the left ADC when the DAC is "on" for other reasons. At the same
time the sidetone needs to turn on the left channel ADC when the DAC
is "on", so using DAPM seems like the right thing. Is this an old
problem that have been solves elsewhere? If so, how is that handled in
other codecs?

Cheers,
Peter

diff --git a/include/sound/soc.h b/include/sound/soc.h
index 02b4a215fd75..79b6a2f2a3a9 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1239,6 +1239,7 @@ struct soc_mixer_control {
 	unsigned int sign_bit;
 	unsigned int invert:1;
 	unsigned int autodisable:1;
+	int mute_value;
 	struct snd_soc_dobj dobj;
 };
 
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c4464858bf01..765dfdfe0f4a 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -732,6 +732,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 	unsigned int max = mc->max;
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
+	int mute_value = mc->mute_value;
 	unsigned int val;
 
 	if (reg != SND_SOC_NOPM) {
@@ -739,7 +740,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 		val = (val >> shift) & mask;
 		if (invert)
 			val = max - val;
-		p->connect = !!val;
+		p->connect = val != mute_value;
 	} else {
 		p->connect = 0;
 	}
@@ -3045,6 +3046,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 	int max = mc->max;
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
+	int mute_value = mc->mute_value;
 	unsigned int val;
 	int connect, change, reg_change = 0;
 	struct snd_soc_dapm_update update;
@@ -3056,7 +3058,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 			 kcontrol->id.name);
 
 	val = (ucontrol->value.integer.value[0] & mask);
-	connect = !!val;
+	connect = val != mute_value;
 
 	if (invert)
 		val = max - val;
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
index ee30832b0afb..075874be3877 100644
--- a/sound/soc/codecs/max9860.c
+++ b/sound/soc/codecs/max9860.c
@@ -3,9 +3,6 @@
  *
  * https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf
  *
- * The driver does not support sidetone since the DVST register field is
- * backwards with the mute near the maximum level instead of the minimum.
- *
  * Author: Peter Rosin <peda at axentia.s>
  *         Copyright 2016 Axentia Technologies
  *
@@ -135,6 +132,10 @@ const struct regmap_config max9860_regmap = {
 static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1);
 static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0);
 static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
+/* The dvst field has its mute in the wrong end. Sigh. */
+static const DECLARE_TLV_DB_RANGE(dvst_tlv,
+	0, MAX9860_DVST_MIN - 1,            TLV_DB_SCALE_ITEM(-6000, 200, 0),
+	MAX9860_DVST_MIN, MAX9860_DVST_MIN, TLV_DB_SCALE_ITEM(0, 0, 1));
 static const DECLARE_TLV_DB_RANGE(pam_tlv,
 	0, MAX9860_PAM_MAX - 1,             TLV_DB_SCALE_ITEM(-2000, 2000, 1),
 	MAX9860_PAM_MAX, MAX9860_PAM_MAX,   TLV_DB_SCALE_ITEM(3000, 0, 0));
@@ -214,6 +215,21 @@ SOC_ENUM("ADC Filter", avflt_enum),
 SOC_ENUM("DAC Filter", dvflt_enum),
 };
 
+static const struct snd_kcontrol_new max9860_mixer_controls[] = {
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Sidetone Volume",
+	.info = snd_soc_info_volsw,
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ
+		| SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.tlv.p = dvst_tlv,
+	.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw,
+	.private_value = ((unsigned long)&(struct soc_mixer_control) {
+		.reg = MAX9860_DACGAIN, .rreg = MAX9860_DACGAIN,
+		.shift = MAX9860_DVST_SHIFT, .rshift = MAX9860_DVST_SHIFT,
+		.max = MAX9860_DVST_MIN, .platform_max = MAX9860_DVST_MIN,
+		.invert = 1, .autodisable = 0, .mute_value = MAX9860_DVST_MIN
+	})},
+};
+
 static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = {
 SND_SOC_DAPM_INPUT("MICL"),
 SND_SOC_DAPM_INPUT("MICR"),
@@ -224,6 +240,10 @@ SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0),
 SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
 SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
 
+SND_SOC_DAPM_MIXER("Mixer", SND_SOC_NOPM, 0, 0,
+		   max9860_mixer_controls,
+		   ARRAY_SIZE(max9860_mixer_controls)),
+
 SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
 SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
 
@@ -244,8 +264,10 @@ static const struct snd_soc_dapm_route max9860_dapm_routes[] = {
 	{ "AIFOUTL", NULL, "ADCL" },
 	{ "AIFOUTR", NULL, "ADCR" },
 
-	{ "DAC", NULL, "AIFINL" },
-	{ "DAC", NULL, "AIFINR" },
+	{ "Mixer", NULL, "AIFINL" },
+	{ "Mixer", NULL, "AIFINR" },
+	{ "Mixer", "Sidetone Volume", "ADCL" },
+	{ "DAC", NULL, "Mixer" },
 	{ "OUT", NULL, "DAC" },
 
 	{ "Supply", NULL, "AVDD" },



More information about the Alsa-devel mailing list