[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