📄 cx88-video.c
字号:
return 0; } cx_andor(MO_AFECFG_IO, 0x1f, 0x0); cx88_set_tvaudio(dev); // cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); cx_write(MO_AUDD_LNGTH, 128/8); /* fifo size */ cx_write(MO_AUDR_LNGTH, 128/8); /* fifo size */ cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */ return 0;}static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm){ u32 fsc8; u32 adc_clock; u32 vdec_clock; u64 tmp64; u32 bdelay,agcdelay,htotal; dev->tvnorm = norm; fsc8 = norm_fsc8(norm); adc_clock = xtal; vdec_clock = fsc8; dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d\n", norm->name, fsc8, adc_clock, vdec_clock); set_pll(dev,2,vdec_clock); dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat);#if 1 // FIXME: as-is from DScaler dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", norm->cxoformat, cx_read(MO_OUTPUT_FORMAT)); cx_write(MO_OUTPUT_FORMAT, norm->cxoformat);#endif // MO_SCONV_REG = adc clock / video dec clock * 2^17 tmp64 = adc_clock * (u64)(1 << 17); do_div(tmp64, vdec_clock); dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n", (u32)tmp64, cx_read(MO_SCONV_REG)); cx_write(MO_SCONV_REG, (u32)tmp64); // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22 tmp64 = fsc8 * (u64)(1 << 22); do_div(tmp64, vdec_clock); dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n", (u32)tmp64, cx_read(MO_SUB_STEP)); cx_write(MO_SUB_STEP, (u32)tmp64); // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22 tmp64 = 4406250 * 8 * (u64)(1 << 22); do_div(tmp64, vdec_clock); dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n", (u32)tmp64, cx_read(MO_SUB_STEP_DR)); cx_write(MO_SUB_STEP_DR, (u32)tmp64); // bdelay + agcdelay bdelay = vdec_clock * 65 / 20000000 + 21; agcdelay = vdec_clock * 68 / 20000000 + 15; dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n", (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay); cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay); // htotal tmp64 = norm_htotal(norm) * (u64)vdec_clock; do_div(tmp64, fsc8); htotal = (u32)tmp64 | (norm_notchfilter(norm) << 11); dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n", htotal, cx_read(MO_HTOTAL), (u32)tmp64); cx_write(MO_HTOTAL, htotal); // vbi stuff cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm) << 11) | */ norm_vbipack(norm))); // audio set_tvaudio(dev); // tell i2c chips#ifdef V4L2_I2C_CLIENTS cx8800_call_i2c_clients(dev,VIDIOC_S_STD,&norm->id);#else { struct video_channel c; memset(&c,0,sizeof(c)); c.channel = dev->input; c.norm = VIDEO_MODE_PAL; if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP))) c.norm = VIDEO_MODE_NTSC; if (norm->id & V4L2_STD_SECAM) c.norm = VIDEO_MODE_SECAM; cx8800_call_i2c_clients(dev,VIDIOCSCHAN,&c); }#endif // done return 0;}static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field){ unsigned int swidth = norm_swidth(dev->tvnorm); unsigned int sheight = norm_maxh(dev->tvnorm); u32 value; dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, V4L2_FIELD_HAS_TOP(field) ? "T" : "", V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", dev->tvnorm->name); if (!V4L2_FIELD_HAS_BOTH(field)) height *= 2; // recalc H delay and scale registers value = (width * norm_hdelay(dev->tvnorm)) / swidth; value &= 0x3fe; cx_write(MO_HDELAY_EVEN, value); cx_write(MO_HDELAY_ODD, value); dprintk(1,"set_scale: hdelay 0x%04x\n", value); value = (swidth * 4096 / width) - 4096; cx_write(MO_HSCALE_EVEN, value); cx_write(MO_HSCALE_ODD, value); dprintk(1,"set_scale: hscale 0x%04x\n", value); cx_write(MO_HACTIVE_EVEN, width); cx_write(MO_HACTIVE_ODD, width); dprintk(1,"set_scale: hactive 0x%04x\n", width); // recalc V scale Register (delay is constant) cx_write(MO_VDELAY_EVEN, norm_vdelay(dev->tvnorm)); cx_write(MO_VDELAY_ODD, norm_vdelay(dev->tvnorm)); dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(dev->tvnorm)); value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff; cx_write(MO_VSCALE_EVEN, value); cx_write(MO_VSCALE_ODD, value); dprintk(1,"set_scale: vscale 0x%04x\n", value); cx_write(MO_VACTIVE_EVEN, sheight); cx_write(MO_VACTIVE_ODD, sheight); dprintk(1,"set_scale: vactive 0x%04x\n", sheight); // setup filters value = 0; value |= (1 << 19); // CFILT (default) if (V4L2_FIELD_INTERLACED == field) value |= (1 << 3); // VINT (interlaced vertical scaling) if (width < 385) value |= (1 << 0); // 3-tap interpolation if (width < 193) value |= (1 << 1); // 5-tap interpolation cx_write(MO_FILTER_EVEN, value); cx_write(MO_FILTER_ODD, value); dprintk(1,"set_scale: filter 0x%04x\n", value); return 0;}static int video_mux(struct cx8800_dev *dev, unsigned int input){ dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n", input, INPUT(input)->vmux, INPUT(input)->gpio0,INPUT(input)->gpio1, INPUT(input)->gpio2,INPUT(input)->gpio3); dev->input = input; cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14); cx_write(MO_GP0_IO, INPUT(input)->gpio0); cx_write(MO_GP1_IO, INPUT(input)->gpio1); cx_write(MO_GP2_IO, INPUT(input)->gpio2); cx_write(MO_GP3_IO, INPUT(input)->gpio3); switch (INPUT(input)->type) { case CX88_VMUX_SVIDEO: cx_set(MO_AFECFG_IO, 0x00000001); cx_set(MO_INPUT_FORMAT, 0x00010010); break; default: cx_clear(MO_AFECFG_IO, 0x00000001); cx_clear(MO_INPUT_FORMAT, 0x00010010); break; } return 0;}/* ------------------------------------------------------------------ */static int start_video_dma(struct cx8800_dev *dev, struct cx88_dmaqueue *q, struct cx88_buffer *buf){ /* setup fifo + format */ cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21], buf->bpl, buf->risc.dma); set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field); cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); /* reset counter */ cx_write(MO_VIDY_GPCNTRL,0x3); q->count = 1; /* enable irqs */ cx_set(MO_PCI_INTMSK, 0x00fc01); cx_set(MO_VID_INTMSK, 0x0f0011); /* enable capture */ cx_set(VID_CAPTURE_CONTROL,0x06); /* start dma */ cx_set(MO_DEV_CNTRL2, (1<<5)); cx_set(MO_VID_DMACNTRL, 0x11); return 0;}static int restart_video_queue(struct cx8800_dev *dev, struct cx88_dmaqueue *q){ struct cx88_buffer *buf, *prev; struct list_head *item; if (!list_empty(&q->active)) { buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); dprintk(2,"restart_queue [%p/%d]: restart dma\n", buf, buf->vb.i); start_video_dma(dev, q, buf); list_for_each(item,&q->active) { buf = list_entry(item, struct cx88_buffer, vb.queue); buf->count = q->count++; } mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); return 0; } prev = NULL; for (;;) { if (list_empty(&q->queued)) return 0; buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); if (NULL == prev) { list_del(&buf->vb.queue); 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] restart_queue - first active\n", buf,buf->vb.i); } else if (prev->vb.width == buf->vb.width && prev->vb.height == buf->vb.height && prev->fmt == buf->fmt) { list_del(&buf->vb.queue); 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] restart_queue - move to active\n", buf,buf->vb.i); } else { return 0; } prev = buf; }}/* ------------------------------------------------------------------ */static intbuffer_setup(struct file *file, unsigned int *count, unsigned int *size){ struct cx8800_fh *fh = file->private_data; *size = fh->fmt->depth*fh->width*fh->height >> 3; if (0 == *count) *count = 32; while (*size * *count > vid_limit * 1024 * 1024) (*count)--; return 0;}static intbuffer_prepare(struct file *file, struct videobuf_buffer *vb, enum v4l2_field field){ struct cx8800_fh *fh = file->private_data; struct cx8800_dev *dev = fh->dev; struct cx88_buffer *buf = (struct cx88_buffer*)vb; int rc, init_buffer = 0; BUG_ON(NULL == fh->fmt); if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) || fh->height < 32 || fh->height > norm_maxh(dev->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 file *file, struct videobuf_buffer *vb){ struct cx88_buffer *buf = (struct cx88_buffer*)vb; struct cx88_buffer *prev; struct cx8800_fh *fh = file->private_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 | 0x10000); 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 file *file, struct videobuf_buffer *vb){ struct cx88_buffer *buf = (struct cx88_buffer*)vb; struct cx8800_fh *fh = file->private_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))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -