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

📄 tas3001c.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>: * *   TODO: *   ----- *   * Enable control over input line 2 (is this connected?) *   * Implement sleep support (at least mute everything and *   * set gains to minimum during sleep) *   * Look into some of Darwin's tweaks regarding the mute *   * lines (delays & different behaviour on some HW) * */#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/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 "tas3001c.h"#include "tas_ioctl.h"#define TAS3001C_BIQUAD_FILTER_COUNT  6#define TAS3001C_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 tas3001c_data_t {	struct tas_data_t super;	int device_id;	int output_id;	int speaker_id;	struct tas_drce_t drce_state;};static const union tas_biquad_ttas3001c_eq_unity={	.buf = { 0x100000, 0x000000, 0x000000, 0x000000, 0x000000 }};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 tas3001c_reg_t r){	switch(r) {	case TAS3001C_REG_MCR: 	case TAS3001C_REG_TREBLE:	case TAS3001C_REG_BASS:		return 1;	case TAS3001C_REG_DRC:		return 2;	case TAS3001C_REG_MIXER1:	case TAS3001C_REG_MIXER2:		return 3;	case TAS3001C_REG_VOLUME:		return 6;	case TAS3001C_REG_LEFT_BIQUAD0:	case TAS3001C_REG_LEFT_BIQUAD1:	case TAS3001C_REG_LEFT_BIQUAD2:	case TAS3001C_REG_LEFT_BIQUAD3:	case TAS3001C_REG_LEFT_BIQUAD4:	case TAS3001C_REG_LEFT_BIQUAD5:	case TAS3001C_REG_LEFT_BIQUAD6:	case TAS3001C_REG_RIGHT_BIQUAD0:	case TAS3001C_REG_RIGHT_BIQUAD1:	case TAS3001C_REG_RIGHT_BIQUAD2:	case TAS3001C_REG_RIGHT_BIQUAD3:	case TAS3001C_REG_RIGHT_BIQUAD4:	case TAS3001C_REG_RIGHT_BIQUAD5:	case TAS3001C_REG_RIGHT_BIQUAD6:		return 15;	default:		return 0;	}}static inttas3001c_write_register(	struct tas3001c_data_t *self,				enum tas3001c_reg_t reg_num,				char *data,				uint write_mode){	if (reg_num==TAS3001C_REG_MCR ||	    reg_num==TAS3001C_REG_BASS ||	    reg_num==TAS3001C_REG_TREBLE) {		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 inttas3001c_sync_register(	struct tas3001c_data_t *self,			enum tas3001c_reg_t reg_num){	if (reg_num==TAS3001C_REG_MCR ||	    reg_num==TAS3001C_REG_BASS ||	    reg_num==TAS3001C_REG_TREBLE) {		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 inttas3001c_read_register(	struct tas3001c_data_t *self,			enum tas3001c_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 inttas3001c_fast_load(struct tas3001c_data_t *self, int fast){	if (fast)		self->super.shadow[TAS3001C_REG_MCR][0] |= 0x80;	else		self->super.shadow[TAS3001C_REG_MCR][0] &= 0x7f;	return tas3001c_sync_register(self,TAS3001C_REG_MCR);}static uinttas3001c_supported_mixers(struct tas3001c_data_t *self){	return SOUND_MASK_VOLUME |		SOUND_MASK_PCM |		SOUND_MASK_ALTPCM |		SOUND_MASK_TREBLE |		SOUND_MASK_BASS;}static inttas3001c_mixer_is_stereo(struct tas3001c_data_t *self,int mixer){	switch(mixer) {	case SOUND_MIXER_VOLUME:		return 1;	default:		return 0;	}}static uinttas3001c_stereo_mixers(struct tas3001c_data_t *self){	uint r=tas3001c_supported_mixers(self);	uint i;		for (i=1; i<SOUND_MIXER_NRDEVICES; i++)		if (r&(1<<i) && !tas3001c_mixer_is_stereo(self,i))			r &= ~(1<<i);	return r;}static inttas3001c_get_mixer_level(struct tas3001c_data_t *self,int mixer,uint *level){	if (!self)		return -1;			*level=self->super.mixer[mixer];		return 0;}static inttas3001c_set_mixer_level(struct tas3001c_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 (!tas3001c_mixer_is_stereo(self,mixer))		level = tas_mono_to_stereo(level);	switch(mixer) {	case SOUND_MIXER_VOLUME:		temp = tas3001c_gain.master[level&0xff];		shadow[TAS3001C_REG_VOLUME][0] = (temp >> 16) & 0xff;		shadow[TAS3001C_REG_VOLUME][1] = (temp >> 8)  & 0xff;		shadow[TAS3001C_REG_VOLUME][2] = (temp >> 0)  & 0xff;		temp = tas3001c_gain.master[(level>>8)&0xff];		shadow[TAS3001C_REG_VOLUME][3] = (temp >> 16) & 0xff;		shadow[TAS3001C_REG_VOLUME][4] = (temp >> 8)  & 0xff;		shadow[TAS3001C_REG_VOLUME][5] = (temp >> 0)  & 0xff;		rc = tas3001c_sync_register(self,TAS3001C_REG_VOLUME);		break;	case SOUND_MIXER_ALTPCM:		/* tas3001c_fast_load(self, 1); */		level = tas_mono_to_stereo(level);		temp = tas3001c_gain.mixer[level&0xff];		shadow[TAS3001C_REG_MIXER2][offset+0] = (temp >> 16) & 0xff;		shadow[TAS3001C_REG_MIXER2][offset+1] = (temp >> 8)  & 0xff;		shadow[TAS3001C_REG_MIXER2][offset+2] = (temp >> 0)  & 0xff;		rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER2);		/* tas3001c_fast_load(self, 0); */		break;	case SOUND_MIXER_PCM:		/* tas3001c_fast_load(self, 1); */		level = tas_mono_to_stereo(level);		temp = tas3001c_gain.mixer[level&0xff];		shadow[TAS3001C_REG_MIXER1][offset+0] = (temp >> 16) & 0xff;		shadow[TAS3001C_REG_MIXER1][offset+1] = (temp >> 8)  & 0xff;		shadow[TAS3001C_REG_MIXER1][offset+2] = (temp >> 0)  & 0xff;		rc = tas3001c_sync_register(self,TAS3001C_REG_MIXER1);		/* tas3001c_fast_load(self, 0); */		break;	case SOUND_MIXER_TREBLE:		temp = tas3001c_gain.treble[level&0xff];		shadow[TAS3001C_REG_TREBLE][0]=temp&0xff;		rc = tas3001c_sync_register(self,TAS3001C_REG_TREBLE);		break;	case SOUND_MIXER_BASS:		temp = tas3001c_gain.bass[level&0xff];		shadow[TAS3001C_REG_BASS][0]=temp&0xff;		rc = tas3001c_sync_register(self,TAS3001C_REG_BASS);		break;	default:		rc = -1;		break;	}	if (rc < 0)		return rc;	self->super.mixer[mixer]=level;	return 0;}static inttas3001c_leave_sleep(struct tas3001c_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 (tas3001c_write_register(self, TAS3001C_REG_MCR, &mcr,	    WRITE_NORMAL|FORCE_WRITE) < 0)	    	return -1;	tas3001c_fast_load(self, 1);	(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD0);	(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD1);	(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD2);	(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD3);	(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD4);	(void)tas3001c_sync_register(self,TAS3001C_REG_RIGHT_BIQUAD5);	(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD0);	(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD1);	(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD2);	(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD3);	(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD4);	(void)tas3001c_sync_register(self,TAS3001C_REG_LEFT_BIQUAD5);	tas3001c_fast_load(self, 0);	(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);	(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);	(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);	(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);	(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);	return 0;}static inttas3001c_enter_sleep(struct tas3001c_data_t *self){	/* Stub for now, but I have the details on low-power mode */	if (!self)		return -1; 	return 0;}static inttas3001c_sync_biquad(	struct tas3001c_data_t *self,			u_int channel,			u_int filter){	enum tas3001c_reg_t reg;	if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||	    filter  >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;	reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_REG_LEFT_BIQUAD0 ) + filter;	return tas3001c_sync_register(self,reg);}static inttas3001c_write_biquad_shadow(	struct tas3001c_data_t *self,				u_int channel,				u_int filter,				const union tas_biquad_t *biquad){	tas_shadow_t *shadow=self->super.shadow;	enum tas3001c_reg_t reg;	if (channel >= TAS3001C_BIQUAD_CHANNEL_COUNT ||	    filter  >= TAS3001C_BIQUAD_FILTER_COUNT) return -EINVAL;	reg=( channel ? TAS3001C_REG_RIGHT_BIQUAD0 : TAS3001C_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 inttas3001c_write_biquad(	struct tas3001c_data_t *self,			u_int channel,			u_int filter,			const union tas_biquad_t *biquad){	int rc;	rc=tas3001c_write_biquad_shadow(self, channel, filter, biquad);	if (rc < 0) return rc;	return tas3001c_sync_biquad(self, channel, filter);}static inttas3001c_write_biquad_list(	struct tas3001c_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) tas3001c_fast_load(self,1);	for (i=0; i<filter_count; i++) {		rc=tas3001c_write_biquad(self,					 biquads[i].channel,					 biquads[i].filter,					 &biquads[i].data);		if (rc < 0) break;	}	if (flags & TAS_BIQUAD_FAST_LOAD) {		tas3001c_fast_load(self,0);		(void)tas3001c_sync_register(self,TAS3001C_REG_BASS);		(void)tas3001c_sync_register(self,TAS3001C_REG_TREBLE);		(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER1);		(void)tas3001c_sync_register(self,TAS3001C_REG_MIXER2);		(void)tas3001c_sync_register(self,TAS3001C_REG_VOLUME);	}	return rc;}static inttas3001c_read_biquad(	struct tas3001c_data_t *self,

⌨️ 快捷键说明

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