diff -Naur linux-2.3.50-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt --- linux-2.3.50-orig/Documentation/usb/ov511.txt Sat Feb 19 01:19:14 2000 +++ linux/Documentation/usb/ov511.txt Sat Mar 11 02:30:25 2000 @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------- Author: Mark McClelland -Homepage: http://people.delphi.com/mmcclelland/linux/ +Homepage: http://alpha.dyndns.org/ov511 INTRODUCTION: @@ -11,25 +11,40 @@ grab a frame in color (YUV420) at 640x480 or 320x240 using either vidcat or xawtv. Other utilities may work but have not yet been tested. +NEW IN THIS VERSION: + o Preliminary snapshot support + o Experimental red-blue misalignment fixes + o Better YUV420 color conversion + o Module options + o Finer-grained debug message control + o Support for new cameras (4, 36) + SUPPORTED CAMERAS: -________________________________________________________ -Manufacturer | Model | Custom ID | Status ------------------+----------------+-----------+--------- -MediaForte | MV300 | 0 | Working -D-Link | DSB-C300 | 3 | Working -Puretek | PT-6007 | 5 | Untested -Creative Labs | WebCam 3 | 21 | Working -Lifeview | RoboCam | 100 | Untested -AverMedia | InterCam Elite | 102 | Working -MediaForte | MV300 | 112 | Working --------------------------------------------------------- +_________________________________________________________ +Manufacturer | Model | Custom ID | Status +-----------------+-----------------+-----------+--------- +MediaForte | MV300 | 0 | Working +Aiptek | HyperVCam ? | 0 | Working +NetView | NV300M | 0 | Working +D-Link | DSB-C300 | 3 | Working +Hawking Tech. | ??? | 3 | Working +??? | Generic | 4 | Untested +Puretek | PT-6007 | 5 | Working +Creative Labs | WebCam 3 | 21 | Working +??? | Koala-Cam | 36 | Untested +Lifeview | RoboCam | 100 | Untested +AverMedia | InterCam Elite | 102 | Working +MediaForte | MV300 | 112 | Working +Omnivision | OV7110 EV board | 112 | Working* +--------------------------------------------------------- +(*) uses OV7110 (monochrome) Any camera using the OV511 and the OV7610 CCD should work with this driver. The driver only detects known cameras though, based on their custom id number. If you have a currently unsupported camera, the ID number should be reported to you -in the kernel logs. If you have an unsupported camera, please send me the model, -manufacturer and ID number and I will add it to the detection code. In the -meantime, you can add to the code yourself in the function ov511_probe() +in the kernel logs. Please send me the model, manufacturer and ID number and I +will add it to the detection code. In the meantime, you can add to the code +yourself in the function ov511_probe(). WHAT YOU NEED: @@ -44,7 +59,7 @@ You must have first compiled USB support, support for your specific USB host controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend -making them modules.) +making them modules.) Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX): @@ -56,6 +71,14 @@ If it is not already there (it usually is), create the video device: mknod /dev/video c 81 0 + +Sometimes /dev/video is a symlink to /dev/video0 + +You will have to set permissions on this device to allow you to read/write +from it: + + chmod 666 /dev/video + chmod 666 /dev/video0 (if necessary) Now you are ready to run a video app! Both vidcat and xawtv work well for me at 640x480. @@ -83,20 +106,104 @@ you get a scrambled image it is likely that you made a mistake in Xawtv.ad. Try setting the size to 320x240 if all else fails. +FAQ: +Q: "Why does the picture have noise and look grainy" +A: This is a problem at low light levels, and may be also due to subtle bugs in + the code. The cause is most likely the OV7610 settings we are currently + using. I am looking into this problem. + +Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?" +A: The I2C code that allows the OV511 to communicate with the camera chip is a + bit flaky right now. This message means that the I2C bus never got + initialized properly, and the camera will most likely not work even if you + disable this warning. Try unloading/reloading the driver or unplugging/re- + plugging the camera if this happens. + +Q: "Why do you bother with this phony camera detection crap? It doesn't do + anything useful!" +A: The main purpose of only supporting known camera models is to force people + with new camera models to tell me about them, so I can assemble the list + above, and so the code can know what CCD chip you have. Right now, nearly all + of the cameras use the OV7610 and consequently I have not put support for + other ones in, so the value of the detection code is questionable. Eventually + though, new CCDs might appear and we will be fortunate to have the detection. + +MODULE PARAMETERS: + + You can set these with: insmod ov511 NAME=VALUE + There is currently no way to set these on a per-camera basis. + + NAME: autoadjust + TYPE: integer (boolean) + DEFAULT: 1 + DESC: The camera normally adjusts exposure, gain, and hue automatically. This + can be set to 0 to disable this automatic adjustment. Note that there is + currently no way to set these parameters manually once autoadjust is + disabled. (This feature is not working yet) + + NAME: debug + TYPE: integer (0-6) + DEFAULT: 3 + DESC: Sets the threshold for printing debug messages. The higher the value, + the more is printed. The levels are cumulative, and are as follows: + 0=no debug messages + 1=init/detection/unload and other significant messages + 2=some warning messages + 3=config/control function calls + 4=most function calls and data parsing messages + 5=highly repetitive mesgs + + NAME: fix_rgb_offset + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Some people have reported that the blue component of the image is one + or so lines higher than the red component. This is only apparent in + images with white objects on black backgrounds at 640x480. Setting this + to 1 will realign the color planes correctly. NOTE: This is still + experimental and very buggy. + + NAME: snapshot + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot + button is pressed. Note that this does not yet work with most apps, + including xawtv and vidcat. NOTE: See the section "TODO" for more info. + WORKING FEATURES: o Color streaming/capture at 640x480 and 320x240 o YUV420 color + o Monochrome o Setting/getting of saturation, contrast and brightness (no color yet) -WHAT NEEDS TO BE DONE: - -The rest of the work will involve implementing support for all the different -resolutions, color depths, etc. Also, while support for the OV511's proprietary -lossy compression is apparently not necessary (the code currently disables it,) -it would be a nice addition as it improves performance quite a bit. OmniVision -wouldn't tell me how the algorithm works, so we can't really work on that yet. -Please kindly inform OmniVision that you would like them to release their -specifications to the Linux community. +EXPERIMENTAL FEATURES: + o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and + corrupted frames. + o Snapshot mode (only works with some read() based apps; see below for more) + +TODO: + o Fix the noise / grainy image problem. + o Get compression working. It would be a nice addition as it improves + frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works, + so we can't really work on that yet. Please kindly inform OmniVision that you + would like them to release their specifications to the Linux community. + o Get 160x120 working + o YUV422 (and other color modes) + o Fix read(). It only works right now if you run an mmap() based app like xawtv + or vidcat after loading the module and before using read(). Apparently there + are some initialization issues. + o Get snapshot mode working with mmap(). + o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. + o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture() + and ov511_set_picture()) + o Get autoadjust disable working + o Devise some clean way to support different types of CCDs (based on Custom ID) + o OV511A support + o V4L2 support (Probably not until it goes into the kernel) + o Fix I2C initialization. Some people are reporting problems with reading the + 7610 registers. This could be due to timing differences, an excessive I2C + clock rate, or a problem with ov511_i2c_read(). + o Get rid of the memory management functions (put them in videodev.c??) + o Use initcalls HOW TO CONTACT ME: @@ -108,4 +215,5 @@ The code is based in no small part on the CPiA driver by Johannes Erdfelt, Randy Dunlap, and others. Big thanks to them for their pioneering work on that and the USB stack. Thanks to Bret Wallach for getting camera reg IO , ISOC, and -image capture working. +image capture working. Thanks to Orion Sky Lawlor and Kevin Moore for their +work as well. diff -Naur linux-2.3.50-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.3.50-orig/drivers/usb/ov511.c Sat Feb 19 01:19:48 2000 +++ linux/drivers/usb/ov511.c Sat Mar 11 02:30:05 2000 @@ -2,20 +2,17 @@ * OmniVision OV511 Camera-to-USB Bridge Driver * Copyright (c) 1999/2000 Mark W. McClelland * Many improvements by Bret Wallach - * + * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000 + * Snapshot code by Kevin Moore + * * Based on the Linux CPiA driver. * * Released under GPL v.2 license. * - * Important keywords in comments: - * CAMERA SPECIFIC - Camera specific code; may not work with other cameras. - * DEBUG - Debugging code. - * FIXME - Something that is broken or needs improvement. - * - * Version: 1.07 + * Version: 1.08 * * Please see the file: linux/Documentation/usb/ov511.txt - * and the website at: http://people.delphi.com/mmcclelland/linux/ + * and the website at: http://alpha.dyndns.org/ov511 * for more info. */ @@ -67,8 +64,6 @@ #define OV511_I2C_RETRIES 3 -#define OV7610_AUTO_ADJUST 1 - /* Video Size 640 x 480 x 3 bytes for RGB */ #define MAX_FRAME_SIZE (640 * 480 * 3) #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) @@ -77,6 +72,33 @@ #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 +// PARAMETER VARIABLES: +static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ + +/* 0=no debug messages + * 1=init/detection/unload and other significant messages, + * 2=some warning messages + * 3=config/control function calls + * 4=most function calls and data parsing messages + * 5=highly repetitive mesgs + * NOTE: This should be changed to 0, 1, or 2 for production kernels + */ +static int debug = 3; + +/* Fix vertical misalignment of red and blue at 640x480 */ +static int fix_rgb_offset = 0; + +/* Snapshot mode enabled flag */ +static int snapshot = 0; + +MODULE_PARM(autoadjust, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(fix_rgb_offset, "i"); +MODULE_PARM(snapshot, "i"); + +MODULE_AUTHOR("Mark McClelland (and others)"); +MODULE_DESCRIPTION("OV511 USB Camera Driver"); + char kernel_version[] = UTS_RELEASE; /*******************************/ @@ -206,10 +228,8 @@ USB_TYPE_CLASS | USB_RECIP_DEVICE, 0, (__u16)reg, &value, 1, HZ); -#if 0 - PDEBUG("reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); -#endif - + PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); + return rc; } @@ -225,9 +245,7 @@ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE, 0, (__u16)reg, buffer, 1, HZ); -#if 0 - PDEBUG("reg read: 0x%02X:0x%02X", reg, buffer[0]); -#endif + PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]); if(rc < 0) return rc; @@ -239,9 +257,8 @@ { int rc, retries; -#if 0 - PDEBUG("i2c write: 0x%02X:0x%02X", reg, value); -#endif + PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value); + /* Three byte write cycle */ for(retries = OV511_I2C_RETRIES;;) { /* Select camera register */ @@ -321,9 +338,8 @@ } value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); -#if 0 - PDEBUG("i2c read: 0x%02X:0x%02X", reg, value); -#endif + + PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value); /* This is needed to make ov511_i2c_write() work */ rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); @@ -355,9 +371,8 @@ if (rc < 0) return rc; value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); - #if 0 - PDEBUG("i2c read: 0x%02X:0x%02X", reg, value); - #endif + + PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value); return (value); } @@ -391,15 +406,14 @@ int rc; for(i=reg1; i<=regn; i++) { rc = ov511_i2c_read(dev, i); -#if 0 - PDEBUG("OV7610[0x%X] = 0x%X", i, rc); -#endif + + PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc); } } static void ov511_dump_i2c_regs( struct usb_device *dev) { - PDEBUG("I2C REGS"); + PDEBUG(3, "I2C REGS"); ov511_dump_i2c_range(dev, 0x00, 0x38); } @@ -409,27 +423,27 @@ int rc; for(i=reg1; i<=regn; i++) { rc = ov511_reg_read(dev, i); - PDEBUG("OV511[0x%X] = 0x%X", i, rc); + PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc); } } static void ov511_dump_regs( struct usb_device *dev) { - PDEBUG("CAMERA INTERFACE REGS"); + PDEBUG(1, "CAMERA INTERFACE REGS"); ov511_dump_reg_range(dev, 0x10, 0x1f); - PDEBUG("DRAM INTERFACE REGS"); + PDEBUG(1, "DRAM INTERFACE REGS"); ov511_dump_reg_range(dev, 0x20, 0x23); - PDEBUG("ISO FIFO REGS"); + PDEBUG(1, "ISO FIFO REGS"); ov511_dump_reg_range(dev, 0x30, 0x31); - PDEBUG("PIO REGS"); + PDEBUG(1, "PIO REGS"); ov511_dump_reg_range(dev, 0x38, 0x39); ov511_dump_reg_range(dev, 0x3e, 0x3e); - PDEBUG("I2C REGS"); + PDEBUG(1, "I2C REGS"); ov511_dump_reg_range(dev, 0x40, 0x49); - PDEBUG("SYSTEM CONTROL REGS"); + PDEBUG(1, "SYSTEM CONTROL REGS"); ov511_dump_reg_range(dev, 0x50, 0x53); ov511_dump_reg_range(dev, 0x5e, 0x5f); - PDEBUG("OmniCE REGS"); + PDEBUG(1, "OmniCE REGS"); ov511_dump_reg_range(dev, 0x70, 0x79); ov511_dump_reg_range(dev, 0x80, 0x9f); ov511_dump_reg_range(dev, 0xa0, 0xbf); @@ -441,7 +455,7 @@ { int rc; - PDEBUG("Reset: type=0x%X", reset_type); + PDEBUG(3, "Reset: type=0x%X", reset_type); rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); if (rc < 0) err("reset: command failed"); @@ -457,9 +471,7 @@ { int alt, multiplier, rc; -#if 0 - PDEBUG("set packet size: %d", size); -#endif + PDEBUG(3, "set packet size: %d", size); switch (size) { case 992: @@ -576,7 +588,7 @@ p->hue = 0x8000; p->whiteness = 105 << 8; - p->depth = 24; + p->depth = 3; /* Don't know if this is right */ p->palette = VIDEO_PALETTE_RGB24; /* Restart the camera */ @@ -594,10 +606,8 @@ int rc = 0; struct usb_device *dev = ov511->dev; -#if 0 - PDEBUG("ov511_mode_init_regs(ov511, %d, %d, %d, %d)", + PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)", width, height, mode, sub_flag); -#endif // ov511_set_packet_size(ov511, 0); if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) { @@ -606,13 +616,19 @@ } if (mode == VIDEO_PALETTE_GREY) { - ov511_reg_write(dev, 0x16, 0); - ov511_i2c_write(dev, 0xe, 0x44); + ov511_reg_write(dev, 0x16, 0x00); + ov511_i2c_write(dev, 0x0e, 0x44); ov511_i2c_write(dev, 0x13, 0x21); + /* For snapshot */ + ov511_reg_write(dev, 0x1e, 0x00); + ov511_reg_write(dev, 0x1f, 0x01); } else { - ov511_reg_write(dev, 0x16, 1); - ov511_i2c_write(dev, 0xe, 0x4); - ov511_i2c_write(dev, 0x13, 0x1); + ov511_reg_write(dev, 0x16, 0x01); + ov511_i2c_write(dev, 0x0e, 0x04); + ov511_i2c_write(dev, 0x13, 0x01); + /* For snapshot */ + ov511_reg_write(dev, 0x1e, 0x01); + ov511_reg_write(dev, 0x1f, 0x03); } if (width == 640 && height == 480) { @@ -626,13 +642,26 @@ ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1); ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1); ov511_i2c_write(dev, 0x11, 0x01); + + /* Snapshot additions */ + ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1); + ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1); + ov511_reg_write(ov511->dev, 0x1c, 0x00); + ov511_reg_write(ov511->dev, 0x1d, 0x00); } else { ov511_i2c_write(ov511->dev, 0x17, 0x38); ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2)); ov511_i2c_write(ov511->dev, 0x19, 0x5); - ov511_i2c_write(ov511->dev, 0x1c, + (480>>1)); + ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1)); ov511_reg_write(dev, 0x12, 0x4f); ov511_reg_write(dev, 0x13, 0x3d); + + /* Snapshot additions */ + ov511_reg_write(ov511->dev, 0x1a, 0x4f); + ov511_reg_write(ov511->dev, 0x1b, 0x3d); + ov511_reg_write(ov511->dev, 0x1c, 0x00); + ov511_reg_write(ov511->dev, 0x1d, 0x00); + if (mode == VIDEO_PALETTE_GREY) { ov511_i2c_write(dev, 0x11, 4); /* check */ } else { @@ -642,6 +671,8 @@ ov511_reg_write(dev, 0x14, 0x00); ov511_reg_write(dev, 0x15, 0x00); + + /* FIXME?? Shouldn't below be true only for YUV420? */ ov511_reg_write(dev, 0x18, 0x03); ov511_i2c_write(dev, 0x12, 0x24); @@ -654,6 +685,12 @@ ov511_reg_write(dev, 0x15, 0x00); ov511_reg_write(dev, 0x18, 0x03); + /* Snapshot additions */ + ov511_reg_write(dev, 0x1a, 0x27); + ov511_reg_write(dev, 0x1b, 0x1f); + ov511_reg_write(dev, 0x1c, 0x00); + ov511_reg_write(dev, 0x1d, 0x00); + if (mode == VIDEO_PALETTE_GREY) { ov511_i2c_write(dev, 0x11, 1); /* check */ } else { @@ -671,7 +708,7 @@ // ov511_set_packet_size(ov511, 993); if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) { - PDEBUG("reset: command failed"); + err("reset: command failed"); return -EIO; } @@ -683,52 +720,74 @@ Turn a YUV4:2:0 block into an RGB block +Video4Linux seems to use the blue, green, red channel +order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red. + +Color space conversion coefficients taken from the excellent +http://www.inforamp.net/~poynton/ColorFAQ.html +In his terminology, this is a CCIR 601.1 YCbCr -> RGB. +Y values are given for all 4 pixels, but the U (Pb) +and V (Pr) are assumed constant over the 2x2 block. + +To avoid floating point arithmetic, the color conversion +coefficients are scaled into 16.16 fixed-point integers. + *************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) -static inline void ov511_move_420_block(int y00, int y01, int y10, int y11, - int u, int v, int w, - unsigned char * pOut) -{ - int r = 68911 * v; - int g = -16915 * u + -35101 * v; - int b = 87097 * u; - y00 *= 49152; - y01 *= 49152; - y10 *= 49152; - y11 *= 49152; - *(pOut+w*3) = LIMIT(r + y10); - *pOut++ = LIMIT(r + y00); - *(pOut+w*3) = LIMIT(g + y10); - *pOut++ = LIMIT(g + y00); - *(pOut+w*3) = LIMIT(b + y10); - *pOut++ = LIMIT(b + y00); - *(pOut+w*3) = LIMIT(r + y11); - *pOut++ = LIMIT(r + y01); - *(pOut+w*3) = LIMIT(g + y11); - *pOut++ = LIMIT(g + y01); - *(pOut+w*3) = LIMIT(b + y11); - *pOut++ = LIMIT(b + y01); +// LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) +static inline void ov511_move_420_block( + int yTL, int yTR, int yBL, int yBR, + int u, int v, + int rowPixels, unsigned char * rgb) +{ + const double brightness=1.0;//0->black; 1->full scale + const double saturation=1.0;//0->greyscale; 1->full color + const double fixScale=brightness*256*256; + const int rvScale=(int)(1.402*saturation*fixScale); + const int guScale=(int)(-0.344136*saturation*fixScale); + const int gvScale=(int)(-0.714136*saturation*fixScale); + const int buScale=(int)(1.772*saturation*fixScale); + const int yScale=(int)(fixScale); + + int r = rvScale * v; + int g = guScale * u + gvScale * v; + int b = buScale * u; + yTL *= yScale; yTR *= yScale; + yBL *= yScale; yBR *= yScale; + + //Write out top two pixels + rgb[0]=LIMIT(b+yTL); rgb[1]=LIMIT(g+yTL); rgb[2]=LIMIT(r+yTL); + rgb[3]=LIMIT(b+yTR); rgb[4]=LIMIT(g+yTR); rgb[5]=LIMIT(r+yTR); + rgb+=3*rowPixels;//Skip down to next line to write out bottom two pixels + rgb[0]=LIMIT(b+yBL); rgb[1]=LIMIT(g+yBL); rgb[2]=LIMIT(r+yBL); + rgb[3]=LIMIT(b+yBR); rgb[4]=LIMIT(g+yBR); rgb[5]=LIMIT(r+yBR); } + /*************************************************************** For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. The -first 64 bytes of each segment are V, the next 64 are U. The V and -U are arranged as follows: +first 64 bytes of each segment are U, the next 64 are V. The U and +V are arranged as follows: 0 1 ... 7 8 9 ... 15 ... 56 57 ... 63 -The next 256 bytes are Y data and represent 4 squares of 8x8 pixels as -follows: +U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + +The next 256 bytes are full resolution Y data and represent 4 +squares of 8x8 pixels as follows: 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 8 9 ... 15 72 73 ... 79 200 201 ... 207 ... ... ... 56 57 ... 63 120 121 127 248 249 ... 255 +Note that the U and V data in one segment represents a 16 x 16 pixel +area, but the Y data represents a 32 x 8 pixel area. + If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments, verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 this puts the data @@ -780,8 +839,8 @@ int y01 = *(pOut+3); int y10 = *(pOut+iWidth*3); int y11 = *(pOut+iWidth*3+3); - int u = *(pIn+64) - 128; - int v = *pIn++ - 128; + int v = *(pIn+64) - 128; + int u = *pIn++ - 128; ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut); pOut += 6; } @@ -810,8 +869,8 @@ int y00 = *pIn++; int y11 = *(pIn+8); int y01 = *pIn++; - int u = *pOut1 - 128; - int v = *(pOut1+1) - 128; + int v = *pOut1 - 128; + int u = *(pOut1+1) - 128; ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, pOut1); pOut1 += 6; } @@ -868,6 +927,42 @@ } } + +/************************************************************** + * fixFrameRGBoffset-- + * My camera seems to return the red channel about 1 pixel + * low, and the blue channel about 1 pixel high. After YUV->RGB + * conversion, we can correct this easily. OSL 2/24/2000. + *************************************************************/ +static void fixFrameRGBoffset(struct ov511_frame *frame) +{ + int x,y; + int rowBytes=frame->width*3,w=frame->width; + unsigned char *rgb=frame->data; + const int shift=1;//Distance to shift pixels by, vertically + + if (frame->width<400) + return;//Don't bother with little images + + //Shift red channel up + for (y=shift;yheight;y++) + { + int lp=(y-shift)*rowBytes;//Previous line offset + int lc=y*rowBytes;//Current line offset + for (x=0;xheight-shift-1;y>=0;y--) + { + int ln=(y+shift)*rowBytes;//Next line offset + int lc=y*rowBytes;//Current line offset + for (x=0;xcurframe == -1) continue; if (st) - PDEBUG("data error: [%d] len=%d, status=%d", i, n, st); + PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); frame = &ov511->frame[ov511->curframe]; @@ -899,14 +994,15 @@ struct timeval *ts; ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); do_gettimeofday(ts); -#if 0 - PDEBUG("Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d", + + PDEBUG(4, "Frame End, curframe = %d, packnum=%d, hw=%d, vw=%d", ov511->curframe, (int)(cdata[992]), (int)(cdata[9]), (int)(cdata[10])); -#endif if (frame->scanstate == STATE_LINES) { int iFrameNext; + if (fix_rgb_offset) + fixFrameRGBoffset(frame); frame->grabstate = FRAME_DONE; if (waitqueue_active(&frame->wq)) { frame->grabstate = FRAME_DONE; @@ -919,10 +1015,10 @@ ov511->curframe = iFrameNext; ov511->frame[iFrameNext].scanstate = STATE_SCANNING; } else { -#if 0 - PDEBUG("Frame not ready? state = %d", + + PDEBUG(4, "Frame not ready? state = %d", ov511->frame[iFrameNext].grabstate); -#endif + ov511->curframe = -1; } } @@ -932,11 +1028,18 @@ else if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | cdata[4] | cdata[5] | cdata[6] | cdata[7]) == 0 && (cdata[8] & 8)) { -#if 0 - PDEBUG("ov511: Found Frame Start!, framenum = %d", + + PDEBUG(4, "ov511: Found Frame Start!, framenum = %d", ov511->curframe); -#endif - frame->scanstate = STATE_LINES; + + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (cdata[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "ov511_move_data: snapshot detected"); + } + + frame->scanstate = STATE_LINES; frame->segment = 0; } @@ -1014,11 +1117,11 @@ } } -#if 0 - PDEBUG("pn: %d %d %d %d %d %d %d %d %d %d\n", + + PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d\n", aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); -#endif + return totlen; } @@ -1032,7 +1135,7 @@ return; if (!ov511->streaming) { - PDEBUG("hmmm... not streaming, but got interrupt\n"); + PDEBUG(2, "hmmm... not streaming, but got interrupt"); return; } @@ -1166,6 +1269,7 @@ frame->grabstate = FRAME_GRABBING; frame->scanstate = STATE_SCANNING; frame->scanlength = 0; /* accumulated in ov511_parse_data() */ + frame->snapshot = 0; ov511->curframe = framenum; @@ -1192,7 +1296,7 @@ int err = -EBUSY; struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - PDEBUG("ov511_open"); + PDEBUG(4, "ov511_open"); down(&ov511->lock); if (ov511->user) @@ -1212,8 +1316,8 @@ ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE; ov511->sub_flag = 0; - PDEBUG("frame [0] @ %p", ov511->frame[0].data); - PDEBUG("frame [1] @ %p", ov511->frame[1].data); + PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data); + PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data); ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); if (!ov511->sbuf[0].data) @@ -1222,8 +1326,8 @@ if (!ov511->sbuf[1].data) goto open_err_on1; - PDEBUG("sbuf[0] @ %p", ov511->sbuf[0].data); - PDEBUG("sbuf[1] @ %p", ov511->sbuf[1].data); + PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data); + PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data); err = ov511_init_isoc(ov511); if (err) @@ -1254,7 +1358,7 @@ { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; - PDEBUG("ov511_close"); + PDEBUG(4, "ov511_close"); down(&ov511->lock); ov511->user--; @@ -1289,9 +1393,8 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; -#if 0 - PDEBUG("IOCtl: 0x%X", cmd); -#endif + + PDEBUG(4, "IOCtl: 0x%X", cmd); if (!ov511->dev) return -EIO; @@ -1464,11 +1567,9 @@ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) return -EFAULT; -#if 0 - PDEBUG("MCAPTURE"); - PDEBUG("frame: %d, size: %dx%d, format: %d", + PDEBUG(4, "MCAPTURE"); + PDEBUG(4, "frame: %d, size: %dx%d, format: %d", vm.frame, vm.width, vm.height, vm.format); -#endif if (vm.format != VIDEO_PALETTE_RGB24 && vm.format != VIDEO_PALETTE_GREY) @@ -1516,10 +1617,9 @@ if (copy_from_user((void *)&frame, arg, sizeof(int))) return -EFAULT; -#if 0 - PDEBUG("syncing to frame %d, grabstate = %d", frame, + PDEBUG(4, "syncing to frame %d, grabstate = %d", frame, ov511->frame[frame].grabstate); -#endif + switch (ov511->frame[frame].grabstate) { case FRAME_UNUSED: return -EINVAL; @@ -1552,7 +1652,17 @@ } ov511->frame[frame].grabstate = FRAME_UNUSED; - + + /* Reset the hardware snapshot button */ + /* FIXME - Is this the best place for this? */ + if ((ov511->snap_enabled) && + (ov511->frame[frame].snapshot)) { + ov511->frame[frame].snapshot = 0; + ov511_reg_write(ov511->dev, 0x52, 0x01); + ov511_reg_write(ov511->dev, 0x52, 0x03); + ov511_reg_write(ov511->dev, 0x52, 0x01); + } + return 0; } case VIDIOCGFBUF: @@ -1594,7 +1704,7 @@ int frmx = -1; volatile struct ov511_frame *frame; - PDEBUG("ov511_read: %ld bytes, noblock=%d", count, noblock); + PDEBUG(4, "ov511_read: %ld bytes, noblock=%d", count, noblock); if (!dev || !buf) return -EFAULT; @@ -1644,18 +1754,37 @@ goto restart; } - PDEBUG("ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, + + /* Repeat until we get a snapshot frame */ + if (ov511->snap_enabled && !frame->snapshot) { + frame->bytes_read = 0; + if (ov511_new_frame(ov511, frmx)) + err("ov511_read: ov511_new_frame error"); + goto restart; + } + + /* Clear the snapshot */ + if (ov511->snap_enabled && frame->snapshot) { + frame->snapshot = 0; + ov511_reg_write(ov511->dev, 0x52, 0x01); + ov511_reg_write(ov511->dev, 0x52, 0x03); + ov511_reg_write(ov511->dev, 0x52, 0x01); + } + + PDEBUG(4, "ov511_read: frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, frame->bytes_read, frame->scanlength); /* copy bytes to user space; we allow for partials reads */ - if ((count + frame->bytes_read) > frame->scanlength) - count = frame->scanlength - frame->bytes_read; +// if ((count + frame->bytes_read) > frame->scanlength) +// count = frame->scanlength - frame->bytes_read; + /* FIXME - count hardwired to be one frame... */ + count = frame->width * frame->height * frame->depth; if (copy_to_user(buf, frame->data + frame->bytes_read, count)) return -EFAULT; frame->bytes_read += count; - PDEBUG("ov511_read: {copy} count used=%ld, new bytes_read=%ld", + PDEBUG(4, "ov511_read: {copy} count used=%ld, new bytes_read=%ld", count, frame->bytes_read); if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ @@ -1679,7 +1808,7 @@ if (!ov511->dev) return -EIO; - PDEBUG("mmap: %ld (%lX) bytes", size, size); + PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) return -EINVAL; @@ -1702,24 +1831,22 @@ } static struct video_device ov511_template = { - "OV511 USB Camera", - VID_TYPE_CAPTURE, - VID_HARDWARE_OV511, - ov511_open, - ov511_close, - ov511_read, - ov511_write, - NULL, - ov511_ioctl, - ov511_mmap, - ov511_init_done, - NULL, - 0, - 0 + name: "OV511 USB Camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_OV511, + open: ov511_open, + close: ov511_close, + read: ov511_read, + write: ov511_write, + ioctl: ov511_ioctl, + mmap: ov511_mmap, + initialize: ov511_init_done, }; static int ov7610_configure(struct usb_device *dev) { + int tries; + if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, OV7610_I2C_WRITE_ID) < 0) return -1; @@ -1731,6 +1858,7 @@ if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) return -1; + /* Reset the 7610 and wait a bit for it to initialize */ if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; schedule_timeout (1 + 150 * HZ / 1000); @@ -1738,8 +1866,14 @@ if(ov511_i2c_read(dev, 0x00) < 0) return -1; - if((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) || - (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2)) { + tries = 5; + while((tries > 0) && + ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) != 0x7F) || + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) != 0xA2))) { + --tries; + } + + if (tries == 0) { err("Failed to read OV7610 ID. You might not have an OV7610,"); err("or it may be not responding. Report this to"); err("mmcclelland@delphi.com"); @@ -1786,12 +1920,11 @@ {OV511_I2C_BUS, 0x16, 0x06}, {OV511_I2C_BUS, 0x28, 0x24}, /* 24 */ {OV511_I2C_BUS, 0x2b, 0xac}, - {OV511_I2C_BUS, 0x5, 0x00}, - {OV511_I2C_BUS, 0x6, 0x00}, -#if 0 -#endif + {OV511_I2C_BUS, 0x05, 0x00}, + {OV511_I2C_BUS, 0x06, 0x00}, + {OV511_I2C_BUS, 0x12, 0x00}, - {OV511_I2C_BUS, 0x13, 0x00}, +// {OV511_I2C_BUS, 0x13, 0x00}, {OV511_I2C_BUS, 0x38, 0x81}, {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */ {OV511_I2C_BUS, 0x05, 0x00}, @@ -1813,7 +1946,7 @@ {OV511_I2C_BUS, 0x33, 0x20}, {OV511_I2C_BUS, 0x34, 0x48}, {OV511_I2C_BUS, 0x12, 0x24}, - {OV511_I2C_BUS, 0x13, 0x01}, +// {OV511_I2C_BUS, 0x13, 0x01}, {OV511_I2C_BUS, 0x11, 0x01}, {OV511_I2C_BUS, 0x0c, 0x24}, {OV511_I2C_BUS, 0x0d, 0x24}, @@ -1853,7 +1986,8 @@ } ov511->compress = 0; - + ov511->snap_enabled = snapshot; + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ ov511->frame[0].width = DEFAULT_WIDTH; @@ -1864,10 +1998,16 @@ ov511->frame[1].bytes_read = 0; /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ - if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) return rc; + if ((rc = ov511_write_regvals(dev, aRegvalsNorm))) goto error; if ((rc = ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT, - VIDEO_PALETTE_RGB24, 0)) < 0) return rc; + VIDEO_PALETTE_RGB24, 0)) < 0) goto error; + if (autoadjust) { + if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error; + } + else { + if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error; + } return 0; @@ -1887,7 +2027,7 @@ struct usb_ov511 *ov511; int rc; - PDEBUG("probing for device..."); + PDEBUG(1, "probing for device..."); /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -1933,19 +2073,25 @@ case 3: printk("ov511: Camera is a D-Link DSB-C300\n"); break; + case 4: + printk("ov511: Camera is a generic OV511/OV7610\n"); + break; case 5: printk("ov511: Camera is a Puretek PT-6007\n"); break; case 21: printk("ov511: Camera is a Creative Labs WebCam 3\n"); break; + case 36: + printk("ov511: Camera is a Koala-Cam\n"); + break; case 100: printk("ov511: Camera is a Lifeview RoboCam\n"); break; case 102: printk("ov511: Camera is a AverMedia InterCam Elite\n"); break; - case 112: + case 112: /* The OmniVision OV7110 evaluation kit uses this too */ printk("ov511: Camera is a MediaForte MV300\n"); break; default: @@ -2027,7 +2173,7 @@ int usb_ov511_init(void) { - PDEBUG("usb_ov511_init()"); + PDEBUG(1, "usb_ov511_init()"); EXPORT_NO_SYMBOLS; @@ -2049,7 +2195,7 @@ { usb_ov511_cleanup(); - PDEBUG("Module unloaded"); + PDEBUG(1, "Module unloaded"); } #endif diff -Naur linux-2.3.50-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h --- linux-2.3.50-orig/drivers/usb/ov511.h Sat Feb 19 01:19:48 2000 +++ linux/drivers/usb/ov511.h Sat Mar 11 02:30:10 2000 @@ -6,9 +6,10 @@ #define OV511_DEBUG /* Turn on debug messages */ #ifdef OV511_DEBUG -# define PDEBUG(fmt, args...) printk("ov511: " fmt "\n" , ## args) +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) printk("ov511: " fmt "\n" , ## args) #else -# define PDEBUG(fmt, args...) do {} while(0) +# define PDEBUG(level, fmt, args...) do {} while(0) #endif /* Camera interface register numbers */ @@ -227,6 +228,8 @@ long bytes_read; /* amount of scanlength that has been read from *data */ wait_queue_head_t wq; /* Processes waiting */ + + int snapshot; /* True if frame was a snapshot */ }; #define OV511_NUMFRAMES 2 @@ -269,6 +272,8 @@ int scratchlen; wait_queue_head_t wq; /* Processes waiting */ + + int snap_enabled; /* Snapshot mode enabled */ }; #endif