⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tas3004.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
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 + -