📄 saa7134-video.c
字号:
saa7134_set_dmabits(dev); mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT); return 0;}static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field){ struct saa7134_fh *fh = q->priv_data; struct saa7134_dev *dev = fh->dev; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); unsigned int size; int err; /* sanity checks */ if (NULL == fh->fmt) return -EINVAL; if (fh->width < 48 || fh->height < 32 || fh->width/4 > dev->crop_current.width || fh->height/4 > dev->crop_current.height || fh->width > dev->crop_bounds.width || fh->height > dev->crop_bounds.height) return -EINVAL; size = (fh->width * fh->height * fh->fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < size) return -EINVAL; dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n", vb->i,fh->width,fh->height,size,v4l2_field_names[field], fh->fmt->name); if (buf->vb.width != fh->width || buf->vb.height != fh->height || buf->vb.size != size || buf->vb.field != field || buf->fmt != fh->fmt) { saa7134_dma_free(q,buf); } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); buf->vb.width = fh->width; buf->vb.height = fh->height; buf->vb.size = size; buf->vb.field = field; buf->fmt = fh->fmt; buf->pt = &fh->pt_cap; err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); if (err) goto oops; err = saa7134_pgtable_build(dev->pci,buf->pt, dma->sglist, dma->sglen, saa7134_buffer_startpage(buf)); if (err) goto oops; } buf->vb.state = VIDEOBUF_PREPARED; buf->activate = buffer_activate; return 0; oops: saa7134_dma_free(q,buf); return err;}static intbuffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size){ struct saa7134_fh *fh = q->priv_data; *size = fh->fmt->depth * fh->width * fh->height >> 3; if (0 == *count) *count = gbuffers; *count = saa7134_buffer_count(*size,*count); return 0;}static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb){ struct saa7134_fh *fh = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);}static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb){ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); saa7134_dma_free(q,buf);}static struct videobuf_queue_ops video_qops = { .buf_setup = buffer_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .buf_release = buffer_release,};/* ------------------------------------------------------------------ */int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c){ const struct v4l2_queryctrl* ctrl; ctrl = ctrl_by_id(c->id); if (NULL == ctrl) return -EINVAL; switch (c->id) { case V4L2_CID_BRIGHTNESS: c->value = dev->ctl_bright; break; case V4L2_CID_HUE: c->value = dev->ctl_hue; break; case V4L2_CID_CONTRAST: c->value = dev->ctl_contrast; break; case V4L2_CID_SATURATION: c->value = dev->ctl_saturation; break; case V4L2_CID_AUDIO_MUTE: c->value = dev->ctl_mute; break; case V4L2_CID_AUDIO_VOLUME: c->value = dev->ctl_volume; break; case V4L2_CID_PRIVATE_INVERT: c->value = dev->ctl_invert; break; case V4L2_CID_HFLIP: c->value = dev->ctl_mirror; break; case V4L2_CID_PRIVATE_Y_EVEN: c->value = dev->ctl_y_even; break; case V4L2_CID_PRIVATE_Y_ODD: c->value = dev->ctl_y_odd; break; case V4L2_CID_PRIVATE_AUTOMUTE: c->value = dev->ctl_automute; break; default: return -EINVAL; } return 0;}EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal);static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c){ struct saa7134_fh *fh = priv; return saa7134_g_ctrl_internal(fh->dev, fh, c);}int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c){ const struct v4l2_queryctrl* ctrl; unsigned long flags; int restart_overlay = 0; int err; /* When called from the empress code fh == NULL. That needs to be fixed somehow, but for now this is good enough. */ if (fh) { err = v4l2_prio_check(&dev->prio, &fh->prio); if (0 != err) return err; } err = -EINVAL; mutex_lock(&dev->lock); ctrl = ctrl_by_id(c->id); if (NULL == ctrl) goto error; dprintk("set_control name=%s val=%d\n",ctrl->name,c->value); switch (ctrl->type) { case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER: if (c->value < ctrl->minimum) c->value = ctrl->minimum; if (c->value > ctrl->maximum) c->value = ctrl->maximum; break; default: /* nothing */; }; switch (c->id) { case V4L2_CID_BRIGHTNESS: dev->ctl_bright = c->value; saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); break; case V4L2_CID_HUE: dev->ctl_hue = c->value; saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); break; case V4L2_CID_CONTRAST: dev->ctl_contrast = c->value; saa_writeb(SAA7134_DEC_LUMA_CONTRAST, dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); break; case V4L2_CID_SATURATION: dev->ctl_saturation = c->value; saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); break; case V4L2_CID_AUDIO_MUTE: dev->ctl_mute = c->value; saa7134_tvaudio_setmute(dev); break; case V4L2_CID_AUDIO_VOLUME: dev->ctl_volume = c->value; saa7134_tvaudio_setvolume(dev,dev->ctl_volume); break; case V4L2_CID_PRIVATE_INVERT: dev->ctl_invert = c->value; saa_writeb(SAA7134_DEC_LUMA_CONTRAST, dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); saa_writeb(SAA7134_DEC_CHROMA_SATURATION, dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); break; case V4L2_CID_HFLIP: dev->ctl_mirror = c->value; restart_overlay = 1; break; case V4L2_CID_PRIVATE_Y_EVEN: dev->ctl_y_even = c->value; restart_overlay = 1; break; case V4L2_CID_PRIVATE_Y_ODD: dev->ctl_y_odd = c->value; restart_overlay = 1; break; case V4L2_CID_PRIVATE_AUTOMUTE: { struct v4l2_priv_tun_config tda9887_cfg; tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &dev->tda9887_conf; dev->ctl_automute = c->value; if (dev->tda9887_conf) { if (dev->ctl_automute) dev->tda9887_conf |= TDA9887_AUTOMUTE; else dev->tda9887_conf &= ~TDA9887_AUTOMUTE; saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tda9887_cfg); } break; } default: goto error; } if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev,fh); start_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); } err = 0;error: mutex_unlock(&dev->lock); return err;}EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal);static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c){ struct saa7134_fh *fh = f; return saa7134_s_ctrl_internal(fh->dev, fh, c);}/* ------------------------------------------------------------------ */static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh){ struct videobuf_queue* q = NULL; switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: q = &fh->cap; break; case V4L2_BUF_TYPE_VBI_CAPTURE: q = &fh->vbi; break; default: BUG(); } return q;}static int saa7134_resource(struct saa7134_fh *fh){ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) return RESOURCE_VIDEO; if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) return RESOURCE_VBI; BUG(); return 0;}static int video_open(struct inode *inode, struct file *file){ int minor = iminor(inode); struct saa7134_dev *dev; struct saa7134_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int radio = 0; lock_kernel(); list_for_each_entry(dev, &saa7134_devlist, devlist) { if (dev->video_dev && (dev->video_dev->minor == minor)) goto found; if (dev->radio_dev && (dev->radio_dev->minor == minor)) { radio = 1; goto found; } if (dev->vbi_dev && (dev->vbi_dev->minor == minor)) { type = V4L2_BUF_TYPE_VBI_CAPTURE; goto found; } } unlock_kernel(); return -ENODEV; found: dprintk("open minor=%d radio=%d type=%s\n",minor,radio, v4l2_type_names[type]); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) { unlock_kernel(); return -ENOMEM; } file->private_data = fh; fh->dev = dev; fh->radio = radio; fh->type = type; fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); fh->width = 720; fh->height = 576; v4l2_prio_open(&dev->prio,&fh->prio); videobuf_queue_sg_init(&fh->cap, &video_qops, &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct saa7134_buf), fh); videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops, &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct saa7134_buf), fh); saa7134_pgtable_alloc(dev->pci,&fh->pt_cap); saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi); if (fh->radio) { /* switch to radio mode */ saa7134_tvaudio_setinput(dev,&card(dev).radio); saa7134_i2c_call_clients(dev,AUDC_SET_RADIO, NULL); } else { /* switch to video/vbi mode */ video_mux(dev,dev->ctl_input); } unlock_kernel(); return 0;}static ssize_tvideo_read(struct file *file, char __user *data, size_t count, loff_t *ppos){ struct saa7134_fh *fh = file->private_data; switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (res_locked(fh->dev,RESOURCE_VIDEO)) return -EBUSY; return videobuf_read_one(saa7134_queue(fh), data, count, ppos, file->f_flags & O_NONBLOCK); case V4L2_BUF_TYPE_VBI_CAPTURE: if (!res_get(fh->dev,fh,RESOURCE_VBI)) return -EBUSY; return videobuf_read_stream(saa7134_queue(fh), data, count, ppos, 1, file->f_flags & O_NONBLOCK); break; default: BUG(); return 0; }}static unsigned intvideo_poll(struct file *file, struct poll_table_struct *wait){ struct saa7134_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) return videobuf_poll_stream(file, &fh->vbi, wait); if (res_check(fh,RESOURCE_VIDEO)) { if (!list_empty(&fh->cap.stream)) buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); } else { mutex_lock(&fh->cap.vb_lock); if (UNSET == fh->cap.read_off) { /* need to capture a new frame */ if (res_locked(fh->dev,RESOURCE_VIDEO)) goto err; if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field)) goto err; fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); fh->cap.read_off = 0; } mutex_unlock(&fh->cap.vb_lock); buf = fh->cap.read_buf; } if (!buf) return POLLERR; poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) return POLLIN|POLLRDNORM; return 0;err: mutex_unlock(&fh->cap.vb_lock); return POLLERR;}static int video_release(struct inode *inode, struct file *file){ struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; unsigned long flags; /* turn off overlay */ if (res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock,flags); stop_preview(dev,fh); spin_unlock_irqrestore(&dev->slock,flags); res_free(dev,fh,RESOURCE_OVERLAY); } /* stop video capture */ if (res_check(fh, RESOURCE_VIDEO)) { videobuf_streamoff(&fh->cap); res_free(dev,fh,RESOURCE_VIDEO); } if (fh->cap.read_buf) { buffer_release(&fh->cap,fh->cap.read_buf); kfree(fh->cap.read_buf); } /* stop vbi capture */ if (res_check(fh, RESOURCE_VBI)) { videobuf_stop(&fh->vbi); res_free(dev,fh,RESOURCE_VBI); } /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/ saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); /* free stuff */ videobuf_mmap_free(&fh->cap); videobuf_mmap_free(&fh->vbi); saa7134_pgtable_free(dev->pci,&fh->pt_cap); saa7134_pgtable_free(dev->pci,&fh->pt_vbi); v4l2_prio_close(&dev->prio,&fh->prio); file->private_data = NULL; kfree(fh); return 0;}static int video_mmap(struct file *file, struct vm_area_struct * vma){ struct saa7134_fh *fh = file->private_data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -