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
6 Patchset combined and cleaned up by Luke-Jr <luke-jr+git@utopios.org>
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
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,
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.
22 TODO: Change filter coeffs if sample rate is changed. Right now it's fit
23 for 48kHz (and possibly fine with 44.1kHz).
25 Filter designed using scilab:
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);
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
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
43 diff --git a/Documentation/sound/alsa/soc/tlv320aic3x.txt b/Documentation/sound/alsa/soc/tlv320aic3x.txt
45 index 0000000..c0cc4cf
47 +++ b/Documentation/sound/alsa/soc/tlv320aic3x.txt
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
53 + http://www.ti.com/lit/gpn/tlv320aic34
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.
58 +There's an alsa control, "3D Control - Depth" for depth simulation.
59 +The rest of the controls are for the IIR filter:
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
70 +Note: Setting a filter's coeffs automatically turns it off, it needs to
71 +be turned on explicitly.
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.
77 +The IIR Filter consists of 2 cascaded biquads. The formula is:
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) |
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
89 +-------------------------------EXAMPLE----------------------------------
92 +#include <alsa/asoundlib.h>
93 +#include <alsa/hwdep.h>
97 + int16_t N0, N1, N2, D1, D2;
98 + int16_t N3, N4, N5, D4, D5;
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
107 + snd_hwdep_t *hwdep;
110 + ret = snd_hwdep_open(&hwdep, "hw:0,0", SND_HWDEP_OPEN_DUPLEX);
111 + printf("open: %i\n", ret);
114 + ret = snd_hwdep_write(hwdep, (void*)&coeffs, sizeof(coeffs));
115 + printf("write: %i\n", ret);
117 + /* Set state to 2, which is 'Custom'. This writes coeffs to hardware
118 + * and enables filter */
120 + ret = snd_hwdep_ioctl(hwdep, 1, &arg);
121 + printf("ioctl: %i\n", ret);
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
131 config SND_SOC_TLV320AIC3X
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
142 #include <sound/soc-dapm.h>
143 #include <sound/initval.h>
144 #include <sound/tlv.h>
145 +#include <sound/hwdep.h>
147 #include "tlv320aic3x.h"
150 static int hp_dac_lim = 9;
151 module_param(hp_dac_lim, int, 0);
153 +struct aic3x_dacfilter_t {
154 + struct aic3x_iir_coeffs coeffs;
156 +} aic3x_dacfilter = {
158 + .N0 = 27619, .N1 = -27034, .N2 = 26461, .D1 = 32131, .D2 = -31506,
159 + .N3 = 27619, .N4 = -27034, .N5 = 26461, .D4 = 32131, .D5 = -31506,
164 /* codec private data */
169 + struct snd_hwdep *hwdep;
170 + struct aic3x_dacfilter_t dacfilter;
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 */
209 @@ -108,6 +148,17 @@ static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec,
213 +static inline int aic3x_read_coeff_reg_cache(struct snd_soc_codec *codec,
214 + unsigned int msbreg)
217 + val = aic3x_read_reg_cache(codec, msbreg) << 8;
218 + val |= aic3x_read_reg_cache(codec, msbreg+1);
225 * write aic3x register cache
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,
232 + static char curpage = -1;
233 + u8 data[2], page = 0;
239 + if (reg && curpage != page) {
242 + if (codec->hw_write(codec->control_data, data, 2) != 2)
250 * D15..D8 aic3x register offset
251 * D7...D0 register data
253 - data[0] = reg & 0xff;
254 - data[1] = value & 0xff;
258 - aic3x_write_reg_cache(codec, data[0], data[1]);
259 if (codec->hw_write(codec->control_data, data, 2) == 2)
261 + aic3x_write_reg_cache(codec, data[0] + page * 128, data[1]);
263 + curpage = value; /* for reg = 0, ie, page setting */
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)
279 + outp = 65536 + value;
283 + aic3x_write(codec, msbreg, outp >> 8);
284 + aic3x_write(codec, msbreg + 1, outp);
291 * read from the aic3x register space
293 @@ -160,23 +250,37 @@ static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg,
298 - * Reset for getting low power consumption after bypass paths
300 -static void aic3x_reset(struct snd_soc_codec *codec)
301 +static int aic3x_sync_hw(struct snd_soc_codec *codec)
303 u8 *cache = codec->reg_cache;
307 - aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
308 + aic3x_write(codec, AIC3X_PAGE_SELECT, 1);
309 + for (i = 1; i < AIC3X_PAGE1REGNUM; ++i) {
311 + data[1] = cache[i + 128];
312 + codec->hw_write(codec->control_data, data, 2);
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) {
321 codec->hw_write(codec->control_data, data, 2);
328 + * Reset for getting low power consumption after bypass paths
330 +static void aic3x_reset(struct snd_soc_codec *codec)
332 + aic3x_write(codec, AIC3X_RESET, SOFT_RESET);
333 + aic3x_sync_hw(codec);
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" };
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
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),
362 @@ -331,6 +439,211 @@ static int tlv320alc3x_info_volsw(struct snd_kcontrol *kcontrol,
366 +/* DAC and De-emphasis Filter Functions */
367 +int aic3x_deemph_set_coeffs(struct snd_soc_codec *codec, int N0, int N1,
370 + printk("MNZ: setting deemph coeffs\n");
371 + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, DEEMPH_ON, 0);
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);
382 +EXPORT_SYMBOL_GPL(aic3x_deemph_set_coeffs);
384 +int aic3x_deemph_set_state(struct snd_soc_codec *codec, int state)
386 + printk("MNZ: Setting De-Emph filter: %i\n", state);
391 + return snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, DEEMPH_ON,
394 +EXPORT_SYMBOL_GPL(aic3x_deemph_set_state);
396 +static int aic3x_dacfilter_write_coeffs(struct snd_soc_codec *codec,
397 + struct aic3x_iir_coeffs *coeffs)
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);
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);
425 +int aic3x_dacfilter_set_coeffs(struct snd_soc_codec *codec,
426 + struct aic3x_iir_coeffs *coeffs)
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);
435 +EXPORT_SYMBOL_GPL(aic3x_dacfilter_set_coeffs);
437 +int aic3x_dacfilter_set_state(struct snd_soc_codec *codec, int state)
439 + printk("MNZ: dacfilter_set_state to %i\n", state);
440 + struct aic3x_priv *aic3x = codec->private_data;
442 + if (aic3x->dacfilter.state == state)
445 + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL, EFFECTS_ON, 0);
449 + else if(state == 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);
456 + snd_soc_update_bits(codec, AIC3X_CODEC_DFILT_CTRL,
457 + EFFECTS_ON, EFFECTS_ON);
463 + aic3x->dacfilter.state = state;
467 +EXPORT_SYMBOL_GPL(aic3x_dacfilter_set_state);
469 +/* DAC Filter hwdep device callbacks */
471 +static int snd_hwdep_dacfilter_open_aic3x(struct snd_hwdep *hw,
477 +static int snd_hwdep_dacfilter_ioctl_aic3x(struct snd_hwdep *hw,
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
486 + struct snd_soc_codec *codec = hw->private_data;
489 + printk("MNZ: IOCTL: cmd = %i, arg = %i\n", cmd, *((int*)arg));
490 + return aic3x_dacfilter_set_state(codec, *((int*)arg));
493 +static long snd_hwdep_dacfilter_read_aic3x(struct snd_hwdep *hw,
494 + char __user *buf, long count,
497 + struct aic3x_priv *aic3x =
498 + ((struct snd_soc_codec*)hw->private_data)->private_data;
499 + if (count != sizeof(struct aic3x_iir_coeffs))
501 + memcpy((void*)buf, (void*)&aic3x->dacfilter.coeffs, count);
505 +static long snd_hwdep_dacfilter_write_aic3x(struct snd_hwdep *hw,
506 + const char __user *buf,
507 + long count, loff_t *offset)
509 + struct snd_soc_codec *codec = hw->private_data;
510 + if (count != sizeof(struct aic3x_iir_coeffs))
513 + ((struct aic3x_priv*)codec->private_data)->dacfilter.state = 2;
515 + return aic3x_dacfilter_set_coeffs(codec, (struct aic3x_iir_coeffs*)buf);
518 +/* DAC filter and 3D depth ALSA controls callbacks */
520 +static int snd_soc_get_dacfilter_aic3x(struct snd_kcontrol *kcontrol,
521 + struct snd_ctl_elem_value *ucontrol)
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;
529 +static int snd_soc_put_dacfilter_aic3x(struct snd_kcontrol *kcontrol,
530 + struct snd_ctl_elem_value *ucontrol)
532 + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
533 + if (ucontrol->value.enumerated.item[0] > 2)
535 + aic3x_dacfilter_set_state(codec, ucontrol->value.enumerated.item[0]);
539 +static int snd_soc_get_3d_attenuation_aic3x(struct snd_kcontrol *kcontrol,
540 + struct snd_ctl_elem_value *ucontrol)
542 + int val = aic3x_read_coeff_reg_cache(snd_kcontrol_chip(kcontrol),
544 + val = ((val * 100) / 65530) + 50;
545 + ucontrol->value.integer.value[0] = val;
549 +static int snd_soc_put_3d_attenuation_aic3x(struct snd_kcontrol *kcontrol,
550 + struct snd_ctl_elem_value *ucontrol)
552 + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
553 + int val = ucontrol->value.integer.value[0];
554 + if (val > 100 || val < 0)
558 + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, EFFECTS_3D_ON,
560 + val = ((val - 50) * 65535) / 100;
562 + snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA, EFFECTS_3D_ON, 0);
566 + aic3x_write_coeff(codec, EFFECTS_3DATTEN, val);
571 static const struct snd_kcontrol_new aic3x_snd_controls[] = {
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),
577 SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
579 + SOC_ENUM_EXT("Hardware EQ", aic3x_enum[DAC_FILT_ENUM],
580 + snd_soc_get_dacfilter_aic3x, snd_soc_put_dacfilter_aic3x),
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),
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)
591 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
592 - struct snd_soc_codec *codec = socdev->codec;
595 - u8 *cache = codec->reg_cache;
597 - /* Sync reg_cache with the hardware */
598 - for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) {
600 - data[1] = cache[i];
601 - codec->hw_write(codec->control_data, data, 2);
604 - aic3x_set_bias_level(codec, codec->suspend_bias_level);
606 + aic3x_sync_hw(socdev->codec);
607 + aic3x_set_bias_level(socdev->codec,
608 + socdev->codec->suspend_bias_level);
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)
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";
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)
630 aic3x_add_controls(codec);
631 aic3x_add_widgets(codec);
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;
643 ret = snd_soc_register_card(socdev);
646 printk(KERN_ERR "aic3x: failed to register card\n");
650 + /* Set some defaults for coefficients */
651 + aic3x_write_coeff(codec, EFFECTS_3DATTEN, -32768);
652 + printk("MNZ: END aic3x_init\n");
657 @@ -1464,6 +1793,8 @@ static int aic3x_probe(struct platform_device *pdev)
660 aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
661 + memcpy(&aic3x->dacfilter, &aic3x_dacfilter, sizeof(aic3x_dacfilter));
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
673 /* AIC3X register space */
674 -#define AIC3X_CACHEREGNUM 103
675 +#define AIC3X_CACHEREGNUM 205
676 +#define AIC3X_PAGE0REGNUM 103
677 +#define AIC3X_PAGE1REGNUM 77
679 +#define AIC3X_COEFF_CACHE_SIZE 52
680 +#define COEFF_OFFSET(msbreg) ((msbreg + 1) / 2)
682 /* Page select register */
683 #define AIC3X_PAGE_SELECT 0
685 /* Clock generation control register */
686 #define AIC3X_CLKGEN_CTRL_REG 102
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
697 +#define EFFECTS_LEFT_D1 141
698 +#define EFFECTS_LEFT_D2 143
699 +#define EFFECTS_LEFT_D4 145
700 +#define EFFECTS_LEFT_D5 147
702 +/* DAC De-Emphasis for Left Channel */
704 +#define DEEMPH_LEFT_N0 149
705 +#define DEEMPH_LEFT_N1 151
706 +#define DEEMPH_LEFT_D1 153
708 +/* DAC Audio Effects for Right Channel */
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
717 +#define EFFECTS_RIGHT_D1 167
718 +#define EFFECTS_RIGHT_D2 169
719 +#define EFFECTS_RIGHT_D4 171
720 +#define EFFECTS_RIGHT_D5 173
722 +/* DAC De-Emphasis for Right Channel */
724 +#define DEEMPH_RIGHT_N0 175
725 +#define DEEMPH_RIGHT_N1 177
726 +#define DEEMPH_RIGHT_D1 179
728 +/* DAC 3D Attenuation */
730 +#define EFFECTS_3DATTEN 181
732 +/* ADC High-Pass Filter for Left Channel */
734 +#define HIGHPASS_LEFT_NO 193
735 +#define HIGHPASS_LEFT_N1 195
736 +#define HIGHPASS_LEFT_D1 197
738 +/* ADC High-Pass Filter for Right Channel */
740 +#define HIGHPASS_RIGHT_NO 199
741 +#define HIGHPASS_RIGHT_N1 201
742 +#define HIGHPASS_RIGHT_D1 203
744 /* Page select register bits */
745 #define PAGE0_SELECT 0
746 #define PAGE1_SELECT 1
748 /* Default input volume */
749 #define DEFAULT_GAIN 0x20
752 +#define EFFECTS_3D_ON 0x04
753 +#define EFFECTS_ON 0x0a
754 +#define DEEMPH_ON 0x05
758 AIC3X_GPIO1_FUNC_DISABLED = 0,
759 @@ -222,6 +288,19 @@ enum {
760 AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15
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;
769 +int aic3x_deemph_set_coeffs(struct snd_soc_codec *codec, int N0, int N1,
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);
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)
786 if (SND_SOC_DAPM_EVENT_ON(event))
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");
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");
801 @@ -792,6 +800,7 @@ static int rx51_aic34_init(struct snd_soc_codec *codec)
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)
811 snd_soc_dapm_sync(codec);
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);
817 + printk("MNZ: END rx51_aic3x_init\n");