📄 cx88-video.c
字号:
while (*size * *count > vid_limit * 1024 * 1024) (*count)--; return 0;}static intbuffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field){ struct cx8800_fh *fh = q->priv_data; struct cx8800_dev *dev = fh->dev; struct cx88_core *core = dev->core; struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); int rc, init_buffer = 0; BUG_ON(NULL == fh->fmt); if (fh->width < 48 || fh->width > norm_maxw(core->tvnorm) || fh->height < 32 || fh->height > norm_maxh(core->tvnorm)) return -EINVAL; buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; if (buf->fmt != fh->fmt || buf->vb.width != fh->width || buf->vb.height != fh->height || buf->vb.field != field) { buf->fmt = fh->fmt; buf->vb.width = fh->width; buf->vb.height = fh->height; buf->vb.field = field; init_buffer = 1; } if (STATE_NEEDS_INIT == buf->vb.state) { init_buffer = 1; if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) goto fail; } if (init_buffer) { buf->bpl = buf->vb.width * buf->fmt->depth >> 3; switch (buf->vb.field) { case V4L2_FIELD_TOP: cx88_risc_buffer(dev->pci, &buf->risc, buf->vb.dma.sglist, 0, UNSET, buf->bpl, 0, buf->vb.height); break; case V4L2_FIELD_BOTTOM: cx88_risc_buffer(dev->pci, &buf->risc, buf->vb.dma.sglist, UNSET, 0, buf->bpl, 0, buf->vb.height); break; case V4L2_FIELD_INTERLACED: cx88_risc_buffer(dev->pci, &buf->risc, buf->vb.dma.sglist, 0, buf->bpl, buf->bpl, buf->bpl, buf->vb.height >> 1); break; case V4L2_FIELD_SEQ_TB: cx88_risc_buffer(dev->pci, &buf->risc, buf->vb.dma.sglist, 0, buf->bpl * (buf->vb.height >> 1), buf->bpl, 0, buf->vb.height >> 1); break; case V4L2_FIELD_SEQ_BT: cx88_risc_buffer(dev->pci, &buf->risc, buf->vb.dma.sglist, buf->bpl * (buf->vb.height >> 1), 0, buf->bpl, 0, buf->vb.height >> 1); break; default: BUG(); } } dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth, fh->fmt->name, (unsigned long)buf->risc.dma); buf->vb.state = STATE_PREPARED; return 0; fail: cx88_free_buffer(dev->pci,buf); return rc;}static voidbuffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb){ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); struct cx88_buffer *prev; struct cx8800_fh *fh = vq->priv_data; struct cx8800_dev *dev = fh->dev; struct cx88_core *core = dev->core; struct cx88_dmaqueue *q = &dev->vidq; /* add jump to stopper */ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); if (!list_empty(&q->queued)) { list_add_tail(&buf->vb.queue,&q->queued); buf->vb.state = STATE_QUEUED; dprintk(2,"[%p/%d] buffer_queue - append to queued\n", buf, buf->vb.i); } else if (list_empty(&q->active)) { list_add_tail(&buf->vb.queue,&q->active); start_video_dma(dev, q, buf); buf->vb.state = STATE_ACTIVE; buf->count = q->count++; mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); dprintk(2,"[%p/%d] buffer_queue - first active\n", buf, buf->vb.i); } else { prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); if (prev->vb.width == buf->vb.width && prev->vb.height == buf->vb.height && prev->fmt == buf->fmt) { list_add_tail(&buf->vb.queue,&q->active); buf->vb.state = STATE_ACTIVE; buf->count = q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk(2,"[%p/%d] buffer_queue - append to active\n", buf, buf->vb.i); } else { list_add_tail(&buf->vb.queue,&q->queued); buf->vb.state = STATE_QUEUED; dprintk(2,"[%p/%d] buffer_queue - first queued\n", buf, buf->vb.i); } }}static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb){ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); struct cx8800_fh *fh = q->priv_data; cx88_free_buffer(fh->dev->pci,buf);}static struct videobuf_queue_ops cx8800_video_qops = { .buf_setup = buffer_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .buf_release = buffer_release,};/* ------------------------------------------------------------------ *//* ------------------------------------------------------------------ */static struct videobuf_queue* get_queue(struct cx8800_fh *fh){ switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: return &fh->vidq; case V4L2_BUF_TYPE_VBI_CAPTURE: return &fh->vbiq; default: BUG(); return NULL; }}static int get_ressource(struct cx8800_fh *fh){ switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: return RESOURCE_VIDEO; case V4L2_BUF_TYPE_VBI_CAPTURE: return RESOURCE_VBI; default: BUG(); return 0; }}static int video_open(struct inode *inode, struct file *file){ int minor = iminor(inode); struct cx8800_dev *h,*dev = NULL; struct cx88_core *core; struct cx8800_fh *fh; struct list_head *list; enum v4l2_buf_type type = 0; int radio = 0; list_for_each(list,&cx8800_devlist) { h = list_entry(list, struct cx8800_dev, devlist); if (h->video_dev->minor == minor) { dev = h; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } if (h->vbi_dev->minor == minor) { dev = h; type = V4L2_BUF_TYPE_VBI_CAPTURE; } if (h->radio_dev && h->radio_dev->minor == minor) { radio = 1; dev = h; } } if (NULL == dev) return -ENODEV; core = dev->core; dprintk(1,"open minor=%d radio=%d type=%s\n", minor,radio,v4l2_type_names[type]); /* allocate + initialize per filehandle data */ fh = kmalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) return -ENOMEM; memset(fh,0,sizeof(*fh)); file->private_data = fh; fh->dev = dev; fh->radio = radio; fh->type = type; fh->width = 320; fh->height = 240; fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); videobuf_queue_init(&fh->vidq, &cx8800_video_qops, dev->pci, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct cx88_buffer), fh); videobuf_queue_init(&fh->vbiq, &cx8800_vbi_qops, dev->pci, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct cx88_buffer), fh); if (fh->radio) { int board = core->board; dprintk(1,"video_open: setting radio device\n"); cx_write(MO_GP3_IO, cx88_boards[board].radio.gpio3); cx_write(MO_GP0_IO, cx88_boards[board].radio.gpio0); cx_write(MO_GP1_IO, cx88_boards[board].radio.gpio1); cx_write(MO_GP2_IO, cx88_boards[board].radio.gpio2); core->tvaudio = WW_FM; cx88_set_tvaudio(core); cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1); cx88_call_i2c_clients(core,AUDC_SET_RADIO,NULL); } return 0;}static ssize_tvideo_read(struct file *file, char *data, size_t count, loff_t *ppos){ struct cx8800_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(&fh->vidq, 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(&fh->vbiq, data, count, ppos, 1, file->f_flags & O_NONBLOCK); default: BUG(); return 0; }}static unsigned intvideo_poll(struct file *file, struct poll_table_struct *wait){ struct cx8800_fh *fh = file->private_data; struct cx88_buffer *buf; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { if (!res_get(fh->dev,fh,RESOURCE_VBI)) return POLLERR; return videobuf_poll_stream(file, &fh->vbiq, wait); } if (res_check(fh,RESOURCE_VIDEO)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) return POLLERR; buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream); } else { /* read() capture */ buf = (struct cx88_buffer*)fh->vidq.read_buf; if (NULL == buf) return POLLERR; } poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == STATE_DONE || buf->vb.state == STATE_ERROR) return POLLIN|POLLRDNORM; return 0;}static int video_release(struct inode *inode, struct file *file){ struct cx8800_fh *fh = file->private_data; struct cx8800_dev *dev = fh->dev; /* turn off overlay */ if (res_check(fh, RESOURCE_OVERLAY)) { /* FIXME */ res_free(dev,fh,RESOURCE_OVERLAY); } /* stop video capture */ if (res_check(fh, RESOURCE_VIDEO)) { videobuf_queue_cancel(&fh->vidq); res_free(dev,fh,RESOURCE_VIDEO); } if (fh->vidq.read_buf) { buffer_release(&fh->vidq,fh->vidq.read_buf); kfree(fh->vidq.read_buf); } /* stop vbi capture */ if (res_check(fh, RESOURCE_VBI)) { if (fh->vbiq.streaming) videobuf_streamoff(&fh->vbiq); if (fh->vbiq.reading) videobuf_read_stop(&fh->vbiq); res_free(dev,fh,RESOURCE_VBI); } videobuf_mmap_free(&fh->vidq); videobuf_mmap_free(&fh->vbiq); file->private_data = NULL; kfree(fh); cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); return 0;}static intvideo_mmap(struct file *file, struct vm_area_struct * vma){ struct cx8800_fh *fh = file->private_data; return videobuf_mmap_mapper(get_queue(fh), vma);}/* ------------------------------------------------------------------ *//* static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) */static int get_control(struct cx88_core *core, struct v4l2_control *ctl){ /* struct cx88_core *core = dev->core; */ struct cx88_ctrl *c = NULL; u32 value; int i; for (i = 0; i < CX8800_CTLS; i++) if (cx8800_ctls[i].v.id == ctl->id) c = &cx8800_ctls[i]; if (NULL == c) return -EINVAL; value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg); switch (ctl->id) { case V4L2_CID_AUDIO_BALANCE: ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f)); break; case V4L2_CID_AUDIO_VOLUME: ctl->value = 0x3f - (value & 0x3f); break; default: ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; break; } return 0;}/* static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl) */static int set_control(struct cx88_core *core, struct v4l2_control *ctl){ /* struct cx88_core *core = dev->core; */ struct cx88_ctrl *c = NULL; u32 v_sat_value; u32 value; int i; for (i = 0; i < CX8800_CTLS; i++) if (cx8800_ctls[i].v.id == ctl->id) c = &cx8800_ctls[i]; if (NULL == c) return -EINVAL; if (ctl->value < c->v.minimum) ctl->value = c->v.minimum; if (ctl->value > c->v.maximum) ctl->value = c->v.maximum; switch (ctl->id) { case V4L2_CID_AUDIO_BALANCE: value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value; break; case V4L2_CID_AUDIO_VOLUME: value = 0x3f - (ctl->value & 0x3f); break; case V4L2_CID_SATURATION: /* special v_sat handling */ v_sat_value = ctl->value - (0x7f - 0x5a); if (v_sat_value > 0xff) v_sat_value = 0xff; if (v_sat_value < 0x00) v_sat_value = 0x00; cx_andor(MO_UV_SATURATION, 0xff00, v_sat_value << 8); /* fall through to default route for u_sat */ default: value = ((ctl->value - c->off) << c->shift) & c->mask; break; } dprintk(1,"set_control id=0x%X reg=0x%x val=0x%x%s\n", ctl->id, c->reg, value, c->sreg ? " [shadowed]" : ""); if (c->sreg) { cx_sandor(c->sreg, c->reg, c->mask, value); } else { cx_andor(c->reg, c->mask, value); } return 0;}/* static void init_controls(struct cx8800_dev *dev) */static void init_controls(struct cx88_core *core){ static struct v4l2_control mute = { .id = V4L2_CID_AUDIO_MUTE, .value = 1, }; static struct v4l2_control volume = { .id = V4L2_CID_AUDIO_VOLUME, .value = 0x3f, }; static struct v4l2_control hue = { .id = V4L2_CID_HUE, .value = 0x80, }; static struct v4l2_control contrast = { .id = V4L2_CID_CONTRAST, .value = 0x80, }; static struct v4l2_control brightness = { .id = V4L2_CID_BRIGHTNESS, .value = 0x80, }; set_control(core,&mute); set_control(core,&volume); set_control(core,&hue); set_control(core,&contrast); set_control(core,&brightness);}/* ------------------------------------------------------------------ */static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, struct v4l2_format *f){ switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); f->fmt.pix.width = fh->width; f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->vidq.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: cx8800_vbi_fmt(dev, f); return 0; default: return -EINVAL; }}static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, struct v4l2_format *f){ struct cx88_core *core = dev->core; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { struct cx8800_fmt *fmt; enum v4l2_field field; unsigned int maxw, maxh; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) return -EINVAL; field = f->fmt.pix.field; maxw = norm_maxw(core->tvnorm); maxh = norm_maxh(core->tvnorm); if (V4L2_FIELD_ANY == field) { field = (f->fmt.pix.height > maxh/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; } switch (field) { case V4L2_FIELD_TOP:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -