diff options
Diffstat (limited to 'sound/oss/dmasound/tas3004.c')
-rw-r--r-- | sound/oss/dmasound/tas3004.c | 1138 |
1 files changed, 0 insertions, 1138 deletions
diff --git a/sound/oss/dmasound/tas3004.c b/sound/oss/dmasound/tas3004.c deleted file mode 100644 index 678bf0f..0000000 --- a/sound/oss/dmasound/tas3004.c +++ /dev/null @@ -1,1138 +0,0 @@ -/* - * Driver for the i2c/i2s based TA3004 sound chip used - * on some Apple hardware. Also known as "snapper". - * - * Tobias Sargeant <tobias.sargeant@bigpond.com> - * Based upon tas3001c.c by Christopher C. Chimelis <chris@debian.org>: - * - * Input support by Renzo Davoli <renzo@cs.unibo.it> - * - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/proc_fs.h> -#include <linux/ioport.h> -#include <linux/sysctl.h> -#include <linux/types.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/soundcard.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> - -#include <asm/uaccess.h> -#include <asm/errno.h> -#include <asm/io.h> -#include <asm/prom.h> - -#include "dmasound.h" -#include "tas_common.h" -#include "tas3004.h" - -#include "tas_ioctl.h" - -/* #define DEBUG_DRCE */ - -#define TAS3004_BIQUAD_FILTER_COUNT 7 -#define TAS3004_BIQUAD_CHANNEL_COUNT 2 - -#define VOL_DEFAULT (100 * 4 / 5) -#define INPUT_DEFAULT (100 * 4 / 5) -#define BASS_DEFAULT (100 / 2) -#define TREBLE_DEFAULT (100 / 2) - -struct tas3004_data_t { - struct tas_data_t super; - int device_id; - int output_id; - int speaker_id; - struct tas_drce_t drce_state; - struct work_struct change; -}; - -#define MAKE_TIME(sec,usec) (((sec)<<12) + (50000+(usec/10)*(1<<12))/100000) - -#define MAKE_RATIO(i,f) (((i)<<8) + ((500+(f)*(1<<8))/1000)) - - -static const union tas_biquad_t tas3004_eq_unity = { - .buf = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }, -}; - - -static const struct tas_drce_t tas3004_drce_min = { - .enable = 1, - .above = { .val = MAKE_RATIO(16,0), .expand = 0 }, - .below = { .val = MAKE_RATIO(2,0), .expand = 0 }, - .threshold = -0x59a0, - .energy = MAKE_TIME(0, 1700), - .attack = MAKE_TIME(0, 1700), - .decay = MAKE_TIME(0, 1700), -}; - - -static const struct tas_drce_t tas3004_drce_max = { - .enable = 1, - .above = { .val = MAKE_RATIO(1,500), .expand = 1 }, - .below = { .val = MAKE_RATIO(2,0), .expand = 1 }, - .threshold = -0x0, - .energy = MAKE_TIME(2,400000), - .attack = MAKE_TIME(2,400000), - .decay = MAKE_TIME(2,400000), -}; - - -static const unsigned short time_constants[]={ - MAKE_TIME(0, 1700), - MAKE_TIME(0, 3500), - MAKE_TIME(0, 6700), - MAKE_TIME(0, 13000), - MAKE_TIME(0, 26000), - MAKE_TIME(0, 53000), - MAKE_TIME(0,106000), - MAKE_TIME(0,212000), - MAKE_TIME(0,425000), - MAKE_TIME(0,850000), - MAKE_TIME(1,700000), - MAKE_TIME(2,400000), -}; - -static const unsigned short above_threshold_compression_ratio[]={ - MAKE_RATIO( 1, 70), - MAKE_RATIO( 1,140), - MAKE_RATIO( 1,230), - MAKE_RATIO( 1,330), - MAKE_RATIO( 1,450), - MAKE_RATIO( 1,600), - MAKE_RATIO( 1,780), - MAKE_RATIO( 2, 0), - MAKE_RATIO( 2,290), - MAKE_RATIO( 2,670), - MAKE_RATIO( 3,200), - MAKE_RATIO( 4, 0), - MAKE_RATIO( 5,330), - MAKE_RATIO( 8, 0), - MAKE_RATIO(16, 0), -}; - -static const unsigned short above_threshold_expansion_ratio[]={ - MAKE_RATIO(1, 60), - MAKE_RATIO(1,130), - MAKE_RATIO(1,190), - MAKE_RATIO(1,250), - MAKE_RATIO(1,310), - MAKE_RATIO(1,380), - MAKE_RATIO(1,440), - MAKE_RATIO(1,500) -}; - -static const unsigned short below_threshold_compression_ratio[]={ - MAKE_RATIO(1, 70), - MAKE_RATIO(1,140), - MAKE_RATIO(1,230), - MAKE_RATIO(1,330), - MAKE_RATIO(1,450), - MAKE_RATIO(1,600), - MAKE_RATIO(1,780), - MAKE_RATIO(2, 0) -}; - -static const unsigned short below_threshold_expansion_ratio[]={ - MAKE_RATIO(1, 60), - MAKE_RATIO(1,130), - MAKE_RATIO(1,190), - MAKE_RATIO(1,250), - MAKE_RATIO(1,310), - MAKE_RATIO(1,380), - MAKE_RATIO(1,440), - MAKE_RATIO(1,500), - MAKE_RATIO(1,560), - MAKE_RATIO(1,630), - MAKE_RATIO(1,690), - MAKE_RATIO(1,750), - MAKE_RATIO(1,810), - MAKE_RATIO(1,880), - MAKE_RATIO(1,940), - MAKE_RATIO(2, 0) -}; - -static inline int -search( unsigned short val, - const unsigned short *arr, - const int arrsize) { - /* - * This could be a binary search, but for small tables, - * a linear search is likely to be faster - */ - - int i; - - for (i=0; i < arrsize; i++) - if (arr[i] >= val) - goto _1; - return arrsize-1; - _1: - if (i == 0) - return 0; - return (arr[i]-val < val-arr[i-1]) ? i : i-1; -} - -#define SEARCH(a, b) search(a, b, ARRAY_SIZE(b)) - -static inline int -time_index(unsigned short time) -{ - return SEARCH(time, time_constants); -} - - -static inline int -above_threshold_compression_index(unsigned short ratio) -{ - return SEARCH(ratio, above_threshold_compression_ratio); -} - - -static inline int -above_threshold_expansion_index(unsigned short ratio) -{ - return SEARCH(ratio, above_threshold_expansion_ratio); -} - - -static inline int -below_threshold_compression_index(unsigned short ratio) -{ - return SEARCH(ratio, below_threshold_compression_ratio); -} - - -static inline int -below_threshold_expansion_index(unsigned short ratio) -{ - return SEARCH(ratio, below_threshold_expansion_ratio); -} - -static inline unsigned char db_to_regval(short db) { - int r=0; - - r=(db+0x59a0) / 0x60; - - if (r < 0x91) return 0x91; - if (r > 0xef) return 0xef; - return r; -} - -static inline short quantize_db(short db) -{ - return db_to_regval(db) * 0x60 - 0x59a0; -} - -static inline int -register_width(enum tas3004_reg_t r) -{ - switch(r) { - case TAS3004_REG_MCR: - case TAS3004_REG_TREBLE: - case TAS3004_REG_BASS: - case TAS3004_REG_ANALOG_CTRL: - case TAS3004_REG_TEST1: - case TAS3004_REG_TEST2: - case TAS3004_REG_MCR2: - return 1; - - case TAS3004_REG_LEFT_LOUD_BIQUAD_GAIN: - case TAS3004_REG_RIGHT_LOUD_BIQUAD_GAIN: - return 3; - - case TAS3004_REG_DRC: - case TAS3004_REG_VOLUME: - return 6; - - case TAS3004_REG_LEFT_MIXER: - case TAS3004_REG_RIGHT_MIXER: - return 9; - - case TAS3004_REG_TEST: - return 10; - - case TAS3004_REG_LEFT_BIQUAD0: - case TAS3004_REG_LEFT_BIQUAD1: - case TAS3004_REG_LEFT_BIQUAD2: - case TAS3004_REG_LEFT_BIQUAD3: - case TAS3004_REG_LEFT_BIQUAD4: - case TAS3004_REG_LEFT_BIQUAD5: - case TAS3004_REG_LEFT_BIQUAD6: - - case TAS3004_REG_RIGHT_BIQUAD0: - case TAS3004_REG_RIGHT_BIQUAD1: - case TAS3004_REG_RIGHT_BIQUAD2: - case TAS3004_REG_RIGHT_BIQUAD3: - case TAS3004_REG_RIGHT_BIQUAD4: - case TAS3004_REG_RIGHT_BIQUAD5: - case TAS3004_REG_RIGHT_BIQUAD6: - - case TAS3004_REG_LEFT_LOUD_BIQUAD: - case TAS3004_REG_RIGHT_LOUD_BIQUAD: - return 15; - - default: - return 0; - } -} - -static int -tas3004_write_register( struct tas3004_data_t *self, - enum tas3004_reg_t reg_num, - char *data, - uint write_mode) -{ - if (reg_num==TAS3004_REG_MCR || - reg_num==TAS3004_REG_BASS || - reg_num==TAS3004_REG_TREBLE || - reg_num==TAS3004_REG_ANALOG_CTRL) { - return tas_write_byte_register(&self->super, - (uint)reg_num, - *data, - write_mode); - } else { - return tas_write_register(&self->super, - (uint)reg_num, - register_width(reg_num), - data, - write_mode); - } -} - -static int -tas3004_sync_register( struct tas3004_data_t *self, - enum tas3004_reg_t reg_num) -{ - if (reg_num==TAS3004_REG_MCR || - reg_num==TAS3004_REG_BASS || - reg_num==TAS3004_REG_TREBLE || - reg_num==TAS3004_REG_ANALOG_CTRL) { - return tas_sync_byte_register(&self->super, - (uint)reg_num, - register_width(reg_num)); - } else { - return tas_sync_register(&self->super, - (uint)reg_num, - register_width(reg_num)); - } -} - -static int -tas3004_read_register( struct tas3004_data_t *self, - enum tas3004_reg_t reg_num, - char *data, - uint write_mode) -{ - return tas_read_register(&self->super, - (uint)reg_num, - register_width(reg_num), - data); -} - -static inline int -tas3004_fast_load(struct tas3004_data_t *self, int fast) -{ - if (fast) - self->super.shadow[TAS3004_REG_MCR][0] |= 0x80; - else - self->super.shadow[TAS3004_REG_MCR][0] &= 0x7f; - return tas3004_sync_register(self,TAS3004_REG_MCR); -} - -static uint -tas3004_supported_mixers(struct tas3004_data_t *self) -{ - return SOUND_MASK_VOLUME | - SOUND_MASK_PCM | - SOUND_MASK_ALTPCM | - SOUND_MASK_IMIX | - SOUND_MASK_TREBLE | - SOUND_MASK_BASS | - SOUND_MASK_MIC | - SOUND_MASK_LINE; -} - -static int -tas3004_mixer_is_stereo(struct tas3004_data_t *self, int mixer) -{ - switch(mixer) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_PCM: - case SOUND_MIXER_ALTPCM: - case SOUND_MIXER_IMIX: - return 1; - default: - return 0; - } -} - -static uint -tas3004_stereo_mixers(struct tas3004_data_t *self) -{ - uint r = tas3004_supported_mixers(self); - uint i; - - for (i=1; i<SOUND_MIXER_NRDEVICES; i++) - if (r&(1<<i) && !tas3004_mixer_is_stereo(self,i)) - r &= ~(1<<i); - return r; -} - -static int -tas3004_get_mixer_level(struct tas3004_data_t *self, int mixer, uint *level) -{ - if (!self) - return -1; - - *level = self->super.mixer[mixer]; - - return 0; -} - -static int -tas3004_set_mixer_level(struct tas3004_data_t *self, int mixer, uint level) -{ - int rc; - tas_shadow_t *shadow; - uint temp; - uint offset=0; - - if (!self) - return -1; - - shadow = self->super.shadow; - - if (!tas3004_mixer_is_stereo(self,mixer)) - level = tas_mono_to_stereo(level); - switch(mixer) { - case SOUND_MIXER_VOLUME: - temp = tas3004_gain.master[level&0xff]; - SET_4_20(shadow[TAS3004_REG_VOLUME], 0, temp); - temp = tas3004_gain.master[(level>>8)&0xff]; - SET_4_20(shadow[TAS3004_REG_VOLUME], 3, temp); - rc = tas3004_sync_register(self,TAS3004_REG_VOLUME); - break; - case SOUND_MIXER_IMIX: - offset += 3; - case SOUND_MIXER_ALTPCM: - offset += 3; - case SOUND_MIXER_PCM: - /* - * Don't load these in fast mode. The documentation - * says it can be done in either mode, but testing it - * shows that fast mode produces ugly clicking. - */ - /* tas3004_fast_load(self,1); */ - temp = tas3004_gain.mixer[level&0xff]; - SET_4_20(shadow[TAS3004_REG_LEFT_MIXER], offset, temp); - temp = tas3004_gain.mixer[(level>>8)&0xff]; - SET_4_20(shadow[TAS3004_REG_RIGHT_MIXER], offset, temp); - rc = tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER); - if (rc == 0) - rc=tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER); - /* tas3004_fast_load(self,0); */ - break; - case SOUND_MIXER_TREBLE: - temp = tas3004_gain.treble[level&0xff]; - shadow[TAS3004_REG_TREBLE][0]=temp&0xff; - rc = tas3004_sync_register(self,TAS3004_REG_TREBLE); - break; - case SOUND_MIXER_BASS: - temp = tas3004_gain.bass[level&0xff]; - shadow[TAS3004_REG_BASS][0]=temp&0xff; - rc = tas3004_sync_register(self,TAS3004_REG_BASS); - break; - case SOUND_MIXER_MIC: - if ((level&0xff)>0) { - software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff); - if (self->super.mixer[mixer] == 0) { - self->super.mixer[SOUND_MIXER_LINE] = 0; - shadow[TAS3004_REG_ANALOG_CTRL][0]=0xc2; - rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL); - } else rc=0; - } else { - self->super.mixer[SOUND_MIXER_LINE] = SW_INPUT_VOLUME_DEFAULT; - software_input_volume = SW_INPUT_VOLUME_SCALE * - (self->super.mixer[SOUND_MIXER_LINE]&0xff); - shadow[TAS3004_REG_ANALOG_CTRL][0]=0x00; - rc = tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL); - } - break; - case SOUND_MIXER_LINE: - if (self->super.mixer[SOUND_MIXER_MIC] == 0) { - software_input_volume = SW_INPUT_VOLUME_SCALE * (level&0xff); - rc=0; - } - break; - default: - rc = -1; - break; - } - if (rc < 0) - return rc; - self->super.mixer[mixer] = level; - - return 0; -} - -static int -tas3004_leave_sleep(struct tas3004_data_t *self) -{ - unsigned char mcr = (1<<6)+(2<<4)+(2<<2); - - if (!self) - return -1; - - /* Make sure something answers on the i2c bus */ - if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr, - WRITE_NORMAL | FORCE_WRITE) < 0) - return -1; - - tas3004_fast_load(self, 1); - - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6); - - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6); - - tas3004_fast_load(self, 0); - - (void)tas3004_sync_register(self,TAS3004_REG_VOLUME); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_MIXER); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_MIXER); - (void)tas3004_sync_register(self,TAS3004_REG_TREBLE); - (void)tas3004_sync_register(self,TAS3004_REG_BASS); - (void)tas3004_sync_register(self,TAS3004_REG_ANALOG_CTRL); - - return 0; -} - -static int -tas3004_enter_sleep(struct tas3004_data_t *self) -{ - if (!self) - return -1; - return 0; -} - -static int -tas3004_sync_biquad( struct tas3004_data_t *self, - u_int channel, - u_int filter) -{ - enum tas3004_reg_t reg; - - if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT || - filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL; - - reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter; - - return tas3004_sync_register(self,reg); -} - -static int -tas3004_write_biquad_shadow( struct tas3004_data_t *self, - u_int channel, - u_int filter, - const union tas_biquad_t *biquad) -{ - tas_shadow_t *shadow=self->super.shadow; - enum tas3004_reg_t reg; - - if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT || - filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL; - - reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter; - - SET_4_20(shadow[reg], 0,biquad->coeff.b0); - SET_4_20(shadow[reg], 3,biquad->coeff.b1); - SET_4_20(shadow[reg], 6,biquad->coeff.b2); - SET_4_20(shadow[reg], 9,biquad->coeff.a1); - SET_4_20(shadow[reg],12,biquad->coeff.a2); - - return 0; -} - -static int -tas3004_write_biquad( struct tas3004_data_t *self, - u_int channel, - u_int filter, - const union tas_biquad_t *biquad) -{ - int rc; - - rc=tas3004_write_biquad_shadow(self, channel, filter, biquad); - if (rc < 0) return rc; - - return tas3004_sync_biquad(self, channel, filter); -} - -static int -tas3004_write_biquad_list( struct tas3004_data_t *self, - u_int filter_count, - u_int flags, - struct tas_biquad_ctrl_t *biquads) -{ - int i; - int rc; - - if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1); - - for (i=0; i<filter_count; i++) { - rc=tas3004_write_biquad(self, - biquads[i].channel, - biquads[i].filter, - &biquads[i].data); - if (rc < 0) break; - } - - if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,0); - - return rc; -} - -static int -tas3004_read_biquad( struct tas3004_data_t *self, - u_int channel, - u_int filter, - union tas_biquad_t *biquad) -{ - tas_shadow_t *shadow=self->super.shadow; - enum tas3004_reg_t reg; - - if (channel >= TAS3004_BIQUAD_CHANNEL_COUNT || - filter >= TAS3004_BIQUAD_FILTER_COUNT) return -EINVAL; - - reg=( channel ? TAS3004_REG_RIGHT_BIQUAD0 : TAS3004_REG_LEFT_BIQUAD0 ) + filter; - - biquad->coeff.b0=GET_4_20(shadow[reg], 0); - biquad->coeff.b1=GET_4_20(shadow[reg], 3); - biquad->coeff.b2=GET_4_20(shadow[reg], 6); - biquad->coeff.a1=GET_4_20(shadow[reg], 9); - biquad->coeff.a2=GET_4_20(shadow[reg],12); - - return 0; -} - -static int -tas3004_eq_rw( struct tas3004_data_t *self, - u_int cmd, - u_long arg) -{ - void __user *argp = (void __user *)arg; - int rc; - struct tas_biquad_ctrl_t biquad; - - if (copy_from_user((void *)&biquad, argp, sizeof(struct tas_biquad_ctrl_t))) { - return -EFAULT; - } - - if (cmd & SIOC_IN) { - rc=tas3004_write_biquad(self, biquad.channel, biquad.filter, &biquad.data); - if (rc != 0) return rc; - } - - if (cmd & SIOC_OUT) { - rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data); - if (rc != 0) return rc; - - if (copy_to_user(argp, &biquad, sizeof(struct tas_biquad_ctrl_t))) { - return -EFAULT; - } - - } - return 0; -} - -static int -tas3004_eq_list_rw( struct tas3004_data_t *self, - u_int cmd, - u_long arg) -{ - int rc = 0; - int filter_count; - int flags; - int i,j; - char sync_required[TAS3004_BIQUAD_CHANNEL_COUNT][TAS3004_BIQUAD_FILTER_COUNT]; - struct tas_biquad_ctrl_t biquad; - struct tas_biquad_ctrl_list_t __user *argp = (void __user *)arg; - - memset(sync_required,0,sizeof(sync_required)); - - if (copy_from_user(&filter_count, &argp->filter_count, sizeof(int))) - return -EFAULT; - - if (copy_from_user(&flags, &argp->flags, sizeof(int))) - return -EFAULT; - - if (cmd & SIOC_IN) { - } - - for (i=0; i < filter_count; i++) { - if (copy_from_user(&biquad, &argp->biquads[i], - sizeof(struct tas_biquad_ctrl_t))) { - return -EFAULT; - } - - if (cmd & SIOC_IN) { - sync_required[biquad.channel][biquad.filter]=1; - rc=tas3004_write_biquad_shadow(self, biquad.channel, biquad.filter, &biquad.data); - if (rc != 0) return rc; - } - - if (cmd & SIOC_OUT) { - rc=tas3004_read_biquad(self, biquad.channel, biquad.filter, &biquad.data); - if (rc != 0) return rc; - - if (copy_to_user(&argp->biquads[i], &biquad, - sizeof(struct tas_biquad_ctrl_t))) { - return -EFAULT; - } - } - } - - if (cmd & SIOC_IN) { - /* - * This is OK for the tas3004. For the - * tas3001c, going into fast load mode causes - * the treble and bass to be reset to 0dB, and - * volume controls to be muted. - */ - if (flags & TAS_BIQUAD_FAST_LOAD) tas3004_fast_load(self,1); - for (i=0; i<TAS3004_BIQUAD_CHANNEL_COUNT; i++) { - for (j=0; j<TAS3004_BIQUAD_FILTER_COUNT; j++) { - if (sync_required[i][j]) { - rc=tas3004_sync_biquad(self, i, j); - if (rc < 0) goto out; - } - } - } - out: - if (flags & TAS_BIQUAD_FAST_LOAD) - tas3004_fast_load(self,0); - } - - return rc; -} - -static int -tas3004_update_drce( struct tas3004_data_t *self, - int flags, - struct tas_drce_t *drce) -{ - tas_shadow_t *shadow; - int i; - shadow=self->super.shadow; - - if (flags & TAS_DRCE_ABOVE_RATIO) { - self->drce_state.above.expand = drce->above.expand; - if (drce->above.val == (1<<8)) { - self->drce_state.above.val = 1<<8; - shadow[TAS3004_REG_DRC][0] = 0x02; - - } else if (drce->above.expand) { - i=above_threshold_expansion_index(drce->above.val); - self->drce_state.above.val=above_threshold_expansion_ratio[i]; - shadow[TAS3004_REG_DRC][0] = 0x0a + (i<<3); - } else { - i=above_threshold_compression_index(drce->above.val); - self->drce_state.above.val=above_threshold_compression_ratio[i]; - shadow[TAS3004_REG_DRC][0] = 0x08 + (i<<3); - } - } - - if (flags & TAS_DRCE_BELOW_RATIO) { - self->drce_state.below.expand = drce->below.expand; - if (drce->below.val == (1<<8)) { - self->drce_state.below.val = 1<<8; - shadow[TAS3004_REG_DRC][1] = 0x02; - - } else if (drce->below.expand) { - i=below_threshold_expansion_index(drce->below.val); - self->drce_state.below.val=below_threshold_expansion_ratio[i]; - shadow[TAS3004_REG_DRC][1] = 0x08 + (i<<3); - } else { - i=below_threshold_compression_index(drce->below.val); - self->drce_state.below.val=below_threshold_compression_ratio[i]; - shadow[TAS3004_REG_DRC][1] = 0x0a + (i<<3); - } - } - - if (flags & TAS_DRCE_THRESHOLD) { - self->drce_state.threshold=quantize_db(drce->threshold); - shadow[TAS3004_REG_DRC][2] = db_to_regval(self->drce_state.threshold); - } - - if (flags & TAS_DRCE_ENERGY) { - i=time_index(drce->energy); - self->drce_state.energy=time_constants[i]; - shadow[TAS3004_REG_DRC][3] = 0x40 + (i<<4); - } - - if (flags & TAS_DRCE_ATTACK) { - i=time_index(drce->attack); - self->drce_state.attack=time_constants[i]; - shadow[TAS3004_REG_DRC][4] = 0x40 + (i<<4); - } - - if (flags & TAS_DRCE_DECAY) { - i=time_index(drce->decay); - self->drce_state.decay=time_constants[i]; - shadow[TAS3004_REG_DRC][5] = 0x40 + (i<<4); - } - - if (flags & TAS_DRCE_ENABLE) { - self->drce_state.enable = drce->enable; - } - - if (!self->drce_state.enable) { - shadow[TAS3004_REG_DRC][0] |= 0x01; - } - -#ifdef DEBUG_DRCE - printk("DRCE: set [ ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n", - self->drce_state.enable, - self->drce_state.above.expand,self->drce_state.above.val, - self->drce_state.below.expand,self->drce_state.below.val, - self->drce_state.threshold, - self->drce_state.energy, - self->drce_state.attack, - self->drce_state.decay); - - printk("DRCE: reg [ %02x %02x %02x %02x %02x %02x ]\n", - (unsigned char)shadow[TAS3004_REG_DRC][0], - (unsigned char)shadow[TAS3004_REG_DRC][1], - (unsigned char)shadow[TAS3004_REG_DRC][2], - (unsigned char)shadow[TAS3004_REG_DRC][3], - (unsigned char)shadow[TAS3004_REG_DRC][4], - (unsigned char)shadow[TAS3004_REG_DRC][5]); -#endif - - return tas3004_sync_register(self, TAS3004_REG_DRC); -} - -static int -tas3004_drce_rw( struct tas3004_data_t *self, - u_int cmd, - u_long arg) -{ - int rc; - struct tas_drce_ctrl_t drce_ctrl; - void __user *argp = (void __user *)arg; - - if (copy_from_user(&drce_ctrl, argp, sizeof(struct tas_drce_ctrl_t))) - return -EFAULT; - -#ifdef DEBUG_DRCE - printk("DRCE: input [ FLAGS:%x ENABLE:%x ABOVE:%x/%x BELOW:%x/%x THRESH:%x ENERGY:%x ATTACK:%x DECAY:%x\n", - drce_ctrl.flags, - drce_ctrl.data.enable, - drce_ctrl.data.above.expand,drce_ctrl.data.above.val, - drce_ctrl.data.below.expand,drce_ctrl.data.below.val, - drce_ctrl.data.threshold, - drce_ctrl.data.energy, - drce_ctrl.data.attack, - drce_ctrl.data.decay); -#endif - - if (cmd & SIOC_IN) { - rc = tas3004_update_drce(self, drce_ctrl.flags, &drce_ctrl.data); - if (rc < 0) return rc; - } - - if (cmd & SIOC_OUT) { - if (drce_ctrl.flags & TAS_DRCE_ENABLE) - drce_ctrl.data.enable = self->drce_state.enable; - if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO) - drce_ctrl.data.above = self->drce_state.above; - if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO) - drce_ctrl.data.below = self->drce_state.below; - if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) - drce_ctrl.data.threshold = self->drce_state.threshold; - if (drce_ctrl.flags & TAS_DRCE_ENERGY) - drce_ctrl.data.energy = self->drce_state.energy; - if (drce_ctrl.flags & TAS_DRCE_ATTACK) - drce_ctrl.data.attack = self->drce_state.attack; - if (drce_ctrl.flags & TAS_DRCE_DECAY) - drce_ctrl.data.decay = self->drce_state.decay; - - if (copy_to_user(argp, &drce_ctrl, - sizeof(struct tas_drce_ctrl_t))) { - return -EFAULT; - } - } - - return 0; -} - -static void -tas3004_update_device_parameters(struct tas3004_data_t *self) -{ - char data; - int i; - - if (!self) return; - - if (self->output_id == TAS_OUTPUT_HEADPHONES) { - /* turn on allPass when headphones are plugged in */ - data = 0x02; - } else { - data = 0x00; - } - - tas3004_write_register(self, TAS3004_REG_MCR2, &data, WRITE_NORMAL | FORCE_WRITE); - - for (i=0; tas3004_eq_prefs[i]; i++) { - struct tas_eq_pref_t *eq = tas3004_eq_prefs[i]; - - if (eq->device_id == self->device_id && - (eq->output_id == 0 || eq->output_id == self->output_id) && - (eq->speaker_id == 0 || eq->speaker_id == self->speaker_id)) { - - tas3004_update_drce(self, TAS_DRCE_ALL, eq->drce); - tas3004_write_biquad_list(self, eq->filter_count, TAS_BIQUAD_FAST_LOAD, eq->biquads); - - break; - } - } -} - -static void -tas3004_device_change_handler(struct work_struct *work) -{ - struct tas3004_data_t *self; - self = container_of(work, struct tas3004_data_t, change); - tas3004_update_device_parameters(self); -} - -static int -tas3004_output_device_change( struct tas3004_data_t *self, - int device_id, - int output_id, - int speaker_id) -{ - self->device_id=device_id; - self->output_id=output_id; - self->speaker_id=speaker_id; - - schedule_work(&self->change); - - return 0; -} - -static int -tas3004_device_ioctl( struct tas3004_data_t *self, - u_int cmd, - u_long arg) -{ - uint __user *argp = (void __user *)arg; - switch (cmd) { - case TAS_READ_EQ: - case TAS_WRITE_EQ: - return tas3004_eq_rw(self, cmd, arg); - - case TAS_READ_EQ_LIST: - case TAS_WRITE_EQ_LIST: - return tas3004_eq_list_rw(self, cmd, arg); - - case TAS_READ_EQ_FILTER_COUNT: - put_user(TAS3004_BIQUAD_FILTER_COUNT, argp); - return 0; - - case TAS_READ_EQ_CHANNEL_COUNT: - put_user(TAS3004_BIQUAD_CHANNEL_COUNT, argp); - return 0; - - case TAS_READ_DRCE: - case TAS_WRITE_DRCE: - return tas3004_drce_rw(self, cmd, arg); - - case TAS_READ_DRCE_CAPS: - put_user(TAS_DRCE_ENABLE | - TAS_DRCE_ABOVE_RATIO | - TAS_DRCE_BELOW_RATIO | - TAS_DRCE_THRESHOLD | - TAS_DRCE_ENERGY | - TAS_DRCE_ATTACK | - TAS_DRCE_DECAY, - argp); - return 0; - - case TAS_READ_DRCE_MIN: - case TAS_READ_DRCE_MAX: { - struct tas_drce_ctrl_t drce_ctrl; - const struct tas_drce_t *drce_copy; - - if (copy_from_user(&drce_ctrl, argp, - sizeof(struct tas_drce_ctrl_t))) { - return -EFAULT; - } - - if (cmd == TAS_READ_DRCE_MIN) { - drce_copy=&tas3004_drce_min; - } else { - drce_copy=&tas3004_drce_max; - } - - if (drce_ctrl.flags & TAS_DRCE_ABOVE_RATIO) { - drce_ctrl.data.above=drce_copy->above; - } - if (drce_ctrl.flags & TAS_DRCE_BELOW_RATIO) { - drce_ctrl.data.below=drce_copy->below; - } - if (drce_ctrl.flags & TAS_DRCE_THRESHOLD) { - drce_ctrl.data.threshold=drce_copy->threshold; - } - if (drce_ctrl.flags & TAS_DRCE_ENERGY) { - drce_ctrl.data.energy=drce_copy->energy; - } - if (drce_ctrl.flags & TAS_DRCE_ATTACK) { - drce_ctrl.data.attack=drce_copy->attack; - } - if (drce_ctrl.flags & TAS_DRCE_DECAY) { - drce_ctrl.data.decay=drce_copy->decay; - } - - if (copy_to_user(argp, &drce_ctrl, - sizeof(struct tas_drce_ctrl_t))) { - return -EFAULT; - } - } - } - - return -EINVAL; -} - -static int -tas3004_init_mixer(struct tas3004_data_t *self) -{ - unsigned char mcr = (1<<6)+(2<<4)+(2<<2); - - /* Make sure something answers on the i2c bus */ - if (tas3004_write_register(self, TAS3004_REG_MCR, &mcr, - WRITE_NORMAL | FORCE_WRITE) < 0) - return -1; - - tas3004_fast_load(self, 1); - - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD0); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD1); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD2); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD3); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD4); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD5); - (void)tas3004_sync_register(self,TAS3004_REG_RIGHT_BIQUAD6); - - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD0); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD1); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD2); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD3); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD4); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD5); - (void)tas3004_sync_register(self,TAS3004_REG_LEFT_BIQUAD6); - - tas3004_sync_register(self, TAS3004_REG_DRC); - - tas3004_sync_register(self, TAS3004_REG_MCR2); - - tas3004_fast_load(self, 0); - - tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, VOL_DEFAULT<<8 | VOL_DEFAULT); - tas3004_set_mixer_level(self, SOUND_MIXER_PCM, INPUT_DEFAULT<<8 | INPUT_DEFAULT); - tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0); - tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0); - - tas3004_set_mixer_level(self, SOUND_MIXER_BASS, BASS_DEFAULT); - tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, TREBLE_DEFAULT); - - tas3004_set_mixer_level(self, SOUND_MIXER_LINE,SW_INPUT_VOLUME_DEFAULT); - - return 0; -} - -static int -tas3004_uninit_mixer(struct tas3004_data_t *self) -{ - tas3004_set_mixer_level(self, SOUND_MIXER_VOLUME, 0); - tas3004_set_mixer_level(self, SOUND_MIXER_PCM, 0); - tas3004_set_mixer_level(self, SOUND_MIXER_ALTPCM, 0); - tas3004_set_mixer_level(self, SOUND_MIXER_IMIX, 0); - - tas3004_set_mixer_level(self, SOUND_MIXER_BASS, 0); - tas3004_set_mixer_level(self, SOUND_MIXER_TREBLE, 0); - - tas3004_set_mixer_level(self, SOUND_MIXER_LINE, 0); - - return 0; -} - -static int -tas3004_init(struct i2c_client *client) -{ - struct tas3004_data_t *self; - size_t sz = sizeof(*self) + (TAS3004_REG_MAX*sizeof(tas_shadow_t)); - char drce_init[] = { 0x69, 0x22, 0x9f, 0xb0, 0x60, 0xa0 }; - char mcr2 = 0; - int i, j; - - self = kzalloc(sz, GFP_KERNEL); - if (!self) - return -ENOMEM; - - self->super.client = client; - self->super.shadow = (tas_shadow_t *)(self+1); - self->output_id = TAS_OUTPUT_HEADPHONES; - - dev_set_drvdata(&client->dev, self); - - for (i = 0; i < TAS3004_BIQUAD_CHANNEL_COUNT; i++) - for (j = 0; j<TAS3004_BIQUAD_FILTER_COUNT; j++) - tas3004_write_biquad_shadow(self, i, j, - &tas3004_eq_unity); - - tas3004_write_register(self, TAS3004_REG_MCR2, &mcr2, WRITE_SHADOW); - tas3004_write_register(self, TAS3004_REG_DRC, drce_init, WRITE_SHADOW); - - INIT_WORK(&self->change, tas3004_device_change_handler); - return 0; -} - -static void -tas3004_uninit(struct tas3004_data_t *self) -{ - tas3004_uninit_mixer(self); - kfree(self); -} - - -struct tas_driver_hooks_t tas3004_hooks = { - .init = (tas_hook_init_t)tas3004_init, - .post_init = (tas_hook_post_init_t)tas3004_init_mixer, - .uninit = (tas_hook_uninit_t)tas3004_uninit, - .get_mixer_level = (tas_hook_get_mixer_level_t)tas3004_get_mixer_level, - .set_mixer_level = (tas_hook_set_mixer_level_t)tas3004_set_mixer_level, - .enter_sleep = (tas_hook_enter_sleep_t)tas3004_enter_sleep, - .leave_sleep = (tas_hook_leave_sleep_t)tas3004_leave_sleep, - .supported_mixers = (tas_hook_supported_mixers_t)tas3004_supported_mixers, - .mixer_is_stereo = (tas_hook_mixer_is_stereo_t)tas3004_mixer_is_stereo, - .stereo_mixers = (tas_hook_stereo_mixers_t)tas3004_stereo_mixers, - .output_device_change = (tas_hook_output_device_change_t)tas3004_output_device_change, - .device_ioctl = (tas_hook_device_ioctl_t)tas3004_device_ioctl -}; |