Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | }; |