📄 cyberpro.c
字号:
/* * CyberPro 2000 video capture driver for the Rebel.com NetWinder * * (C) 1999-2000 Russell King * * Re-written from Rebel.com's vidcap driver. * * Architecture * ------------ * The NetWinder video capture consists of a SAA7111 video decoder chip * connected to the CyberPro feature bus. The video data is captured to * the VGA memory, where the CyberPro can overlay (by chromakeying) the * data onto the VGA display. * * The CyberPro also has some nifty features, including a second overlay * and picture in picture mode. We do not currently use these features. * * Power Saving * ------------ * Please note that rev.5 NetWinders have the ability to hold the SAA7111 * decoder chip into reset, which saves power. The only time at which * this is done is when the driver is unloaded, which implies that this * is compiled as a module. * * In this case, you will want the kernel to automatically load this * driver when required. Place the following line in /etc/modules.conf * to enable this: * * alias char-major-81-0 cyberpro * * The relevant modules will be automatically loaded by modprobe on a * as and when needed basis. * * Capture resolution * ------------------ * The maximum useful capture resolution is: * 625-line UK: 716x576 * 525-line US: ? * * Bugs * ---- * 1. The CyberPro chip seems to be prone to randomly scribbling over VGA * memory [hopefully fixed with new capture enable/freeze stuff] * 2. read()ing pauses video capture, and sometimes triggers bug 1. * 3. mmap() is not supported (requires BM-DMA - see bug 4) * 4. Really, we want to do scatter BM-DMA. Is the CyberPro capable of this? * The Cyberpro seems to randomly scribble to various PCI addresses if you * transfer >16 words. * 5. We shouldn't ignore O_NONBLOCK when reading a frame. * 6. The incoming stream on the NetWinder is CCIR656, which is YUV422. * CyberPro docs also call the format we capture and overlay "YUV422", * but we actually seem to have Y, U, Y, V bytes (is this YUYV format?) */#include <linux/config.h>#include <linux/module.h>#include <linux/videodev.h>#include <linux/video_decoder.h>#include <linux/mm.h>#include <linux/i2c-old.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/kmod.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/pgtable.h>#include <asm/pgalloc.h>MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");MODULE_DESCRIPTION("CyberPro v4l video grabber");MODULE_LICENSE("GPL");#include "../../video/cyber2000fb.h"/* * These enable various experimental features. Most of these * are just plain broken or just don't work at the moment. *//* * Enable this if you want mmap() access. (see bug 4) */#undef USE_MMAP/* * Enable this if you want mmio access. (slow) */#define USE_MMIO/* * The V4L API is unclear whether VIDIOCSCAPTURE call is allowed while * capture is running. The default is to disallow the call. * * Define this if you do want to allow the call while capture is active. */#undef ALLOW_SCAPTURE_WHILE_CAP/* * We capture two frames */#define NR_FRAMES 2/* * One frame of video is 202 pages, assuming YUV422 format, 716x576 */#define NR_PAGES 202struct src_info { unsigned int offset; /* offset of source data */ unsigned int x; /* source x */ unsigned int y; /* source y */ unsigned int width; /* source width */ unsigned int height; /* source height */ unsigned int format; /* source format */};struct dst_info { unsigned int x; /* destination x */ unsigned int y; /* destination y */ unsigned int width; /* destination width */ unsigned int height; /* destination height */ unsigned int chromakey; /* chromakey */ unsigned int flags; /* flags (eg, chromakey enable) */};struct cyberpro_vidinfo;struct win_info { void (*init)(struct cyberpro_vidinfo *dp, struct win_info *wi); void (*set_src)(struct cyberpro_vidinfo *dp, struct win_info *wi); void (*set_win)(struct cyberpro_vidinfo *dp, struct win_info *wi); void (*ctl)(struct cyberpro_vidinfo *dp, struct win_info *wi, int on_off); /* public */ struct src_info src; struct dst_info dst; /* private */ unsigned short vid_fifo_ctl; unsigned char vid_fmt; unsigned char vid_disp_ctl1; unsigned char vid_fifo_ctl1; unsigned char vid_misc_ctl1;};struct framebuf { unsigned int offset; /* mmap offset for this frame */ unsigned int status;#define FRAME_FREE 0#define FRAME_DONE 1#define FRAME_WAITING 2#define FRAME_GRABBING 3 /* * Bus-Master DMA stuff. Note that we should * probably use the kiovec stuff instead. */ unsigned long bus_addr[NR_PAGES]; /* list of pages */ struct page *pages[NR_PAGES]; void *buffer; int dbg;};struct cyberpro_vidinfo { struct video_device *dev; struct i2c_bus *bus; struct cyberpro_info info; /* host information */ unsigned char *regs; unsigned int irq; /* PCI interrupt number */ /* hardware configuration */ unsigned int stream_fmt; /* format of stream from decoder*/ /* software settings */ unsigned int decoder:1; /* decoder loaded */ unsigned int interlace:1; /* interlace */ unsigned int buf_set:1; /* VIDIOCSFBUF has been issued */ unsigned int win_set:1; /* VIDIOCSWIN has been issued */ unsigned int cap_active:1; /* capture is active */ unsigned int ovl_active:1; /* overlay is active */ unsigned int mmaped:1; /* buffer is mmap()d */ unsigned int unused:25; unsigned int users; /* number of users */ unsigned long cap_mem_offset; /* capture framebuffer offset */ void * buffer; /* kernel capture buffer */ unsigned int norm; /* video standard */ struct video_capability cap; /* capabilities */ struct video_picture pic; /* current picture settings */ struct video_buffer buf; /* display parameters */ struct video_capture capt; /* video capture params */ struct win_info *ovl; /* overlay window set */ struct win_info ext; /* "Extended" window info */ struct win_info v2; /* "V2" window info */ struct win_info x2; /* "X2" window info */ unsigned int bm_offset; /* Cap memory bus master offset */ unsigned int bm_index; /* Cap page index */#ifdef USE_MMAP unsigned int frame_idx; /* currently grabbing frame */ unsigned int frame_size; struct framebuf frame[NR_FRAMES]; wait_queue_head_t frame_wait;#endif wait_queue_head_t vbl_wait; /* * cyberpro registers */ unsigned char cap_mode1; unsigned char cap_mode2; unsigned char cap_miscctl; unsigned char vfac1; unsigned char vfac3;};/* * Our access methods. */#define cyberpro_writel(val,reg,dp) writel(val, (dp)->regs + (reg))#define cyberpro_writew(val,reg,dp) writew(val, (dp)->regs + (reg))#define cyberpro_writeb(val,reg,dp) writeb(val, (dp)->regs + (reg))#define cyberpro_readb(reg,dp) readb((dp)->regs + (reg))static inline voidcyberpro_grphw(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp){ cyberpro_writew((reg & 255) | val << 8, 0x3ce, dp);}static void cyberpro_grphw8(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp){ cyberpro_grphw(reg, val, dp);}static unsigned char cyberpro_grphr8(int reg, struct cyberpro_vidinfo *dp){ cyberpro_writeb(reg, 0x3ce, dp); return cyberpro_readb(0x3cf, dp);}static void cyberpro_grphw16(int reg, unsigned int val, struct cyberpro_vidinfo *dp){ cyberpro_grphw(reg, val, dp); cyberpro_grphw(reg + 1, val >> 8, dp);}static void cyberpro_grphw24(int reg, unsigned int val, struct cyberpro_vidinfo *dp){ cyberpro_grphw(reg, val, dp); cyberpro_grphw(reg + 1, val >> 8, dp); cyberpro_grphw(reg + 2, val >> 16, dp);}#if 0static voidcyberpro_dbg_dump(void){ int i; unsigned char idx[] = { 0x30, 0x3e, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad }; printk(KERN_DEBUG); for (i = 0; i < sizeof(idx); i++) printk("%02x ", idx[i]); printk("\n" KERN_DEBUG); for (i = 0; i < sizeof(idx); i++) printk("%02x ", cyberpro_grphr8(idx[i])); printk("\n");}#endif/* * On the NetWinder, we can put the SAA7111 to sleep by holding * it in reset. * * Note: once we have initialised the SAA7111, we can't put it back to * sleep and expect it to keep its settings. Maybe a better solution * is to register/de-register the i2c bus in open/release? */static voiddecoder_sleep(int sleep){#ifdef CONFIG_ARCH_NETWINDER extern spinlock_t gpio_lock; spin_lock_irq(&gpio_lock); cpld_modify(CPLD_7111_DISABLE, sleep ? CPLD_7111_DISABLE : 0); spin_unlock_irq(&gpio_lock); if (!sleep) { /* * wait 20ms for device to wake up */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ / 50); }#endif}/* -------------------------------- I2C support ---------------------------- */#define I2C_DELAY 100static voidcyberpro_i2c_setlines(struct i2c_bus *bus, int ctrl, int data){ struct cyberpro_vidinfo *dp = bus->data; int v; v = (ctrl ? EXT_LATCH2_I2C_CLKEN : 0x00) | (data ? EXT_LATCH2_I2C_DATEN : 0x00); cyberpro_grphw8(EXT_LATCH2, v, dp); udelay(I2C_DELAY);}static intcyberpro_i2c_getdataline(struct i2c_bus *bus){ struct cyberpro_vidinfo *dp = bus->data; unsigned long flags; int v; save_flags(flags); cli(); v = cyberpro_grphr8(EXT_LATCH2, dp); restore_flags(flags); return v & EXT_LATCH2_I2C_DAT ? 1 : 0;}static voidcyberpro_i2c_attach(struct i2c_bus *bus, int id){ struct cyberpro_vidinfo *dp = bus->data; int zero = 0; if (id == I2C_DRIVERID_VIDEODECODER) { __u16 norm = dp->norm; i2c_control_device(bus, id, DECODER_SET_NORM, &norm); i2c_control_device(bus, id, DECODER_SET_PICTURE, &dp->pic); i2c_control_device(bus, id, DECODER_ENABLE_OUTPUT, &zero); dp->decoder = 1; }}static voidcyberpro_i2c_detach(struct i2c_bus *bus, int id){ struct cyberpro_vidinfo *dp = bus->data; if (id == I2C_DRIVERID_VIDEODECODER) dp->decoder = 0;}static struct i2c_bus cyberpro_i2c_bus = { name: "", id: I2C_BUSID_CYBER2000, bus_lock: SPIN_LOCK_UNLOCKED, attach_inform: cyberpro_i2c_attach, detach_inform: cyberpro_i2c_detach, i2c_setlines: cyberpro_i2c_setlines, i2c_getdataline: cyberpro_i2c_getdataline,};/*------------------------- Extended Overlay Window ------------------------- * Initialise 1st overlay window (works) */static voidcyberpro_ext_init(struct cyberpro_vidinfo *dp, struct win_info *wi){ wi->vid_fifo_ctl = 0xf87c; wi->vid_fmt = EXT_VID_FMT_YUV422; wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | EXT_VID_DISP_CTL1_NOCLIP; wi->vid_fifo_ctl1 = EXT_VID_FIFO_CTL1_INTERLEAVE | EXT_VID_FIFO_CTL1_OE_HIGH; wi->vid_misc_ctl1 = 0; cyberpro_grphw8 (EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); cyberpro_grphw16(EXT_DDA_X_INIT, 0x0800, dp); cyberpro_grphw16(EXT_DDA_Y_INIT, 0x0800, dp); cyberpro_grphw16(EXT_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); cyberpro_grphw8 (EXT_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp);}/* * Set the source parameters for the extended window */static voidcyberpro_ext_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi){ unsigned int phase, pitch; pitch = (wi->src.width >> 2) & 0x0fff; phase = (wi->src.width + 3) >> 2; wi->vid_fmt &= ~7; switch (wi->src.format) { case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; } cyberpro_grphw24(EXT_MEM_START, wi->src.offset, dp); cyberpro_grphw16(EXT_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); cyberpro_grphw8 (EXT_SRC_WIN_WIDTH, phase, dp); cyberpro_grphw8 (EXT_VID_FMT, wi->vid_fmt, dp);}/* * Set overlay1 window */static voidcyberpro_ext_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi){ unsigned int xscale, yscale; unsigned int xoff, yoff; /* * Note: the offset does not appear to be influenced by * hardware scrolling. */ xoff = yoff = 0; xoff += wi->dst.x; yoff += wi->dst.y; xscale = wi->src.width; if (wi->dst.width >= wi->src.width * 2) { wi->vid_fmt |= EXT_VID_FMT_DBL_H_PIX; xscale *= 2; } else { wi->vid_fmt &= ~EXT_VID_FMT_DBL_H_PIX; } xscale = ((xscale - /*2*/0) * 4096) / wi->dst.width; yscale = ((wi->src.height - /*2*/0) * 4096) / wi->dst.height; cyberpro_grphw16(EXT_X_START, xoff, dp); cyberpro_grphw16(EXT_X_END, xoff + wi->dst.width, dp); cyberpro_grphw16(EXT_Y_START, yoff, dp); cyberpro_grphw16(EXT_Y_END, yoff + wi->dst.height, dp); cyberpro_grphw24(EXT_COLOUR_COMPARE, wi->dst.chromakey, dp); cyberpro_grphw16(EXT_DDA_X_INC, xscale, dp); cyberpro_grphw16(EXT_DDA_Y_INC, yscale, dp); cyberpro_grphw8(EXT_VID_FMT, wi->vid_fmt, dp); if (wi->dst.flags & VIDEO_WINDOW_CHROMAKEY) wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_IGNORE_CCOMP; else wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_IGNORE_CCOMP;}/* * Enable or disable the 1st overlay window. Note that for anything * useful to be displayed, we must have capture enabled. */static voidcyberpro_ext_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on){ if (on) wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; else wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; cyberpro_grphw8(EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp);}/*------------------------------- V2 Overlay Window ------------------------- * Initialise 2nd overlay window (guesswork) */static voidcyberpro_v2_init(struct cyberpro_vidinfo *dp, struct win_info *wi){ wi->vid_fifo_ctl = 0xf87c; wi->vid_fmt = EXT_VID_FMT_YUV422; wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | EXT_VID_DISP_CTL1_NOCLIP; wi->vid_fifo_ctl1 = 0x06; wi->vid_misc_ctl1 = 0; cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); cyberpro_grphw8 (Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); /* No DDA init values */ cyberpro_grphw16(Y_V2_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); cyberpro_grphw8 (Y_V2_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp);}/* * Set the source parameters for the v2 window */static voidcyberpro_v2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi){ unsigned int phase, pitch; pitch = (wi->src.width >> 2) & 0x0fff; phase = (wi->src.width + 3) >> 2; wi->vid_fmt &= ~7; switch (wi->src.format) { case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -