📄 cx88-video.c
字号:
fields++; /* estimate risc mem: worst case is (clip+1) * lines instructions + syncs + jump (all 2 dwords) */ instructions = (fh->nclips+1) * fh->win.w.height; instructions += 3 + 4; if ((rc = btcx_riscmem_alloc(dev->pci,&buf->risc,instructions*8)) < 0) { kfree(skips); return rc; } /* write risc instructions */ rp = buf->risc.cpu; switch (fh->win.field) { case V4L2_FIELD_TOP: rp = ov_risc_field(dev, fh, rp, skips, 0, 0, 0); break; case V4L2_FIELD_BOTTOM: rp = ov_risc_field(dev, fh, rp, skips, 0x200, 0, 0); break; case V4L2_FIELD_INTERLACED: rp = ov_risc_field(dev, fh, rp, skips, 0, 0, 1); rp = ov_risc_field(dev, fh, rp, skips, 0x200, 1, 0); break; default: BUG(); } /* save pointer to jmp instruction address */ buf->risc.jmp = rp; kfree(skips); return 0;}static int verify_window(struct cx8800_dev *dev, struct v4l2_window *win){ enum v4l2_field field; int maxw, maxh; if (NULL == dev->fbuf.base) return -EINVAL; if (win->w.width < 48 || win->w.height < 32) return -EINVAL; if (win->clipcount > 2048) return -EINVAL; field = win->field; maxw = norm_maxw(dev->tvnorm); maxh = norm_maxh(dev->tvnorm); if (V4L2_FIELD_ANY == field) { field = (win->w.height > maxh/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; } switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: break; default: return -EINVAL; } win->field = field; if (win->w.width > maxw) win->w.width = maxw; if (win->w.height > maxh) win->w.height = maxh; return 0;}static int setup_window(struct cx8800_dev *dev, struct cx8800_fh *fh, struct v4l2_window *win){ struct v4l2_clip *clips = NULL; int n,size,retval = 0; if (NULL == fh->fmt) return -EINVAL; retval = verify_window(dev,win); if (0 != retval) return retval; /* copy clips -- luckily v4l1 + v4l2 are binary compatible here ...*/ n = win->clipcount; size = sizeof(*clips)*(n+4); clips = kmalloc(size,GFP_KERNEL); if (NULL == clips) return -ENOMEM; if (n > 0) { if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) { kfree(clips); return -EFAULT; } } /* clip against screen */ if (NULL != dev->fbuf.base) n = btcx_screen_clips(dev->fbuf.fmt.width, dev->fbuf.fmt.height, &win->w, clips, n); btcx_sort_clips(clips,n); /* 4-byte alignments */ switch (fh->fmt->depth) { case 8: case 24: btcx_align(&win->w, clips, n, 3); break; case 16: btcx_align(&win->w, clips, n, 1); break; case 32: /* no alignment fixups needed */ break; default: BUG(); } down(&fh->vidq.lock); if (fh->clips) kfree(fh->clips); fh->clips = clips; fh->nclips = n; fh->win = *win;#if 0 fh->ov.setup_ok = 1;#endif /* update overlay if needed */ retval = 0;#if 0 if (check_btres(fh, RESOURCE_OVERLAY)) { struct bttv_buffer *new; new = videobuf_alloc(sizeof(*new)); bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); retval = bttv_switch_overlay(btv,fh,new); }#endif up(&fh->vidq.lock); return retval;}#endif/* ------------------------------------------------------------------ */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 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; 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)); 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)); init_MUTEX(&fh->vidq.lock); init_MUTEX(&fh->vbiq.lock); if (fh->radio) { dprintk(1,"video_open: setting radio device\n"); cx_write(MO_GP0_IO, cx88_boards[dev->board].radio.gpio0); cx_write(MO_GP1_IO, cx88_boards[dev->board].radio.gpio1); cx_write(MO_GP2_IO, cx88_boards[dev->board].radio.gpio2); cx_write(MO_GP3_IO, cx88_boards[dev->board].radio.gpio3); dev->tvaudio = WW_FM; cx88_set_tvaudio(dev); cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); cx8800_call_i2c_clients(dev,AUDC_SET_RADIO,NULL); } return 0;}static ssize_tvideo_read(struct file *file, char __user *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(file, &fh->vidq, data, count, ppos); case V4L2_BUF_TYPE_VBI_CAPTURE: if (!res_get(fh->dev,fh,RESOURCE_VBI)) return -EBUSY; return videobuf_read_stream(file, &fh->vbiq, data, count, ppos, 1); default: BUG(); return 0; }}static unsigned intvideo_poll(struct file *file, struct poll_table_struct *wait){ struct cx8800_fh *fh = file->private_data; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) return videobuf_poll_stream(file, &fh->vbiq, wait); /* FIXME */ return POLLERR;}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(file,&fh->vidq); res_free(dev,fh,RESOURCE_VIDEO); } if (fh->vidq.read_buf) { buffer_release(file,fh->vidq.read_buf); kfree(fh->vidq.read_buf); } /* stop vbi capture */ if (res_check(fh, RESOURCE_VBI)) { if (fh->vbiq.streaming) videobuf_streamoff(file,&fh->vbiq); if (fh->vbiq.reading) videobuf_read_stop(file,&fh->vbiq); res_free(dev,fh,RESOURCE_VBI); } file->private_data = NULL; kfree(fh); return 0;}static intvideo_mmap(struct file *file, struct vm_area_struct * vma){ struct cx8800_fh *fh = file->private_data; return videobuf_mmap_mapper(vma, get_queue(fh));}/* ------------------------------------------------------------------ */static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl){ 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){ 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) return -ERANGE; if (ctl->value > c->v.maximum) return -ERANGE; 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 struct v4l2_control mute = { .id = V4L2_CID_AUDIO_MUTE, .value = 1, }; static struct v4l2_control volume = { .id = V4L2_CID_AUDIO_VOLUME, .value = 0x3f, }; set_control(dev,&mute); set_control(dev,&volume);}/* ------------------------------------------------------------------ */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){ 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(dev->tvnorm); maxh = norm_maxh(dev->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: case V4L2_FIELD_BOTTOM: maxh = maxh / 2; break; case V4L2_FIELD_INTERLACED: break; default: return -EINVAL; } f->fmt.pix.field = field; if (f->fmt.pix.height < 32) f->fmt.pix.height = 32; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; if (f->fmt.pix.width < 48) f->fmt.pix.width = 48; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * 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_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, struct v4l2_format *f){ int err; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: err = cx8800_try_fmt(dev,fh,f); if (0 != err) return err; fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); fh->width = f->fmt.pix.width; fh->height = f->fmt.pix.height; fh->vidq.field = f->fmt.pix.field; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -