diff -Naur linux-2.4.0-test11-pre3-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.4.0-test11-pre3-orig/Documentation/usb/ov511.txt	Sat Nov 11 20:50:28 2000
+++ linux/Documentation/usb/ov511.txt	Sat Nov 11 22:32:35 2000
@@ -5,22 +5,17 @@
 Author: Mark McClelland
 Homepage: http://alpha.dyndns.org/ov511
 
-NEW IN THIS VERSION:
- o Stability improvements
- o Support for hue control
- o 160x120 mostly working
- o OV6620 color problems fixed
- o More WebCam 3 detection 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 
 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
+API. Most V4L apps are compatible with it, but a few video-conferencing programs
 do not work yet. The following resolutions are supported: 640x480, 448x336,
 384x288, 352x288, and 320x240.
 
+If you need more information, please visit the OV511 homepage at the above URL.
+
 WHAT YOU NEED:
 
 - If you want to help with the development, get the chip's specification docs at
@@ -81,29 +76,6 @@
 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. Also try increasing the i2c_detect_tries
-   parameter (see below).
-
-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
@@ -136,7 +108,7 @@
         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. You will likely need a fast (500 Mhz) CPU.
+        experimental and very buggy. You will likely need a fast (500 MHz) CPU.
 
   NAME: snapshot
   TYPE: integer (boolean)
@@ -203,15 +175,21 @@
   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.
- 
+
+  NAME: sensor_gbr
+  TYPE: boolean
+  DEFAULT: 0
+  DESC: This makes the sensor output GBR422 instead of YUV420. This saves the
+        driver the trouble of converting YUV to RGB, but it currently does not
+        work very well (the colors are not quite right)
+
 WORKING FEATURES:
- o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, 320x240, and
-   160x120
- o RGB24, YUV420, YUV422, YUYV, and YUV422P color
+ o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
+ o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
  o Monochrome
  o Setting/getting of saturation, contrast, brightness, and hue (only some of
    them work the OV7620 and OV7620AE)
- o proc status reporting
+ o /proc status reporting
 
 EXPERIMENTAL FEATURES:
  o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
@@ -219,6 +197,7 @@
  o Snapshot mode (only works with some read() based apps; see below for more)
  o OV6620 sensor support
  o GBR422 parsing
+ o 160x120
 
 TODO:
  o Fix the noise / grainy image problem.
@@ -227,18 +206,19 @@
    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 YUV422
- o Get snapshot mode working with mmap().
  o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
- o Get autoadjust disable working
  o V4L2 support (Probably not until it goes into the kernel)
- 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/7620AE
  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
+ o 176x144 support
+ o Driver sometimes hangs upon close() with OHCI
+ o The image should always be written properly to the mmap'ed buffer as long as
+   the requested image size is at least the minimum size. This will likely
+   require a rewrite of all the parsing code.
 
 HOW TO CONTACT ME:
 
diff -Naur linux-2.4.0-test11-pre3-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.4.0-test11-pre3-orig/drivers/usb/ov511.c	Sat Nov 11 20:50:14 2000
+++ linux/drivers/usb/ov511.c	Sat Nov 11 22:29:39 2000
@@ -30,7 +30,7 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-static const char version[] = "1.25";
+static const char version[] = "1.28";
 
 #define __NO_VERSION__
 
@@ -51,23 +51,6 @@
 
 #include "ov511.h"
 
-#undef OV511_GBR422		/* Experimental -- sets the 7610 to GBR422 */
-
-static struct usb_device_id ov511_table [] = {
-
-    { idVendor: 0x05a9, idProduct: 0x0511, bInterfaceClass: 0xFF },
-    { idVendor: 0x05a9, idProduct: 0xA511, bInterfaceClass: 0xFF },
-    { idVendor: 0x05a9, idProduct: 0x0002, bInterfaceClass: 0xFF },
-
-    { idVendor: 0x0813, idProduct: 0x0511, bInterfaceClass: 0xFF },
-    { idVendor: 0x0813, idProduct: 0xA511, bInterfaceClass: 0xFF },
-
-    { }						/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, ov511_table);
-
-
 #define OV511_I2C_RETRIES 3
 
 /* Video Size 640 x 480 x 3 bytes for RGB */
@@ -75,8 +58,6 @@
 #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
 
 #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
-#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... */
@@ -89,7 +70,7 @@
  * 5=highly repetitive mesgs
  * NOTE: This should be changed to 0, 1, or 2 for production kernels
  */
-static int debug = 3;
+static int debug = 0;
 
 /* Fix vertical misalignment of red and blue at 640x480 */
 static int fix_rgb_offset = 0;
@@ -127,6 +108,13 @@
 /* Display test pattern - doesn't work yet either */
 static int testpat = 0;
 
+/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only
+ * affects RGB24 mode. */
+static int sensor_gbr = 0;
+
+/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
+static int dumppix = 0;
+
 MODULE_PARM(autoadjust, "i");
 MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
 MODULE_PARM(debug, "i");
@@ -153,6 +141,10 @@
 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_PARM(sensor_gbr, "i");
+MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+MODULE_PARM(dumppix, "i");
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details");
 
 MODULE_AUTHOR("Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>");
 MODULE_DESCRIPTION("OV511 USB Camera Driver");
@@ -182,6 +174,15 @@
 	{  -1, NULL }
 };
 
+static __devinitdata struct usb_device_id device_table [] = {
+	{ idVendor: 0x05a9, idProduct: 0x0511 },  /* OV511 */
+	{ idVendor: 0x05a9, idProduct: 0xA511 },  /* OV511+ */
+	{ idVendor: 0x0813, idProduct: 0x0002 },  /* Intel Play Me2Cam OV511+ */
+	{ }  /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, device_table);
+
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 static struct palette_list plist[] = {
 	{ VIDEO_PALETTE_GREY,	"GREY" },
@@ -372,6 +373,7 @@
 		ov511->bridge == BRG_OV511PLUS ? "OV511+" :
 		"unknown");
 	out += sprintf (out, "sensor          : %s\n",
+		ov511->sensor == SEN_OV6620 ? "OV6620" :
 		ov511->sensor == SEN_OV7610 ? "OV7610" :
 		ov511->sensor == SEN_OV7620 ? "OV7620" :
 		ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
@@ -463,7 +465,6 @@
 }
 #endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
 
-
 /**********************************************************************
  *
  * Camera interface
@@ -715,7 +716,7 @@
 {
 	int rc;
 	
-	PDEBUG(3, "Reset: type=0x%X", reset_type);
+	PDEBUG(4, "Reset: type=0x%X", reset_type);
 	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
 	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
 
@@ -844,14 +845,21 @@
 	} else if ((ov511->sensor == SEN_OV7620) 
 	         || (ov511->sensor == SEN_OV7620AE)) {
 #if 0
-		cur_con = ov511_i2c_read(dev, OV7610_REG_CNT);
-		cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT);
-	        // DEBUG_CODE
-	        PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
-	         ov511_i2c_read(dev, OV7610_REG_BRT)); 
+		int cur_sat, new_sat, tmp;
 
-		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+		cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE);
+
+		tmp = (p->hue >> 8) - cur_sat;
+		new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp;
+
+	        PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); 
+
+		if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0)
 			return -EIO;
+
+	        // DEBUG_CODE
+	        PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); 
+
 #endif
 	}
 
@@ -897,6 +905,23 @@
 	return 0;
 }
 
+/* Returns number of bits per pixel (regardless of where they are located; planar or
+ * not), or zero for unsupported format.
+ */
+static int ov511_get_depth(int palette)
+{
+	switch (palette) {
+	case VIDEO_PALETTE_GREY:    return 8;
+	case VIDEO_PALETTE_RGB565:  return 16;
+	case VIDEO_PALETTE_RGB24:   return 24;  
+	case VIDEO_PALETTE_YUV422:  return 16;
+	case VIDEO_PALETTE_YUYV:    return 16;
+	case VIDEO_PALETTE_YUV420:  return 24;
+	case VIDEO_PALETTE_YUV422P: return 24; /* Planar */
+	default:		    return 0;  /* Invalid format */
+	}
+}
+
 /* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
 static struct mode_list mlist[] = {
 	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COML */
@@ -932,6 +957,12 @@
 	if (ov511_stop(ov511->dev) < 0)
 		return -EIO;
 
+	/* Dumppix only works with RGB24 */
+	if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
+		err("dumppix only supported with RGB 24");
+		return -EINVAL;
+	}
+
 	if (mode == VIDEO_PALETTE_GREY) {
 		ov511_reg_write(dev, 0x16, 0x00);
 		if (ov511->sensor == SEN_OV7610
@@ -968,9 +999,9 @@
 		break;
 	case SEN_OV6620:
 		hwsbase = 0x38;
-		hwebase = 0x39;
-		vwsbase = 0x03;
-		vwebase = 0x04;
+		hwebase = 0x3a;
+		vwsbase = 0x05;
+		vwebase = 0x06;
 		break;
 	case SEN_OV7620:
 		hwsbase = 0x2c;
@@ -1054,13 +1085,16 @@
 		/* Calculate and set the clock divisor */
 		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
 			* (mlist[i].color ? 3 : 2) / 2) / 66000;
+#if 0
+		clock *= cams;
+#endif
 		ov511_i2c_write(dev, 0x11, clock);
 
-#ifdef OV511_GBR422
-		ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08);
-#else
-		ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
-#endif
+		/* We only have code to convert GBR -> RGB24 */
+		if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
+			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08));
+		else
+			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
 
 		/* 7620/6620 don't have register 0x35, so play it safe */
 		if (ov511->sensor == SEN_OV7610 ||
@@ -1128,7 +1162,7 @@
 
 static inline void
 ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, 
-	int rowPixels, unsigned char * rgb)
+	int rowPixels, unsigned char * rgb, int bits)
 {
 	const int rvScale = 91881;
 	const int guScale = -22553;
@@ -1149,14 +1183,32 @@
 	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);
-
-	/* Skip down to next line to write out bottom two pixels */
-	rgb += 3 * rowPixels;
-	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);
+	if (bits == 24) {
+		/* 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);
+
+		/* Skip down to next line to write out bottom two pixels */
+		rgb += 3 * rowPixels;
+		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);
+	} else if (bits == 16) {
+		/* Write out top two pixels */
+		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8);
+
+		/* Skip down to next line to write out bottom two pixels */
+		rgb += 2 * rowPixels;
+
+		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8);
+	}
 }
 
 /*
@@ -1182,7 +1234,7 @@
  * 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,
+ * If dumppix module param is set, _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 on the standard output and can be analyzed with the
  * parseppm.c utility I wrote.  That's a much faster way for figuring out how
@@ -1192,14 +1244,8 @@
 #define HDIV 8
 #define WDIV (256/HDIV)
 
-#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,
+ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0,
 		       int iOutY, int iOutUV, int iHalf, int iWidth)
 {
 	int k, l, m;
@@ -1246,14 +1292,12 @@
 	}
 }
 
-#else
-
 static void
-ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth)
+ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0,
+		       int iOutY, int iOutUV, int iHalf, int iWidth, int bits)
 {
-#ifndef OV511_DUMPPIX
 	int k, l, m;
+	int bytes = bits >> 3;
 	unsigned char *pIn;
 	unsigned char *pOut, *pOut1;
 
@@ -1266,11 +1310,11 @@
 			for (l = 0; l < 8; l++) {
 				for (m = 0; m < 8; m++) {
 					*pOut1 = *pIn++;
-					pOut1 += 3;
+					pOut1 += bytes;
 				}
-				pOut1 += (iWidth - 8) * 3;
+				pOut1 += (iWidth - 8) * bytes;
 			}
-			pOut += 8 * 3;
+			pOut += 8 * bytes;
 		}
 	}
 
@@ -1280,16 +1324,16 @@
 	for (l = 0; l < 4; l++) {
 		for (m=0; m<8; m++) {
 			int y00 = *(pOut);
-			int y01 = *(pOut+3);
-			int y10 = *(pOut+iWidth*3);
-			int y11 = *(pOut+iWidth*3+3);
+			int y01 = *(pOut+bytes);
+			int y10 = *(pOut+iWidth*bytes);
+			int y11 = *(pOut+iWidth*bytes+bytes);
 			int v   = *(pIn+64) - 128;
 			int u   = *pIn++ - 128;
 			ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
-				pOut);
-			pOut += 6;
+				pOut, bits);
+			pOut += 2 * bytes;
 		}
-		pOut += (iWidth*2 - 16) * 3;
+		pOut += (iWidth*2 - 16) * bytes;
 	}
 
 	/* Just copy the other UV rows */
@@ -1297,9 +1341,9 @@
 		for (m = 0; m < 8; m++) {
 			*pOut++ = *(pIn + 64);
 			*pOut = *pIn++;
-			pOut += 5;
+			pOut += 2 * bytes - 1;
 		}
-		pOut += (iWidth*2 - 16) * 3;
+		pOut += (iWidth*2 - 16) * bytes;
 	}
 
 	/* Calculate values if it's the second half */
@@ -1317,72 +1361,65 @@
 					int v   = *pOut1 - 128;
 					int u   = *(pOut1+1) - 128;
 					ov511_move_420_block(y00, y01, y10,
-						y11, u, v, iWidth, pOut1);
-					pOut1 += 6;
+						y11, u, v, iWidth, pOut1, bits);
+					pOut1 += 2 * bytes;
 				}
-				pOut1 += (iWidth*2 - 8) * 3;
+				pOut1 += (iWidth*2 - 8) * bytes;
 				pIn += 8;
 			}
-			pOut += 8 * 3;
+			pOut += 8 * bytes;
 		}
 	}
-#else
+}
 
-#ifndef OV511_FORCE_MONO
-	/* Just dump pix data straight out for debug */
-	int i, j;
-
-	pOut0 += iOutY;
-	for (i = 0; i < HDIV; i++) {
-		for (j = 0; j < WDIV; j++) {
-			*pOut0++ = *pIn0++;
-			*pOut0++ = *pIn0++;
-			*pOut0++ = *pIn0++;
-		}
-		pOut0 += (iWidth - WDIV) * 3;
-	}
-#else
-
-#if 1
-	/* This converts the Y data to "black-and-white" RGB data */
-	/* Useful for experimenting with compression */
-	int k, l, m;
+static void
+ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0,
+	      int iOutY, int iOutUV, int iHalf, int iWidth)
+{
+	int i, j, k;
 	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++;
+	switch (dumppix) {
+	case 1: /* Just dump YUV data straight out for debug */
+		pOut0 += iOutY;
+		for (i = 0; i < HDIV; i++) {
+			for (j = 0; j < WDIV; j++) {
+				*pOut0++ = *pIn0++;
+				*pOut0++ = *pIn0++;
+				*pOut0++ = *pIn0++;
 			}
-			pOut1 += (iWidth - 8) * 3;
+			pOut0 += (iWidth - WDIV) * 3;
 		}
-		pOut += 8 * 3;
-	}
-#else
-	/* This will dump the Y channel data stream as-is */ 
-	int count;
-	unsigned char *pIn, *pOut;
-
-	pIn = pIn0 + 128;
-	pOut = pOut0 + output_offset;
-	for (count = 0; count < 256; count++) {
-		*pOut++ = *pIn;
-		*pOut++ = *pIn;
-		*pOut++ = *pIn++;
-		output_offset += 3;
-	}	
-#endif
-
-#endif
-
-#endif
+		break;
+	case 2: /* This converts the Y data to "black-and-white" RGB data */
+		/* Useful for experimenting with compression */
+		pIn = pIn0 + 128;
+		pOut = pOut0 + iOutY;
+		for (i = 0; i < 4; i++) {
+			pOut1 = pOut;
+			for (j = 0; j < 8; j++) {
+				for (k = 0; k < 8; k++) {
+					*pOut1++ = *pIn;
+					*pOut1++ = *pIn;
+					*pOut1++ = *pIn++;
+				}
+				pOut1 += (iWidth - 8) * 3;
+			}
+			pOut += 8 * 3;
+		}
+		break;
+	case 3: /* This will dump only the Y channel data stream as-is */
+		pIn = pIn0 + 128;
+		pOut = pOut0 + output_offset;
+		for (i = 0; i < 256; i++) {
+			*pOut++ = *pIn;
+			*pOut++ = *pIn;
+			*pOut++ = *pIn++;
+			output_offset += 3;
+		}
+		break;
+	} /* End switch (dumppix) */
 }
-#endif
 
 /* This converts YUV420 segments to YUYV */
 static void
@@ -1677,6 +1714,11 @@
 			/* Frame start */
 			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
+#if 0
+			/* Make sure no previous data carries over; necessary
+			 * for compression experimentation */
+			memset(frame->data, 0, MAX_DATA_SIZE);
+#endif
 			output_offset = 0;
 
 			/* Check to see if it's a snapshot frame */
@@ -1755,8 +1797,19 @@
 				ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
 				break;
 			case VIDEO_PALETTE_RGB24:
-				ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
-					iY & 1, frame->width);
+				if (dumppix)
+					ov511_dumppix(pData, pOut, iOutY, iOutUV,
+						iY & 1, frame->width);
+				else if (sensor_gbr)
+					ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV,
+						iY & 1, frame->width);
+				else
+					ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
+						iY & 1, frame->width, 24);
+				break;
+			case VIDEO_PALETTE_RGB565:
+				ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
+					iY & 1, frame->width, 16);
 				break;
 			case VIDEO_PALETTE_YUV422:
 			case VIDEO_PALETTE_YUYV:
@@ -1797,11 +1850,20 @@
 static void ov511_isoc_irq(struct urb *urb)
 {
 	int len;
-	struct usb_ov511 *ov511 = urb->context;
+	struct usb_ov511 *ov511;
 	struct ov511_sbuf *sbuf;
 
-	if (!ov511->dev)
+	if (!urb->context) {
+		PDEBUG(4, "no context");
 		return;
+	}
+
+	ov511 = (struct usb_ov511 *) urb->context;
+
+	if (!ov511->dev || !ov511->user) {
+		PDEBUG(4, "no device, or not open");
+		return;
+	}
 
 	if (!ov511->streaming) {
 		PDEBUG(4, "hmmm... not streaming, but got interrupt");
@@ -1820,6 +1882,8 @@
 	/* Move to the next sbuf */
 	ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
 
+	urb->dev = ov511->dev;
+
 	return;
 }
 
@@ -1887,6 +1951,7 @@
 		ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
 
 	for (n = 0; n < OV511_NUMSBUF; n++) {
+		ov511->sbuf[n].urb->dev = ov511->dev;
 		err = usb_submit_urb(ov511->sbuf[n].urb);
 		if (err)
 			err("init isoc: usb_submit_urb(%d) ret %d", n, err);
@@ -2094,7 +2159,7 @@
 static int ov511_open(struct video_device *dev, int flags)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int err = 0;
+	int err;
 
 	MOD_INC_USE_COUNT;
 	PDEBUG(4, "opening");
@@ -2189,8 +2254,8 @@
 		b.audios = 0;
 		b.maxwidth = ov511->maxwidth;
 		b.maxheight = ov511->maxheight;
-		b.minwidth = 32;
-		b.minheight = 16;
+		b.minwidth = 160;
+		b.minheight = 120;
 
 		if (copy_to_user(arg, &b, sizeof(b)))
 			return -EFAULT;
@@ -2252,12 +2317,7 @@
 		if (copy_from_user(&p, arg, sizeof(p)))
 			return -EFAULT;
 
-		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)
+		if (!ov511_get_depth(p.palette))
 			return -EINVAL;
 			
 		if (ov7610_set_picture(ov511, &p))
@@ -2388,7 +2448,7 @@
 	case VIDIOCMCAPTURE:
 	{
 		struct video_mmap vm;
-		int ret;
+		int ret, depth;
 
 		if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
 			return -EFAULT;
@@ -2397,19 +2457,21 @@
 		PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
 			vm.frame, vm.width, vm.height, vm.format);
 
-		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)
+		depth = ov511_get_depth(vm.format);
+		if (!depth) {
+			err("VIDIOCMCAPTURE: invalid format (%d)", vm.format);
 			return -EINVAL;
+		}
 
-		if ((vm.frame != 0) && (vm.frame != 1))
+		if ((vm.frame != 0) && (vm.frame != 1)) {
+			err("VIDIOCMCAPTURE: invalid frame (%d)", vm.frame);
 			return -EINVAL;
-				
-		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight)
+		}
+
+		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) {
+			err("VIDIOCMCAPTURE: requested dimensions too big");
 			return -EINVAL;
+		}
 
 		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
 			return -EBUSY;
@@ -2437,7 +2499,7 @@
 		ov511->frame[vm.frame].format = vm.format;
 		ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
 		ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
-		ov511->frame[vm.frame].depth = GET_DEPTH(vm.format);
+		ov511->frame[vm.frame].depth = depth;
 
 		/* Mark it as ready */
 		ov511->frame[vm.frame].grabstate = FRAME_READY;
@@ -2496,6 +2558,13 @@
 				goto redo;
 			}			
 		case FRAME_DONE:
+			if (ov511->snap_enabled && !ov511->frame[frame].snapshot) {
+				int ret;
+				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+					return ret;
+				goto redo;
+			}
+
 			ov511->frame[frame].grabstate = FRAME_UNUSED;
 
 			/* Reset the hardware snapshot button */
@@ -2672,7 +2741,7 @@
 
 	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
 
-	if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
 		return -EINVAL;
 
 	pos = (unsigned long)ov511->fbuf;
@@ -3072,7 +3141,7 @@
 
 	init_waitqueue_head(&ov511->wq);
 
-	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) {
+	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) {
 		err("video_register_device failed");
 		return -EBUSY;
 	}
@@ -3159,8 +3228,9 @@
  *
  ***************************************************************************/
 
-static void* ov511_probe(struct usb_device *dev, unsigned int ifnum,
-			 const struct usb_device_id *id)
+static void * __devinit
+ov511_probe(struct usb_device *dev, unsigned int ifnum,
+	const struct usb_device_id *id)
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_ov511 *ov511;
@@ -3174,9 +3244,9 @@
 
 	interface = &dev->actconfig->interface[ifnum].altsetting[0];
 
-	/* Checking vendor/product should be enough, but what the hell.
-	   USB ID matching cannot check for a bInterfaceSubclass value
-	   of 0, so we check it here. */
+	/* Checking vendor/product should be enough, but what the hell */
+	if (interface->bInterfaceClass != 0xFF)
+		return NULL;
 	if (interface->bInterfaceSubClass != 0x00)
 		return NULL;
 
@@ -3203,9 +3273,14 @@
 		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);
@@ -3265,7 +3340,8 @@
 }
 
 
-static void ov511_disconnect(struct usb_device *dev, void *ptr)
+static void __devexit 
+ov511_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
 	int n;
@@ -3324,9 +3400,9 @@
 
 static struct usb_driver ov511_driver = {
 	name:		"ov511",
+	id_table:       device_table,
 	probe:		ov511_probe,
-	disconnect:	ov511_disconnect,
-	id_table:	ov511_table,
+	disconnect:	ov511_disconnect
 };
 
 
