📄 tas3004.c
字号:
static inttas3004_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 inttas3004_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 inttas3004_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 inttas3004_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 inttas3004_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 inttas3004_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 inttas3004_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 voidtas3004_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 voidtas3004_device_change_handler(void *self){ if (!self) return; tas3004_update_device_parameters((struct tas3004_data_t *)self);}static struct work_struct device_change;static inttas3004_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(&device_change); return 0;}static inttas3004_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 inttas3004_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 inttas3004_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 inttas3004_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 = kmalloc(sz, GFP_KERNEL); if (!self) return -ENOMEM; memset(self, 0, sz); 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(&device_change, tas3004_device_change_handler, self); 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};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -