Signed-off-by: Steve Chen schen@mvista.com Signed-off-by: Martin Ambrose martin@ti.com --- sound/soc/codecs/tlv320aic3x.c | 185 ++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/tlv320aic3x.h | 11 ++- 2 files changed, 190 insertions(+), 6 deletions(-)
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index ab099f4..5720cad 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -57,13 +57,17 @@ struct aic3x_priv { int master; };
+static u8 pg0_end = AIC3X_GEN_PG0_END; +static u8 pg1_end = AIC3X_GEN_PG1_END; +static enum aic3x_codec_variant codec_variant; + /* * AIC3X register cache * We can't read the AIC3X register space when we are * using 2 wire for device control, so we cache them instead. * There is no point in caching the reset register */ -static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { +static const u8 aic3x_reg[] = { 0x00, 0x00, 0x00, 0x10, /* 0 */ 0x04, 0x00, 0x00, 0x00, /* 4 */ 0x00, 0x00, 0x00, 0x01, /* 8 */ @@ -90,6 +94,35 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { 0x00, 0x00, 0x00, 0x00, /* 92 */ 0x00, 0x00, 0x00, 0x00, /* 96 */ 0x00, 0x00, 0x02, /* 100 */ + + 0x00, 0x00, 0x00, 0x00, /* 103-127 unused */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, + + 0x00, 0x6b, 0xe3, 0x96, /* 0 */ + 0x66, 0x67, 0x5d, 0x6b, /* 4 */ + 0xe3, 0x96, 0x66, 0x67, /* 8 */ + 0x5d, 0x7d, 0x83, 0x84, /* 12 */ + 0xee, 0x7d, 0x83, 0x84, /* 16 */ + 0xee, 0x39, 0x55, 0xf3, /* 20 */ + 0x2d, 0x53, 0x7e, 0x6b, /* 24 */ + 0xe3, 0x96, 0x66, 0x67, /* 28 */ + 0x5d, 0x6b, 0xe3, 0x96, /* 32 */ + 0x66, 0x67, 0x5d, 0x7d, /* 36 */ + 0x83, 0x84, 0xee, 0x7d, /* 40 */ + 0x83, 0x84, 0xee, 0x39, /* 44 */ + 0x55, 0xf3, 0x2d, 0x53, /* 48 */ + 0x7e, 0x7f, 0xff, 0x00, /* 52 */ + 0x00, 0x00, 0x00, 0x00, /* 56 */ + 0x00, 0x00, 0x00, 0x00, /* 60 */ + 0x00, 0x39, 0x55, 0xf3, /* 64 */ + 0x2d, 0x53, 0x7e, 0x39, /* 68 */ + 0x55, 0xf3, 0x2d, 0x53, /* 72 */ + 0x7e, /* 76 */ };
/* @@ -99,7 +132,8 @@ static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec, unsigned int reg) { u8 *cache = codec->reg_cache; - if (reg >= AIC3X_CACHEREGNUM) + if ((reg >= pg1_end) || + ((reg >= pg0_end) && (reg < AIC3X_GEN_PG1_BEG))) return -1; return cache[reg]; } @@ -111,7 +145,8 @@ static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec, u8 reg, u8 value) { u8 *cache = codec->reg_cache; - if (reg >= AIC3X_CACHEREGNUM) + if ((reg >= pg1_end) || + ((reg >= pg0_end) && (reg < AIC3X_GEN_PG1_BEG))) return; cache[reg] = value; } @@ -119,8 +154,8 @@ static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec, /* * write to the aic3x register space */ -static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) +static int _aic3x_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) { u8 data[2];
@@ -132,12 +167,41 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0xff;
aic3x_write_reg_cache(codec, data[0], data[1]); + + /* adjust for page 1 before updating hardware if necessary */ + if (data[0] >= AIC3X_GEN_PG1_BEG) + data[0] -= AIC3X_GEN_PG1_BEG; + if (codec->hw_write(codec->control_data, data, 2) == 2) return 0; else return -EIO; }
+static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 cur_pg; + u8 reg_pg; + int ret = 0; + + cur_pg = aic3x_read_reg_cache(codec, 0); + if (reg < pg0_end) + reg_pg = 0; + else if ((reg >= AIC3X_GEN_PG1_BEG) && (reg < pg1_end)) + reg_pg = 1; + else + return -EIO; + + if (cur_pg != reg_pg) + ret = _aic3x_write(codec, 0, reg_pg); + + if (ret == 0) + ret = _aic3x_write(codec, reg, value); + + return ret; +} + /* * read from the aic3x register space */ @@ -218,6 +282,65 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, return ret; }
+static int tlv320aic3x_dual_reg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + return 0; +} + +static int tlv320aic3x_dual_reg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg_msb = kcontrol->private_value & 0xff; + int reg_lsb = (kcontrol->private_value >> 8) & 0xff; + int val = aic3x_read_reg_cache(codec, reg_msb) << 8; + + val |= aic3x_read_reg_cache(codec, reg_lsb); + + /* convert 2's complement to unsigned int */ + val ^= 0x8000; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tlv320aic3x_dual_reg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int err; + int reg_msb = kcontrol->private_value & 0xff; + int reg_lsb = (kcontrol->private_value >> 8) & 0xff; + int val_msb, val_lsb; + + val_msb = (ucontrol->value.integer.value[0] >> 8) & 0xff; + val_lsb = ucontrol->value.integer.value[0] & 0xff; + + /* convert unsigned int to 2's complement */ + val_msb ^= 0x80; + + err = snd_soc_update_bits(codec, reg_msb, 0xff, val_msb); + if (err < 0) + return err; + err = snd_soc_update_bits(codec, reg_lsb, 0xff, val_lsb); + return err; +} + +#define TLV320AIC3X_DUAL_R(xname, page, reg_msb, reg_lsb) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = tlv320aic3x_dual_reg_info, \ + .get = tlv320aic3x_dual_reg_get, .put = tlv320aic3x_dual_reg_put, \ + .private_value = ((reg_msb) + page) | (((reg_lsb) + page) << 8) } + +#define TLV320AIC3X_PG1_DUAL_R(xname, reg_msb, reg_lsb) \ + TLV320AIC3X_DUAL_R(xname, AIC3X_GEN_PG1_BEG, reg_msb, reg_lsb) + static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" }; static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" }; static const char *aic3x_left_hpcom_mux[] = @@ -344,6 +467,36 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]), + + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient N0", 1, 2), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient N1", 3, 4), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient N2", 5, 6), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient N3", 7, 8), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient N4", 9, 10), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient N5", 11, 12), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient D1", 13, 14), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient D2", 15, 16), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient D4", 17, 18), + TLV320AIC3X_PG1_DUAL_R("Left Effects Coefficient D5", 19, 20), + TLV320AIC3X_PG1_DUAL_R("Left De-Emphasis Coefficient N0", 21, 22), + TLV320AIC3X_PG1_DUAL_R("Left De-Emphasis Coefficient N1", 23, 24), + TLV320AIC3X_PG1_DUAL_R("Left De-Emphasis Coefficient D1", 25, 26), + + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient N0", 27, 28), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient N1", 29, 30), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient N2", 31, 32), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient N3", 33, 34), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient N4", 35, 36), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient N5", 37, 38), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient D1", 39, 40), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient D2", 41, 42), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient D4", 43, 44), + TLV320AIC3X_PG1_DUAL_R("Right Effects Coefficient D5", 45, 46), + TLV320AIC3X_PG1_DUAL_R("Right De-Emphasis Coefficient N0", 47, 48), + TLV320AIC3X_PG1_DUAL_R("Right De-Emphasis Coefficient N1", 49, 50), + TLV320AIC3X_PG1_DUAL_R("Right De-Emphasis Coefficient D1", 51, 52), + + TLV320AIC3X_PG1_DUAL_R("3-D Attenuation Coefficient", 53, 54), };
/* Left DAC Mux */ @@ -454,6 +607,18 @@ static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = { SOC_DAPM_SINGLE("HPRCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0), };
+static const struct snd_kcontrol_new aic3106_snd_controls[] = { + TLV320AIC3X_PG1_DUAL_R("Left Capture High Pass Coefficient N0", 65, 66), + TLV320AIC3X_PG1_DUAL_R("Left Capture High Pass Coefficient N1", 67, 68), + TLV320AIC3X_PG1_DUAL_R("Left Capture High Pass Coefficient D1", 69, 70), + TLV320AIC3X_PG1_DUAL_R("Right Capture High Pass Coefficient N0", + 71, 72), + TLV320AIC3X_PG1_DUAL_R("Right Capture High Pass Coefficient N1", + 73, 74), + TLV320AIC3X_PG1_DUAL_R("Right Capture High Pass Coefficient D1", + 75, 76), +}; + static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { /* Left DAC to Left Outputs */ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0), @@ -1165,6 +1330,10 @@ static int aic3x_init(struct snd_soc_device *socdev) if (codec->reg_cache == NULL) return -ENOMEM;
+ /* setup register cache sizes */ + if (codec_variant == AIC3106_CODEC) + pg1_end = AIC3106_PG1_END; + aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
@@ -1247,6 +1416,11 @@ static int aic3x_init(struct snd_soc_device *socdev)
snd_soc_add_controls(codec, aic3x_snd_controls, ARRAY_SIZE(aic3x_snd_controls)); + + if (codec_variant == AIC3106_CODEC) + snd_soc_add_controls(codec, aic3106_snd_controls, + ARRAY_SIZE(aic3106_snd_controls)); + aic3x_add_widgets(codec); ret = snd_soc_init_card(socdev); if (ret < 0) { @@ -1374,6 +1548,7 @@ static int aic3x_probe(struct platform_device *pdev) printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION);
setup = socdev->codec_data; + codec_variant = setup->variant; codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index ac827e5..ee40e9c 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -13,7 +13,10 @@ #define _AIC3X_H
/* AIC3X register space */ -#define AIC3X_CACHEREGNUM 103 +#define AIC3X_GEN_PG0_END 103 +#define AIC3X_GEN_PG1_BEG 128 +#define AIC3X_GEN_PG1_END 183 +#define AIC3106_PG1_END 205
/* Page select register */ #define AIC3X_PAGE_SELECT 0 @@ -199,6 +202,11 @@ /* Default input volume */ #define DEFAULT_GAIN 0x20
+enum aic3x_codec_variant { + AIC3X_GENERIC_CODEC, + AIC3106_CODEC, +}; + /* GPIO API */ enum { AIC3X_GPIO1_FUNC_DISABLED = 0, @@ -284,6 +292,7 @@ int aic3x_button_pressed(struct snd_soc_codec *codec); struct aic3x_setup_data { int i2c_bus; unsigned short i2c_address; + enum aic3x_codec_variant variant; unsigned int gpio_func[2]; };