diff options
Diffstat (limited to 'sound/oss/dmasound/tas3004.c')
-rw-r--r-- | sound/oss/dmasound/tas3004.c | 1140 |
1 files changed, 1140 insertions, 0 deletions
diff --git a/sound/oss/dmasound/tas3004.c b/sound/oss/dmasound/tas3004.c new file mode 100644 index 000000000000..82eaaca2db9a --- /dev/null +++ b/sound/oss/dmasound/tas3004.c | |||
@@ -0,0 +1,1140 @@ | |||
1 | /* | ||
2 | * Driver for the i2c/i2s based TA3004 sound chip used | ||
3 | * on some Apple hardware. Also known as "snapper". | ||
4 | * | ||
5 | * Tobias Sargeant <tobias.sargeant@bigpond.com> | ||
6 | * Based upon tas3001c.c by Christopher C. Chimelis <chris@debian.org>: | ||
7 | * | ||
8 | * Input support by Renzo Davoli <renzo@cs.unibo.it> | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/proc_fs.h> | ||
15 | #include <linux/ioport.h> | ||
16 | #include <linux/sysctl.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/soundcard.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/workqueue.h> | ||
23 | |||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/errno.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/prom.h> | ||
28 | |||
29 | #include "dmasound.h" | ||
30 | #include "tas_common.h" | ||
31 | #include "tas3004.h" | ||
32 | |||
33 | #include "tas_ioctl.h" | ||
34 | |||
35 | /* #define DEBUG_DRCE */ | ||
36 | |||
37 | #define TAS3004_BIQUAD_FILTER_COUNT 7 | ||
38 | #define TAS3004_BIQUAD_CHANNEL_COUNT 2 | ||
39 | |||
40 | #define VOL_DEFAULT (100 * 4 / 5) | ||
41 | #define INPUT_DEFAULT (100 * 4 / 5) | ||
42 | #define BASS_DEFAULT (100 / 2) | ||
43 | #define TREBLE_DEFAULT (100 / 2) | ||
44 | |||
45 | struct tas3004_data_t { | ||
46 | struct tas_data_t super; | ||
47 | int device_id; | ||
48 | int output_id; | ||
49 | int speaker_id; | ||
50 | struct tas_drce_t drce_state; | ||
51 | }; | ||
52 | |||
53 | #define MAKE_TIME(sec,usec) (((sec)<<12) + (50000+(usec/10)*(1<<12))/100000) | ||
54 | |||
55 | #define MAKE_RATIO(i,f) (((i)<<8) + ((500+(f)*(1<<8))/1000)) | ||
56 | |||
57 | |||
58 | static const union tas_biquad_t tas3004_eq_unity = { | ||
59 | .buf = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }, | ||
60 | }; | ||
61 | |||
62 | |||
63 | static const struct tas_drce_t tas3004_drce_min = { | ||
64 | .enable = 1, | ||
65 | .above = { .val = MAKE_RATIO(16,0), .expand = 0 }, | ||
66 | .below = { .val = MAKE_RATIO(2,0), .expand = 0 }, | ||
67 | .threshold = -0x59a0, | ||
68 | .energy = MAKE_TIME(0, 1700), | ||
69 | .attack = MAKE_TIME(0, 1700), | ||
70 | .decay = MAKE_TIME(0, 1700), | ||
71 | }; | ||
72 | |||
73 | |||
74 | static const struct tas_drce_t tas3004_drce_max = { | ||
75 | .enable = 1, | ||
76 | .above = { .val = MAKE_RATIO(1,500), .expand = 1 }, | ||
77 | .below = { .val = MAKE_RATIO(2,0), .expand = 1 }, | ||
78 | .threshold = -0x0, | ||
79 | .energy = MAKE_TIME(2,400000), | ||
80 | .attack = MAKE_TIME(2,400000), | ||
81 | .decay = MAKE_TIME(2,400000), | ||
82 | }; | ||
83 | |||
84 | |||
85 | static const unsigned short time_constants[]={ | ||
86 | MAKE_TIME(0, 1700), | ||
87 | MAKE_TIME(0, 3500), | ||
88 | MAKE_TIME(0, 6700), | ||
89 | MAKE_TIME(0, 13000), | ||
90 | MAKE_TIME(0, 26000), | ||
91 | MAKE_TIME(0, 53000), | ||
92 | MAKE_TIME(0,106000), | ||
93 | MAKE_TIME(0,212000), | ||
94 | MAKE_TIME(0,425000), | ||
95 | MAKE_TIME(0,850000), | ||
96 | MAKE_TIME(1,700000), | ||
97 | MAKE_TIME(2,400000), | ||
98 | }; | ||
99 | |||
100 | static const unsigned short above_threshold_compression_ratio[]={ | ||
101 | MAKE_RATIO( 1, 70), | ||
102 | MAKE_RATIO( 1,140), | ||
103 | MAKE_RATIO( 1,230), | ||
104 | MAKE_RATIO( 1,330), | ||
105 | MAKE_RATIO( 1,450), | ||
106 | MAKE_RATIO( 1,600), | ||
107 | MAKE_RATIO( 1,780), | ||
108 | MAKE_RATIO( 2, 0), | ||
109 | MAKE_RATIO( 2,290), | ||
110 | MAKE_RATIO( 2,670), | ||
111 | MAKE_RATIO( 3,200), | ||
112 | MAKE_RATIO( 4, 0), | ||
113 | MAKE_RATIO( 5,330), | ||
114 | MAKE_RATIO( 8, 0), | ||
115 | MAKE_RATIO(16, 0), | ||
116 | }; | ||
117 | |||
118 | static const unsigned short above_threshold_expansion_ratio[]={ | ||
119 | MAKE_RATIO(1, 60), | ||
120 | MAKE_RATIO(1,130), | ||
121 | MAKE_RATIO(1,190), | ||
122 | MAKE_RATIO(1,250), | ||
123 | MAKE_RATIO(1,310), | ||
124 | MAKE_RATIO(1,380), | ||
125 | MAKE_RATIO(1,440), | ||
126 | MAKE_RATIO(1,500) | ||
127 | }; | ||
128 | |||
129 | static const unsigned short below_threshold_compression_ratio[]={ | ||
130 | MAKE_RATIO(1, 70), | ||
131 | MAKE_RATIO(1,140), | ||
132 | MAKE_RATIO(1,230), | ||
133 | MAKE_RATIO(1,330), | ||
134 | MAKE_RATIO(1,450), | ||
135 | MAKE_RATIO(1,600), | ||
136 | MAKE_RATIO(1,780), | ||
137 | MAKE_RATIO(2, 0) | ||
138 | }; | ||
139 | |||
140 | static const unsigned short below_threshold_expansion_ratio[]={ | ||
141 | MAKE_RATIO(1, 60), | ||
142 | MAKE_RATIO(1,130), | ||
143 | MAKE_RATIO(1,190), | ||
144 | MAKE_RATIO(1,250), | ||
145 | MAKE_RATIO(1,310), | ||
146 | MAKE_RATIO(1,380), | ||
147 | MAKE_RATIO(1,440), | ||
148 | MAKE_RATIO(1,500), | ||
149 | MAKE_RATIO(1,560), | ||
150 | MAKE_RATIO(1,630), | ||
151 | MAKE_RATIO(1,690), | ||
152 | MAKE_RATIO(1,750), | ||
153 | MAKE_RATIO(1,810), | ||
154 | MAKE_RATIO(1,880), | ||
155 | MAKE_RATIO(1,940), | ||
156 | MAKE_RATIO(2, 0) | ||
157 | }; | ||
158 | |||
159 | static inline int | ||
160 | search( unsigned short val, | ||
161 | const unsigned short *arr, | ||
162 | const int arrsize) { | ||
163 | /* | ||
164 | * This could be a binary search, but for small tables, | ||
165 | * a linear search is likely to be faster | ||
166 | */ | ||
167 | |||
168 | int i; | ||
169 | |||
170 | for (i=0; i < arrsize; i++) | ||
171 | if (arr[i] >= val) | ||
172 | goto _1; | ||
173 | return arrsize-1; | ||
174 | _1: | ||
175 | if (i == 0) | ||
176 | return 0; | ||
177 | return (arr[i]-val < val-arr[i-1]) ? i : i-1; | ||
178 | } | ||
179 | |||
180 | #define SEARCH(a, b) search(a, b, ARRAY_SIZE(b)) | ||
181 | |||
182 | static inline int | ||
183 | time_index(unsigned short time) | ||
184 | { | ||
185 | return SEARCH(time, time_constants); | ||
186 | } | ||
187 | |||
188 | |||
189 | static inline int | ||
190 | above_threshold_compression_index(unsigned short ratio) | ||
191 | { | ||
192 | return SEARCH(ratio, above_threshold_compression_ratio); | ||
193 | } | ||
194 | |||
195 | |||
196 | static inline int | ||
197 | above_threshold_expansion_index(unsigned short ratio) | ||
198 | { | ||
199 | return SEARCH(ratio, above_threshold_expansion_ratio); | ||
200 | } | ||
201 | |||
202 | |||
203 | static inline int | ||
204 | below_threshold_compression_index(unsigned short ratio) | ||
205 | { | ||
206 | return SEARCH(ratio, below_threshold_compression_ratio); | ||
207 | } | ||
208 | |||
209 | |||
210 | static inline int | ||
211 | below_threshold_expansion_index(unsigned short ratio) | ||
212 | { | ||
213 | return SEARCH(ratio, below_threshold_expansion_ratio); | ||
214 | } | ||
215 | |||
216 | static inline unsigned char db_to_regval(short db) { | ||
217 | int r=0; | ||
218 | |||
219 | r=(db+0x59a0) / 0x60; | ||
220 | |||
221 | if (r < 0x91) return 0x91; | ||
222 | if (r > 0xef) return 0xef; | ||
223 | return r; | ||
224 | } | ||
225 | |||
226 | static inline short quantize_db(short db) | ||
227 | { | ||
228 | return db_to_regval(db) * 0x60 - 0x59a0; | ||
229 | } | ||
230 | |||
231 | static inline int | ||
232 | register_width(enum tas3004_reg_t r) | ||
233 | { | ||
234 | switch(r) { | ||
235 | case TAS3004_REG_MCR: | ||
236 | case TAS3004_REG_TREBLE: | ||
237 | case TAS3004_REG_BASS: | ||
238 | case TAS3004_REG_ANALOG_CTRL: | ||
239 | case TAS3004_REG_TEST1: | ||
240 | case TAS3004_REG_TEST2: | ||
241 | case TAS3004_REG_MCR2: | ||
242 | return 1; | ||
243 | |||
244 | case TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN: | ||
245 | case TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN: | ||
246 | return 3; | ||
247 | |||
248 | case TAS3004_REG_DRC: | ||
249 | case TAS3004_REG_VOLUME: | ||
250 | return 6; | ||
251 | |||
252 | case TAS3004_REG_LEFT_MIXER: | ||
253 | case TAS3004_REG_RIGHT_MIXER: | ||
254 | return 9; | ||
255 | |||
256 | case TAS3004_REG_TEST: | ||
257 | return 10; | ||
258 | |||
259 | case TAS3004_REG_LEFT_BIQUAD0: | ||
260 | case TAS3004_REG_LEFT_BIQUAD1: | ||
261 | case TAS3004_REG_LEFT_BIQUAD2: | ||
262 | case TAS3004_REG_LEFT_BIQUAD3: | ||
263 | case TAS3004_REG_LEFT_BIQUAD4: | ||
264 | case TAS3004_REG_LEFT_BIQUAD5: | ||
265 | case TAS3004_REG_LEFT_BIQUAD6: | ||
266 | |||
267 | case TAS3004_REG_RIGHT_BIQUAD0: | ||
268 | case TAS3004_REG_RIGHT_BIQUAD1: | ||
269 | case TAS3004_REG_RIGHT_BIQUAD2: | ||
270 | case TAS3004_REG_RIGHT_BIQUAD3: | ||
271 | case TAS3004_REG_RIGHT_BIQUAD4: | ||
272 | case TAS3004_REG_RIGHT_BIQUAD5: | ||
273 | case TAS3004_REG_RIGHT_BIQUAD6: | ||
274 | |||
275 | case TAS3004_REG_LEFT_LOUD_BIQUAD: | ||
276 | case TAS3004_REG_RIGHT_LOUD_BIQUAD: | ||
277 | return 15; | ||
278 | |||
279 | default: | ||
280 | return 0; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | static int | ||
285 | tas3004_write_register( struct tas3004_data_t *self, | ||
286 | enum tas3004_reg_t reg_num, | ||
287 | char *data, | ||
288 | uint write_mode) | ||
289 | { | ||
290 | if (reg_num==TAS3004_REG_MCR || | ||
291 | reg_num==TAS3004_REG_BASS || | ||
292 | reg_num==TAS3004_REG_TREBLE || | ||
293 | reg_num==TAS3004_REG_ANALOG_CTRL) { | ||
294 | return tas_write_byte_register(&self->super, | ||
295 | (uint)reg_num, | ||
296 | *data, | ||
297 | write_mode); | ||
298 | } else { | ||
299 | return tas_write_register(&self->super, | ||
300 | (uint)reg_num, | ||
301 | register_width(reg_num), | ||
302 | data, | ||
303 | write_mode); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | static int | ||
308 | tas3004_sync_register( struct tas3004_data_t *self, | ||
309 | enum tas3004_reg_t reg_num) | ||
310 | { | ||
311 | if (reg_num==TAS3004_REG_MCR || | ||
312 | reg_num==TAS3004_REG_BASS || | ||
313 | reg_num==TAS3004_REG_TREBLE || | ||
314 | reg_num==TAS3004_REG_ANALOG_CTRL) { | ||
315 | return tas_sync_byte_register(&self->super, | ||
316 | (uint)reg_num, | ||
317 | register_width(reg_num)); | ||
318 | } else { | ||
319 | return tas_sync_register(&self->super, | ||
320 | (uint)reg_num, | ||
321 | register_width(reg_num)); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | static int | ||
326 | tas3004_read_register( struct tas3004_data_t *self, | ||
327 | enum tas3004_reg_t reg_num, | ||
328 | char *data, | ||
329 | uint write_mode) | ||
330 | { | ||
331 | return tas_read_register(&self->super, | ||
332 | (uint)reg_num, | ||
333 | register_width(reg_num), | ||
334 | data); | ||
335 | } | ||
336 | |||
337 | static inline int | ||
338 | tas3004_fast_load(struct tas3004_data_t *self, int fast) | ||
339 | { | ||
340 | if (fast) | ||
341 | self->super.shadow[TAS3004_REG_MCR][0] |= 0x80; | ||
342 | else | ||
343 | self->super.shadow[TAS3004_REG_MCR][0] &= 0x7f; | ||
344 | return tas3004_sync_register(self,TAS3004_REG_MCR); | ||
345 | } | ||
346 | |||
347 | static uint | ||
348 | tas3004_supported_mixers(struct tas3004_data_t *self) | ||
349 | { | ||
350 | return SOUND_MASK_VOLUME | | ||
351 | SOUND_MASK_PCM | | ||
352 | SOUND_MASK_ALTPCM | | ||
353 | SOUND_MASK_IMIX | | ||
354 | SOUND_MASK_TREBLE | | ||
355 | SOUND_MASK_BASS | | ||
356 | SOUND_MASK_MIC | | ||
357 | SOUND_MASK_LINE; | ||
358 | } | ||
359 | |||
360 | static int | ||
361 | tas3004_mixer_is_stereo(struct tas3004_data_t *self, int mixer) | ||
362 | { | ||
363 | switch(mixer) { | ||
364 | case SOUND_MIXER_VOLUME: | ||
365 | case SOUND_MIXER_PCM: | ||
366 | case SOUND_MIXER_ALTPCM: | ||
367 | case SOUND_MIXER_IMIX: | ||
368 | return 1; | ||
369 | default: | ||
370 | return 0; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | static uint | ||
375 | tas3004_stereo_mixers(struct tas3004_data_t *self) | ||
376 | { | ||
377 | uint r = tas3004_supported_mixers(self); | ||
378 | uint i; | ||
379 | |||
380 | for (i=1; i<SOUND_MIXER_NRDEVICES; i++) | ||
381 | if (r&(1<<i) && !tas3004_mixer_is_stereo(self,i)) | ||
382 | r &= ~(1<<i); | ||
383 | return r; | ||
384 | } | ||
385 | |||
386 | static int | ||
387 | tas3004_get_mixer_level(struct tas3004_data_t *self, int mixer, uint *level) | ||
388 | { | ||
389 | if (!self) | ||
390 | return -1; | ||
391 | |||
392 | *level = self->super.mixer[mixer]; | ||
393 | |||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | static int | ||
398 | tas3004_set_mixer_level(struct tas3004_data_t *self, int mixer, uint level) | ||
399 | { | ||
400 | int rc; | ||
401 | tas_shadow_t *shadow; | ||
402 | uint temp; | ||
403 | uint offset=0; | ||
404 | |||
405 | if (!self) | ||
406 | return -1; | ||
407 | |||
408 | shadow = self->super.shadow; | ||
409 | |||
410 | if (!tas3004_mixer_is_stereo(self,mixer)) | ||
411 | level = tas_mono_to_stereo(level); | ||
412 | switch(mixer) { | ||
413 | case SOUND_MIXER_VOLUME: | ||
414 | temp = tas3004_gain.master[level&0xff]; | ||
415 | SET_4_20(shadow[TAS3004_REG_VOLUME], 0, temp); | ||
416 | temp = tas3004_gain.master[(level>>8)&0xff]; | ||
417 | SET_4_20(shadow[TAS3004_REG_VOLUME], 3, temp); | ||
418 | rc = tas3004_sync_register(self,TAS3004_REG_VOLUME); | ||
419 | break; | ||
420 | case SOUND_MIXER_IMIX: | ||
421 | offset += 3; | ||
422 | case SOUND_MIXER_ALTPCM: | ||
423 | offset += 3; | ||
424 | case SOUND_MIXER_PCM: | ||
425 | /* | ||
426 | * Don't load these in fast mode. The documentation | ||
427 | * says it can be done in either mode, but testing it | ||
428 | * shows that fast mode produces ugly clicking. | ||
429 | */ | ||
430 | /* tas3004_fast_load(self,1); */ | ||
431 | temp = tas3004_gain.mixer[level&0xff]; | ||
432 | SET_4_20(shadow[TAS3004_REG_LEFT_MIXER], offset, temp); | ||
433 | temp = tas3004_gain.mixer[(level>>8)&0xff]; | ||
434 | SET_4_20(shadow[TAS3004_REG_RIGHT_MIXER], offset, temp); | ||
435 | rc = tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER); | ||
436 | if (rc == 0) | ||
437 | rc=tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER); | ||
438 | /* tas3004_fast_load(self,0); */ | ||
439 | break; | ||
440 | case SOUND_MIXER_TREBLE: | ||
441 | temp = tas3004_gain.treble[level&0xff]; | ||
442 | shadow[TAS3004_REG_TREBLE][0]=temp&0xff; | ||
443 | rc = tas3004_sync_register(self,TAS3004_REG_TREBLE); | ||
444 | break; | ||
445 | case SOUND_MIXER_BASS: | ||
446 | temp = tas3004_gain.bass[level&0xff]; | ||
447 | shadow[TAS3004_REG_BASS][0]=temp&0xff; | ||
448 | rc = tas3004_sync_register(self,TAS3004_REG_BASS); | ||
449 | break; | ||
450 | case SOUND_MIXER_MIC: | ||
451 | if ((level&0xff)>0) { | ||
452 | software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff); | ||
453 | if (self->super.mixer[mixer] == 0) { | ||
454 | self->super.mixer[SOUND_MIXER_LINE] = 0; | ||
455 | shadow[TAS3004_REG_ANALOG_CTRL][0]=0xc2; | ||
456 | rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL); | ||
457 | } else rc=0; | ||
458 | } else { | ||
459 | self->super.mixer[SOUND_MIXER_LINE] = SW_INPUT_VOLUME_DEFAULT; | ||
460 | software_input_volume = SW_INPUT_VOLUME_SCALE * | ||
461 | (self->super.mixer[SOUND_MIXER_LINE]&0xff); | ||
462 | shadow[TAS3004_REG_ANALOG_CTRL][0]=0x00; | ||
463 | rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL); | ||
464 | } | ||
465 | break; | ||
466 | case SOUND_MIXER_LINE: | ||
467 | if (self->super.mixer[SOUND_MIXER_MIC] == 0) { | ||
468 | software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff); | ||
469 | rc=0; | ||
470 | } | ||
471 | break; | ||
472 | default: | ||
473 | rc = -1; | ||
474 | break; | ||
475 | } | ||
476 | if (rc < 0) | ||
477 | return rc; | ||
478 | self->super.mixer[mixer] = level; | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static int | ||
484 | tas3004_leave_sleep(struct tas3004_data_t *self) | ||
485 | { | ||
486 | unsigned char mcr = (1<<6)+(2<<4)+(2<<2); | ||
487 | |||
488 | if (!self) | ||
489 | return -1; | ||
490 | |||
491 | /* Make sure something answers on the i2c bus */ | ||
492 | if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr, | ||
493 | WRITE_NORMAL | FORCE_WRITE) < 0) | ||
494 | return -1; | ||
495 | |||
496 | tas3004_fast_load(self, 1); | ||
497 | |||
498 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0); | ||
499 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1); | ||
500 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2); | ||
501 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3); | ||
502 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4); | ||
503 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5); | ||
504 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6); | ||
505 | |||
506 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0); | ||
507 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1); | ||
508 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2); | ||
509 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3); | ||
510 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4); | ||
511 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5); | ||
512 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6); | ||
513 | |||
514 | tas3004_fast_load(self, 0); | ||
515 | |||
516 | (void)tas3004_sync_register(self,TAS3004_REG_VOLUME); | ||
517 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER); | ||
518 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER); | ||
519 | (void)tas3004_sync_register(self,TAS3004_REG_TREBLE); | ||
520 | (void)tas3004_sync_register(self,TAS3004_REG_BASS); | ||
521 | (void)tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL); | ||
522 | |||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int | ||
527 | tas3004_enter_sleep(struct tas3004_data_t *self) | ||
528 | { | ||
529 | if (!self) | ||
530 | return -1; | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static int | ||
535 | tas3004_sync_biquad( struct tas3004_data_t *self, | ||
536 | u_int channel, | ||
537 | u_int filter) | ||
538 | { | ||
539 | enum tas3004_reg_t reg; | ||
540 | |||
541 | if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT || | ||
542 | filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL; | ||
543 | |||
544 | reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter; | ||
545 | |||
546 | return tas3004_sync_register(self,reg); | ||
547 | } | ||
548 | |||
549 | static int | ||
550 | tas3004_write_biquad_shadow( struct tas3004_data_t *self, | ||
551 | u_int channel, | ||
552 | u_int filter, | ||
553 | const union tas_biquad_t *biquad) | ||
554 | { | ||
555 | tas_shadow_t *shadow=self->super.shadow; | ||
556 | enum tas3004_reg_t reg; | ||
557 | |||
558 | if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT || | ||
559 | filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL; | ||
560 | |||
561 | reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter; | ||
562 | |||
563 | SET_4_20(shadow[reg], 0,biquad->coeff.b0); | ||
564 | SET_4_20(shadow[reg], 3,biquad->coeff.b1); | ||
565 | SET_4_20(shadow[reg], 6,biquad->coeff.b2); | ||
566 | SET_4_20(shadow[reg], 9,biquad->coeff.a1); | ||
567 | SET_4_20(shadow[reg],12,biquad->coeff.a2); | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | static int | ||
573 | tas3004_write_biquad( struct tas3004_data_t *self, | ||
574 | u_int channel, | ||
575 | u_int filter, | ||
576 | const union tas_biquad_t *biquad) | ||
577 | { | ||
578 | int rc; | ||
579 | |||
580 | rc=tas3004_write_biquad_shadow(self, channel, filter, biquad); | ||
581 | if (rc < 0) return rc; | ||
582 | |||
583 | return tas3004_sync_biquad(self, channel, filter); | ||
584 | } | ||
585 | |||
586 | static int | ||
587 | tas3004_write_biquad_list( struct tas3004_data_t *self, | ||
588 | u_int filter_count, | ||
589 | u_int flags, | ||
590 | struct tas_biquad_ctrl_t *biquads) | ||
591 | { | ||
592 | int i; | ||
593 | int rc; | ||
594 | |||
595 | if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1); | ||
596 | |||
597 | for (i=0; i<filter_count; i++) { | ||
598 | rc=tas3004_write_biquad(self, | ||
599 | biquads[i].channel, | ||
600 | biquads[i].filter, | ||
601 | &biquads[i].data); | ||
602 | if (rc < 0) break; | ||
603 | } | ||
604 | |||
605 | if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,0); | ||
606 | |||
607 | return rc; | ||
608 | } | ||
609 | |||
610 | static int | ||
611 | tas3004_read_biquad( struct tas3004_data_t *self, | ||
612 | u_int channel, | ||
613 | u_int filter, | ||
614 | union tas_biquad_t *biquad) | ||
615 | { | ||
616 | tas_shadow_t *shadow=self->super.shadow; | ||
617 | enum tas3004_reg_t reg; | ||
618 | |||
619 | if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT || | ||
620 | filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL; | ||
621 | |||
622 | reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter; | ||
623 | |||
624 | biquad->coeff.b0=GET_4_20(shadow[reg], 0); | ||
625 | biquad->coeff.b1=GET_4_20(shadow[reg], 3); | ||
626 | biquad->coeff.b2=GET_4_20(shadow[reg], 6); | ||
627 | biquad->coeff.a1=GET_4_20(shadow[reg], 9); | ||
628 | biquad->coeff.a2=GET_4_20(shadow[reg],12); | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | static int | ||
634 | tas3004_eq_rw( struct tas3004_data_t *self, | ||
635 | u_int cmd, | ||
636 | u_long arg) | ||
637 | { | ||
638 | void __user *argp = (void __user *)arg; | ||
639 | int rc; | ||
640 | struct tas_biquad_ctrl_t biquad; | ||
641 | |||
642 | if (copy_from_user((void *)&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) { | ||
643 | return -EFAULT; | ||
644 | } | ||
645 | |||
646 | if (cmd & SIOC_IN) { | ||
647 | rc=tas3004_write_biquad(self, biquad.channel, biquad.filter, &biquad.data); | ||
648 | if (rc != 0) return rc; | ||
649 | } | ||
650 | |||
651 | if (cmd & SIOC_OUT) { | ||
652 | rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data); | ||
653 | if (rc != 0) return rc; | ||
654 | |||
655 | if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) { | ||
656 | return -EFAULT; | ||
657 | } | ||
658 | |||
659 | } | ||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | static int | ||
664 | tas3004_eq_list_rw( struct tas3004_data_t *self, | ||
665 | u_int cmd, | ||
666 | u_long arg) | ||
667 | { | ||
668 | int rc = 0; | ||
669 | int filter_count; | ||
670 | int flags; | ||
671 | int i,j; | ||
672 | char sync_required[TAS3004_BIQUAD_CHANNEL_COUNT][TAS3004_BIQUAD_FILTER_COUNT]; | ||
673 | struct tas_biquad_ctrl_t biquad; | ||
674 | struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg; | ||
675 | |||
676 | memset(sync_required,0,sizeof(sync_required)); | ||
677 | |||
678 | if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int))) | ||
679 | return -EFAULT; | ||
680 | |||
681 | if (copy_from_user(&flags, &argp->flags, sizeof(int))) | ||
682 | return -EFAULT; | ||
683 | |||
684 | if (cmd & SIOC_IN) { | ||
685 | } | ||
686 | |||
687 | for (i=0; i < filter_count; i++) { | ||
688 | if (copy_from_user(&biquad, &argp->biquads[i], | ||
689 | sizeof(struct tas_biquad_ctrl_t))) { | ||
690 | return -EFAULT; | ||
691 | } | ||
692 | |||
693 | if (cmd & SIOC_IN) { | ||
694 | sync_required[biquad.channel][biquad.filter]=1; | ||
695 | rc=tas3004_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data); | ||
696 | if (rc != 0) return rc; | ||
697 | } | ||
698 | |||
699 | if (cmd & SIOC_OUT) { | ||
700 | rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data); | ||
701 | if (rc != 0) return rc; | ||
702 | |||
703 | if (copy_to_user(&argp->biquads[i], &biquad, | ||
704 | sizeof(struct tas_biquad_ctrl_t))) { | ||
705 | return -EFAULT; | ||
706 | } | ||
707 | } | ||
708 | } | ||
709 | |||
710 | if (cmd & SIOC_IN) { | ||
711 | /* | ||
712 | * This is OK for the tas3004. For the | ||
713 | * tas3001c, going into fast load mode causes | ||
714 | * the treble and bass to be reset to 0dB, and | ||
715 | * volume controls to be muted. | ||
716 | */ | ||
717 | if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1); | ||
718 | for (i=0; i<TAS3004_BIQUAD_CHANNEL_COUNT; i++) { | ||
719 | for (j=0; j<TAS3004_BIQUAD_FILTER_COUNT; j++) { | ||
720 | if (sync_required[i][j]) { | ||
721 | rc=tas3004_sync_biquad(self, i, j); | ||
722 | if (rc < 0) goto out; | ||
723 | } | ||
724 | } | ||
725 | } | ||
726 | out: | ||
727 | if (flags & TAS_BIQUAD_FAST_LOAD) | ||
728 | tas3004_fast_load(self,0); | ||
729 | } | ||
730 | |||
731 | return rc; | ||
732 | } | ||
733 | |||
734 | static int | ||
735 | tas3004_update_drce( struct tas3004_data_t *self, | ||
736 | int flags, | ||
737 | struct tas_drce_t *drce) | ||
738 | { | ||
739 | tas_shadow_t *shadow; | ||
740 | int i; | ||
741 | shadow=self->super.shadow; | ||
742 | |||
743 | if (flags & TAS_DRCE_ABOVE_RATIO) { | ||
744 | self->drce_state.above.expand = drce->above.expand; | ||
745 | if (drce->above.val == (1<<8)) { | ||
746 | self->drce_state.above.val = 1<<8; | ||
747 | shadow[TAS3004_REG_DRC][0] = 0x02; | ||
748 | |||
749 | } else if (drce->above.expand) { | ||
750 | i=above_threshold_expansion_index(drce->above.val); | ||
751 | self->drce_state.above.val=above_threshold_expansion_ratio[i]; | ||
752 | shadow[TAS3004_REG_DRC][0] = 0x0a + (i<<3); | ||
753 | } else { | ||
754 | i=above_threshold_compression_index(drce->above.val); | ||
755 | self->drce_state.above.val=above_threshold_compression_ratio[i]; | ||
756 | shadow[TAS3004_REG_DRC][0] = 0x08 + (i<<3); | ||
757 | } | ||
758 | } | ||
759 | |||
760 | if (flags & TAS_DRCE_BELOW_RATIO) { | ||
761 | self->drce_state.below.expand = drce->below.expand; | ||
762 | if (drce->below.val == (1<<8)) { | ||
763 | self->drce_state.below.val = 1<<8; | ||
764 | shadow[TAS3004_REG_DRC][1] = 0x02; | ||
765 | |||
766 | } else if (drce->below.expand) { | ||
767 | i=below_threshold_expansion_index(drce->below.val); | ||
768 | self->drce_state.below.val=below_threshold_expansion_ratio[i]; | ||
769 | shadow[TAS3004_REG_DRC][1] = 0x08 + (i<<3); | ||
770 | } else { | ||
771 | i=below_threshold_compression_index(drce->below.val); | ||
772 | self->drce_state.below.val=below_threshold_compression_ratio[i]; | ||
773 | shadow[TAS3004_REG_DRC][1] = 0x0a + (i<<3); | ||
774 | } | ||
775 | } | ||
776 | |||
777 | if (flags & TAS_DRCE_THRESHOLD) { | ||
778 | self->drce_state.threshold=quantize_db(drce->threshold); | ||
779 | shadow[TAS3004_REG_DRC][2] = db_to_regval(self->drce_state.threshold); | ||
780 | } | ||
781 | |||
782 | if (flags & TAS_DRCE_ENERGY) { | ||
783 | i=time_index(drce->energy); | ||
784 | self->drce_state.energy=time_constants[i]; | ||
785 | shadow[TAS3004_REG_DRC][3] = 0x40 + (i<<4); | ||
786 | } | ||
787 | |||
788 | if (flags & TAS_DRCE_ATTACK) { | ||
789 | i=time_index(drce->attack); | ||
790 | self->drce_state.attack=time_constants[i]; | ||
791 | shadow[TAS3004_REG_DRC][4] = 0x40 + (i<<4); | ||
792 | } | ||
793 | |||
794 | if (flags & TAS_DRCE_DECAY) { | ||
795 | i=time_index(drce->decay); | ||
796 | self->drce_state.decay=time_constants[i]; | ||
797 | shadow[TAS3004_REG_DRC][5] = 0x40 + (i<<4); | ||
798 | } | ||
799 | |||
800 | if (flags & TAS_DRCE_ENABLE) { | ||
801 | self->drce_state.enable = drce->enable; | ||
802 | } | ||
803 | |||
804 | if (!self->drce_state.enable) { | ||
805 | shadow[TAS3004_REG_DRC][0] |= 0x01; | ||
806 | } | ||
807 | |||
808 | #ifdef DEBUG_DRCE | ||
809 | printk("DRCE: set [ ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n", | ||
810 | self->drce_state.enable, | ||
811 | self->drce_state.above.expand,self->drce_state.above.val, | ||
812 | self->drce_state.below.expand,self->drce_state.below.val, | ||
813 | self->drce_state.threshold, | ||
814 | self->drce_state.energy, | ||
815 | self->drce_state.attack, | ||
816 | self->drce_state.decay); | ||
817 | |||
818 | printk("DRCE: reg [ %02x %02x %02x %02x %02x %02x ]\n", | ||
819 | (unsigned char)shadow[TAS3004_REG_DRC][0], | ||
820 | (unsigned char)shadow[TAS3004_REG_DRC][1], | ||
821 | (unsigned char)shadow[TAS3004_REG_DRC][2], | ||
822 | (unsigned char)shadow[TAS3004_REG_DRC][3], | ||
823 | (unsigned char)shadow[TAS3004_REG_DRC][4], | ||
824 | (unsigned char)shadow[TAS3004_REG_DRC][5]); | ||
825 | #endif | ||
826 | |||
827 | return tas3004_sync_register(self, TAS3004_REG_DRC); | ||
828 | } | ||
829 | |||
830 | static int | ||
831 | tas3004_drce_rw( struct tas3004_data_t *self, | ||
832 | u_int cmd, | ||
833 | u_long arg) | ||
834 | { | ||
835 | int rc; | ||
836 | struct tas_drce_ctrl_t drce_ctrl; | ||
837 | void __user *argp = (void __user *)arg; | ||
838 | |||
839 | if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t))) | ||
840 | return -EFAULT; | ||
841 | |||
842 | #ifdef DEBUG_DRCE | ||
843 | printk("DRCE: input [ FLAGS:%x ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n", | ||
844 | drce_ctrl.flags, | ||
845 | drce_ctrl.data.enable, | ||
846 | drce_ctrl.data.above.expand,drce_ctrl.data.above.val, | ||
847 | drce_ctrl.data.below.expand,drce_ctrl.data.below.val, | ||
848 | drce_ctrl.data.threshold, | ||
849 | drce_ctrl.data.energy, | ||
850 | drce_ctrl.data.attack, | ||
851 | drce_ctrl.data.decay); | ||
852 | #endif | ||
853 | |||
854 | if (cmd & SIOC_IN) { | ||
855 | rc = tas3004_update_drce(self, drce_ctrl.flags, &drce_ctrl.data); | ||
856 | if (rc < 0) return rc; | ||
857 | } | ||
858 | |||
859 | if (cmd & SIOC_OUT) { | ||
860 | if (drce_ctrl.flags & TAS_DRCE_ENABLE) | ||
861 | drce_ctrl.data.enable = self->drce_state.enable; | ||
862 | if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO) | ||
863 | drce_ctrl.data.above = self->drce_state.above; | ||
864 | if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO) | ||
865 | drce_ctrl.data.below = self->drce_state.below; | ||
866 | if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) | ||
867 | drce_ctrl.data.threshold = self->drce_state.threshold; | ||
868 | if (drce_ctrl.flags & TAS_DRCE_ENERGY) | ||
869 | drce_ctrl.data.energy = self->drce_state.energy; | ||
870 | if (drce_ctrl.flags & TAS_DRCE_ATTACK) | ||
871 | drce_ctrl.data.attack = self->drce_state.attack; | ||
872 | if (drce_ctrl.flags & TAS_DRCE_DECAY) | ||
873 | drce_ctrl.data.decay = self->drce_state.decay; | ||
874 | |||
875 | if (copy_to_user(argp, &drce_ctrl, | ||
876 | sizeof(struct tas_drce_ctrl_t))) { | ||
877 | return -EFAULT; | ||
878 | } | ||
879 | } | ||
880 | |||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | static void | ||
885 | tas3004_update_device_parameters(struct tas3004_data_t *self) | ||
886 | { | ||
887 | char data; | ||
888 | int i; | ||
889 | |||
890 | if (!self) return; | ||
891 | |||
892 | if (self->output_id == TAS_OUTPUT_HEADPHONES) { | ||
893 | /* turn on allPass when headphones are plugged in */ | ||
894 | data = 0x02; | ||
895 | } else { | ||
896 | data = 0x00; | ||
897 | } | ||
898 | |||
899 | tas3004_write_register(self, TAS3004_REG_MCR2, &data, WRITE_NORMAL | FORCE_WRITE); | ||
900 | |||
901 | for (i=0; tas3004_eq_prefs[i]; i++) { | ||
902 | struct tas_eq_pref_t *eq = tas3004_eq_prefs[i]; | ||
903 | |||
904 | if (eq->device_id == self->device_id && | ||
905 | (eq->output_id == 0 || eq->output_id == self->output_id) && | ||
906 | (eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) { | ||
907 | |||
908 | tas3004_update_drce(self, TAS_DRCE_ALL, eq->drce); | ||
909 | tas3004_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads); | ||
910 | |||
911 | break; | ||
912 | } | ||
913 | } | ||
914 | } | ||
915 | |||
916 | static void | ||
917 | tas3004_device_change_handler(void *self) | ||
918 | { | ||
919 | if (!self) return; | ||
920 | |||
921 | tas3004_update_device_parameters((struct tas3004_data_t *)self); | ||
922 | } | ||
923 | |||
924 | static struct work_struct device_change; | ||
925 | |||
926 | static int | ||
927 | tas3004_output_device_change( struct tas3004_data_t *self, | ||
928 | int device_id, | ||
929 | int output_id, | ||
930 | int speaker_id) | ||
931 | { | ||
932 | self->device_id=device_id; | ||
933 | self->output_id=output_id; | ||
934 | self->speaker_id=speaker_id; | ||
935 | |||
936 | schedule_work(&device_change); | ||
937 | |||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | static int | ||
942 | tas3004_device_ioctl( struct tas3004_data_t *self, | ||
943 | u_int cmd, | ||
944 | u_long arg) | ||
945 | { | ||
946 | uint __user *argp = (void __user *)arg; | ||
947 | switch (cmd) { | ||
948 | case TAS_READ_EQ: | ||
949 | case TAS_WRITE_EQ: | ||
950 | return tas3004_eq_rw(self, cmd, arg); | ||
951 | |||
952 | case TAS_READ_EQ_LIST: | ||
953 | case TAS_WRITE_EQ_LIST: | ||
954 | return tas3004_eq_list_rw(self, cmd, arg); | ||
955 | |||
956 | case TAS_READ_EQ_FILTER_COUNT: | ||
957 | put_user(TAS3004_BIQUAD_FILTER_COUNT, argp); | ||
958 | return 0; | ||
959 | |||
960 | case TAS_READ_EQ_CHANNEL_COUNT: | ||
961 | put_user(TAS3004_BIQUAD_CHANNEL_COUNT, argp); | ||
962 | return 0; | ||
963 | |||
964 | case TAS_READ_DRCE: | ||
965 | case TAS_WRITE_DRCE: | ||
966 | return tas3004_drce_rw(self, cmd, arg); | ||
967 | |||
968 | case TAS_READ_DRCE_CAPS: | ||
969 | put_user(TAS_DRCE_ENABLE | | ||
970 | TAS_DRCE_ABOVE_RATIO | | ||
971 | TAS_DRCE_BELOW_RATIO | | ||
972 | TAS_DRCE_THRESHOLD | | ||
973 | TAS_DRCE_ENERGY | | ||
974 | TAS_DRCE_ATTACK | | ||
975 | TAS_DRCE_DECAY, | ||
976 | argp); | ||
977 | return 0; | ||
978 | |||
979 | case TAS_READ_DRCE_MIN: | ||
980 | case TAS_READ_DRCE_MAX: { | ||
981 | struct tas_drce_ctrl_t drce_ctrl; | ||
982 | const struct tas_drce_t *drce_copy; | ||
983 | |||
984 | if (copy_from_user(&drce_ctrl, argp, | ||
985 | sizeof(struct tas_drce_ctrl_t))) { | ||
986 | return -EFAULT; | ||
987 | } | ||
988 | |||
989 | if (cmd == TAS_READ_DRCE_MIN) { | ||
990 | drce_copy=&tas3004_drce_min; | ||
991 | } else { | ||
992 | drce_copy=&tas3004_drce_max; | ||
993 | } | ||
994 | |||
995 | if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO) { | ||
996 | drce_ctrl.data.above=drce_copy->above; | ||
997 | } | ||
998 | if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO) { | ||
999 | drce_ctrl.data.below=drce_copy->below; | ||
1000 | } | ||
1001 | if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) { | ||
1002 | drce_ctrl.data.threshold=drce_copy->threshold; | ||
1003 | } | ||
1004 | if (drce_ctrl.flags & TAS_DRCE_ENERGY) { | ||
1005 | drce_ctrl.data.energy=drce_copy->energy; | ||
1006 | } | ||
1007 | if (drce_ctrl.flags & TAS_DRCE_ATTACK) { | ||
1008 | drce_ctrl.data.attack=drce_copy->attack; | ||
1009 | } | ||
1010 | if (drce_ctrl.flags & TAS_DRCE_DECAY) { | ||
1011 | drce_ctrl.data.decay=drce_copy->decay; | ||
1012 | } | ||
1013 | |||
1014 | if (copy_to_user(argp, &drce_ctrl, | ||
1015 | sizeof(struct tas_drce_ctrl_t))) { | ||
1016 | return -EFAULT; | ||
1017 | } | ||
1018 | } | ||
1019 | } | ||
1020 | |||
1021 | return -EINVAL; | ||
1022 | } | ||
1023 | |||
1024 | static int | ||
1025 | tas3004_init_mixer(struct tas3004_data_t *self) | ||
1026 | { | ||
1027 | unsigned char mcr = (1<<6)+(2<<4)+(2<<2); | ||
1028 | |||
1029 | /* Make sure something answers on the i2c bus */ | ||
1030 | if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr, | ||
1031 | WRITE_NORMAL | FORCE_WRITE) < 0) | ||
1032 | return -1; | ||
1033 | |||
1034 | tas3004_fast_load(self, 1); | ||
1035 | |||
1036 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0); | ||
1037 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1); | ||
1038 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2); | ||
1039 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3); | ||
1040 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4); | ||
1041 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5); | ||
1042 | (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6); | ||
1043 | |||
1044 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0); | ||
1045 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1); | ||
1046 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2); | ||
1047 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3); | ||
1048 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4); | ||
1049 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5); | ||
1050 | (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6); | ||
1051 | |||
1052 | tas3004_sync_register(self, TAS3004_REG_DRC); | ||
1053 | |||
1054 | tas3004_sync_register(self, TAS3004_REG_MCR2); | ||
1055 | |||
1056 | tas3004_fast_load(self, 0); | ||
1057 | |||
1058 | tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT); | ||
1059 | tas3004_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT); | ||
1060 | tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0); | ||
1061 | tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0); | ||
1062 | |||
1063 | tas3004_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT); | ||
1064 | tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT); | ||
1065 | |||
1066 | tas3004_set_mixer_level(self, SOUND_MIXER_LINE,SW_INPUT_VOLUME_DEFAULT); | ||
1067 | |||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | static int | ||
1072 | tas3004_uninit_mixer(struct tas3004_data_t *self) | ||
1073 | { | ||
1074 | tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, 0); | ||
1075 | tas3004_set_mixer_level(self, SOUND_MIXER_PCM, 0); | ||
1076 | tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0); | ||
1077 | tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0); | ||
1078 | |||
1079 | tas3004_set_mixer_level(self, SOUND_MIXER_BASS, 0); | ||
1080 | tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, 0); | ||
1081 | |||
1082 | tas3004_set_mixer_level(self, SOUND_MIXER_LINE, 0); | ||
1083 | |||
1084 | return 0; | ||
1085 | } | ||
1086 | |||
1087 | static int | ||
1088 | tas3004_init(struct i2c_client *client) | ||
1089 | { | ||
1090 | struct tas3004_data_t *self; | ||
1091 | size_t sz = sizeof(*self) + (TAS3004_REG_MAX*sizeof(tas_shadow_t)); | ||
1092 | char drce_init[] = { 0x69, 0x22, 0x9f, 0xb0, 0x60, 0xa0 }; | ||
1093 | char mcr2 = 0; | ||
1094 | int i, j; | ||
1095 | |||
1096 | self = kmalloc(sz, GFP_KERNEL); | ||
1097 | if (!self) | ||
1098 | return -ENOMEM; | ||
1099 | memset(self, 0, sz); | ||
1100 | |||
1101 | self->super.client = client; | ||
1102 | self->super.shadow = (tas_shadow_t *)(self+1); | ||
1103 | self->output_id = TAS_OUTPUT_HEADPHONES; | ||
1104 | |||
1105 | dev_set_drvdata(&client->dev, self); | ||
1106 | |||
1107 | for (i = 0; i < TAS3004_BIQUAD_CHANNEL_COUNT; i++) | ||
1108 | for (j = 0; j<TAS3004_BIQUAD_FILTER_COUNT; j++) | ||
1109 | tas3004_write_biquad_shadow(self, i, j, | ||
1110 | &tas3004_eq_unity); | ||
1111 | |||
1112 | tas3004_write_register(self, TAS3004_REG_MCR2, &mcr2, WRITE_SHADOW); | ||
1113 | tas3004_write_register(self, TAS3004_REG_DRC, drce_init, WRITE_SHADOW); | ||
1114 | |||
1115 | INIT_WORK(&device_change, tas3004_device_change_handler, self); | ||
1116 | return 0; | ||
1117 | } | ||
1118 | |||
1119 | static void | ||
1120 | tas3004_uninit(struct tas3004_data_t *self) | ||
1121 | { | ||
1122 | tas3004_uninit_mixer(self); | ||
1123 | kfree(self); | ||
1124 | } | ||
1125 | |||
1126 | |||
1127 | struct tas_driver_hooks_t tas3004_hooks = { | ||
1128 | .init = (tas_hook_init_t)tas3004_init, | ||
1129 | .post_init = (tas_hook_post_init_t)tas3004_init_mixer, | ||
1130 | .uninit = (tas_hook_uninit_t)tas3004_uninit, | ||
1131 | .get_mixer_level = (tas_hook_get_mixer_level_t)tas3004_get_mixer_level, | ||
1132 | .set_mixer_level = (tas_hook_set_mixer_level_t)tas3004_set_mixer_level, | ||
1133 | .enter_sleep = (tas_hook_enter_sleep_t)tas3004_enter_sleep, | ||
1134 | .leave_sleep = (tas_hook_leave_sleep_t)tas3004_leave_sleep, | ||
1135 | .supported_mixers = (tas_hook_supported_mixers_t)tas3004_supported_mixers, | ||
1136 | .mixer_is_stereo = (tas_hook_mixer_is_stereo_t)tas3004_mixer_is_stereo, | ||
1137 | .stereo_mixers = (tas_hook_stereo_mixers_t)tas3004_stereo_mixers, | ||
1138 | .output_device_change = (tas_hook_output_device_change_t)tas3004_output_device_change, | ||
1139 | .device_ioctl = (tas_hook_device_ioctl_t)tas3004_device_ioctl | ||
1140 | }; | ||