diff -Naur linux-2.4.1-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.4.1-orig/Documentation/usb/ov511.txt	Wed Feb  7 01:12:10 2001
+++ linux/Documentation/usb/ov511.txt	Wed Feb  7 01:15:00 2001
@@ -8,7 +8,7 @@
 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 OV6620/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 video-conferencing programs
 do not work yet. The following resolutions are supported: 640x480, 448x336,
@@ -29,14 +29,14 @@
 
 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.) Make sure "Enforce bandwidth allocation" is NOT enabled.
 
-Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
+Next, (as root):
 
-	insmod usb/usbcore.o
-	insmod usb/usb-uhci.o  <OR>  insmod usb/ohci-hcd.o
-	insmod misc/videodev.o
-	insmod usb/ov511.o
+	modprobe usbcore
+	modprobe usb-uhci  <OR>  modprobe usb-ohci
+	modprobe videodev
+	modprobe ov511
 
 If it is not already there (it usually is), create the video device:
 
@@ -60,21 +60,14 @@
 	
 [Using xawtv:]
 
-You must make some modifications to the source and compile it before you use it.
-(Note: this may not be applicable to versions other than 3.06)
-
-In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next,
-in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv
-directory:
+From the main xawtv directory:
 
 	make clean
 	./configure
 	make
 	make install
 
-Now you should be able to run xawtv. Right click for the options dialog. If
-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.
+Now you should be able to run xawtv. Right click for the options dialog. 
 
 MODULE PARAMETERS:
 
@@ -82,7 +75,7 @@
   There is currently no way to set these on a per-camera basis.
 
   NAME: autoadjust
-  TYPE: integer (boolean)
+  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
@@ -102,7 +95,7 @@
           5=highly repetitive mesgs
 
   NAME: fix_rgb_offset
-  TYPE: integer (boolean)
+  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 
@@ -111,21 +104,30 @@
         experimental and very buggy. You will likely need a fast (500 MHz) CPU.
 
   NAME: snapshot
-  TYPE: integer (boolean)
+  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.
 
   NAME: sensor
-  TYPE: integer ([0, 1, 3])
-  DEFAULT: [varies]
+  TYPE: integer (0 - 7)
+  DEFAULT: Initially zero, varies after detection
   DESC: If you know that your camera sensor is not detected correctly, set this
         parameter. This is a global option for all attached OV511 cameras. You
         will probably never need to set this, but if you do, valid values are:
-        	0 for OV7620
-        	1 for OV7620AE
-        	3 for OV7610
+		0 for autodetect
+		1 for OV7610
+		2 for OV7620
+		3 for OV7620AE
+		4 for OV6620
+		5 for OV6630
+		6 for OV8600 (unsupported)
+		7 for KS0127 (unsupported)
+
+	Note that you cannot set an OV6xxx series sensor to detect as an OV7xxx
+	series one or vice versa, since they use different communication
+	addresses.
 
   NAME: i2c_detect_tries
   TYPE: integer (don't set it insanely high!)
@@ -144,7 +146,7 @@
         for if you want to play with the camera's pixel saturation.
 
   NAME: force_rgb
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Force image to be read in RGB instead of BGR. This option allow
         programs that expect RGB data (e.g. gqcam) to work with this driver. If
@@ -170,19 +172,53 @@
         finding the optimum setting.
 
   NAME: retry_sync
-  TYPE: boolean
+  TYPE: integer (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.
 
   NAME: sensor_gbr
-  TYPE: boolean
+  TYPE: integer (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)
 
+  NAME: dumppix
+  TYPE: integer (0-3)
+  DEFAULT: 0
+  DESC: Dumps raw pixel data, in one of three formats. Only works with RGB24 
+	format, and is for debugging purposes only. Options are:
+	0: Disable (default)
+	1: Dump formatted YUV data. One RGB24 pixel per data byte.
+	2: Convert Y data to formatted "monochrome" RGB data
+	3: Dumps the Y channel as-is. One RGB24 pixel per data byte.
+	4: Dumps unformatted YUV data as-is.
+
+  NAME: led
+  TYPE: integer (0-2)
+  DEFAULT: 1 (Always on)
+  DESC: Controls whether the LED (the little light) on the front of the camera
+	is always off (0), always on (1), or only on when driver is open (2).
+	This is only supported with the OV511+ chipset, and even then only on
+	some cameras (ones that actually have the LED wired to the control pin,
+	and not just hardwired to be on all the time).
+
+  NAME: dump_regs
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the camera (OV511) and i2c (sensor) register values to the system
+	log. Only useful for serious debugging/development purposes.
+
+  NAME: printph
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This
+	is only useful if you are trying to debug problems with the isoc data
+	stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be
+	warned that this dumps a large number of messages to your kernel log.
+
 WORKING FEATURES:
  o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
  o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
@@ -190,35 +226,38 @@
  o Setting/getting of saturation, contrast, brightness, and hue (only some of
    them work the OV7620 and OV7620AE)
  o /proc status reporting
+ o OV6620 sensor support
 
 EXPERIMENTAL FEATURES:
  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 OV6620 sensor support
+ o Snapshot mode
  o GBR422 parsing
  o 160x120
+ o OV6630 sensor support
 
-TODO:
+TO-DO:
  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 compression working.
  o YUV422
  o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
  o V4L2 support (Probably not until it goes into the kernel)
  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 Setting of color and hue not working with OV7620
+ o Setting of contrast and hue not working with OV7620AE
  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.
+ o OV8600 sensor support (Not used in anything yet)
+ o OV518+ support
+ o cams >= 3 not working
+ o Some cameras are sending all zeros for isochronous (image) data.
+ o Small image sizes need to be supported for apps such as ohphone.
 
 HOW TO CONTACT ME:
 
@@ -232,3 +271,4 @@
 and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and
 image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio
 Matsuoka for their work as well.
+
diff -Naur linux-2.4.1-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.4.1-orig/drivers/usb/ov511.c	Wed Feb  7 01:11:50 2001
+++ linux/drivers/usb/ov511.c	Wed Feb  7 01:14:51 2001
@@ -30,7 +30,7 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-static const char version[] = "1.28";
+static const char version[] = "1.32";
 
 #define __NO_VERSION__
 
@@ -52,11 +52,16 @@
 #include "ov511.h"
 
 #define OV511_I2C_RETRIES 3
+#define ENABLE_Y_QUANTABLE 1
+#define ENABLE_UV_QUANTABLE 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))
 
+/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
+#define MAX_RAW_DATA_SIZE (((640 * 480 * 3) / 2) + 1024)
+
 #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
 
 /* PARAMETER VARIABLES: */
@@ -115,6 +120,19 @@
 /* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
 static int dumppix = 0;
 
+/* LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto
+ * (on when open) */
+static int led = 1;
+
+/* Set this to 1 to dump the bridge and sensor register contents after
+ * initialization */
+static int dump_regs = 0;
+
+/* Temporary option for debugging "works, but no image" problem. Prints the
+ * first 12 bytes of data (potentially a packet header) in each isochronous
+ * data frame. */
+static int printph = 0;
+
 MODULE_PARM(autoadjust, "i");
 MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
 MODULE_PARM(debug, "i");
@@ -145,29 +163,32 @@
 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_PARM(led, "i");
+MODULE_PARM_DESC(led, "Sets LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto (on when open)");
+MODULE_PARM(dump_regs, "i");
+MODULE_PARM_DESC(dump_regs, "Dump the bridge and sensor registers");
+MODULE_PARM(printph, "i");
+MODULE_PARM_DESC(printph, "Print the (potential) packet header of each isoc frame");
 
 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");
 
-char kernel_version[] = UTS_RELEASE;
-
 static struct usb_driver ov511_driver;
 
-/* I know, I know, global variables suck. This is only a temporary hack */
-int output_offset;
-
 /**********************************************************************
  * List of known OV511-based cameras
  **********************************************************************/
 
 static struct cam_list clist[] = {
 	{   0, "generic model (no ID)" },
+	{   1, "Mustek WCam 3X" },
 	{   3, "D-Link DSB-C300" },
 	{   4, "generic OV511/OV7610" },
 	{   5, "Puretek PT-6007" },
 	{  21, "Creative Labs WebCam 3" },
 	{  36, "Koala-Cam" },
 	{  38, "Lifeview USB Life TV" },	/* No support yet! */
+	{  43, "Mtekvision Zeca MV402" },
 	{ 100, "Lifeview RoboCam" },
 	{ 102, "AverMedia InterCam Elite" },
 	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
@@ -177,6 +198,7 @@
 static __devinitdata struct usb_device_id device_table [] = {
 	{ USB_DEVICE(0x05a9, 0x0511) },  /* OV511 */
 	{ USB_DEVICE(0x05a9, 0xA511) },  /* OV511+ */
+	{ USB_DEVICE(0x05a9, 0x0518) },  /* OV518+ */
 	{ USB_DEVICE(0x0813, 0x0002) },  /* Intel Play Me2Cam OV511+ */
 	{ }  /* Terminating entry */
 };
@@ -205,6 +227,9 @@
 };
 #endif
 
+static unsigned char yQuanTable[] = YQUANTABLE;
+static unsigned char uvQuanTable[] = UVQUANTABLE;
+
 /**********************************************************************
  *
  * Memory management
@@ -346,6 +371,7 @@
 	out += sprintf (out, "brightness      : %d\n", ov511->brightness >> 8);
 	out += sprintf (out, "colour          : %d\n", ov511->colour >> 8);
 	out += sprintf (out, "contrast        : %d\n", ov511->contrast >> 8);
+	out += sprintf (out, "hue             : %d\n", ov511->hue >> 8);
 	out += sprintf (out, "num_frames      : %d\n", OV511_NUMFRAMES);
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
 		out += sprintf (out, "frame           : %d\n", i);
@@ -371,12 +397,16 @@
 	out += sprintf (out, "bridge          : %s\n",
 		ov511->bridge == BRG_OV511 ? "OV511" :
 		ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+		ov511->bridge == BRG_OV518PLUS ? "OV518+" :
 		"unknown");
 	out += sprintf (out, "sensor          : %s\n",
 		ov511->sensor == SEN_OV6620 ? "OV6620" :
+		ov511->sensor == SEN_OV6630 ? "OV6630" :
 		ov511->sensor == SEN_OV7610 ? "OV7610" :
 		ov511->sensor == SEN_OV7620 ? "OV7620" :
 		ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
+		ov511->sensor == SEN_OV8600 ? "OV8600" :
+		ov511->sensor == SEN_KS0127 ? "KS0127" :
 		"unknown");
 	out += sprintf (out, "packet_size     : %d\n", ov511->packet_size);
 	out += sprintf (out, "framebuffer     : 0x%p\n", ov511->fbuf);
@@ -477,14 +507,14 @@
 {
 	int rc;
 
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
 	rc = usb_control_msg(dev,
 		usb_sndctrlpipe(dev, 0),
 		2 /* REG_IO */,
 		USB_TYPE_CLASS | USB_RECIP_DEVICE,
 		0, (__u16)reg, &value, 1, HZ);	
 
-	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
-
 	if (rc < 0)
 		err("reg write: error %d", rc);
 
@@ -503,7 +533,7 @@
 		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
 		0, (__u16)reg, buffer, 1, HZ);
                                
-	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
+	PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]);
 	
 	if (rc < 0) {
 		err("reg read: error %d", rc);
@@ -513,13 +543,141 @@
 	}
 }
 
+/*
+ * Writes bits at positions specified by mask to a reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int ov511_reg_write_mask(struct usb_device *dev,
+				unsigned char reg,
+				unsigned char value,
+				unsigned char mask)
+{
+	int ret;
+	unsigned char oldval, newval;
+
+	ret = ov511_reg_read(dev, reg);
+	if (ret < 0)
+		return ret;
+
+	oldval = (unsigned char) ret;
+	oldval &= (~mask);		/* Clear the masked bits */
+	value &= mask;			/* Enforce mask on value */
+	newval = oldval | value;	/* Set the desired bits */
+
+	return (ov511_reg_write(dev, reg, newval));
+}
+
+/* Writes multiple (n) values to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce). I don't know yet what this really does */
+static int ov518_reg_write_multi(struct usb_device *dev,
+				unsigned char reg,
+				unsigned char *values,
+				int n)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:[multiple], n=%d", reg, n);  // FIXME
+
+	if (values == NULL) {
+		err("reg write multiple: NULL buffer");
+		return -EINVAL;
+	}
+
+	rc = usb_control_msg(dev,
+		usb_sndctrlpipe(dev, 0),
+		2 /* REG_IO */,
+		USB_TYPE_CLASS | USB_RECIP_DEVICE,
+		0, (__u16)reg, values, n, HZ);	
+
+	if (rc < 0)
+		err("reg write multiple: error %d", rc);
+
+	return rc;
+}
+
+static int ov511_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable;
+	unsigned char *pUVTable = uvQuanTable;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	for(i = 0; i < QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + QUANTABLESIZE / 2, val0);
+			if (rc)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov511_i2c_write(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int ov518_i2c_write(struct usb_device *dev,
+			   unsigned char reg,
+			   unsigned char value)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Write "value" to I2C data port of OV511 */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value);
+	if (rc < 0) goto error;
+
+	/* Initiate 3-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x01);
+	if (rc < 0) goto error;
+
+	return 0;
+
+error:
+	err("ov518 i2c write: error %d", rc);
+	return rc;
+}
+
 static int ov511_i2c_write(struct usb_device *dev,
 			   unsigned char reg,
 			   unsigned char value)
 {
 	int rc, retries;
 
-	PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+	/* OV518 needs a different write algorithm */
+	if (dev->descriptor.idProduct == 0x0518)
+		return (ov518_i2c_write(dev, reg, value));
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
 	/* Three byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
@@ -559,11 +717,46 @@
 	return rc;
 }
 
+/* The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov511_i2c_read(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int ov518_i2c_read(struct usb_device *dev, unsigned char reg)
+{
+	int rc, value;
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x03);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte read cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x05);
+	if (rc < 0) goto error;
+
+	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	return value;
+
+error:
+	err("ov518 i2c read: error %d", rc);
+	return rc;
+}
+
 /* returns: negative is error, pos or zero is data */
 static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
 {
 	int rc, value, retries;
 
+	/* OV518 needs a different read algorithm */
+	if (dev->descriptor.idProduct == 0x0518)
+		return (ov518_i2c_read(dev, reg));
+
 	/* Two byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
@@ -617,7 +810,7 @@
 
 	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
 
-	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "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);
@@ -631,6 +824,31 @@
 	return rc;
 }
 
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int ov511_i2c_write_mask(struct usb_device *dev,
+				unsigned char reg,
+				unsigned char value,
+				unsigned char mask)
+{
+	int ret;
+	unsigned char oldval, newval;
+
+	ret = ov511_i2c_read(dev, reg);
+	if (ret < 0)
+		return ret;
+
+	oldval = (unsigned char) ret;
+	oldval &= (~mask);		/* Clear the masked bits */
+	value &= mask;			/* Enforce mask on value */
+	newval = oldval | value;	/* Set the desired bits */
+
+	return (ov511_i2c_write(dev, reg, newval));
+}
+
 static int ov511_write_regvals(struct usb_device *dev,
 			       struct ov511_regvals * pRegvals)
 {
@@ -666,59 +884,62 @@
 	int rc;
 	for(i = reg1; i <= regn; i++) {
 		rc = ov511_i2c_read(dev, i);
-		PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
+		info("OV7610[0x%X] = 0x%X", i, rc);
 	}
 }
 
 static void ov511_dump_i2c_regs(struct usb_device *dev)
 {
-	PDEBUG(3, "I2C REGS");
+	info("I2C REGS");
 	ov511_dump_i2c_range(dev, 0x00, 0x7C);
 }
 
-#if 0
 static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
 {
 	int i;
 	int rc;
 	for(i = reg1; i <= regn; i++) {
 	  rc = ov511_reg_read(dev, i);
-	  PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
+	  info("OV511[0x%X] = 0x%X", i, rc);
 	}
 }
 
 static void ov511_dump_regs(struct usb_device *dev)
 {
-	PDEBUG(1, "CAMERA INTERFACE REGS");
+	info("CAMERA INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x10, 0x1f);
-	PDEBUG(1, "DRAM INTERFACE REGS");
+	info("DRAM INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x20, 0x23);
-	PDEBUG(1, "ISO FIFO REGS");
+	info("ISO FIFO REGS");
 	ov511_dump_reg_range(dev, 0x30, 0x31);
-	PDEBUG(1, "PIO REGS");
+	info("PIO REGS");
 	ov511_dump_reg_range(dev, 0x38, 0x39);
 	ov511_dump_reg_range(dev, 0x3e, 0x3e);
-	PDEBUG(1, "I2C REGS");
+	info("I2C REGS");
 	ov511_dump_reg_range(dev, 0x40, 0x49);
-	PDEBUG(1, "SYSTEM CONTROL REGS");
+	info("SYSTEM CONTROL REGS");
 	ov511_dump_reg_range(dev, 0x50, 0x55);
 	ov511_dump_reg_range(dev, 0x5e, 0x5f);
-	PDEBUG(1, "OmniCE REGS");
+	info("OmniCE REGS");
 	ov511_dump_reg_range(dev, 0x70, 0x79);
 	ov511_dump_reg_range(dev, 0x80, 0x9f);
 	ov511_dump_reg_range(dev, 0xa0, 0xbf);
 
 }
 #endif
-#endif
 
-static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
+static int ov511_reset(struct usb_ov511 *ov511, unsigned char reset_type)
 {
 	int rc;
-	
+		
+	/* Setting bit 0 not allowed on 518/518Plus */
+	if (ov511->bridge == BRG_OV518PLUS)
+		reset_type &= 0xfe;
+
 	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);
+
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, reset_type);
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0);
 
 	if (rc < 0)
 		err("reset: command failed");
@@ -728,24 +949,83 @@
 
 /* Temporarily stops OV511 from functioning. Must do this before changing
  * registers while the camera is streaming */
-static inline int ov511_stop(struct usb_device *dev)
+static inline int ov511_stop(struct usb_ov511 *ov511)
 {
 	PDEBUG(4, "stopping");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
+	if (ov511->bridge == BRG_OV518PLUS)
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3c));
+	else
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3d));
 }
 
 /* Restarts OV511 after ov511_stop() is called */
-static inline int ov511_restart(struct usb_device *dev)
+static inline int ov511_restart(struct usb_ov511 *ov511)
 {
 	PDEBUG(4, "restarting");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
+	return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x00));
+}
+
+/* Sets I2C read and write slave IDs. Returns <0 for error */
+static int ov51x_set_slave_ids(struct usb_ov511 *ov511,
+			       unsigned char write_id,
+			       unsigned char read_id)
+{
+	struct usb_device *dev = ov511->dev;
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0)
+		return -EIO;
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0)
+		return -EIO;
+
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 for failure.
+ */
+static int ov51x_init_ov_sensor(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int i, success;
+
+	/* Reset the sensor */ 
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -EIO;
+
+	/* 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 sensor */ 
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -EIO;
+		/* Wait for it to initialize */ 
+		schedule_timeout (1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov511_i2c_read(dev, 0x00) < 0) return -EIO;
+	}
+
+	if (!success)
+		return -EIO;
+	
+	PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+	return 0;
 }
 
 static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
 {
 	int alt, mult;
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_stop(ov511) < 0)
 		return -EIO;
 
 	mult = size >> 5;
@@ -773,6 +1053,19 @@
 			err("Set packet size: invalid size (%d)", size);
 			return -EINVAL;
 		}
+	} else if (ov511->bridge == BRG_OV518PLUS) {
+		if (size == 0) alt = OV518PLUS_ALT_SIZE_0;
+		else if (size == 128) alt = OV518PLUS_ALT_SIZE_128;
+		else if (size == 256) alt = OV518PLUS_ALT_SIZE_256;
+		else if (size == 384) alt = OV518PLUS_ALT_SIZE_384;
+		else if (size == 512) alt = OV518PLUS_ALT_SIZE_512;
+		else if (size == 640) alt = OV518PLUS_ALT_SIZE_640;
+		else if (size == 768) alt = OV518PLUS_ALT_SIZE_768;
+		else if (size == 896) alt = OV518PLUS_ALT_SIZE_896;
+		else {
+			err("Set packet size: invalid size (%d)", size);
+			return -EINVAL;
+		}
 	} else {
 		err("Set packet size: Invalid bridge type");
 		return -EINVAL;
@@ -780,21 +1073,28 @@
 
 	PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
 
-	if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
-		return -ENOMEM;
+	// FIXME: Don't know how to do this on OV518 yet
+	if (ov511->bridge != BRG_OV518PLUS)
+		if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
+			return -EIO;
 	
 	if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
 		err("Set packet size: set interface error");
 		return -EBUSY;
 	}
 
+	/* I don't know what this does */
+	if (ov511->bridge == BRG_OV518PLUS)
+		if (ov511_reg_write(ov511->dev, 0x2f, 0x80) < 0)
+			return -EIO;
+
 	// FIXME - Should we only reset the FIFO?
-	if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0)
-		return -ENOMEM;
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
 
 	ov511->packet_size = size;
 
-	if (ov511_restart(ov511->dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
 	return 0;
@@ -809,7 +1109,7 @@
 
 	PDEBUG(4, "ov511_set_picture");
 
-	if (ov511_stop(dev) < 0)
+	if (ov511_stop(ov511) < 0)
 		return -EIO;
 
 	ov511->contrast = p->contrast;
@@ -840,10 +1140,18 @@
 		if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0)
 			return -EIO;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
+	} else if (ov511->sensor == SEN_OV7620) {
+		/* Use gamma control instead. Bit 0 enables it. */
+		if (ov511_i2c_write(dev, 0x64, (p->contrast >> 8) | 1) < 0)
+			return -EIO;
+
+		/* These regs are undocumented. They may be backwards */
+		if (ov511_i2c_write(dev, 0x7a, 0xFF - (p->hue >> 8)) < 0)
 			return -EIO;
-	} else if ((ov511->sensor == SEN_OV7620) 
-	         || (ov511->sensor == SEN_OV7620AE)) {
+
+		if (ov511_i2c_write(dev, 0x79, p->hue >> 8) < 0)
+			return -EIO;
+
 #if 0
 		int cur_sat, new_sat, tmp;
 
@@ -861,9 +1169,13 @@
 	        PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); 
 
 #endif
+	} else if (ov511->sensor == SEN_OV7620AE) {
 	}
 
-	if (ov511_restart(dev) < 0)
+	if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
+		return -EIO;
+
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
 	return 0;
@@ -877,7 +1189,7 @@
 
 	PDEBUG(4, "ov511_get_picture");
 
-	if (ov511_stop(dev) < 0)
+	if (ov511_stop(ov511) < 0)
 		return -EIO;
 
 	if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
@@ -899,12 +1211,26 @@
 	p->depth = ov511->frame[0].depth;
 	p->palette = ov511->frame[0].format;
 
-	if (ov511_restart(dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
 	return 0;
 }
 
+/* Turns on or off the LED. Only has an effect with OV511+/OV518+ */
+static inline void 
+ov511_led_control(struct usb_ov511 *ov511, int enable) {
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_LED_CTL, 
+			        enable ? 1 : 0);
+	else if (ov511->bridge == BRG_OV518PLUS)
+		ov511_reg_write_mask(ov511->dev, OV518_REG_GPIO_OUT, 
+				     enable ? 0x02 : 0x00, 0x02);
+	return;
+}
+
 /* Returns number of bits per pixel (regardless of where they are located; planar or
  * not), or zero for unsupported format.
  */
@@ -923,7 +1249,7 @@
 }
 
 /* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
-static struct mode_list mlist[] = {
+static struct mode_list_511 mlist511[] = {
 	/* 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 },
@@ -954,7 +1280,7 @@
 	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
 	       width, height, mode, sub_flag);
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_stop(ov511) < 0)
 		return -EIO;
 
 	/* Dumppix only works with RGB24 */
@@ -967,10 +1293,10 @@
 		ov511_reg_write(dev, 0x16, 0x00);
 		if (ov511->sensor == SEN_OV7610
 		    || ov511->sensor == SEN_OV7620AE) {
-			/* these aren't valid on the OV6620/OV7620 */
+			/* these aren't valid on the OV6620/OV7620/6630? */
 			ov511_i2c_write(dev, 0x0e, 0x44);
 		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
+		ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x21 : 0x20, 0x21);
 
 		/* For snapshot */
 		ov511_reg_write(dev, 0x1e, 0x00);
@@ -979,10 +1305,10 @@
 		ov511_reg_write(dev, 0x16, 0x01);
 		if (ov511->sensor == SEN_OV7610
 		    || ov511->sensor == SEN_OV7620AE) {
-			/* not valid on the OV6620/OV7620 */
+			/* not valid on the OV6620/OV7620/6630? */
 			ov511_i2c_write(dev, 0x0e, 0x04);
 		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
+		ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x01 : 0x00, 0x21);
 
 		/* For snapshot */
 		ov511_reg_write(dev, 0x1e, 0x01);
@@ -998,6 +1324,7 @@
 		vwsbase = vwebase = 0x05;
 		break;
 	case SEN_OV6620:
+	case SEN_OV6630:	// FIXME: Is this right?
 		hwsbase = 0x38;
 		hwebase = 0x3a;
 		vwsbase = 0x05;
@@ -1055,51 +1382,351 @@
 		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
 	}
 
-	for (i = 0; mlist[i].width; i++) {
+	for (i = 0; mlist511[i].width; i++) {
 		int lncnt, pxcnt, clock;
 
-		if (width != mlist[i].width || height != mlist[i].height)
+		if (width != mlist511[i].width || height != mlist511[i].height)
 			continue;
 
-		if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
+		if (!mlist511[i].color && mode != VIDEO_PALETTE_GREY)
 			continue;
 
 		/* Here I'm assuming that snapshot size == image size.
 		 * I hope that's always true. --claudio
 		 */
-		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
-		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist511[i].pxcnt;
+		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist511[i].lncnt;
 
 		ov511_reg_write(dev, 0x12, pxcnt);
 		ov511_reg_write(dev, 0x13, lncnt);
-		ov511_reg_write(dev, 0x14, mlist[i].pxdv);
-		ov511_reg_write(dev, 0x15, mlist[i].lndv);
-		ov511_reg_write(dev, 0x18, mlist[i].m420);
+		ov511_reg_write(dev, 0x14, mlist511[i].pxdv);
+		ov511_reg_write(dev, 0x15, mlist511[i].lndv);
+		ov511_reg_write(dev, 0x18, mlist511[i].m420);
 
 		/* Snapshot additions */
 		ov511_reg_write(dev, 0x1a, pxcnt);
 		ov511_reg_write(dev, 0x1b, lncnt);
-                ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
-                ov511_reg_write(dev, 0x1d, mlist[i].lndv);
+                ov511_reg_write(dev, 0x1c, mlist511[i].pxdv);
+                ov511_reg_write(dev, 0x1d, mlist511[i].lndv);
+
+		/* The OV6620 needs special handling. This prevents the 
+		 * severe banding that normally occurs */
+		if (ov511->sensor == SEN_OV6620)
+		{
+			/* Clock down */
+			ov511_i2c_write(dev, 0x2a, 0x04);
+			ov511_i2c_write(dev, 0x11, 0x03);
+			ov511_i2c_write(dev, 0x2a, 0x84);
+			/* This next setting is critical. It seems to improve
+			 * the gain or the contrast. The "reserved" bits seem
+			 * to have some effect in this case. */
+			ov511_i2c_write(dev, 0x2d, 0x85);
+
+		}
+		else
+		{
+			/* Calculate and set the clock divisor */
+			clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
+			* (mlist511[i].color ? 3 : 2) / 2) / 66000;
+#if 0
+			clock *= cams;
+#endif
+			ov511_i2c_write(dev, 0x11, clock);
+		}
+
+		/* We only have code to convert GBR -> RGB24 */
+		if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
+			ov511_i2c_write(dev, 0x12, mlist511[i].common_A | (testpat?0x0a:0x08));
+		else
+			ov511_i2c_write(dev, 0x12, mlist511[i].common_A | (testpat?0x02:0x00));
+
+		/* Enable/Disable AGC */
+		if (ov511_i2c_write_mask(dev, 0x12, autoadjust ? 0x20 : 0x00, 0x20) < 0)
+			return -1;
+
+		/* 7620/6620/6630? don't have register 0x35, so play it safe */
+		if (ov511->sensor == SEN_OV7610 ||
+		    ov511->sensor == SEN_OV7620AE)
+			ov511_i2c_write(dev, 0x35, mlist511[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) < 0)
+		return -EIO;
+
+	if (mlist511[i].width == 0) {
+		err("Unknown mode (%d, %d): %d", width, height, mode);
+		return -EINVAL;
+	}
+
+#ifdef OV511_DEBUG
+	if (dump_regs)
+		ov511_dump_i2c_regs(dev);
+#endif
+
+	return 0;
+}
+
+static struct mode_list_518 mlist518[] = {
+	/* W    H   reg28 reg29 reg2a reg2c reg2e reg24 reg25 */
+	{ 352, 288, 0x80, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 },
+	{ 320, 240, 0x80, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 },
+	{ 176, 144, 0x85, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 },
+	{ 160, 120, 0x85, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 },
+	{ 0, 0 }
+};
+
+/* OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Many register ops are commented out until we
+ * can find out if they are still valid. Also, only 15 FPS is supported now.
+ */
+static int
+ov518_mode_init_regs(struct usb_ov511 *ov511,
+		     int width, int height, int mode, int sub_flag)
+{
+	int i;
+	struct usb_device *dev = ov511->dev;
+	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
+	int hwscale = 0, vwscale = 0;
+	unsigned char b[3]; /* Multiple-value reg buffer */
+
+	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+	       width, height, mode, sub_flag);
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	/* Dumppix only works with RGB24 */
+	if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
+		err("dumppix only supported with RGB 24");
+		return -EINVAL;
+	}
+
+	/* I don't know how to set YUV400 mode on OV518 yet */
+	if (mode == VIDEO_PALETTE_GREY) {
+//		ov511_reg_write(dev, 0x16, 0x00);
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* these aren't valid on the OV6620/OV7620/6630? */
+			ov511_i2c_write(dev, 0x0e, 0x44);
+		}
+		ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x21 : 0x20, 0x21);
+	} else {
+//		ov511_reg_write(dev, 0x16, 0x01);
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* not valid on the OV6620/OV7620/6630? */
+			ov511_i2c_write(dev, 0x0e, 0x04);
+		}
+		ov511_i2c_write_mask(dev, 0x13, autoadjust ? 0x01 : 0x00, 0x21);
+	}
+
+	/* The different sensor ICs handle setting up of window differently */
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:	// FIXME: Is this right?
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = 0x05;
+		vwebase = 0x06;
+		break;
+	case SEN_OV7620:
+		hwsbase = 0x2c;
+		hwebase = 0x2d;
+		vwsbase = vwebase = 0x05;
+		break;
+	default:
+		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;
+		}	
+	}
 
-		/* Calculate and set the clock divisor */
-		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
-			* (mlist[i].color ? 3 : 2) / 2) / 66000;
+	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+	if (sub_flag) {
+		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 + (hwsize>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase);
+		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
+	}
+
+	for (i = 0; mlist518[i].width; i++) {
+//		int lncnt, pxcnt;
+		int clock;
+
+		if (width != mlist518[i].width || height != mlist518[i].height)
+			continue;
+
+// FIXME: Subcapture won't be possible until we know what the registers do
+// FIXME: We can't handle anything but YUV420 so far
+
+//		/* Here I'm assuming that snapshot size == image size.
+//		 * I hope that's always true. --claudio
+//		 */
+//		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+//		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+//
+//		ov511_reg_write(dev, 0x12, pxcnt);
+//		ov511_reg_write(dev, 0x13, lncnt);
+
+		/******** Set the mode ********/		
+
+		/* Mode independent regs */
+		ov511_reg_write(dev, 0x2b, 0x00);
+		ov511_reg_write(dev, 0x2d, 0x00);
+		ov511_reg_write(dev, 0x3b, 0x00);
+		ov511_reg_write(dev, 0x3d, 0x00);
+
+		/* Mode dependent regs. Regs 38 - 3e are always the same as
+		 * regs 28 - 2e */
+		ov511_reg_write_mask(dev, 0x28, mlist518[i].reg28, 0x0f);
+		ov511_reg_write(dev, 0x29, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x2a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x2c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x2e, mlist518[i].reg2e);
+		ov511_reg_write_mask(dev, 0x38, mlist518[i].reg28, 0x0f);
+		ov511_reg_write(dev, 0x39, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x3a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x3c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x3e, mlist518[i].reg2e);
+		ov511_reg_write(dev, 0x24, mlist518[i].reg24);
+		ov511_reg_write(dev, 0x25, mlist518[i].reg25);
+
+		/* Windows driver does this here; who knows why */
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/******** Set the framerate (to 15 FPS) ********/		
+
+		/* Mode independent, but framerate dependent, regs */
+		/* These are for 15 FPS only */
+		ov511_reg_write(dev, 0x51, 0x08);
+		ov511_reg_write(dev, 0x22, 0x18);
+		ov511_reg_write(dev, 0x23, 0xff);
+		ov511_reg_write(dev, 0x71, 0x19);  /* Compression-related? */
+		/* Bit 5 is what matters here. Of course, it is "reserved" */
+		ov511_i2c_write(dev, 0x54, 0x23);
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/* Mode dependent regs */
+		if ((width == 352 && height == 288) ||
+		    (width == 320 && height == 240)) {
+			b[0]=0x80; b[1]=0x02; ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0x90; b[1]=0x01; ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0xf4; b[1]=0x01; ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0xf4; b[1]=0x01; ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x8e; b[1]=0x00; ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x1a; b[1]=0x00; b[2]=0x02; ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x14; b[1]=0x02; ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xd0; b[1]=0x07; ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x20; b[1]=0x00; ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x60; b[1]=0x02; ov518_reg_write_multi(dev, 0xce, b, 2);
+
+		} else if ((width == 176 && height == 144) ||
+			   (width == 160 && height == 120)) {
+			b[0]=0x80; b[1]=0x01; ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0xc8; b[1]=0x00; ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0x40; b[1]=0x01; ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0x40; b[1]=0x01; ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x60; b[1]=0x00; ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x0f; b[1]=0x33; b[2]=0x01; ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x40; b[1]=0x01; ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xec; b[1]=0x04; ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x13; b[1]=0x00; ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x6d; b[1]=0x01; ov518_reg_write_multi(dev, 0xce, b, 2);
+		} else {
+			/* Can't happen, since we already handled this case */
+			err("ov518_mode_init_regs(): **** logic error ****");
+		}
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/* The OV6620 needs special handling. This prevents the 
+		 * severe banding that normally occurs */
+		if (ov511->sensor == SEN_OV6620)
+		{
+			/* Clock down */
+			ov511_i2c_write(dev, 0x2a, 0x04);
+			ov511_i2c_write(dev, 0x11, 0x03);
+			ov511_i2c_write(dev, 0x2a, 0x84);
+			/* This next setting is critical. It seems to improve
+			 * the gain or the contrast. The "reserved" bits seem
+			 * to have some effect in this case. */
+			ov511_i2c_write(dev, 0x2d, 0x85);
+
+		}
+		else
+		{
+			/* Calculate and set the clock divisor */
+// Color is always on
+//			clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
+//			* (mlist518[i].color ? 3 : 2) / 2) / 66000;
+			clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
+			* (3 / 2)) / 66000;
 #if 0
-		clock *= cams;
+			clock *= cams;
 #endif
-		ov511_i2c_write(dev, 0x11, clock);
+			ov511_i2c_write(dev, 0x11, clock);
+		}
 
 		/* 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));
+			ov511_i2c_write(dev, 0x12, 0x04 | (testpat?0x0a:0x08));
 		else
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+			ov511_i2c_write(dev, 0x12, 0x04 | (testpat?0x02:0x00));
+
+		/* Enable/Disable AGC */
+		if (ov511_i2c_write_mask(dev, 0x12, autoadjust ? 0x20 : 0x00, 0x20) < 0)
+			return -1;
 
-		/* 7620/6620 don't have register 0x35, so play it safe */
+		/* 7620/6620/6630? 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);
+			ov511_i2c_write(dev, 0x35, 0x1e);
 
 		break;
 	}
@@ -1109,22 +1736,60 @@
 		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
 	}
 
-	if (ov511_restart(ov511->dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
-	if (mlist[i].width == 0) {
+	if (mlist518[i].width == 0) {
 		err("Unknown mode (%d, %d): %d", width, height, mode);
 		return -EINVAL;
 	}
 
 #ifdef OV511_DEBUG
-	if (debug >= 5)
+	if (dump_regs)
 		ov511_dump_i2c_regs(dev);
 #endif
 
 	return 0;
 }
 
+/* This is a wrapper around the OV511, OV518, and sensor specific functions */
+static int
+mode_init_regs(struct usb_ov511 *ov511,
+	       int width, int height, int mode, int sub_flag)
+{
+	if (ov511->bridge == BRG_OV518PLUS)
+		return ov518_mode_init_regs(ov511, width, height, mode, sub_flag);
+	else
+		return ov511_mode_init_regs(ov511, width, height, mode, sub_flag);
+}
+
+/* This sets the default image parameters (Size = max, RGB24). This is
+ * useful for apps that use read() and do not set these.
+ */
+static int ov51x_set_default_params(struct usb_ov511 *ov511)
+{
+	int i;
+
+	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+	 * (using read() instead). */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		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;
+		ov511->frame[i].format = VIDEO_PALETTE_RGB24;
+		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
+	}
+
+	/* Initialize to max width/height, RGB24 */
+	if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
+			   VIDEO_PALETTE_RGB24, 0) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
 /**********************************************************************
  *
  * Color correction functions
@@ -1373,7 +2038,8 @@
 }
 
 static void
-ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0,
+ov511_dumppix(struct ov511_frame *frame, 
+	      unsigned char *pIn0, unsigned char *pOut0,
 	      int iOutY, int iOutUV, int iHalf, int iWidth)
 {
 	int i, j, k;
@@ -1410,12 +2076,22 @@
 		break;
 	case 3: /* This will dump only the Y channel data stream as-is */
 		pIn = pIn0 + 128;
-		pOut = pOut0 + output_offset;
+		pOut = pOut0 + frame->output_offset;
 		for (i = 0; i < 256; i++) {
 			*pOut++ = *pIn;
 			*pOut++ = *pIn;
 			*pOut++ = *pIn++;
-			output_offset += 3;
+			frame->output_offset += 3;
+		}
+		break;
+	case 4: /* This will dump the whole unformatted data stream as-is */
+		pIn = pIn0;
+		pOut = pOut0 + frame->output_offset;
+		for (i = 0; i < 384; i++) {
+			*pOut++ = *pIn;
+			*pOut++ = *pIn;
+			*pOut++ = *pIn++;
+			frame->output_offset += 3;
 		}
 		break;
 	} /* End switch (dumppix) */
@@ -1612,6 +2288,16 @@
 
 /**********************************************************************
  *
+ * Decompression
+ *
+ **********************************************************************/
+static void ov511_decompress(struct ov511_frame *frame)
+{
+	err("Not implemented yet (but given %d bytes)", frame->bytes_recvd);
+}
+
+/**********************************************************************
+ *
  * OV511 data transfer, IRQ handler
  *
  **********************************************************************/
@@ -1623,10 +2309,19 @@
 	int aPackNum[10];
 	struct ov511_frame *frame;
 	unsigned char *pData;
-	int iPix;
+	int iPix, data_size, pn, num, offset;
 
 	PDEBUG (4, "Moving %d packets", urb->number_of_packets);
 
+	/* OV518+ has no packet numbering */
+	if (ov511->bridge == BRG_OV518PLUS) {
+		pn = 0;
+		data_size = ov511->packet_size;
+	} else {
+		pn = 1;
+		data_size = ov511->packet_size - 1;
+	}
+
 	for (i = 0; i < urb->number_of_packets; i++) {
 		int n = urb->iso_frame_desc[i].actual_length;
 		int st = urb->iso_frame_desc[i].status;
@@ -1636,7 +2331,8 @@
 
 		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
 
-		aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1;
+		if (pn)
+			aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1;
 
 		if (!n || ov511->curframe == -1)
 			continue;
@@ -1645,21 +2341,27 @@
 			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
 
 		frame = &ov511->frame[ov511->curframe];
-		
+
 		/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
 		 * byte non-zero. The EOF packet has image width/height in the
-		 * 10th and 11th packets. The 9th bit is given as follows:
+		 * 10th and 11th bytes. The 9th byte is given as follows:
 		 *
 		 * bit 7: EOF
 		 *     6: compression enabled
 		 *     5: 422/420/400 modes
 		 *     4: 422/420/400 modes
 		 *     3: 1
-		 *     2: snapshot bottom on
+		 *     2: snapshot button on
 		 *     1: snapshot frame
 		 *     0: even/odd field
 		 */
 
+		if (printph) {
+			info("packet header: %x %x %x %x %x %x %x %x %x %x %x %x",
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
+
 		/* Check for SOF/EOF packet */
 		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
 		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
@@ -1673,16 +2375,21 @@
 			ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
 			do_gettimeofday (ts);
 
-		 	PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
-				ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
-				(int)(cdata[9]), (int)(cdata[10]));
+	 		PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d, recvd=%d",
+				ov511->curframe,
+				(int)(pn ? cdata[ov511->packet_size - 1] : -1),
+				(int)(cdata[9]), (int)(cdata[10]), frame->bytes_recvd);
 
 			if (frame->scanstate == STATE_LINES) {
 		    		int iFrameNext;
 
+				/* Would ideally be done on user's time */
+				if (frame->compressed)
+					ov511_decompress(frame);
+
 				if (fix_rgb_offset)
 					fixFrameRGBoffset(frame);
-				frame->grabstate = FRAME_DONE;
+				frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
 
 				if (waitqueue_active(&frame->wq)) {
 					frame->grabstate = FRAME_DONE;
@@ -1719,7 +2426,6 @@
 			 * for compression experimentation */
 			memset(frame->data, 0, MAX_DATA_SIZE);
 #endif
-			output_offset = 0;
 
 			/* Check to see if it's a snapshot frame */
 			/* FIXME?? Should the snapshot reset go here? Performance? */
@@ -1730,6 +2436,8 @@
 
 			frame->scanstate = STATE_LINES;
 			frame->segment = 0;
+			frame->bytes_recvd = 0;
+			frame->output_offset = 0;
 		}
 
 check_middle:
@@ -1737,6 +2445,25 @@
 		if (frame->scanstate != STATE_LINES)
 			continue;
 
+		/* If frame start, skip header */
+		if (frame->bytes_recvd == 0)
+			offset = 9;
+		else
+			offset = 0;
+
+		num = n - offset - (pn ? 1 : 0);
+		frame->bytes_recvd += num;
+
+		if (compress) {
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE)
+				memmove(frame->rawdata + frame->bytes_recvd - num,
+					&cdata[offset], num);
+			else
+				err("Raw data buffer overrun!!");
+
+			continue; /* Skip all other processing */
+		}
+
 		/* Deal with leftover from last segment, if any */
 		if (frame->segment) {
 			pData = ov511->scratch;
@@ -1744,12 +2471,12 @@
 			memmove(pData + ov511->scratchlen, cdata,
 				iPix+frame->segsize);
 		} else {
-			pData = &cdata[iPix = 9];
+			pData = &cdata[iPix = 9]; /* Start of frame */
 	 	}
 
 		/* Parse the segments */
-		while (iPix <= (ov511->packet_size - 1) - frame->segsize &&
-		    frame->segment < frame->width * frame->height / 256) {
+		while (iPix <= data_size - frame->segsize &&
+		    frame->segment < frame->width * frame->height / PIXELS_PER_SEG) {
 			int iSegY, iSegUV;
 			int iY, jY, iUV, jUV;
 			int iOutY, iOutYP, iOutUV, iOutUVP;
@@ -1767,7 +2494,7 @@
 				iSeg1 = iSegY / (ov511->subw / 32);
 				iSeg1 *= frame->width / 32;
 				iSegY = iSeg1 + (iSegY % (ov511->subw / 32));
-				if (iSegY >= frame->width * ov511->subh / 256)
+				if (iSegY >= frame->width * ov511->subh / PIXELS_PER_SEG)
 					break;
 
 				iSeg1 = iSegUV / (ov511->subw / 16);
@@ -1798,7 +2525,7 @@
 				break;
 			case VIDEO_PALETTE_RGB24:
 				if (dumppix)
-					ov511_dumppix(pData, pOut, iOutY, iOutUV,
+					ov511_dumppix(frame, pData, pOut, iOutY, iOutUV,
 						iY & 1, frame->width);
 				else if (sensor_gbr)
 					ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV,
@@ -1831,8 +2558,8 @@
 		}
 
 		/* Save extra data for next time */
-		if (frame->segment < frame->width * frame->height / 256) {
-			ov511->scratchlen = (ov511->packet_size - 1) - iPix;
+		if (frame->segment < frame->width * frame->height / PIXELS_PER_SEG) {
+			ov511->scratchlen = data_size - iPix;
 			if (ov511->scratchlen < frame->segsize)
 				memmove(ov511->scratch, pData, ov511->scratchlen);
 			else
@@ -1840,9 +2567,11 @@
 		}
 	}
 
-	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
-	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
-	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
+	if (pn) {
+		PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
+			aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
+			aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
+	}
 
 	return totlen;
 }
@@ -1869,9 +2598,14 @@
 		PDEBUG(4, "hmmm... not streaming, but got interrupt");
 		return;
 	}
-	
+
 	sbuf = &ov511->sbuf[ov511->cursbuf];
 
+	if (sbuf == NULL) {
+		err("null scrath buffer pointer!");
+		return;
+	}
+
 	/* Copy the data received into our scratch buffer */
 	if (ov511->curframe >= 0) {
 		len = ov511_move_data(ov511, urb);
@@ -1899,30 +2633,43 @@
 	ov511->cursbuf = 0;
 	ov511->scratchlen = 0;
 
-	if (ov511->bridge == BRG_OV511)
-		if (cams == 1)			 size = 993;
-		else if (cams == 2)		 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
+	if (ov511->bridge == BRG_OV511) {
+		if (cams == 1)				size = 993;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
+		else {
+			err("\"cams\" parameter too high!");
+			return -1;
+		}
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (cams == 1)				size = 961;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
+		else if (cams >= 5 && cams <= 8)	size = 129;
+		else if (cams >= 9 && cams <= 31)	size = 33;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else if (ov511->bridge == BRG_OV511PLUS)
-		if (cams == 1)                      size = 961;
-		else if (cams == 2)                 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
-		else if (cams >= 5 && cams <= 8)    size = 129;
-		else if (cams >= 9 && cams <= 31)   size = 33;
+	} else if (ov511->bridge == BRG_OV518PLUS) {
+		if (cams == 1)				size = 896;
+		else if (cams == 2)			size = 512;
+		else if (cams == 3 || cams == 4)	size = 256;
+		else if (cams >= 5 && cams <= 8)	size = 128;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else {
+	} else {
 		err("invalid bridge type");
 		return -1;
 	}
 
-	ov511_set_packet_size(ov511, size);
+	// FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
+	if (ov511->bridge == BRG_OV518PLUS)
+		ov511_set_packet_size(ov511, 640);
+	else
+		ov511_set_packet_size(ov511, size);
 
 	for (n = 0; n < OV511_NUMSBUF; n++) {
 		urb = usb_alloc_urb(FRAMES_PER_DESC);
@@ -2053,9 +2800,17 @@
 	if (!ov511->fbuf)
 		goto error;
 
+	ov511->rawfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE);
+	if (!ov511->rawfbuf) {
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+		ov511->fbuf = NULL;
+		goto error;
+	}
+
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
 		ov511->frame[i].grabstate = FRAME_UNUSED;
 		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE;
+		ov511->frame[i].rawdata = ov511->rawfbuf + i * MAX_RAW_DATA_SIZE;
 		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
 
 		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
@@ -2064,7 +2819,11 @@
 			while (--i) {
 				kfree(ov511->sbuf[i].data);
 				ov511->sbuf[i].data = NULL;
+				ov511->frame[i].data = NULL;
+				ov511->frame[i].rawdata = NULL;
 			}
+			vfree(ov511->rawfbuf);
+			ov511->rawfbuf = NULL;
 			rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
 			ov511->fbuf = NULL;
 			goto error;
@@ -2098,11 +2857,18 @@
 		ov511->fbuf = NULL;
 	}
 
+	if (ov511->rawfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+	}
+
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
 		if (ov511->sbuf[i].data) {
 			kfree(ov511->sbuf[i].data);
 			ov511->sbuf[i].data = NULL;
 		}
+		ov511->frame[i].data = NULL;
+		ov511->frame[i].rawdata = NULL;
 	}
 
 	PDEBUG(4, "buffer memory deallocated");
@@ -2182,6 +2948,9 @@
 	}
 
 	ov511->user++;
+	
+	if (ov511->led_policy == LED_AUTO)
+		ov511_led_control(ov511, 1);
 
 out:
 	up(&ov511->lock);
@@ -2203,6 +2972,9 @@
 	ov511->user--;
 	ov511_stop_isoc(ov511);
 
+	if (ov511->led_policy == LED_AUTO)
+		ov511_led_control(ov511, 0);
+
 	if (ov511->dev)
 		ov511_dealloc(ov511, 0);
 
@@ -2274,6 +3046,7 @@
 		v.flags = 0;
 		v.tuners = 0;
 		v.type = VIDEO_TYPE_CAMERA;
+		v.norm = 0;
 		strcpy(v.name, "Camera");
 
 		if (copy_to_user(arg, &v, sizeof(v)))
@@ -2400,7 +3173,7 @@
 		interruptible_sleep_on(&ov511->wq);
 		if (signal_pending(current)) return -EINTR;
 
-		result = ov511_mode_init_regs(ov511, vw.width, vw.height,
+		result = mode_init_regs(ov511, vw.width, vw.height,
 			ov511->frame[0].format, ov511->sub_flag);
 		if (result < 0)
 			return result;
@@ -2486,7 +3259,7 @@
 			   before changing modes */
 			interruptible_sleep_on(&ov511->wq);
 			if (signal_pending(current)) return -EINTR;
-			ret = ov511_mode_init_regs(ov511, vm.width, vm.height,
+			ret = mode_init_regs(ov511, vm.width, vm.height,
 				vm.format, ov511->sub_flag);
 #if 0
 			if (ret < 0)
@@ -2615,7 +3388,7 @@
 	return 0;
 }
 
-static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+static inline long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 	int i;
@@ -2775,11 +3548,14 @@
 
 /****************************************************************************
  *
- * OV511/OV7610 configuration
+ * OV511 and sensor configuration
  *
  ***************************************************************************/
 
-static int ov76xx_configure(struct usb_ov511 *ov511)
+/* This initializes the OV7610, OV7620, or OV7620AE sensor. The OV7620AE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int ov7xx0_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
 	int i, success;
@@ -2855,40 +3631,12 @@
 	PDEBUG (4, "starting configuration");
 
 	/* This looks redundant, but is necessary for WebCam 3 */
-	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)
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_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;
-
-	/* 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 76xx */ 
-		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) (method 1)", i);
+	if (ov51x_init_ov_sensor(ov511) >= 0) {
+		PDEBUG(1, "OV7xx0 sensor initalized (method 1)");
 	} else {
 		/* Reset the 76xx */
 		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
@@ -2908,13 +3656,16 @@
 			}
 		}
 
-		if ((i == i2c_detect_tries) && (success == 0)) {
+// Was (i == i2c_detect_tries) previously. This obviously used to always report
+// success. Whether anyone actually depended on that bug is unknown
+		if ((i >= i2c_detect_tries) && (success == 0)) {
 			err("Failed to read sensor ID. You might not have an OV7610/20,");
-			err("or it may be not responding. Report this to");
-			err("mwm@i.am");
-			return -1;
+			err("or it may be not responding. Report this to mwm@i.am");
+			err("This is only a warning. You can attempt to use your camera anyway");
+// Only issue a warning for now  
+//			return -1;
 		} else {
-			PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1);
+			PDEBUG(1, "OV7xx0 initialized in %d attempt(s) (method 2)", i+1);
 		}
 	}
 
@@ -2930,7 +3681,16 @@
 			ov511->sensor = SEN_OV7610;
 		} else if((rc & 3) == 1) {
 			info("Sensor is an OV7620AE");
-			ov511->sensor = SEN_OV7620AE;
+
+			/* OV511+ will return all zero isoc data unless we
+			 * configure the sensor as a 7620. Someone needs to
+			 * find the exact reg. setting that causes this. */
+			if (ov511->bridge == BRG_OV511PLUS) {
+				info("Enabling 511+/7620AE workaround");
+				ov511->sensor = SEN_OV7620;
+			} else {
+				ov511->sensor = SEN_OV7620AE;
+			}
 		} else if((rc & 3) == 0) {
 			info("Sensor is an OV7620");
 			ov511->sensor = SEN_OV7620;
@@ -2981,74 +3741,137 @@
 	return 0;
 }
 
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
 static int ov6xx0_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i, success, rc;
+	int rc;
 
 	static struct ov511_regvals aRegvalsNorm6x20[] = {
-		{ OV511_I2C_BUS, 0x12, 0x80 },  /* reset */
+		{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
 		{ OV511_I2C_BUS, 0x11, 0x01 },
-		{ OV511_I2C_BUS, 0x03, 0xd0 },
-		{ OV511_I2C_BUS, 0x05, 0x7f },
+		{ OV511_I2C_BUS, 0x03, 0x60 },
+		{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
 		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		/* The ratio of 0x0c and 0x0d  controls the white point */
 		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0d, 0x24 },
-		{ OV511_I2C_BUS, 0x10, 0xff },  /* ? */
+		{ OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */
 		{ 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 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+		{ OV511_I2C_BUS, 0x16, 0x06 },
+//		{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+		{ OV511_I2C_BUS, 0x28, 0x05 }, /* Selects RGB format if RGB on */
+		{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//		{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+		{ OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
 		{ OV511_I2C_BUS, 0x38, 0x8b },
-		{ OV511_I2C_BUS, 0x3c, 0x5c },
+		{ OV511_I2C_BUS, 0x39, 0x40 },
+		
+		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+
 		{ OV511_I2C_BUS, 0x3d, 0x80 },
-		{ OV511_I2C_BUS, 0x3f, 0x00 },
-		{ OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4d, 0xd2 },
+		/* These next two registers (0x4a, 0x4b) are undocumented. They 
+		 * control the color balance */
+		{ OV511_I2C_BUS, 0x4a, 0x80 },
+		{ OV511_I2C_BUS, 0x4b, 0x80 },
+		{ OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */
 		{ OV511_I2C_BUS, 0x4e, 0xc1 },
 		{ OV511_I2C_BUS, 0x4f, 0x04 },
+// Do 50-53 have any effect?
+// Toggle 0x12[2] off and on here?
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	/* This chip is undocumented so many of these are guesses. OK=verified,
+	 * A=Added since 6620, U=unknown function (not a 6620 reg) */
+	static struct ov511_regvals aRegvalsNorm6x30[] = {
+	/*OK*/	{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+	/*00?*/	{ OV511_I2C_BUS, 0x11, 0x01 },
+	/*OK*/	{ OV511_I2C_BUS, 0x03, 0x60 },
+	/*0A?*/	{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+//		{ OV511_I2C_BUS, 0x07, 0xa8 }, /* Does 6630 have this? */
+		/* The ratio of 0x0c and 0x0d  controls the white point */
+	/*OK*/	{ OV511_I2C_BUS, 0x0c, 0x24 },
+	/*OK*/	{ OV511_I2C_BUS, 0x0d, 0x24 },
+	/*A*/	{ OV511_I2C_BUS, 0x0e, 0x20 },
+	/*24?*/	{ OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */
+	/*A*/	{ OV511_I2C_BUS, 0x13, 0x21 },
+	/*04?*/	{ OV511_I2C_BUS, 0x14, 0x80 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+	/*03?*/	{ OV511_I2C_BUS, 0x16, 0x06 },
+//	/*OK*/	{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		// 21 & 22? The suggested values look wrong. Go with default
+	/*A*/	{ OV511_I2C_BUS, 0x23, 0xc0 },
+	/*A*/	{ OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
+	/*OK*/	{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x05 }, /* Selects RGB format if RGB on */
+	/*OK*/	{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//	/*OK*/	{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+	/*U*/	{ OV511_I2C_BUS, 0x2c, 0xa0 },
+	/*A*/	{ OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
+		// Reg 0x34: More 6620 reserved junk
+	/*d2?*/	{ OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
+	/*U*/	{ OV511_I2C_BUS, 0x36, 0x8f }, // May not be necessary
+	/*U*/	{ OV511_I2C_BUS, 0x37, 0x80 }, // May not be necessary
+	/*8b?*/	{ OV511_I2C_BUS, 0x38, 0x83 },
+	/*40?*/	{ OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
+		//Check these again
+		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+	/*OK*/	{ OV511_I2C_BUS, 0x3d, 0x80 },
+	/*A*/	{ OV511_I2C_BUS, 0x3f, 0x0e }, // Reserved bits on 6620. Default may be safer.
+
+	/*U*/	{ OV511_I2C_BUS, 0x40, 0x00 },
+	/*U*/	{ OV511_I2C_BUS, 0x41, 0x00 },
+	/*U*/	{ OV511_I2C_BUS, 0x42, 0x80 },
+	/*U*/	{ OV511_I2C_BUS, 0x43, 0x3f },
+	/*U*/	{ OV511_I2C_BUS, 0x44, 0x80 },
+	/*U*/	{ OV511_I2C_BUS, 0x45, 0x20 },
+	/*U*/	{ OV511_I2C_BUS, 0x46, 0x20 },
+	/*U*/	{ OV511_I2C_BUS, 0x47, 0x80 },
+	/*U*/	{ OV511_I2C_BUS, 0x48, 0x7f },
+	/*U*/	{ OV511_I2C_BUS, 0x49, 0x00 },
+
+		/* These next two registers (0x4a, 0x4b) are undocumented. They 
+		 * control the color balance */
+	/*OK?*/	{ OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
+	/*OK?*/	{ OV511_I2C_BUS, 0x4b, 0x80 },
+	/*U*/	{ OV511_I2C_BUS, 0x4c, 0xd0 }, // These high registers are always suspect
+	/*d2?*/	{ OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */
+	/*c1?*/	{ OV511_I2C_BUS, 0x4e, 0x40 },
+	/*04?*/	{ OV511_I2C_BUS, 0x4f, 0x07 },
+
+	/*U*/	{ OV511_I2C_BUS, 0x50, 0xff }, // These are all undocumented (by 6620 docs)
+	/*U*/	{ OV511_I2C_BUS, 0x54, 0x23 },
+	/*U*/	{ OV511_I2C_BUS, 0x55, 0xff },
+	/*U*/	{ OV511_I2C_BUS, 0x56, 0x12 },
+	/*U*/	{ OV511_I2C_BUS, 0x57, 0x81 },
+	/*U*/	{ OV511_I2C_BUS, 0x58, 0x75 },
+	/*U*/	{ OV511_I2C_BUS, 0x59, 0x01 },
+	/*U*/	{ OV511_I2C_BUS, 0x5a, 0x2c },
+	/*U*/	{ OV511_I2C_BUS, 0x5b, 0x0f },
+	/*U*/	{ OV511_I2C_BUS, 0x5c, 0x10 },
+
+// Set 0x3d to 0x80 here?
+// Set 0x27 to 0xa6 here?
+// Toggle 0x12[2] off and on here?
 		{ 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 {
+	if (ov51x_init_ov_sensor(ov511) < 0) {
 		err("Failed to read sensor ID. You might not have an OV6xx0,");
-		err("or it may be not responding. Report this to");
-		err("mwm@i.am");
+		err("or it may be not responding. Report this to mwm@i.am");
 		return -1;
+	} else {
+		PDEBUG(1, "OV6xx0 sensor detected");
 	}
 
 	/* Detect sensor if user didn't use override param */
@@ -3058,10 +3881,19 @@
 		if (rc < 0) {
 			err("Error detecting sensor type");
 			return -1;
-		} else {
-			info("Sensor is an OV6xx0 (version %d)", rc & 3);
+		} else if((rc & 3) == 0) {
+			info("Sensor is an OV6630");
+			ov511->sensor = SEN_OV6630;
+		} else if((rc & 3) == 1) {
+			info("Sensor is an OV6620");
 			ov511->sensor = SEN_OV6620;
-		}
+		} else if((rc & 3) == 2) {
+			info("Sensor is an OV6630AE");
+			ov511->sensor = SEN_OV6630;
+		} else if((rc & 3) == 3) {
+			info("Sensor is an OV6630AF");
+			ov511->sensor = SEN_OV6630;
+		} 
 	} else {	/* sensor != 0; user overrode detection */
 		ov511->sensor = sensor;
 		info("Sensor set to type %d", ov511->sensor);
@@ -3071,10 +3903,16 @@
 	ov511->maxwidth = 352;
 	ov511->maxheight = 288;
 
-	PDEBUG(4, "Writing 6x20 registers");
-	if (ov511_write_regvals(dev, aRegvalsNorm6x20))
-		return -1;
-
+	if (ov511->sensor == SEN_OV6620) {
+		PDEBUG(4, "Writing 6x20 registers");
+		if (ov511_write_regvals(dev, aRegvalsNorm6x20))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 6x30 registers");
+		if (ov511_write_regvals(dev, aRegvalsNorm6x30))
+			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 */
@@ -3086,26 +3924,24 @@
 	}
 
 	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;
+		if (ov511_i2c_write_mask(dev, 0x13, 0x01, 0x01) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 0x99) < 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);
+		if (ov511_i2c_write_mask(dev, 0x13, 0x00, 0x01) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 0x89) < 0) return -1;
+		if (ov511_i2c_write_mask(dev, 0x28, 0x08, 0x08) < 0) return -1;
 	}
 
 	return 0;
 }
 
-
+/* This initializes the OV511/OV511+ and the sensor */
 static int ov511_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
 	int i;
 
-	static struct ov511_regvals aRegvalsInit[] = {
+	static struct ov511_regvals aRegvalsInit511[] = {
 		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
 	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
 	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
@@ -3121,19 +3957,50 @@
 		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
 		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
 		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x1f },
 		{ 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_Y, 0xff },
 		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0xff },
 		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 },
-		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x00 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
+	PDEBUG(4, "");
+
+	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
+	if (ov511->customid < 0) {
+		err("Unable to read camera bridge registers");
+		return -EIO;
+	}
+
+	ov511->desc = -1;
+	PDEBUG (4, "CustomID = %d", ov511->customid);
+	for (i = 0; clist[i].id >= 0; i++) {
+		if (ov511->customid == clist[i].id) {
+			info("camera: %s", clist[i].description);
+			ov511->desc = i;
+			break;
+		}
+	}
+
+	/* Lifeview USB Life TV not supported */
+	if (clist[i].id == 38) {
+		err("This device is not supported yet.");
+		return -EIO;
+	}
+
+	if (clist[i].id == -1) {
+		err("Camera type (%d) not recognized", ov511->customid);
+		err("Please notify mwm@i.am of the name, manufacturer, model,");
+		err("and camera type number of your camera. Also include the");
+		err("complete output of the detection process.");
+	}
+
 	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
 
 	for (i = 0; i < OV511_NUMFRAMES; i++)
@@ -3146,79 +4013,236 @@
 		return -EBUSY;
 	}
 
-	if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
+	if (ov511_write_regvals(dev, aRegvalsInit511)) goto error;
+
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov511_led_control(ov511, 0);
+
 	if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
+	if (compress)
+		ov511_upload_quan_tables(dev);
 
 	ov511_set_packet_size(ov511, 0);
 
 	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;
+	/* Test for 7xx0 */
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
+		return -1;
 
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                   OV7610_I2C_READ_ID) < 0)
-		goto error;
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+		/* Test for 6xx0 */
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
+			return -1;
+
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+			/* Test for 8xx0 */
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID))
+				return -1;
+
+			if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+				err("Can't determine sensor slave IDs");
+ 				goto error;
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if(ov6xx0_configure(ov511) < 0) {
+				err("failed to configure OV6xx0");
+ 				goto error;
+			}
+		}
+	} else {
+		if(ov7xx0_configure(ov511) < 0) {
+			err("failed to configure OV7xx0");
+	 		goto error;
+		}
+	}
 
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+	if (ov51x_set_default_params(ov511) < 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;
+	return 0;
 
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-		                    OV6xx0_I2C_READ_ID) < 0)
-			goto error;
+error:
+	err("OV511 Config failed");
 
-		if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-			goto error;
+	#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	/* Safe to call even if entry doesn't exist */
+	destroy_proc_ov511_cam((struct usb_ov511 *)dev);
+	#endif
 
-		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;
+	video_unregister_device(&ov511->vdev);
+	usb_driver_release_interface(&ov511_driver,
+		&dev->actconfig->interface[ov511->iface]);
+
+	return -EBUSY;
+}
+
+/* This initializes the OV518/OV518+ and the sensor */
+static int ov518_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int i;
+
+#if 0
+	/* Conservative sequence, based on OV511 */
+	static struct ov511_regvals aRegvalsInit518[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7e },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7e },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3c },
+		{ OV511_DONE_BUS, 0x0, 0x00},
+	};
+#else
+	/* Experimental sequence, based on OV518 Windows settings */
+	static struct ov511_regvals aRegvalsInit518[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x40 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, 0x5d, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00},
+	};
+#endif
+
+#if 0
+	/* Old sequence - Note: the mnemonics for these registers may not be
+	 * right with the OV518, but their values are right. */
+	static struct ov511_regvals aRegvalsNorm518[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x09 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x0f },
+		{ OV511_REG_BUS, 0x24, 0x9f },
+		{ OV511_REG_BUS, 0x25, 0x90 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_CLOCK_DIVISOR, 0x04 }, /* ? */
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x00 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+#else
+	/* New values, based on Windows driver. Since what they do is not
+	 * known yet, this may be incorrect. */
+	static struct ov511_regvals aRegvalsNorm518[] = {
+		{ OV511_REG_BUS, 0x52, 0x02 },
+		{ OV511_REG_BUS, 0x52, 0x01 },
+		{ OV511_REG_BUS, 0x31, 0x0f },
+		{ OV511_REG_BUS, 0x5d, 0x03 },
+		{ OV511_REG_BUS, 0x24, 0x9f },
+		{ OV511_REG_BUS, 0x25, 0x90 },
+		{ OV511_REG_BUS, 0x20, 0x08 },
+		{ OV511_REG_BUS, 0x51, 0x04 },
+		{ OV511_REG_BUS, 0x71, 0x19 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+#endif
+	PDEBUG(4, "");
+
+	/* First 5 bits of custom ID reg are a revision ID on OV518 */
+	info("Camera revision %d", 0x1F & ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID));
+
+	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+
+	for (i = 0; i < OV511_NUMFRAMES; i++)
+		init_waitqueue_head(&ov511->frame[i].wq);
+
+	init_waitqueue_head(&ov511->wq);
+
+	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) < 0) {
+		err("video_register_device failed");
+		return -EBUSY;
+	}
+
+	if (ov511_write_regvals(dev, aRegvalsInit518)) goto error;
+
+	/* LED is off by default with OV518; have to explicitly turn it on */
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov511_led_control(ov511, 0);
+	else
+		ov511_led_control(ov511, 1);
+
+	if (compress) {
+		compress = 0;
+		warn("Compression disabled on OV518+");
+	}
+
+	if (ov511_write_regvals(dev, aRegvalsNorm518)) goto error;
+	/* The next setting may not be necessary */
+	if (ov511_reg_write_mask(dev, 0x57,0x00, 0x02) < 0) goto error;
+
+	ov511_set_packet_size(ov511, 0);
+
+	ov511->snap_enabled = snapshot;
+
+	/* Test for 76xx */
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
+		return -1;
+
+	/* The OV518 must be more aggressive about sensor detection since
+	 * I2C write will never fail if the sensor is not present. We have
+	 * to try to initialize the sensor to detect its presence */
+
+	if (ov51x_init_ov_sensor(ov511) < 0) {
+		/* Test for 6xx0 */
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
+			return -1;
+
+		if (ov51x_init_ov_sensor(ov511) < 0) {
+			/* Test for 8xx0 */
+			if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+						OV6xx0_I2C_READ_ID) < 0)
+				return -1;
+
+			if (ov51x_init_ov_sensor(ov511) < 0) {
+				err("Can't determine sensor slave IDs");
+ 				goto error;
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if(ov6xx0_configure(ov511) < 0) {
+				err("failed to configure OV6xx0");
+ 				goto error;
+			}
 		}
 	} else {
-		if(ov76xx_configure(ov511) < 0) {
-			err("failed to configure OV76xx");
+		if(ov7xx0_configure(ov511) < 0) {
+			err("failed to configure OV7xx0");
 	 		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 = ov511->maxwidth;
-		ov511->frame[i].height = ov511->maxheight;
-		ov511->frame[i].depth = 24;
-		ov511->frame[i].bytes_read = 0;
-		ov511->frame[i].segment = 0;
-		ov511->frame[i].format = VIDEO_PALETTE_RGB24;
-		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
-	}
 
-	/* Initialize to max width/height, RGB24 */
-	if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
-				 VIDEO_PALETTE_RGB24, 0) < 0)
+	if (ov51x_set_default_params(ov511) < 0)
 		goto error;
 
 	return 0;
-	
+
 error:
+	err("OV518 Config failed");
+
+	#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	/* Safe to call even if entry doesn't exist */
+	destroy_proc_ov511_cam((struct usb_ov511 *)dev);
+	#endif
+
 	video_unregister_device(&ov511->vdev);
 	usb_driver_release_interface(&ov511_driver,
 		&dev->actconfig->interface[ov511->iface]);
 
-	return -EBUSY;	
+	return -EBUSY;
 }
 
 
@@ -3234,7 +4258,6 @@
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_ov511 *ov511;
-	int i;
 
 	PDEBUG(1, "probing for device...");
 
@@ -3262,6 +4285,7 @@
 
 	ov511->dev = dev;
 	ov511->iface = interface->bInterfaceNumber;
+	ov511->led_policy = led;
 
 	switch (dev->descriptor.idProduct) {
 	case 0x0511:
@@ -3272,6 +4296,10 @@
 		info("USB OV511+ camera found");
 		ov511->bridge = BRG_OV511PLUS;
 		break;
+	case 0x0518:
+		info("USB OV518+ camera found");
+		ov511->bridge = BRG_OV518PLUS;
+		break;
 	case 0x0002:
 		if (dev->descriptor.idVendor != 0x0813)
 			goto error;
@@ -3283,53 +4311,36 @@
 		goto error;
 	}
 
-	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
-	if (ov511->customid < 0) {
-		err("Unable to read camera bridge registers");
-		goto error;
-	}
-
-	ov511->desc = -1;
-	PDEBUG (4, "CustomID = %d", ov511->customid);
-	for (i = 0; clist[i].id >= 0; i++) {
-		if (ov511->customid == clist[i].id) {
-			info("camera: %s", clist[i].description);
-			ov511->desc = i;
-			break;
-		}
-	}
-
-	/* Lifeview USB Life TV not supported */
-	if (clist[i].id == 38) {
-		err("This device is not supported yet.");
-		goto error;
-	}
-
-	if (clist[i].id == -1) {
-		err("Camera type (%d) not recognized", ov511->customid);
-		err("Please contact mwm@i.am to request");
-		err("support for your camera.");
-	}
-
 	/* Workaround for some applications that want data in RGB
-	 * instead of BGR */
+	 * instead of BGR. */
 	if (force_rgb)
 		info("data format set to RGB");
 
-	if (!ov511_configure(ov511)) {
-		ov511->user = 0;
-		init_MUTEX(&ov511->lock);	/* to 1 == available */
-		init_MUTEX(&ov511->buf_lock);
-		ov511->buf_state = BUF_NOT_ALLOCATED;
+	if (ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_configure(ov511) < 0)
+			goto error;
 	} else {
-		err("Failed to configure camera");
-		goto error;
+		if (ov511_configure(ov511) < 0)
+			goto error;
 	}
 
+	init_MUTEX(&ov511->lock);	/* to 1 == available */
+	init_MUTEX(&ov511->buf_lock);
+	init_MUTEX(&ov511->param_lock);
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+
+#ifdef OV511_DEBUG
+	if (dump_regs)
+		ov511_dump_regs(dev);
+#endif
+
+	PDEBUG(1, "Camera initialization successful");
+
 	MOD_DEC_USE_COUNT;
      	return ov511;
 
 error:
+	err("Camera initialization failed");
 	if (ov511) {
 		kfree(ov511);
 		ov511 = NULL;
diff -Naur linux-2.4.1-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux-2.4.1-orig/drivers/usb/ov511.h	Wed Feb  7 01:11:50 2001
+++ linux/drivers/usb/ov511.h	Wed Feb  7 01:14:51 2001
@@ -52,6 +52,7 @@
 
 /* I2C register numbers */
 #define OV511_REG_I2C_CONTROL			0x40
+#define OV518_REG_I2C_CONTROL			0x47	/* OV518+ only */
 #define OV511_REG_I2C_SLAVE_ID_WRITE		0x41
 #define OV511_REG_I2C_SUB_ADDRESS_3_BYTE	0x42
 #define OV511_REG_I2C_SUB_ADDRESS_2_BYTE	0x43
@@ -78,8 +79,16 @@
 #define OV511_REG_SYSTEM_CLOCK_DIVISOR		0x51
 #define OV511_REG_SYSTEM_SNAPSHOT		0x52
 #define OV511_REG_SYSTEM_INIT         		0x53
-#define OV511_REG_SYSTEM_PWR_CLK		0x54	/* OV511+ only */
+#define OV511_REG_SYSTEM_PWR_CLK		0x54	/* OV511+/OV518+ only */
 #define OV511_REG_SYSTEM_LED_CTL		0x55	/* OV511+ only */
+#define OV518_REG_GPIO_IN			0x55	/* OV518+ only */
+#define OV518_REG_GPIO_OUT			0x56	/* OV518+ only */
+#define OV518_REG_GPIO_CTL			0x57	/* OV518+ only */
+#define OV518_REG_GPIO_PULSE_IN			0x58	/* OV518+ only */
+#define OV518_REG_GPIO_PULSE_CLEAR		0x59	/* OV518+ only */
+#define OV518_REG_GPIO_PULSE_POLARITY		0x5a	/* OV518+ only */
+#define OV518_REG_GPIO_PULSE_EN			0x5b	/* OV518+ only */
+#define OV518_REG_GPIO_RESET			0x5c	/* OV518+ only */
 #define OV511_REG_SYSTEM_USER_DEFINED		0x5E
 #define OV511_REG_SYSTEM_CUSTOM_ID		0x5F
 
@@ -119,6 +128,16 @@
 #define OV511PLUS_ALT_SIZE_769	6
 #define OV511PLUS_ALT_SIZE_961	7
 
+/* Alternate numbers for various max packet sizes (OV518+ only) */
+#define OV518PLUS_ALT_SIZE_0	0
+#define OV518PLUS_ALT_SIZE_128	1
+#define OV518PLUS_ALT_SIZE_256	2
+#define OV518PLUS_ALT_SIZE_384	3
+#define OV518PLUS_ALT_SIZE_512	4
+#define OV518PLUS_ALT_SIZE_640	5
+#define OV518PLUS_ALT_SIZE_768	6
+#define OV518PLUS_ALT_SIZE_896	7
+
 /* OV7610 registers */
 #define OV7610_REG_GAIN          0x00	/* gain setting (5:0) */
 #define OV7610_REG_BLUE          0x01	/* blue channel balance */
@@ -176,15 +195,17 @@
 #define FRAMES_PER_DESC		10	/* FIXME - What should this be? */
 #define FRAME_SIZE_PER_DESC	993	/* FIXME - Deprecated */
 #define MAX_FRAME_SIZE_PER_DESC	993	/* For statically allocated stuff */
+#define PIXELS_PER_SEG		256	/* Pixels per segment */
 
 #define OV511_ENDPOINT_ADDRESS 1	/* Isoc endpoint number */
 
-// CAMERA SPECIFIC
-// FIXME - these can vary between specific models
-#define OV7610_I2C_WRITE_ID 0x42
-#define OV7610_I2C_READ_ID  0x43
+/* CAMERA SPECIFIC */
+#define OV7xx0_I2C_WRITE_ID 0x42
+#define OV7xx0_I2C_READ_ID  0x43
 #define OV6xx0_I2C_WRITE_ID 0xC0
 #define OV6xx0_I2C_READ_ID  0xC1
+#define OV8xx0_I2C_WRITE_ID 0xA0
+#define OV8xx0_I2C_READ_ID  0xA1
 
 #define OV511_I2C_CLOCK_PRESCALER 0x03
 
@@ -196,17 +217,22 @@
 
 /* Bridge types */
 enum {
+	BRG_UNKNOWN,
 	BRG_OV511,
 	BRG_OV511PLUS,
+	BRG_OV518PLUS,
 };
 
-/* Sensor types */
+/* Sensor types - Update ov511.txt (sensor param docs) if you change this! */
 enum {
 	SEN_UNKNOWN,
 	SEN_OV7610,
 	SEN_OV7620,
 	SEN_OV7620AE,
 	SEN_OV6620,
+	SEN_OV6630,
+	SEN_OV8600,
+	SEN_KS0127,
 };
 
 enum {
@@ -222,6 +248,13 @@
 	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
 };
 
+/* LED options */
+enum {
+	LED_OFF,
+	LED_ON,
+	LED_AUTO,
+};
+
 struct usb_device;
 
 struct ov511_sbuf {
@@ -249,6 +282,7 @@
 
 struct ov511_frame {
 	char *data;		/* Frame buffer */
+	char *rawdata;		/* Raw camera data buffer */
 
 	int depth;		/* Bytes per pixel */
 	int width;		/* Width application is expecting */
@@ -259,6 +293,7 @@
 
 	int sub_flag;		/* Sub-capture mode for this frame? */
 	unsigned int format;	/* Format for this frame */
+	int compressed;		/* Is frame compressed? */
 	int segsize;		/* How big is each segment from the camera? */
 
 	volatile int grabstate;	/* State of grabbing */
@@ -267,9 +302,11 @@
 	int curline;		/* Line of frame we're working on */
 	int curpix;
 	int segment;		/* Segment from the incoming data */
+	int bytes_recvd;	/* Number of image bytes received from camera */
 
 	long scanlength;	/* uncompressed, raw data length of frame */
 	long bytes_read;	/* amount of scanlength that has been read from *data */
+	int output_offset;	/* Current offset where we are dumping raw data */
 
 	wait_queue_head_t wq;	/* Processes waiting */
 
@@ -299,6 +336,8 @@
 	int hue;
 	int whiteness;
 
+	int led_policy;		/* LED: off|on|auto; OV511+ only */
+
 	struct semaphore lock;
 	int user;		/* user count for exclusive use */
 
@@ -308,6 +347,7 @@
 	int compress;		/* Should the next frame be compressed? */
 
 	char *fbuf;		/* Videodev buffer area */
+	char *rawfbuf;		/* Raw camera data buffer area */
 
 	int sub_flag;		/* Pix Array subcapture on flag */
 	int subx;		/* Pix Array subcapture x offset */
@@ -354,7 +394,7 @@
 	char *name;
 };
 
-struct mode_list {
+struct mode_list_511 {
 	int width;
 	int height;
 	int color;		/* 0=grayscale, 1=color */
@@ -366,6 +406,92 @@
 	u8 common_A;
 	u8 common_L;
 };
+
+struct mode_list_518 {
+	int width;
+	int height;
+	u8 reg28;
+	u8 reg29;
+	u8 reg2a;
+	u8 reg2c;
+	u8 reg2e;
+	u8 reg24;
+	u8 reg25;
+};
+
+/* Compression stuff */
+
+#define QUANTABLESIZE	64
+#define IDCTTABLESIZE	256
+#define ZIGZAGTABLESIZE	64
+
+#define YQUANTABLE { \
+	0,  1,  1,  2,  2,  3,  3,  4, \
+	1,  1,  1,  2,  2,  3,  4,  4, \
+	1,  1,  2,  2,  3,  4,  4,  4, \
+	2,  2,  2,  3,  4,  4,  4,  4, \
+	2,  2,  3,  4,  4,  5,  5,  5, \
+	3,  3,  4,  4,  5,  5,  5,  5, \
+	3,  4,  4,  4,  5,  5,  5,  5, \
+	4,  4,  4,  4,  5,  5,  5,  5  \
+}
+
+#define UVQUANTABLE { \
+	0,  2,  2,  3,  4,  4,  4,  4, \
+	2,  2,  2,  4,  4,  4,  4,  4, \
+	2,  2,  3,  4,  4,  4,  4,  4, \
+	3,  4,  4,  4,  4,  4,  4,  4, \
+	4,  4,  4,  4,  4,  4,  4,  4, \
+	4,  4,  4,  4,  4,  4,  4,  4, \
+	4,  4,  4,  4,  4,  4,  4,  4, \
+	4,  4,  4,  4,  4,  4,  4,  4  \
+}
+
+#define IDCTTABLE { \
+	 11584,  11584,  11584,  11584,	    16708,  16708,  16708,  16708, \
+	 17696,  17696,  17696,  17696,	    13640,  13640,  13640,  13640, \
+	 11584,  11584,  11584,  11584,	     9360,   9360,   9360,   9360, \
+	  6274,   6274,   6274,   6274,	     3204,   3204,   3204,   3204, \
+	 11584,  11584,  11584,  11584,	    13640,  13640,  13640,  13640, \
+	  6274,   6274,   6274,   6274,	    -3204,  -3204,  -3204,  -3204, \
+	-11584, -11584, -11584, -11584,    -16708, -16708, -16708, -16708, \
+	-17696, -17696, -17696, -17696,     -9360,  -9360,  -9360,  -9360, \
+	 11584,  11584,  11584,  11584,      9360,   9360,   9360,   9360, \
+	 -6274,  -6274,  -6274,  -6274,    -16708, -16708, -16708, -16708, \
+	-11584, -11584, -11584, -11584,      3204,   3204,   3204,   3204, \
+	 17696,  17696,  17696,  17696,     13640,  13640,  13640,  13640, \
+	 11584,  11584,  11584,  11584,      3204,   3204,   3204,   3204, \
+	-17696, -17696, -17696, -17696,     -9360,  -9360,  -9360,  -9360, \
+	 11584,  11584,  11584,  11584,     13640,  13640,  13640,  13640, \
+	 -6274,  -6274,  -6274,  -6274,    -16708, -16708, -16708, -16708, \
+	 11584,  11584,  11584,  11584,     -3204,  -3204,  -3204,  -3204, \
+	-17696, -17696, -17696, -17696,      9360,   9360,   9360,   9360, \
+	 11584,  11584,  11584,  11584,    -13640, -13640, -13640, -13640, \
+	 -6274,  -6274,  -6274,  -6274,     16708,  16708,  16708,  16708, \
+	 11584,  11584,  11584,  11584,     -9360,  -9360,  -9360,  -9360, \
+	 -6274,  -6274,  -6274,  -6274,     16708,  16708,  16708,  16708, \
+	-11584, -11584, -11584, -11584,     -3204,  -3204,  -3204,  -3204, \
+	 17696,  17696,  17696,  17696,    -13640, -13640, -13640, -13640, \
+	 11584,  11584,  11584,  11584,    -13640, -13640, -13640, -13640, \
+	  6274,   6274,   6274,   6274,      3204,   3204,   3204,   3204, \
+	-11584, -11584, -11584, -11584,     16708,  16708,  16708,  16708, \
+	-17696, -17696, -17696, -17696,      9360,   9360,   9360,   9360, \
+	 11584,  11584,  11584,  11584,    -16708, -16708, -16708, -16708, \
+	 17696,  17696,  17696,  17696,    -13640, -13640, -13640, -13640, \
+	 11584,  11584,  11584,  11584,     -9360,  -9360,  -9360,  -9360, \
+	  6274,   6274,   6274,   6274,     -3204,  -3204,  -3204,  -3204  \
+}
+
+#define ZIGZAGTABLE { \
+	  0,   2,  16,  32,  18,   4,   6,  20, \
+	 34,  48,  64,  50,  36,  22,   8,  10, \
+	 24,  38,  52,  66,  80,  96,  82,  68, \
+	 54,  40,  26,  12,  14,  28,  42,  56, \
+	 70,  84,  98, 112, 114, 100,  86,  72, \
+	 58,  44,  30,  46,  60,  74,  88, 102, \
+	116, 118, 104,  90,  76,  62,  78,  92, \
+	106, 120, 122, 108,  94, 110, 124, 126  \
+}
 
 #endif
 
