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

📄 tas3004.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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;};#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 intsearch(	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 inttime_index(unsigned short time){	return SEARCH(time, time_constants);}static inline intabove_threshold_compression_index(unsigned short ratio){	return SEARCH(ratio, above_threshold_compression_ratio);}static inline intabove_threshold_expansion_index(unsigned short ratio){	return SEARCH(ratio, above_threshold_expansion_ratio);}static inline intbelow_threshold_compression_index(unsigned short ratio){	return SEARCH(ratio, below_threshold_compression_ratio);}static inline intbelow_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 intregister_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 inttas3004_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 inttas3004_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 inttas3004_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 inttas3004_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 uinttas3004_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 inttas3004_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 uinttas3004_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 inttas3004_get_mixer_level(struct tas3004_data_t *self, int mixer, uint *level){	if (!self)		return -1;	*level = self->super.mixer[mixer];	return 0;}static inttas3004_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 inttas3004_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 inttas3004_enter_sleep(struct tas3004_data_t *self){	if (!self)		return -1; 	return 0;}static inttas3004_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 inttas3004_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;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -