/* * Copyright (C) Jim Zajkowski (package maintainer) * Copyright (C) Chris Lahey (original author) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * This code was taken from the Winnov Videum V4L2 driver, by Bill Dirks * . His notice follows, */ /* Winnov Videum * Video for Linux Two driver * * This program is copyright 1998 * by Bill Dirks * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * gcc -c -O2 -Wall wnv.c */ #ifndef __KERNEL__ #define __KERNEL__ #endif #ifndef MODULE #define MODULE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define USE_CCD #ifndef PCI_DEVICE_ID_CMD_647 #define PCI_DEVICE_ID_CMD_647 0x0647 #endif #define PKMOD "wnv: " #ifdef PCMCIA_DEBUG #define debug_msg(fmt,arg...) printk(KERN_DEBUG PKMOD fmt,##arg) #else #define debug_msg(fmt,arg...) #endif #ifdef PCMCIA_DEBUG #define err_msg(fmt,arg...) printk(KERN_ERR PKMOD fmt,##arg) #else #define err_msg(fmt,arg...) #endif #ifdef PCMCIA_DEBUG #define info_msg(fmt,arg...) printk(KERN_INFO PKMOD fmt,##arg) #else #define info_msg(fmt,arg...) #endif /* This is the Videum configuration EEPROM */ #include "wnvrom.h" /* Video controls */ static struct v4l2_queryctrl videum_control[] = { {V4L2_CID_BRIGHTNESS, "Brightness", 0, 15, 1, 8, V4L2_CTRL_TYPE_INTEGER}, {V4L2_CID_CONTRAST, "Contrast", 0, 15, 1, 4, V4L2_CTRL_TYPE_INTEGER}, {V4L2_CID_SATURATION, "Saturation", 0, 15, 1, 4, V4L2_CTRL_TYPE_INTEGER}, {V4L2_CID_HUE, "Hue", 0, 15, 1, 8, V4L2_CTRL_TYPE_INTEGER}, }; static dev_info_t dev_info = "wnv_cs"; #define MAXCONTROLS (sizeof(videum_control)/sizeof(videum_control[0])) #define VCTRL_BRIGHTNESS 0 #define VCTRL_CONTRAST 1 #define VCTRL_SATURATION 2 #define VCTRL_HUE 3 static int find_vctrl(int id) { int i; if (id == V4L2_CID_PRIVATE_BASE || id < V4L2_CID_BASE || id > V4L2_CID_LASTP1) return -EDOM; for (i = MAXCONTROLS - 1; i >= 0; i--) if (videum_control[i].id == id) break; if (i < 0) i = -EINVAL; return i; } void wnv_sleep( int milliseconds ) { current->state = TASK_INTERRUPTIBLE; schedule_timeout (HZ * milliseconds / 1000); } struct videum_device;/* forward reference */ struct video_decoder { int type; int version; int is_initialized; int i2c_addr1; int i2c_addr2; int ntsc_hskip; int ntsc_vskip; int ntsc_width; int ntsc_height; int ntsc_field_order; int pal_hskip; int pal_vskip; int pal_width; int pal_height; int pal_field_order; int preferred_field; int invert_vsync; int hadjust; int vadjust; int num_inputs; int input; __u32 standards; __u32 standard; __u32 frame_period; int decoder_is_stable; __u32 decoder_stable_time; /* Changable method functions helps us support multiple */ /* different types of video decoders easily */ int (*initialize)(struct videum_device *dev); int (*set_input)(struct videum_device *dev, int x); int (*set_standard)(struct videum_device *dev, int x); int (*set_vcrmode)(struct videum_device *dev, int x); int (*is_stable)(struct videum_device *dev); int (*set_white_balance)(struct videum_device *dev, int x); }; struct video_source { struct v4l2_input input; int control[MAXCONTROLS]; struct v4l2_tuner tuner; int wavi_vsc; int wavi_vss; int wavi_vhsd; int vcrmode; }; /* Indices into the array of video_source's */ #define VSOURCE_MXC 0 #define VSOURCE_COMP 1 #define VSOURCE_SVIDEO 2 #define VSOURCE_TUNER 3 #define VSOURCE_COUNT 4 /* Microcode programs */ struct ucode_parms { __u8 *buffer; int source_x; int source_y; int source_width; int source_height; int field; int capture_width; int capture_height; int capture_min_size; int capture_max_size; int dma_line; int int1_line; int int2_line; int flags; int filter; }; /* Microcode compile flags */ #define COMPFLAG_NOVERTFILTER 0x00000001 #define COMPFLAG_FIELDORDER 0x00000002 #define COMPFLAG_VSDELAY 0x00000004 #define COMPFLAG_AVPRO_DMA 0x00000008 #define COMPFLAG_AVPRO_INT 0x00000010 #define COMPFLAG_HUFFMAN 0x00000020 #define COMPFLAG_MARKERCODE 0x00000040 #define COMPFLAG_EOFINT 0x00000080 #define COMPFLAG_VST_WHEN_DONE 0x00000100 #define UC_SIZE 4096 struct microcode { struct ucode_parms parm; __u8 uc[UC_SIZE]; int length; }; /* Bus-master scatter list */ struct scatter_node { __u32 addr; __u32 len; }; #define END_OF_SCATTER_LIST 0x80000000 /* Image translations */ struct lookup_rgb24 { __u32 u_rgb[256]; __u32 v_rgb[256]; __u32 y_rgb[256]; __u8 sat[1024]; }; struct lookup_dehuff { __u8 decode1[512]; /* Decode one code */ __u8 length1[512]; /* Length of one code */ __u32 decode4[4096]; /* Decode four codes */ __u8 length4[4096]; /* Length of four codes */ __u8 dcase[4096]; /* Case */ }; struct lookup { int type; int size; union { void *base; /* vmalloc() */ __u16 *rgb16; struct lookup_rgb24 *rgb24; struct lookup_dehuff *dehuff; } table; }; #define LUT_NULL 0 #define LUT_RGB555 1 #define LUT_RGB565 2 #define LUT_RGB24 3 #define LUT_DEHUFF4 4 #define LUT_DEHUFF5 5 #define LUT_DEHUFF6 6 #define LUT_DEHUFF7 7 #define LUT_STRETCH_H 8 #define LUT_STRETCH_V 9 struct translation { int type; int width; int height; __u8 *in; int in_stride; __u8 *out; int out_stride; int output_size; int output_is_user; int ilut; struct lookup lut[4]; }; #define XLAT_NULL 0 #define XLAT_YUYV_TO_UYVY 1 #define XLAT_YUYV_TO_YUV420 2 #define XLAT_YUYV_TO_GREY 4 #define XLAT_YUYV_TO_RGB555 6 #define XLAT_YUYV_TO_RGB565 7 #define XLAT_YUYV_TO_RGB24 8 #define XLAT_YUYV_TO_RGB32 9 #define XLAT_YCQ_TO_YUYV 10 #define XLAT_HUFF_TO_YUYV 11 /* Per-open data for handling multiple opens on one device */ struct device_open { int isopen; int noncapturing; struct videum_device *dev; }; #define MAX_OPENS 3 /* Streaming data buffer */ struct stream_buffer { struct v4l2_q_node qnode; struct v4l2_buffer vidbuf; int requested; __u8 *vaddress; /* vmalloc() */ struct scatter_node *dma_list; /* get_free_page() */ }; #define MAX_CAPTURE_BUFFERS 30 #define MAX_LOCKED_MEMORY 2000000 /* * Videum device structure * * One for each Videum in the system. * This structure holds all the global information the driver * needs about each board. */ typedef struct videum_device { struct v4l2_device v; char shortname[16]; int is_registered; int open_count; struct device_open open_data[MAX_OPENS]; int capturing_opens; /* Per-bus index number for each Videum */ int index; /* General type of Videum. See defines below */ int type; int wavi_type; /* Pointer to the pci_dev structure for this board */ struct pci_dev *pci; /* I/O Base address for non-PCI devices */ unsigned int iobase; unsigned int cfg; /* I/O performance */ int slave_rate; int dma_rate; /* Interrupts */ int irq; int ints_enabled; struct tq_struct tqnode_dpc;/* for BH */ struct timer_list tlnode;/* for polling interrupts */ struct wait_queue *new_video_frame; /* Copy of the board's configuration EEPROM */ struct EEPROM eeprom; /* Used for I2C routines */ __u16 ctl; /* On-board SRAM memory map */ int sram_base; int sram_size; int sram_audio; int sram_audio_size; int sram_ucode; int sram_ucode_size; int sram_linebuf; int sram_linebuf_size; int sram_video; int sram_video_size; /* Video decoder stuff */ struct video_decoder videc; struct video_source source[VSOURCE_COUNT]; /* WAVI video sampler microcode */ int uc_loaded; struct ucode_parms uc_parm[2]; __u8 uc_buffer[2][UC_SIZE]; int uc_length[2]; struct v4l2_format clientfmt; struct v4l2_captureparm capture; int input; /* Hardware capture format (see ucode_parms for width & height) */ int capture_compress; int capture_vdf; int capture_bpp; int capture_size; int capture_min_size; int capture_wcase_size; int capture_low_water; int capture_high_water; int capture_marker_spacing; /* Capture buffer */ __u8 *capture_buffer;/* vmalloc() */ int capture_buffer_size; struct scatter_node *capture_dma_list;/* get_free_page() */ /* Capture state */ int ready_to_capture; int sampler_enabled; int capture_completed; unsigned long time_acquired;/* millisecond time stamp */ int streaming; struct stream_buffer stream_buf[MAX_CAPTURE_BUFFERS]; int stream_buffers_mapped; struct v4l2_queue stream_q_capture; struct v4l2_queue stream_q_done; /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* struct timeval stream_begin; */ stamp_t stream_begin; unsigned long stream_last_frame; __u8 *stream_capture_buffer; /* DMA */ int dma_mode; int dma_state; /* Image format conversions */ struct translation translation1; struct translation translation2; __u8 *xlat_temp;/* vmalloc() */ /* Performance statistics */ struct v4l2_performance perf; /* PCMCIA Client data */ dev_link_t *link; __u16 *mem_start; __u16 *data; } videum_device; /* Values for type field */ #define VIDEUM_TYPE_BOGUS 0 #define VIDEUM_TYPE_PCI 1 #define VIDEUM_TYPE_ISA 2 #define VIDEUM_TYPE_PNPISA 3 #define VIDEUM_TYPE_PCMCIA 4 #define WINNOV_WAVI_93 93 #define WINNOV_WAVI_95 95 #define WINNOV_WAVI_97 97 #define WINNOV_WIMP 99 /* Values for videc.type, the video decoder type */ #define VIDEC_7110 0 #define VIDEC_7111A 1 #define VIDEC_VPX 2 #define VIDEC_CS76X5 3 /* Values for the microcode loaded status */ #define UC_UNDEF (-1) #define UC_NORMAL 0 #define UC_WORSTCASE 1 /* DMA modes */ #define DMAMODE_SLAVE 0/* not using DMA */ #define DMAMODE_SIMPLE 1/* DMA to capture_buffer */ #define DMAMODE_FRAMEBUFFER 2/* DMA to external frame buffer */ #define DMA_STATE_IDLE 0 #define DMA_STATE_ARMING 1 #define DMA_STATE_SUSPENDED 2 #define DMA_STATE_RUNNING 3 /* Extreme video dimensions */ #define MIN_WIDTH 32 #define MIN_HEIGHT 24 #define MAX_WIDTH 640 #define MAX_HEIGHT 288 #define MAX_FRAME_AGE 200 /* ms */ #define LOW_WATER 60 /* (%) Switch to higher quality */ #define HIGH_WATER 90 /* (%) Switch to lower quality */ /* Macros for PCI Videum i/o port addressing */ #define PCIBA0(dev) (dev->pci->base_address[0]&~1) #define PCIBA1(dev) (dev->pci->base_address[1]&~1) #define PCIBA2(dev) (dev->pci->base_address[2]&~1) #define PCIBA3(dev) (dev->pci->base_address[3]&~1) #define PCIBA4(dev) (dev->pci->base_address[4]&~1) #define WDATA(dev) (PCIBA2(dev)+0) #define LBA(dev) (PCIBA2(dev)+4) #define JTAG(dev) (PCIBA0(dev)+0) #define DMAC(dev) (PCIBA3(dev)+2) #define BMCMD0(dev) (PCIBA4(dev)+0) #define MRDMODE(dev) (PCIBA4(dev)+1) #define BMSTATUS0(dev) (PCIBA4(dev)+2) #define BMPRDT0(dev) (PCIBA4(dev)+4) #define BMCMD1(dev) (PCIBA4(dev)+8) #define BMSTATUS1(dev) (PCIBA4(dev)+10) #define BMPRDT1(dev) (PCIBA4(dev)+12) #define BMCMD_RW_CNRL (1 <<3) #define BMCMD_START_STOP (1<<0) #define BMSTATUS_DMA_INT (1<<2) #define BMSTATUS_ERROR (1<<1) #define BMSTATUS_ACTIVE (1<<0) #define DMAC_HIRQREQn (1<<4) #define DMAC_SWAP (1<<3) #define DMAC_RSTMDMA1 (1<<2) #define DMAC_HDMAEN1 (1<<1) #define DMAC_HIRQEN (1<<0) typedef struct local_info_t { dev_node_t node; int stop; struct videum_device *device; } local_info_t; /* * The Videum device structure array. This is the only global * variable in the module besides those used by the device probing * and enumeration routines (command line overrides) */ #define NBOARDS 4 static struct videum_device videum[NBOARDS]; static int unit_video[NBOARDS] = { 0, 1, 2, 3, }; MODULE_PARM(unit_video, "1-"__MODULE_STRING(NBOARDS)"i"); /* Some config/debug options */ static int nodma = 0; /* 1 = Don't use DMA */ static int noirq = 0; /* 1 = Don't use hardware interrupts */ #ifdef MODULE_PARM MODULE_PARM(nodma, "i"); MODULE_PARM(noirq, "i"); #endif static inline struct videum_device * videum_device_from_file(struct file *file) { return (struct videum_device *)v4l2_device_from_file(file); } /* * * W A V I R E G I S T E R A N D S R A M A C C E S S * */ /* These are the WAVI registers symbols */ #include "wnvwavi.h" /* The spinlock is used to make sure device accesses are atomic. */ /* To avoid a compiler warning: 'wavi_lock' is unreferenced on non-SMP systems, so only define it if __SMP__ is defined */ #ifdef __SMP__ static spinlock_t wavi_lock = SPIN_LOCK_UNLOCKED; #define WAVI_LOCK &wavi_lock #else #define WAVI_LOCK NULL #endif #define BEGIN_CRITICAL_SECTION \ do{unsigned long flags;spin_lock_irqsave(WAVI_LOCK,flags) #define END_CRITICAL_SECTION \ spin_unlock_irqrestore(WAVI_LOCK,flags);}while(0) /* wavi_readreg() and wavi_writereg() have regular and nolock versions. The nolock versions do not use a critical section to guarantee atomicity. When inside a critical section, always call the nolock functions, and when not inside a critical section, always call the regular functions. Failure to obey the rules will result in error, crash or system lockup. */ #include "wnv_mp.h" static __u16 wavi_readreg_nolock(struct videum_device *dev, int reg) { switch( dev->type ) { case VIDEUM_TYPE_PCI: { __u16 x; outb(reg, LBA(dev)); x = inw(WDATA(dev)); return x; } case VIDEUM_TYPE_ISA: return inw(dev->iobase + (reg << 10)); case VIDEUM_TYPE_PNPISA: return inw(dev->iobase + (reg << 2)); case VIDEUM_TYPE_PCMCIA: return mp_register_read( dev, reg ); default: return 0; } } static __u16 wavi_writereg_nolock(struct videum_device *dev, int reg, __u16 value) { switch (dev->type) { case VIDEUM_TYPE_PCI: { __u8 t = 0; if (dev->dma_state == DMA_STATE_RUNNING) { t = inb(DMAC(dev)); outb(t & ~DMAC_HIRQEN, DMAC(dev)); } outb(reg, LBA(dev)); outw(value, WDATA(dev)); if (dev->dma_state == DMA_STATE_RUNNING) outb(t, DMAC(dev)); break; } case VIDEUM_TYPE_ISA: outw(value, dev->iobase + (reg << 10)); break; case VIDEUM_TYPE_PNPISA: outw(value, dev->iobase + (reg << 2)); break; case VIDEUM_TYPE_PCMCIA: mp_register_write( dev, reg, value ); break; } return value; } static __u16 wavi_readreg(struct videum_device *dev, int reg) { if (dev->type == VIDEUM_TYPE_PCI || dev->type == VIDEUM_TYPE_PCMCIA) {/* For device types that use more than one I/O call */ __u16 x; BEGIN_CRITICAL_SECTION; x = wavi_readreg_nolock(dev, reg); END_CRITICAL_SECTION; return x; } return wavi_readreg_nolock(dev, reg); } static __u16 wavi_writereg(struct videum_device *dev, int reg, __u16 value) { if (dev->type == VIDEUM_TYPE_PCI || dev->type == VIDEUM_TYPE_PCMCIA) {/* For device types that use more than one I/O call */ BEGIN_CRITICAL_SECTION; wavi_writereg_nolock(dev, reg, value); END_CRITICAL_SECTION; return value; } wavi_writereg_nolock(dev, reg, value); return value; } static void wavi_block_read(struct videum_device *dev, int sram_address, __u8 *buffer, int length) { BEGIN_CRITICAL_SECTION; switch (dev->type) { case VIDEUM_TYPE_PCI: wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); outb(WAVI_HST1DAT, LBA(dev)); insl(WDATA(dev), buffer, length >> 2); break; case VIDEUM_TYPE_ISA: wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); insl(dev->iobase + (WAVI_HST1DAT << 10), buffer, length >> 2); break; case VIDEUM_TYPE_PNPISA: wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); insl(dev->iobase + (WAVI_HST1DAT << 2), buffer, length >> 2); break; case VIDEUM_TYPE_PCMCIA: wavi_writereg_nolock( dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); mp_stream_read( dev, WAVI_HST1DAT, (__u16 *) buffer, length >> 1 ); break; } END_CRITICAL_SECTION; } static void wavi_block_write(struct videum_device *dev, int sram_address, __u8 *buffer, int length) { BEGIN_CRITICAL_SECTION; switch (dev->type) { case VIDEUM_TYPE_PCI: { __u8 t = 0; if (dev->dma_state == DMA_STATE_RUNNING) { t = inb(DMAC(dev)); outb(t & ~DMAC_HIRQEN, DMAC(dev)); } wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); outb(WAVI_HST1DAT, LBA(dev)); outsl(WDATA(dev), buffer, length >> 2); if (dev->dma_state == DMA_STATE_RUNNING) outb(t, DMAC(dev)); break; } case VIDEUM_TYPE_ISA: wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); outsl(dev->iobase + (WAVI_HST1DAT << 10), buffer, length >> 2); break; case VIDEUM_TYPE_PNPISA: wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); outsl(dev->iobase + (WAVI_HST1DAT << 2), buffer, length >> 2); break; case VIDEUM_TYPE_PCMCIA: wavi_writereg_nolock(dev, WAVI_HST1PTR, (__u16)(sram_address >> 4)); mp_stream_write( dev, WAVI_HST1DAT, (__u16 *) buffer, length >> 1 ); break; } END_CRITICAL_SECTION; } /* * W A V I F U N C T I O N S */ static void wavi_initialize(struct videum_device *dev) { /* Put WAVI into a sensible state and */ /* do the one-time startup things */ int t; t = wavi_readreg(dev, WAVI_MMA); switch (t & WAVI_FULLID) { case 0x02: dev->wavi_type = WINNOV_WAVI_93; break; case 0x12: dev->wavi_type = WINNOV_WAVI_95; break; case 0x22: dev->wavi_type = WINNOV_WAVI_97; break; case 0x03: dev->wavi_type = WINNOV_WIMP; break; default: dev->wavi_type = 0; err_msg("Unknown WAVI chip\n"); break; } info_msg("Found WAVI-%d chip\n", dev->wavi_type); if (dev->wavi_type == WINNOV_WAVI_97) { /* FIXME */ /* || dev->wavi_type == WINNOV_WIMP)*/ wavi_writereg(dev, WAVI_MMA, WAVI_DHRND | WAVI_MEDREN); } if (dev->type == VIDEUM_TYPE_PNPISA) { t = wavi_readreg(dev, WAVI_MMA); wavi_writereg(dev, WAVI_MMA, t | WAVI_BANKCS); } switch(dev->wavi_type) { case WINNOV_WIMP: if ( ( dev->eeprom.dwHwFlags & HWFLAGS_CS4218 ) && ( dev->eeprom.dwHwFlags & HWFLAGS_AUDXSEL ) && ( dev->eeprom.dwHwFlags & HWFLAGS_XTAL_11K_NOT_PRESENT ) ) { t = wavi_readreg( dev, WAVI_MMA ); t |= WAVI_CS4218BIT; t |= WAVI_AUDXSEL_INT97; wavi_writereg( dev, WAVI_MMA, t ); wnv_sleep( 150 ); } t = wavi_readreg( dev, WAVI_CTL2 ); t |= WAVI_DPLL_EN; wavi_writereg( dev, WAVI_CTL2, t ); t |= WAVI_CCLK_EN; t &= ~WAVI_VCLK_EN; wavi_writereg( dev, WAVI_CTL2, t ); wnv_sleep(50); t |= WAVI_MCLK_EN_CCLK; wavi_writereg( dev, WAVI_CTL2, t ); t |= WAVI_VCLK_EN_MOV2; wavi_writereg( dev, WAVI_CTL2, t ); if ( dev->eeprom.dwHwFlags & HWFLAGS_VCLK_INV ) { t |= WAVI_VCLK_INV; wavi_writereg( dev, WAVI_CTL2, t ); } t = wavi_readreg( dev, WAVI_CTL2 ); t |= WAVI_VSSBIT; wavi_writereg( dev, WAVI_CTL2, t ); /* Set GPIOs. */ wavi_writereg( dev, WAVI_TSTMODE, 0xFCFF ); t = wavi_readreg( dev, WAVI_CTL2 ); t &= ~WAVI_VCLK_EN; t |= WAVI_VCLK_EN_MOV2; t |= WAVI_GPIO2_OE; t &= ~WAVI_GPIO2; t &= ~WAVI_GPIO1_OE; t |= WAVI_GPIO0_OE; t &= ~WAVI_GPIO0; t &= ~WAVI_GPIO4; t &= ~WAVI_GPIO3; wavi_writereg( dev, WAVI_CTL2, t); t |= WAVI_GPIO4; wavi_writereg( dev, WAVI_CTL2, t ); wnv_sleep(10); t &= ~WAVI_GPIO4; wavi_writereg( dev, WAVI_CTL2, t ); wnv_sleep(10); /* Set config memory. */ if ( 0 ) /* dev->eeprom.dwHwFlags & HWFLAGS_SRAM_HAIRY_MAPPING ) */ wavi_writereg( dev, WAVI_MBNK, 0xF7FE ); else wavi_writereg( dev, WAVI_MBNK, 0xF0F0 ); break; default: } wavi_writereg(dev, WAVI_CTL, WAVI_VDF_YCH); wavi_writereg(dev, WAVI_ITR, 0); wavi_readreg(dev, WAVI_ITR); wavi_writereg(dev, WAVI_VOF, 0x8880); wavi_writereg(dev, WAVI_VCT, 0x4440); if (dev->type == VIDEUM_TYPE_PNPISA) wavi_writereg(dev, WAVI_TSTMODE, 0xFFEE); if (dev->type == VIDEUM_TYPE_PCI) wavi_writereg(dev, WAVI_TSTMODE, 0xFFEE); wavi_writereg(dev, WAVI_AUDPTR, 0); wavi_writereg(dev, WAVI_ATTN, 0); wavi_writereg(dev, WAVI_GAIN, 0); wavi_writereg(dev, WAVI_FREQ, 0); wavi_writereg(dev, WAVI_AUDMIX1, 0); wavi_writereg(dev, WAVI_AUDMIX2, 0); } static void wavi_brightness(struct videum_device *dev, int x) { int t; t = wavi_readreg(dev, WAVI_VOF); t &= 0xFF0F; x = x & 0x000F; wavi_writereg(dev, WAVI_VOF, t | (x << 4)); } static void wavi_contrast(struct videum_device *dev, int x) { int t; t = wavi_readreg(dev, WAVI_VCT); t &= 0xFF0F; x = x & 0x000F; wavi_writereg(dev, WAVI_VCT, t | (x << 4)); } static void wavi_saturation(struct videum_device *dev, int x) { int t; t = wavi_readreg(dev, WAVI_VCT); t &= 0x00FF; x = x & 0x000F; wavi_writereg(dev, WAVI_VCT, t | (x << 8) | (x << 12)); } static void wavi_hue(struct videum_device *dev, int x) { int t; t = wavi_readreg(dev, WAVI_VOF); t &= 0x00FF; x = x & 0x000F; wavi_writereg(dev, WAVI_VOF, t | (x << 8) | (x << 12)); } static void wavi_tone_controls(struct videum_device *dev) { int *ctrl; ctrl = dev->source[dev->input].control; wavi_brightness(dev, ctrl[VCTRL_BRIGHTNESS]); wavi_contrast(dev, ctrl[VCTRL_CONTRAST]); wavi_saturation(dev, ctrl[VCTRL_SATURATION]); wavi_hue(dev, ctrl[VCTRL_HUE]); } static void wavi_vhsd(struct videum_device *dev, int x) { __u16 t = wavi_readreg(dev, WAVI_VCT) & 0xFFF0; wavi_writereg(dev, WAVI_VCT, t | (x & 0x000F)); } static void wavi_filter(struct videum_device *dev, int x) { __u16 t = wavi_readreg(dev, WAVI_CTL) & ~WAVI_VFSFIELD; wavi_writereg(dev, WAVI_CTL, t | x); } static void wavi_vdf(struct videum_device *dev, int x) { __u16 t = wavi_readreg(dev, WAVI_CTL) & ~WAVI_VDFFIELD; wavi_writereg(dev, WAVI_CTL, t | x); } static void wavi_vsc(struct videum_device *dev, int x) { __u16 t = wavi_readreg(dev, WAVI_CTL); wavi_writereg(dev, WAVI_CTL, (x) ? t | WAVI_VSCBIT : t & ~WAVI_VSCBIT); } static void wavi_vss(struct videum_device *dev, int x) { __u16 t = wavi_readreg(dev, WAVI_CTL); wavi_writereg(dev, WAVI_CTL, (x) ? t | WAVI_VSSBIT : t & ~WAVI_VSSBIT); } static int wavi_vst(struct videum_device *dev) { return (wavi_readreg(dev, WAVI_CTL) & WAVI_VSTBIT); } static void wavi_video_sampler(struct videum_device *dev, int x) { __u16 t = wavi_readreg(dev, WAVI_CTL); wavi_writereg(dev, WAVI_CTL, (x) ? t | WAVI_VSEBIT : t & ~WAVI_VSEBIT); } /* Do an RVS instruction */ static void wavi_do_rvs(struct videum_device *dev) { static __u8 uc_rvs[] = {UI_RVS, UI_HLT, UI_HLT, UI_HLT,}; int timeout; if (!wavi_vst(dev)) return;// already reset wavi_video_sampler(dev, 0); wavi_block_write(dev, dev->sram_ucode, uc_rvs, sizeof(uc_rvs)); wavi_writereg(dev, WAVI_SPLPTR, dev->sram_ucode >> 4); wavi_video_sampler(dev, 1); timeout = 10; while (timeout-- && wavi_vst(dev)) continue; wavi_video_sampler(dev, 0); dev->uc_loaded = UC_UNDEF; } static u32 marker[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; static void wavi_write_markers(struct videum_device *dev, __u32 start, int count, __u32 interval) { int i; for (i = 0; i < count; ++i) { wavi_block_write(dev, start - sizeof(marker), (__u8 *)marker, sizeof(marker)); start += interval; } } static int isqrt(unsigned int q) {/* A little integer square root routine */ int i; int r; unsigned int b2 = 0x40000000; unsigned int t; for (i = 16, r = 0; i > 0 && q; --i) { t = ((unsigned int)r << i) + b2; if (t <= q) { q -= t; r |= (1 << (i - 1)); } b2 >>= 2; } return r; } static unsigned long current_time_ms(void) { struct timeval now; do_gettimeofday(&now); return now.tv_sec * 1000 + now.tv_usec / 1000; } /* * * B U S M A S T E R F U N C T I O N S * */ static int bm_cleanup(struct videum_device *dev) { __u8 t; if (dev->type != VIDEUM_TYPE_PCI) return 0; BEGIN_CRITICAL_SECTION; t = inb(DMAC(dev)); t |= DMAC_HIRQREQn; /* no interrupt request */ t |= DMAC_HDMAEN1; /* block dma state machine */ outb(t, DMAC(dev)); outb(0, BMCMD0(dev)); /* stops bus master operation */ t = inb(BMSTATUS0(dev)); t |= BMSTATUS_DMA_INT; /* clear int bit */ t |= BMSTATUS_ERROR; /* clear error bit */ outb(t, BMSTATUS0(dev)); outb(0, BMCMD1(dev)); /* stops bus mastering operation */ t = inb(BMSTATUS1(dev)); t |= BMSTATUS_DMA_INT; /* clear int bit */ t |= BMSTATUS_ERROR; /* clear error bit */ outb(t, BMSTATUS1(dev)); t = inb(DMAC(dev)); t |= DMAC_RSTMDMA1; /* toggling removes MDMAEN1 */ outb(t, DMAC(dev)); t &= ~DMAC_RSTMDMA1; /* ...complete the toggle */ outb(t, DMAC(dev)); END_CRITICAL_SECTION; dev->dma_state = DMA_STATE_IDLE; return 1; } static int bm_resync(struct videum_device *dev) { return bm_cleanup(dev); } static int bm_disarm(struct videum_device *dev) { if (dev->dma_state == DMA_STATE_IDLE) return 1; return bm_cleanup(dev); } static int bm_arm(struct videum_device *dev, struct scatter_node *list) { __u8 t; if (dev->type != VIDEUM_TYPE_PCI || list == NULL) return -1;/* error */ bm_disarm(dev); dev->dma_state = DMA_STATE_ARMING; BEGIN_CRITICAL_SECTION; t = inb(DMAC(dev)); if (dev->clientfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) t |= DMAC_SWAP; else t &= ~DMAC_SWAP; outb(t, DMAC(dev)); outl(virt_to_bus(list), BMPRDT0(dev)); /* pointer to dma list */ outl(virt_to_bus(list), BMPRDT1(dev)); /* pointer to dma list */ t = inb(BMCMD0(dev)); t |= BMCMD_RW_CNRL; /* write mode! */ outb(t, BMCMD0(dev)); t |= BMCMD_START_STOP; /* start bus master! */ outb(t, BMCMD0(dev)); t = inb(BMCMD1(dev)); t |= BMCMD_RW_CNRL; /* write mode! */ outb(t, BMCMD1(dev)); t |= BMCMD_START_STOP; /* start bus master! */ outb(t, BMCMD1(dev)); /* generate an interrupt, needed to prime the controller */ t = inb(DMAC(dev)); t &= ~DMAC_HIRQREQn; outb(t, DMAC(dev)); t |= DMAC_HIRQREQn; t &= ~DMAC_HDMAEN1; /* let dma state machine run! */ outb(t, DMAC(dev)); END_CRITICAL_SECTION; // if (!(inb(BMSTATUS0(dev)) & BMSTATUS_ACTIVE)) // { // info_msg("Couldn't arm DMA\n"); // dev->dma_state = DMA_STATE_IDLE; // return -1; // } // dev->dma_state = DMA_STATE_RUNNING; return 0;/* ok */ } static int/* 0=not done, 1=done */ bm_is_done(struct videum_device *dev) { if (dev->type != VIDEUM_TYPE_PCI || dev->dma_state != DMA_STATE_RUNNING) return 0; return !(inb(BMSTATUS0(dev)) & BMSTATUS_ACTIVE); } static int bm_build_scatter_list(struct videum_device *dev, unsigned char *buffer, struct scatter_node **plist) { struct scatter_node *list; int i, n; unsigned char *a; if (dev->type != VIDEUM_TYPE_PCI || buffer == NULL) return 0; list = *plist; if (list == NULL) {/* Assuming one page will be big enough. 4KB = 512 pieces */ list = (struct scatter_node *)get_free_page(GFP_KERNEL); if (list == NULL) return 0; } /* Simple algorithm will just map the buffer contiguously by pages */ /* Note: capture_buffer is vmalloc()ed, so it's page-aligned */ n = (dev->capture_size + PAGE_SIZE - 1) / PAGE_SIZE; a = buffer; for (i = 0; i < n; ++i) { list[i].addr = v4l2_vmalloc_to_bus(a); list[i].len = PAGE_SIZE; a += PAGE_SIZE; } /* Last page might not be full */ if (dev->capture_size < n * PAGE_SIZE) list[n - 1].len = dev->capture_size - (n - 1) * PAGE_SIZE; list[n - 1].len |= END_OF_SCATTER_LIST; #if 0 debug_msg("Scatter list %08lX\n", virt_to_bus(list)); for (i = 0; i < n; ++i) debug_msg("List %2d: A=%08X L=%08X\n",i, list[i].addr,list[i].len); #endif *plist = list; return 1;/* ok */ } static void wavi_benchmark(struct videum_device *dev) { struct timeval start, end; char *test; int i; u32 t; struct scatter_node *list; if (dev->slave_rate) return; dev->slave_rate = 0; dev->dma_rate = 0; test = (char *)get_free_page(GFP_KERNEL); if (test == NULL) return; list = (struct scatter_node *)get_free_page(GFP_KERNEL); if (list == NULL) { free_page((unsigned long)test); return; } /* Test slave mode SRAM readout rate. Read 1M of data in 1K */ /* chunks and look at elapsed time. */ do_gettimeofday(&start); for (i = 0; i < 1024; ++i) wavi_block_read(dev, 0, test, 1024); do_gettimeofday(&end); t = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); if (t < 250) t = 250; dev->slave_rate = 4*1048576000UL / (t / 250); if (dev->type == VIDEUM_TYPE_PCI && dev->irq && !nodma) { /* Test DMA mode SRAM readout rate. Read 1M of data in */ /* PAGE_SIZE chunks and look at elapsed time. */ /* This microcode will start the DMA engine */ static char dmaucode[4] = {UI_SVS, UI_SVS, UI_RVS, UI_HLT,}; u8 al; for (i = 0; i < 1048576 / PAGE_SIZE; ++i) { list[i].addr = virt_to_bus(test); list[i].len = PAGE_SIZE; } list[i - 1].len |= END_OF_SCATTER_LIST; wavi_video_sampler(dev, 0); wavi_block_write(dev, dev->sram_ucode, dmaucode, 4); wavi_writereg(dev, WAVI_SPLPTR, dev->sram_ucode >> 4); al = inb(DMAC(dev)); al |= DMAC_HIRQEN; /* Interrupts enabled */ outb(al, DMAC(dev)); bm_resync(dev); bm_arm(dev, list); wavi_video_sampler(dev, 1); do_gettimeofday(&start); for (i = 100000/*1sec*/; i && !bm_is_done(dev); --i) udelay(10);/* 10 us resolution, good enough */ do_gettimeofday(&end); bm_resync(dev); if (i == 0) {/* timed out! */ dev->dma_rate = 0; err_msg("Videum bus-master not working\n"); } else { t = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); dev->dma_rate = 4*1048576000UL / (t / 250 + 1); } } free_page((unsigned long)test); free_page((unsigned long)list); //debug_msg("Measured slave rate = %8d\n", dev->slave_rate); //debug_msg("Measured dma rate = %8d\n", dev->dma_rate); } /* * 2 * I C B U S I N T E R F A C E * */ static __u16 in_ctl(struct videum_device *dev) { return wavi_readreg(dev, WAVI_CTL); } static void out_ctl(struct videum_device *dev) { wavi_writereg(dev, WAVI_CTL, dev->ctl); } static void i2c_delay(void) { udelay(2); } static void i2c_clock(struct videum_device *dev, int c) { int t = 1000; if (c) dev->ctl |= WAVI_I2CBIT; else dev->ctl &= ~WAVI_I2CBIT; out_ctl(dev); if (!c) while (--t && (in_ctl(dev) & WAVI_I2CBIT)); } static void i2c_data(struct videum_device *dev, int d) { if (d) dev->ctl |= WAVI_IMDBIT; else dev->ctl &= ~WAVI_IMDBIT; out_ctl(dev); } /* BUG: I2C bit read routines, polarity */ static void i2c_start(struct videum_device *dev) { dev->ctl &= ~(WAVI_I2CBIT | WAVI_IMDBIT); out_ctl(dev); i2c_delay(); i2c_data(dev, 1); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); } static int i2c_get_ack(struct videum_device *dev) { i2c_clock(dev, 1); i2c_delay(); i2c_data(dev, 0); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); i2c_delay(); return in_ctl(dev) & WAVI_IMDBIT; } static void i2c_send_ack(struct videum_device *dev) { i2c_clock(dev, 1); i2c_delay(); i2c_data(dev, 1); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); } static void i2c_send_nak(struct videum_device *dev) { i2c_clock(dev, 1); i2c_delay(); i2c_data(dev, 0); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); } static void i2c_end(struct videum_device *dev) { i2c_clock(dev, 1); i2c_delay(); i2c_data(dev, 1); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); i2c_data(dev, 0); i2c_delay(); } static void i2c_vpxrestart(struct videum_device *dev) { i2c_clock(dev, 1); i2c_delay(); i2c_data(dev, 0); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); } static void i2c_out8(struct videum_device *dev, int ndata) { int i; ndata = ~ndata; for (i = 0; i < 8; ++i) { i2c_clock(dev, 1); i2c_delay(); i2c_data(dev, ndata & 128); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); ndata <<= 1; } } static int i2c_in8(struct videum_device *dev) { int i; int ndata = ~0; dev->ctl &= ~WAVI_IMDBIT;// Don't write to h/w now for (i = 0; i < 8; ++i) { i2c_clock(dev, 1); i2c_delay(); i2c_clock(dev, 0); i2c_delay(); ndata = (in_ctl(dev) & 1) | (ndata << 1); } return ~ndata; } static void i2c_get_ready(struct videum_device *dev) {/* Prepare I2C bus for operation */ dev->ctl = in_ctl(dev); /* initialize the CTL register shadow */ i2c_end(dev); /* idle the I2C bus */ } static int i2c_error(struct videum_device *dev) { i2c_end(dev); return -1; } static int /* -1 on error */ i2c_read_reg_byte(struct videum_device *dev, int addr, int reg) { int ndata; i2c_start(dev); i2c_out8(dev, addr); if (!i2c_get_ack(dev)) return i2c_error(dev);/* no ACK from device */ i2c_out8(dev, reg); i2c_get_ack(dev); /* assume it succeeds if the first one succeeded */ if (addr == 0x86 || addr == 0x8E)/* ITT VPX is a little different */ i2c_vpxrestart(dev); else i2c_end(dev); i2c_start(dev); i2c_out8(dev, addr | 1); if (!i2c_get_ack(dev)) return i2c_error(dev); ndata = i2c_in8(dev); if (addr == 0x86 || addr == 0x8E)/* ITT VPX is a little different */ i2c_send_nak(dev); i2c_end(dev); return ndata; } static int /* -1 on error */ i2c_write_reg_byte(struct videum_device *dev, int addr, int reg, int ndata) { i2c_start(dev); i2c_out8(dev, addr); if (!i2c_get_ack(dev)) return i2c_error(dev);/* no ACK from device */ i2c_out8(dev, reg); i2c_get_ack(dev); i2c_out8(dev, ndata); i2c_get_ack(dev); i2c_end(dev); return 0; } static int /* -1 on error */ i2c_read_reg_word(struct videum_device *dev, int addr, int reg) { int ndata; i2c_start(dev); i2c_out8(dev, addr); if (!i2c_get_ack(dev)) return i2c_error(dev);/* no ACK from device */ i2c_out8(dev, reg); i2c_get_ack(dev); /* assume it succeeds if the first one succeeded */ if (addr == 0x86 || addr == 0x8E)/* ITT VPX is a little different */ i2c_vpxrestart(dev); else i2c_end(dev); i2c_start(dev); i2c_out8(dev, addr | 1); if (!i2c_get_ack(dev)) return i2c_error(dev); ndata = i2c_in8(dev) << 8; i2c_send_ack(dev); ndata |= i2c_in8(dev); i2c_send_nak(dev); i2c_end(dev); return ndata; } static int /* -1 on error */ i2c_write_reg_word(struct videum_device *dev, int addr, int reg, int ndata) { i2c_start(dev); i2c_out8(dev, addr); if (!i2c_get_ack(dev)) return i2c_error(dev);/* no ACK from device */ i2c_out8(dev, reg); i2c_get_ack(dev); i2c_out8(dev, ndata >> 8); i2c_get_ack(dev); i2c_out8(dev, ndata & 0xFF); i2c_get_ack(dev); i2c_end(dev); return 0; } /* * Read the configuration EEPROM */ #define EEPROM_ADDR 0xA0 static int /* 1 = OK, 0 = error */ eeprom_load(struct videum_device *dev) { __u8 *p = (__u8 *)&dev->eeprom; int i; int d; i2c_get_ready(dev); i2c_end(dev); /* EEPROM readability/bogosity test */ if (i2c_read_reg_byte(dev, EEPROM_ADDR, 0) != EEPROM_SIG) { err_msg("Couldn't read configuration EEPROM\n"); return 0; } for (i = 0; i < sizeof(struct EEPROM); ++i) { d = i2c_read_reg_byte(dev, EEPROM_ADDR, i); if (d == -1) break; p[i] = (__u8)d; } // Run checksum d = 0; for (i = 0; i < sizeof(struct EEPROM); ++i) d += (__u8)p[i]; if ((__u8)d != 0xff) { err_msg("Checksum error in configuration EEPROM\n"); return 0; } info_msg("Identified: %s\n", dev->eeprom.szProduct); return 1; } /* * * V I D E O D E C O D E R S * */ struct videc_reg_init { __u16 reg; __u16 value; }; /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Philips 7110 Video Decoder routines */ static struct videc_reg_init init_table_p7110[] = { {0x00, 0x4C}, {0x01, 0x3C}, {0x02, 0x0D}, {0x03, 0xEF}, {0x04, 0xBD}, {0x05, 0xF0}, {0x06, 0x00}, {0x07, 0x00}, {0x08, 0xF8}, {0x09, 0xF8}, {0x0A, 0x60}, {0x0B, 0x60}, {0x0C, 0x00}, {0x0D, 0x86}, {0x0E, 0x18}, {0x0F, 0x90}, {0x10, 0x00}, {0x11, 0x2C}, {0x12, 0x40}, {0x13, 0x46}, {0x14, 0x42}, {0x15, 0x1A}, {0x16, 0xFF}, {0x17, 0xDA}, {0x18, 0xF0}, {0x19, 0x8B}, {0x1A, 0x00}, {0x1B, 0x00}, {0x1C, 0x00}, {0x1D, 0x00}, {0x1E, 0x00}, {0x1F, 0x00}, {0x20, 0xD9}, {0x21, 0x17}, {0x22, 0x40}, {0x23, 0x41}, {0x24, 0x80}, {0x25, 0x41}, {0x26, 0x80}, {0x27, 0x4F}, {0x28, 0xFE}, {0x29, 0x01}, {0x2A, 0xCF}, {0x2B, 0x0F}, {0x2C, 0x03}, {0x2D, 0x01}, {0x2E, 0x81}, {0x2F, 0x03}, {0x30, 0x44}, {0x31, 0x75}, {0x32, 0x01}, {0x33, 0x8C}, {0x34, 0x03}, }; #define NUM_P7110_REGISTERS \ (sizeof(init_table_p7110)/sizeof(struct videc_reg_init)) static int p7110_initialize(struct videum_device *dev) { int i; /* Video decoder information fields */ dev->videc.standards = (1 << V4L2_STD_NTSC) | (1 << V4L2_STD_PAL); dev->videc.ntsc_hskip = 30; dev->videc.ntsc_vskip = 12; dev->videc.ntsc_width = 640; dev->videc.ntsc_height = 240; dev->videc.ntsc_field_order = 0; dev->videc.pal_hskip = 62; dev->videc.pal_vskip = 14; dev->videc.pal_width = 768; dev->videc.pal_height = 288; dev->videc.pal_field_order = 0; dev->videc.preferred_field = 0; dev->videc.hadjust = 0; dev->videc.vadjust = 0; dev->videc.num_inputs = 3; for (i = 0; i < VSOURCE_COUNT; ++i) { dev->source[i].wavi_vsc = 0; dev->source[i].wavi_vss = 0; dev->source[i].wavi_vhsd = 6; dev->source[i].vcrmode = 1; } for (i = 0; i < NUM_P7110_REGISTERS; ++i) { i2c_write_reg_byte(dev, dev->videc.i2c_addr1, init_table_p7110[i].reg, init_table_p7110[i].value); } dev->videc.decoder_is_stable = 1; return 1; } static int p7110_set_input(struct videum_device *dev, int i) { dev->input = i; switch (i) { case VSOURCE_MXC: i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x06, 0x00); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x20, 0xBA); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x21, 0x07); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x22, 0x91); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x2c, 0x03); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x30, 0x60); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x31, 0xB5); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x21, 0x05); break; case VSOURCE_COMP: i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x06, 0x00); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x20, 0xD9); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x21, 0x17); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x22, 0x40); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x2c, 0x03); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x30, 0x60); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x31, 0x75); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x21, 0x16); break; case VSOURCE_SVIDEO: i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x06, 0x80); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x20, 0x98); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x21, 0x17); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x22, 0x41); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x2c, 0x23); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x30, 0x44); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x31, 0x75); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x21, 0x14); break; } //dev->videc.decoder_is_stable = 0; return 1; } static int p7110_set_standard(struct videum_device *dev, int x) { switch (x) { case V4L2_STD_NTSC: i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x0D, 0x86); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x11, 0x2C); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x2E, 0x81); dev->videc.frame_period = 333667; break; case V4L2_STD_PAL: i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x0D, 0x86); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x11, 0x59); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x2E, 0x9A); dev->videc.frame_period = 400000; break; case V4L2_STD_SECAM: dev->videc.frame_period = 400000; break; } dev->videc.standard = x; return 1; } static int p7110_set_vcrmode(struct videum_device *dev, int x) { /* Leave 7110 always in VCR mode (7110 bug) */ dev->source[dev->input].vcrmode = x; return 1; } static int p7110_is_stable(struct videum_device *dev) { return 1; } static int p7110_probe(struct videum_device *dev) { int x; if (dev->videc.i2c_addr1 != 0) return 0;/* Another device was already found */ x = i2c_read_reg_byte(dev, 0x9C, 0);/* Version */ if (x != -1) { dev->videc.i2c_addr1 = 0x9C; } else { x = i2c_read_reg_byte(dev, 0x9E, 0); if (x == -1) return 0;/* Not found */ dev->videc.i2c_addr1 = 0x9E; } dev->videc.type = VIDEC_7110; /* Fill in the method fields */ dev->videc.initialize = p7110_initialize; dev->videc.set_input = p7110_set_input; dev->videc.set_standard = p7110_set_standard; dev->videc.set_vcrmode = p7110_set_vcrmode; dev->videc.is_stable = p7110_is_stable; dev->videc.set_white_balance = NULL; info_msg("Found Philips SAA7110 decoder chip at %2X\n", dev->videc.i2c_addr1); return 1;/* Found */ } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Philips 7111A Video Decoder routines */ static struct videc_reg_init init_table_p7111a[] = { {0x02, 0xC0}, {0x03, 0x23}, {0x04, 0x00}, {0x05, 0x00}, {0x06, 0xEB}, {0x07, 0xE0}, {0x08, 0x88}, {0x09, 0x01}, {0x0A, 0x80}, {0x0B, 0x47}, {0x0C, 0x40}, {0x0D, 0x00}, {0x0E, 0x01}, {0x10, 0x40}, {0x11, 0x1C}, {0x12, 0x01}, {0x13, 0x00}, {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x17, 0x00}, }; #define NUM_P7111A_REGISTERS \ (sizeof(init_table_p7111a)/sizeof(struct videc_reg_init)) static int p7111a_initialize(struct videum_device *dev) { int i; /* Video decoder information fields */ dev->videc.standards = (1 << V4L2_STD_NTSC) | (1 << V4L2_STD_PAL); dev->videc.ntsc_hskip = 72; dev->videc.ntsc_vskip = 13; dev->videc.ntsc_width = 640; dev->videc.ntsc_height = 240; dev->videc.ntsc_field_order = 0; dev->videc.pal_hskip = 73; dev->videc.pal_vskip = 17; dev->videc.pal_width = 640; dev->videc.pal_height = 288; dev->videc.pal_field_order = 0; dev->videc.preferred_field = 0; dev->videc.hadjust = 0; dev->videc.vadjust = 0; dev->videc.num_inputs = 3; for (i = 0; i < VSOURCE_COUNT; ++i) { dev->source[i].wavi_vsc = 0; dev->source[i].wavi_vss = 0; dev->source[i].wavi_vhsd = 6; dev->source[i].vcrmode = 0; } for (i = 0; i < NUM_P7111A_REGISTERS; ++i) { i2c_write_reg_byte(dev, dev->videc.i2c_addr1, init_table_p7111a[i].reg, init_table_p7111a[i].value); } dev->videc.decoder_is_stable = 1; return 1; } static int p7111a_set_input(struct videum_device *dev, int i) { int m, s; dev->input = i; m = i2c_read_reg_byte(dev, dev->videc.i2c_addr1, 0x02) & 0xF8; s = i2c_read_reg_byte(dev, dev->videc.i2c_addr1, 0x09) & 0x7F; switch (i) { case VSOURCE_SVIDEO: m |= 0x05; s |= 0x80; break; case VSOURCE_COMP: m |= 0x00; s |= 0x00; break; case VSOURCE_MXC: m |= 0x02; s |= 0x00; break; } i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x02, m); i2c_write_reg_byte(dev, dev->videc.i2c_addr1, 0x09, s); //dev->videc.decoder_is_stable = 0; return 1; } static int p7111a_set_standard(struct videum_device *dev, int x) { /* The initialize sets up the 7111A to detect the standard automatically, no need to do anything here on the 7111A */ dev->videc.standard = x; return 1; } static int p7111a_set_vcrmode(struct videum_device *dev, int x) { /* Leave 7111A always in VCR mode */ dev->source[dev->input].vcrmode = x; return 1; } static int p7111a_is_stable(struct videum_device *dev) { return 1; } static int p7111a_probe(struct videum_device *dev) { int x; if (dev->videc.i2c_addr1 != 0) return 0;/* Another device was already found */ x = i2c_read_reg_byte(dev, 0x48, 0);/* Version */ if (x != -1) { dev->videc.i2c_addr1 = 0x48; } else { x = i2c_read_reg_byte(dev, 0x4A, 0); if (x == -1) return 0;/* Not found */ dev->videc.i2c_addr1 = 0x4A; } dev->videc.type = VIDEC_7111A; /* Fill in the method fields */ dev->videc.initialize = p7111a_initialize; dev->videc.set_input = p7111a_set_input; dev->videc.set_standard = p7111a_set_standard; dev->videc.set_vcrmode = p7111a_set_vcrmode; dev->videc.is_stable = p7111a_is_stable; dev->videc.set_white_balance = NULL; info_msg("Found Philips SAA7111A decoder chip at %2X\n", dev->videc.i2c_addr1); return 1;/* Found */ } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * ITT VPX Video Decoder routines */ static struct videc_reg_init init_table_ittvpx[] = {/* Registers 0x1000-0x11FF are FP regs, 0x000-0x0FF I2C regs */ {0x10D0, 0xFFF}, {0x101C, 0x000}, {0x10A0, 0x700}, {0x1088, 0x00C}, {0x1089, 0x0F8}, /* PAL: 128 */ {0x108A, 0x0F8}, /* PAL: 128 */ {0x108B, 0x000}, {0x108C, 0x420}, {0x108D, 0x420}, {0x108F, 0x000}, {0x10F0, 0x076}, {0x10F2, 0x3D3}, /* PAL: 3DB */ {0x10B2, 0x300}, {0x0033, 0x001}, {0x00D8, 0x00C}, {0x00D0, 0x040}, {0x00D1, 0x040}, {0x00E6, 0x008}, {0x00E7, 0x020}, {0x00E8, 0x0F8}, {0x00F0, 0x00B}, {0x00F1, 0x018}, {0x00F2, 0x01B}, {0x00F8, 0x0FA}, }; #define NUM_ITTVPX_REGISTERS \ (sizeof(init_table_ittvpx)/sizeof(struct videc_reg_init)) static int ittvpx_read_reg(struct videum_device *dev, int reg) { int addr = dev->videc.i2c_addr1; if (reg & 0x1000) { int x = i2c_read_reg_byte(dev, addr, 0x29); int timeout; if (x < 0) return x; for (timeout = 200; timeout && (x & 7) != 0; --timeout) x = i2c_read_reg_byte(dev, addr, 0x29); reg &= ~0x1000; i2c_write_reg_word(dev, addr, 0x26, reg); return i2c_read_reg_word(dev, addr, 0x28); } return i2c_read_reg_byte(dev, addr, reg); } static int ittvpx_write_reg(struct videum_device *dev, int reg, int ndata) { int addr = dev->videc.i2c_addr1; if (reg & 0x1000) { int x = i2c_read_reg_byte(dev, addr, 0x29); int timeout; if (x < 0) return x; for (timeout = 200; timeout && (x & 7) != 0; --timeout) x = i2c_read_reg_byte(dev, addr, 0x29); reg &= ~0x1000; i2c_write_reg_word(dev, addr, 0x27, reg); return i2c_write_reg_word(dev, addr, 0x28, ndata); } return i2c_write_reg_byte(dev, addr, reg, ndata); } static int ittvpx_initialize(struct videum_device *dev) { int i; /* Video decoder information fields */ dev->videc.standards = (1 << V4L2_STD_NTSC) | (1 << V4L2_STD_PAL) | //(1 << V4L2_STD_SECAM) | 0; dev->videc.ntsc_hskip = 16; dev->videc.ntsc_vskip = 14; dev->videc.ntsc_width = 1025; dev->videc.ntsc_height = 240; dev->videc.ntsc_field_order = 0; dev->videc.pal_hskip = 16; dev->videc.pal_vskip = 14; dev->videc.pal_width = 1025; dev->videc.pal_height = 288; dev->videc.pal_field_order = 0; dev->videc.preferred_field = 0; dev->videc.hadjust = 0; dev->videc.vadjust = 0; dev->videc.num_inputs = 3; for (i = 0; i < VSOURCE_COUNT; ++i) { dev->source[i].wavi_vsc = 0; dev->source[i].wavi_vss = 0; dev->source[i].wavi_vhsd = 0; dev->source[i].vcrmode = 1; } for (i = 0; i < NUM_ITTVPX_REGISTERS; ++i) { ittvpx_write_reg(dev, init_table_ittvpx[i].reg, init_table_ittvpx[i].value); } dev->videc.decoder_is_stable = 1; return 1; } static int ittvpx_set_input(struct videum_device *dev, int i) { int svhs = 0, luma = 1, chroma = 0; int x; dev->input = i; switch (i) { case VSOURCE_MXC: luma = 1; break; case VSOURCE_COMP: luma = 3; break; case VSOURCE_SVIDEO: luma = 2; svhs = 1; break; } luma = 3 - luma; chroma = (1 - chroma) << 2; svhs <<= 5; /* input multiplexers */ x = ittvpx_read_reg(dev, 0x33); ittvpx_write_reg(dev, 0x33, (x & 0xF8) | 0x08 | chroma | luma); /* SVHS mode */ x = ittvpx_read_reg(dev, 0x10F2); ittvpx_write_reg(dev, 0x10F2, (x & ~0x20) | svhs | 0x10); //dev->videc.decoder_is_stable = 0; return 1; } static int ittvpx_set_standard(struct videum_device *dev, int x) { switch (x) { case V4L2_STD_NTSC: ittvpx_write_reg(dev, 0x1089, 0x0F8); ittvpx_write_reg(dev, 0x108A, 0x0F8); ittvpx_write_reg(dev, 0x10F2, 0x3D3); dev->videc.frame_period = 333667; break; case V4L2_STD_PAL: ittvpx_write_reg(dev, 0x1089, 0x128); ittvpx_write_reg(dev, 0x108A, 0x128); ittvpx_write_reg(dev, 0x10F2, 0x3DB); dev->videc.frame_period = 400000; break; case V4L2_STD_SECAM:/* BUG: this is not right for SECAM */ ittvpx_write_reg(dev, 0x1089, 0x128); ittvpx_write_reg(dev, 0x108A, 0x128); ittvpx_write_reg(dev, 0x10F2, 0x3DB); dev->videc.frame_period = 400000; break; } dev->videc.standard = x; return 1; } static int ittvpx_set_vcrmode(struct videum_device *dev, int x) { dev->source[dev->input].vcrmode = x; ittvpx_write_reg(dev, 0x104B, (x) ? 0x2FF : 0x29C); return 1; } static int ittvpx_is_stable(struct videum_device *dev) { return 1; } static int ittvpx_probe(struct videum_device *dev) { int x; if (dev->videc.i2c_addr1 != 0) return 0;/* Another device was already found */ dev->videc.i2c_addr1 = 0x86; x = ittvpx_read_reg(dev, 0x1050);/* Version */ if (x == -1) { dev->videc.i2c_addr1 = 0x8E; x = ittvpx_read_reg(dev, 0x1050); if (x == -1) { dev->videc.i2c_addr1 = 0; return 0;/* Not found */ } } dev->videc.type = VIDEC_VPX; dev->videc.version = x; /* Fill in the method fields */ dev->videc.initialize = ittvpx_initialize; dev->videc.set_input = ittvpx_set_input; dev->videc.set_standard = ittvpx_set_standard; dev->videc.set_vcrmode = ittvpx_set_vcrmode; dev->videc.is_stable = ittvpx_is_stable; dev->videc.set_white_balance = NULL; info_msg("Found ITT VPX (ver.%d) decoder chip at %2X\n", dev->videc.version, dev->videc.i2c_addr1); if (x == 700) err_msg("Old VPX version not correctly supported.\n"); return 1;/* Found */ } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * CS76x5 Video Decoder routines */ static __u8 cs7665_gamma[256] = { 0x00, 0x0c, 0x12, 0x16, 0x1A, 0x1D, 0x20, 0x23, 0x26, 0x29, 0x2B, 0x2D, 0x2F, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3B, 0x3D, 0x3F, 0x41, 0x42, 0x44, 0x46, 0x47, 0x49, 0x4A, 0x4A, 0x4D, 0x4F, 0x50, 0x51, 0x53, 0x54, 0x56, 0x57, 0x58, 0x5A, 0x5B, 0x5C, 0x5D, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA5, 0xA6, 0xA7, 0xA8, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAC, 0xAD, 0xAE, 0xAF, 0xAF, 0xB0, 0xB1, 0xB2, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6, 0xB7, 0xB7, 0xB8, 0xB9, 0xBA, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBE, 0xBF, 0xC0, 0xC1, 0xC1, 0xC2, 0xC3, 0xC3, 0xC4, 0xC5, 0xC5, 0xC6, 0xC7, 0xC7, 0xC8, 0xC9, 0xC9, 0xCA, 0xCB, 0xCB, 0xCC, 0xCD, 0xCD, 0xCE, 0xCF, 0xCF, 0xD0, 0xD1, 0xD1, 0xD2, 0xD3, 0xD3, 0xD4, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD8, 0xD9, 0xDA, 0xDA, 0xDB, 0xDB, 0xDC, 0xDD, 0xDD, 0xDE, 0xDE, 0xDF, 0xE0, 0xE0, 0xE1, 0xE2, 0xE2, 0xE3, 0xE3, 0xE4, 0xE5, 0xE5, 0xE6, 0xE6, 0xE7, 0xE8, 0xE8, 0xE9, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xEC, 0xED, 0xED, 0xEE, 0xEF, 0xEF, 0xF0, 0xF0, 0xF1, 0xF2, 0xF2, 0xF3, 0xF3, 0xF4, 0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF7, 0xF8, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFC, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF }; static struct videc_reg_init init_table_cs7615[] = { {0x00, 0x10}, {0x00, 0x10}, {0x00, 0x10}, {0x24, 0x14}, {0x24, 0x26}, {0x2B, 0x03}, {0x2A, 0xF2}, {0x41, 0x9D}, {0x4B, 0x4D}, {0x2C, 0x00}, {0x29, 0x05} }; #define NUM_CS7615_REGISTERS \ (sizeof(init_table_cs7615)/sizeof(struct videc_reg_init)) /* static struct videc_reg_init ntsc_cs7615 = { 0x24, 0x26 }; static struct videc_reg_init pal_cs7615 = { 0x24, 0x27 }; */ static struct videc_reg_init init_table_cs7665[] = { {0x00, 0x11}, {0x06, 0x69}, {0x03, 0x04}, {0x0C, 0x07}, {0x05, 0x04}, {0x0A, 0x90}, {0x0B, 0x90}, {0x08, 0x81}, {0x09, 0x81}, {0x10, 0x58}, {0x11, 0x78}, {0x12, 0x88}, {0x13, 0xBB}, {0x14, 0x90}, {0x15, 0x88}, {0x16, 0xA8}, {0x17, 0xB0}, {0x18, 0x7F} }; #define NUM_CS7665_REGISTERS \ (sizeof(init_table_cs7665)/sizeof(struct videc_reg_init)) static int cs7615_read_reg(struct videum_device *dev, int reg) { int addr = dev->videc.i2c_addr1; return i2c_read_reg_byte(dev, addr, reg); } static int cs7615_write_reg(struct videum_device *dev, int reg, int ndata) { int addr = dev->videc.i2c_addr1; return i2c_write_reg_byte(dev, addr, reg, ndata); } static int cs7665_read_reg(struct videum_device *dev, int reg) { int addr = dev->videc.i2c_addr2; return i2c_read_reg_byte(dev, addr, reg); } static int cs7665_write_reg(struct videum_device *dev, int reg, int ndata) { int addr = dev->videc.i2c_addr2; return i2c_write_reg_byte(dev, addr, reg, ndata); } static int cs7665_write_gamma(struct videum_device *dev, int reg, int mask, int count, u8 *buffer) { int i; i2c_start(dev); i2c_out8(dev, dev->videc.i2c_addr2);/*James,verify addr2 is correct for7665*/ if (!i2c_get_ack(dev)) { i2c_end(dev); return i2c_error(dev);/* no ACK from device */ } i2c_out8(dev, reg); i2c_get_ack(dev); /* assume it succeeds if the first one succeeded */ i2c_out8(dev, mask); i2c_get_ack(dev); /* assume it succeeds if the first one succeeded */ for (i = 0; i < count; ++i) { i2c_out8(dev, buffer[i]); if (!i2c_get_ack(dev)) { i2c_end(dev); return i2c_error(dev); } } i2c_end(dev); return 0; } static int check_video_synchs(struct videum_device *dev) { __u16 CTLreg; __u16 block[3] = { 0xFBF4, 0xF9FA, 0xFFF5 }; int i; CTLreg = wavi_readreg( dev, WAVI_CTL ); wavi_video_sampler(dev, 0); wavi_block_write(dev, dev->sram_ucode, (__u8 *) block, 4); wavi_writereg(dev, WAVI_SPLPTR, dev->sram_ucode >> 4); wavi_video_sampler(dev, 1); for (i = 10000/*1sec*/; i && ((wavi_readreg( dev, WAVI_CTL ) & WAVI_VSTBIT) == 0 ); --i) udelay(10);/* 10 us resolution, good enough */ wavi_video_sampler( dev, 0 ); return (wavi_readreg( dev, WAVI_CTL ) & WAVI_VSTBIT) != 0; } static int ccd_initialize(struct videum_device *dev) { int i; int r; int j; int variable; int result; info_msg("Initializing the CS7665.\n"); /* Video decoder information fields */ dev->videc.standards = (1 << V4L2_STD_NTSC); dev->videc.ntsc_hskip = 30; dev->videc.ntsc_vskip = 20; dev->videc.ntsc_width = 640; dev->videc.ntsc_height = 240; dev->videc.ntsc_field_order = 0; /* XXX */ dev->videc.pal_hskip = 30; dev->videc.pal_vskip = 20; dev->videc.pal_width = 640; dev->videc.pal_height = 240; dev->videc.pal_field_order = 0; dev->videc.preferred_field = 0; dev->videc.hadjust = 0; dev->videc.vadjust = 0; dev->videc.num_inputs = 1; for (i = 0; i < VSOURCE_COUNT; ++i) { dev->source[i].wavi_vsc = 0; dev->source[i].wavi_vss = 1; dev->source[i].wavi_vhsd = 4; dev->source[i].vcrmode = 1; } for (i = 0; i < NUM_CS7665_REGISTERS; ++i) { if ( init_table_cs7665[i].reg == 0xC ) { result = cs7665_write_gamma( dev, init_table_cs7665[i].reg, init_table_cs7665[i].value, sizeof(cs7665_gamma), cs7665_gamma ); debug_msg( "Write gamma = %d.\n", result); continue; } if ( init_table_cs7665[i].reg == 0x00 ) { cs7665_write_reg(dev, init_table_cs7665[i].reg, init_table_cs7665[i].value); while( cs7665_read_reg(dev, init_table_cs7665[i].reg) > 0 ) /* Empty loop. */; continue; } for( r = 100; r > 0; r-- ) { /* FIXME: Something is missing here? A video HW check? */ cs7665_write_reg(dev, init_table_cs7665[i].reg, init_table_cs7665[i].value); if( (variable = cs7665_read_reg(dev, init_table_cs7665[i].reg) ) == init_table_cs7665[i].value ) break; wnv_sleep(20); } if ( r <= 0 ) { err_msg("Why isn't this working 7665 = %d." "reg = %d, value = %d\n", NUM_CS7665_REGISTERS - i, variable, init_table_cs7665[i].value ); } } variable = cs7665_read_reg( dev, 0x05 ); if ( variable == -1 ) cs7665_write_reg( dev, 0x05, variable | 0x02 ); debug_msg("Initializing the CS7615.\n"); /* Video decoder information fields */ j = 0; while ( j < 5 ) { for( i = 0; i < NUM_CS7615_REGISTERS; i++ ) { if ( init_table_cs7615[i].reg == 0x00 ) { cs7615_write_reg(dev, init_table_cs7615[i].reg, init_table_cs7615[i].value); while( cs7615_read_reg(dev, init_table_cs7615[i].reg) > 0 ) /* Empty loop. */; continue; } for( r = 100; r > 0; r-- ) { /* FIXME: Something is missing here? A video HW check? */ cs7615_write_reg(dev, init_table_cs7615[i].reg, init_table_cs7615[i].value); if( cs7615_read_reg(dev, init_table_cs7615[i].reg) == init_table_cs7615[i].value ) break; wnv_sleep(20); } if( r <= 0 ) err_msg("Why isn't this working 7615 = %d.\n", NUM_CS7615_REGISTERS - i ); } if( check_video_synchs( dev ) ) j = 6; else j++; } dev->videc.decoder_is_stable = 1; return 1; } static int ccd_set_input(struct videum_device *dev, int i) { #ifdef USE_CCD int svhs = 0, luma = 1, chroma = 0; int x; #endif info_msg("ccd_set_input type %d\n", i); #ifdef USE_CCD dev->input = i; switch (i) { case VSOURCE_MXC: luma = 1; break; case VSOURCE_COMP: luma = 3; break; case VSOURCE_SVIDEO: luma = 2; svhs = 1; break; } luma = 3 - luma; chroma = (1 - chroma) << 2; svhs <<= 5; /* input multiplexers */ x = ccd_read_reg(dev, 0x33); ccd_write_reg(dev, 0x33, (x & 0xF8) | 0x08 | chroma | luma); /* SVHS mode */ x = ccd_read_reg(dev, 0x10F2); ccd_write_reg(dev, 0x10F2, (x & ~0x20) | svhs | 0x10); //dev->videc.decoder_is_stable = 0; #endif return 1; } static int ccd_set_standard(struct videum_device *dev, int x) { info_msg("ccd_set_standard type %d\n", x); switch (x) { case V4L2_STD_NTSC: #ifdef USE_CCD ccd_write_reg(dev, 0x1089, 0x0F8); ccd_write_reg(dev, 0x108A, 0x0F8); ccd_write_reg(dev, 0x10F2, 0x3D3); #endif dev->videc.frame_period = 333667; break; case V4L2_STD_PAL: #ifdef USE_CCD ccd_write_reg(dev, 0x1089, 0x128); ccd_write_reg(dev, 0x108A, 0x128); ccd_write_reg(dev, 0x10F2, 0x3DB); #endif dev->videc.frame_period = 400000; break; case V4L2_STD_SECAM:/* BUG: this is not right for SECAM */ #ifdef USE_CCD ccd_write_reg(dev, 0x1089, 0x128); ccd_write_reg(dev, 0x108A, 0x128); ccd_write_reg(dev, 0x10F2, 0x3DB); #endif dev->videc.frame_period = 400000; break; } dev->videc.standard = x; return 1; } static int ccd_set_vcrmode(struct videum_device *dev, int x) { return 1; } static int ccd_is_stable(struct videum_device *dev) { return 1; } static int ccd_set_white_balance(struct videum_device *dev, int x) { return 1; } static int ccd_probe(struct videum_device *dev) { int x; if (dev->videc.i2c_addr1 != 0) return 0;/* Another device was already found */ dev->videc.i2c_addr1 = CS7615_ADDRESS0; dev->videc.i2c_addr2 = CS7665_ADDRESS; x = cs7615_read_reg(dev, 0);/* Version */ if (x == -1) { dev->videc.i2c_addr1 = CS7615_ADDRESS1; x = cs7615_read_reg(dev, 0x0); if (x == -1) { dev->videc.i2c_addr1 = 0; dev->videc.i2c_addr2 = 0; } } if ( dev->videc.i2c_addr2 != 0 ) { x = cs7665_read_reg( dev, CS7665_VERSION_REG ); if ( x == -1 ) { dev->videc.i2c_addr1 = 0; dev->videc.i2c_addr2 = 0; return 0;/* Not found */ } } dev->videc.type = VIDEC_CS76X5; dev->videc.version = x; /* Fill in the method fields */ dev->videc.initialize = ccd_initialize; dev->videc.set_input = ccd_set_input; dev->videc.set_standard = ccd_set_standard; dev->videc.set_vcrmode = ccd_set_vcrmode; dev->videc.is_stable = ccd_is_stable; dev->videc.set_white_balance = ccd_set_white_balance; info_msg("Found CS7615/CS7665 (ver.%d) CCDs at %2X and %2X\n", dev->videc.version, dev->videc.i2c_addr1, dev->videc.i2c_addr2 ); return 1;/* Found */ } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Probe I2C bus for video decoder and fill in the device fields */ static int find_decoder(struct videum_device *dev) { if (!p7110_probe(dev) && !p7111a_probe(dev) && !ittvpx_probe(dev) && !ccd_probe(dev)) return 0;/* Failure */ return 1; } static void set_video_input(struct videum_device *dev, int i) { if (i < 0 || i >= dev->videc.num_inputs) return; dev->videc.set_input(dev, i); /*dev->videc.set_vcrmode(dev, dev->source[i].vcrmode);*/ wavi_vsc(dev, dev->source[i].wavi_vsc); wavi_vss(dev, dev->source[i].wavi_vss); wavi_vhsd(dev, dev->source[i].wavi_vhsd); wavi_tone_controls(dev); } /* * * W A V I V I D E O C A P T U R E M I C R O C O D E * */ /* Video line capture modes */ #define VMODE_NORMAL 0 /* Capture incoming pixels to VID1PTR */ #define VMODE_AVGBUF 1 /* Capture incoming pixels to VID2PTR */ #define VMODE_AVG 2 /* Average incoming with VID2PTR to VID1PTR */ #define VMODE_SKIP (~0) /* Fake mode: Skip whole line */ struct uc_buffer { __u8 *p; /* Current position */ int len; /* Bytes remaining */ }; static void compile_repeat(struct uc_buffer *buf, __u8 ui, int n) { if (buf->len < n) n = buf->len; buf->len -= n; while (n--) *(buf->p)++ = ui; } static void compile_start_of_field(struct uc_buffer *buf, int nfield, int vskip) { if (vskip < 0 || vskip > 256) vskip = 0; if (buf->len < 4 + vskip) return; *(buf->p)++ = UI_WTVB; if (nfield == 1) *(buf->p)++ = UI_WTVF1; else *(buf->p)++ = UI_WTVF2; *(buf->p)++ = UI_WTVB; *(buf->p)++ = UI_WTVA; compile_repeat(buf, UI_WTHS, vskip); buf->len -= 4 + vskip; } static void compile_interrupt(struct uc_buffer *buf, __u8 ui_interrupt, int flags) { if (buf->len < 3) return; *(buf->p)++ = ui_interrupt; --buf->len; if (flags & COMPFLAG_AVPRO_INT) { *(buf->p)++ = UI_SVS; *(buf->p)++ = UI_RVS; buf->len -= 2; } } static void compile_dma_trigger(struct uc_buffer *buf, int flags) { if (buf->len < 3) return; if (flags & COMPFLAG_AVPRO_DMA) { *(buf->p)++ = UI_SVS; *(buf->p)++ = UI_SVS; *(buf->p)++ = UI_RVS; buf->len -= 3; } } static void compile_set_videomode(struct uc_buffer *buf, int *videomode, int newmode) { int n = (newmode - *videomode) & 3; if (n == 0 || buf->len < n) return; buf->len -= n; while (n--) *(buf->p)++ = UI_SVM; *videomode = newmode; } static void compile_xcrop(struct uc_buffer *buf, int x) { if (x < 1 || buf->len < 2) return; if (x > 960) x = 960; *((__u16 *)buf->p)++ = UI_SKIP_LOWPREC(x); buf->len -= 2; } static void compile_horizontal_decimation( int source_width, int capture_width, int *hrate, int *hclocks, int flags) { int npx; if (source_width == 0) return; *hclocks = source_width; *hrate = (capture_width * 64) / source_width; if (*hrate < 2) *hrate = 2; npx = (*hclocks * *hrate + 32) / 64; /* Force the decimation ratio up if not enough pixels captured */ while (npx < capture_width) { (*hrate)++; npx = (*hclocks * *hrate + 32) / 64; } /* Force the number of clocks down if too many pixels captured */ while (npx > capture_width) { (*hclocks)--; npx = ((*hclocks * *hrate) + 32) / 64; } /* Attempt to work around WAVI video sampler bugs: */ /* Capture for the maximum number of clocks that take the number of */ /* pixels we want */ while (((*hclocks + 1) * *hrate + 32) / 64 == npx) ++(*hclocks); } static void compile_line(struct uc_buffer *buf, int hskip, __u16 ui_capture1, __u16 ui_capture2, int *videomode, int vmode) { int x; if (buf->len < 15)/* No room at the inn... */ return; if (vmode == VMODE_SKIP)/* Don't capture any pixels on this line */ { *(buf->p)++ = UI_WTHS; --buf->len; return; } compile_set_videomode(buf, videomode, vmode); *(buf->p)++ = UI_WTHS; --buf->len; x = hskip; if (vmode == VMODE_AVGBUF || vmode == VMODE_AVG) {/* Rewind VID2PTR before capturing this line */ *(buf->p)++ = UI_RVP2; --buf->len; --x; } compile_xcrop(buf, x); *((__u16 *)buf->p)++ = ui_capture1; buf->len -= 2; if (ui_capture2) { *((__u16 *)buf->p)++ = ui_capture2; buf->len -= 2; } } static void compile_field(struct uc_buffer *buf, int source_height, int capture_height, int hskip, __u16 ui_capture1, __u16 ui_capture2, int interrupt_line, int dma_line, int flags) { int a, m; int source_line_count = 0; int captured_line_count = 0; int avg_buf_full = 0; int videomode = 0; a = source_height / 2; while (captured_line_count < capture_height) { if (a >= source_height) {/* Capture this line to output buffer */ a -= source_height; ++captured_line_count; m = (avg_buf_full) ? VMODE_AVG : VMODE_NORMAL; avg_buf_full = 0; } else {/* Capture to average buffer or just skip */ if (avg_buf_full || (flags & COMPFLAG_NOVERTFILTER)) m = VMODE_SKIP; else { m = VMODE_AVGBUF; avg_buf_full = 1; } } compile_line(buf, hskip, ui_capture1, ui_capture2, &videomode, m); ++source_line_count; if (source_line_count == dma_line) compile_dma_trigger(buf, flags); if (source_line_count == interrupt_line) compile_interrupt(buf, UI_ITR1, flags); a += capture_height; } compile_set_videomode(buf, &videomode, 0); if ((flags & COMPFLAG_HUFFMAN) && buf->len > 1) { *((__u16 *)buf->p)++ = UI_DECIMATE_LOWPREC(32, 64); buf->len -= 2; } while ( source_line_count < interrupt_line || source_line_count < dma_line) { compile_line(buf, 0, 0, 0, 0, VMODE_SKIP); ++source_line_count; if (source_line_count == dma_line) compile_dma_trigger(buf, flags); if (source_line_count == interrupt_line) compile_interrupt(buf, UI_ITR1, flags); } } static int/* Number of bytes of code generated */ compile_microcode(struct videum_device *dev, struct ucode_parms *parm) { struct uc_buffer buffer; int hrate; int hclocks; int field1 = 1; __u16 ui_capture1 = 0; __u16 ui_capture2 = 0; buffer.p = parm->buffer; buffer.len = dev->sram_ucode_size; if (parm->flags & COMPFLAG_VST_WHEN_DONE) { *(buffer.p)++ = UI_RVS; --buffer.len; } compile_horizontal_decimation(parm->source_width, parm->capture_width, &hrate, &hclocks, parm->flags); if (!((parm->flags & COMPFLAG_HUFFMAN) && hrate > 32) || ((parm->flags & COMPFLAG_HUFFMAN) && hrate > 19)) parm->flags |= COMPFLAG_NOVERTFILTER; if (hclocks > 960) { ui_capture1 = UI_DECIMATE_LOWPREC(hrate, hclocks/2); ui_capture2 = UI_DECIMATE_LOWPREC(hrate, hclocks - hclocks/2); } else { ui_capture1 = UI_DECIMATE_LOWPREC(hrate, hclocks); } /* Center the actual sample range within desired sample range */ if (hclocks > parm->source_width) hclocks = parm->source_width; parm->source_x += (parm->source_width - hclocks) / 2; /* Horizontal filter */ parm->filter = 0; if (hrate < 48) parm->filter |= WAVI_VFS_MED3; if (hrate < 16) parm->filter |= WAVI_VFS_FIR4; else if (hrate < 32) parm->filter |= WAVI_VFS_FIR2; field1 = parm->field; compile_start_of_field(&buffer, field1, parm->source_y); compile_field(&buffer, parm->source_height, parm->capture_height, parm->source_x, ui_capture1, ui_capture2, parm->int1_line, parm->dma_line, parm->flags); if (buffer.len < 16) { err_msg("Microcode buffer too small.\n"); return 0;/* Ran out of buffer! Oh, no! */ } if (parm->flags & COMPFLAG_VST_WHEN_DONE) { *(buffer.p)++ = UI_SVS; --buffer.len; } *(buffer.p)++ = UI_HLT; --buffer.len; return ((dev->sram_ucode_size - buffer.len) + 3) & ~3; } /* * * I M A G E F O R M A T T R A N S L A T I O N * */ static int translate_yuyv_grey(struct translation *xlat) { __u8 *esi, *edi; __u32 eax, ebx, ecx, edx; int row; esi = xlat->in; edi = xlat->out; eax = xlat->in_stride - xlat->width * 2; ebx = xlat->out_stride - xlat->width; for (row = xlat->height; row; esi += eax, edi += ebx, --row) { for (ecx = xlat->width >> 2; ecx; --ecx) { edx = esi[4] | (esi[6] << 8); edx <<= 16; edx |= esi[0] | (esi[2] << 8); esi += 8; *(__u32 *)edi = edx; edi += 4; } } return 1; } static int translate_yuyv_yuv420(struct translation *xlat) { __u8 *esi, *edi; __u32 eax, ebx, ecx, edx; __u8 dl; int row; /* Y's */ esi = xlat->in; edi = xlat->out; eax = xlat->in_stride - xlat->width * 2; ebx = xlat->out_stride - xlat->width; for (row = xlat->height; row; esi += eax, edi += ebx, --row) { for (ecx = xlat->width >> 2; ecx; --ecx) { edx = esi[4] | (esi[6] << 8); edx <<= 16; edx |= esi[0] | (esi[2] << 8); esi += 8; *(__u32 *)edi = edx; edi += 4; } } /* U's */ esi = xlat->in + xlat->in_stride; eax = xlat->in_stride * 2 - xlat->width * 2; ebx >>= 1; for (row = xlat->height >> 1; row; esi += eax, edi += ebx, --row) { for (ecx = xlat->width >> 1; ecx; ++edi, --ecx) { dl = esi[1]; esi += 4; *edi = dl; } } /* V's */ esi = xlat->in + xlat->in_stride; for (row = xlat->height >> 1; row; esi += eax, edi += ebx, --row) { for (ecx = xlat->width >> 1; ecx; ++edi, --ecx) { dl = esi[3]; esi += 4; *edi = dl; } } return 1; } #define K12_1 4096 #define K12_S 12 #define K12_GU -1409 #define K12_BU 7258 #define K12_RV 5743 #define K12_GV -2925 static int translate_expand_y(int y) { y = (255 * y + 110) / 220; if (y < 0) y = 0; else if (y > 255) y = 255; return y; } static int translate_expand_c(int c) { c = (127 * c + 56) / 112; if (c < -128) c = -128; else if (c > 127) c = 127; return c; } struct temp_rgb16_lut { long gu[32], bu[32], rv[32], gv[32]; int rscale[256], gscale[256], bscale[256]; }; static int translate_make_rgb16_lut(struct translation *xlat) { __u16 *lut; struct temp_rgb16_lut *tmp; int rrange, grange, brange; int rshift, gshift, bshift; long x; int y, u, v; int r, g, b; int i, t; if ((xlat->type == XLAT_YUYV_TO_RGB555 && xlat->lut[0].type == LUT_RGB555) || (xlat->type == XLAT_YUYV_TO_RGB565 && xlat->lut[0].type == LUT_RGB565)) return 1; if (xlat->lut[0].table.base) vfree(xlat->lut[0].table.base); xlat->lut[0].table.base = vmalloc(1 << 17); if (xlat->lut[0].table.base == NULL) { err_msg("vmalloc() failed in make_rgb16_lut\n"); return 0; } lut = xlat->lut[0].table.rgb16; // Compute all different chroma components to 8-bit precision tmp = (struct temp_rgb16_lut *)vmalloc(sizeof(struct temp_rgb16_lut)); if (tmp == NULL) { err_msg("vmalloc() failed in make_rgb16_lut\n"); return 0; } for (i = 0, t = -128; t < 128; t += 8, ++i) { x = translate_expand_c(t) + 2; tmp->gu[i] = (K12_GU * x + K12_1/2) >> K12_S; tmp->bu[i] = (K12_BU * x + K12_1/2) >> K12_S; tmp->rv[i] = (K12_RV * x + K12_1/2) >> K12_S; tmp->gv[i] = (K12_GV * x + K12_1/2) >> K12_S; } // 8-bit to ?-bit scaling tables if (xlat->type == XLAT_YUYV_TO_RGB555) { xlat->lut[0].type = LUT_RGB555; rrange = grange = brange = 31; rshift = 10; gshift = 5; bshift = 0; } else { xlat->lut[0].type = LUT_RGB565; rrange = brange = 31; grange = 63; rshift = 11; gshift = 5; bshift = 0; } for (i = 0; i < 256; ++i) { tmp->rscale[i] = ((i * rrange + 127) / 255) << rshift; tmp->gscale[i] = ((i * grange + 127) / 255) << gshift; tmp->bscale[i] = ((i * brange + 127) / 255) << bshift; } // Fill in the RGB values for each combination of YUV for (i = 0; i < 256; i += 4) { y = translate_expand_y(i) + 2; if (y > 255) y = 255; for (u = 0; u < 32; ++u) for (v = 0; v < 32; ++v) { // Red, Green and Blue r = y + tmp->rv[v]; g = y + tmp->gu[u] + tmp->gv[v]; b = y + tmp->bu[u]; // Saturate if (r < 0) r = 0; else if (r > 254) r = 254; if (g < 0) g = 0; else if (g > 254) g = 254; if (b < 0) b = 0; else if (b > 254) b = 254; // scale, shift and combine *lut++ = tmp->rscale[r] + tmp->gscale[g] + tmp->bscale[b]; } } vfree(tmp); return 1; } static int translate_yuyv_rgb16(struct translation *xlat) { __u32 *src, *dst; __u32 uv, yuv0, yuv1, dual; __u16 *lut; int stride; int row, i; if (!translate_make_rgb16_lut(xlat)) return 0; src = (__u32 *)xlat->in; dst = (__u32 *)xlat->out; lut = xlat->lut[0].table.rgb16; stride = (xlat->out_stride - xlat->width * 2) >> 2; for (row = xlat->height; row; --row) { for (i = xlat->width >> 1; i; --i) { dual = *src++; uv = ((dual & 0x0000F800) >> 6) + ((dual & 0xF8000000) >> 27); yuv1 = ((dual & 0x00FC0000) >> 8) + uv; yuv0 = ((dual & 0x000000FC) << 8) + uv; *dst++ = ((__u32)lut[yuv1] << 16) | lut[yuv0]; } dst += stride; } return 1; } static int translate_make_rgb24_lut(struct translation *xlat) { struct lookup_rgb24 *lut; int r, g, b; int i; int x; if (xlat->lut[0].type == LUT_RGB24) return 1; if (xlat->lut[0].table.base) vfree(xlat->lut[0].table.base); xlat->lut[0].table.base = vmalloc(sizeof(struct lookup_rgb24)); if (xlat->lut[0].table.base == NULL) { err_msg("vmalloc() failed in make_rgb24_lut\n"); return 0; } xlat->lut[0].type = LUT_RGB24; lut = xlat->lut[0].table.rgb24; for (i = 0; i < 256; ++i) { x = i; // Value is in excess-128 format if (x < 128) ++x; // Add 1 to negative values for noise rejection x -= 128; // Convert to two's complement format x = translate_expand_c(x); g = (K12_GU * x + K12_1/2) >> K12_S; b = (K12_BU * x + K12_1/2) >> K12_S; lut->u_rgb[i] = ((g & 0x3FF) << 11) | (b & 0x3FF); r = (K12_RV * x + K12_1/2) >> K12_S; g = (K12_GV * x + K12_1/2) >> K12_S; lut->v_rgb[i] = (r << 22) | ((g & 0x3FF) << 11); x = translate_expand_y(i); lut->y_rgb[i] = (x << 22) | (x << 11) | x; } for (i = 0; i < 1024; ++i) { x = (i > 511) ? 0 : ((i > 255) ? 255 : i); lut->sat[i] = x; } return 1; } static int translate_yuyv_rgb24(struct translation *xlat) { struct lookup_rgb24 *lut; __u8 *src; __u32 *dst; int i; int row; int stride; __u32 pela, pelb, pelc, peld; if (!translate_make_rgb24_lut(xlat)) return 0; lut = xlat->lut[0].table.rgb24; src = (__u8 *)xlat->in; dst = (__u32 *)xlat->out; stride = (xlat->out_stride - 3 * xlat->width) >> 2; for (row = xlat->height; row; --row) { for (i = xlat->width >> 2; i; --i) { pelb = lut->u_rgb[src[1]] + lut->v_rgb[src[3]]; pela = lut->y_rgb[src[0]] + pelb; pelb += lut->y_rgb[src[2]]; peld = lut->u_rgb[src[5]] + lut->v_rgb[src[7]]; pelc = lut->y_rgb[src[4]] + peld; peld += lut->y_rgb[src[6]]; src += 8; dst[0] = ((u32)lut->sat[pela & 0x3FF]) + ((u32)lut->sat[(pela >> 11) & 0x3FF] << 8) + ((u32)lut->sat[pela >> 22] << 16) + ((u32)lut->sat[pelb & 0x3FF] << 24); dst[1] = ((u32)lut->sat[(pelb >> 11) & 0x3FF]) + ((u32)lut->sat[pelb >> 22] << 8) + ((u32)lut->sat[pelc & 0x3FF] << 16) + ((u32)lut->sat[(pelc >> 11) & 0x3FF] << 24); dst[2] = ((u32)lut->sat[pelc >> 22]) + ((u32)lut->sat[peld & 0x3FF] << 8) + ((u32)lut->sat[(peld >> 11) & 0x3FF] << 16) + ((u32)lut->sat[peld >> 22] << 24); dst += 3; } dst += stride; } return 1; } static int translate_yuyv_rgb32(struct translation *xlat) { struct lookup_rgb24 *lut; __u8 *src; __u32 *dst; int i; int row; int stride; __u32 pela, pelb; if (!translate_make_rgb24_lut(xlat)) return 0; lut = xlat->lut[0].table.rgb24; src = (__u8 *)xlat->in; dst = (__u32 *)xlat->out; stride = (xlat->out_stride - 4 * xlat->width) >> 2; for (row = xlat->height; row; --row) { for (i = xlat->width >> 1; i; --i) { pelb = lut->u_rgb[src[1]] + lut->v_rgb[src[3]]; pela = lut->y_rgb[src[0]] + pelb; pelb += lut->y_rgb[src[2]]; src += 4; dst[0] = (u32)lut->sat[pela & 0x3FF] + ((u32)lut->sat[(pela >> 11) & 0x3FF] << 8) + ((u32)lut->sat[pela >> 22] << 16); dst[1] = (u32)lut->sat[pelb & 0x3FF] + ((u32)lut->sat[(pelb >> 11) & 0x3FF] << 8) + ((u32)lut->sat[pelb >> 22] << 16); dst += 2; } dst += stride; } return 1; } /* Delta-Huffman decompression routines */ /* translate_make_dehuff_lut() */ #include "wnvhuff.h" static void translate_close(struct videum_device *dev) { int i; dev->translation1.type = XLAT_NULL; dev->translation1.in = NULL; dev->translation1.out = NULL; for (i = 0; i < 4; ++i) { dev->translation1.lut[i].type = LUT_NULL; if (dev->translation1.lut[i].table.base) vfree(dev->translation1.lut[i].table.base); dev->translation1.lut[i].table.base = NULL; } dev->translation2.type = XLAT_NULL; dev->translation2.in = NULL; dev->translation2.out = NULL; dev->translation2.lut[0].type = LUT_NULL; if (dev->translation2.lut[0].table.base) vfree(dev->translation2.lut[0].table.base); dev->translation2.lut[0].table.base = NULL; if (dev->xlat_temp) vfree(dev->xlat_temp); dev->xlat_temp = NULL; } static int translate_setup(struct videum_device *dev) { int npix2; translate_close(dev); /* Translation 1: Capture format to YUYV */ dev->translation1.width = dev->uc_parm[UC_NORMAL].capture_width; dev->translation1.height = dev->uc_parm[UC_NORMAL].capture_height; dev->translation1.in_stride = 0; if (dev->capture_compress) { dev->translation1.type = XLAT_HUFF_TO_YUYV; dev->translation1.out_stride = dev->translation1.width * 2; dev->translation1.output_size = dev->translation1.out_stride * dev->translation1.height; if (!translate_make_dehuff_lut(&dev->translation1)) return 0; if (dev->xlat_temp != NULL) vfree(dev->xlat_temp); dev->xlat_temp = vmalloc(dev->translation1.output_size); if (dev->xlat_temp == NULL) { err_msg("Can't allocate xlat temp buffer %d bytes\n", dev->translation1.output_size); return 0; } } else { dev->translation1.type = XLAT_NULL; dev->translation1.output_size = 0; } /* Translation 2: YUYV to client format */ dev->translation2.width = dev->uc_parm[UC_NORMAL].capture_width; dev->translation2.height = dev->uc_parm[UC_NORMAL].capture_height; dev->translation2.in_stride = dev->translation2.width * 2; dev->translation2.output_size = dev->clientfmt.fmt.pix.sizeimage; npix2 = dev->translation2.width * dev->translation2.height; switch (dev->clientfmt.fmt.pix.pixelformat) { case V4L2_PIX_FMT_YUYV: dev->translation2.type = XLAT_NULL; break; case V4L2_PIX_FMT_GREY: dev->translation2.type = XLAT_YUYV_TO_GREY; dev->translation2.out_stride = dev->translation2.width; break; case V4L2_PIX_FMT_YUV420: dev->translation2.type = XLAT_YUYV_TO_YUV420; dev->translation2.out_stride = dev->translation2.width; break; case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB565: dev->translation2.type = (dev->clientfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB555) ? XLAT_YUYV_TO_RGB555 : XLAT_YUYV_TO_RGB565; dev->translation2.out_stride = dev->translation2.width * 2; if (!translate_make_rgb16_lut(&dev->translation2)) return 0; break; case V4L2_PIX_FMT_BGR24: dev->translation2.type = XLAT_YUYV_TO_RGB24; dev->translation2.out_stride = dev->translation2.width * 3; if (!translate_make_rgb24_lut(&dev->translation2)) return 0; break; case V4L2_PIX_FMT_BGR32: dev->translation2.type = XLAT_YUYV_TO_RGB32; dev->translation2.out_stride = dev->translation2.width * 4; if (!translate_make_rgb24_lut(&dev->translation2)) return 0; break; } return 1; } static void translate_inandout(struct videum_device *dev, __u8 *input_buffer, __u8 *output_buffer, __u8 output_is_user_space) { /* Translation 1: Capture format to YUYV */ dev->translation1.in = input_buffer; dev->translation1.output_is_user = 0; if (dev->capture_compress) { if (dev->capture_vdf == WAVI_VDF_YCQ) dev->translation1.type = XLAT_YCQ_TO_YUYV; else { dev->translation1.type = XLAT_HUFF_TO_YUYV; dev->translation1.ilut = (dev->capture_vdf >> 5) - 4; } dev->translation1.out = dev->xlat_temp; } else { dev->translation1.out = input_buffer; } /* Translation 2: YUYV to client format */ dev->translation2.in = dev->translation1.out; dev->translation2.out = output_buffer; dev->translation2.output_is_user = output_is_user_space; } static int /* length of output image or negative error */ translate_image(struct videum_device *dev, __u8 *input_buffer, __u8 *output_buffer, int len, int output_is_user) { int err; /* The buffer must be large enough for the whole image */ if (len < dev->translation2.output_size) { debug_msg("Read buffer too small, %d < %d\n", len, dev->translation2.output_size); return -EFAULT; } if (len > dev->translation2.output_size) len = dev->translation2.output_size; translate_inandout(dev, input_buffer, output_buffer, output_is_user); /* Translation 1: Capture format to YUYV */ if (dev->capture_compress) { if (dev->translation1.out == NULL) { err_msg("Internal error, xlat_temp == NULL\n"); return -EFAULT; } switch (dev->translation1.type) { case XLAT_YCQ_TO_YUYV: translate_ycq_yuyv(&dev->translation1); break; case XLAT_HUFF_TO_YUYV: translate_dh_yuyv(&dev->translation1); break; } } /* Translation 2: YUYV to client format */ if (dev->translation2.in == dev->translation2.out) { return len; } if (dev->translation2.type == XLAT_NULL) { if (!output_is_user) memcpy(output_buffer, dev->translation2.in, len); else { err = copy_to_user(output_buffer, dev->translation2.in, len); len = (err) ? -EFAULT : len; } return len; } if (output_is_user && !access_ok(VERIFY_WRITE, output_buffer, len)) return -EFAULT; switch (dev->translation2.type) { case XLAT_YUYV_TO_GREY: translate_yuyv_grey(&dev->translation2); break; case XLAT_YUYV_TO_YUV420: translate_yuyv_yuv420(&dev->translation2); break; case XLAT_YUYV_TO_RGB555: case XLAT_YUYV_TO_RGB565: translate_yuyv_rgb16(&dev->translation2); break; case XLAT_YUYV_TO_RGB24: translate_yuyv_rgb24(&dev->translation2); break; case XLAT_YUYV_TO_RGB32: translate_yuyv_rgb32(&dev->translation2); break; } dev->translation2.out = NULL; return len; } /* * * V I D E O C A P T U R E F U N C T I O N S * */ /* * Supported capture formats */ static struct v4l2_fmtdesc capfmt[] = { { 0, {"RGB-16 (5-5-5)"}, V4L2_PIX_FMT_RGB555, 0, 16, {0, 0}, }, { 1, {"RGB-16 (5-6-5)"}, V4L2_PIX_FMT_RGB565, 0, 16, {0, 0}, }, { 2, {"RGB-24 (B-G-R)"}, V4L2_PIX_FMT_BGR24, 0, 24, {0, 0}, }, { 3, {"RGB-32 (B-G-R-?)"}, V4L2_PIX_FMT_BGR32, 0, 32, {0, 0}, }, { 4, {"Greyscale-8"}, V4L2_PIX_FMT_GREY, V4L2_FMT_CS_601YUV, 8, {0, 0}, }, { 5, {"YUV 4:2:2 (Y-U-Y-V)"}, V4L2_PIX_FMT_YUYV, V4L2_FMT_CS_601YUV, 16, {0, 0}, }, { 6, {"YUV 4:2:0 (planar)"}, V4L2_PIX_FMT_YUV420, V4L2_FMT_CS_601YUV, 12, {0, 0}, }, }; #define NUM_CAPFMT (sizeof(capfmt)/sizeof(capfmt[0])) static void interrupt_enable(struct videum_device *dev); /* The image format has changed, width, height, pixel format. * Decide if the format is ok or take the closest valid format. */ static void capture_new_format(struct videum_device *dev) { int ntsc; int max_height; int max_width; int max_pixels; int t; if (dev->stream_buffers_mapped) return; /* BUG: won't work for auto */ ntsc = (dev->videc.standard == V4L2_STD_NTSC); dev->ready_to_capture = 0; dev->uc_parm[UC_NORMAL].source_x = (ntsc) ? dev->videc.ntsc_hskip : dev->videc.pal_hskip; dev->uc_parm[UC_NORMAL].source_y = (ntsc) ? dev->videc.ntsc_vskip : dev->videc.pal_vskip; dev->uc_parm[UC_NORMAL].source_width = (ntsc) ? dev->videc.ntsc_width : dev->videc.pal_width; dev->uc_parm[UC_NORMAL].source_height = (ntsc) ? dev->videc.ntsc_height : dev->videc.pal_height; dev->uc_parm[UC_NORMAL].field = dev->videc.preferred_field + 1; dev->clientfmt.fmt.pix.flags = V4L2_FMT_CS_601YUV; switch (dev->clientfmt.fmt.pix.pixelformat) { case V4L2_PIX_FMT_GREY: dev->clientfmt.fmt.pix.depth = 8; break; case V4L2_PIX_FMT_YUV420: dev->clientfmt.fmt.pix.depth = 12; break; case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB565: dev->clientfmt.fmt.pix.flags = 0; /* fall thru */ case V4L2_PIX_FMT_YUYV: dev->clientfmt.fmt.pix.depth = 16; break; case V4L2_PIX_FMT_BGR24: dev->clientfmt.fmt.pix.depth = 24; dev->clientfmt.fmt.pix.flags = 0; break; case V4L2_PIX_FMT_BGR32: dev->clientfmt.fmt.pix.depth = 32; dev->clientfmt.fmt.pix.flags = 0; break; default: debug_msg("Unknown format %4.4s\n", (char *)&dev->clientfmt.fmt.pix.pixelformat); dev->clientfmt.fmt.pix.depth = 16; dev->clientfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565; dev->clientfmt.fmt.pix.flags = 0; //return; break; } if (dev->clientfmt.fmt.pix.width < MIN_WIDTH) dev->clientfmt.fmt.pix.width = MIN_WIDTH; if (dev->clientfmt.fmt.pix.height < MIN_HEIGHT) dev->clientfmt.fmt.pix.height = MIN_HEIGHT; dev->clientfmt.fmt.pix.width &= ~3; dev->clientfmt.fmt.pix.height &= ~3; /* Decide compressed or uncompressed. Use uncompressed if there */ /* is enough SRAM memory */ dev->capture_compress = 0; dev->capture_bpp = 16; max_pixels = 8 * dev->sram_video_size / dev->capture_bpp; if (max_pixels <= dev->clientfmt.fmt.pix.width * dev->clientfmt.fmt.pix.height) {/* Compress! */ dev->capture_marker_spacing = 1024; dev->capture_compress = 1; dev->capture_bpp = 4; dev->capture_size = dev->sram_video_size & ~(dev->capture_marker_spacing - 1); max_pixels = 8 * dev->capture_size / dev->capture_bpp; debug_msg("Capturing compressed\n"); } max_width = dev->uc_parm[UC_NORMAL].source_width; max_height = dev->uc_parm[UC_NORMAL].source_height; t = isqrt((max_pixels * dev->clientfmt.fmt.pix.width) / dev->clientfmt.fmt.pix.height); if (t < max_width) max_width = t; t = isqrt((max_pixels * dev->clientfmt.fmt.pix.height) / dev->clientfmt.fmt.pix.width); if (t < max_height) max_height = t; if (dev->clientfmt.fmt.pix.width > max_width) dev->clientfmt.fmt.pix.width = max_width; if (dev->clientfmt.fmt.pix.height > max_height) dev->clientfmt.fmt.pix.height = max_height; dev->clientfmt.fmt.pix.width &= ~3; dev->clientfmt.fmt.pix.height &= ~3; dev->clientfmt.fmt.pix.sizeimage = (dev->clientfmt.fmt.pix.width * dev->clientfmt.fmt.pix.height * dev->clientfmt.fmt.pix.depth) / 8; if (!dev->capture_compress) { dev->capture_size = dev->clientfmt.fmt.pix.width * dev->clientfmt.fmt.pix.height * dev->capture_bpp / 8; dev->capture_min_size = dev->capture_size; dev->capture_vdf = WAVI_VDF_YCH; } if (dev->capture_compress) { dev->capture_min_size = dev->clientfmt.fmt.pix.width * dev->clientfmt.fmt.pix.height / 4; /* Two bits per pixel */ dev->capture_wcase_size = dev->clientfmt.fmt.pix.width * dev->clientfmt.fmt.pix.height / 2; /* Four bits per pixel */ dev->capture_vdf = WAVI_VDF_DH5; dev->capture_low_water = LOW_WATER * dev->capture_size / 100; dev->capture_high_water = HIGH_WATER * dev->capture_size / 100; } /* Normal microcode */ dev->uc_parm[UC_NORMAL].capture_width = dev->clientfmt.fmt.pix.width; dev->uc_parm[UC_NORMAL].capture_height = dev->clientfmt.fmt.pix.height; dev->uc_parm[UC_NORMAL].capture_min_size = dev->capture_min_size; dev->uc_parm[UC_NORMAL].capture_max_size = dev->capture_size; /* Worstcase microcode */ if (dev->capture_compress) { memcpy(&dev->uc_parm[UC_WORSTCASE], &dev->uc_parm[UC_NORMAL], sizeof(dev->uc_parm[0])); dev->uc_parm[UC_WORSTCASE].capture_height = dev->clientfmt.fmt.pix.height >> 1; dev->uc_parm[UC_WORSTCASE].capture_min_size = dev->capture_wcase_size; dev->uc_parm[UC_WORSTCASE].capture_max_size = dev->capture_wcase_size; } } /* Stop the music! */ static void capture_abort(struct videum_device *dev) { dev->sampler_enabled = 0; wavi_video_sampler(dev, 0); bm_disarm(dev); } static void capture_armdma(struct videum_device *dev, struct scatter_node *list) { if (dev->dma_mode == DMAMODE_SLAVE) return; wavi_writereg(dev, WAVI_MXRPTR, dev->sram_video >> 4); bm_arm(dev, list); } static void capture_dma_line(struct videum_device *dev, struct ucode_parms *parm) { int height = parm->source_height; int rate; int line; parm->dma_line = 0; rate = dev->dma_rate / 15625;/* bytes per 64us */ if (dev->dma_mode == DMAMODE_SLAVE || rate == 0 || height == 0) return; /* number of horizontal periods to read frame */ line = parm->capture_min_size / rate; line = height - line; if (line < 5) line = 5; if (line > height) line = height; //debug_msg("DMA line %d\n", line); parm->dma_line = line; } static void capture_int_line(struct videum_device *dev, struct ucode_parms *parm) { int height = parm->source_height; int rate; int line = height; parm->int1_line = height; if (height == 0) return; if (dev->dma_mode == DMAMODE_SLAVE) {/* Non-DMA case */ if (dev->slave_rate == 0) return; rate = dev->slave_rate / 15625; line = parm->capture_min_size / rate; line = height - line; if (line < 1) line = 1; if (line > height) line = height; } if (dev->dma_mode == DMAMODE_SIMPLE) { if (dev->dma_rate == 0) return; rate = dev->dma_rate / 15625; line = parm->capture_max_size / rate; line = (line * 110) / 100;/* fudge dma rate variations */ line = parm->dma_line + line; if (line < parm->dma_line) line = parm->dma_line; if (line > 1000) line = 1000; } //debug_msg("Interrupt line %d\n",line); parm->int1_line = line; } static int compile_motion_microcode(struct videum_device *dev, int uc) { capture_dma_line(dev, &dev->uc_parm[uc]); if (dev->uc_parm[uc].dma_line == 0) { //debug_msg("DMA line zero\n"); dev->dma_mode = DMAMODE_SLAVE; } capture_int_line(dev, &dev->uc_parm[uc]); dev->uc_parm[uc].int2_line = 0; dev->uc_parm[uc].flags = 0; if (dev->type == VIDEUM_TYPE_PCI && dev->dma_mode != DMAMODE_SLAVE) dev->uc_parm[uc].flags |= COMPFLAG_AVPRO_DMA | COMPFLAG_AVPRO_INT; else dev->uc_parm[uc].flags |= COMPFLAG_VST_WHEN_DONE; if (dev->capture_compress && uc == UC_NORMAL) dev->uc_parm[uc].flags |= COMPFLAG_HUFFMAN; dev->uc_loaded = UC_UNDEF; dev->uc_parm[uc].buffer = dev->uc_buffer[uc]; dev->uc_length[uc] = compile_microcode(dev, &dev->uc_parm[uc]); if (dev->uc_length[uc] == 0) {/* ucode too long, no vertical filter reduces the length */ dev->uc_parm[uc].flags |= COMPFLAG_NOVERTFILTER; dev->uc_length[uc] = compile_microcode( dev, &dev->uc_parm[uc]); if (dev->uc_length[uc] == 0) { err_msg("Internal error - can't compile ucode\n"); return 0;/* failed */ } } return 1;/* succeeded */ } /* Allocate buffers, compile microcode, and get everything ready to capture * an image, but don't start capturing yet. */ static int capture_begin(struct videum_device *dev) { capture_abort(dev); if (dev->ready_to_capture) return dev->ready_to_capture; if (dev->capture_buffer_size < dev->capture_size) { if (dev->capture_buffer != NULL) vfree(dev->capture_buffer); dev->capture_buffer_size = (dev->capture_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); dev->capture_buffer = (__u8 *) vmalloc(dev->capture_buffer_size); if (dev->capture_buffer == NULL) { dev->capture_buffer_size = 0; err_msg("Can't allocate capture buffer" " %d bytes\n", dev->capture_size); return dev->ready_to_capture; } } dev->dma_mode = DMAMODE_SLAVE; if (dev->irq && dev->dma_rate && bm_build_scatter_list(dev, dev->capture_buffer, &dev->capture_dma_list)) { dev->dma_mode = DMAMODE_SIMPLE; } if (!compile_motion_microcode(dev, UC_NORMAL)) return dev->ready_to_capture; if (dev->capture_compress && !compile_motion_microcode(dev, UC_WORSTCASE)) return dev->ready_to_capture; if (!translate_setup(dev)) return dev->ready_to_capture; debug_msg("Ready to capture DMA mode = %d\n",dev->dma_mode); interrupt_enable(dev); return (dev->ready_to_capture = 1); } /* Start an image capture */ static void capture_grab_frame(struct videum_device *dev) { struct scatter_node *list; int uc = UC_NORMAL; if (dev->ready_to_capture && dev->sampler_enabled) return; capture_begin(dev); if (!dev->ready_to_capture) return; if (dev->capture_vdf == WAVI_VDF_YCQ) uc = UC_WORSTCASE; if (dev->uc_loaded != uc) { if (dev->type == VIDEUM_TYPE_PCI) wavi_do_rvs(dev); wavi_block_write(dev, dev->sram_ucode, dev->uc_buffer[uc], dev->uc_length[uc]); wavi_filter(dev, dev->uc_parm[uc].filter); dev->uc_loaded = uc; } wavi_vdf(dev, dev->capture_vdf); wavi_writereg(dev, WAVI_SPLPTR, dev->sram_ucode >> 4); wavi_writereg(dev, WAVI_VID2PTR, dev->sram_linebuf >> 4); wavi_writereg(dev, WAVI_VID1PTR, dev->sram_video >> 4); if (dev->capture_vdf >= WAVI_VDF_DH4) { int n; int start; int msm1 = dev->capture_marker_spacing - 1; start = (dev->capture_min_size + msm1) & ~msm1; n = ((dev->capture_size + msm1) & ~msm1) - start; wavi_write_markers(dev, dev->sram_video + start, n >> 10, dev->capture_marker_spacing); } list = dev->capture_dma_list;/* DMA list for capture_buffer */ if (dev->streaming) { struct stream_buffer *buf; /* Capture to capture_buffer */ dev->stream_capture_buffer = dev->capture_buffer; /* Capture straight into streaming buffer? */ if (dev->translation1.type == XLAT_NULL && dev->translation2.type == XLAT_NULL) { // dev->stream_capture_buffer = NULL;/*test:*/ buf = v4l2_q_peek_head(&dev->stream_q_capture); if (buf != NULL) { dev->stream_capture_buffer = buf->vaddress; list = buf->dma_list; } // if (buf == NULL)/*test:*/ // return; } } if (dev->dma_mode != DMAMODE_SLAVE) capture_armdma(dev, list); wavi_video_sampler(dev, 1); dev->sampler_enabled = 1; dev->capture_completed = 0; } /* * STREAMING CAPTURE */ static int/* 1 = success; 0 = failed */ capture_queuebuffer(struct videum_device *dev, struct v4l2_buffer *vidbuf) { int i = vidbuf->index; struct stream_buffer *buf = NULL; if (!dev->stream_buffers_mapped || vidbuf->type != V4L2_BUF_TYPE_CAPTURE) { debug_msg("QBUF no buffers mapped or wrong type\n"); return 0; } if (i < 0 || i >= MAX_CAPTURE_BUFFERS || !dev->stream_buf[i].requested) { debug_msg("QBUF buffer index %d is out of range\n", i); return 0; } buf = &dev->stream_buf[i]; if (!(buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED)) { debug_msg("QBUF buffer %d is not mapped\n", i); return 0; } if ((buf->vidbuf.flags & V4L2_BUF_FLAG_QUEUED)) { debug_msg("QBUF buffer %d is already queued\n", i); return 0; } buf->vidbuf.flags &= ~V4L2_BUF_FLAG_DONE; v4l2_q_add_tail(&dev->stream_q_capture, &buf->qnode); buf->vidbuf.flags |= V4L2_BUF_FLAG_QUEUED; capture_grab_frame(dev);/* does nothing if already capturing */ return 1; } static int/* 1 = got a buffer; 0 = no buffers */ capture_dequeuebuffer(struct videum_device *dev, struct v4l2_buffer *buf) { struct stream_buffer *newbuf; if (!dev->streaming || buf->type != V4L2_BUF_TYPE_CAPTURE) { debug_msg("DQBUF not streaming or wrong buffer type\n"); return 0; } newbuf = v4l2_q_del_head(&dev->stream_q_done); if (newbuf == NULL) { debug_msg("DQBUF nothing on done queue\n"); return 0; } newbuf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; *buf = newbuf->vidbuf; return 1; } static int capture_streamon(struct videum_device *dev, __u32 type) { struct stream_buffer *buf; if (dev->streaming || type != V4L2_BUF_TYPE_CAPTURE) return 0; capture_abort(dev);/* cancel any capture that might be in progress */ /* -2 is a magic number that triggers start-of-stream logic in */ /* capture_interrupt() */ dev->stream_last_frame = -2; dev->perf.frames = 0; dev->perf.framesdropped = 0; dev->perf.bytesout = 0; /* Can't capture frames faster than the video input */ if (dev->capture.timeperframe < dev->videc.frame_period) dev->capture.timeperframe = dev->videc.frame_period; /* Move any leftover DONE buffers to the free pool */ while ((buf = v4l2_q_del_head(&dev->stream_q_done))) buf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; /* Kick off the machine */ dev->streaming = 1; capture_grab_frame(dev); return 1; } static void capture_streamoff(struct videum_device *dev, __u32 type) { if (!dev->streaming || type != V4L2_BUF_TYPE_CAPTURE) return; capture_abort(dev); dev->streaming = 0; /* Note: should really delay this till next capture */ dev->perf.frames = 0; dev->perf.framesdropped = 0; dev->perf.bytesout = 0; } /* Read out and convert the next frame */ static int /* returns length of data or negative for error */ capture_imagereadout(struct videum_device *dev, __u8 *capture_buffer, __u8 *output_buffer, int output_size, int output_is_user) { int len = dev->capture_size; int native_len; if (dev->capture_vdf == WAVI_VDF_YCQ) len = dev->capture_wcase_size; if (dev->dma_mode == DMAMODE_SLAVE && dev->capture_vdf < WAVI_VDF_DH4) { int i, n, ram, block = 1024; __u8 *p; /* Remember that ints are disabled in block_read() */ n = (len + block - 1) / block; p = capture_buffer; ram = dev->sram_video; for (i = 0; i < n; ++i, ram += block, p += block) wavi_block_read(dev, ram, p, block); if (!wavi_vst(dev)) { debug_msg("Slave readout underrun!\n"); len = -1;/* don't copy to user */ } } if (dev->dma_mode == DMAMODE_SLAVE && dev->capture_vdf >= WAVI_VDF_DH4) { int ms = dev->capture_marker_spacing; int i, ram = dev->sram_video; u32 *p = (u32 *)capture_buffer; /* Find compressed length */ len = 0; for (i = 0; i < dev->capture_size; i += ms) { wavi_block_read(dev, ram, (__u8 *)p, ms); ram += ms; (char *)p += ms; if (p[-4] == 0xffffffff && p[-3] == 0xffffffff && p[-2] == 0xffffffff && p[-1] == 0xffffffff) { len = i + ms; break; } } if (!wavi_vst(dev)) { debug_msg("Slave readout underrun!\n"); len = -1;/* don't copy to user */ } } if (dev->dma_mode == DMAMODE_SIMPLE && dev->capture_vdf < WAVI_VDF_DH4) { if (!bm_is_done(dev)) { //debug_msg("DMA not finished\n"); /* discard frame and try again */ len = -1;/* don't copy to user */ } else { //debug_msg("DMA is done.\n"); } bm_resync(dev); } if (dev->dma_mode == DMAMODE_SIMPLE && dev->capture_vdf >= WAVI_VDF_DH4) { int i, ms = dev->capture_marker_spacing; u32 *p = (u32 *)(capture_buffer + ms); if (!bm_is_done(dev)) { //debug_msg("DMA not finished\n"); /* discard frame and try again */ len = -1;/* don't copy to user */ } /* Find compressed length */ len = 0; for (i = ms; i <= dev->capture_size; i += ms) { if (p[-4] == 0xffffffff && p[-3] == 0xffffffff && p[-2] == 0xffffffff && p[-1] == 0xffffffff) { len = i; break; } (char *)p += ms; } bm_resync(dev); } dev->sampler_enabled = 0; wavi_video_sampler(dev, 0); if (len <= 0) { dev->capture_vdf = compression_adjust(dev, dev->capture_vdf, len); return 0; } native_len = len; len = translate_image(dev, capture_buffer, output_buffer, output_size, output_is_user); dev->capture_vdf = compression_adjust(dev, dev->capture_vdf, native_len); if (len < 0) return len; ++dev->perf.frames; dev->perf.bytesout += len; return len; } /* The hardware has issued the interrupt signal, do any post-capture * processing that may be necessary. * [This function is called indirectly through the immediate task queue; * it executes at elevated IRQL, but it is interruptible. (It's a b.h.)] */ static void capture_interrupt(void *v) { struct videum_device *dev = (struct videum_device *)v; int itr; struct stream_buffer *buf; int len; /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* struct timeval timestamp_rough;*/ stamp_t timestamp_rough; unsigned long raw_frame_num; unsigned long next_raw_frame_to_keep; unsigned long stream_frame_num; u64 temp64; itr = wavi_readreg(dev, WAVI_ITR) & (WAVI_VI1BIT | WAVI_VI2BIT); if (itr == 0) return; if (dev->capture_buffer == NULL || !dev->sampler_enabled || !dev->ints_enabled) { err_msg("Interrupt handler aborted.\n"); return; } dev->capture_completed = 1; if (!dev->streaming) { dev->time_acquired = current_time_ms(); /* DMA might not have finished, but we'll check in read() */ wake_up_interruptible(&dev->new_video_frame); return; } /* Only get here in streaming mode */ if (dev->stream_last_frame == -2) { v4l2_masterclock_gettime(&dev->stream_begin); dev->stream_last_frame = -1; } if (dev->stream_capture_buffer == NULL) { return; } buf = v4l2_q_peek_head(&dev->stream_q_capture); if (buf == NULL) {/* No available buffers. Skip this frame */ dev->sampler_enabled = 0; wavi_video_sampler(dev, 0); capture_grab_frame(dev); return; } v4l2_masterclock_gettime(×tamp_rough); /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* v4l2_timeval_delta(×tamp_rough, */ /* &dev->stream_begin, ×tamp_rough); */ timestamp_rough -= dev->stream_begin; /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* raw_frame_num = v4l2_timeval_divide( */ /* ×tamp_rough, dev->videc.frame_period); */ raw_frame_num = v4l2_timestamp_divide( timestamp_rough, dev->videc.frame_period); temp64 = (u64)dev->capture.timeperframe * (dev->stream_last_frame + 1) + (dev->videc.frame_period >> 1); next_raw_frame_to_keep = v4l2_math_div6432(temp64, dev->videc.frame_period, NULL); if (raw_frame_num < next_raw_frame_to_keep) {/* Not time yet, don't keep this frame */ dev->sampler_enabled = 0; wavi_video_sampler(dev, 0); capture_grab_frame(dev); return; } len = capture_imagereadout(dev, dev->stream_capture_buffer, buf->vaddress, buf->vidbuf.length, 0); if (len <= 0) {/* Frame no good, DMA did not finish, etc. */ capture_grab_frame(dev); return; } buf->vidbuf.bytesused = len; buf->vidbuf.flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_KEYFRAME; buf->vidbuf.timestamp = timestamp_rough; /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* stream_frame_num = v4l2_timeval_correct(&buf->vidbuf.timestamp, */ /* dev->capture.timeperframe); */ stream_frame_num = v4l2_timestamp_correct(&buf->vidbuf.timestamp, dev->capture.timeperframe); //debug_msg("Stream frame %4lu T= %lu.%06lu\n", stream_frame_num, // buf->vidbuf.timestamp.tv_sec,buf->vidbuf.timestamp.tv_usec); if (stream_frame_num > dev->stream_last_frame + 1) {/* We have missed one or more frames */ dev->perf.framesdropped += stream_frame_num - dev->stream_last_frame - 1; } dev->stream_last_frame = stream_frame_num; buf = v4l2_q_del_head(&dev->stream_q_capture); v4l2_q_add_tail(&dev->stream_q_done, &buf->qnode); capture_grab_frame(dev); wake_up_interruptible(&dev->new_video_frame); } /* Read captured data into a user buffer. * Return: negative = error * 0 = keep waiting * positive = count of bytes read successfully */ static long capture_read(struct videum_device *dev, __u8 *user_buffer, int user_buffer_size) { int len = user_buffer_size; unsigned long now; if (!dev->ints_enabled) return -EIO; if (!dev->capture_completed) {/* No interrupt has occurred yet, or DMA didn't finish. */ //debug_msg("No data ready.\n"); if (!dev->sampler_enabled) capture_grab_frame(dev); return 0;/* caller should keep waiting */ } now = current_time_ms(); if (now - dev->time_acquired > MAX_FRAME_AGE) {/* Frame in buffer is stale, get a new one */ //debug_msg("Stale frame, re-acquiring.\n"); dev->sampler_enabled = 0; wavi_video_sampler(dev, 0); capture_grab_frame(dev); return 0;/* caller should keep waiting */ } len = capture_imagereadout(dev, dev->capture_buffer, user_buffer, user_buffer_size, 1); capture_grab_frame(dev); return len; } /* Stop capturing and free all resources used for capture. */ static void capture_close(struct videum_device *dev) { int i; if (dev->streaming) capture_streamoff(dev, V4L2_BUF_TYPE_CAPTURE); capture_abort(dev); dev->ready_to_capture = 0; translate_close(dev); if (dev->capture_dma_list) free_page((unsigned long)dev->capture_dma_list); dev->capture_dma_list = 0; if (dev->capture_buffer != NULL) vfree(dev->capture_buffer); dev->capture_buffer = NULL; dev->capture_buffer_size = 0; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) { dev->stream_buf[i].requested = 0; if (dev->stream_buf[i].vaddress) vfree(dev->stream_buf[i].vaddress); dev->stream_buf[i].vaddress = NULL; if (dev->stream_buf[i].dma_list) free_page((unsigned long)dev->stream_buf[i].dma_list); dev->stream_buf[i].dma_list = NULL; } } /* * * I N T E R R U P T R O U T I N E S * */ /* This function runs at interrupt time, either in response to a hardware * interrupt, or on each timer tick if there is no hardware interrupt. */ static void interrupt_handler(void *v) { struct videum_device *dev = (struct videum_device *)v; if (!dev->ints_enabled || dev->dma_state == DMA_STATE_ARMING) return; /* Call "bottom half" of handler */ dev->tqnode_dpc.routine = capture_interrupt; dev->tqnode_dpc.data = dev; queue_task(&dev->tqnode_dpc, &tq_immediate); mark_bh(IMMEDIATE_BH); if (dev->tlnode.function != NULL && dev->ints_enabled) {/* Poll again on next timer tick */ dev->tlnode.expires = jiffies + HZ/100; add_timer(&dev->tlnode); } } /* Called by the system when our hardware interrupt occurs */ static void interrupt_hw(int irq, void *v, struct pt_regs *regs) { struct videum_device *dev = (struct videum_device *)v; if (v == NULL) return; if (dev->type == VIDEUM_TYPE_PCI) { __u8 t; t = inb(BMSTATUS1(dev)); if (!(t & BMSTATUS_DMA_INT)) {/* Not us. Likely another device is sharing the IRQ */ //debug_msg("HW interrupt (not Videum)\n"); return; } outb(t, BMSTATUS1(dev)); t = inb(DMAC(dev)); t |= DMAC_HIRQREQn; outb(t, DMAC(dev)); if (dev->dma_state == DMA_STATE_ARMING) { //debug_msg("HW interrupt (arming)\n"); dev->dma_state = DMA_STATE_RUNNING; return; } } //debug_msg("HW interrupt (real)\n"); interrupt_handler(v); } static void interrupt_disable(struct videum_device *dev) { if (!dev->ints_enabled) return; dev->ints_enabled = 0; wavi_writereg(dev, WAVI_ISR, 0); if (dev->irq == 0 || dev->dma_mode == DMAMODE_SLAVE) { del_timer(&dev->tlnode); } if (dev->type == VIDEUM_TYPE_PCI) { __u8 t; t = inb(DMAC(dev)); t &= ~DMAC_HIRQEN; outb(t, DMAC(dev)); } wake_up_interruptible(&dev->new_video_frame); } static void interrupt_enable(struct videum_device *dev) { if (dev->ints_enabled) interrupt_disable(dev); dev->ints_enabled = 1; if (dev->type == VIDEUM_TYPE_PCI) { __u8 t; t = inb(DMAC(dev)); t |= DMAC_HIRQEN; t |= DMAC_HIRQREQn;/* disable interrupt request bit */ outb(t, DMAC(dev)); } wavi_readreg(dev, WAVI_ITR); wavi_writereg(dev, WAVI_ISR, WAVI_VIEBIT); dev->tlnode.function = NULL; /* NULL indicates h/w interrupts */ if (dev->irq == 0 || dev->dma_mode == DMAMODE_SLAVE) { init_timer(&dev->tlnode); dev->tlnode.function = (void(*)(unsigned long))interrupt_handler; dev->tlnode.data = (unsigned long)dev; dev->tlnode.expires = jiffies + HZ/100; add_timer(&dev->tlnode); } debug_msg("%s interrupts enabled\n", dev->tlnode.function?"Polled":"Hardware"); } /* * * M E M O R Y M A P P I N G * */ static struct stream_buffer * mmap_stream_buffer_from_offset(struct videum_device *dev, unsigned long offset) { int i; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) if (offset == dev->stream_buf[i].vidbuf.offset) return &dev->stream_buf[i]; return NULL; } static int mmap_request_buffers(struct videum_device *dev, struct v4l2_requestbuffers *req) { int i; u32 buflen; if (dev->stream_buffers_mapped) return 0;/* can't make requests if buffers are mapped */ if (req->count < 1) req->count = 1; if (req->count > MAX_CAPTURE_BUFFERS) req->count = MAX_CAPTURE_BUFFERS; req->type = V4L2_BUF_TYPE_CAPTURE; /* only kind I know */ buflen = (dev->clientfmt.fmt.pix.sizeimage + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); //debug_msg("Granting %d buffers\n",req->count); /* Now initialize the buffer structures. Don't allocate the */ /* buffers until they're mapped. */ for (i = 0; i < req->count; ++i) { dev->stream_buf[i].requested = 1; dev->stream_buf[i].vidbuf.index = i; dev->stream_buf[i].vidbuf.type = req->type; dev->stream_buf[i].vidbuf.offset = 4*i;/* anything unique */ dev->stream_buf[i].vidbuf.length = buflen; dev->stream_buf[i].vidbuf.bytesused = 0; /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* dev->stream_buf[i].vidbuf.timestamp.tv_sec = 0; */ /* dev->stream_buf[i].vidbuf.timestamp.tv_usec = 0;*/ dev->stream_buf[i].vidbuf.timestamp = 0; dev->stream_buf[i].vidbuf.flags = 0; } for (i = req->count; i < MAX_CAPTURE_BUFFERS; ++i) dev->stream_buf[i].requested = 0; return 1; } static void mmap_unrequest_buffers(struct videum_device *dev) { int i; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) dev->stream_buf[i].requested = 0; } static void mmap_vma_open(struct vm_area_struct *vma) { struct videum_device *dev = videum_device_from_file(vma->vm_file); if (dev == NULL) return; //debug_msg("vma_open called\n"); ++dev->stream_buffers_mapped; //MOD_INC_USE_COUNT; } static void mmap_vma_close(struct vm_area_struct *vma) { struct videum_device *dev = videum_device_from_file(vma->vm_file); struct stream_buffer *buf = mmap_stream_buffer_from_offset(dev, vma->vm_offset); if (dev->streaming) { info_msg("Warning- munmap() called while streaming\n"); capture_streamoff(dev, buf->vidbuf.type); } v4l2_q_yank_node(&dev->stream_q_capture, &buf->qnode); v4l2_q_yank_node(&dev->stream_q_done, &buf->qnode); if (buf->vaddress != NULL) vfree(buf->vaddress); buf->vaddress = NULL; if (buf->dma_list) free_page((unsigned long)buf->dma_list); buf->dma_list = NULL; buf->vidbuf.flags = 0; //debug_msg("Buffer %d deallocated\n",(int)vma->vm_offset); if (dev->stream_buffers_mapped > 0) --dev->stream_buffers_mapped; //MOD_DEC_USE_COUNT; } static unsigned long mmap_vma_nopage(struct vm_area_struct *vma, unsigned long address, int write) { struct videum_device *dev; struct stream_buffer *buf; unsigned long offset_into_buffer; unsigned long page; dev = videum_device_from_file(vma->vm_file); if (dev == NULL) return 0; buf = mmap_stream_buffer_from_offset(dev, vma->vm_offset); if (buf == NULL) return 0; offset_into_buffer = address - vma->vm_start; if (offset_into_buffer >= buf->vidbuf.length) return 0; page = v4l2_vmalloc_to_page(buf->vaddress + offset_into_buffer); if (page == 0) return 0; atomic_inc(&mem_map[MAP_NR(page)].count); return page; } static struct vm_operations_struct videum_vma_operations = { mmap_vma_open, mmap_vma_close, NULL, NULL, NULL, NULL, mmap_vma_nopage, }; /* * * V I D E O F O R L I N U X I N T E R F A C I N G * */ static int videum_open(struct v4l2_device *v, int flags, void **idptr) { struct videum_device *dev = (struct videum_device *)v; int i, n; int cap; for (i = 0, n = -1, cap = 0; i < MAX_OPENS; ++i) { if (!dev->open_data[i].isopen) n = i;/* available open_data structure */ else if (!dev->open_data[i].noncapturing) cap = 1;/* another open is already capturing */ } if (n == -1)/* No available open_data structures */ return -EBUSY; if (flags & O_NONCAP)/* Non-capturing open */ dev->open_data[n].noncapturing = 1; else if (cap) return -EBUSY; else { dev->open_data[n].noncapturing = 0; ++dev->capturing_opens; dev->perf.frames = 0; dev->perf.framesdropped = 0; dev->perf.bytesout = 0; } //MOD_INC_USE_COUNT; ++dev->open_count; dev->open_data[n].isopen = 1; dev->open_data[n].dev = dev; *idptr = &dev->open_data[n]; if (dev->open_count == 1) { if (dev->pci && dev->irq == 0 && !noirq) { dev->irq = dev->pci->irq; if (request_irq(dev->irq, interrupt_hw, SA_SHIRQ, dev->shortname, dev) < 0) { err_msg("Denied IRQ %d\n", dev->irq); dev->irq = 0; } } wavi_benchmark(dev); dev->ready_to_capture = 0;/* benchmark changes parameters! */ dev->capture_completed = 0; dev->sampler_enabled = 0; v4l2_q_init(&dev->stream_q_capture); v4l2_q_init(&dev->stream_q_done); } return 0; } static void videum_close(void *id) { struct device_open *o = (struct device_open *)id; struct videum_device *dev = o->dev; if (!o->noncapturing) { --dev->capturing_opens; debug_msg("Close\n"); } o->isopen = 0; --dev->open_count; if (dev->open_count == 0) { interrupt_disable(dev); capture_close(dev); if (dev->irq) free_irq(dev->irq, dev); dev->irq = 0; } //MOD_DEC_USE_COUNT; } static long videum_write(void *id, const char *buf, unsigned long count, int noblock) { return -EINVAL; } static int videum_ioctl(void *id, unsigned int cmd, void *arg) { struct device_open *o = (struct device_open *)id; struct videum_device *dev = o->dev; //debug_msg("ioctl %d\n", _IOC_NR(cmd)); switch(cmd) { case VIDIOC_QUERYCAP: { struct v4l2_capability *b = arg; strcpy(b->name, dev->v.name); b->type = V4L2_TYPE_CAPTURE; b->flags = V4L2_FLAG_READ | V4L2_FLAG_STREAMING | V4L2_FLAG_SELECT; b->inputs = dev->videc.num_inputs; b->outputs = 0; b->audios = 0; b->maxwidth = MAX_WIDTH; b->maxheight = MAX_HEIGHT; b->minwidth = MIN_WIDTH; b->minheight = MIN_HEIGHT; b->maxframerate = 30; return 0; } case VIDIOC_ENUM_CAPFMT: { struct v4l2_fmtdesc *f = arg; if (f->index < 0 || f->index >= NUM_CAPFMT) return -EINVAL; *f = capfmt[f->index]; return 0; } case VIDIOC_G_FMT: { memcpy(arg, &dev->clientfmt, sizeof(dev->clientfmt)); return 0; } case VIDIOC_S_FMT: { struct v4l2_format *fmt = arg; if (o->noncapturing || dev->stream_buffers_mapped) return -EPERM; dev->clientfmt = *fmt; capture_new_format(dev); mmap_unrequest_buffers(dev); *fmt = dev->clientfmt; return 0; } case VIDIOC_G_COMP: return -EINVAL; case VIDIOC_S_COMP: return -EINVAL; case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; if (o->noncapturing || dev->stream_buffers_mapped) return -EPERM; capture_begin(dev); if (!mmap_request_buffers(dev, req)) return -EINVAL; return 0; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *buf = arg; int i; if (o->noncapturing) return -EPERM; i = buf->index; if (i < 0 || i >= MAX_CAPTURE_BUFFERS || !dev->stream_buf[i].requested || (buf->type & V4L2_BUF_TYPE_field) != (dev->stream_buf[i].vidbuf.type & V4L2_BUF_TYPE_field)) return -EINVAL; *buf = dev->stream_buf[i].vidbuf; return 0; } case VIDIOC_QBUF: { struct v4l2_buffer *buf = arg; if (o->noncapturing) return -EPERM; if (!dev->stream_buffers_mapped) return -EINVAL; if (!capture_queuebuffer(dev, buf)) return -EINVAL; return 0; } case VIDIOC_DQBUF: { struct v4l2_buffer *buf = arg; if (o->noncapturing) return -EPERM; if (!capture_dequeuebuffer(dev, buf)) return -EINVAL; return 0; } case VIDIOC_STREAMON: { __u32 type = (__u32)arg; if (o->noncapturing) return -EPERM; if (!capture_streamon(dev, type)) return -EINVAL; return 0; } case VIDIOC_STREAMOFF: { __u32 type = (__u32)arg; if (o->noncapturing) return -EPERM; capture_streamoff(dev, type); return 0; } case VIDIOC_ENUM_FBUFFMT: return -EINVAL; case VIDIOC_G_FBUF: return -EINVAL; case VIDIOC_S_FBUF: return -EINVAL; case VIDIOC_G_WIN: return -EINVAL; case VIDIOC_S_WIN: return -EINVAL; case VIDIOC_PREVIEW: return -EINVAL; case VIDIOC_G_PERF: { memcpy(arg, &dev->perf, sizeof(dev->perf)); return 0; } case VIDIOC_G_INPUT: { memcpy(arg, &dev->input, sizeof(dev->input)); return 0; } case VIDIOC_S_INPUT: { int input = (int)arg; if (input < 0 || input >= dev->videc.num_inputs) { debug_msg("Input out of range %d\n", input); return -EINVAL; } if (input != dev->input) { dev->input = input; set_video_input(dev, input); /* Changing the input disrupts the capture, so */ /* kick it off again */ if (dev->sampler_enabled) { capture_abort(dev); capture_grab_frame(dev); } } return 0; } case VIDIOC_G_PARM: { memcpy(arg, &dev->capture, sizeof(dev->capture)); return 0; } case VIDIOC_S_PARM: { struct v4l2_captureparm *vp = arg; if (vp->capturemode & ~dev->capture.capability) return -EINVAL; if (vp->timeperframe < 10000) return -EINVAL; if (vp->capturemode != dev->capture.capturemode && !o->noncapturing && dev->streaming) return -EINVAL; if (o->noncapturing) return 0; if (vp->capturemode != dev->capture.capturemode) { dev->capture.capturemode = vp->capturemode; capture_new_format(dev); } if ((vp->capturemode & V4L2_CAP_TIMEPERFRAME) && vp->timeperframe >= dev->videc.frame_period) dev->capture.timeperframe = vp->timeperframe; else dev->capture.timeperframe = dev->videc.frame_period; return 0; } case VIDIOC_G_STD: { struct v4l2_standard *std = arg; v4l2_video_std_construct(std, dev->videc.standard, 0); return 0; } case VIDIOC_S_STD: { struct v4l2_standard *std = arg; int id; if ((o->noncapturing && dev->capturing_opens) || dev->stream_buffers_mapped) return -EPERM; id = v4l2_video_std_confirm(std); if (!((1 << id) & dev->videc.standards)) { debug_msg("Bad standard: %u\n", (unsigned)id); return -EINVAL; } dev->videc.set_standard(dev, id); return 0; } case VIDIOC_ENUMSTD: { struct v4l2_enumstd *estd = arg; __u32 b, i; if (estd->index < 0 || estd->index > 30) return -EINVAL; for (b = 1, i = 0; b < 32; ++b) { if (((1 << b) & dev->videc.standards) == 0) continue; if (i == estd->index) { v4l2_video_std_construct(&estd->std, b, 0); estd->inputs = (__u32)-1; /* all inputs */ estd->outputs = 0; return 0; } ++i; } return -EINVAL; } case VIDIOC_ENUMINPUT: { struct v4l2_input *vi = arg; if (vi->index < 0 || vi->index >= dev->videc.num_inputs) return -EINVAL; *vi = dev->source[vi->index].input; return 0; } case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *qc = arg; int i; i = find_vctrl(qc->id); if (i == -EINVAL) { qc->flags = V4L2_CTRL_FLAG_DISABLED; return 0; } if (i < 0) return -EINVAL; videum_control[i].category = qc->category; /* Bug found!! */ /* Fixed by Doe-Wan Kim */ /* Date: 5/01/2001 */ /* memcpy(videum_control[i].catname, qc->catname, */ /* sizeof(qc->catname)); ` */ memcpy(videum_control[i].group, qc->group, sizeof(qc->group)); *qc = videum_control[i]; return 0; } case VIDIOC_G_CTRL: { struct v4l2_control *vc = arg; int i; i = find_vctrl(vc->id); if (i < 0) return -EINVAL; vc->value = dev->source[dev->input].control[i]; return 0; } case VIDIOC_S_CTRL: { struct v4l2_control *vc = arg; int i; i = find_vctrl(vc->id); if (i < 0) return -EINVAL; dev->source[dev->input].control[i] = vc->value; wavi_tone_controls(dev); return 0; } case VIDIOC_G_TUNER: return -EINVAL; case VIDIOC_S_TUNER: return -EINVAL; case VIDIOC_G_FREQ: return -EINVAL; case VIDIOC_S_FREQ: return -EINVAL; case VIDIOC_G_AUDIO: return -EINVAL; case VIDIOC_S_AUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } return 0; } static int videum_mmap(void *id, struct vm_area_struct *vma) { struct device_open *o = (struct device_open *)id; struct videum_device *dev = o->dev; struct stream_buffer *buf; if (o->noncapturing) return -ENODEV; buf = mmap_stream_buffer_from_offset(dev, vma->vm_offset); if (buf == NULL) return -EINVAL;/* no such buffer */ if (!buf->requested || (buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED)) return -EINVAL;/* not requested or already mapped */ if (buf->vidbuf.length != vma->vm_end - vma->vm_start) return -EINVAL;/* wrong length */ if (buf->vaddress != NULL) vfree(buf->vaddress); buf->vaddress = vmalloc(buf->vidbuf.length); if (buf->vaddress == NULL) { err_msg("Couldn't allocate mmap() buffer\n"); return -ENODEV; } if (dev->dma_mode == DMAMODE_SIMPLE && !bm_build_scatter_list(dev, buf->vaddress, &buf->dma_list)) return -ENODEV; buf->vidbuf.flags |= V4L2_BUF_FLAG_MAPPED; vma->vm_ops = &videum_vma_operations; if (vma->vm_ops->open) vma->vm_ops->open(vma); /* Note: vma->vm_file will be set up by V4L2 */ return 0; } static int videum_poll(void *id, struct file *file, poll_table *table) { struct device_open *o = (struct device_open *)id; struct videum_device *dev = o->dev; if (o->noncapturing) return POLLERR; if (dev->streaming) { void *node; node = v4l2_q_peek_head(&dev->stream_q_done); if (node != NULL) return (POLLIN | POLLRDNORM);/* data is ready now */ node = v4l2_q_peek_head(&dev->stream_q_capture); if (node == NULL) return POLLERR; /* no frames queued */ poll_wait(file, &dev->new_video_frame, table); return 0; } /* Capture is through read() call */ if (dev->capture_completed)/* data is ready now */ return (POLLIN | POLLRDNORM); capture_grab_frame(dev);/* does nothing if capture is in progress */ if (!dev->ready_to_capture)/* Can't grab frames! */ return POLLERR; poll_wait(file, &dev->new_video_frame, table); return 0; } static long videum_read(void *id, char *buf, unsigned long count, int noblock) { struct device_open *o = (struct device_open *)id; struct videum_device *dev = o->dev; long len = 0; int retries = 2; long my_timeout; int res1, res2; if (o->noncapturing || dev->streaming) return -EPERM; capture_grab_frame(dev);/* does nothing if capture is in progress */ if (!dev->ready_to_capture) { debug_msg("Can't grab frames!\n"); return 0; } my_timeout = HZ / 4; /*#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127)) current->timeout = jiffies + my_timeout; #endif*/ while (len == 0) { if (noblock) { if (!dev->capture_completed) return -EAGAIN; } else { /* watch out for race condition going to sleep! */ cli(); if (!dev->capture_completed) { /*#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127) interruptible_sleep_on(&dev->new_video_frame); #else*/ my_timeout = interruptible_sleep_on_timeout( &dev->new_video_frame, my_timeout); /*#endif*/ } sti(); } /*#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127) if (current->timeout <= jiffies) #else*/ if (my_timeout == 0) /*#endif*/ {/* Timeout! */ --retries; if (retries == 0) {/* Out of patience... */ res1 = wavi_readreg(dev, WAVI_ISR); res2 = wavi_readreg(dev, WAVI_CTL); debug_msg("Timeout on read. " "ISR=%04X CTL=%04X\n", res1, res2); break; } /* Reset and restart */ capture_abort(dev); capture_grab_frame(dev); continue; } len = capture_read(dev, buf, count); } /*#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,127) current->timeout = 0; #endif*/ //debug_msg("read %d\n", (int)len); return len; } /* * Remaining initialization of video decoder etc. This is only * done when the device is successfully identified and registered. */ static int videum_init_done(struct v4l2_device *v) { struct videum_device *dev = (struct videum_device *)v; int t; int i; /* Initialize video input array */ for (i = 0; i < VSOURCE_COUNT; ++i) { dev->source[i].input.index = i; dev->source[i].input.type = V4L2_INPUT_TYPE_CAMERA; dev->source[i].input.capability = 0; /* Initialize video control properties */ dev->source[i].control[VCTRL_BRIGHTNESS] = videum_control[VCTRL_BRIGHTNESS].default_value; dev->source[i].control[VCTRL_CONTRAST] = videum_control[VCTRL_CONTRAST].default_value; dev->source[i].control[VCTRL_SATURATION] = videum_control[VCTRL_SATURATION].default_value; dev->source[i].control[VCTRL_HUE] = videum_control[VCTRL_HUE].default_value; } strcpy(dev->source[VSOURCE_MXC].input.name, "MXC"); strcpy(dev->source[VSOURCE_COMP].input.name, "VID (Composite)"); strcpy(dev->source[VSOURCE_SVIDEO].input.name, "SVHS (S-Video)"); strcpy(dev->source[VSOURCE_TUNER].input.name, "Tuner"); dev->source[VSOURCE_TUNER].input.type = V4L2_INPUT_TYPE_TUNER; /* Initialize the video decoder hardware */ dev->videc.initialize(dev); t = wavi_readreg(dev, WAVI_MMA); if (dev->eeprom.dwHwFlags & HWFLAGS_INV_VSYNC) t |= WAVI_SAA7110; else t &= ~WAVI_SAA7110; wavi_writereg(dev, WAVI_MMA, t); /* BUG: get defaults from user somehow... */ dev->videc.set_standard(dev, V4L2_STD_NTSC); dev->videc.set_vcrmode(dev, 0); set_video_input(dev, VSOURCE_MXC); /* Capture parameters */ dev->capture.capability = V4L2_CAP_TIMEPERFRAME; dev->capture.capturemode = 0; dev->capture.extendedmode = 0; dev->capture.timeperframe = dev->videc.frame_period; /* Default capture dimensions */ dev->clientfmt.fmt.pix.width = 160; dev->clientfmt.fmt.pix.height = 120; dev->clientfmt.fmt.pix.depth = 16; dev->clientfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565; dev->clientfmt.fmt.pix.flags = 0; dev->clientfmt.fmt.pix.bytesperline = 0; dev->clientfmt.fmt.pix.sizeimage = 0; capture_new_format(dev); return 0; } /* ===================================================================== * The functions below this point are only called during loading * and unloading of the driver. */ /* * D E V I C E I N I A L I Z A T I O N R O U T I N E S * * These routines locate and enable the Videum, and initialize * the device structure. After enabling, the WAVI register access * functions and I2C functions will work. */ /* Initialize v4l2_device fields */ static int init_device_fields(struct videum_device *dev) { int num = 0; // sprintf(dev->v.name, "Winnov Videum (%d)", num); sprintf(dev->v.name, "%s %s (%d)", dev->eeprom.szOemName, dev->eeprom.szProduct, num); dev->v.type = V4L2_TYPE_CAPTURE; dev->v.minor = unit_video[num]; dev->v.open = videum_open; dev->v.close = videum_close; dev->v.read = videum_read; dev->v.write = videum_write; dev->v.ioctl = videum_ioctl; dev->v.mmap = videum_mmap; dev->v.poll = videum_poll; dev->v.initialize = videum_init_done; dev->v.priv = NULL; return 1;/* OK */ } static int config_pcmcia_videum(struct videum_device *dev, int idev) { dev->type = VIDEUM_TYPE_PCMCIA; dev->sram_base = 0x00000; dev->sram_size = 0x10000; dev->irq = 0;/* We will use the jiffy timer interrupt to poll */ dev->index = idev; return 1; } static void unconfig_pcmcia_videum(struct videum_device *dev) { } static void unconfig_any_videum(struct videum_device *dev) { interrupt_disable(dev); capture_close(dev); switch (dev->type) { case VIDEUM_TYPE_PCMCIA: unconfig_pcmcia_videum(dev); break; } if (dev->is_registered) { debug_msg("unconfig_any_videum: Calling v4l2_unregister_device.\n" ); v4l2_unregister_device((struct v4l2_device *)dev); info_msg("Removed device %s\n", dev->shortname); } memset(dev, 0, sizeof(videum[0])); } static int config_any_videum(struct videum_device *dev) { static int pcmcia = 0; sprintf(dev->shortname, "videum%d", pcmcia); if (!config_pcmcia_videum(dev, pcmcia++)) return 0; if (!eeprom_load(dev)) { err_msg("What?!?!\n" ); err_msg("EEPROM read failure\n"); /* unconfig_any_videum(dev); return 0;*/ } /* The WAVI registers are now accessible! */ wavi_initialize(dev); /* SRAM memory map */ if (dev->eeprom.bBoardType == TYPE_VIDEUM) dev->sram_base = 0x10000;/* correction for early Videums */ dev->sram_audio = dev->sram_base; dev->sram_audio_size = 0; dev->sram_ucode = dev->sram_audio + dev->sram_audio_size; dev->sram_ucode_size = UC_SIZE; dev->sram_linebuf = dev->sram_ucode + dev->sram_ucode_size; dev->sram_linebuf_size = 0x00800; dev->sram_video = dev->sram_linebuf + dev->sram_linebuf_size; dev->sram_video_size = dev->sram_base + dev->sram_size - dev->sram_video; if (!init_device_fields(dev)) {/* unconfig_any_videum(dev);*/ err_msg("What!\n"); return 0; } if (!find_decoder(dev)) { err_msg("Bad or unrecognized video decoder\n"); /* unconfig_any_videum(dev); return 0;*/ } return 1; } /*====================================================================== A dummy PCMCIA client driver This is provided as an example of how to write an IO card client. As written, it will function as a sort of generic point enabler, configuring any card as that card's CIS specifies. dummy_cs.c 1.10 1999/02/13 06:47:20 The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The initial developer of the original code is David A. Hinds . Portions created by David A. Hinds are Copyright (C) 1998 David A. Hinds. All Rights Reserved. ======================================================================*/ /* All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If you do not define PCMCIA_DEBUG at all, all the debug code will be left out. If you compile with PCMCIA_DEBUG=0, the debug code will be present but disabled -- but it can then be enabled for specific modules at load time with a 'pc_debug=#' option to insmod. */ #if 0 #ifdef PCMCIA_DEBUG static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); #else #define DEBUG(n, args...) #endif #else #define DEBUG(n, args...) printk(KERN_DEBUG args); static char *version = "wnv_cs.c 0.1.0 1999/03/24 (Chris Lahey)"; #endif /*====================================================================*/ /* Parameters that can be set with 'insmod' */ /* The old way: bit map of interrupts to choose from */ /* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ static u_int irq_mask = 0xdeb8; /* Newer, simpler way of listing specific interrupts */ static int irq_list[4] = { -1 }; MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); /*====================================================================*/ /* The event() function is this driver's Card Services event handler. It will be called by Card Services when an appropriate card status event is received. The config() and release() entry points are used to configure or release a socket, in response to card insertion and ejection events. They are invoked from the dummy event handler. */ static void wnv_config(dev_link_t *link); static void wnv_release(u_long arg); static int wnv_event(event_t event, int priority, event_callback_args_t *args); /* The attach() and detach() entry points are used to create and destroy "instances" of the driver, where each instance represents everything needed to manage one actual PCMCIA card. */ static dev_link_t *wnv_attach(void); static void wnv_detach(dev_link_t *); /* You'll also need to prototype all the functions that will actually be used to talk to your device. See 'pcmem_cs' for a good example of a fully self-sufficient driver; the other drivers rely more or less on other parts of the kernel. */ /* A linked list of "instances" of the dummy device. Each actual PCMCIA card corresponds to one device instance, and is described by one dev_link_t structure (defined in ds.h). You may not want to use a linked list for this -- for example, the memory card driver uses an array of dev_link_t pointers, where minor device numbers are used to derive the corresponding array index. */ static dev_link_t *dev_list = NULL; /* A dev_link_t structure has fields for most things that are needed to keep track of a socket, but there will usually be some device specific information that also needs to be kept track of. The 'priv' pointer in a dev_link_t structure can be used to point to a device-specific private data structure, like this. A driver needs to provide a dev_node_t structure for each device on a card. In some cases, there is only one device per card (for example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept in a linked list starting at the 'dev' field of a dev_link_t structure. We allocate them in the card's private data structure, because they generally shouldn't be allocated dynamically. In this case, we also provide a flag to indicate if a device is "stopped" due to a power management event, or card ejection. The device IO routines can use a flag like this to throttle IO to a card that is not ready to accept it. */ /*====================================================================*/ static void cs_error(client_handle_t handle, int func, int ret) { error_info_t err = { func, ret }; CardServices(ReportError, handle, &err); } /*====================================================================== dummy_attach() creates an "instance" of the driver, allocating local data structures for one device. The device is registered with Card Services. The dev_link structure is initialized, but we don't actually configure the card at this point -- we wait until we receive a card insertion event. ======================================================================*/ static dev_link_t *wnv_attach(void) { client_reg_t client_reg; dev_link_t *link; local_info_t *local; int ret, i; DEBUG(2,"wnv_attach()\n"); /* Initialize the dev_link_t structure */ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); memset(link, 0, sizeof(struct dev_link_t)); link->release.function = &wnv_release; link->release.data = (u_long)link; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; if (irq_list[0] == -1) link->irq.IRQInfo2 = irq_mask; else for (i = 0; i < 4; i++) link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = NULL; /* General socket configuration defaults can go here. In this client, we assume very little, and rely on the CIS for almost everything. In most clients, many details (i.e., number, sizes, and attributes of IO windows) are fixed by the nature of the device, and can be hard-wired here. */ link->conf.Attributes = 0; link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; /* Allocate space for private device-specific data */ local = kmalloc(sizeof(local_info_t), GFP_KERNEL); memset(local, 0, sizeof(local_info_t)); link->priv = local; /* Register with Card Services */ link->next = dev_list; dev_list = link; client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &wnv_event; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; DEBUG(2,"wnv_attach: Calling RegisterClient\n" ); ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != 0) { DEBUG(0,"wnv_attach: Registration error\n" ); cs_error(link->handle, RegisterClient, ret); DEBUG(2,"wnv_attach: Calling wnv_detach(%p).\n", link); wnv_detach(link); return NULL; } DEBUG(2,"Returning link=%p from wnv_attach.\n", link); return link; } /* wnv_attach */ /*====================================================================== This deletes a driver "instance". The device is de-registered with Card Services. If it has been released, all local data structures are freed. Otherwise, the structures will be freed when the device is released. ======================================================================*/ static void wnv_detach(dev_link_t *link) { dev_link_t **linkp; DEBUG(2,"wnv_detach(0x%p)\n", link); /* Locate device structure */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; if (*linkp == NULL) return; /* If the device is currently configured and active, we won't actually delete it yet. Instead, it is marked so that when the release() function is called, that will trigger a proper detach(). */ if (link->state & DEV_CONFIG) { DEBUG(1, "wnv_cs: detach postponed, '%s' " "still locked\n", link->dev->dev_name); link->state |= DEV_STALE_LINK; return; } /* Break the link with Card Services */ if (link->handle) DEBUG(1, "wnv_detach: DeregisterClient=%d\n", CardServices(DeregisterClient, link->handle) ); /* Unlink device structure, free pieces */ *linkp = link->next; if (link->priv) { kfree_s(link->priv, sizeof(local_info_t)); } kfree_s(link, sizeof(struct dev_link_t)); } /* wnv_detach */ /*====================================================================== wnv_config() is scheduled to run after a CARD_INSERTION event is received, to configure the PCMCIA socket, and to make the device available to the system. ======================================================================*/ #define CS_CHECK(fn, args...) \ while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed #define CFG_CHECK(fn, args...) \ if (CardServices(fn, args) != 0) goto next_entry static void wnv_config(dev_link_t *link) { client_handle_t handle; local_info_t *dev; win_req_t req; memreq_t map; int variable; int result; videum_device *videum; handle = link->handle; dev = link->priv; printk("wnv_config(0x%p)\n", link); printk( KERN_DEBUG "wnv_config: Memory Mapped\n" ); dev->device = kmalloc( sizeof( struct videum_device ), GFP_KERNEL ); memset( dev->device, 0, sizeof( struct videum_device ) ); dev->device->link = link; videum = dev->device; req.Attributes = WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; req.Base = 0; req.Size = 0x2000; req.AccessSpeed = 0; link->win = (window_handle_t)link->handle; result = CardServices(RequestWindow, &link->win, &req); printk("wnv_config: RequestWindow=%d\n", result); map.Page = 0; map.CardOffset = 0; result = CardServices(MapMemPage, link->win, &map); printk("wnv_config: MapMemPage=%d\n", result); videum->mem_start = ioremap(req.Base, 0x2000); videum->data = (__u16 *)(((long)videum->mem_start) + 0x1000); if ( mp_cable_attached( videum ) ) printk("wnv_config: Cable attached.\n" ); else printk("wnv_config: Cable not attached.\n"); if ( mp_cable_attached( videum ) ) { variable = 0; mp_power_on( videum ); mp_reset_camera( videum ); mp_light_on( videum ); } config_any_videum( videum ); if (v4l2_register_device(&(videum->v)) != 0) { printk("Ah!\n" ); } else videum->is_registered = 1; link->state &= ~DEV_CONFIG_PENDING; /* Configure card */ link->state |= DEV_CONFIG; /* At this point, the dev_node_t structure(s) need to be initialized and arranged in a linked list at link->dev. */ sprintf(dev->node.dev_name, "video0"); dev->node.major = 81; dev->node.minor = 0; link->dev = &dev->node; return; } /* wnv_config */ /*====================================================================== After a card is removed, wnv_release() will unregister the device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed. ======================================================================*/ static void wnv_release(u_long arg) { dev_link_t *link = (dev_link_t *)arg; local_info_t *dev = link->priv; int result; printk("wnv_release(0x%p)\n", link); /* If the device is currently in use, we won't release until it is actually closed, because until then, we can't be sure that no one will try to access the device or its data structures. */ if (link->open) { printk("wnv_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } /* Unlink the device chain */ link->dev = NULL; /* In a normal driver, additional code may be needed to release other kernel data structures associated with this device. */ mp_power_off( dev->device ); printk("Calling unconfig_any_videum\n"); unconfig_any_videum( ((local_info_t *)link->priv)->device ); /* Don't bother checking to see if these succeed or not */ if (link->win) { iounmap( ((local_info_t *)link->priv)->device->mem_start ); result = CardServices(ReleaseWindow, link->win); printk("wnv_release: ReleaseWindow=%d.\n", result); } if (link->io.NumPorts1) CardServices(ReleaseIO, link->handle, &link->io); if (link->irq.AssignedIRQ) CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~DEV_CONFIG; if (link->state & DEV_STALE_LINK) { printk("wnv_release: Calling wnv_detach(%p).\n", link); wnv_detach(link); } } /* wnv_release */ /*====================================================================== The card status event handler. Mostly, this schedules other stuff to run after an event is received. When a CARD_REMOVAL event is received, we immediately set a private flag to block future accesses to this device. All the functions that actually access the device should check this flag to make sure the card is still present. ======================================================================*/ static int wnv_event(event_t event, int priority, event_callback_args_t *args) { dev_link_t *link = args->client_data; printk("wnv_event(0x%06x, 0x%p)\n", event, link); switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { ((local_info_t *)link->priv)->stop = 1; link->release.expires = RUN_AT(HZ/20); add_timer(&link->release); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; wnv_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: /* Mark the device as stopped, to block IO until later */ ((local_info_t *)link->priv)->stop = 1; if (link->state & DEV_CONFIG) CardServices(ReleaseConfiguration, link->handle); break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) CardServices(RequestConfiguration, link->handle, &link->conf); ((local_info_t *)link->priv)->stop = 0; /* In a normal driver, additional code may go here to restore the device state and restart IO. */ break; } return 0; } /* wnv_event */ /*====================================================================*/ int init_module(void) { servinfo_t serv; printk("%s\n", version); CardServices(GetCardServicesInfo, &serv); printk("init_module: Got Server\n"); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "wnv_cs: Card Services release " "does not match!\n"); return -1; } printk("init_module: Server Matches\n"); register_pcmcia_driver(&dev_info, &wnv_attach, &wnv_detach); printk("init_module: Driver Registered\n"); return 0; } void cleanup_module(void) { printk("wnv_cs: unloading\n"); unregister_pcmcia_driver(&dev_info); while (dev_list != NULL) { if (dev_list->state & DEV_CONFIG) wnv_release((u_long)dev_list); printk("cleanup_module: Calling wnv_detach(%p).\n", dev_list); wnv_detach(dev_list); } }