diff -Naur linux-2.4.0-test7-pre2-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt --- linux-2.4.0-test7-pre2-orig/Documentation/usb/ov511.txt Sat Aug 12 04:26:57 2000 +++ linux/Documentation/usb/ov511.txt Sat Aug 12 05:46:48 2000 @@ -6,14 +6,13 @@ Homepage: http://alpha.dyndns.org/ov511 NEW IN THIS VERSION: - o Sensor detection fixes - o More efficient/reliable buffer allocation - o Many minor fixes + o OV6620 works at 384x288 and 320x240 (colors are still wrong though) + o Minor code improvements INTRODUCTION: This is a driver for the OV511, a USB-only chip used in many "webcam" devices. -Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work.It +Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It supports streaming and capture of color or monochrome video via the Video4Linux API. Most V4L apps are compatible with it, but a few videoconferencing programs do not work yet. The following resolutions are supported: 640x480, 448x336, @@ -195,10 +194,16 @@ both OV511 and OV511+ cameras, trial-and-error may be necessary for finding the optimum setting. - + NAME: retry_sync + TYPE: boolean + DEFAULT: 0 + DESC: Prevent apps from timing out if frame is not done in time. This is + useful if you are having problems with Xawtv getting "stuck" on a frame + when your system is under heavy load. + WORKING FEATURES: o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 - o YUV420 and YUV422P color + o RGB24, YUV420, YUV422, YUYV, and YUV422P color o Monochrome o Setting/getting of saturation, contrast and brightness (no hue yet; only works with OV7610, not the OV7620 or OV7620AE) @@ -208,7 +213,9 @@ o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and corrupted frames. If you have a very fast CPU, you can try it. o Snapshot mode (only works with some read() based apps; see below for more) - o read() support + o 160x120 support + o OV6620 sensor support + o GBR422 parsing TODO: o Fix the noise / grainy image problem. @@ -216,21 +223,21 @@ 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 YUV422 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 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 Creative WebCam III has problems initializing its sensor. This should be + fixed now, but if you still have problems let me know. o Get rid of the memory management functions (put them in videodev.c??) - o Setting of contrast and brightness not working with 7620 + o Setting of contrast and brightness not working with 7620/7620AE/6620 o Driver/camera state save/restore for when USB supports suspend/resume o Unstable on SMP systems + o OV7620/OV6620 experience frame corruption with moving objects + o OV6620 is too dark and colors are incorrect HOW TO CONTACT ME: diff -Naur linux-2.4.0-test7-pre2-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c --- linux-2.4.0-test7-pre2-orig/drivers/usb/ov511.c Sat Aug 12 04:27:18 2000 +++ linux/drivers/usb/ov511.c Sat Aug 12 05:46:28 2000 @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.20"; +static const char version[] = "1.23"; #define __NO_VERSION__ @@ -59,11 +59,9 @@ #define MAX_FRAME_SIZE (640 * 480 * 3) #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 480 - #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) -#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : ((p) == VIDEO_PALETTE_YUV422 ? 8 : 24)) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \ + ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24)) /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -107,6 +105,13 @@ /* Prevent apps from timing out if frame is not done in time */ static int retry_sync = 0; +/* Enable compression. This is for experimentation only; compressed images + * still cannot be decoded yet. */ +static int compress = 0; + +/* Display test pattern - doesn't work yet either */ +static int testpat = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure"); MODULE_PARM(debug, "i"); @@ -122,13 +127,17 @@ MODULE_PARM(aperture, "i"); MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs"); MODULE_PARM(force_rgb, "i"); -MODULE_PARM_DESC(force_rgb, "Read RBG instead of BGR"); +MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR"); MODULE_PARM(buf_timeout, "i"); MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation"); MODULE_PARM(cams, "i"); MODULE_PARM_DESC(cams, "Number of simultaneous cameras"); MODULE_PARM(retry_sync, "i"); MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out"); +MODULE_PARM(compress, "i"); +MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)"); +MODULE_PARM(testpat, "i"); +MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)"); MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); MODULE_DESCRIPTION("OV511 USB Camera Driver"); @@ -860,20 +869,23 @@ return 0; } -/* FIXME: 176x144, 160x140 */ /* LNCNT values fixed by Lawrence Glaister */ static struct mode_list mlist[] = { - /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COMC COML */ - { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, - { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, - { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, - { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, - { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, - { 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COML */ + { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e }, + { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e }, + { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e }, + { 176, 144, 0, 0x15, 0x12, 0x01, 0x01, 0x03, 0x04, 0x1e }, + { 176, 144, 1, 0x15, 0x12, 0x01, 0x01, 0x03, 0x04, 0x1e }, + { 160, 120, 0, 0x13, 0x0e, 0x01, 0x01, 0x03, 0x04, 0x1e }, + { 160, 120, 1, 0x13, 0x0e, 0x01, 0x01, 0x03, 0x04, 0x1e }, { 0, 0 } }; @@ -883,7 +895,8 @@ { int i; struct usb_device *dev = ov511->dev; - int hwsbase = 0, hwebase = 0; + int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; + int hwscale = 0, vwscale = 0; PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); @@ -895,7 +908,7 @@ ov511_reg_write(dev, 0x16, 0x00); if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) { - /* these aren't valid on the OV7620 */ + /* these aren't valid on the OV6620/OV7620 */ ov511_i2c_write(dev, 0x0e, 0x44); } ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20); @@ -907,7 +920,7 @@ ov511_reg_write(dev, 0x16, 0x01); if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) { - /* not valid on the OV7620 */ + /* not valid on the OV6620/OV7620 */ ov511_i2c_write(dev, 0x0e, 0x04); } ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00); @@ -923,27 +936,64 @@ case SEN_OV7620AE: hwsbase = 0x38; hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + hwsbase = 0x38; + hwebase = 0x39; + vwsbase = 0x03; + vwebase = 0x04; break; case SEN_OV7620: hwsbase = 0x2c; hwebase = 0x2d; + vwsbase = vwebase = 0x05; break; default: - hwsbase = 0; - hwebase = 0; - break; + err("Invalid sensor"); + return -EINVAL; } + /* Bit 5 of COM C register varies with sensor */ + if (ov511->sensor == SEN_OV6620) { + if (width > 176 && height > 144) { /* CIF */ + ov511_i2c_write(dev, 0x14, 0x04); + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + hwsize = 352; + vwsize = 288; + } else { /* QCIF */ + ov511_i2c_write(dev, 0x14, 0x24); + hwsize = 176; + vwsize = 144; + } + } + else { + if (width > 320 && height > 240) { /* VGA */ + ov511_i2c_write(dev, 0x14, 0x04); + hwscale = 2; + vwscale = 1; + hwsize = 640; + vwsize = 480; + } else { /* QVGA */ + ov511_i2c_write(dev, 0x14, 0x24); + hwscale = 1; + hwsize = 320; + vwsize = 240; + } + } + + /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */ if (sub_flag) { - ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2)); - ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2)); - ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1)); - ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1)); + ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale)); + ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale)); + ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale)); } else { ov511_i2c_write(dev, 0x17, hwsbase); - ov511_i2c_write(dev, 0x18, hwebase + (640>>2)); - ov511_i2c_write(dev, 0x19, 0x5); - ov511_i2c_write(dev, 0x1a, 5 + (480>>1)); + ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale)); + ov511_i2c_write(dev, 0x19, vwsbase); + ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale)); } for (i = 0; mlist[i].width; i++) { @@ -981,17 +1031,22 @@ #ifdef OV511_GBR422 ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08); #else - ov511_i2c_write(dev, 0x12, mlist[i].common_A); + ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00)); #endif - ov511_i2c_write(dev, 0x14, mlist[i].common_C); - /* 7620 doesn't have register 0x35, so play it safe */ - if (ov511->sensor != SEN_OV7620) + /* 7620/6620 don't have register 0x35, so play it safe */ + if (ov511->sensor == SEN_OV7610 || + ov511->sensor == SEN_OV7620AE) ov511_i2c_write(dev, 0x35, mlist[i].common_L); break; } + if (compress) { + ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression + ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs + } + if (ov511_restart(ov511->dev) < 0) return -EIO; @@ -1028,6 +1083,16 @@ * * To avoid floating point arithmetic, the color conversion * coefficients are scaled into 16.16 fixed-point integers. + * They were determined as follows: + * + * double brightness = 1.0; (0->black; 1->full scale) + * double saturation = 1.0; (0->greyscale; 1->full color) + * double fixScale = brightness * 256 * 256; + * int rvScale = (int)(1.402 * saturation * fixScale); + * int guScale = (int)(-0.344136 * saturation * fixScale); + * int gvScale = (int)(-0.714136 * saturation * fixScale); + * int buScale = (int)(1.772 * saturation * fixScale); + * int yScale = (int)(fixScale); */ /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */ @@ -1037,14 +1102,11 @@ 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); + const int rvScale = 91881; + const int guScale = -22553; + const int gvScale = -46801; + const int buScale = 116129; + const int yScale = 65536; int r, g, b; g = guScale * u + gvScale * v; @@ -1104,6 +1166,9 @@ #undef OV511_DUMPPIX +/* #define this and OV511_DUMPPIX to disable parsing of UV data */ +#undef OV511_FORCE_MONO + #ifdef OV511_GBR422 static void ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, @@ -1234,6 +1299,8 @@ } } #else + +#ifndef OV511_FORCE_MONO /* Just dump pix data straight out for debug */ int i, j; @@ -1246,95 +1313,107 @@ } pOut0 += (iWidth - WDIV) * 3; } +#else + /* This converts the Y data to "black-and-white" RGB data */ + /* Useful for experimenting with compression */ + int k, l, m; + unsigned char *pIn, *pOut, *pOut1; + + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1++ = *pIn; + *pOut1++ = *pIn; + *pOut1++ = *pIn++; + } + pOut1 += (iWidth - 8) * 3; + } + pOut += 8 * 3; + } +#endif + #endif } #endif +/* This converts YUV420 segments to YUYV */ static void ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0, - int iOutY, int iOutUV, int iHalf, int iWidth) + int iOutY, int iOutUV, int iWidth) { int k, l, m; - unsigned char *pIn; - unsigned char *pOut, *pOut1; + unsigned char *pIn, *pOut, *pOut1; - /* Just copy the Y's if in the first stripe */ - if (!iHalf) { - pIn = pIn0 + 128; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l = 0; l < 8; l++) { - for (m = 0; m < 8; m++) { - *pOut1++ = (*pIn++) & 0xF0; - } - pOut1 += iWidth - 8; + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1 = (*pIn++); + pOut1 += 2; } - pOut += 8; + pOut1 += (iWidth - 8) * 2; } + pOut += 8 * 2; } - /* Use the first half of VUs to calculate value */ pIn = pIn0; - pOut = pOut0 + iOutUV; - for (l = 0; l < 4; l++) { + pOut = pOut0 + iOutUV + 1; + for (l = 0; l < 8; l++) { for (m=0; m<8; m++) { - unsigned char *p00 = pOut; - unsigned char *p01 = pOut+1; - unsigned char *p10 = pOut+iWidth; - unsigned char *p11 = pOut+iWidth+1; - int v = *(pIn+64) - 128; - int u = *pIn++ - 128; - int uv = ((u >> 4) & 0x0C) + (v >> 6); - - *p00 |= uv; - *p01 |= uv; - *p10 |= uv; - *p11 |= uv; - - pOut += 2; + int v = *(pIn+64); + int u = *pIn++; + + *pOut = u; + *(pOut+2) = v; + *(pOut+iWidth) = u; + *(pOut+iWidth+2) = v; + pOut += 4; } - pOut += (iWidth*2 - 16); + pOut += (iWidth*4 - 32); } +} - /* Just copy the other UV rows */ - for (l = 0; l < 4; l++) { - for (m = 0; m < 8; m++) { - int v = *(pIn + 64) - 128; - int u = (*pIn++) - 128; - int uv = ((u >> 4) & 0x0C) + (v >> 6); - *(pOut) = uv; - pOut += 2; - } - pOut += (iWidth*2 - 16); +static void +ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iWidth, int iHeight) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + unsigned a = iWidth * iHeight; + unsigned w = iWidth / 2; + + pIn = pIn0; + pOut = pOut0 + iOutUV + a; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) *pOut1++ = *pIn++; + pOut += w; } - /* Calculate values if it's the second half */ - if (iHalf) { - pIn = pIn0 + 128; - pOut = pOut0 + iOutY; - for (k = 0; k < 4; k++) { - pOut1 = pOut; - for (l=0; l<4; l++) { - for (m=0; m<4; m++) { - int y10 = *(pIn+8); - int y00 = *pIn++; - int y11 = *(pIn+8); - int y01 = *pIn++; - int uv = *pOut1; + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + a + a/4; + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) *pOut1++ = *pIn++; + pOut += w; + } - *pOut1 = (y00 & 0xF0) | uv; - *(pOut1+1) = (y01 & 0xF0) | uv; - *(pOut1+iWidth) = (y10 & 0xF0) | uv; - *(pOut1+iWidth+1) = (y11 & 0xF0) | uv; - - pOut1 += 2; - } - pOut1 += (iWidth*2 - 8); - pIn += 8; - } - pOut += 8; + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) + *pOut1++ =*pIn++; + pOut1 += iWidth - 8; } + pOut += 8; } } @@ -1633,8 +1712,12 @@ iY & 1, frame->width); break; case VIDEO_PALETTE_YUV422: - ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, - iY & 1, frame->width); + case VIDEO_PALETTE_YUYV: + ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width); + break; + case VIDEO_PALETTE_YUV420: + ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4, + frame->width, frame->height); break; case VIDEO_PALETTE_YUV422P: ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2, @@ -1674,7 +1757,7 @@ return; if (!ov511->streaming) { - PDEBUG(2, "hmmm... not streaming, but got interrupt"); + PDEBUG(4, "hmmm... not streaming, but got interrupt"); return; } @@ -1794,7 +1877,6 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) { struct ov511_frame *frame; - int width, height; PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe, framenum); @@ -1810,11 +1892,9 @@ return 0; frame = &ov511->frame[framenum]; - width = frame->width; - height = frame->height; - PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, width, - height); + PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, + frame->width, frame->height); frame->grabstate = FRAME_GRABBING; frame->scanstate = STATE_SCANNING; @@ -1824,15 +1904,15 @@ ov511->curframe = framenum; /* Make sure it's not too big */ - if (width > DEFAULT_WIDTH) - width = DEFAULT_WIDTH; + if (frame->width > ov511->maxwidth) + frame->width = ov511->maxwidth; - width &= ~7L; /* Multiple of 8 */ + frame->width &= ~7L; /* Multiple of 8 */ - if (height > DEFAULT_HEIGHT) - height = DEFAULT_HEIGHT; + if (frame->height > ov511->maxheight) + frame->height = ov511->maxheight; - width &= ~3L; /* Multiple of 4 */ + frame->height &= ~3L; /* Multiple of 4 */ return 0; } @@ -2058,8 +2138,8 @@ b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; b.channels = 1; b.audios = 0; - b.maxwidth = DEFAULT_WIDTH; - b.maxheight = DEFAULT_HEIGHT; + b.maxwidth = ov511->maxwidth; + b.maxheight = ov511->maxheight; b.minwidth = 32; b.minheight = 16; @@ -2126,6 +2206,8 @@ if (p.palette != VIDEO_PALETTE_GREY && p.palette != VIDEO_PALETTE_RGB24 && p.palette != VIDEO_PALETTE_YUV422 && + p.palette != VIDEO_PALETTE_YUYV && + p.palette != VIDEO_PALETTE_YUV420 && p.palette != VIDEO_PALETTE_YUV422P) return -EINVAL; @@ -2198,9 +2280,9 @@ return -EINVAL; if (vw.clipcount) return -EINVAL; - if (vw.height != DEFAULT_HEIGHT) + if (vw.height != ov511->maxheight) return -EINVAL; - if (vw.width != DEFAULT_WIDTH) + if (vw.width != ov511->maxwidth) return -EINVAL; #endif @@ -2268,6 +2350,8 @@ if (vm.format != VIDEO_PALETTE_RGB24 && vm.format != VIDEO_PALETTE_YUV422 && + vm.format != VIDEO_PALETTE_YUYV && + vm.format != VIDEO_PALETTE_YUV420 && vm.format != VIDEO_PALETTE_YUV422P && vm.format != VIDEO_PALETTE_GREY) return -EINVAL; @@ -2275,7 +2359,7 @@ if ((vm.frame != 0) && (vm.frame != 1)) return -EINVAL; - if (vm.width > DEFAULT_WIDTH || vm.height > DEFAULT_HEIGHT) + if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) return -EINVAL; if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) @@ -2392,14 +2476,14 @@ return 0; } case VIDIOCKEY: - return 0; + return 0; case VIDIOCCAPTURE: return -EINVAL; case VIDIOCSFBUF: return -EINVAL; case VIDIOCGTUNER: case VIDIOCSTUNER: - return -EINVAL; + return -EINVAL; case VIDIOCGFREQ: case VIDIOCSFREQ: return -EINVAL; @@ -2599,12 +2683,9 @@ { OV511_I2C_BUS, 0x16, 0x06 }, { OV511_I2C_BUS, 0x28, 0x24 }, { OV511_I2C_BUS, 0x2b, 0xac }, - { OV511_I2C_BUS, 0x05, 0x00 }, - { OV511_I2C_BUS, 0x06, 0x00 }, { OV511_I2C_BUS, 0x12, 0x00 }, { OV511_I2C_BUS, 0x38, 0x81 }, { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ - { OV511_I2C_BUS, 0x05, 0x00 }, { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ { OV511_I2C_BUS, 0x15, 0x01 }, { OV511_I2C_BUS, 0x20, 0x1c }, @@ -2612,7 +2693,6 @@ { OV511_I2C_BUS, 0x24, 0x10 }, { OV511_I2C_BUS, 0x25, 0x8a }, { OV511_I2C_BUS, 0x27, 0xc2 }, - { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ { OV511_I2C_BUS, 0x2a, 0x04 }, { OV511_I2C_BUS, 0x2c, 0xfe }, { OV511_I2C_BUS, 0x30, 0x71 }, @@ -2634,14 +2714,12 @@ { OV511_I2C_BUS, 0x2b, 0xac }, { OV511_I2C_BUS, 0x12, 0x00 }, { OV511_I2C_BUS, 0x28, 0x24 }, - { OV511_I2C_BUS, 0x05, 0x00 }, { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ { OV511_I2C_BUS, 0x15, 0x01 }, { OV511_I2C_BUS, 0x23, 0x00 }, { OV511_I2C_BUS, 0x24, 0x10 }, { OV511_I2C_BUS, 0x25, 0x8a }, { OV511_I2C_BUS, 0x27, 0xe2 }, - { OV511_I2C_BUS, 0x29, 0x03 }, { OV511_I2C_BUS, 0x2a, 0x00 }, { OV511_I2C_BUS, 0x2c, 0xfe }, { OV511_I2C_BUS, 0x30, 0x71 }, @@ -2658,24 +2736,13 @@ PDEBUG (4, "starting configuration"); - if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, - OV7610_I2C_WRITE_ID) < 0) - return -1; - - if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, - OV7610_I2C_READ_ID) < 0) - return -1; - - if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) - return -1; /* Reset the 76xx */ if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; -#if 1 /* Maybe this will fix detection problems? MM */ + /* Maybe this will fix detection problems? MM */ /* Wait for it to initialize */ schedule_timeout (1 + 150 * HZ / 1000); -#endif for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && @@ -2736,6 +2803,126 @@ return -1; } + /* Set sensor-specific vars */ + ov511->maxwidth = 640; + ov511->maxheight = 480; + + if (aperture < 0) { /* go with the default */ + if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1; + } else if (aperture <= 0xf) { /* user overrode default */ + if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0) + return -1; + } else { + err("Invalid setting for aperture; legal value: 0 - 15"); + return -1; + } + + if (autoadjust) { + if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, + ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1; + } else { + if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, + ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1; + ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8); + } + + return 0; +} + +static int ov6xx0_configure(struct usb_ov511 *ov511) +{ + struct usb_device *dev = ov511->dev; + int i, success, rc; + + static struct ov511_regvals aRegvalsNorm6x20[] = { + { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */ + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x03, 0xd0 }, + { OV511_I2C_BUS, 0x05, 0x7f }, + { OV511_I2C_BUS, 0x07, 0xa8 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_I2C_BUS, 0x10, 0xff }, /* ? */ + { OV511_I2C_BUS, 0x14, 0x04 }, + { OV511_I2C_BUS, 0x16, 0x06 }, /* ? */ + { OV511_I2C_BUS, 0x19, 0x04 }, + { OV511_I2C_BUS, 0x1a, 0x93 }, + { OV511_I2C_BUS, 0x20, 0x28 }, + { OV511_I2C_BUS, 0x27, 0xa2 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2a, 0x04 }, /* 84? */ + { OV511_I2C_BUS, 0x2b, 0xac }, /* a8? */ + { OV511_I2C_BUS, 0x2d, 0x95 }, + { OV511_I2C_BUS, 0x33, 0x28 }, + { OV511_I2C_BUS, 0x34, 0xc7 }, + { OV511_I2C_BUS, 0x38, 0x8b }, + { OV511_I2C_BUS, 0x3c, 0x5c }, + { OV511_I2C_BUS, 0x3d, 0x80 }, + { OV511_I2C_BUS, 0x3f, 0x00 }, + { OV511_I2C_BUS, 0x4d, 0xd2 }, + { OV511_I2C_BUS, 0x4e, 0xc1 }, + { OV511_I2C_BUS, 0x4f, 0x04 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG (4, "starting sensor configuration"); + + /* Reset the 6xx0 */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; + + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) { + success = 1; + continue; + } + + /* Reset the 6xx0 */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + /* Dummy read to sync I2C */ + if (ov511_i2c_read(dev, 0x00) < 0) return -1; + } + + if (success) { + PDEBUG(1, "I2C synced in %d attempt(s)", i); + } else { + err("Failed to read sensor ID. You might not have an OV6xx0,"); + err("or it may be not responding. Report this to"); + err("mmcclelland@delphi.com"); + return -1; + } + + /* Detect sensor if user didn't use override param */ + if (sensor == 0) { + rc = ov511_i2c_read(dev, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else { + info("Sensor is an OV6xx0 (version %d)", rc & 3); + ov511->sensor = SEN_OV6620; + } + } else { /* sensor != 0; user overrode detection */ + ov511->sensor = sensor; + info("Sensor set to type %d", ov511->sensor); + } + + /* Set sensor-specific vars */ + ov511->maxwidth = 352; + ov511->maxheight = 288; + + PDEBUG(4, "Writing 6x20 registers"); + if (ov511_write_regvals(dev, aRegvalsNorm6x20)) + return -1; + if (aperture < 0) { /* go with the default */ if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1; } else if (aperture <= 0xf) { /* user overrode default */ @@ -2781,10 +2968,10 @@ { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, - { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, /* 0f */ - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x3f }, - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x3f }, - { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 }, { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 }, { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 }, { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 }, @@ -2814,11 +3001,54 @@ ov511->snap_enabled = snapshot; + /* Test for 76xx */ + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, + OV7610_I2C_WRITE_ID) < 0) + goto error; + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, + OV7610_I2C_READ_ID) < 0) + goto error; + + if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + goto error; + + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + /* Test for 6xx0 */ + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, + OV6xx0_I2C_WRITE_ID) < 0) + goto error; + + if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, + OV6xx0_I2C_READ_ID) < 0) + goto error; + + if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + goto error; + + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) { + err("Can't determine sensor slave IDs"); + goto error; + } + + if(ov6xx0_configure(ov511) < 0) { + err("failed to configure OV6xx0"); + goto error; + } + } else { + if(ov76xx_configure(ov511) < 0) { + err("failed to configure OV76xx"); + goto error; + } + } + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ for (i = 0; i < OV511_NUMFRAMES; i++) { - ov511->frame[i].width = DEFAULT_WIDTH; - ov511->frame[i].height = DEFAULT_HEIGHT; + ov511->frame[i].width = ov511->maxwidth; + ov511->frame[i].height = ov511->maxheight; ov511->frame[i].depth = 24; ov511->frame[i].bytes_read = 0; ov511->frame[i].segment = 0; @@ -2826,14 +3056,8 @@ ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); } - /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ - - if(ov76xx_configure(ov511) < 0) { - err("failed to configure OV76xx"); - goto error; - } - - if (ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT, + /* Initialize to max width/height, RGB24 */ + if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight, VIDEO_PALETTE_RGB24, 0) < 0) goto error; @@ -2869,10 +3093,12 @@ interface = &dev->actconfig->interface[ifnum].altsetting[0]; /* Is it an OV511/OV511+? */ - if (dev->descriptor.idVendor != 0x05a9) + if (dev->descriptor.idVendor != 0x05a9 + && dev->descriptor.idVendor != 0x0813) return NULL; if (dev->descriptor.idProduct != 0x0511 - && dev->descriptor.idProduct != 0xA511) + && dev->descriptor.idProduct != 0xA511 + && dev->descriptor.idProduct != 0x0002) return NULL; /* Checking vendor/product should be enough, but what the hell */ @@ -2903,6 +3129,15 @@ info("USB OV511+ camera found"); ov511->bridge = BRG_OV511PLUS; break; + case 0x0002: + if (dev->descriptor.idVendor != 0x0813) + goto error; + info("Intel Play Me2Cam (OV511+) found"); + ov511->bridge = BRG_OV511PLUS; + break; + default: + err("Unknown product ID"); + goto error; } ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID); diff -Naur linux-2.4.0-test7-pre2-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h --- linux-2.4.0-test7-pre2-orig/drivers/usb/ov511.h Fri Jul 14 03:54:14 2000 +++ linux/drivers/usb/ov511.h Sat Aug 12 05:46:37 2000 @@ -183,6 +183,9 @@ // FIXME - these can vary between specific models #define OV7610_I2C_WRITE_ID 0x42 #define OV7610_I2C_READ_ID 0x43 +#define OV6xx0_I2C_WRITE_ID 0xC0 +#define OV6xx0_I2C_READ_ID 0xC1 + #define OV511_I2C_CLOCK_PRESCALER 0x03 /* Prototypes */ @@ -203,6 +206,7 @@ SEN_OV7610, SEN_OV7620, SEN_OV7620AE, + SEN_OV6620, }; enum { @@ -254,7 +258,7 @@ int hdrheight; /* Height */ int sub_flag; /* Sub-capture mode for this frame? */ - int format; /* Format for this frame */ + unsigned int format; /* Format for this frame */ int segsize; /* How big is each segment from the camera? */ volatile int grabstate; /* State of grabbing */ @@ -285,6 +289,10 @@ int desc; unsigned char iface; + /* Determined by sensor type */ + int maxwidth; + int maxheight; + int brightness; int colour; int contrast; @@ -356,7 +364,6 @@ u8 lndv; /* line divisor */ u8 m420; u8 common_A; - u8 common_C; u8 common_L; };