📄 tvaudio.c
字号:
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 = "tea6320", .id = I2C_DRIVERID_TEA6300, .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_TEA6300 >> 1, .addr_hi = I2C_TEA6300 >> 1, .registers = 8, .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, .leftreg = TEA6320_V, .rightreg = TEA6320_V, .bassreg = TEA6320_BA, .treblereg = TEA6320_TR, .volfunc = tea6320_volume, .bassfunc = tea6320_shift11, .treblefunc = tea6320_shift11, .inputreg = TEA6320_S, .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, .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_PV9, .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 */static int chip_attach(struct i2c_adapter *adap, int addr, int kind){ struct CHIPSTATE *chip; struct CHIPDESC *desc; chip = kzalloc(sizeof(*chip),GFP_KERNEL); if (!chip) return -ENOMEM; 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 */ v4l_dbg(1, debug, &chip->c, "chip found @ 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) { v4l_dbg(1, debug, &chip->c, "no matching chip description found\n"); return -EIO; } v4l_info(&chip->c, "%s found @ 0x%x (%s)\n", desc->name, addr<<1, adap->name); if (desc->flags) { v4l_dbg(1, debug, &chip->c, "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(chip->c.name, desc->name); chip->type = desc-chiplist; chip->shadow.count = desc->registers+1; chip->prevmode = -1; /* register */ 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)); } chip->tpid = -1; if (desc->checkmode) { /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; init_waitqueue_head(&chip->wq); init_completion(&chip->texit); chip->tpid = kernel_thread(chip_thread,(void *)chip,0); if (chip->tpid < 0) v4l_warn(&chip->c, "%s: kernel_thread() failed\n", chip->c.name); wake_up_interruptible(&chip->wq); } return 0;}static int chip_probe(struct i2c_adapter *adap){ /* don't attach on saa7146 based cards, because dedicated drivers are used */ if ((adap->id == I2C_HW_SAA7146)) return 0; if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, chip_attach); return 0;}static int chip_detach(struct i2c_client *client){ struct CHIPSTATE *chip = i2c_get_clientdata(client); del_timer_sync(&chip->wt); if (chip->tpid >= 0) { /* shutdown async thread */ chip->done = 1; wake_up_interruptible(&chip->wq); wait_for_completion(&chip->texit); } i2c_detach_client(&chip->c); kfree(chip); 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; v4l_dbg(1, debug, &chip->c, "%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; case AUDC_SET_RADIO: chip->radio = 1; 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); if (va->volume) va->balance = (32768*min(chip->left,chip->right))/ va->volume; else va->balance = 32768; } if (desc->flags & CHIP_HAS_BASSTREBLE) { va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; va->bass = chip->bass; va->treble = chip->treble; } if (!chip->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 VIDIOC_S_TUNER: { struct v4l2_tuner *vt = arg; int mode = 0; switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: mode = VIDEO_SOUND_MONO; break; case V4L2_TUNER_MODE_STEREO: mode = VIDEO_SOUND_STEREO; break; case V4L2_TUNER_MODE_LANG1: mode = VIDEO_SOUND_LANG1; break; case V4L2_TUNER_MODE_LANG2: mode = VIDEO_SOUND_LANG2; break; default: break; } if (desc->setmode && mode) { chip->watch_stereo = 0; /* del_timer(&chip->wt); */ chip->mode = mode; desc->setmode(chip, mode); } break; } case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; int mode = VIDEO_SOUND_MONO; if (chip->radio) break; vt->audmode = 0; vt->rxsubchans = 0; vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; if (desc->getmode) mode = desc->getmode(chip); if (mode & VIDEO_SOUND_MONO) vt->rxsubchans |= V4L2_TUNER_SUB_MONO; if (mode & VIDEO_SOUND_STEREO) vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; if (mode & VIDEO_SOUND_LANG1) vt->rxsubchans |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; mode = chip->mode; if (mode & VIDEO_SOUND_MONO) vt->audmode = V4L2_TUNER_MODE_MONO; if (mode & VIDEO_SOUND_STEREO) vt->audmode = V4L2_TUNER_MODE_STEREO; if (mode & VIDEO_SOUND_LANG1) vt->audmode = V4L2_TUNER_MODE_LANG1; if (mode & VIDEO_SOUND_LANG2) vt->audmode = V4L2_TUNER_MODE_LANG2; break; } case VIDIOCSCHAN: case VIDIOC_S_STD: chip->radio = 0; break; case VIDIOCSFREQ: case VIDIOC_S_FREQUENCY: 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 */ } break; } return 0;}static struct i2c_driver driver = { .driver = { .name = "tvaudio", }, .id = I2C_DRIVERID_TVAUDIO, .attach_adapter = chip_probe, .detach_client = chip_detach, .command = chip_command,};static struct i2c_client client_template ={ .name = "(unset)", .driver = &driver,};static int __init audiochip_init_module(void){ struct CHIPDESC *desc; if (debug) { 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"); } return i2c_add_driver(&driver);}static void __exit 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 + -