[alsa-devel] [PATCH] Add TLV support for UDA1380

Mark Brown broonie at opensource.wolfsonmicro.com
Tue Nov 20 10:25:44 CET 2007


From: Philipp Zabel <philipp.zabel at gmail.com>

Signed-off-by: Philipp Zabel <philipp.zabel at gmail.com>
---
 sound/soc/codecs/uda1380.c |  255 ++++++++++++++++++++++++++++++++++++++------
 1 files changed, 222 insertions(+), 33 deletions(-)

diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 0fa9832..71960f0 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -5,6 +5,10 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
+ * Copyright (c) 2007 Philipp Zabel <philipp.zabel at gmail.com>
+ * Improved support for DAPM and audio routing/mixing capabilities,
+ * added TLV support.
+ *
  * Modified by Richard Purdie <richard at openedhand.com> to fit into SoC
  * codec model.
  *
@@ -28,6 +32,7 @@
 #include <sound/info.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/tlv.h>
 
 #include "uda1380.h"
 
@@ -111,14 +116,14 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
 	 */
 	if (!codec->active && (reg >= UDA1380_MVOL))
 		return 0;
-	dbg("uda hw write %x val %x\n", reg, value);
+	dbg("uda hw write %x val %x", reg, value);
 	if (codec->hw_write(codec->control_data, data, 3) == 3) {
 		unsigned int val;
 		i2c_master_send(codec->control_data, data, 1);
 		i2c_master_recv(codec->control_data, data, 2);
 		val = (data[0]<<8) | data[1];
 		if (val != value) {
-			dbg("READ BACK VAL %x\n", (data[0]<<8) | data[1]);
+			dbg("READ BACK VAL %x", (data[0]<<8) | data[1]);
 			return -EIO;
 		}
 		return 0;
@@ -131,37 +136,202 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
 /* declarations of ALSA reg_elem_REAL controls */
 static const char *uda1380_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz",
 				      "96kHz"};
-static const char *uda1380_input_sel[] = {"Line", "Mic"};
-static const char *uda1380_output_sel[] = {"Direct", "Mixer"};
+static const char *uda1380_input_sel[] = { "Line", "Mic + Line R", "Line L", "Mic" };
+static const char *uda1380_output_sel[] = {"DAC", "Analog Mixer"};
 static const char *uda1380_spf_mode[] = {"Flat", "Minimum1", "Minimum2",
 					 "Maximum"};
+static const char *uda1380_capture_sel[] = {"ADC", "Digital Mixer"};
+static const char *uda1380_sel_ns[] = { "3rd-order", "5th-order" };
+static const char *uda1380_mix_control[] = { "off", "PCM only", "before sound processing", "after sound processing" };
+static const char *uda1380_sdet_setting[] = { "3200", "4800", "9600", "19200" };
+static const char *uda1380_os_setting[] = { "single-speed", "double-speed (no mixing)", "quad-speed (no mixing)" };
+
+static const struct soc_enum uda1380_deemp_enum[] = {
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 8, 5, uda1380_deemp),
+	SOC_ENUM_SINGLE(UDA1380_DEEMP, 0, 5, uda1380_deemp),
+};
+static const struct soc_enum uda1380_input_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_ADC, 2, 4, uda1380_input_sel);		/* SEL_MIC, SEL_LNA */
+static const struct soc_enum uda1380_output_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel);		/* R02_EN_AVC */
+static const struct soc_enum uda1380_spf_enum =
+	SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode);		/* M */
+static const struct soc_enum uda1380_capture_sel_enum =
+	SOC_ENUM_SINGLE(UDA1380_IFACE, 6, 2, uda1380_capture_sel);	/* SEL_SOURCE */
+static const struct soc_enum uda1380_sel_ns_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 14, 2, uda1380_sel_ns);		/* SEL_NS */
+static const struct soc_enum uda1380_mix_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 12, 4, uda1380_mix_control);	/* MIX, MIX_POS */
+static const struct soc_enum uda1380_sdet_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 4, 4, uda1380_sdet_setting);	/* SD_VALUE */
+static const struct soc_enum uda1380_os_enum =
+	SOC_ENUM_SINGLE(UDA1380_MIXER, 0, 3, uda1380_os_setting);	/* OS */
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	int max = (kcontrol->private_value >> 16) & 0xff;
+	int min = (kcontrol->private_value >> 24) & 0xff;
+
+	/* 00000000 (0) ...(-0.25 dB)... 11111000 (-78 dB), 11111100 -INF */
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = min;
+	uinfo->value.integer.max = max;
+	return 0;
+}
+
+/**
+ * snd_soc_get_volsw_sgn - single mixer get callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to get the value of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int max = (kcontrol->private_value >> 16) & 0xff;
+	int min = (kcontrol->private_value >> 24) & 0xff;
+
+	ucontrol->value.integer.value[0] =
+		(signed char)(snd_soc_read(codec, reg) & 0xff);
+	ucontrol->value.integer.value[1] =
+		(signed char)((snd_soc_read(codec, reg) >> 8) & 0xff);
+
+	return 0;
+}
+
+/**
+ * snd_soc_put_volsw_sgn - single mixer put callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int max = (kcontrol->private_value >> 16) & 0xff;
+	int min = (kcontrol->private_value >> 24) & 0xff;
+	int err;
+	unsigned short val, val2, val_mask;
+
+	val = (signed char)ucontrol->value.integer.value[0] & 0xff;
+	val2 = (signed char)ucontrol->value.integer.value[1] & 0xff;
+	val |= val2<<8;
+	err = snd_soc_update_bits(codec, reg, val_mask, val);
+	return err;
+}
+
+DECLARE_TLV_DB_SCALE(amix_tlv, -4950, 150, 1);	/* from -48 dB in 1.5 dB steps (mute instead of -49.5 dB) */
+
+static const unsigned int mvol_tlv[] = {
+	TLV_DB_RANGE_HEAD(3),
+	0,15, TLV_DB_SCALE_ITEM(-8200, 100, 1),	/* from -78 dB in 1 dB steps (3 dB steps, really) */
+	16,43, TLV_DB_SCALE_ITEM(-6600, 50, 0),	/* from -66 dB in 0.5 dB steps (2 dB steps, really) */
+	44,252, TLV_DB_SCALE_ITEM(-5200, 25, 0),	/* from -52 dB in 0.25 dB steps */
+};
 
-static const struct soc_enum uda1380_enum[] = {
-	SOC_ENUM_DOUBLE(UDA1380_DEEMP, 0, 8, 5, uda1380_deemp),
-	SOC_ENUM_SINGLE(UDA1380_ADC, 3, 2, uda1380_input_sel),
-	SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode),
-	SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel), /* R02_EN_AVC */
+static const unsigned int vc_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0,7, TLV_DB_SCALE_ITEM(-7800, 150, 1),	/* from -72 dB in 1.5 dB steps (6 dB steps really) */
+	8,15, TLV_DB_SCALE_ITEM(-6600, 75, 0),	/* from -66 dB in 0.75 dB steps (3 dB steps really) */
+	16,43, TLV_DB_SCALE_ITEM(-6000, 50, 0),	/* from -60 dB in 0.5 dB steps (2 dB steps really) */
+	44,228, TLV_DB_SCALE_ITEM(-4600, 25, 0),/* from -46 dB in 0.25 dB steps */
 };
 
+DECLARE_TLV_DB_SCALE(tr_tlv, 0, 200, 0);	/* from 0 dB to 6 dB in 2 dB steps, if SPF mode != flat */
+DECLARE_TLV_DB_SCALE(bb_tlv, 0, 200, 0);	/* from 0 dB to 24 dB in 2 dB steps, if SPF mode == maximum */
+						/* (SPF mode == flat cuts off at 18 dB max */
+
+static const unsigned int dec_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	-128,48, TLV_DB_SCALE_ITEM(-6350, 50, 1),
+	/* 0011000 (48 dB) ...(0.5 dB)... 10000001 (-63 dB) 10000000 -INF */
+};
+
+DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);	/* from 0 dB to 24 dB in 3 dB steps */
+DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);	/* from 0 dB to 30 dB in 2 dB steps */
+
+
+#define SOC_DOUBLE_S8_TLV(xname, reg, min, max, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
+	.put = snd_soc_put_volsw_s8, \
+	.private_value = (reg) | (((signed char)max) << 16) | (((signed char)min) << 24) }
+
 static const struct snd_kcontrol_new uda1380_snd_controls[] = {
-	SOC_DOUBLE("Playback Volume", UDA1380_MVOL, 0, 8, 255, 1),
-	SOC_DOUBLE("Mixer Volume", UDA1380_MIXVOL, 0, 8, 255, 1),
-	SOC_ENUM("Sound Processing Filter Mode", uda1380_enum[2]),
-	SOC_DOUBLE("Treble Volume", UDA1380_MODE, 4, 12, 3, 0),
-	SOC_DOUBLE("Bass Volume", UDA1380_MODE, 0, 8, 15, 0),
-	SOC_ENUM("Playback De-emphasis", uda1380_enum[0]),
-	SOC_DOUBLE("Capture Volume", UDA1380_DEC, 0, 8, 127, 0),
-	SOC_DOUBLE("Line Capture Volume", UDA1380_PGA, 0, 8, 15, 0),
-	SOC_SINGLE("Mic Capture Volume", UDA1380_PGA, 8, 11, 0),
-	SOC_DOUBLE("Playback Switch", UDA1380_DEEMP, 3, 11, 1, 1),
-	SOC_SINGLE("Capture Switch", UDA1380_PGA, 15, 1, 0),
-	SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),
-	SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),
+	SOC_DOUBLE_TLV("Analog Mixer Volume", UDA1380_AMIX, 0, 8, 44, 1, amix_tlv),	/* AVCR, AVCL */
+	SOC_DOUBLE_TLV("Master Playback Volume", UDA1380_MVOL, 0, 8, 252, 1, mvol_tlv),	/* MVCL, MVCR */
+	SOC_SINGLE_TLV("ADC Playback Volume", UDA1380_MIXVOL, 8, 228, 1, vc_tlv),	/* VC2 */
+	SOC_SINGLE_TLV("PCM Playback Volume", UDA1380_MIXVOL, 0, 228, 1, vc_tlv),	/* VC1 */
+	SOC_ENUM("Sound Processing Filter", uda1380_spf_enum),				/* M */
+	SOC_DOUBLE_TLV("Tone Control - Treble", UDA1380_MODE, 4, 12, 3, 0, tr_tlv), 	/* TRL, TRR */
+	SOC_DOUBLE_TLV("Tone Control - Bass", UDA1380_MODE, 0, 8, 15, 0, bb_tlv),	/* BBL, BBR */
+/**/	SOC_SINGLE("Master Playback Switch", UDA1380_DEEMP, 14, 1, 1),		/* MTM */
+	SOC_SINGLE("ADC Playback Switch", UDA1380_DEEMP, 11, 1, 1),		/* MT2 from decimation filter */
+	SOC_ENUM("ADC Playback De-emphasis", uda1380_deemp_enum[0]),		/* DE2 */
+	SOC_SINGLE("PCM Playback Switch", UDA1380_DEEMP, 3, 1, 1),		/* MT1, from digital data input */
+	SOC_ENUM("PCM Playback De-emphasis", uda1380_deemp_enum[1]),		/* DE1 */
+	SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0),	/* DA_POL_INV */
+	SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum),				/* SEL_NS */
+	SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum),		/* MIX_POS, MIX */
+	SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0),			/* SILENCE, force DAC output to silence */
+	SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0),		/* SDET_ON */
+	SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum),		/* SD_VALUE */
+	SOC_ENUM("Oversampling Input", uda1380_os_enum),			/* OS */
+	SOC_DOUBLE_S8_TLV("ADC Capture Volume", UDA1380_DEC, -128, 48, dec_tlv),	/* ML_DEC, MR_DEC */
+/**/	SOC_SINGLE("ADC Capture Switch", UDA1380_PGA, 15, 1, 1),		/* MT_ADC */
+	SOC_DOUBLE_TLV("Line Capture Volume", UDA1380_PGA, 0, 8, 8, 0, pga_tlv), /* PGA_GAINCTRLL, PGA_GAINCTRLR */
+	SOC_SINGLE("ADC Polarity inverting Switch", UDA1380_ADC, 12, 1, 0),	/* ADCPOL_INV */
+	SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv),	/* VGA_CTRL */
+	SOC_SINGLE("DC Filter Bypass Switch", UDA1380_ADC, 1, 1, 0),		/* SKIP_DCFIL (before decimator) */
+	SOC_SINGLE("DC Filter Enable Switch", UDA1380_ADC, 0, 1, 0),		/* EN_DCFIL (at output of decimator) */
+	SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),			/* TODO: enum, see table 62 */
+	SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),			/* AGC_LEVEL */
+	/* -5.5, -8, -11.5, -14 dBFS */
 	SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
-	SOC_SINGLE("Silence", UDA1380_MIXER, 7, 1, 0),
-	SOC_SINGLE("Silence Detection", UDA1380_MIXER, 6, 1, 0),
 };
 
+/**
+ * uda1380_snd_soc_cnew
+ *
+ * temporary copy of snd_soc_cnew that doesn't overwrite .access
+ */
+struct snd_kcontrol *uda1380_snd_soc_cnew(const struct snd_kcontrol_new *_template,
+	void *data, char *long_name)
+{
+	struct snd_kcontrol_new template;
+
+	memcpy(&template, _template, sizeof(template));
+	if (long_name)
+		template.name = long_name;
+	template.index = 0;
+
+	return snd_ctl_new1(&template, data);
+}
+
 /* add non dapm controls */
 static int uda1380_add_controls(struct snd_soc_codec *codec)
 {
@@ -169,7 +339,7 @@ static int uda1380_add_controls(struct snd_soc_codec *codec)
 
 	for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
 		err = snd_ctl_add(codec->card,
-			snd_soc_cnew(&uda1380_snd_controls[i],codec, NULL));
+			uda1380_snd_soc_cnew(&uda1380_snd_controls[i],codec, NULL));
 		if (err < 0)
 			return err;
 	}
@@ -179,17 +349,24 @@ static int uda1380_add_controls(struct snd_soc_codec *codec)
 
 /* Input mux */
 static const struct snd_kcontrol_new uda1380_input_mux_control =
-	SOC_DAPM_ENUM("Input Select", uda1380_enum[1]);
+	SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
 
 /* Output mux */
 static const struct snd_kcontrol_new uda1380_output_mux_control =
-	SOC_DAPM_ENUM("Output Select", uda1380_enum[3]);
+	SOC_DAPM_ENUM("Route", uda1380_output_sel_enum);
+
+/* Capture mux */
+static const struct snd_kcontrol_new uda1380_capture_mux_control =
+	SOC_DAPM_ENUM("Route", uda1380_capture_sel_enum);
+
 
 static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
 	SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
 		&uda1380_input_mux_control),
 	SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
 		&uda1380_output_mux_control),
+	SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0,
+		&uda1380_capture_mux_control),
 	SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
 	SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
@@ -218,8 +395,10 @@ static const char *audio_map[][3] = {
 	{"Analog Mixer", NULL, "VINL"},
 	{"Analog Mixer", NULL, "DAC"},
 
-	{"Output Mux", "Direct", "DAC"},
-	{"Output Mux", "Mixer", "Analog Mixer"},
+	{"Output Mux", "DAC", "DAC"},
+	{"Output Mux", "Analog Mixer", "Analog Mixer"},
+
+	/* {"DAC", "Digital Mixer", "I2S" } */
 
 	/* headphone driver */
 	{"VOUTLHP", NULL, "HeadPhone Driver"},
@@ -228,10 +407,13 @@ static const char *audio_map[][3] = {
 	/* input mux */
 	{"Left ADC", NULL, "Input Mux"},
 	{"Input Mux", "Mic", "Mic LNA"},
+	{"Input Mux", "Mic + Line R", "Mic LNA"},
+	{"Input Mux", "Line L", "Left PGA"},
 	{"Input Mux", "Line", "Left PGA"},
 
 	/* right input */
-	{"Right ADC", NULL, "Right PGA"},
+	{"Right ADC", "Mic + Line R", "Right PGA"},
+	{"Right ADC", "Line", "Right PGA"},
 
 	/* inputs */
 	{"Mic LNA", NULL, "VINM"},
@@ -307,6 +489,13 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
 	} else {
 		reg_start = UDA1380_DEC;
 		reg_end = UDA1380_AGC;
+		/* FIXME - wince values */
+//		uda1380_write(codec, UDA1380_PM, uda1380_read_reg_cache (codec, UDA1380_PM) | R02_PON_LNA | R02_PON_DAC);
+		uda1380_write(codec, UDA1380_PM, 0x851f);
+//		uda1380_write_reg_cache(codec, UDA1380_DEC, 0x0000);
+//		uda1380_write_reg_cache(codec, UDA1380_PGA, uda1380_read_reg_cache (codec, UDA1380_PM) & ~R21_MT_ADC); /* unmute */
+//		uda1380_write_reg_cache(codec, UDA1380_ADC, 0x0001/*0x090d*/);
+			// ADC_POL_INV=0 VGA_CTRL=9(1001:18dB) gain SEL_LNA=1 SEL_MIC=1 EN_DCFIL=1
 	}
 
 	/* FIXME disable DAC_CLK */
@@ -314,7 +503,7 @@ static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
 	uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
 
 	for (reg = reg_start; reg <= reg_end; reg++ ) {
-		dbg("reg %x val %x\n",reg, uda1380_read_reg_cache (codec, reg));
+		dbg("flush reg %x val %x:",reg, uda1380_read_reg_cache (codec, reg));
 		uda1380_write(codec, reg, uda1380_read_reg_cache (codec, reg));
 	}
 
@@ -387,7 +576,7 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
 static int uda1380_mute(struct snd_soc_codec_dai *codec_dai, int mute)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & 0xbfff;
+	u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
 
 	/* FIXME: mute(codec,0) is called when the magician clock is already
 	 * set to WSPLL, but for some unknown reason writing to interpolator
@@ -395,7 +584,7 @@ static int uda1380_mute(struct snd_soc_codec_dai *codec_dai, int mute)
 	u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
 	uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
 	if (mute)
-		uda1380_write(codec, UDA1380_DEEMP, mute_reg | 0x4000);
+		uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
 	else
 		uda1380_write(codec, UDA1380_DEEMP, mute_reg);
 	uda1380_write(codec, UDA1380_CLK, clk);
-- 
1.5.3.5



More information about the Alsa-devel mailing list