Fixed ioctl code and added coeff setting example code
[aic34-eq] / kernel-2.6.28 / sound / soc / codecs / tlv320aic3x.c
index b39b3ae..ec84be5 100644 (file)
@@ -46,6 +46,7 @@
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 #include <sound/tlv.h>
+#include <sound/hwdep.h>
 
 #include "tlv320aic3x.h"
 
 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;
-       u16 coeff_cache[AIC3X_COEFF_CACHE_SIZE];
+       struct snd_hwdep *hwdep;
+       struct aic3x_dacfilter_t dacfilter;
 };
 
 /*
@@ -94,29 +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 */
-};
-
-/* 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 +148,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 +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;
 
        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;
+    aic3x_write(codec, msbreg, outp >> 8);
+    aic3x_write(codec, msbreg+1, outp);
 
-       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;
-       }
-
-       cache[COEFF_OFFSET(msbreg)] = outp;
        return 0;
 }
 
+
 /*
  * read from the aic3x register space
  */
@@ -223,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) \
@@ -318,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
@@ -328,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),
@@ -339,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),
 };
 
 /*
@@ -394,35 +448,194 @@ 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 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)
+{
+       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)
 {
-       int val = aic3x_read_coeff_cache(snd_kcontrol_chip(kcontrol), EFFECTS_3DATTEN);
-    if(val > 32767)
-         val = val - 65536;
+       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)
+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 +709,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 +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 *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
@@ -1352,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;
@@ -1448,16 +1641,27 @@ 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(codec, AIC3X_PAGE_SELECT, PAGE1_SELECT);
        aic3x_write_coeff(codec, EFFECTS_3DATTEN, -32768);
-       aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT);
+       printk("MNZ: END aic3x_init\n");
 
        return ret;
 
@@ -1584,7 +1788,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);