From: mnzaki Date: Thu, 9 Sep 2010 16:43:39 +0000 (+0300) Subject: DAC Filter controls and hwdep device added. X-Git-Url: http://git.maemo.org/git/?p=aic34-eq;a=commitdiff_plain;h=d4d41d216a427e8d1fe69df86a29eb06873eb048 DAC Filter controls and hwdep device added. Added controls for the filter, off/on/custom. hwdep device called "IIR Filter" added, documentation on how to use it also added in: Documentation/sound/alsa/soc/codecs/tlv320aic3x.txt Fixed some things up and refactored some things. --- diff --git a/kernel-2.6.28/Documentation/sound/alsa/soc/tlv320aic3x.txt b/kernel-2.6.28/Documentation/sound/alsa/soc/tlv320aic3x.txt new file mode 100644 index 0000000..ec748f2 --- /dev/null +++ b/kernel-2.6.28/Documentation/sound/alsa/soc/tlv320aic3x.txt @@ -0,0 +1,59 @@ +The TLV320AIC3X is a powerful four channel low power audio codec family. +More information is available at: + http://focus.ti.com/docs/prod/folders/print/tlv320aic34.html +Datasheet: + http://www.ti.com/lit/gpn/tlv320aic34 + +The codec driver leverages the codecs effects through alsa controls and a +hwdep device for controlling the hardware fourth-order IIR filter block. + +There's an alsa control, "3D Control - Depth" for depth simulation. +The rest of the controls are for the IIR filter: + +1- A control for setting the bass/treble gain, which sets the filter's + coefficients to certain precalculated values. +2- A control for 'off' / 'on' / 'external control'. 'On' means the + bass/treble gain is used, while 'external control' means the + coefficients have been set through the hwdep device (see below). + + + +The IIR Filter consists of 2 cascaded biquads. The formula is: + / \ / \ +| (N0 + 2*N1*z^-1 + N2*z^-2) || (N3 + 2*N4*z^-1 + N5*z^-2) | +| ------------------------------ || ------------------------------ | + \ (32768 - 2*D1*z^-1 - D2*z^-2) / \ (32768 - 2*D4*z^-1 - D5*z^-2) / + +The filter can be controlled through an alsa hwdep device, via +libalsa. A short example follows, note that the data struct must be +passed *EXACTLY* as shown: + +#include +#include + +struct _iirfilter_data { + int16_t N0, N1, N2, D1, D2; + int16_t N3, N4, N5, D4, D5; +} iirfilter_data; + +/* Initialize */ +snd_hwdep *hwdep; + +snd_hwdep_open(&hwdep, "IIR Filter", "w"); + +/* To write: */ +iirfilter_data = { + .N0 = 27619, .N1 = -27034, .N2 = 26461, .D1 = 32131, .D2 = -31506, + .N3 = 27619, .N4 = -27034, .N5 = 26461, .D4 = 32131, .D5 = -31506 +}; + +snd_hwdep_write(hwdep, (void*)iirfilter_data, sizeof(iirfilter_data)); + +/* To read: */ +snd_hwdep_read(hwdep, (void*)iirfilter_data, sizeof(iirfilter_data)); + +/* To enable/disable filtering: */ +int arg = 1; /* 1 = enable, 0 = disable */ +snd_hwdep_ioctl(hwdep, 1, &arg); + + diff --git a/kernel-2.6.28/sound/soc/codecs/Kconfig b/kernel-2.6.28/sound/soc/codecs/Kconfig new file mode 100644 index 0000000..022e37a --- /dev/null +++ b/kernel-2.6.28/sound/soc/codecs/Kconfig @@ -0,0 +1,118 @@ +config SND_SOC_ALL_CODECS + tristate "Build all ASoC CODEC drivers" + depends on I2C + select SPI + select SPI_MASTER + select SND_SOC_AD73311 + select SND_SOC_AK4535 + select SND_SOC_CS4270 + select SND_SOC_SSM2602 + select SND_SOC_TLV320AIC23 + select SND_SOC_TLV320AIC26 + select SND_SOC_TLV320AIC3X + select SND_SOC_TWL4030 + select SND_SOC_UDA1380 + select SND_SOC_WM8510 + select SND_SOC_WM8580 + select SND_SOC_WM8731 + select SND_SOC_WM8750 + select SND_SOC_WM8753 + select SND_SOC_WM8900 + select SND_SOC_WM8903 + select SND_SOC_WM8971 + select SND_SOC_WM8990 + help + Normally ASoC codec drivers are only built if a machine driver which + uses them is also built since they are only usable with a machine + driver. Selecting this option will allow these drivers to be built + without an explicit machine driver for test and development purposes. + + If unsure select "N". + + +config SND_SOC_AC97_CODEC + tristate + select SND_AC97_CODEC + +config SND_SOC_AD1980 + tristate + +config SND_SOC_AD73311 + tristate + +config SND_SOC_AK4535 + tristate + +# Cirrus Logic CS4270 Codec +config SND_SOC_CS4270 + tristate + +# Cirrus Logic CS4270 Codec Hardware Mute Support +# Select if you have external muting circuitry attached to your CS4270. +config SND_SOC_CS4270_HWMUTE + bool + depends on SND_SOC_CS4270 + +# Cirrus Logic CS4270 Codec VD = 3.3V Errata +# Select if you are affected by the errata where the part will not function +# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will +# not select any sample rates that require MCLK to be divided by 1.5. +config SND_SOC_CS4270_VD33_ERRATA + bool + depends on SND_SOC_CS4270 + +config SND_SOC_SSM2602 + tristate + +config SND_SOC_TLV320AIC23 + tristate + depends on I2C + +config SND_SOC_TLV320AIC26 + tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE + depends on SPI + +config SND_SOC_TLV320AIC3X + tristate + select SND_HWDEP + depends on I2C + +config SND_SOC_TWL4030 + tristate + depends on TWL4030_CORE + +config SND_SOC_UDA1380 + tristate + +config SND_SOC_WM8510 + tristate + +config SND_SOC_WM8580 + tristate + +config SND_SOC_WM8731 + tristate + +config SND_SOC_WM8750 + tristate + +config SND_SOC_WM8753 + tristate + +config SND_SOC_WM8900 + tristate + +config SND_SOC_WM8903 + tristate + +config SND_SOC_WM8971 + tristate + +config SND_SOC_WM8990 + tristate + +config SND_SOC_WM9712 + tristate + +config SND_SOC_WM9713 + tristate diff --git a/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.c b/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.c index b39b3ae..20747a6 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,12 +55,30 @@ static int hp_dac_lim = 9; module_param(hp_dac_lim, int, 0); +/* Data for reading/writing to the IIR Filter hwdep */ +struct aic3x_iir_coeffs { + short N0, N1, N2, D1, D2; + short N3, N4, N5, D4, D5; +}; + +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; - u16 coeff_cache[AIC3X_COEFF_CACHE_SIZE]; + struct snd_hwdep *hwdep; + struct aic3x_dacfilter_t dacfilter; }; /* @@ -94,29 +113,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 */ -}; - -/* Page 1 registers store 2s compliment 16-bit numbers used for - * coefficients for effects. Each number is in 2 register, MSB followed - * by LSB. Therefore this array contains AIC3X_CACHEREGNUM/2 + 1 items. - */ -static const u16 aic3x_coeff[AIC3X_COEFF_CACHE_SIZE] = -{ - 0x00, /* Reg 0 */ - 0x6be3, 0x9666, 0x675d, 0x6be3, /* Reg 1 - 8 */ - 0x9666, 0x675d, 0x7d83, 0x84ee, /* 9 - 16 */ - 0x7d83, 0x84ee, 0x3955, 0xf32d, /* 17 - 24 */ - 0x537e, 0x6be3, 0x9666, 0x675d, /* 25 - 32 */ - 0x6be3, 0x9666, 0x675d, 0x7d83, /* 33 - 40 */ - 0x84ee, 0x7d83, 0x84ee, 0x3955, /* 41 - 48 */ - 0xf32d, 0x537e, 0x7fff, 0x0000, /* 49 - 56 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 57 - 64 */ - 0x3955, 0xf32d, 0x537e, 0x3955, /* 65 - 72 */ - 0xf32d, 0x537e, 0x0000, 0x0000, /* 73 - 80 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 81 - 88 */ - 0x0000, 0x0000, 0x0000, 0x0000, /* 89 - 96 */ - 0x0000, 0x0000, 0x0000, /* 97 - 102 */ + 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 */ }; /* @@ -131,10 +154,14 @@ static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec, return cache[reg]; } -static inline unsigned int aic3x_read_coeff_cache(struct snd_soc_codec *codec, - unsigned int reg) { - u16 *cache = ((struct aic3x_priv*)codec->private_data)->coeff_cache; - return cache[COEFF_OFFSET(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; } /* @@ -152,59 +179,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; + printk("MNZ: aic3x_write(reg = %i, val = %x), curpage = %i\n", + reg, value, curpage); + /*mutex_lock(&codec->mutex);*/ + + if (reg > 127){ + reg -= 128; + page = 1; + } + 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; + } + printk("MNZ: aic3x_write(), switched to page%i\n", curpage); + } + /* 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; if (codec->hw_write(codec->control_data, data, 2) == 2){ - aic3x_write_reg_cache(codec, data[0], data[1]); + 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; } -/* Write a coefficient to the page 1 registers. Switching the page is - * not done here and is left to the caller. - */ -static int aic3x_write_coeff(struct snd_soc_codec *codec, unsigned int msbreg, - int value) +/* Convert a value to 2s compliment and write to registers */ +static int aic3x_write_coeff(struct snd_soc_codec *codec, + u8 msbreg, int value) { - u8 i, values[2], data[2]; u16 outp; - u16 *cache = ((struct aic3x_priv*)codec->private_data)->coeff_cache; - - if (msbreg >= AIC3X_CACHEREGNUM) - return -1; - /* Change to 2s compliment and break into MSB and LSB */ if (value < 0) outp = 65536 + value; else outp = value; - values[0] = (outp >> 8) & 0xff; - values[1] = outp & 0xff; - - for(i = 0; i < 2; i++){ - data[0] = (msbreg + i) & 0xff; - data[1] = values[i] & 0xff; - - if (codec->hw_write(codec->control_data, data, 2) != 2) - return -EIO; - } + aic3x_write(codec, msbreg, outp >> 8); + aic3x_write(codec, msbreg+1, outp); - cache[COEFF_OFFSET(msbreg)] = outp; return 0; } + /* * read from the aic3x register space */ @@ -223,23 +261,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) \ @@ -318,6 +374,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", "On", "Custom" }; #define LDAC_ENUM 0 #define RDAC_ENUM 1 @@ -328,6 +386,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), @@ -339,6 +398,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), }; /* @@ -394,35 +454,152 @@ static int tlv320alc3x_info_volsw(struct snd_kcontrol *kcontrol, return 0; } +/* DAC Filter Functions */ -static int snd_soc_get_3d_attenuation_aic3x(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int aic3x_dacfilter_write_coeffs + (struct snd_soc_codec *codec, struct aic3x_iir_coeffs *coeffs) { - int val = aic3x_read_coeff_cache(snd_kcontrol_chip(kcontrol), EFFECTS_3DATTEN); - if(val > 32767) - val = val - 65536; + struct aic3x_priv *aic3x = codec->private_data; + printk("MNZ: dacfilter_write_coeffs\n"); + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, EFFECTS_ON, 0); + 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); + + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, EFFECTS_ON, EFFECTS_ON); + + memcpy((void*)&aic3x->dacfilter.coeffs, (void*)coeffs, + sizeof(struct aic3x_iir_coeffs)); + return 0; +} + +static int aic3x_dacfilter_set_state(struct snd_soc_codec *codec, + int state) +{ + struct aic3x_priv *aic3x = codec->private_data; + printk("MNZ: dacfilter_set_state(state = %i)\n", state); + + if(aic3x->dacfilter.state == state) return 0; + + aic3x->dacfilter.state = state; + + if(state == 0) + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, + EFFECTS_ON, 0); + else if(state == 1) {} + /* FIXME MNZ. Set preset from current chosen preset */ + else if (state == 2) + return aic3x_dacfilter_write_coeffs(codec, + &aic3x->dacfilter.coeffs); + return 0; +} + +/* DAC Filter hwdep device callbacks */ + +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; + return aic3x_dacfilter_set_state(codec, *((int*)arg)); + return 0; +} + +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_write_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); +printk("MNZ: get_dacfilter getting state: %i\n",((struct aic3x_priv*)codec->private_data)->dacfilter.state ); + 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); + printk("MNZ: put_dacfilter setting to %i\n", ucontrol->value.enumerated.item[0]); + 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) +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 reg = aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLA); int val = ucontrol->value.integer.value[0]; + if(val > 100 || val < 0) return -EINVAL; if(val){ - aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, reg | 0x04); + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, + EFFECTS_3D_ON, EFFECTS_3D_ON); + val = ((val - 50) * 65535) / 100 ; } else { - aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, reg & 0xfb); + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, + EFFECTS_3D_ON, 0); + val = -32768; } - val = ((val - 50) * 65535) / 100 ; - aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE1_SELECT); aic3x_write_coeff(codec, EFFECTS_3DATTEN, val); - aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); return 1; } @@ -496,6 +673,9 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { 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), }; @@ -1311,39 +1491,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 *reg_cache = codec->reg_cache; - u16 *coeff_cache = ((struct aic3x_priv*)codec->private_data)->coeff_cache; - - /* Sync hardware with the coeff_cache first so that filters can be - * turned on safely - */ - aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE1_SELECT); - for (i = 1; i < ARRAY_SIZE(aic3x_coeff); i++){ - data[0] = i*2 - 1; - data[1] = (coeff_cache[i] >> 8) && 0xff; - codec->hw_write(codec->control_data, data, 2); - - data[0]++; - data[1] = coeff_cache[i] & 0xff; - codec->hw_write(codec->control_data, data, 2); - } - - /* Sync hardware with the reg_cache */ - aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); - for (i = 1; i < ARRAY_SIZE(aic3x_reg); i++) { - data[0] = i; - data[1] = reg_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 @@ -1455,9 +1609,7 @@ static int aic3x_init(struct snd_soc_device *socdev) } /* Set some defaults for coefficients */ - aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE1_SELECT); aic3x_write_coeff(codec, EFFECTS_3DATTEN, -32768); - aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); return ret; @@ -1574,6 +1726,7 @@ static int aic3x_probe(struct platform_device *pdev) struct aic3x_setup_data *setup; struct snd_soc_codec *codec; struct aic3x_priv *aic3x; + struct snd_hwdep *hwdep; int ret = 0; printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION); @@ -1584,7 +1737,7 @@ static int aic3x_probe(struct platform_device *pdev) return -ENOMEM; aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); - memcpy(aic3x->coeff_cache, aic3x_coeff, sizeof(aic3x_coeff)); + memcpy(&aic3x->dacfilter, &aic3x_dacfilter, sizeof(aic3x_dacfilter)); if (aic3x == NULL) { kfree(codec); @@ -1608,6 +1761,15 @@ static int aic3x_probe(struct platform_device *pdev) /* Add other interfaces here */ #endif + if(snd_hwdep_new(codec->card, "IIR Filter", 0, &hwdep) == 0){ + printk("MNZ: hwdep initialized\n"); + hwdep->private_data = codec; + hwdep->ops.ioctl = snd_hwdep_dacfilter_ioctl_aic3x; + hwdep->ops.read = snd_hwdep_dacfilter_read_aic3x; + hwdep->ops.write = snd_hwdep_dacfilter_write_aic3x; + aic3x->hwdep = hwdep; + } + if (ret != 0) { kfree(codec->private_data); kfree(codec); diff --git a/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.h b/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.h index c71cafc..eb1a5a2 100644 --- a/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.h +++ b/kernel-2.6.28/sound/soc/codecs/tlv320aic3x.h @@ -13,7 +13,10 @@ #define _AIC3X_H /* AIC3X register space */ -#define AIC3X_CACHEREGNUM 103 +#define AIC3X_CACHEREGNUM 205 +#define AIC3X_PAGE0REGNUM 103 +#define AIC3X_PAGE1REGNUM 77 + #define AIC3X_COEFF_CACHE_SIZE 52 #define COEFF_OFFSET(msbreg) ((msbreg+1)/2) @@ -127,59 +130,59 @@ /* Page 1 registers for setting coefficients for filters */ /* DAC Audio Effects for Left Channel */ -#define EFFECTS_LEFT_N0 1 -#define EFFECTS_LEFT_N1 3 -#define EFFECTS_LEFT_N2 5 -#define EFFECTS_LEFT_N3 7 -#define EFFECTS_LEFT_N4 9 -#define EFFECTS_LEFT_N5 11 - -#define EFFECTS_LEFT_D1 13 -#define EFFECTS_LEFT_D2 15 -#define EFFECTS_LEFT_D4 17 -#define EFFECTS_LEFT_D5 19 +#define EFFECTS_LEFT_N0 129 +#define EFFECTS_LEFT_N1 131 +#define EFFECTS_LEFT_N2 133 +#define EFFECTS_LEFT_N3 135 +#define EFFECTS_LEFT_N4 137 +#define EFFECTS_LEFT_N5 139 + +#define EFFECTS_LEFT_D1 141 +#define EFFECTS_LEFT_D2 143 +#define EFFECTS_LEFT_D4 145 +#define EFFECTS_LEFT_D5 147 /* DAC De-Emphasis for Left Channel */ -#define DEEMPH_LEFT_N0 21 -#define DEEMPH_LEFT_N1 23 -#define DEEMPH_LEFT_D1 25 +#define DEEMPH_LEFT_N0 149 +#define DEEMPH_LEFT_N1 151 +#define DEEMPH_LEFT_D1 153 /* DAC Audio Effects for Right Channel */ -#define EFFECTS_RIGHT_N0 27 -#define EFFECTS_RIGHT_N1 29 -#define EFFECTS_RIGHT_N2 31 -#define EFFECTS_RIGHT_N3 33 -#define EFFECTS_RIGHT_N4 35 -#define EFFECTS_RIGHT_N5 37 +#define EFFECTS_RIGHT_N0 155 +#define EFFECTS_RIGHT_N1 157 +#define EFFECTS_RIGHT_N2 159 +#define EFFECTS_RIGHT_N3 161 +#define EFFECTS_RIGHT_N4 163 +#define EFFECTS_RIGHT_N5 165 -#define EFFECTS_RIGHT_D1 39 -#define EFFECTS_RIGHT_D2 41 -#define EFFECTS_RIGHT_D4 43 -#define EFFECTS_RIGHT_D5 45 +#define EFFECTS_RIGHT_D1 167 +#define EFFECTS_RIGHT_D2 169 +#define EFFECTS_RIGHT_D4 171 +#define EFFECTS_RIGHT_D5 173 /* DAC De-Emphasis for Right Channel */ -#define DEEMPH_RIGHT_N0 47 -#define DEEMPH_RIGHT_N1 49 -#define DEEMPH_RIGHT_D1 51 +#define DEEMPH_RIGHT_N0 175 +#define DEEMPH_RIGHT_N1 177 +#define DEEMPH_RIGHT_D1 179 /* DAC 3D Attenuation */ -#define EFFECTS_3DATTEN 53 +#define EFFECTS_3DATTEN 181 /* ADC High-Pass Filter for Left Channel */ -#define HIGHPASS_LEFT_NO 65 -#define HIGHPASS_LEFT_N1 67 -#define HIGHPASS_LEFT_D1 69 +#define HIGHPASS_LEFT_NO 193 +#define HIGHPASS_LEFT_N1 195 +#define HIGHPASS_LEFT_D1 197 /* ADC High-Pass Filter for Right Channel */ -#define HIGHPASS_RIGHT_NO 71 -#define HIGHPASS_RIGHT_N1 73 -#define HIGHPASS_RIGHT_D1 75 +#define HIGHPASS_RIGHT_NO 199 +#define HIGHPASS_RIGHT_N1 201 +#define HIGHPASS_RIGHT_D1 203 /* Page select register bits */ #define PAGE0_SELECT 0 @@ -244,6 +247,11 @@ /* Default input volume */ #define DEFAULT_GAIN 0x20 +/* Filter bits */ +#define EFFECTS_3D_ON 0x04 +#define EFFECTS_ON 0x0a +#define DEEMPH_ON 0x05 + /* GPIO API */ enum { AIC3X_GPIO1_FUNC_DISABLED = 0,