📄 mxb.c
字号:
static int mxb_detach(struct saa7146_dev *dev){ struct mxb *mxb = (struct mxb *)dev->ext_priv; DEB_EE(("dev:%p\n", dev)); i2c_release_client(mxb->tea6420_1); i2c_release_client(mxb->tea6420_2); i2c_release_client(mxb->tea6415c); i2c_release_client(mxb->tda9840); i2c_release_client(mxb->saa7111a); i2c_release_client(mxb->tuner); saa7146_unregister_device(&mxb->video_dev,dev); if (MXB_BOARD_CAN_DO_VBI(dev)) saa7146_unregister_device(&mxb->vbi_dev, dev); saa7146_vv_release(dev); mxb_num--; i2c_del_adapter(&mxb->i2c_adapter); kfree(mxb); return 0;}static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg){ struct saa7146_dev *dev = fh->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; struct saa7146_vv *vv = dev->vv_data; switch(cmd) { case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); if (i->index < 0 || i->index >= MXB_INPUTS) return -EINVAL; memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); return 0; } /* the saa7146 provides some controls (brightness, contrast, saturation) which gets registered *after* this function. because of this we have to return with a value != 0 even if the function succeded.. */ case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *qc = arg; int i; for (i = MAXCONTROLS - 1; i >= 0; i--) { if (mxb_controls[i].id == qc->id) { *qc = mxb_controls[i]; DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); return 0; } } return -EAGAIN; } case VIDIOC_G_CTRL: { struct v4l2_control *vc = arg; int i; for (i = MAXCONTROLS - 1; i >= 0; i--) { if (mxb_controls[i].id == vc->id) break; } if (i < 0) return -EAGAIN; if (vc->id == V4L2_CID_AUDIO_MUTE) { vc->value = mxb->cur_mute; DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); return 0; } DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); return 0; } case VIDIOC_S_CTRL: { struct v4l2_control *vc = arg; int i = 0; for (i = MAXCONTROLS - 1; i >= 0; i--) { if (mxb_controls[i].id == vc->id) break; } if (i < 0) return -EAGAIN; if (vc->id == V4L2_CID_AUDIO_MUTE) { mxb->cur_mute = vc->value; if (!vc->value) { /* switch the audio-source */ mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); } else { mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]); mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]); } DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); } return 0; } case VIDIOC_G_INPUT: { int *input = (int *)arg; *input = mxb->cur_input; DEB_EE(("VIDIOC_G_INPUT %d.\n", *input)); return 0; } case VIDIOC_S_INPUT: { int input = *(int *)arg; struct tea6415c_multiplex vm; struct v4l2_routing route; int i = 0; DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); if (input < 0 || input >= MXB_INPUTS) return -EINVAL; mxb->cur_input = input; saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync); /* prepare switching of tea6415c and saa7111a; have a look at the 'background'-file for further informations */ switch (input) { case TUNER: i = SAA7115_COMPOSITE0; vm.in = 3; vm.out = 17; if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); return -EFAULT; } /* connect tuner-output always to multicable */ vm.in = 3; vm.out = 13; break; case AUX3_YC: /* nothing to be done here. aux3_yc is directly connected to the saa711a */ i = SAA7115_SVIDEO1; break; case AUX3: /* nothing to be done here. aux3 is directly connected to the saa711a */ i = SAA7115_COMPOSITE1; break; case AUX1: i = SAA7115_COMPOSITE0; vm.in = 1; vm.out = 17; break; } /* switch video in tea6415c only if necessary */ switch (input) { case TUNER: case AUX1: if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); return -EFAULT; } break; default: break; } /* switch video in saa7111a */ route.input = i; route.output = 0; if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route)) printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n"); /* switch the audio-source only if necessary */ if( 0 == mxb->cur_mute ) { mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]); mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]); } return 0; } case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; if (t->index) { DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); return -EINVAL; } DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); memset(t, 0, sizeof(*t)); i2c_clients_command(&mxb->i2c_adapter, cmd, arg); strlcpy(t->name, "TV Tuner", sizeof(t->name)); t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; t->audmode = mxb->cur_mode; return 0; } case VIDIOC_S_TUNER: { struct v4l2_tuner *t = arg; if (t->index) { DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index)); return -EINVAL; } mxb->cur_mode = t->audmode; i2c_clients_command(&mxb->i2c_adapter, cmd, arg); return 0; } case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *f = arg; if (mxb->cur_input) { DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); return -EINVAL; } *f = mxb->cur_freq; DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency)); return 0; } case VIDIOC_S_FREQUENCY: { struct v4l2_frequency *f = arg; if (f->tuner) return -EINVAL; if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; if (mxb->cur_input) { DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); return -EINVAL; } mxb->cur_freq = *f; DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency)); /* tune in desired frequency */ mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq); /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ spin_lock(&dev->slock); vv->vbi_fieldcount = 0; spin_unlock(&dev->slock); return 0; } case MXB_S_AUDIO_CD: { int i = *(int*)arg; if (i < 0 || i >= MXB_AUDIOS) { DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i)); return -EINVAL; } DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i)); mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]); mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]); return 0; } case MXB_S_AUDIO_LINE: { int i = *(int*)arg; if (i < 0 || i >= MXB_AUDIOS) { DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i)); return -EINVAL; } DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i)); mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]); mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]); return 0; } case VIDIOC_G_AUDIO: { struct v4l2_audio *a = arg; if (a->index < 0 || a->index > MXB_INPUTS) { DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); return -EINVAL; } DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index)); memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); return 0; } case VIDIOC_S_AUDIO: { struct v4l2_audio *a = arg; DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index)); return 0; }#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: i2c_clients_command(&mxb->i2c_adapter, cmd, arg); return 0;#endif default:/* DEB2(printk("does not handle this ioctl.\n"));*/ return -ENOIOCTLCMD; } return 0;}static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard){ struct mxb *mxb = (struct mxb *)dev->ext_priv; int zero = 0; int one = 1; if (V4L2_STD_PAL_I == standard->id) { v4l2_std_id std = V4L2_STD_PAL_I; DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n")); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero); mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n")); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one); mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); } return 0;}static struct saa7146_standard standard[] = { { .name = "PAL-BG", .id = V4L2_STD_PAL_BG, .v_offset = 0x17, .v_field = 288, .h_offset = 0x14, .h_pixels = 680, .v_max_out = 576, .h_max_out = 768, }, { .name = "PAL-I", .id = V4L2_STD_PAL_I, .v_offset = 0x17, .v_field = 288, .h_offset = 0x14, .h_pixels = 680, .v_max_out = 576, .h_max_out = 768, }, { .name = "NTSC", .id = V4L2_STD_NTSC, .v_offset = 0x16, .v_field = 240, .h_offset = 0x06, .h_pixels = 708, .v_max_out = 480, .h_max_out = 640, }, { .name = "SECAM", .id = V4L2_STD_SECAM, .v_offset = 0x14, .v_field = 288, .h_offset = 0x14, .h_pixels = 720, .v_max_out = 576, .h_max_out = 768, }};static struct saa7146_pci_extension_data mxb = { .ext_priv = "Multimedia eXtension Board", .ext = &extension,};static struct pci_device_id pci_tbl[] = { { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7146, .subvendor = 0x0000, .subdevice = 0x0000, .driver_data = (unsigned long)&mxb, }, { .vendor = 0, }};MODULE_DEVICE_TABLE(pci, pci_tbl);static struct saa7146_ext_vv vv_data = { .inputs = MXB_INPUTS, .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE, .stds = &standard[0], .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), .std_callback = &std_callback, .ioctls = &ioctls[0], .ioctl = mxb_ioctl,};static struct saa7146_extension extension = { .name = MXB_IDENTIFIER, .flags = SAA7146_USE_I2C_IRQ, .pci_tbl = &pci_tbl[0], .module = THIS_MODULE, .probe = mxb_probe, .attach = mxb_attach, .detach = mxb_detach, .irq_mask = 0, .irq_func = NULL,};static int __init mxb_init_module(void){ if (saa7146_register_extension(&extension)) { DEB_S(("failed to register extension.\n")); return -ENODEV; } return 0;}static void __exit mxb_cleanup_module(void){ saa7146_unregister_extension(&extension);}module_init(mxb_init_module);module_exit(mxb_cleanup_module);MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -