📄 saa7134-video.c
字号:
/* no, someone else uses it */ mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ fh->resources |= bit; dev->resources |= bit; dprintk("res: get %d\n",bit); mutex_unlock(&dev->lock); return 1;}static int res_check(struct saa7134_fh *fh, unsigned int bit){ return (fh->resources & bit);}static int res_locked(struct saa7134_dev *dev, unsigned int bit){ return (dev->resources & bit);}staticvoid res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits){ BUG_ON((fh->resources & bits) != bits); mutex_lock(&dev->lock); fh->resources &= ~bits; dev->resources &= ~bits; dprintk("res: put %d\n",bits); mutex_unlock(&dev->lock);}/* ------------------------------------------------------------------ */static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm){ dprintk("set tv norm = %s\n",norm->name); dev->tvnorm = norm; /* setup cropping */ dev->crop_bounds.left = norm->h_start; dev->crop_defrect.left = norm->h_start; dev->crop_bounds.width = norm->h_stop - norm->h_start +1; dev->crop_defrect.width = norm->h_stop - norm->h_start +1; dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2; dev->crop_defrect.top = norm->video_v_start*2; dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624) - dev->crop_bounds.top; dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2; dev->crop_current = dev->crop_defrect; saa7134_set_tvnorm_hw(dev);}static void video_mux(struct saa7134_dev *dev, int input){ dprintk("video input = %d [%s]\n", input, card_in(dev, input).name); dev->ctl_input = input; set_tvnorm(dev, dev->tvnorm); saa7134_tvaudio_setinput(dev, &card_in(dev, input));}static void saa7134_set_decoder(struct saa7134_dev *dev){ int luma_control, sync_control, mux; struct saa7134_tvnorm *norm = dev->tvnorm; mux = card_in(dev, dev->ctl_input).vmux; luma_control = norm->luma_control; sync_control = norm->sync_control; if (mux > 5) luma_control |= 0x80; /* svideo */ if (noninterlaced || dev->nosignal) sync_control |= 0x20; /* setup video decoder */ saa_writeb(SAA7134_INCR_DELAY, 0x08); saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00); saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90); saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90); saa_writeb(SAA7134_HSYNC_START, 0xeb); saa_writeb(SAA7134_HSYNC_STOP, 0xe0); saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing); saa_writeb(SAA7134_SYNC_CTRL, sync_control); saa_writeb(SAA7134_LUMA_CTRL, luma_control); saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); 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); saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); saa_writeb(SAA7134_ANALOG_ADC, 0x01); saa_writeb(SAA7134_VGATE_START, 0x11); saa_writeb(SAA7134_VGATE_STOP, 0xfe); saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);}void saa7134_set_tvnorm_hw(struct saa7134_dev *dev){ saa7134_set_decoder(dev); if (card_in(dev, dev->ctl_input).tv) saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id); /* Set the correct norm for the saa6752hs. This function does nothing if there is no saa6752hs. */ saa7134_i2c_call_saa6752(dev, VIDIOC_S_STD, &dev->tvnorm->id);}static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale){ static const struct { int xpsc; int xacl; int xc2_1; int xdcg; int vpfy; } vals[] = { /* XPSC XACL XC2_1 XDCG VPFY */ { 1, 0, 0, 0, 0 }, { 2, 2, 1, 2, 2 }, { 3, 4, 1, 3, 2 }, { 4, 8, 1, 4, 2 }, { 5, 8, 1, 4, 2 }, { 6, 8, 1, 4, 3 }, { 7, 8, 1, 4, 3 }, { 8, 15, 0, 4, 3 }, { 9, 15, 0, 4, 3 }, { 10, 16, 1, 5, 3 }, }; static const int count = ARRAY_SIZE(vals); int i; for (i = 0; i < count; i++) if (vals[i].xpsc == prescale) break; if (i == count) return; saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc); saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl); saa_writeb(SAA7134_LEVEL_CTRL(task), (vals[i].xc2_1 << 3) | (vals[i].xdcg)); saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f, (vals[i].vpfy << 2) | vals[i].vpfy);}static void set_v_scale(struct saa7134_dev *dev, int task, int yscale){ int val,mirror; saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff); saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8); mirror = (dev->ctl_mirror) ? 0x02 : 0x00; if (yscale < 2048) { /* LPI */ dprintk("yscale LPI yscale=%d\n",yscale); saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror); saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40); saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40); } else { /* ACM */ val = 0x40 * 1024 / yscale; dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val); saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror); saa_writeb(SAA7134_LUMA_CONTRAST(task), val); saa_writeb(SAA7134_CHROMA_SATURATION(task), val); } saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80);}static void set_size(struct saa7134_dev *dev, int task, int width, int height, int interlace){ int prescale,xscale,yscale,y_even,y_odd; int h_start, h_stop, v_start, v_stop; int div = interlace ? 2 : 1; /* setup video scaler */ h_start = dev->crop_current.left; v_start = dev->crop_current.top/2; h_stop = (dev->crop_current.left + dev->crop_current.width -1); v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2; saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff); saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8); saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff); saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8); saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff); saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8); saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff); saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8); prescale = dev->crop_current.width / width; if (0 == prescale) prescale = 1; xscale = 1024 * dev->crop_current.width / prescale / width; yscale = 512 * div * dev->crop_current.height / height; dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale); set_h_prescale(dev,task,prescale); saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff); saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8); set_v_scale(dev,task,yscale); saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff); saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8); saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff); saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8); /* deinterlace y offsets */ y_odd = dev->ctl_y_odd; y_even = dev->ctl_y_even; saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);}/* ------------------------------------------------------------------ */struct cliplist { __u16 position; __u8 enable; __u8 disable;};static void set_cliplist(struct saa7134_dev *dev, int reg, struct cliplist *cl, int entries, char *name){ __u8 winbits = 0; int i; for (i = 0; i < entries; i++) { winbits |= cl[i].enable; winbits &= ~cl[i].disable; if (i < 15 && cl[i].position == cl[i+1].position) continue; saa_writeb(reg + 0, winbits); saa_writeb(reg + 2, cl[i].position & 0xff); saa_writeb(reg + 3, cl[i].position >> 8); dprintk("clip: %s winbits=%02x pos=%d\n", name,winbits,cl[i].position); reg += 8; } for (; reg < 0x400; reg += 8) { saa_writeb(reg+ 0, 0); saa_writeb(reg + 1, 0); saa_writeb(reg + 2, 0); saa_writeb(reg + 3, 0); }}static int clip_range(int val){ if (val < 0) val = 0; return val;}/* Sort into smallest position first order */static int cliplist_cmp(const void *a, const void *b){ const struct cliplist *cla = a; const struct cliplist *clb = b; if (cla->position < clb->position) return -1; if (cla->position > clb->position) return 1; return 0;}static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, int nclips, int interlace){ struct cliplist col[16], row[16]; int cols = 0, rows = 0, i; int div = interlace ? 2 : 1; memset(col, 0, sizeof(col)); memset(row, 0, sizeof(row)); for (i = 0; i < nclips && i < 8; i++) { col[cols].position = clip_range(clips[i].c.left); col[cols].enable = (1 << i); cols++; col[cols].position = clip_range(clips[i].c.left+clips[i].c.width); col[cols].disable = (1 << i); cols++; row[rows].position = clip_range(clips[i].c.top / div); row[rows].enable = (1 << i); rows++; row[rows].position = clip_range((clips[i].c.top + clips[i].c.height) / div); row[rows].disable = (1 << i); rows++; } sort(col, cols, sizeof col[0], cliplist_cmp, NULL); sort(row, rows, sizeof row[0], cliplist_cmp, NULL); set_cliplist(dev,0x380,col,cols,"cols"); set_cliplist(dev,0x384,row,rows,"rows"); return 0;}static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win){ enum v4l2_field field; int maxw, maxh; if (NULL == dev->ovbuf.base) return -EINVAL; if (NULL == dev->ovfmt) return -EINVAL; if (win->w.width < 48 || win->w.height < 32) return -EINVAL; if (win->clipcount > 2048) return -EINVAL; field = win->field; maxw = dev->crop_current.width; maxh = dev->crop_current.height; 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 start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh){ unsigned long base,control,bpl; int err; err = verify_preview(dev,&fh->win); if (0 != err) return err; dev->ovfield = fh->win.field; dprintk("start_preview %dx%d+%d+%d %s field=%s\n", fh->win.w.width,fh->win.w.height, fh->win.w.left,fh->win.w.top, dev->ovfmt->name,v4l2_field_names[dev->ovfield]); /* setup window + clipping */ set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height, V4L2_FIELD_HAS_BOTH(dev->ovfield)); setup_clipping(dev,fh->clips,fh->nclips, V4L2_FIELD_HAS_BOTH(dev->ovfield)); if (dev->ovfmt->yuv) saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03); else saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01); saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20); /* dma: setup channel 1 (= Video Task B) */ base = (unsigned long)dev->ovbuf.base; base += dev->ovbuf.fmt.bytesperline * fh->win.w.top; base += dev->ovfmt->depth/8 * fh->win.w.left; bpl = dev->ovbuf.fmt.bytesperline; control = SAA7134_RS_CONTROL_BURST_16; if (dev->ovfmt->bswap) control |= SAA7134_RS_CONTROL_BSWAP; if (dev->ovfmt->wswap) control |= SAA7134_RS_CONTROL_WSWAP; if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) { saa_writel(SAA7134_RS_BA1(1),base); saa_writel(SAA7134_RS_BA2(1),base+bpl); saa_writel(SAA7134_RS_PITCH(1),bpl*2); saa_writel(SAA7134_RS_CONTROL(1),control); } else { saa_writel(SAA7134_RS_BA1(1),base); saa_writel(SAA7134_RS_BA2(1),base); saa_writel(SAA7134_RS_PITCH(1),bpl); saa_writel(SAA7134_RS_CONTROL(1),control); } /* start dma */ dev->ovenable = 1; saa7134_set_dmabits(dev); return 0;}static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh){ dev->ovenable = 0; saa7134_set_dmabits(dev); return 0;}/* ------------------------------------------------------------------ */static int buffer_activate(struct saa7134_dev *dev, struct saa7134_buf *buf, struct saa7134_buf *next){ unsigned long base,control,bpl; unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */ dprintk("buffer_activate buf=%p\n",buf); buf->vb.state = VIDEOBUF_ACTIVE; buf->top_seen = 0; set_size(dev,TASK_A,buf->vb.width,buf->vb.height, V4L2_FIELD_HAS_BOTH(buf->vb.field)); if (buf->fmt->yuv) saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03); else saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01); saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm); /* DMA: setup channel 0 (= Video Task A0) */ base = saa7134_buffer_base(buf); if (buf->fmt->planar) bpl = buf->vb.width; else bpl = (buf->vb.width * buf->fmt->depth) / 8; control = SAA7134_RS_CONTROL_BURST_16 | SAA7134_RS_CONTROL_ME | (buf->pt->dma >> 12); if (buf->fmt->bswap) control |= SAA7134_RS_CONTROL_BSWAP; if (buf->fmt->wswap) control |= SAA7134_RS_CONTROL_WSWAP; if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { /* interlaced */ saa_writel(SAA7134_RS_BA1(0),base); saa_writel(SAA7134_RS_BA2(0),base+bpl); saa_writel(SAA7134_RS_PITCH(0),bpl*2); } else { /* non-interlaced */ saa_writel(SAA7134_RS_BA1(0),base); saa_writel(SAA7134_RS_BA2(0),base); saa_writel(SAA7134_RS_PITCH(0),bpl); } saa_writel(SAA7134_RS_CONTROL(0),control); if (buf->fmt->planar) { /* DMA: setup channel 4+5 (= planar task A) */ bpl_uv = bpl >> buf->fmt->hshift; lines_uv = buf->vb.height >> buf->fmt->vshift; base2 = base + bpl * buf->vb.height; base3 = base2 + bpl_uv * lines_uv; if (buf->fmt->uvswap) tmp = base2, base2 = base3, base3 = tmp; dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", bpl_uv,lines_uv,base2,base3); if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { /* interlaced */ saa_writel(SAA7134_RS_BA1(4),base2); saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv); saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2); saa_writel(SAA7134_RS_BA1(5),base3); saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv); saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2); } else { /* non-interlaced */ saa_writel(SAA7134_RS_BA1(4),base2); saa_writel(SAA7134_RS_BA2(4),base2); saa_writel(SAA7134_RS_PITCH(4),bpl_uv); saa_writel(SAA7134_RS_BA1(5),base3); saa_writel(SAA7134_RS_BA2(5),base3); saa_writel(SAA7134_RS_PITCH(5),bpl_uv); } saa_writel(SAA7134_RS_CONTROL(4),control); saa_writel(SAA7134_RS_CONTROL(5),control); } /* start DMA */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -