📄 av7110_v4l.c
字号:
return -EINVAL; } memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); return 0; } case VIDIOC_G_INPUT: { int *input = (int *)arg; *input = av7110->current_input; dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); return 0; } case VIDIOC_S_INPUT: { int input = *(int *)arg; dprintk(2, "VIDIOC_S_INPUT: %d\n", input); if (!av7110->analog_tuner_flags) return 0; if (input < 0 || input >= 4) return -EINVAL; av7110->current_input = input; return av7110_dvb_c_switch(fh); } case VIDIOC_G_AUDIO: { struct v4l2_audio *a = arg; dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); if (a->index != 0) return -EINVAL; memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio)); break; } case VIDIOC_S_AUDIO: { struct v4l2_audio *a = arg; dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); break; } case VIDIOC_G_SLICED_VBI_CAP: { struct v4l2_sliced_vbi_cap *cap = arg; dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); memset(cap, 0, sizeof *cap); if (FW_VERSION(av7110->arm_app) >= 0x2623) { cap->service_set = V4L2_SLICED_WSS_625; cap->service_lines[0][23] = V4L2_SLICED_WSS_625; } break; } case VIDIOC_G_FMT: { struct v4l2_format *f = arg; dprintk(2, "VIDIOC_G_FMT:\n"); if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT || FW_VERSION(av7110->arm_app) < 0x2623) return -EAGAIN; /* handled by core driver */ memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); if (av7110->wssMode) { f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data); } break; } case VIDIOC_S_FMT: { struct v4l2_format *f = arg; dprintk(2, "VIDIOC_S_FMT\n"); if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT || FW_VERSION(av7110->arm_app) < 0x2623) return -EAGAIN; /* handled by core driver */ if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); /* WSS controlled by firmware */ av7110->wssMode = 0; av7110->wssData = 0; return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); } else { memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data); /* WSS controlled by userspace */ av7110->wssMode = 1; av7110->wssData = 0; } break; } default: printk("no such ioctl\n"); return -ENOIOCTLCMD; } return 0;}static int av7110_vbi_reset(struct inode *inode, struct file *file){ struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; struct av7110 *av7110 = (struct av7110*) dev->ext_priv; dprintk(2, "%s\n", __func__); av7110->wssMode = 0; av7110->wssData = 0; if (FW_VERSION(av7110->arm_app) < 0x2623) return 0; else return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);}static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos){ struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; struct av7110 *av7110 = (struct av7110*) dev->ext_priv; struct v4l2_sliced_vbi_data d; int rc; dprintk(2, "%s\n", __func__); if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) return -EINVAL; if (copy_from_user(&d, data, count)) return -EFAULT; if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) return -EINVAL; if (d.id) av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; else av7110->wssData = 0x8000; rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); return (rc < 0) ? rc : count;}/**************************************************************************** * INITIALIZATION ****************************************************************************/static struct saa7146_extension_ioctls ioctls[] = { { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE }, { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE }, { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE }, { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE }, { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE }, { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE }, { VIDIOC_G_SLICED_VBI_CAP, SAA7146_EXCLUSIVE }, { VIDIOC_G_FMT, SAA7146_BEFORE }, { VIDIOC_S_FMT, SAA7146_BEFORE }, { 0, 0 }};static u8 saa7113_init_regs[] = { 0x02, 0xd0, 0x03, 0x23, 0x04, 0x00, 0x05, 0x00, 0x06, 0xe9, 0x07, 0x0d, 0x08, 0x98, 0x09, 0x02, 0x0a, 0x80, 0x0b, 0x40, 0x0c, 0x40, 0x0d, 0x00, 0x0e, 0x01, 0x0f, 0x7c, 0x10, 0x48, 0x11, 0x0c, 0x12, 0x8b, 0x13, 0x1a, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x41, 0x77, 0x42, 0x77, 0x43, 0x77, 0x44, 0x77, 0x45, 0x77, 0x46, 0x77, 0x47, 0x77, 0x48, 0x77, 0x49, 0x77, 0x4a, 0x77, 0x4b, 0x77, 0x4c, 0x77, 0x4d, 0x77, 0x4e, 0x77, 0x4f, 0x77, 0x50, 0x77, 0x51, 0x77, 0x52, 0x77, 0x53, 0x77, 0x54, 0x77, 0x55, 0x77, 0x56, 0x77, 0x57, 0xff, 0xff};static struct saa7146_ext_vv av7110_vv_data_st;static struct saa7146_ext_vv av7110_vv_data_c;int av7110_init_analog_module(struct av7110 *av7110){ u16 version1, version2; if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3400\n", av7110->dvb_adapter.num); av7110->adac_type = DVB_ADAC_MSP34x0; } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3415\n", av7110->dvb_adapter.num); av7110->adac_type = DVB_ADAC_MSP34x5; } else return -ENODEV; msleep(100); // the probing above resets the msp... msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", av7110->dvb_adapter.num, version1, version2); msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { INFO(("saa7113 not accessible.\n")); } else { u8 *i = saa7113_init_regs; if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { /* Fujitsu/Siemens DVB-Cable */ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { /* Hauppauge/TT DVB-C premium */ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { /* Hauppauge/TT DVB-C premium */ av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; } /* setup for DVB by default */ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) dprintk(1, "setting band in demodulator failed.\n"); } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) } /* init the saa7113 */ while (*i != 0xff) { if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); break; } i += 2; } /* setup msp for analog sound: B/G Dual-FM */ msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 } memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); /* set dd1 stream a & b */ saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); saa7146_write(av7110->dev, DD1_INIT, 0x03000700); saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); return 0;}int av7110_init_v4l(struct av7110 *av7110){ struct saa7146_dev* dev = av7110->dev; int ret; /* special case DVB-C: these cards have an analog tuner plus need some special handling, so we have separate saa7146_ext_vv data for these... */ if (av7110->analog_tuner_flags) ret = saa7146_vv_init(dev, &av7110_vv_data_c); else ret = saa7146_vv_init(dev, &av7110_vv_data_st); if (ret) { ERR(("cannot init capture device. skipping.\n")); return -ENODEV; } if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { ERR(("cannot register capture device. skipping.\n")); saa7146_vv_release(dev); return -ENODEV; } if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) ERR(("cannot register vbi v4l2 device. skipping.\n")); return 0;}int av7110_exit_v4l(struct av7110 *av7110){ struct saa7146_dev* dev = av7110->dev; saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); saa7146_vv_release(dev); return 0;}/* FIXME: these values are experimental values that look better than the values from the latest "official" driver -- at least for me... (MiHu) */static struct saa7146_standard standard[] = { { .name = "PAL", .id = V4L2_STD_PAL_BG, .v_offset = 0x15, .v_field = 288, .h_offset = 0x48, .h_pixels = 708, .v_max_out = 576, .h_max_out = 768, }, { .name = "NTSC", .id = V4L2_STD_NTSC, .v_offset = 0x10, .v_field = 244, .h_offset = 0x40, .h_pixels = 708, .v_max_out = 480, .h_max_out = 640, }};static struct saa7146_standard analog_standard[] = { { .name = "PAL", .id = V4L2_STD_PAL_BG, .v_offset = 0x1b, .v_field = 288, .h_offset = 0x08, .h_pixels = 708, .v_max_out = 576, .h_max_out = 768, }, { .name = "NTSC", .id = V4L2_STD_NTSC, .v_offset = 0x10, .v_field = 244, .h_offset = 0x40, .h_pixels = 708, .v_max_out = 480, .h_max_out = 640, }};static struct saa7146_standard dvb_standard[] = { { .name = "PAL", .id = V4L2_STD_PAL_BG, .v_offset = 0x14, .v_field = 288, .h_offset = 0x48, .h_pixels = 708, .v_max_out = 576, .h_max_out = 768, }, { .name = "NTSC", .id = V4L2_STD_NTSC, .v_offset = 0x10, .v_field = 244, .h_offset = 0x40, .h_pixels = 708, .v_max_out = 480, .h_max_out = 640, }};static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std){ struct av7110 *av7110 = (struct av7110*) dev->ext_priv; if (std->id & V4L2_STD_PAL) { av7110->vidmode = AV7110_VIDEO_MODE_PAL; av7110_set_vidmode(av7110, av7110->vidmode); } else if (std->id & V4L2_STD_NTSC) { av7110->vidmode = AV7110_VIDEO_MODE_NTSC; av7110_set_vidmode(av7110, av7110->vidmode); } else return -1; return 0;}static struct saa7146_ext_vv av7110_vv_data_st = { .inputs = 1, .audios = 1, .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT, .flags = 0, .stds = &standard[0], .num_stds = ARRAY_SIZE(standard), .std_callback = &std_callback, .ioctls = &ioctls[0], .ioctl = av7110_ioctl, .vbi_fops.open = av7110_vbi_reset, .vbi_fops.release = av7110_vbi_reset, .vbi_fops.write = av7110_vbi_write,};static struct saa7146_ext_vv av7110_vv_data_c = { .inputs = 1, .audios = 1, .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT, .flags = SAA7146_USE_PORT_B_FOR_VBI, .stds = &standard[0], .num_stds = ARRAY_SIZE(standard), .std_callback = &std_callback, .ioctls = &ioctls[0], .ioctl = av7110_ioctl, .vbi_fops.open = av7110_vbi_reset, .vbi_fops.release = av7110_vbi_reset, .vbi_fops.write = av7110_vbi_write,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -