📄 tvaudio.c
字号:
/* ---------------------------------------------------------------------- *//* audio chip descriptions - defines+functions for tda8425 */#define TDA8425_VL 0x00 /* volume left */#define TDA8425_VR 0x01 /* volume right */#define TDA8425_BA 0x02 /* bass */#define TDA8425_TR 0x03 /* treble */#define TDA8425_S1 0x08 /* switch functions */ /* values for those registers: */#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */#define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */int tda8425_shift10(int val) { return val >> 10 | 0xc0; }int tda8425_shift12(int val) { return val >> 12 | 0xf0; }/* ---------------------------------------------------------------------- *//* audio chip descriptions - defines+functions for pic16c54 (PV951) *//* the registers of 16C54, I2C sub address. */#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */#define PIC16C54_REG_MISC 0x02/* bit definition of the RESET register, I2C data. */#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ /* code of remote controller */#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner *//* ---------------------------------------------------------------------- *//* audio chip descriptions - struct CHIPDESC *//* insmod options to enable/disable individual audio chips */int tda8425 = 1;int tda9840 = 1;int tda9850 = 1;int tda9855 = 1;int tda9873 = 1;int tda9874a = 1;int tea6300 = 0;int tea6420 = 1;int pic16c54 = 1;MODULE_PARM(tda8425,"i");MODULE_PARM(tda9840,"i");MODULE_PARM(tda9850,"i");MODULE_PARM(tda9855,"i");MODULE_PARM(tda9873,"i");MODULE_PARM(tda9874a,"i");MODULE_PARM(tea6300,"i");MODULE_PARM(tea6420,"i");MODULE_PARM(pic16c54,"i");static struct CHIPDESC chiplist[] = { { name: "tda9840", id: I2C_DRIVERID_TDA9840, insmodopt: &tda9840, addr_lo: I2C_TDA9840 >> 1, addr_hi: I2C_TDA9840 >> 1, registers: 5, getmode: tda9840_getmode, setmode: tda9840_setmode, checkmode: generic_checkmode, init: { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN /* ,TDA9840_SW, TDA9840_MONO */} } }, { name: "tda9873h", id: I2C_DRIVERID_TDA9873, checkit: tda9873_checkit, insmodopt: &tda9873, addr_lo: I2C_TDA985x_L >> 1, addr_hi: I2C_TDA985x_H >> 1, registers: 3, flags: CHIP_HAS_INPUTSEL, getmode: tda9873_getmode, setmode: tda9873_setmode, checkmode: generic_checkmode, init: { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, inputreg: TDA9873_SW, inputmute: TDA9873_MUTE | TDA9873_AUTOMUTE, inputmap: {0xa0, 0xa2, 0xa0, 0xa0, 0xc0}, inputmask: TDA9873_INP_MASK | TDA9873_MUTE | TDA9873_AUTOMUTE }, { name: "tda9874a", id: I2C_DRIVERID_TDA9874A, checkit: tda9874a_checkit, initialize: tda9874a_initialize, insmodopt: &tda9874a, addr_lo: I2C_TDA9874A >> 1, addr_hi: I2C_TDA9874A >> 1, getmode: tda9874a_getmode, setmode: tda9874a_setmode, checkmode: generic_checkmode, }, { name: "tda9850", id: I2C_DRIVERID_TDA9850, insmodopt: &tda9850, addr_lo: I2C_TDA985x_L >> 1, addr_hi: I2C_TDA985x_H >> 1, registers: 11, getmode: tda985x_getmode, setmode: tda985x_setmode, init: { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } }, { name: "tda9855", id: I2C_DRIVERID_TDA9855, insmodopt: &tda9855, addr_lo: I2C_TDA985x_L >> 1, addr_hi: I2C_TDA985x_H >> 1, registers: 11, flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, leftreg: TDA9855_VR, rightreg: TDA9855_VL, bassreg: TDA9855_BA, treblereg: TDA9855_TR, volfunc: tda9855_volume, bassfunc: tda9855_bass, treblefunc: tda9855_treble, getmode: tda985x_getmode, setmode: tda985x_setmode, init: { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, 0x07, 0x10, 0x10, 0x03 }} }, { name: "tea6300", id: I2C_DRIVERID_TEA6300, insmodopt: &tea6300, addr_lo: I2C_TEA6300 >> 1, addr_hi: I2C_TEA6300 >> 1, registers: 6, flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, leftreg: TEA6300_VR, rightreg: TEA6300_VL, bassreg: TEA6300_BA, treblereg: TEA6300_TR, volfunc: tea6300_shift10, bassfunc: tea6300_shift12, treblefunc: tea6300_shift12, inputreg: TEA6300_S, inputmap: { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, inputmute: TEA6300_S_GMU, }, { name: "tea6420", id: I2C_DRIVERID_TEA6420, insmodopt: &tea6420, addr_lo: I2C_TEA6420 >> 1, addr_hi: I2C_TEA6420 >> 1, registers: 1, flags: CHIP_HAS_INPUTSEL, inputreg: -1, inputmap: { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, inputmute: TEA6300_S_GMU, }, { name: "tda8425", id: I2C_DRIVERID_TDA8425, insmodopt: &tda8425, addr_lo: I2C_TDA8425 >> 1, addr_hi: I2C_TDA8425 >> 1, registers: 9, flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, leftreg: TDA8425_VR, rightreg: TDA8425_VL, bassreg: TDA8425_BA, treblereg: TDA8425_TR, volfunc: tda8425_shift10, bassfunc: tda8425_shift12, treblefunc: tda8425_shift12, inputreg: TDA8425_S1, inputmap: { TDA8425_S1_ON, TDA8425_S1_ON, TDA8425_S1_ON }, inputmute: TDA8425_S1_OFF, }, { name: "pic16c54 (PV951)", id: I2C_DRIVERID_PIC16C54_PV951, insmodopt: &pic16c54, addr_lo: I2C_PIC16C54 >> 1, addr_hi: I2C_PIC16C54>> 1, registers: 2, flags: CHIP_HAS_INPUTSEL, inputreg: PIC16C54_REG_MISC, inputmap: {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, PIC16C54_MISC_SND_MUTE,PIC16C54_MISC_SND_MUTE, PIC16C54_MISC_SND_NOTMUTE}, inputmute: PIC16C54_MISC_SND_MUTE, }, { name: NULL } /* EOF */};/* ---------------------------------------------------------------------- *//* i2c registration */static int chip_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind){ struct CHIPSTATE *chip; struct CHIPDESC *desc; chip = kmalloc(sizeof(*chip),GFP_KERNEL); if (!chip) return -ENOMEM; memset(chip,0,sizeof(*chip)); memcpy(&chip->c,&client_template,sizeof(struct i2c_client)); chip->c.adapter = adap; chip->c.addr = addr; chip->c.data = chip; /* find description for the chip */ dprintk("tvaudio: chip @ addr=0x%x\n", addr<<1); for (desc = chiplist; desc->name != NULL; desc++) { if (0 == *(desc->insmodopt)) continue; if (addr < desc->addr_lo || addr > desc->addr_hi) continue; if (desc->checkit && !desc->checkit(chip)) continue; break; } if (desc->name == NULL) { dprintk("tvaudio: no matching chip description found\n"); return -EIO; } dprintk("tvaudio: %s matches:%s%s%s\n",desc->name, (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); /* fill required data structures */ strcpy(chip->c.name,desc->name); chip->type = desc-chiplist; chip->shadow.count = desc->registers+1; chip->prevmode = -1; /* register */ MOD_INC_USE_COUNT; i2c_attach_client(&chip->c); /* initialization */ if (desc->initialize != NULL) desc->initialize(chip); else chip_cmd(chip,"init",&desc->init); if (desc->flags & CHIP_HAS_VOLUME) { chip->left = desc->leftinit ? desc->leftinit : 65536; chip->right = desc->rightinit ? desc->rightinit : 65536; chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); } if (desc->flags & CHIP_HAS_BASSTREBLE) { chip->treble = desc->trebleinit ? desc->trebleinit : 32768; chip->bass = desc->bassinit ? desc->bassinit : 32768; chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); } if (desc->checkmode) { /* start async thread */ DECLARE_MUTEX_LOCKED(sem); chip->notify = &sem; chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; init_waitqueue_head(&chip->wq); kernel_thread(chip_thread,(void *)chip,0); down(&sem); chip->notify = NULL; wake_up_interruptible(&chip->wq); } return 0;}static int chip_probe(struct i2c_adapter *adap){ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) return i2c_probe(adap, &addr_data, chip_attach); return 0;}static int chip_detach(struct i2c_client *client){ struct CHIPSTATE *chip = client->data; del_timer(&chip->wt); if (NULL != chip->thread) { /* shutdown async thread */ DECLARE_MUTEX_LOCKED(sem); chip->notify = &sem; chip->done = 1; wake_up_interruptible(&chip->wq); down(&sem); chip->notify = NULL; } i2c_detach_client(&chip->c); kfree(chip); MOD_DEC_USE_COUNT; return 0;}/* ---------------------------------------------------------------------- *//* video4linux interface */static int chip_command(struct i2c_client *client, unsigned int cmd, void *arg){ __u16 *sarg = arg; struct CHIPSTATE *chip = client->data; struct CHIPDESC *desc = chiplist + chip->type; dprintk("%s: chip_command 0x%x\n",chip->c.name,cmd); switch (cmd) { case AUDC_SET_INPUT: if (desc->flags & CHIP_HAS_INPUTSEL) { if (*sarg & 0x80) chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); else chip_write_masked(chip,desc->inputreg,desc->inputmap[*sarg],desc->inputmask); } break; /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ case VIDIOCGAUDIO: { struct video_audio *va = arg; if (desc->flags & CHIP_HAS_VOLUME) { va->flags |= VIDEO_AUDIO_VOLUME; va->volume = MAX(chip->left,chip->right); va->balance = (32768*MIN(chip->left,chip->right))/ (va->volume ? va->volume : 1); } if (desc->flags & CHIP_HAS_BASSTREBLE) { va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; va->bass = chip->bass; va->treble = chip->treble; } if (desc->getmode) va->mode = desc->getmode(chip); else va->mode = VIDEO_SOUND_MONO; break; } case VIDIOCSAUDIO: { struct video_audio *va = arg; if (desc->flags & CHIP_HAS_VOLUME) { chip->left = (MIN(65536 - va->balance,32768) * va->volume) / 32768; chip->right = (MIN(va->balance,32768) * va->volume) / 32768; chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); } if (desc->flags & CHIP_HAS_BASSTREBLE) { chip->bass = va->bass; chip->treble = va->treble; chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); } if (desc->setmode && va->mode) { chip->mode = va->mode; desc->setmode(chip,va->mode); } break; } case VIDIOCSFREQ: { chip->mode = 0; /* automatic */ if (desc->checkmode) { desc->setmode(chip,VIDEO_SOUND_MONO); if (chip->prevmode != VIDEO_SOUND_MONO) chip->prevmode = -1; /* reset previous mode */ mod_timer(&chip->wt, jiffies+2*HZ); /* the thread will call checkmode() later */ } } } return 0;}static struct i2c_driver driver = { name: "generic i2c audio driver", id: I2C_DRIVERID_TVAUDIO, /* FIXME */ flags: I2C_DF_NOTIFY, attach_adapter: chip_probe, detach_client: chip_detach, command: chip_command,};static struct i2c_client client_template ={ name: "(unset)", driver: &driver,};int audiochip_init_module(void){ struct CHIPDESC *desc; printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); printk(KERN_INFO "tvaudio: known chips: "); for (desc = chiplist; desc->name != NULL; desc++) printk("%s%s", (desc == chiplist) ? "" : ",",desc->name); printk("\n"); i2c_add_driver(&driver); return 0;}void audiochip_cleanup_module(void){ i2c_del_driver(&driver);}module_init(audiochip_init_module);module_exit(audiochip_cleanup_module);/* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -