📄 cx88-video.c
字号:
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_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);}struct videobuf_queue_ops cx8800_video_qops = { .buf_setup = buffer_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .buf_release = buffer_release,};/* ------------------------------------------------------------------ */#if 0 /* overlay support not finished yet */static u32* ov_risc_field(struct cx8800_dev *dev, struct cx8800_fh *fh, u32 *rp, struct btcx_skiplist *skips, u32 sync_line, int skip_even, int skip_odd){ int line,maxy,start,end,skip,nskips; u32 ri,ra; u32 addr; /* sync instruction */ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); addr = (unsigned long)dev->fbuf.base; addr += dev->fbuf.fmt.bytesperline * fh->win.w.top; addr += (fh->fmt->depth >> 3) * fh->win.w.left; /* scan lines */ for (maxy = -1, line = 0; line < fh->win.w.height; line++, addr += dev->fbuf.fmt.bytesperline) { if ((line%2) == 0 && skip_even) continue; if ((line%2) == 1 && skip_odd) continue; /* calculate clipping */ if (line > maxy) btcx_calc_skips(line, fh->win.w.width, &maxy, skips, &nskips, fh->clips, fh->nclips); /* write out risc code */ for (start = 0, skip = 0; start < fh->win.w.width; start = end) { if (skip >= nskips) { ri = RISC_WRITE; end = fh->win.w.width; } else if (start < skips[skip].start) { ri = RISC_WRITE; end = skips[skip].start; } else { ri = RISC_SKIP; end = skips[skip].end; skip++; } if (RISC_WRITE == ri) ra = addr + (fh->fmt->depth>>3)*start; else ra = 0; if (0 == start) ri |= RISC_SOL; if (fh->win.w.width == end) ri |= RISC_EOL; ri |= (fh->fmt->depth>>3) * (end-start); *(rp++)=cpu_to_le32(ri); if (0 != ra) *(rp++)=cpu_to_le32(ra); } } kfree(skips); return rp;}static int ov_risc_frame(struct cx8800_dev *dev, struct cx8800_fh *fh, struct cx88_buffer *buf){ struct btcx_skiplist *skips; u32 instructions,fields; u32 *rp; int rc; /* skip list for window clipping */ if (NULL == (skips = kmalloc(sizeof(*skips) * fh->nclips,GFP_KERNEL))) return -ENOMEM; fields = 0; if (V4L2_FIELD_HAS_TOP(fh->win.field)) fields++; if (V4L2_FIELD_HAS_BOTTOM(fh->win.field)) 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(core->tvnorm); maxh = norm_maxh(core->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), 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) { struct cx88_core *core = dev->core; int board = core->board; dprintk(1,"video_open: setting radio device\n"); 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); cx_write(MO_GP3_IO, cx88_boards[board].radio.gpio3); dev->core->tvaudio = WW_FM; cx88_set_tvaudio(core); cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO); cx88_call_i2c_clients(dev->core,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(&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); } 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(get_queue(fh), vma);}/* ------------------------------------------------------------------ */static int get_control(struct cx8800_dev *dev, 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){ 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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -