📄 tvaudio.c
字号:
.checkit = tda9874a_checkit,
.initialize = tda9874a_initialize,
.insmodopt = &tda9874a,
.addr_lo = I2C_TDA9874 >> 1,
.addr_hi = I2C_TDA9874 >> 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_VL,
.rightreg = TDA9855_VR,
.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_VL,
.rightreg = TDA8425_VR,
.bassreg = TDA8425_BA,
.treblereg = TDA8425_TR,
.volfunc = tda8425_shift10,
.bassfunc = tda8425_shift12,
.treblefunc = tda8425_shift12,
.inputreg = TDA8425_S1,
.inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
.inputmute = TDA8425_S1_OFF,
.setmode = tda8425_setmode,
.initialize = tda8425_initialize,
},
{
.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 = "ta8874z",
.id = -1,
//.id = I2C_DRIVERID_TA8874Z,
.checkit = ta8874z_checkit,
.insmodopt = &ta8874z,
.addr_lo = I2C_TDA9840 >> 1,
.addr_hi = I2C_TDA9840 >> 1,
.registers = 2,
.getmode = ta8874z_getmode,
.setmode = ta8874z_setmode,
.checkmode = generic_checkmode,
.init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
},
{ .name = NULL } /* EOF */
};
/* ---------------------------------------------------------------------- */
/* i2c registration */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
static int chip_attach(struct i2c_adapter *adap, int addr, int kind)
#else
static int chip_attach(struct i2c_adapter *adap, int addr,
unsigned short flags, int kind)
#endif
{
struct CHIPSTATE *chip;
struct CHIPDESC *desc;
int rc;
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;
i2c_set_clientdata(&chip->c, chip);
/* find description for the chip */
dprintk("tvaudio: chip found @ i2c-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;
}
printk("tvaudio: found %s @ 0x%x\n", desc->name, addr<<1);
dprintk("tvaudio: matches:%s%s%s.\n",
(desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
(desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
(desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
/* fill required data structures */
strcpy(i2c_clientname(&chip->c),desc->name);
chip->type = desc-chiplist;
chip->shadow.count = desc->registers+1;
chip->prevmode = -1;
/* register */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#endif
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 : 65535;
chip->right = desc->rightinit ? desc->rightinit : 65535;
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;
init_timer(&chip->wt);
chip->wt.function = chip_thread_wake;
chip->wt.data = (unsigned long)chip;
init_waitqueue_head(&chip->wq);
rc = kernel_thread(chip_thread,(void *)chip,0);
if (rc < 0)
printk(KERN_WARNING "%s: kernel_thread() failed\n",
i2c_clientname(&chip->c));
else
down(&sem);
chip->notify = NULL;
wake_up_interruptible(&chip->wq);
}
return 0;
}
static int chip_probe(struct i2c_adapter *adap)
{
#ifdef I2C_ADAP_CLASS_TV_ANALOG
if (adap->class & I2C_ADAP_CLASS_TV_ANALOG)
return i2c_probe(adap, &addr_data, chip_attach);
#else
switch (adap->id) {
case I2C_ALGO_BIT | I2C_HW_B_BT848:
case I2C_ALGO_BIT | I2C_HW_B_RIVA:
case I2C_ALGO_SAA7134:
return i2c_probe(adap, &addr_data, chip_attach);
}
#endif
return 0;
}
static int chip_detach(struct i2c_client *client)
{
struct CHIPSTATE *chip = i2c_get_clientdata(client);
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);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#endif
return 0;
}
/* ---------------------------------------------------------------------- */
/* video4linux interface */
static int chip_command(struct i2c_client *client,
unsigned int cmd, void *arg)
{
__u16 *sarg = arg;
struct CHIPSTATE *chip = i2c_get_clientdata(client);
struct CHIPDESC *desc = chiplist + chip->type;
dprintk("%s: chip_command 0x%x\n",i2c_clientname(&chip->c),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;
case AUDC_SET_RADIO:
dprintk(KERN_DEBUG "tvaudio: AUDC_SET_RADIO\n");
chip->norm = VIDEO_MODE_RADIO;
chip->watch_stereo = 0;
/* del_timer(&chip->wt); */
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 (chip->norm != VIDEO_MODE_RADIO) {
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,(__u16)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->watch_stereo = 0;
/* del_timer(&chip->wt); */
chip->mode = va->mode;
desc->setmode(chip,va->mode);
}
break;
}
case VIDIOCSCHAN:
{
struct video_channel *vc = arg;
dprintk(KERN_DEBUG "tvaudio: VIDIOCSCHAN\n");
chip->norm = vc->norm;
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 = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "generic i2c audio driver",
.id = I2C_DRIVERID_TVAUDIO,
.flags = I2C_DF_NOTIFY,
.attach_adapter = chip_probe,
.detach_client = chip_detach,
.command = chip_command,
};
static struct i2c_client client_template =
{
I2C_DEVNAME("(unset)"),
.flags = I2C_CLIENT_ALLOW_USE,
.driver = &driver,
};
static 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;
}
static 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 + -