📄 tvaudio.c
字号:
}, { .name = "tda8425", .insmodopt = &tda8425, .addr_lo = I2C_ADDR_TDA8425 >> 1, .addr_hi = I2C_ADDR_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)", .insmodopt = &pic16c54, .addr_lo = I2C_ADDR_PIC16C54 >> 1, .addr_hi = I2C_ADDR_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}, .inputmute = PIC16C54_MISC_SND_MUTE, }, { .name = "ta8874z", .checkit = ta8874z_checkit, .insmodopt = &ta8874z, .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_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_probe(struct i2c_client *client, const struct i2c_device_id *id){ struct CHIPSTATE *chip; 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"); } chip = kzalloc(sizeof(*chip),GFP_KERNEL); if (!chip) return -ENOMEM; chip->c = client; i2c_set_clientdata(client, chip); /* find description for the chip */ v4l_dbg(1, debug, client, "chip found @ 0x%x\n", client->addr<<1); for (desc = chiplist; desc->name != NULL; desc++) { if (0 == *(desc->insmodopt)) continue; if (client->addr < desc->addr_lo || client->addr > desc->addr_hi) continue; if (desc->checkit && !desc->checkit(chip)) continue; break; } if (desc->name == NULL) { v4l_dbg(1, debug, client, "no matching chip description found\n"); return -EIO; } v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); if (desc->flags) { v4l_dbg(1, debug, client, "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 */#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) strcpy(client->name, desc->name);#else if (!id) strlcpy(client->name, desc->name, I2C_NAME_SIZE);#endif chip->type = desc-chiplist; chip->shadow.count = desc->registers+1; chip->prevmode = -1; chip->audmode = V4L2_TUNER_MODE_LANG1; /* 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->thread = NULL; if (desc->checkmode) { /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; chip->thread = kthread_run(chip_thread, chip, chip->c->name); if (IS_ERR(chip->thread)) { v4l_warn(chip->c, "%s: failed to create kthread\n", chip->c->name); chip->thread = NULL; } } return 0;}static int chip_remove(struct i2c_client *client){ struct CHIPSTATE *chip = i2c_get_clientdata(client); del_timer_sync(&chip->wt); if (chip->thread) { /* shutdown async thread */ kthread_stop(chip->thread); chip->thread = NULL; } kfree(chip); return 0;}static int tvaudio_get_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl){ struct CHIPDESC *desc = chiplist + chip->type; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value=chip->muted; return 0; case V4L2_CID_AUDIO_VOLUME: if (!(desc->flags & CHIP_HAS_VOLUME)) break; ctrl->value = max(chip->left,chip->right); return 0; case V4L2_CID_AUDIO_BALANCE: { int volume; if (!(desc->flags & CHIP_HAS_VOLUME)) break; volume = max(chip->left,chip->right); if (volume) ctrl->value=(32768*min(chip->left,chip->right))/volume; else ctrl->value=32768; return 0; } case V4L2_CID_AUDIO_BASS: if (desc->flags & CHIP_HAS_BASSTREBLE) break; ctrl->value = chip->bass; return 0; case V4L2_CID_AUDIO_TREBLE: if (desc->flags & CHIP_HAS_BASSTREBLE) return -EINVAL; ctrl->value = chip->treble; return 0; } return -EINVAL;}static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl){ struct CHIPDESC *desc = chiplist + chip->type; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: if (ctrl->value < 0 || ctrl->value >= 2) return -ERANGE; chip->muted = ctrl->value; if (chip->muted) chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); else chip_write_masked(chip,desc->inputreg, desc->inputmap[chip->input],desc->inputmask); return 0; case V4L2_CID_AUDIO_VOLUME: { int volume,balance; if (!(desc->flags & CHIP_HAS_VOLUME)) break; volume = max(chip->left,chip->right); if (volume) balance=(32768*min(chip->left,chip->right))/volume; else balance=32768; volume=ctrl->value; chip->left = (min(65536 - balance,32768) * volume) / 32768; chip->right = (min(balance,volume *(__u16)32768)) / 32768; chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); return 0; } case V4L2_CID_AUDIO_BALANCE: { int volume, balance; if (!(desc->flags & CHIP_HAS_VOLUME)) break; volume = max(chip->left,chip->right); balance = ctrl->value; chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); return 0; } case V4L2_CID_AUDIO_BASS: if (desc->flags & CHIP_HAS_BASSTREBLE) break; chip->bass = ctrl->value; chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); return 0; case V4L2_CID_AUDIO_TREBLE: if (desc->flags & CHIP_HAS_BASSTREBLE) return -EINVAL; chip->treble = ctrl->value; chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); return 0; } return -EINVAL;}/* ---------------------------------------------------------------------- *//* video4linux interface */static int chip_command(struct i2c_client *client, unsigned int cmd, void *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_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 VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *qc = arg; switch (qc->id) { case V4L2_CID_AUDIO_MUTE: break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BALANCE: if (!(desc->flags & CHIP_HAS_VOLUME)) return -EINVAL; break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: if (desc->flags & CHIP_HAS_BASSTREBLE) return -EINVAL; break; default: return -EINVAL; } return v4l2_ctrl_query_fill_std(qc); } case VIDIOC_S_CTRL: return tvaudio_set_ctrl(chip, arg); case VIDIOC_G_CTRL: return tvaudio_get_ctrl(chip, arg); case VIDIOC_INT_G_AUDIO_ROUTING: { struct v4l2_routing *rt = arg; rt->input = chip->input; rt->output = 0; break; } case VIDIOC_INT_S_AUDIO_ROUTING: { struct v4l2_routing *rt = arg; if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) return -EINVAL; /* There are four inputs: tuner, radio, extern and intern. */ chip->input = rt->input; if (chip->muted) break; chip_write_masked(chip, desc->inputreg, desc->inputmap[chip->input], desc->inputmask); break; } case VIDIOC_S_TUNER: { struct v4l2_tuner *vt = arg; int mode = 0; if (chip->radio) break; switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1: case V4L2_TUNER_MODE_LANG2: mode = vt->audmode; break; case V4L2_TUNER_MODE_LANG1_LANG2: mode = V4L2_TUNER_MODE_STEREO; break; default: return -EINVAL; } chip->audmode = vt->audmode; 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 = V4L2_TUNER_MODE_MONO; if (chip->radio) break; vt->audmode = chip->audmode; 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 & V4L2_TUNER_MODE_MONO) vt->rxsubchans |= V4L2_TUNER_SUB_MONO; if (mode & V4L2_TUNER_MODE_STEREO) vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; /* Note: for SAP it should be mono/lang2 or stereo/lang2. When this module is converted fully to v4l2, then this should change for those chips that can detect SAP. */ if (mode & V4L2_TUNER_MODE_LANG1) vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; break; } case VIDIOC_S_STD: chip->radio = 0; break; case VIDIOC_S_FREQUENCY: chip->mode = 0; /* automatic */ if (desc->checkmode && desc->setmode) { desc->setmode(chip,V4L2_TUNER_MODE_MONO); if (chip->prevmode != V4L2_TUNER_MODE_MONO) chip->prevmode = -1; /* reset previous mode */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); /* the thread will call checkmode() later */ } break; case VIDIOC_G_CHIP_IDENT: return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0); } return 0;}static int chip_legacy_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 1; return 0;}#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)/* This driver supports many devices and the idea is to let the driver detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */static const struct i2c_device_id chip_id[] = { { "tvaudio", 0 }, { }};MODULE_DEVICE_TABLE(i2c, chip_id);#endifstatic struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvaudio", .driverid = I2C_DRIVERID_TVAUDIO, .command = chip_command, .probe = chip_probe, .remove = chip_remove, .legacy_probe = chip_legacy_probe,#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = chip_id,#endif};/* * Local variables: * c-basic-offset: 8 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -