📄 tas3001c.c
字号:
/* * 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 + -