Added luke-jr patch to add highpass filter (so speakers don't blow up)
[kernel-power] / kernel-power-2.6.28 / debian / patches / Support-for-tlv320aic3x-codec-highpass-filter-needed.diff
1 From 4c4fb7d671cd645caf7b7490535815d876dc4864 Mon Sep 17 00:00:00 2001
2 From: mnzaki <mnzaki@gmail.com>
3 Date: Fri, 17 Sep 2010 01:15:25 +0300
4 Subject: [PATCH] Support for tlv320aic3x codec highpass filter needed to avoid destroying Nokia N900 speakers
5
6 Patchset combined and cleaned up by Luke-Jr <luke-jr+git@utopios.org>
7
8 - All page 1 registers added as a u16 array of coefficients with routines
9   to read/write the coefficients (aic3x_read_coeff_cache and
10   aic3x_write_coeff respectively)
11 - Added syncing coeff_cache with hardware after a suspend/resume cycle
12 - Added ALSA controls for the 3-D depth simulation filter
13 - DAC Filter controls
14 - hwdep device called "IIR Filter" added, documentation on how to use it
15   also added in: Documentation/sound/alsa/soc/codecs/tlv320aic3x.txt
16 - Added de-emphasis filter functions and speaker protection.
17 - Added functions to manipulate the filter's coeffs and enable/disable it,
18   and example code.
19 - Also changed the machine layer (rx51.c) to set up the filter as a highpass
20   and turn it on when on speaker output for speaker protection.
21
22 TODO: Change filter coeffs if sample rate is changed. Right now it's fit
23       for 48kHz (and possibly fine with 44.1kHz).
24
25 Filter designed using scilab:
26
27 fc = [0.015 0.010884354 0.01]
28 hz = iir(1, 'hp', 'ellip', [fc(3) 0], [0.1 0.1])
29 [hzm,fr]=frmag(hz,256);
30 plot2d(fr',hzm')
31 xtitle('Discrete IIR filter band pass  0.15<fr<0.25 ',' ',' ');
32 q=poly(0,'q');     //to express the result in terms of the ...
33 hzd=horner(hz,1/q) //delay operator q=z^-1
34 ---
35  Documentation/sound/alsa/soc/tlv320aic3x.txt |   76 +++++
36  sound/soc/codecs/Kconfig                     |    1 +
37  sound/soc/codecs/tlv320aic3x.c               |  381 ++++++++++++++++++++++++--
38  sound/soc/codecs/tlv320aic3x.h               |   81 ++++++-
39  sound/soc/omap/rx51.c                        |   14 +
40  5 files changed, 527 insertions(+), 26 deletions(-)
41  create mode 100644 Documentation/sound/alsa/soc/tlv320aic3x.txt
42
43 diff --git a/Documentation/sound/alsa/soc/tlv320aic3x.txt b/Documentation/sound/alsa/soc/tlv320aic3x.txt
44 new file mode 100644
45 index 0000000..c0cc4cf
46 --- /dev/null
47 +++ b/Documentation/sound/alsa/soc/tlv320aic3x.txt
48 @@ -0,0 +1,76 @@
49 +The TLV320AIC3X is a powerful four channel low power audio codec family.
50 +More information is available at:
51 +    http://focus.ti.com/docs/prod/folders/print/tlv320aic34.html
52 +Datasheet:
53 +    http://www.ti.com/lit/gpn/tlv320aic34
54 +
55 +The codec driver leverages the codecs effects through alsa controls and a
56 +hwdep device for controlling the hardware fourth-order IIR filter block.
57 +
58 +There's an alsa control, "3D Control - Depth" for depth simulation.
59 +The rest of the controls are for the IIR filter:
60 +
61 +1- A control for setting the bass/treble gain, which sets the filter's
62 +   coefficients to certain precalculated values.
63 +2- A control for 'off' / 'Bass/Treble' / 'Custom'. 'Bass/Treble' means
64 +   the bass/treble gain controls are used, while 'custom' means the
65 +   coefficients have been set through the hwdep device (see below).
66 +Note: bass/treble controls are not yet implemented
67 +
68 +Filters
69 +--------
70 +Note: Setting a filter's coeffs automatically turns it off, it needs to
71 +be turned on explicitly.
72 +
73 +The De-emphasis filter can only be controlled on the machine driver level.
74 +For example for the n900 (rx51.c) it is used as highpass filter for
75 +speaker protection. See tlv320aic3x.h, aic3x_deemph_set_* for details.
76 +
77 +The IIR Filter consists of 2 cascaded biquads. The formula is:
78 + /                                \  /                                \
79 +|    (N0 + 2*N1*z^-1 + N2*z^-2)    ||    (N3 + 2*N4*z^-1 + N5*z^-2)    |
80 +|  ------------------------------  ||  ------------------------------  |
81 +|  (32768 - 2*D1*z^-1 - D2*z^-2)   ||   (32768 - 2*D4*z^-1 - D5*z^-2)  |
82 + \                                /  \                                /
83 +
84 +The filter can be controlled through an alsa hwdep device, via
85 +libasound. A short example follows, note that the data struct must be
86 +passed *EXACTLY* as shown. Remember to link against libasound:
87 +gcc myapp.c -lasound -o myapp
88 +
89 +-------------------------------EXAMPLE----------------------------------
90 +#include <stdint.h>
91 +#include <fcntl.h>
92 +#include <alsa/asoundlib.h>
93 +#include <alsa/hwdep.h>
94 +#include <stdio.h>
95 +
96 +struct iir_coeffs {
97 +       int16_t N0, N1, N2, D1, D2;
98 +       int16_t N3, N4, N5, D4, D5;
99 +};
100 +
101 +int main(){
102 +       struct iir_coeffs coeffs = {
103 +               .N0 = 32767, .N1 = 4265, .N2 = -10472, .D1 = -6269, .D2 = 0,
104 +               .N3 = 32027, .N4 = -31187, .N5 = 30352, .D4 = 31187, .D5 = -29613
105 +       };
106 +
107 +       snd_hwdep_t *hwdep;
108 +       int ret; int arg;
109 +
110 +       ret = snd_hwdep_open(&hwdep, "hw:0,0", SND_HWDEP_OPEN_DUPLEX);
111 +       printf("open: %i\n", ret);
112 +       if(ret) return 1;
113 +
114 +       ret = snd_hwdep_write(hwdep, (void*)&coeffs, sizeof(coeffs));
115 +       printf("write: %i\n", ret);
116 +
117 +       /* Set state to 2, which is 'Custom'. This writes coeffs to hardware
118 +       * and enables filter */
119 +       arg = 2;
120 +       ret = snd_hwdep_ioctl(hwdep, 1, &arg);
121 +       printf("ioctl: %i\n", ret);
122 +       return 0;
123 +}
124 +------------------------------------------------------------------------
125 diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
126 index 5df7402..022e37a 100644
127 --- a/sound/soc/codecs/Kconfig
128 +++ b/sound/soc/codecs/Kconfig
129 @@ -74,6 +74,7 @@ config SND_SOC_TLV320AIC26
130  
131  config SND_SOC_TLV320AIC3X
132         tristate
133 +       select SND_HWDEP
134         depends on I2C
135  
136  config SND_SOC_TWL4030
137 diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
138 index 4f70822e..4d15440 100644
139 --- a/sound/soc/codecs/tlv320aic3x.c
140 +++ b/sound/soc/codecs/tlv320aic3x.c
141 @@ -46,6 +46,7 @@
142  #include <sound/soc-dapm.h>
143  #include <sound/initval.h>
144  #include <sound/tlv.h>
145 +#include <sound/hwdep.h>
146  
147  #include "tlv320aic3x.h"
148  
149 @@ -54,11 +55,24 @@
150  static int hp_dac_lim = 9;
151  module_param(hp_dac_lim, int, 0);
152  
153 +struct aic3x_dacfilter_t {
154 +       struct aic3x_iir_coeffs coeffs;
155 +       int state;
156 +} aic3x_dacfilter = {
157 +       .coeffs = {
158 +               .N0 = 27619, .N1 = -27034, .N2 = 26461, .D1 = 32131, .D2 = -31506,
159 +               .N3 = 27619, .N4 = -27034, .N5 = 26461, .D4 = 32131, .D5 = -31506,
160 +       },
161 +       .state = 0,
162 +};
163 +
164  /* codec private data */
165  struct aic3x_priv {
166         unsigned int sysclk;
167         int master;
168         int prepare_reset;
169 +       struct snd_hwdep *hwdep;
170 +       struct aic3x_dacfilter_t dacfilter;
171  };
172  
173  /*
174 @@ -93,7 +107,33 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
175         0x00, 0x00, 0x00, 0x00, /* 88 */
176         0x00, 0x00, 0x00, 0x00, /* 92 */
177         0x00, 0x00, 0x00, 0x00, /* 96 */
178 -       0x00, 0x00, 0x02,       /* 100 */
179 +       0x00, 0x00, 0x02, 0x00, /* 100 */
180 +       0x00, 0x00, 0x00, 0x00, /* 104 */
181 +       0x00, 0x00, 0x00, 0x00, /* 108 */
182 +       0x00, 0x00, 0x00, 0x00, /* 112 */
183 +       0x00, 0x00, 0x00, 0x00, /* 116 */
184 +       0x00, 0x00, 0x00, 0x00, /* 120 */
185 +       0x00, 0x00, 0x00, 0x00, /* 124 */
186 +       0x01, 0x6b, 0xe3, 0x96, /* 128 */
187 +       0x66, 0x67, 0x5d, 0x6b, /* 132 */
188 +       0xe3, 0x96, 0x66, 0x67, /* 136 */
189 +       0x5d, 0x7d, 0x83, 0x84, /* 140 */
190 +       0xee, 0x7d, 0x83, 0x84, /* 144 */
191 +       0xee, 0x39, 0x55, 0xf3, /* 148 */
192 +       0x2d, 0x53, 0x7e, 0x6b, /* 152 */
193 +       0xe3, 0x96, 0x66, 0x67, /* 156 */
194 +       0x5d, 0x6b, 0xe3, 0x96, /* 160 */
195 +       0x66, 0x67, 0x5d, 0x7d, /* 164 */
196 +       0x83, 0x84, 0xee, 0x7d, /* 168 */
197 +       0x83, 0x84, 0xee, 0x39, /* 172 */
198 +       0x55, 0xf3, 0x2d, 0x53, /* 176 */
199 +       0x7e, 0x7f, 0xff, 0x00, /* 180 */
200 +       0x00, 0x00, 0x00, 0x00, /* 184 */
201 +       0x00, 0x00, 0x00, 0x00, /* 188 */
202 +       0x00, 0x39, 0x55, 0xf3, /* 192 */
203 +       0x2d, 0x53, 0x7e, 0x39, /* 196 */
204 +       0x55, 0xf3, 0x2d, 0x53, /* 200 */
205 +       0x7e,                   /* 204 */
206  };
207  
208  /*
209 @@ -108,6 +148,17 @@ static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec,
210         return cache[reg];
211  }
212  
213 +static inline int aic3x_read_coeff_reg_cache(struct snd_soc_codec *codec,
214 +                                            unsigned int msbreg)
215 +{
216 +       int val;
217 +       val = aic3x_read_reg_cache(codec, msbreg) << 8;
218 +       val |= aic3x_read_reg_cache(codec, msbreg+1);
219 +       if (val > 32767)
220 +               val -= 65536;
221 +       return val;
222 +}
223 +
224  /*
225   * write aic3x register cache
226   */
227 @@ -126,22 +177,61 @@ static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec,
228  static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg,
229                        unsigned int value)
230  {
231 -       u8 data[2];
232 +       static char curpage = -1;
233 +       u8 data[2], page = 0;
234 +
235 +       if (reg > 127){
236 +               reg -= 128;
237 +               page = 1;
238 +       }
239 +       if (reg && curpage != page) {
240 +               data[0] = 0;
241 +               data[1] = page;
242 +               if (codec->hw_write(codec->control_data, data, 2) != 2)
243 +                       return -EIO;
244 +               else
245 +                       curpage = page;
246 +       }
247 +
248  
249         /* data is
250          *   D15..D8 aic3x register offset
251          *   D7...D0 register data
252          */
253 -       data[0] = reg & 0xff;
254 -       data[1] = value & 0xff;
255 +       data[0] = reg;
256 +       data[1] = value;
257  
258 -       aic3x_write_reg_cache(codec, data[0], data[1]);
259         if (codec->hw_write(codec->control_data, data, 2) == 2)
260 +       {
261 +               aic3x_write_reg_cache(codec, data[0] + page * 128, data[1]);
262 +               if(!reg)
263 +                       curpage = value; /* for reg = 0, ie, page setting */
264                 return 0;
265 +       }
266         else
267                 return -EIO;
268 +
269 +       return -EIO;
270 +}
271 +
272 +/* Convert a value to 2s compliment and write to registers */
273 +static int aic3x_write_coeff(struct snd_soc_codec *codec,
274 +                            u8 msbreg, int value)
275 +{
276 +       u16 outp;
277 +
278 +       if (value < 0)
279 +               outp = 65536 + value;
280 +       else
281 +               outp = value;
282 +
283 +       aic3x_write(codec, msbreg, outp >> 8);
284 +       aic3x_write(codec, msbreg + 1, outp);
285 +
286 +       return 0;
287  }
288  
289 +
290  /*
291   * read from the aic3x register space
292   */
293 @@ -160,23 +250,37 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
294         return 0;
295  }
296  
297 -/*
298 - * Reset for getting low power consumption after bypass paths
299 - */
300 -static void aic3x_reset(struct snd_soc_codec *codec)
301 +static int aic3x_sync_hw(struct snd_soc_codec *codec)
302  {
303         u8 *cache = codec->reg_cache;
304         u8 data[2];
305         int i;
306  
307 -       aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
308 +       aic3x_write(codec, AIC3X_PAGE_SELECT, 1);
309 +       for (i = 1; i < AIC3X_PAGE1REGNUM; ++i) {
310 +               data[0] = i;
311 +               data[1] = cache[i + 128];
312 +               codec->hw_write(codec->control_data, data, 2);
313 +       }
314  
315         /* We do not rewrite page select nor reset again */
316 -       for (i = AIC3X_SAMPLE_RATE_SEL_REG; i < ARRAY_SIZE(aic3x_reg); i++) {
317 +       aic3x_write(codec, AIC3X_PAGE_SELECT, 0);
318 +       for (i = 2; i < AIC3X_PAGE0REGNUM; ++i) {
319                 data[0] = i;
320                 data[1] = cache[i];
321                 codec->hw_write(codec->control_data, data, 2);
322         }
323 +
324 +       return 0;
325 +}
326 +
327 +/*
328 + * Reset for getting low power consumption after bypass paths
329 + */
330 +static void aic3x_reset(struct snd_soc_codec *codec)
331 +{
332 +       aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
333 +       aic3x_sync_hw(codec);
334  }
335  
336  #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
337 @@ -255,6 +359,8 @@ static const char *aic3x_right_hpcom_mux[] =
338  static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
339  static const char *aic3x_adc_hpf[] =
340      { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
341 +static const char *aic3x_dac_filt[] =
342 +    { "Off", "Bass/Treble", "Custom" };
343  
344  #define LDAC_ENUM      0
345  #define RDAC_ENUM      1
346 @@ -265,6 +371,7 @@ static const char *aic3x_adc_hpf[] =
347  #define LINE2L_ENUM    6
348  #define LINE2R_ENUM    7
349  #define ADC_HPF_ENUM   8
350 +#define DAC_FILT_ENUM  9
351  
352  static const struct soc_enum aic3x_enum[] = {
353         SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
354 @@ -276,6 +383,7 @@ static const struct soc_enum aic3x_enum[] = {
355         SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
356         SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
357         SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
358 +       SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 1, 3, 3, aic3x_dac_filt),
359  };
360  
361  /*
362 @@ -331,6 +439,211 @@ static int tlv320alc3x_info_volsw(struct snd_kcontrol *kcontrol,
363         return 0;
364  }
365  
366 +/* DAC and De-emphasis Filter Functions */
367 +int aic3x_deemph_set_coeffs(struct snd_soc_codec *codec, int N0, int N1,
368 +                           int D1)
369 +{
370 +       printk("MNZ: setting deemph coeffs\n");
371 +       snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, DEEMPH_ON, 0);
372 +
373 +       aic3x_write_coeff(codec, DEEMPH_LEFT_N0, N0);
374 +       aic3x_write_coeff(codec, DEEMPH_LEFT_N1, N1);
375 +       aic3x_write_coeff(codec, DEEMPH_LEFT_D1, D1);
376 +       aic3x_write_coeff(codec, DEEMPH_RIGHT_N0, N0);
377 +       aic3x_write_coeff(codec, DEEMPH_RIGHT_N1, N1);
378 +       aic3x_write_coeff(codec, DEEMPH_RIGHT_D1, D1);
379 +
380 +       return 0;
381 +}
382 +EXPORT_SYMBOL_GPL(aic3x_deemph_set_coeffs);
383 +
384 +int aic3x_deemph_set_state(struct snd_soc_codec *codec, int state)
385 +{
386 +       printk("MNZ: Setting De-Emph filter: %i\n", state);
387 +       if(state)
388 +               state = DEEMPH_ON;
389 +       else
390 +               state = 0;
391 +       return snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, DEEMPH_ON,
392 +                                  state);
393 +}
394 +EXPORT_SYMBOL_GPL(aic3x_deemph_set_state);
395 +
396 +static int aic3x_dacfilter_write_coeffs(struct snd_soc_codec *codec,
397 +                                       struct aic3x_iir_coeffs *coeffs)
398 +{
399 +       printk("MNZ: dacfilter_write_coeffs\n");
400 +       aic3x_write_coeff(codec, EFFECTS_LEFT_N0, coeffs->N0);
401 +       aic3x_write_coeff(codec, EFFECTS_LEFT_N1, coeffs->N1);
402 +       aic3x_write_coeff(codec, EFFECTS_LEFT_N2, coeffs->N2);
403 +       aic3x_write_coeff(codec, EFFECTS_LEFT_D1, coeffs->D1);
404 +       aic3x_write_coeff(codec, EFFECTS_LEFT_D2, coeffs->D2);
405 +       aic3x_write_coeff(codec, EFFECTS_LEFT_N3, coeffs->N3);
406 +       aic3x_write_coeff(codec, EFFECTS_LEFT_N4, coeffs->N4);
407 +       aic3x_write_coeff(codec, EFFECTS_LEFT_N5, coeffs->N5);
408 +       aic3x_write_coeff(codec, EFFECTS_LEFT_D4, coeffs->D4);
409 +       aic3x_write_coeff(codec, EFFECTS_LEFT_D5, coeffs->D5);
410 +
411 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_N0, coeffs->N0);
412 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_N1, coeffs->N1);
413 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_N2, coeffs->N2);
414 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_D1, coeffs->D1);
415 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_D2, coeffs->D2);
416 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_N3, coeffs->N3);
417 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_N4, coeffs->N4);
418 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_N5, coeffs->N5);
419 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_D4, coeffs->D4);
420 +       aic3x_write_coeff(codec, EFFECTS_RIGHT_D5, coeffs->D5);
421 +
422 +       return 1;
423 +}
424 +
425 +int aic3x_dacfilter_set_coeffs(struct snd_soc_codec *codec,
426 +                              struct aic3x_iir_coeffs *coeffs)
427 +{
428 +       struct aic3x_priv *aic3x = codec->private_data;
429 +       memcpy((void*)&aic3x->dacfilter.coeffs, (void*)coeffs,
430 +                  sizeof(struct aic3x_iir_coeffs));
431 +       if(aic3x->dacfilter.state == 2)
432 +               aic3x_dacfilter_set_state(codec, 0);
433 +       return 0;
434 +}
435 +EXPORT_SYMBOL_GPL(aic3x_dacfilter_set_coeffs);
436 +
437 +int aic3x_dacfilter_set_state(struct snd_soc_codec *codec, int state)
438 +{
439 +       printk("MNZ: dacfilter_set_state to %i\n", state);
440 +       struct aic3x_priv *aic3x = codec->private_data;
441 +       int ret = 0;
442 +       if (aic3x->dacfilter.state == state)
443 +               return 0;
444 +
445 +       snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, EFFECTS_ON, 0);
446 +
447 +       if(state == 0)
448 +               ret = 1;
449 +       else if(state == 1)
450 +               ret = 1;
451 +               /* FIXME MNZ. Set preset from current chosen preset */
452 +       else if (state == 2) {
453 +               ret = aic3x_dacfilter_write_coeffs(codec,
454 +                                                  &aic3x->dacfilter.coeffs);
455 +               if (ret)
456 +                       snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL,
457 +                                           EFFECTS_ON, EFFECTS_ON);
458 +       }
459 +       else
460 +               ret = 0;
461 +
462 +       if (ret)
463 +               aic3x->dacfilter.state = state;
464 +       return ret;
465 +}
466 +
467 +EXPORT_SYMBOL_GPL(aic3x_dacfilter_set_state);
468 +
469 +/* DAC Filter hwdep device callbacks */
470 +
471 +static int snd_hwdep_dacfilter_open_aic3x(struct snd_hwdep *hw,
472 +                                         struct file *file)
473 +{
474 +       return 0;
475 +}
476 +
477 +static int snd_hwdep_dacfilter_ioctl_aic3x(struct snd_hwdep *hw,
478 +                                          struct file *file,
479 +                                          unsigned int cmd,
480 +                                          unsigned long arg)
481 +{
482 +       /* Only IOCTL command is for enabling/disabling filter, cmd = 1
483 +        * arg = 0 to disable, 1 to enable and set to bass/treble,
484 +        *       2 to enable and set to custom coeffs
485 +        */
486 +       struct snd_soc_codec *codec = hw->private_data;
487 +       if (cmd != 1)
488 +               return -EINVAL;
489 +       printk("MNZ: IOCTL: cmd = %i, arg = %i\n", cmd, *((int*)arg));
490 +       return aic3x_dacfilter_set_state(codec, *((int*)arg));
491 +}
492 +
493 +static long snd_hwdep_dacfilter_read_aic3x(struct snd_hwdep *hw,
494 +                                          char __user *buf, long count,
495 +                                          loff_t *offset)
496 +{
497 +       struct aic3x_priv *aic3x =
498 +               ((struct snd_soc_codec*)hw->private_data)->private_data;
499 +       if (count != sizeof(struct aic3x_iir_coeffs))
500 +               return -EINVAL;
501 +       memcpy((void*)buf, (void*)&aic3x->dacfilter.coeffs, count);
502 +       return 0;
503 +}
504 +
505 +static long snd_hwdep_dacfilter_write_aic3x(struct snd_hwdep *hw,
506 +                                           const char __user *buf,
507 +                                           long count, loff_t *offset)
508 +{
509 +       struct snd_soc_codec *codec = hw->private_data;
510 +       if (count != sizeof(struct aic3x_iir_coeffs))
511 +               return -EINVAL;
512 +
513 +       ((struct aic3x_priv*)codec->private_data)->dacfilter.state = 2;
514 +
515 +       return aic3x_dacfilter_set_coeffs(codec, (struct aic3x_iir_coeffs*)buf);
516 +}
517 +
518 +/* DAC filter and 3D depth ALSA controls callbacks */
519 +
520 +static int snd_soc_get_dacfilter_aic3x(struct snd_kcontrol *kcontrol,
521 +                                      struct snd_ctl_elem_value *ucontrol)
522 +{
523 +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
524 +       ucontrol->value.enumerated.item[0] =
525 +               ((struct aic3x_priv*)codec->private_data)->dacfilter.state;
526 +       return 0;
527 +}
528 +
529 +static int snd_soc_put_dacfilter_aic3x(struct snd_kcontrol *kcontrol,
530 +                                      struct snd_ctl_elem_value *ucontrol)
531 +{
532 +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
533 +       if (ucontrol->value.enumerated.item[0] > 2)
534 +               return -EINVAL;
535 +       aic3x_dacfilter_set_state(codec, ucontrol->value.enumerated.item[0]);
536 +       return 1;
537 +}
538 +
539 +static int snd_soc_get_3d_attenuation_aic3x(struct snd_kcontrol *kcontrol,
540 +                                       struct snd_ctl_elem_value *ucontrol)
541 +{
542 +       int val = aic3x_read_coeff_reg_cache(snd_kcontrol_chip(kcontrol),
543 +                                            EFFECTS_3DATTEN);
544 +       val = ((val * 100) / 65530) + 50;
545 +       ucontrol->value.integer.value[0] = val;
546 +       return 0;
547 +}
548 +
549 +static int snd_soc_put_3d_attenuation_aic3x(struct snd_kcontrol *kcontrol,
550 +                                       struct snd_ctl_elem_value *ucontrol)
551 +{
552 +       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
553 +       int val = ucontrol->value.integer.value[0];
554 +       if (val > 100 || val < 0)
555 +               return -EINVAL;
556 +
557 +       if (val) {
558 +               snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, EFFECTS_3D_ON,
559 +                                   EFFECTS_3D_ON);
560 +               val = ((val - 50) * 65535) / 100;
561 +       } else {
562 +               snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, EFFECTS_3D_ON, 0);
563 +               val = -32768;
564 +       }
565 +
566 +       aic3x_write_coeff(codec, EFFECTS_3DATTEN, val);
567 +
568 +       return 1;
569 +}
570 +
571  static const struct snd_kcontrol_new aic3x_snd_controls[] = {
572         /* Output */
573         SOC_DOUBLE_R_TLV("PCM Playback Volume",
574 @@ -399,6 +712,13 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
575         SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
576  
577         SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
578 +
579 +       SOC_ENUM_EXT("Hardware EQ", aic3x_enum[DAC_FILT_ENUM],
580 +                    snd_soc_get_dacfilter_aic3x, snd_soc_put_dacfilter_aic3x),
581 +
582 +       SOC_SINGLE_EXT("3D Control - Depth", EFFECTS_3DATTEN, 0, 100, 0,
583 +                      snd_soc_get_3d_attenuation_aic3x,
584 +                      snd_soc_put_3d_attenuation_aic3x),
585  };
586  
587  /* add non dapm controls */
588 @@ -1212,23 +1532,13 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
589  static int aic3x_resume(struct platform_device *pdev)
590  {
591         struct snd_soc_device *socdev = platform_get_drvdata(pdev);
592 -       struct snd_soc_codec *codec = socdev->codec;
593 -       int i;
594 -       u8 data[2];
595 -       u8 *cache = codec->reg_cache;
596 -
597 -       /* Sync reg_cache with the hardware */
598 -       for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) {
599 -               data[0] = i;
600 -               data[1] = cache[i];
601 -               codec->hw_write(codec->control_data, data, 2);
602 -       }
603 -
604 -       aic3x_set_bias_level(codec, codec->suspend_bias_level);
605 -
606 +       aic3x_sync_hw(socdev->codec);
607 +       aic3x_set_bias_level(socdev->codec,
608 +                       socdev->codec->suspend_bias_level);
609         return 0;
610  }
611  
612 +
613  /*
614   * initialise the AIC3X driver
615   * register the mixer and dsp interfaces with the kernel
616 @@ -1237,8 +1547,11 @@ static int aic3x_init(struct snd_soc_device *socdev)
617  {
618         struct snd_soc_codec *codec = socdev->codec;
619         struct aic3x_setup_data *setup = socdev->codec_data;
620 +       struct snd_hwdep *hwdep;
621 +       char hwdepid[] = "IIR Filter";
622         int reg, ret = 0;
623  
624 +       printk("MNZ: BEGIN aic3x_init\n");
625         codec->name = "tlv320aic3x";
626         codec->owner = THIS_MODULE;
627         codec->read = aic3x_read_reg_cache;
628 @@ -1333,12 +1646,28 @@ static int aic3x_init(struct snd_soc_device *socdev)
629  
630         aic3x_add_controls(codec);
631         aic3x_add_widgets(codec);
632 +
633 +       if(snd_hwdep_new(codec->card, hwdepid, 0, &hwdep) == 0){
634 +               hwdep->private_data = codec;
635 +               sprintf(hwdep->name, hwdepid);
636 +               hwdep->ops.open = snd_hwdep_dacfilter_open_aic3x;
637 +               hwdep->ops.ioctl = snd_hwdep_dacfilter_ioctl_aic3x;
638 +               hwdep->ops.read = snd_hwdep_dacfilter_read_aic3x;
639 +               hwdep->ops.write = snd_hwdep_dacfilter_write_aic3x;
640 +               ((struct aic3x_priv*)codec->private_data)->hwdep = hwdep;
641 +       }
642 +
643         ret = snd_soc_register_card(socdev);
644 +
645         if (ret < 0) {
646                 printk(KERN_ERR "aic3x: failed to register card\n");
647                 goto card_err;
648         }
649  
650 +       /* Set some defaults for coefficients */
651 +       aic3x_write_coeff(codec, EFFECTS_3DATTEN, -32768);
652 +       printk("MNZ: END aic3x_init\n");
653 +
654         return ret;
655  
656  card_err:
657 @@ -1464,6 +1793,8 @@ static int aic3x_probe(struct platform_device *pdev)
658                 return -ENOMEM;
659  
660         aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
661 +       memcpy(&aic3x->dacfilter, &aic3x_dacfilter, sizeof(aic3x_dacfilter));
662 +
663         if (aic3x == NULL) {
664                 kfree(codec);
665                 return -ENOMEM;
666 diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
667 index 15a98aa..9927887 100644
668 --- a/sound/soc/codecs/tlv320aic3x.h
669 +++ b/sound/soc/codecs/tlv320aic3x.h
670 @@ -13,7 +13,12 @@
671  #define _AIC3X_H
672  
673  /* AIC3X register space */
674 -#define AIC3X_CACHEREGNUM              103
675 +#define AIC3X_CACHEREGNUM              205
676 +#define AIC3X_PAGE0REGNUM              103
677 +#define AIC3X_PAGE1REGNUM              77
678 +
679 +#define AIC3X_COEFF_CACHE_SIZE 52
680 +#define COEFF_OFFSET(msbreg)   ((msbreg + 1) / 2)
681  
682  /* Page select register */
683  #define AIC3X_PAGE_SELECT              0
684 @@ -123,6 +128,62 @@
685  /* Clock generation control register */
686  #define AIC3X_CLKGEN_CTRL_REG          102
687  
688 +/* Page 1 registers for setting coefficients for filters */
689 +/* DAC Audio Effects for Left Channel */
690 +#define EFFECTS_LEFT_N0 129
691 +#define EFFECTS_LEFT_N1 131
692 +#define EFFECTS_LEFT_N2 133
693 +#define EFFECTS_LEFT_N3 135
694 +#define EFFECTS_LEFT_N4 137
695 +#define EFFECTS_LEFT_N5 139
696 +
697 +#define EFFECTS_LEFT_D1 141
698 +#define EFFECTS_LEFT_D2 143
699 +#define EFFECTS_LEFT_D4 145
700 +#define EFFECTS_LEFT_D5 147
701 +
702 +/* DAC De-Emphasis for Left Channel */
703 +
704 +#define DEEMPH_LEFT_N0 149
705 +#define DEEMPH_LEFT_N1 151
706 +#define DEEMPH_LEFT_D1 153
707 +
708 +/* DAC Audio Effects for Right Channel */
709 +
710 +#define EFFECTS_RIGHT_N0 155
711 +#define EFFECTS_RIGHT_N1 157
712 +#define EFFECTS_RIGHT_N2 159
713 +#define EFFECTS_RIGHT_N3 161
714 +#define EFFECTS_RIGHT_N4 163
715 +#define EFFECTS_RIGHT_N5 165
716 +
717 +#define EFFECTS_RIGHT_D1 167
718 +#define EFFECTS_RIGHT_D2 169
719 +#define EFFECTS_RIGHT_D4 171
720 +#define EFFECTS_RIGHT_D5 173
721 +
722 +/* DAC De-Emphasis for Right Channel */
723 +
724 +#define DEEMPH_RIGHT_N0 175
725 +#define DEEMPH_RIGHT_N1 177
726 +#define DEEMPH_RIGHT_D1 179
727 +
728 +/* DAC 3D Attenuation */
729 +
730 +#define EFFECTS_3DATTEN 181
731 +
732 +/* ADC High-Pass Filter for Left Channel */
733 +
734 +#define HIGHPASS_LEFT_NO 193
735 +#define HIGHPASS_LEFT_N1 195
736 +#define HIGHPASS_LEFT_D1 197
737 +
738 +/* ADC High-Pass Filter for Right Channel */
739 +
740 +#define HIGHPASS_RIGHT_NO 199
741 +#define HIGHPASS_RIGHT_N1 201
742 +#define HIGHPASS_RIGHT_D1 203
743 +
744  /* Page select register bits */
745  #define PAGE0_SELECT           0
746  #define PAGE1_SELECT           1
747 @@ -186,6 +247,11 @@
748  /* Default input volume */
749  #define DEFAULT_GAIN    0x20
750  
751 +/* Filter bits */
752 +#define EFFECTS_3D_ON          0x04
753 +#define EFFECTS_ON             0x0a
754 +#define DEEMPH_ON              0x05
755 +
756  /* GPIO API */
757  enum {
758         AIC3X_GPIO1_FUNC_DISABLED               = 0,
759 @@ -222,6 +288,19 @@ enum {
760         AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ       = 15
761  };
762  
763 +/* Data for reading/writing to the IIR Filter hwdep */
764 +struct aic3x_iir_coeffs {
765 +       short N0, N1, N2, D1, D2;
766 +       short N3, N4, N5, D4, D5;
767 +};
768 +
769 +int aic3x_deemph_set_coeffs(struct snd_soc_codec *codec, int N0, int N1,
770 +                           int D1);
771 +int aic3x_deemph_set_state(struct snd_soc_codec *codec, int state);
772 +int aic3x_dacfilter_set_coeffs(struct snd_soc_codec *codec,
773 +                              struct aic3x_iir_coeffs *coeffs);
774 +int aic3x_dacfilter_set_state(struct snd_soc_codec *codec, int state);
775 +
776  void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state);
777  int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);
778  int aic3x_headset_detected(struct snd_soc_codec *codec);
779 diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
780 index 19cb292..eefda0d 100644
781 --- a/sound/soc/omap/rx51.c
782 +++ b/sound/soc/omap/rx51.c
783 @@ -327,9 +327,17 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
784                           struct snd_kcontrol *k, int event)
785  {
786         if (SND_SOC_DAPM_EVENT_ON(event))
787 +       {
788 +               aic3x_deemph_set_state(w->codec, 1);
789                 gpio_set_value(RX51_SPEAKER_AMP_TWL_GPIO, 1);
790 +               printk("MNZ: Speaker Amp on!\n");
791 +       }
792         else
793 +       {
794 +               aic3x_deemph_set_state(w->codec, 0);
795                 gpio_set_value(RX51_SPEAKER_AMP_TWL_GPIO, 0);
796 +               printk("MNZ: Speaker Amp off!\n");
797 +       }
798  
799         return 0;
800  }
801 @@ -792,6 +800,7 @@ static int rx51_aic34_init(struct snd_soc_codec *codec)
802  {
803         int i, err;
804  
805 +       printk("MNZ: BEGIN rx51_aic3x_init\n");
806         /* set up NC codec pins */
807         snd_soc_dapm_nc_pin(codec, "MIC3L");
808         snd_soc_dapm_nc_pin(codec, "MIC3R");
809 @@ -822,6 +831,11 @@ static int rx51_aic34_init(struct snd_soc_codec *codec)
810  
811         snd_soc_dapm_sync(codec);
812  
813 +       /* Default De-emphasis filter coefficients to use as a highpass for
814 +        * cheap speaker protection */
815 +       aic3x_deemph_set_coeffs(codec, 32276, -32276, 31785);
816 +
817 +       printk("MNZ: END rx51_aic3x_init\n");
818         return 0;
819  }
820  
821 -- 
822 1.7.3.4
823