X-Git-Url: http://git.maemo.org/git/?p=aic34-eq;a=blobdiff_plain;f=kernel-2.6.28%2Fsound%2Fsoc%2Fcodecs%2Ftlv320aic3x.c;h=ec84be56de3bf15560da8d28483c201f4d073c33;hp=4f70822e421b4dbf66e27904ddb08b0231167cdf;hb=HEAD;hpb=a7b36c00eba8824bd7b155dfa099d88f72ac65a6;ds=sidebyside diff --git a/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.c b/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.c index 4f70822..ec84be5 100644 --- a/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.c +++ b/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "tlv320aic3x.h" @@ -54,11 +55,24 @@ static int hp_dac_lim = 9; module_param(hp_dac_lim, int, 0); +struct aic3x_dacfilter_t { + struct aic3x_iir_coeffs coeffs; + int state; +} aic3x_dacfilter = { + .coeffs = { + .N0 = 27619, .N1 = -27034, .N2 = 26461, .D1 = 32131, .D2 = -31506, + .N3 = 27619, .N4 = -27034, .N5 = 26461, .D4 = 32131, .D5 = -31506, + }, + .state = 0, +}; + /* codec private data */ struct aic3x_priv { unsigned int sysclk; int master; int prepare_reset; + struct snd_hwdep *hwdep; + struct aic3x_dacfilter_t dacfilter; }; /* @@ -93,7 +107,33 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { 0x00, 0x00, 0x00, 0x00, /* 88 */ 0x00, 0x00, 0x00, 0x00, /* 92 */ 0x00, 0x00, 0x00, 0x00, /* 96 */ - 0x00, 0x00, 0x02, /* 100 */ + 0x00, 0x00, 0x02, 0x00, /* 100 */ + 0x00, 0x00, 0x00, 0x00, /* 104 */ + 0x00, 0x00, 0x00, 0x00, /* 108 */ + 0x00, 0x00, 0x00, 0x00, /* 112 */ + 0x00, 0x00, 0x00, 0x00, /* 116 */ + 0x00, 0x00, 0x00, 0x00, /* 120 */ + 0x00, 0x00, 0x00, 0x00, /* 124 */ + 0x01, 0x6b, 0xe3, 0x96, /* 128 */ + 0x66, 0x67, 0x5d, 0x6b, /* 132 */ + 0xe3, 0x96, 0x66, 0x67, /* 136 */ + 0x5d, 0x7d, 0x83, 0x84, /* 140 */ + 0xee, 0x7d, 0x83, 0x84, /* 144 */ + 0xee, 0x39, 0x55, 0xf3, /* 148 */ + 0x2d, 0x53, 0x7e, 0x6b, /* 152 */ + 0xe3, 0x96, 0x66, 0x67, /* 156 */ + 0x5d, 0x6b, 0xe3, 0x96, /* 160 */ + 0x66, 0x67, 0x5d, 0x7d, /* 164 */ + 0x83, 0x84, 0xee, 0x7d, /* 168 */ + 0x83, 0x84, 0xee, 0x39, /* 172 */ + 0x55, 0xf3, 0x2d, 0x53, /* 176 */ + 0x7e, 0x7f, 0xff, 0x00, /* 180 */ + 0x00, 0x00, 0x00, 0x00, /* 184 */ + 0x00, 0x00, 0x00, 0x00, /* 188 */ + 0x00, 0x39, 0x55, 0xf3, /* 192 */ + 0x2d, 0x53, 0x7e, 0x39, /* 196 */ + 0x55, 0xf3, 0x2d, 0x53, /* 200 */ + 0x7e, /* 204 */ }; /* @@ -108,6 +148,16 @@ static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec, return cache[reg]; } +static inline int aic3x_read_coeff_reg_cache + (struct snd_soc_codec *codec, unsigned int msbreg) +{ + int val; + val = aic3x_read_reg_cache(codec, msbreg) << 8; + val |= aic3x_read_reg_cache(codec, msbreg+1); + if(val > 32767) val -= 65536; + return val; +} + /* * write aic3x register cache */ @@ -123,25 +173,70 @@ 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]; + static char curpage = -1; + u8 data[2], page = 0; + + /*mutex_lock(&codec->mutex);*/ + + if (reg > 127){ + reg -= 128; + page = 1; + } + //printk("MNZ: aic3x_write(reg = %i, val = 0x%x, page = %i)\n", + //reg, value, page); + if(reg && curpage != page){ + data[0] = 0; + data[1] = page; + if (codec->hw_write(codec->control_data, data, 2) != 2){ + /*mutex_unlock(&codec->mutex);*/ + return -EIO; + } else { + curpage = page; + } + } + /* data is * D15..D8 aic3x register offset * D7...D0 register data */ - data[0] = reg & 0xff; - data[1] = value & 0xff; + data[0] = reg; + data[1] = value; - aic3x_write_reg_cache(codec, data[0], data[1]); - if (codec->hw_write(codec->control_data, data, 2) == 2) + if (codec->hw_write(codec->control_data, data, 2) == 2){ + aic3x_write_reg_cache(codec, data[0] + page * 128, data[1]); + if(!reg) curpage = value; /* for reg = 0, ie, page setting */ + /*mutex_unlock(&codec->mutex);*/ return 0; - else + } else { + /*mutex_unlock(&codec->mutex);*/ return -EIO; + } + + return -EIO; } +/* Convert a value to 2s compliment and write to registers */ +static int aic3x_write_coeff(struct snd_soc_codec *codec, + u8 msbreg, int value) +{ + u16 outp; + + if (value < 0) + outp = 65536 + value; + else + outp = value; + + aic3x_write(codec, msbreg, outp >> 8); + aic3x_write(codec, msbreg+1, outp); + + return 0; +} + + /* * read from the aic3x register space */ @@ -160,23 +255,41 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg, return 0; } -/* - * Reset for getting low power consumption after bypass paths - */ -static void aic3x_reset(struct snd_soc_codec *codec) +static int aic3x_sync_hw(struct snd_soc_codec *codec) { u8 *cache = codec->reg_cache; u8 data[2]; int i; - - aic3x_write(codec, AIC3X_RESET, SOFT_RESET); - + + aic3x_write(codec, AIC3X_PAGE_SELECT, 1); + /*mutex_lock(&codec->mutex);*/ + for (i = 1; i < AIC3X_PAGE1REGNUM; i++) { + data[0] = i; + data[1] = cache[i+128]; + codec->hw_write(codec->control_data, data, 2); + } + /*mutex_unlock(&codec->mutex);*/ + /* We do not rewrite page select nor reset again */ - for (i = AIC3X_SAMPLE_RATE_SEL_REG; i < ARRAY_SIZE(aic3x_reg); i++) { + aic3x_write(codec, AIC3X_PAGE_SELECT, 0); + /*mutex_lock(&codec->mutex);*/ + for (i = 2; i < AIC3X_PAGE0REGNUM; i++) { data[0] = i; data[1] = cache[i]; codec->hw_write(codec->control_data, data, 2); } + /*mutex_unlock(&codec->mutex);*/ + + return 0; +} + +/* + * Reset for getting low power consumption after bypass paths + */ +static void aic3x_reset(struct snd_soc_codec *codec) +{ + aic3x_write(codec, AIC3X_RESET, SOFT_RESET); + aic3x_sync_hw(codec); } #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ @@ -255,6 +368,8 @@ static const char *aic3x_right_hpcom_mux[] = static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" }; static const char *aic3x_adc_hpf[] = { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" }; +static const char *aic3x_dac_filt[] = + { "Off", "Bass/Treble", "Custom" }; #define LDAC_ENUM 0 #define RDAC_ENUM 1 @@ -265,6 +380,7 @@ static const char *aic3x_adc_hpf[] = #define LINE2L_ENUM 6 #define LINE2R_ENUM 7 #define ADC_HPF_ENUM 8 +#define DAC_FILT_ENUM 9 static const struct soc_enum aic3x_enum[] = { SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux), @@ -276,6 +392,7 @@ static const struct soc_enum aic3x_enum[] = { SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf), + SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 1, 3, 3, aic3x_dac_filt), }; /* @@ -331,6 +448,198 @@ static int tlv320alc3x_info_volsw(struct snd_kcontrol *kcontrol, return 0; } +/* DAC and De-emphasis Filter Functions */ +int aic3x_deemph_set_coeffs(struct snd_soc_codec *codec, + int N0, int N1, int D1){ + printk("MNZ: setting deemph coeffs\n"); + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, DEEMPH_ON, 0); + + aic3x_write_coeff(codec, DEEMPH_LEFT_N0, N0); + aic3x_write_coeff(codec, DEEMPH_LEFT_N1, N1); + aic3x_write_coeff(codec, DEEMPH_LEFT_D1, D1); + aic3x_write_coeff(codec, DEEMPH_RIGHT_N0, N0); + aic3x_write_coeff(codec, DEEMPH_RIGHT_N1, N1); + aic3x_write_coeff(codec, DEEMPH_RIGHT_D1, D1); + + return 0; +} +EXPORT_SYMBOL_GPL(aic3x_deemph_set_coeffs); + +int aic3x_deemph_set_state(struct snd_soc_codec *codec, int state){ + printk("MNZ: Setting De-Emph filter: %i\n", state); + if(state) state = DEEMPH_ON; + else state = 0; + return snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, + DEEMPH_ON, state); +} +EXPORT_SYMBOL_GPL(aic3x_deemph_set_state); + +static int aic3x_dacfilter_write_coeffs + (struct snd_soc_codec *codec, struct aic3x_iir_coeffs *coeffs) +{ + printk("MNZ: dacfilter_write_coeffs\n"); + aic3x_write_coeff(codec, EFFECTS_LEFT_N0, coeffs->N0); + aic3x_write_coeff(codec, EFFECTS_LEFT_N1, coeffs->N1); + aic3x_write_coeff(codec, EFFECTS_LEFT_N2, coeffs->N2); + aic3x_write_coeff(codec, EFFECTS_LEFT_D1, coeffs->D1); + aic3x_write_coeff(codec, EFFECTS_LEFT_D2, coeffs->D2); + aic3x_write_coeff(codec, EFFECTS_LEFT_N3, coeffs->N3); + aic3x_write_coeff(codec, EFFECTS_LEFT_N4, coeffs->N4); + aic3x_write_coeff(codec, EFFECTS_LEFT_N5, coeffs->N5); + aic3x_write_coeff(codec, EFFECTS_LEFT_D4, coeffs->D4); + aic3x_write_coeff(codec, EFFECTS_LEFT_D5, coeffs->D5); + + aic3x_write_coeff(codec, EFFECTS_RIGHT_N0, coeffs->N0); + aic3x_write_coeff(codec, EFFECTS_RIGHT_N1, coeffs->N1); + aic3x_write_coeff(codec, EFFECTS_RIGHT_N2, coeffs->N2); + aic3x_write_coeff(codec, EFFECTS_RIGHT_D1, coeffs->D1); + aic3x_write_coeff(codec, EFFECTS_RIGHT_D2, coeffs->D2); + aic3x_write_coeff(codec, EFFECTS_RIGHT_N3, coeffs->N3); + aic3x_write_coeff(codec, EFFECTS_RIGHT_N4, coeffs->N4); + aic3x_write_coeff(codec, EFFECTS_RIGHT_N5, coeffs->N5); + aic3x_write_coeff(codec, EFFECTS_RIGHT_D4, coeffs->D4); + aic3x_write_coeff(codec, EFFECTS_RIGHT_D5, coeffs->D5); + + return 1; +} + +int aic3x_dacfilter_set_coeffs + (struct snd_soc_codec *codec, struct aic3x_iir_coeffs *coeffs) +{ + struct aic3x_priv *aic3x = codec->private_data; + memcpy((void*)&aic3x->dacfilter.coeffs, (void*)coeffs, + sizeof(struct aic3x_iir_coeffs)); + if(aic3x->dacfilter.state == 2) + aic3x_dacfilter_set_state(codec, 0); + return 0; +} +EXPORT_SYMBOL_GPL(aic3x_dacfilter_set_coeffs); + +int aic3x_dacfilter_set_state(struct snd_soc_codec *codec, int state) +{ + printk("MNZ: dacfilter_set_state to %i\n", state); + struct aic3x_priv *aic3x = codec->private_data; + int ret = 0; + if(aic3x->dacfilter.state == state) return 0; + + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, + EFFECTS_ON, 0); + + if(state == 0) + ret = 1; + else if(state == 1) + ret = 1; + /* FIXME MNZ. Set preset from current chosen preset */ + else if (state == 2){ + ret = aic3x_dacfilter_write_coeffs(codec, + &aic3x->dacfilter.coeffs); + if(ret) snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, + EFFECTS_ON, EFFECTS_ON); + } + else ret = 0; + + if(ret) aic3x->dacfilter.state = state; + return ret; +} + +EXPORT_SYMBOL_GPL(aic3x_dacfilter_set_state); + +/* DAC Filter hwdep device callbacks */ + +static int snd_hwdep_dacfilter_open_aic3x(struct snd_hwdep *hw, + struct file *file) +{ + return 0; +} + +static int snd_hwdep_dacfilter_ioctl_aic3x(struct snd_hwdep *hw, + struct file *file, unsigned int cmd, unsigned long arg) +{ + /* Only IOCTL command is for enabling/disabling filter, cmd = 1 + * arg = 0 to disable, 1 to enable and set to bass/treble, + * 2 to enable and set to custom coeffs + */ + struct snd_soc_codec *codec = hw->private_data; + if (cmd != 1) return -EINVAL; + printk("MNZ: IOCTL: cmd = %i, arg = %i\n", cmd, *((int*)arg)); + return aic3x_dacfilter_set_state(codec, *((int*)arg)); +} + +static long snd_hwdep_dacfilter_read_aic3x(struct snd_hwdep *hw, + char __user *buf, long count, loff_t *offset) +{ + struct aic3x_priv *aic3x = + ((struct snd_soc_codec*)hw->private_data)->private_data; + if(count != sizeof(struct aic3x_iir_coeffs)) return -EINVAL; + memcpy((void*)buf, (void*)&aic3x->dacfilter.coeffs, count); + return 0; +} + +static long snd_hwdep_dacfilter_write_aic3x(struct snd_hwdep *hw, + const char __user *buf, long count, loff_t *offset) +{ + struct snd_soc_codec *codec = hw->private_data; + if(count != sizeof(struct aic3x_iir_coeffs)) return -EINVAL; + + ((struct aic3x_priv*)codec->private_data)->dacfilter.state = 2; + + return aic3x_dacfilter_set_coeffs(codec, + (struct aic3x_iir_coeffs*)buf); +} + +/* DAC filter and 3D depth ALSA controls callbacks */ + +static int snd_soc_get_dacfilter_aic3x(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = + ((struct aic3x_priv*)codec->private_data)->dacfilter.state; + return 0; +} + +static int snd_soc_put_dacfilter_aic3x(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + aic3x_dacfilter_set_state(codec, ucontrol->value.enumerated.item[0]); + return 1; +} + +static int snd_soc_get_3d_attenuation_aic3x + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + int val = aic3x_read_coeff_reg_cache + (snd_kcontrol_chip(kcontrol), EFFECTS_3DATTEN); + val = ((val*100)/65530) + 50; + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int snd_soc_put_3d_attenuation_aic3x + (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int val = ucontrol->value.integer.value[0]; + if(val > 100 || val < 0) return -EINVAL; + + if(val){ + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, + EFFECTS_3D_ON, EFFECTS_3D_ON); + val = ((val - 50) * 65535) / 100 ; + } else { + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, + EFFECTS_3D_ON, 0); + val = -32768; + } + + aic3x_write_coeff(codec, EFFECTS_3DATTEN, val); + + return 1; +} + static const struct snd_kcontrol_new aic3x_snd_controls[] = { /* Output */ SOC_DOUBLE_R_TLV("PCM Playback Volume", @@ -399,6 +708,12 @@ 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]), + + SOC_ENUM_EXT("Hardware EQ", aic3x_enum[DAC_FILT_ENUM], + snd_soc_get_dacfilter_aic3x, snd_soc_put_dacfilter_aic3x), + + SOC_SINGLE_EXT("3D Control - Depth", EFFECTS_3DATTEN, 0, 100, 0, + snd_soc_get_3d_attenuation_aic3x, snd_soc_put_3d_attenuation_aic3x), }; /* add non dapm controls */ @@ -1212,23 +1527,13 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state) static int aic3x_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; - int i; - u8 data[2]; - u8 *cache = codec->reg_cache; - - /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) { - data[0] = i; - data[1] = cache[i]; - codec->hw_write(codec->control_data, data, 2); - } - - aic3x_set_bias_level(codec, codec->suspend_bias_level); - + aic3x_sync_hw(socdev->codec); + aic3x_set_bias_level(socdev->codec, + socdev->codec->suspend_bias_level); return 0; } + /* * initialise the AIC3X driver * register the mixer and dsp interfaces with the kernel @@ -1237,8 +1542,11 @@ static int aic3x_init(struct snd_soc_device *socdev) { struct snd_soc_codec *codec = socdev->codec; struct aic3x_setup_data *setup = socdev->codec_data; + struct snd_hwdep *hwdep; + char hwdepid[] = "IIR Filter"; int reg, ret = 0; + printk("MNZ: BEGIN aic3x_init\n"); codec->name = "tlv320aic3x"; codec->owner = THIS_MODULE; codec->read = aic3x_read_reg_cache; @@ -1333,12 +1641,28 @@ static int aic3x_init(struct snd_soc_device *socdev) aic3x_add_controls(codec); aic3x_add_widgets(codec); + + if(snd_hwdep_new(codec->card, hwdepid, 0, &hwdep) == 0){ + hwdep->private_data = codec; + sprintf(hwdep->name, hwdepid); + hwdep->ops.open = snd_hwdep_dacfilter_open_aic3x; + hwdep->ops.ioctl = snd_hwdep_dacfilter_ioctl_aic3x; + hwdep->ops.read = snd_hwdep_dacfilter_read_aic3x; + hwdep->ops.write = snd_hwdep_dacfilter_write_aic3x; + ((struct aic3x_priv*)codec->private_data)->hwdep = hwdep; + } + ret = snd_soc_register_card(socdev); + if (ret < 0) { printk(KERN_ERR "aic3x: failed to register card\n"); goto card_err; } + /* Set some defaults for coefficients */ + aic3x_write_coeff(codec, EFFECTS_3DATTEN, -32768); + printk("MNZ: END aic3x_init\n"); + return ret; card_err: @@ -1464,6 +1788,8 @@ static int aic3x_probe(struct platform_device *pdev) return -ENOMEM; aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); + memcpy(&aic3x->dacfilter, &aic3x_dacfilter, sizeof(aic3x_dacfilter)); + if (aic3x == NULL) { kfree(codec); return -ENOMEM;