diff -Naur linux-2.4.2-orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.4.2-orig/Documentation/usb/ov511.txt	Fri Mar  2 03:22:17 2001
+++ linux/Documentation/usb/ov511.txt	Tue Mar 27 22:24:41 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.
+  DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until
+	the snapshot button is pressed. Note: enabling this mode disables
+	/proc/video/ov511/<minor#>/button
 
   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,112 @@
         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: compress
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Set this to 1 to turn on the camera's compression engine. This can
+        potentially increase the frame rate at the expense of quality, if you
+        have a fast CPU. This does not work reliably yet (only stable with very
+        dark images.)
+
+  NAME: testpat
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: This configures the camera's sensor to transmit a colored test-pattern
+        instead of an image. This does not work correctly yet.
+
   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_bridge
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system
+	log. Only useful for serious debugging/development purposes.
+
+  NAME: dump_sensor
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the 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.
+
+  NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv
+  TYPE: integer (0-63 for phy and phuv, 0-255 for rest)
+  DEFAULT: OV511 default values
+  DESC: These are registers 70h - 77h of the OV511, which control the
+	prediction ranges and quantization thresholds of the compressor, for
+	the Y and UV channels in the horizontal and vertical directions. See
+	the OV511 or OV511+ data sheet for more detailed descriptions. These
+	normally do not need to be changed.
+
+  NAME: lightfreq
+  TYPE: integer (0, 50, or 60)
+  DEFAULT: 0 (use sensor default)
+  DESC: Sets the sensor to match your lighting frequency. This can reduce the
+	appearance of "banding", i.e. horizontal lines or waves of light and
+	dark that are often caused by artificial lighting. Valid values are:
+		0 - Use default (depends on sensor, most likely 60 Hz)
+		50 - For European and Asian 50 Hz power
+		60 - For American 60 Hz power
+
+  NAME: bandingfilter
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Enables the sensor´s banding filter exposure algorithm. This reduces
+	or stabilizes the "banding" caused by some artificial light sources
+	(especially fluorescent). You might have to set lightfreq correctly for
+	this to work right. As an added bonus, this sometimes makes it
+	possible to capture your monitor´s output. NOTE: This is not yet
+	working with the OV7620 (it ruins the exposure).
+
+  NAME: ttpp
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Task-Time Post-Processing. Set this to 1 to do post-processing
+	(decompression, format conversion, etc...) at task-time instead of
+	interrupt-time. This only supports RGB and YUV420/420P, and may
+	increase frame latency since all processing is deferred until the frame
+	is totally received. If the benefits of this (simpler code, less lost
+	interrupts) outweigh the costs, this will be default in the future.
+
 WORKING FEATURES:
  o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
  o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
@@ -194,31 +289,32 @@
 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 176x144, 160x120
+ o OV6630 sensor support
+ o Compression support
+ o Banding filter
+ o Task-time post-processing
 
-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 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 OV511+ experiences frame corruption with moving objects
  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
 
 HOW TO CONTACT ME:
 
@@ -232,3 +328,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.2-orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.4.2-orig/drivers/usb/ov511.c	Fri Mar  2 03:21:10 2001
+++ linux/drivers/usb/ov511.c	Tue Mar 27 22:24:30 2001
@@ -1,13 +1,14 @@
 /*
  * OmniVision OV511 Camera-to-USB Bridge Driver
  *
- * Copyright (c) 1999-2000 Mark W. McClelland
+ * Copyright (c) 1999-2001 Mark W. McClelland
+ * Original decompression code Copyright 1998-2000 OmniVision Technologies
  * Many improvements by Bret Wallach <bwallac1@san.rr.com>
  * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
  * Snapshot code by Kevin Moore
  * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
  * Changes by Claudio Matsuoka <claudio@conectiva.com>
- * 
+ *
  * Based on the Linux CPiA driver written by Peter Pregler,
  * Scott J. Bertin and Johannes Erdfelt.
  * 
@@ -30,7 +31,7 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-static const char version[] = "1.28";
+static const char version[] = "1.35";
 
 #define __NO_VERSION__
 
@@ -52,11 +53,17 @@
 #include "ov511.h"
 
 #define OV511_I2C_RETRIES 3
+#define ENABLE_OV511_COMPRESSION
+#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: */
@@ -101,8 +108,7 @@
 /* Prevent apps from timing out if frame is not done in time */
 static int retry_sync = 0;
 
-/* Enable compression. This is for experimentation only; compressed images
- * still cannot be decoded yet. */
+/* Enable compression. Does not work reliably yet, and needs a fast CPU. */
 static int compress = 0;
 
 /* Display test pattern - doesn't work yet either */
@@ -115,6 +121,46 @@
 /* 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 register contents after initialization */
+static int dump_bridge = 0;
+
+/* Set this to 1 to dump the sensor register contents after initialization */
+static int dump_sensor = 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;
+
+/* Compression parameters - I'm not exactly sure what these do yet */
+static int phy = 0x1f;
+static int phuv = 0x05;
+static int pvy = 0x06;
+static int pvuv = 0x06;
+static int qhy = 0x14;
+static int qhuv = 0x03;
+static int qvy = 0x04;
+static int qvuv = 0x04;
+
+/* Light frequency. Set to 50 or 60 (Hz), or zero for default settings */
+static int lightfreq = 0;
+
+/* Set this to 1 to enable banding filter by default. Compensates for alternating
+ * horizontal light/dark bands caused by (usually fluorescent) lights */
+static int bandingfilter = 0;
+
+/* Set this to 1 to do post-processing (decompression, format conversion, etc...) at
+ * task-time instead of interrupt-time. This only supports RGB and YUV420/420P, and may
+ * increase frame latency since all processing is deferred until the frame is totally
+ * received. If the benefits of this (simpler code, less lost interrupts) outweigh the
+ * costs, this will be default in the future.
+ */
+static int ttpp = 0;
+
 MODULE_PARM(autoadjust, "i");
 MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
 MODULE_PARM(debug, "i");
@@ -138,23 +184,53 @@
 MODULE_PARM(retry_sync, "i");
 MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out");
 MODULE_PARM(compress, "i");
-MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
+MODULE_PARM_DESC(compress, "Turn on compression (not reliable 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_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_bridge, "i");
+MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
+MODULE_PARM(dump_sensor, "i");
+MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
+MODULE_PARM(printph, "i");
+MODULE_PARM_DESC(printph, "Print the (potential) packet header of each isoc frame");
+MODULE_PARM(phy, "i");
+MODULE_PARM_DESC(phy, "Prediction range (horizontal Y)");
+MODULE_PARM(phuv, "i");
+MODULE_PARM_DESC(phuv, "Prediction range (horizontal UV)");
+MODULE_PARM(pvy, "i");
+MODULE_PARM_DESC(pvy, "Prediction range (vertical Y)");
+MODULE_PARM(pvuv, "i");
+MODULE_PARM_DESC(pvuv, "Prediction range (vertical UV)");
+MODULE_PARM(qhy, "i");
+MODULE_PARM_DESC(qhy, "Quantization threshold (horizontal Y)");
+MODULE_PARM(qhuv, "i");
+MODULE_PARM_DESC(qhuv, "Quantization threshold (horizontal UV)");
+MODULE_PARM(qvy, "i");
+MODULE_PARM_DESC(qvy, "Quantization threshold (vertical Y)");
+MODULE_PARM(qvuv, "i");
+MODULE_PARM_DESC(qvuv, "Quantization threshold (vertical UV)");
+MODULE_PARM(lightfreq, "i");
+MODULE_PARM_DESC(lightfreq, "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
+MODULE_PARM(bandingfilter, "i");
+MODULE_PARM_DESC(bandingfilter, "Enable banding filter (to reduce effects of fluorescent lighting)");
+MODULE_PARM(ttpp, "i");
+MODULE_PARM_DESC(ttpp, "Do post-processing at task-time instead of interrupt-time (experimental)");
 
-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_AUTHOR("Mark McClelland <mmcclell@bigfoot.com> & 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;
+/* Function prototypes */
+static void ov51x_clear_snapshot(struct usb_ov511 *);
+static int ov51x_check_snapshot(struct usb_ov511 *);
+static inline int ov7610_get_picture(struct usb_ov511 *, struct video_picture *);
 
 /**********************************************************************
  * List of known OV511-based cameras
@@ -162,12 +238,15 @@
 
 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! */
+	{  41, "Samsung Anycam MPC-M10" },
+	{  43, "Mtekvision Zeca MV402" },
 	{ 100, "Lifeview RoboCam" },
 	{ 102, "AverMedia InterCam Elite" },
 	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
@@ -177,6 +256,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 +285,9 @@
 };
 #endif
 
+static unsigned char yQuanTable[] = YQUANTABLE;
+static unsigned char uvQuanTable[] = UVQUANTABLE;
+
 /**********************************************************************
  *
  * Memory management
@@ -322,12 +405,16 @@
 
 #define YES_NO(x) ((x) ? "yes" : "no")
 
-static int ov511_read_proc(char *page, char **start, off_t off,
-                          int count, int *eof, void *data)
+/* /proc/video/ov511/<minor#>/info */
+static int ov511_read_proc_info(char *page, char **start, off_t off,
+                                 int count, int *eof, void *data)
 {
 	char *out = page;
 	int i, j, len;
 	struct usb_ov511 *ov511 = data;
+	struct video_picture p;
+
+	ov7610_get_picture(ov511, &p);
 
 	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
 	 *            or we need to get more sophisticated. */
@@ -343,9 +430,10 @@
 	out += sprintf (out, "sub_size        : %d %d %d %d\n",
 		ov511->subx, ov511->suby, ov511->subw, ov511->subh);
 	out += sprintf (out, "data_format     : %s\n", force_rgb ? "RGB" : "BGR");
-	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, "brightness      : %d\n", p.brightness >> 8);
+	out += sprintf (out, "colour          : %d\n", p.colour >> 8);
+	out += sprintf (out, "contrast        : %d\n", p.contrast >> 8);
+	out += sprintf (out, "hue             : %d\n", p.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 +459,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);
@@ -394,45 +486,109 @@
 	return len;
 }
 
-static int ov511_write_proc(struct file *file, const char *buffer,
-                           unsigned long count, void *data)
+/* /proc/video/ov511/<minor#>/button
+ *
+ * When the camera's button is pressed, the output of this will change from a
+ * 0 to a 1 (ASCII). It will retain this value until it is read, after which
+ * it will reset to zero.
+ * 
+ * SECURITY NOTE: Since reading this file can change the state of the snapshot
+ * status, it is important for applications that open it to keep it locked
+ * against access by other processes, using flock() or a similar mechanism. No
+ * locking is provided by this driver.
+ */
+static int ov511_read_proc_button(char *page, char **start, off_t off,
+                                  int count, int *eof, void *data)
 {
-	return -EINVAL;
+	char *out = page;
+	int len, status;
+	struct usb_ov511 *ov511 = data;
+
+	status = ov51x_check_snapshot(ov511);
+	out += sprintf(out, "%d", status);
+
+	if (status)
+		ov51x_clear_snapshot(ov511);
+
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0) return 0;
+	} else
+		len = count;
+
+	*start = page + off;
+
+	return len;
 }
 
-static void create_proc_ov511_cam (struct usb_ov511 *ov511)
+static void create_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
-	struct proc_dir_entry *ent;
-	
+	char dirname[4];
+
 	if (!ov511_proc_entry || !ov511)
 		return;
 
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "creating /proc/video/ov511/%s", name);
-	
-	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry);
+	/* Create per-device directory */
+	sprintf(dirname, "%d", ov511->vdev.minor);
+	PDEBUG(4, "creating /proc/video/ov511/%s/", dirname);
+	ov511->proc_devdir = create_proc_entry(dirname, S_IFDIR,
+		ov511_proc_entry);
+	if (!ov511->proc_devdir)
+		return;
 
-	if (!ent)
+	/* Create "info" entry (human readable device information) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname);
+	ov511->proc_info = create_proc_read_entry("info",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+		ov511_read_proc_info, ov511);
+	if (!ov511->proc_info)
 		return;
 
-	ent->data = ov511;
-	ent->read_proc = ov511_read_proc;
-	ent->write_proc = ov511_write_proc;
-	ov511->proc_entry = ent;
+	/* Don't create it if old snapshot mode on (would cause race cond.) */
+	if (!snapshot) {
+		/* Create "button" entry (snapshot button status) */
+		PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname);
+		ov511->proc_button = create_proc_read_entry("button",
+			S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+			ov511_read_proc_button, ov511);
+		if (!ov511->proc_button)
+			return;
+	}
+
+	// Will create control device entry here soon
 }
 
-static void destroy_proc_ov511_cam (struct usb_ov511 *ov511)
+static void destroy_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
+	char dirname[4];
 	
-	if (!ov511 || !ov511->proc_entry)
+	if (!ov511 || !ov511->proc_devdir)
 		return;
-	
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "destroying %s", name);
-	remove_proc_entry(name, ov511_proc_entry);
-	ov511->proc_entry = NULL;
+
+	sprintf(dirname, "%d", ov511->vdev.minor);
+
+	// Destroy control device here eventually
+
+	/* Destroy "button" entry */
+	if (ov511->proc_button) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname);
+		remove_proc_entry("button", ov511->proc_devdir);
+		ov511->proc_button = NULL;
+	}
+
+	/* Destroy "info" entry */
+	if (ov511->proc_info) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname);
+		remove_proc_entry("info", ov511->proc_devdir);
+		ov511->proc_info = NULL;
+	}
+
+	/* Destroy per-device directory */
+	PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname);
+	remove_proc_entry(dirname, ov511_proc_entry);
+	ov511->proc_devdir = NULL;
 }
 
 static void proc_ov511_create(void)
@@ -442,7 +598,7 @@
 	 * -claudio
 	 */
 	if (video_proc_entry == NULL) {
-		err("Unable to initialise /proc/video/ov511");
+		err("Error: /proc/video/ does not exist");
 		return;
 	}
 
@@ -451,7 +607,7 @@
 	if (ov511_proc_entry)
 		ov511_proc_entry->owner = THIS_MODULE;
 	else
-		err("Unable to initialise /proc/ov511");
+		err("Unable to create /proc/video/ov511");
 }
 
 static void proc_ov511_destroy(void)
@@ -477,14 +633,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 +659,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 +669,143 @@
 	}
 }
 
+/*
+ * 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;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	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 < 0)
+				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 < 0)
+				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 +845,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 +938,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 +952,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 +1012,64 @@
 	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);
+	/* NOTE: Quantization tables are not readable. You will get the value
+	 * in reg. 0x79 for every table register */
 	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 +1079,120 @@
 
 /* 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));
+}
+
+/* Resets the hardware snapshot button */
+static void ov51x_clear_snapshot(struct usb_ov511 *ov511)
+{
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+	} else if (ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot reset not supported yet on OV518(+)");
+	} else {
+		err("clear snap: invalid sensor type");
+	}
+	
+}
+
+/* Checks the status of the snapshot button. Returns 1 if it was pressed since
+ * it was last cleared, and zero in all other cases (including errors) */
+static int ov51x_check_snapshot(struct usb_ov511 *ov511)
+{
+	int ret, status = 0;
+
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ret = ov511_reg_read(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT);
+		if (ret < 0) {
+			err("Error checking snspshot status (%d)", ret);
+		} else if (ret & 0x08) {
+			status = 1;
+		}
+	} else if (ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot check not supported yet on OV518(+)");
+	} else {
+		err("check snap: invalid sensor type");
+	}
+
+	return status;
+}
+
+/* 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 +1220,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 +1240,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 +1276,7 @@
 
 	PDEBUG(4, "ov511_set_picture");
 
-	if (ov511_stop(dev) < 0)
+	if (ov511_stop(ov511) < 0)
 		return -EIO;
 
 	ov511->contrast = p->contrast;
@@ -840,10 +1307,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;
-	} else if ((ov511->sensor == SEN_OV7620) 
-	         || (ov511->sensor == SEN_OV7620AE)) {
+
+		/* These regs are undocumented. They may be backwards */
+		if (ov511_i2c_write(dev, 0x7a, 0xFF - (p->hue >> 8)) < 0)
+			return -EIO;
+
+		if (ov511_i2c_write(dev, 0x79, p->hue >> 8) < 0)
+			return -EIO;
+
 #if 0
 		int cur_sat, new_sat, tmp;
 
@@ -861,9 +1336,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 +1356,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 +1378,92 @@
 	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;
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ *	50 - 50Hz, for European and Asian lighting
+ *	60 - 60Hz, for American lighting
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620
+ * Returns: 0 for success
+ */
+static inline int
+ov51x_set_light_freq(struct usb_ov511 *ov511, int freq) {
+	int sixty;
+
+	PDEBUG(4, "%d Hz", freq);
+
+	if (freq == 60)
+		sixty = 1;
+	else if (freq == 50)
+		sixty = 0;
+	else {
+		err("Invalid light freq (%d Hz)", freq);
+		return -EINVAL;
+	}
+	
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov511_i2c_write_mask(ov511->dev, 0x2a, sixty?0x00:0x80, 0x80);
+		ov511_i2c_write(ov511->dev, 0x2b, sixty?0x00:0xac);
+		ov511_i2c_write_mask(ov511->dev, 0x13, 0x10, 0x10);
+		ov511_i2c_write_mask(ov511->dev, 0x13, 0x00, 0x10);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov511_i2c_write_mask(ov511->dev, 0x2a, sixty?0x00:0x80, 0x80);
+		ov511_i2c_write(ov511->dev, 0x2b, sixty?0x00:0xac);
+		ov511_i2c_write_mask(ov511->dev, 0x76, 0x01, 0x01);
+		break;		
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov511_i2c_write(ov511->dev, 0x2b, sixty?0xa8:0x28);
+		ov511_i2c_write(ov511->dev, 0x2a, sixty?0x84:0xa4);
+		break;
+	default:
+		err("Sensor not supported for set_light_freq");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's banding filter, otherwise turn it off. This
+ * filter tries to reduce the pattern of horizontal light/dark bands caused by some
+ * (usually fluorescent) lighting. The light frequency must be set either before or
+ * after enabling it with ov51x_set_light_freq().
+ *
+ * Tested with: OV7610, OV7620AE, OV6620. Ruins exposure with OV7620.
+ * Returns: 0 for success
+ */
+static inline int
+ov51x_set_banding_filter(struct usb_ov511 *ov511, int enable)
+{	
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	return (ov511_i2c_write_mask(ov511->dev, 0x2d, enable?0x04:0x00, 0x04));
+}
+
 /* Returns number of bits per pixel (regardless of where they are located; planar or
  * not), or zero for unsupported format.
  */
@@ -918,12 +1477,13 @@
 	case VIDEO_PALETTE_YUYV:    return 16;
 	case VIDEO_PALETTE_YUV420:  return 24;
 	case VIDEO_PALETTE_YUV422P: return 24; /* Planar */
+	case VIDEO_PALETTE_YUV420P: return 24; /* Planar */
 	default:		    return 0;  /* Invalid format */
 	}
 }
 
 /* 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 },
@@ -935,8 +1495,8 @@
 	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
 	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
 	{ 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 176, 144, 0, 0x13, 0x13, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 176, 144, 1, 0x13, 0x13, 0x00, 0x00, 0x03, 0x04, 0x1e },
 	{ 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
 	{ 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
 	{ 0, 0 }
@@ -949,12 +1509,12 @@
 	int i;
 	struct usb_device *dev = ov511->dev;
 	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
-	int hwscale = 0, vwscale = 0;
+	int hoffset, voffset, hwscale = 0, vwscale = 0;
 
 	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 +1527,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,17 +1539,18 @@
 		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);
 		ov511_reg_write(dev, 0x1f, 0x03);
 	}
 
-	/* The different sensor ICs handle setting up of window differently */
+	/* The different sensor ICs handle setting up of window differently.
+	 * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV511!!! */
 	switch (ov511->sensor) {
 	case SEN_OV7610:
 	case SEN_OV7620AE:
@@ -998,6 +1559,7 @@
 		vwsbase = vwebase = 0x05;
 		break;
 	case SEN_OV6620:
+	case SEN_OV6630:	// FIXME: Is this right?
 		hwsbase = 0x38;
 		hwebase = 0x3a;
 		vwsbase = 0x05;
@@ -1042,6 +1604,10 @@
 		}	
 	}
 
+	/* Center the window */
+	hoffset = ((hwsize - width) / 2) >> hwscale;
+	voffset = ((vwsize - height) / 2) >> vwscale;
+
 	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
 	if (sub_flag) {
 		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale));
@@ -1049,102 +1615,459 @@
 		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));
+		ov511_i2c_write(dev, 0x17, hwsbase + hoffset);
+		ov511_i2c_write(dev, 0x18, hwebase + hoffset + (hwsize>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase + voffset);
+		ov511_i2c_write(dev, 0x1a, vwebase + voffset + (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);
 
-		/* Calculate and set the clock divisor */
-		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
-			* (mlist[i].color ? 3 : 2) / 2) / 66000;
+		}
+		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;
+			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, mlist511[i].common_A | (testpat?0x0a:0x08));
 		else
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+			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 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, mlist511[i].common_L);
 
 		break;
 	}
 
+	if (lightfreq)
+		if (ov51x_set_light_freq(ov511, lightfreq))
+			return -1;
+
+	if (ov51x_set_banding_filter(ov511, bandingfilter))
+			return -1;
+
 	if (compress) {
-		ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
-		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
+		ov511_reg_write(dev, 0x78, 0x07); // Turn on Y and UV compression
+		ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs
+		ov511_reset(ov511, OV511_RESET_OMNICE);
 	}
 
-	if (ov511_restart(ov511->dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
-	if (mlist[i].width == 0) {
+	if (mlist511[i].width == 0) {
 		err("Unknown mode (%d, %d): %d", width, height, mode);
 		return -EINVAL;
 	}
 
 #ifdef OV511_DEBUG
-	if (debug >= 5)
+	if (dump_sensor)
 		ov511_dump_i2c_regs(dev);
 #endif
 
 	return 0;
 }
 
-/**********************************************************************
- *
- * Color correction functions
- *
- **********************************************************************/
+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 }
+};
 
-/*
- * Turn a YUV4:2:0 block into an RGB block
- *
- * Video4Linux seems to use the blue, green, red channel
- * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
- *
- * Color space conversion coefficients taken from the excellent
- * http://www.inforamp.net/~poynton/ColorFAQ.html
- * In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
- * Y values are given for all 4 pixels, but the U (Pb)
- * and V (Pr) are assumed constant over the 2x2 block.
- *
- * To avoid floating point arithmetic, the color conversion
- * coefficients are scaled into 16.16 fixed-point integers.
+/* 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 hoffset, voffset, 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_OV6630) {
+		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;
+		}	
+	}
+
+	/* Center the window */
+	hoffset = ((hwsize - width) / 2) >> hwscale;
+	voffset = ((vwsize - height) / 2) >> vwscale;
+
+	/* 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 + hoffset);
+		ov511_i2c_write(dev, 0x18, hwebase + hoffset + (hwsize>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase + voffset);
+		ov511_i2c_write(dev, 0x1a, vwebase + voffset + (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;
+#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, 0x04 | (testpat?0x0a:0x08));
+		else
+			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/6630? don't have register 0x35, so play it safe */
+		if (ov511->sensor == SEN_OV7610 ||
+		    ov511->sensor == SEN_OV7620AE)
+			ov511_i2c_write(dev, 0x35, 0x1e);
+
+		break;
+	}
+
+	if (lightfreq)
+		if (ov51x_set_light_freq(ov511, lightfreq))
+			return -1;
+
+	if (ov51x_set_banding_filter(ov511, bandingfilter))
+			return -1;
+
+	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 (mlist518[i].width == 0) {
+		err("Unknown mode (%d, %d): %d", width, height, mode);
+		return -EINVAL;
+	}
+
+#ifdef OV511_DEBUG
+	if (dump_sensor)
+		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
+ *
+ **********************************************************************/
+
+/*
+ * Turn a YUV4:2:0 block into an RGB block
+ *
+ * Video4Linux seems to use the blue, green, red channel
+ * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red.
+ *
+ * Color space conversion coefficients taken from the excellent
+ * http://www.inforamp.net/~poynton/ColorFAQ.html
+ * In his terminology, this is a CCIR 601.1 YCbCr -> RGB.
+ * Y values are given for all 4 pixels, but the U (Pb)
+ * and V (Pr) are assumed constant over the 2x2 block.
+ *
+ * To avoid floating point arithmetic, the color conversion
+ * coefficients are scaled into 16.16 fixed-point integers.
  * They were determined as follows:
  *
  *	double brightness = 1.0;  (0->black; 1->full scale) 
@@ -1373,7 +2296,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 +2334,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 +2546,1625 @@
 
 /**********************************************************************
  *
+ * Decompression
+ *
+ **********************************************************************/
+
+/* Large memory buffer allocation. */
+#define ALLOCATE(a, size, b) vmalloc(size)
+#define FREE(addr) vfree(addr)
+
+#ifdef ENABLE_OV511_COMPRESSION
+
+/* Tables */
+static UCHAR YQuanTable[] = YQUANTABLE;
+static UCHAR UVQuanTable[] = UVQUANTABLE;
+static SHORT IDCTTable[] = IDCTTABLE;
+static ULONG ZigZagTable[] = ZIGZAGTABLE;
+
+static PUCHAR GetYQuanTable(void)
+{
+	return YQuanTable;
+}
+
+static PUCHAR GetUVQuanTable(void)
+{
+	return UVQuanTable;
+}
+
+static PSHORT GetIDCTTable(void)
+{
+	return IDCTTable;
+}
+
+static PULONG GetZigZagTable(void)
+{
+	return ZigZagTable;
+}
+
+/************************************************************************/
+/* Avoid changing anything below this point, to make synchronizing with */
+/* newer code from OmniVision easier.					*/
+/************************************************************************/
+
+static int DecompressYHINoMMX(	LPBYTE    lpDestBuf,
+				LPBYTE    lpTMPBUF,
+				ULONG     index,
+				DWORD     IPTRR,
+				DWORD     X_WIDE,
+				PUCHAR    TableBufferYUV,
+				PSHORT    IDCT_Coef,
+				PULONG    ZZLN,
+				DWORDLONG UV_Scalor,
+				ULONG     YUVFlag,
+				struct    ov511_frame *frame)
+{
+	short ZigZag[64];
+	int temp[64];
+	int Zcnt_Flag=0;
+	int Num8_Flag=0;
+	int tmp, tmp1, tmp2, tmp3;
+	unsigned char header, ZTable[64];
+	short tmpl, tmph, half_byte, idx, count;
+	DWORD ZigZag_length=0, ZT_length, i, j;
+//	DWORD Start, k;
+	short DeZigZag[64]; 
+
+	const short a = 11584;
+	const short b = 16068;
+	const short c = 15136;
+	const short d = 13624;
+	const short e =  9104;
+	const short f =  6270;
+	const short g =  3196;
+
+	int	IPTRR_YUV;
+
+#if 0
+	int	 coef[64] = 
+	{
+		11584,  11584,  11584,  11584,  11584,  11584,  11584,  11584,
+		16068,  13624,   9104,   3196,  -3196,  -9104, -13624, -16068,
+		15136,   6270,  -6270, -15136, -15136,  -6270,   6270,  15136,
+		13624,  -3196, -16068,  -9104,   9104,  16068,   3196, -13624,
+		11584, -11584, -11584,  11584,  11584, -11584, -11584,  11584,
+		 9104, -16068,   3196,  13624, -13624,  -3196,  16068,  -9104,
+		 6270, -15136,  15136,  -6270,  -6270,  15136, -15136,   6270,
+		 3196,  -9104,  13624, -16068,  16068, -13624,   9104,  -3196
+	};
+
+	int	 coeft[64] = 
+	{
+		11584,  16068,  15136,  13624,  11584,   9104,   6270,   3196, 
+		11584,  13624,   6270,  -3196, -11584, -16068, -15136,  -9104, 
+		11584,   9104,  -6270, -16068, -11584,   3196,  15136,  13624, 
+		11584,   3196, -15136,  -9104,  11584,  13624,  -6270, -16068, 
+		11584,  -3196, -15136,   9104,  11584, -13624,  -6270,  16068, 
+		11584,  -9104,  -6270,  16068, -11584,  -3196,  15136, -13624, 
+		11584, -13624,   6270,   3196, -11584,  16068, -15136,   9104, 
+		11584, -16068,  15136, -13624,  11584,  -9104,   6270,  -3196
+	};
+#endif
+
+	for(i=0; i<64; i++)
+	{
+		ZigZag[i] = 0;
+	}
+
+
+// Read in the Y header byte  
+
+
+	header = lpTMPBUF[index];
+	index++;
+
+	ZigZag_length = header & 0x3f;
+	ZigZag_length = ZigZag_length + 1;
+
+	Num8_Flag = header & 0x40;
+	Zcnt_Flag = header & 0x80;
+
+// Read in the Y content
+#if 1
+	if(Zcnt_Flag==0)  // Without Zero Table Read contents directly
+	{
+
+		// Read in ZigZag[0]
+		ZigZag[0] = lpTMPBUF[index++];
+		tmpl = lpTMPBUF[index++];
+		tmph = tmpl<<8;
+		ZigZag[0] = ZigZag[0] | tmph;
+		ZigZag[0] = ZigZag[0]<<4;
+		ZigZag[0] = ZigZag[0]>>4;
+
+		if(Num8_Flag)  // 8 Bits
+		{
+			for(i=1; i<ZigZag_length; i++)
+			{
+				ZigZag[i] = lpTMPBUF[index++];
+				ZigZag[i] = ZigZag[i]<<8;
+				ZigZag[i] = ZigZag[i]>>8;
+
+			}
+		}
+		else     // 12 bits and has no Zero table
+		{
+
+				idx=1;
+				half_byte=0;
+				for(i=1; i<ZigZag_length; i++)
+				{
+					if(half_byte==0)
+					{
+						ZigZag[i] = lpTMPBUF[index++];
+						tmpl = lpTMPBUF[index++];
+						tmph = tmpl<<8;
+						tmph = tmph&0x0f00;
+						ZigZag[i] = ZigZag[i] | tmph;
+						ZigZag[i] = ZigZag[i]<<4;
+						ZigZag[i] = ZigZag[i]>>4;
+						half_byte=1;
+					}
+					else
+					{
+						ZigZag[i] = lpTMPBUF[index++];
+						ZigZag[i] = ZigZag[i]<<8;
+						tmpl = tmpl & 0x00f0;
+						ZigZag[i] = ZigZag[i] | tmpl;
+						ZigZag[i] = ZigZag[i]>>4;
+						half_byte=0;
+					}
+				}
+
+		}	// end of Num8_Flag=0
+	}	//  end of without Zero Table
+	else  // Has Zero Table
+	{
+
+			// calculate Z-Table length
+			ZT_length = ZigZag_length/8;
+			tmp = ZigZag_length%8;
+			if(tmp> 0)
+			{
+				ZT_length = ZT_length + 1;
+			}
+
+			// read in Zero Table
+			for(j=0; j<ZT_length; j++)
+			{
+ 
+				ZTable[j]=lpTMPBUF[index++];
+
+			}
+
+			// Read in ZigZag[0]
+			ZigZag[0] = lpTMPBUF[index++];
+			tmpl = lpTMPBUF[index++];
+			tmph = tmpl<<8;
+			ZigZag[0] = ZigZag[0] | tmph;
+			ZigZag[0] = ZigZag[0]<<4;
+			ZigZag[0] = ZigZag[0]>>4;
+
+			// Decode ZiaZag
+			idx=0;
+			ZTable[idx]=ZTable[idx]<<1;
+			count=7;
+
+		if(Num8_Flag)  // 8 Bits and has zero table
+		{
+
+			for( i=1; i<ZigZag_length; i++)
+			{
+//				tmp=(ZTable[idx]&0x80);
+//				if(tmp)
+				if((ZTable[idx]&0x80))
+				{
+					ZigZag[i] = lpTMPBUF[index++];
+					ZigZag[i] = ZigZag[i]<<8;
+					ZigZag[i] = ZigZag[i]>>8;
+				}
+				
+				ZTable[idx]=ZTable[idx]<<1;
+				count--;
+				if(count==0)
+				{
+					count=8;
+					idx++;
+						
+				}
+			}
+
+		}// end of 8 bits with zero table
+		else   // 12bits has zero table
+		{
+
+
+			half_byte=0;
+			for( i=1; i<ZigZag_length; i++)
+			{
+
+				if(ZTable[idx]&0x80)
+				{
+					if(half_byte==0)
+					{
+						ZigZag[i] = lpTMPBUF[index++];
+						tmpl = lpTMPBUF[index++];
+						tmph = tmpl <<8;
+						tmph = tmph & 0x0f00;
+						ZigZag[i] = ZigZag[i] | tmph;
+						ZigZag[i] = ZigZag[i]<<4;
+						ZigZag[i] = ZigZag[i]>>4;
+						half_byte=1;
+					}
+					else
+					{
+						ZigZag[i] = lpTMPBUF[index++];
+						ZigZag[i] = ZigZag[i]<<8;
+						tmpl = tmpl & 0x00f0;
+						ZigZag[i] = ZigZag[i] | tmpl;
+						ZigZag[i] = ZigZag[i]>>4;
+						half_byte=0;
+					}
+				}
+				
+				ZTable[idx]=ZTable[idx]<<1;
+				count--;
+				if(count==0)
+				{
+					count=8;
+					idx++;
+						
+				}
+			}
+
+		}// end of 12 bits with zero table
+			
+	}		
+#endif 
+
+
+#if 0
+	//DeZigZag
+	idx=0;
+	for(i=0; i<8; i++)
+	{
+		for(j=0; j<8; j++)
+		{
+
+			DeZigZag[idx] = ZigZag[ZZTB[idx]];
+			idx++;
+		}
+	}
+#endif
+#if 1	
+	for(j=0; j<64; j++)
+	{
+
+		DeZigZag[j] = 0;
+	}
+
+	if(YUVFlag == 1)
+	{
+	DeZigZag[0] = ZigZag[0];
+	DeZigZag[1] = ZigZag[1]<<1;
+	DeZigZag[2] = ZigZag[5]<<1;
+	DeZigZag[3] = ZigZag[6]<<2;
+
+	DeZigZag[8] = ZigZag[2]<<1;
+	DeZigZag[9] = ZigZag[4]<<1;
+	DeZigZag[10] = ZigZag[7]<<1;
+	DeZigZag[11] = ZigZag[13]<<2;
+
+	DeZigZag[16] = ZigZag[3]<<1;
+	DeZigZag[17] = ZigZag[8]<<1;
+	DeZigZag[18] = ZigZag[12]<<2;
+	DeZigZag[19] = ZigZag[17]<<2;
+
+	DeZigZag[24] = ZigZag[9]<<2;
+	DeZigZag[25] = ZigZag[11]<<2;
+	DeZigZag[26] = ZigZag[18]<<2;
+	DeZigZag[27] = ZigZag[24]<<3;
+	}
+	else 
+	{
+	DeZigZag[0] = ZigZag[0];
+	DeZigZag[1] = ZigZag[1]<<2;
+	DeZigZag[2] = ZigZag[5]<<2;
+	DeZigZag[3] = ZigZag[6]<<3;
+
+	DeZigZag[8] = ZigZag[2]<<2;
+	DeZigZag[9] = ZigZag[4]<<2;
+	DeZigZag[10] = ZigZag[7]<<2;
+	DeZigZag[11] = ZigZag[13]<<4;
+
+	DeZigZag[16] = ZigZag[3]<<2;
+	DeZigZag[17] = ZigZag[8]<<2;
+	DeZigZag[18] = ZigZag[12]<<3;
+	DeZigZag[19] = ZigZag[17]<<4;
+
+	DeZigZag[24] = ZigZag[9]<<3;
+	DeZigZag[25] = ZigZag[11]<<4;
+	DeZigZag[26] = ZigZag[18]<<4;
+	DeZigZag[27] = ZigZag[24]<<4;
+	}
+#endif 
+
+#if 0
+	// Dequante
+	if(YUVFlag == 1)
+	{
+		// Dequante Y
+		for(i=0; i<64; i++)
+		{
+			ZigZag[i] = DeZigZag[i]<<DQTY[i];
+		}
+	}
+	else
+	{
+		// Dequante UV
+		for(i=0; i<64; i++)
+		{
+			ZigZag[i] = DeZigZag[i]<<DQTUV[i];
+		}
+	}
+#endif 
+#if 1
+	// =============
+	// == IDCT 1D ==
+	// =============
+
+
+//	for(i=0;i<8; i++)
+//	{
+//		for(j=0; j<4; j++)
+//		{
+			tmp1=a*DeZigZag[0]+c*DeZigZag[2];
+			tmp2=b*DeZigZag[1];
+			tmp3=d*DeZigZag[3];
+
+			tmp=tmp1+tmp2+tmp3;
+			temp[0] = tmp>>15;
+
+			tmp=tmp1-tmp2-tmp3;
+			temp[56] = tmp>>15;
+
+			tmp1=a*DeZigZag[8]+c*DeZigZag[10];
+			tmp2=b*DeZigZag[9];
+			tmp3=d*DeZigZag[11];
+
+			tmp=tmp1+tmp2+tmp3;
+			temp[1] = tmp>>15;
+
+			tmp=tmp1-tmp2-tmp3;
+			temp[57] = tmp>>15;
+
+			tmp1=a*DeZigZag[16]+c*DeZigZag[18];
+			tmp2=b*DeZigZag[17];
+			tmp3=d*DeZigZag[19];
+
+			tmp=tmp1+tmp2+tmp3;
+			temp[2] = tmp>>15;
+
+			tmp=tmp1-tmp2-tmp3;
+			temp[58] = tmp>>15;
+
+			tmp1=a*DeZigZag[24]+c*DeZigZag[26];
+			tmp2=b*DeZigZag[25];
+			tmp3=d*DeZigZag[27];
+
+			tmp=tmp1+tmp2+tmp3;
+			temp[3] = tmp>>15;
+
+			tmp=tmp1-tmp2-tmp3;
+			temp[59] = tmp>>15;
+
+//		for(j=0; j<4; j++)
+//		{
+			tmp1=a*DeZigZag[0]+f*DeZigZag[2];
+			tmp2=d*DeZigZag[1];
+			tmp3=g*DeZigZag[3];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[8] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[48] = tmp>>15;
+
+			tmp1=a*DeZigZag[8]+f*DeZigZag[10];
+			tmp2=d*DeZigZag[9];
+			tmp3=g*DeZigZag[11];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[9] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[49] = tmp>>15;
+
+			tmp1=a*DeZigZag[16]+f*DeZigZag[18];
+			tmp2=d*DeZigZag[17];
+			tmp3=g*DeZigZag[19];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[10] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[50] = tmp>>15;
+
+			tmp1=a*DeZigZag[24]+f*DeZigZag[26];
+			tmp2=d*DeZigZag[25];
+			tmp3=g*DeZigZag[27];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[11] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[51] = tmp>>15;
+//		}
+
+//		for(j=0; j<4; j++)
+//		{
+			tmp1=a*DeZigZag[0]-f*DeZigZag[2];
+			tmp2=e*DeZigZag[1];
+			tmp3=b*DeZigZag[3];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[16] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[40] = tmp>>15;
+
+			tmp1=a*DeZigZag[8]-f*DeZigZag[10];
+			tmp2=e*DeZigZag[9];
+			tmp3=b*DeZigZag[11];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[17] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[41] = tmp>>15;
+
+			tmp1=a*DeZigZag[16]-f*DeZigZag[18];
+			tmp2=e*DeZigZag[17];
+			tmp3=b*DeZigZag[19];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[18] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[42] = tmp>>15;
+
+			tmp1=a*DeZigZag[24]-f*DeZigZag[26];
+			tmp2=e*DeZigZag[25];
+			tmp3=b*DeZigZag[27];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[19] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[43] = tmp>>15;
+//		}
+
+//		for(j=0; j<4; j++)
+//		{
+			tmp1=a*DeZigZag[0]-c*DeZigZag[2];
+			tmp2=g*DeZigZag[1];
+			tmp3=e*DeZigZag[3];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[24] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[32] = tmp>>15;
+
+			tmp1=a*DeZigZag[8]-c*DeZigZag[10];
+			tmp2=g*DeZigZag[9];
+			tmp3=e*DeZigZag[11];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[25] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[33] = tmp>>15;
+
+			tmp1=a*DeZigZag[16]-c*DeZigZag[18];
+			tmp2=g*DeZigZag[17];
+			tmp3=e*DeZigZag[19];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[26] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[34] = tmp>>15;
+
+			tmp1=a*DeZigZag[24]-c*DeZigZag[26];
+			tmp2=g*DeZigZag[25];
+			tmp3=e*DeZigZag[27];
+
+			tmp=tmp1+tmp2-tmp3;
+			temp[27] = tmp>>15;
+
+			tmp=tmp1-tmp2+tmp3;
+			temp[35] = tmp>>15;
+//		}
+		
+//		for(j=0; j<4; j++)
+//		{
+/*			
+			tmp = 0;
+			for(k=0; k<4; k++)
+			{
+				tmp = tmp + coeft[32+k]*DeZigZag[8*j + k];
+			}
+			temp[32+j] = tmp>>15;
+*/	
+//		}
+
+//		for(j=0; j<4; j++)
+//		{
+			
+/*			tmp=a*DeZigZag[0] - e*DeZigZag[1] - f*DeZigZag[2] + b*DeZigZag[3];
+			temp[40] = tmp>>15;
+			
+			tmp=a*DeZigZag[8] - e*DeZigZag[9] - f*DeZigZag[10] + b*DeZigZag[11];
+			temp[41] = tmp>>15;
+			
+			tmp=a*DeZigZag[16] - e*DeZigZag[17] - f*DeZigZag[18] + b*DeZigZag[19];
+			temp[42] = tmp>>15;
+			
+			tmp=a*DeZigZag[24] - e*DeZigZag[25] - f*DeZigZag[26] + b*DeZigZag[27];
+			temp[43] = tmp>>15;
+*//*
+			tmp = 0;
+			for(k=0; k<4; k++)
+			{
+				tmp = tmp + coeft[40 + k]*DeZigZag[8*j + k];
+			}
+			temp[40+j] = tmp>>15;
+*/
+//		}
+
+//		for(j=0; j<4; j++)
+//		{
+			
+/*			tmp=a*DeZigZag[0] - d*DeZigZag[1]+ f*DeZigZag[2] + g*DeZigZag[3];
+			temp[48] = tmp>>15;
+			
+			tmp=a*DeZigZag[8] - d*DeZigZag[9] + f*DeZigZag[10] + g*DeZigZag[11];
+			temp[49] = tmp>>15;
+			
+			tmp=a*DeZigZag[16] - d*DeZigZag[17] + f*DeZigZag[18] + g*DeZigZag[19];
+			temp[50] = tmp>>15;
+			
+			tmp=a*DeZigZag[24] - d*DeZigZag[25] + f*DeZigZag[26] + g*DeZigZag[27];
+			temp[51] = tmp>>15;
+*/
+//		}
+
+//		for(j=0; j<4; j++)
+//		{
+			
+/*			
+			tmp = a*DeZigZag[0] - b*DeZigZag[1]+ c*DeZigZag[2] - d*DeZigZag[3];
+			temp[56] = tmp>>15;
+			
+			tmp = a*DeZigZag[8] - b*DeZigZag[9]+ c*DeZigZag[10] - d*DeZigZag[11];
+			temp[57] = tmp>>15;
+			
+			tmp = a*DeZigZag[16] - b*DeZigZag[17]+ c*DeZigZag[18] - d*DeZigZag[19];
+			temp[58] = tmp>>15;
+			
+			tmp = a*DeZigZag[24] - b*DeZigZag[25]+ c*DeZigZag[26] - d*DeZigZag[27];
+			temp[59] = tmp>>15;
+*/
+
+//		}
+
+
+//	}
+
+#endif 
+	// =============
+	// == IDCT 2D ==
+	// =============
+#if 1
+//	for(i=0; i<8; i++)
+//	{
+			IPTRR_YUV = IPTRR;
+
+//      i=0	
+//		for(j=0; j<8; j++)
+//		{ 
+			// j=0
+			tmp = temp[0]*a+temp[1]*b+temp[2]*c+temp[3]*d;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[0+IPTRR] = (BYTE) tmp;		
+
+			// j=1
+			tmp = temp[0]*a+temp[1]*d+temp[2]*f+temp[3]*(-g);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[1+IPTRR] = (BYTE) tmp;		
+
+			// j=2
+			tmp = temp[0]*a+temp[1]*e+temp[2]*(-f)+temp[3]*(-b);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[2+IPTRR] = (BYTE) tmp;		
+
+			// j=3
+			tmp = temp[0]*a+temp[1]*g+temp[2]*(-c)+temp[3]*(-e);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[3+IPTRR] = (BYTE) tmp;	
+
+			// j=4
+			tmp = temp[0]*a+temp[1]*(-g)+temp[2]*(-c)+temp[3]*e;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[4+IPTRR] = (BYTE) tmp;		
+
+			// j=5
+			tmp = temp[0]*a+temp[1]*(-e)+temp[2]*(-f)+temp[3]*b;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[5+IPTRR] = (BYTE) tmp;		
+
+			// j=6
+			tmp = temp[0]*a+temp[1]*(-d)+temp[2]*f+temp[3]*g;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[6+IPTRR] = (BYTE) tmp;		
+
+			// j=7
+			tmp = temp[0]*a+temp[1]*(-b)+temp[2]*c+temp[3]*(-d);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[7+IPTRR] = (BYTE) tmp;		
+//		}
+
+//		i=1
+//		for(j=0; j<8; j++)
+//		{ 
+			IPTRR_YUV = X_WIDE+IPTRR_YUV;
+			// j=0
+			tmp = temp[8]*a+temp[9]*b+temp[10]*c+temp[11]*d;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=1
+			tmp = temp[8]*a+temp[9]*d+temp[10]*f+temp[11]*(-g);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=2
+			tmp = temp[8]*a+temp[9]*e+temp[10]*(-f)+temp[11]*(-b);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=3
+			tmp = temp[8]*a+temp[9]*g+temp[10]*(-c)+temp[11]*(-e);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp = temp[8]*a+temp[9]*(-g)+temp[10]*(-c)+temp[11]*e;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+
+			// j=5
+			tmp = temp[8]*a+temp[9]*(-e)+temp[10]*(-f)+temp[11]*b;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			// j=6
+			tmp = temp[8]*a+temp[9]*(-d)+temp[10]*f+temp[11]*g;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+
+			// j=7
+			tmp = temp[8]*a+temp[9]*(-b)+temp[10]*c+temp[11]*(-d);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+//		}
+
+		IPTRR_YUV = X_WIDE+IPTRR_YUV;
+
+//		i=2
+//		for(j=0; j<8; j++)
+//		{ 
+			// j=0
+			tmp = temp[16]*a+temp[17]*b+temp[18]*c+temp[19]*d;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=1
+			tmp = temp[16]*a+temp[17]*d+temp[18]*f+temp[19]*(-g);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=2
+			tmp = temp[16]*a+temp[17]*e+temp[18]*(-f)+temp[19]*(-b);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=3
+			tmp = temp[16]*a+temp[17]*g+temp[18]*(-c)+temp[19]*(-e);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp = temp[16]*a+temp[17]*(-g)+temp[18]*(-c)+temp[19]*e;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+
+			// j=5
+			tmp = temp[16]*a+temp[17]*(-e)+temp[18]*(-f)+temp[19]*b;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			// j=6
+			tmp = temp[16]*a+temp[17]*(-d)+temp[18]*f+temp[19]*g;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+
+			// j=7
+			tmp = temp[16]*a+temp[17]*(-b)+temp[18]*c+temp[19]*(-d);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+//		}
+		IPTRR_YUV = X_WIDE+IPTRR_YUV;
+//		i=3
+//		for(j=0; j<8; j++)
+//		{ 
+			// j=0
+			tmp = temp[24]*a+temp[25]*b+temp[26]*c+temp[27]*d;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=1
+			tmp = temp[24]*a+temp[25]*d+temp[26]*f+temp[27]*(-g);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=2
+			tmp = temp[24]*a+temp[25]*e+temp[26]*(-f)+temp[27]*(-b);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=3
+			tmp = temp[24]*a+temp[25]*g+temp[26]*(-c)+temp[27]*(-e);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp = temp[24]*a+temp[25]*(-g)+temp[26]*(-c)+temp[27]*e;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+
+			// j=5
+			tmp = temp[24]*a+temp[25]*(-e)+temp[26]*(-f)+temp[27]*b;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			// j=6
+			tmp = temp[24]*a+temp[25]*(-d)+temp[26]*f+temp[27]*g;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+
+			// j=7
+			tmp = temp[24]*a+temp[25]*(-b)+temp[26]*c+temp[27]*(-d);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+//		}
+		IPTRR_YUV = X_WIDE+IPTRR_YUV;
+//		i=4
+//		for(j=0; j<8; j++)
+//		{ 
+			// j=0
+			tmp = temp[32]*a+temp[33]*b+temp[34]*c+temp[35]*d;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=1
+			tmp = temp[32]*a+temp[33]*d+temp[34]*f+temp[35]*(-g);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=2
+			tmp = temp[32]*a+temp[33]*e+temp[34]*(-f)+temp[35]*(-b);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=3
+			tmp = temp[32]*a+temp[33]*g+temp[34]*(-c)+temp[35]*(-e);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp = temp[32]*a+temp[33]*(-g)+temp[34]*(-c)+temp[35]*e;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+
+			// j=5
+			tmp = temp[32]*a+temp[33]*(-e)+temp[34]*(-f)+temp[35]*b;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			// j=6
+			tmp = temp[32]*a+temp[33]*(-d)+temp[34]*f+temp[35]*g;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+
+			// j=7
+			tmp = temp[32]*a+temp[33]*(-b)+temp[34]*c+temp[35]*(-d);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+//		}
+		IPTRR_YUV = X_WIDE+IPTRR_YUV;
+//		i=5
+//		for(j=0; j<8; j++)
+//		{ 
+			// j=0
+			tmp = temp[40]*a+temp[41]*b+temp[42]*c+temp[43]*d;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=1
+			tmp = temp[40]*a+temp[41]*d+temp[42]*f+temp[43]*(-g);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=2
+			tmp = temp[40]*a+temp[41]*e+temp[42]*(-f)+temp[43]*(-b);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=3
+			tmp = temp[40]*a+temp[41]*g+temp[42]*(-c)+temp[43]*(-e);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp = temp[40]*a+temp[41]*(-g)+temp[42]*(-c)+temp[43]*e;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+
+			// j=5
+			tmp = temp[40]*a+temp[41]*(-e)+temp[42]*(-f)+temp[43]*b;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			// j=6
+			tmp = temp[40]*a+temp[41]*(-d)+temp[42]*f+temp[43]*g;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+
+			// j=7
+			tmp = temp[40]*a+temp[41]*(-b)+temp[42]*c+temp[43]*(-d);
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+
+//		}
+		IPTRR_YUV = X_WIDE+IPTRR_YUV;
+//		i=6
+//		for(j=0; j<8; j++)
+//		{ 
+			tmp1=temp[48]*a+temp[50]*c;
+			tmp2=temp[49]*b;
+			tmp3=temp[51]*d;
+			// j=0
+			tmp=tmp1+tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=7
+			tmp=tmp1-tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+
+			tmp1=temp[48]*a+temp[50]*f;
+			tmp2=temp[49]*d;
+			tmp3=temp[51]*g;
+			// j=1
+			tmp=tmp1+tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=6
+			tmp=tmp1-tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+			
+			tmp1=temp[48]*a-temp[50]*f;
+			tmp2=temp[49]*e;
+			tmp3=temp[51]*b;
+			// j=2
+			tmp=tmp1+tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=5
+			tmp=tmp1-tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			tmp1=temp[48]*a-temp[50]*c;
+			tmp2=temp[49]*g;
+			tmp3=temp[51]*e;
+			// j=3
+			tmp=tmp1+tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp=tmp1-tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+//		}
+		IPTRR_YUV = X_WIDE+IPTRR_YUV;
+//		i=7
+//		for(j=0; j<8; j++)
+//		{ 
+			tmp1=temp[56]*a+temp[58]*c;
+			tmp2=temp[57]*b;
+			tmp3=temp[59]*d;
+
+			// j=0
+			tmp=tmp1+tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV] = (BYTE) tmp;		
+
+			// j=7
+			tmp=tmp1-tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+7] = (BYTE) tmp;		
+
+			tmp1=temp[56]*a+temp[58]*f;
+			tmp2=temp[57]*d;
+			tmp3=temp[59]*g;
+
+			// j=1
+			tmp=tmp1+tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+1] = (BYTE) tmp;
+
+			// j=6
+			tmp=tmp1-tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+6] = (BYTE) tmp;		
+
+			tmp1=temp[56]*a-temp[58]*f;
+			tmp2=temp[57]*e;
+			tmp3=temp[59]*b;
+
+			// j=2
+			tmp=tmp1+tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+2] = (BYTE) tmp;
+
+			// j=5
+			tmp=tmp1-tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+5] = (BYTE) tmp;		
+
+			tmp1=temp[56]*a-temp[58]*c;
+			tmp2=temp[57]*g;
+			tmp3=temp[59]*e;
+
+			// j=3
+			tmp=tmp1+tmp2-tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+3] = (BYTE) tmp;		
+
+			// j=4
+			tmp=tmp1-tmp2+tmp3;
+			tmp = tmp>>15;
+			tmp=tmp+128;
+			if(tmp >255) tmp=255;
+			if(tmp < 0 ) tmp=0;
+			lpDestBuf[IPTRR_YUV+4] = (BYTE) tmp;		
+
+//		}
+
+	
+//	}
+
+#endif 
+	frame->TempInfo.index=index;
+	frame->TempInfo.IPTRR_U=IPTRR+8;
+
+	return 0;
+} 
+
+static NTSTATUS 
+Decompress420HiNoMMX(
+	PUCHAR  pY,
+	ULONG   FrameLength,
+	ULONG   X_SIZE, 
+	ULONG   Y_SIZE, 
+	PUCHAR	TableBuffer,
+	PUCHAR	TableUVBuffer,
+	PSHORT  CoefBuffer,
+	PULONG  ZigZagTable,
+	PUCHAR  RawFrameBuffer,
+	ULONG   ProcessedDataLength,	
+	PUCHAR  TempBuffer,
+	struct  ov511_frame *frame)
+
+{
+
+// Begin of decoder definition 
+//POVTCAM_DEVICE_CONTEXT deviceContext = DeviceContext;
+LPBYTE lpYUV, lpU, lpV, lpTMPBUF;
+ULONG	PTRX, PTRY;
+// ULONG i;
+ULONG	IPTRR, IPTRR_U, IPTRR_V;
+//ULONG   ipm, tms, Tcnt=1600;
+ULONG   index;
+ULONG   SrcUVDataLength;
+//ULONG   SrcYDataLength;
+//short  a,b,c,d,e,mid;
+
+PUCHAR  TempBuffU = NULL; 
+PUCHAR  TempBuffV = NULL; 
+PUCHAR  TempBuffY = NULL; 
+
+DWORDLONG   Y_Scalor=0x7fff7fff7fff7fff;
+
+//   u=0.564
+DWORDLONG   U_Scalor=0x4831483148314831;
+//   v=0.713
+DWORDLONG   V_Scalor=0x5b435b435b435b43;
+
+//DWORDLONG   U_Scalor=0x7fff7fff7fff7fff;
+//DWORDLONG   V_Scalor=0x7fff7fff7fff7fff;
+//DWORDLONG   CONST128=0x8080808080808080;
+//DWORDLONG   CONST128=0x0080008000800080; /* Previously disabled by OVT */
+//DWORDLONG   Const4000=0x4000400040004000;
+
+
+// 	TempBuffer = deviceContext->TempBuffer;
+//	TableBuffer = deviceContext->TableBuffer;  //TempBuffer;
+//	CoefBuffer = deviceContext->CoefBuffer;  //TempBuffer;
+ //	ZigZagTable = deviceContext->ZigZagTable;
+ 	lpTMPBUF = RawFrameBuffer; // + 9;  //TempBuffer;
+
+//ProcessedDataLength = deviceContext->ProcessedDataLength;
+//ProcessedDataLength = ProcessedDataLength/8;
+//ProcessedDataLength = ProcessedDataLength*8 + 8;
+
+SrcUVDataLength = X_SIZE  * Y_SIZE/4;
+//SrcYDataLength = X_SIZE  * Y_SIZE;
+
+TempBuffU= TempBuffer;
+TempBuffV= TempBuffU + SrcUVDataLength;
+TempBuffY= TempBuffV + SrcUVDataLength;
+
+		   lpYUV = pY;
+		     lpU = lpYUV+X_SIZE*Y_SIZE;
+		     lpV = lpU + X_SIZE*Y_SIZE/4;
+
+index=0;
+// ===== 320x240 images    
+ for (PTRY=0; PTRY<Y_SIZE; PTRY=PTRY+16)
+ {// start Y Loop
+
+IPTRR=X_SIZE*PTRY;
+IPTRR_U=(IPTRR)>>2;
+IPTRR_V=IPTRR_U; 
+if(index > ProcessedDataLength)
+return STATUS_UNSUCCESSFUL;
+ for (PTRX=0; PTRX<X_SIZE; PTRX=PTRX+32)
+	{ // start X loop
+		// ##############################
+		// # Decompression				#
+		// ##############################
+	     /* lpDestBuf, lpTMPBUF, index, IPTRR, X_WIDE, TableBufferYUV, IDCT_Coef, ZZLN, UV_Scalor, YUVFlag */
+//DecompressYHI(lpU, lpTMPBUF, index, IPTRR_U, X_SIZE/2,  TableBuffer+64, CoefBuffer, ZigZagTable, U_Scalor, 2, frame);
+DecompressYHINoMMX(lpU, lpTMPBUF, index, IPTRR_U, X_SIZE/2,  TableUVBuffer, CoefBuffer, ZigZagTable, U_Scalor, 2, frame);
+index=frame->TempInfo.index;
+IPTRR_U=frame->TempInfo.IPTRR_U;
+
+//DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableBuffer+64, CoefBuffer, ZigZagTable, V_Scalor, 2, frame);
+DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableUVBuffer, CoefBuffer, ZigZagTable, V_Scalor, 2, frame);
+index=frame->TempInfo.index;
+IPTRR_V=frame->TempInfo.IPTRR_U;
+
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+ 
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE,  TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE,  TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+	 } //Stop X
+
+
+IPTRR=X_SIZE*(PTRY+8);
+IPTRR_V=(X_SIZE*PTRY + X_SIZE); // /4;
+IPTRR_V=IPTRR_V>>2;
+IPTRR_U=IPTRR_V;
+ 
+ for (PTRX=0; PTRX<X_SIZE; PTRX=PTRX+32)
+	{ // start X loop
+		// ##############################
+		// # Decompression				#
+		// ##############################
+
+//DecompressYHI(lpU, lpTMPBUF, index, IPTRR_U, X_SIZE/2, TableBuffer+64,CoefBuffer, ZigZagTable, U_Scalor, 2, frame);
+DecompressYHINoMMX(lpU, lpTMPBUF, index, IPTRR_U, X_SIZE/2,  TableUVBuffer, CoefBuffer, ZigZagTable, U_Scalor, 2, frame);
+index=frame->TempInfo.index;
+IPTRR_U=frame->TempInfo.IPTRR_U;
+
+//DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableBuffer+64, CoefBuffer, ZigZagTable, V_Scalor, 2, frame);
+DecompressYHINoMMX(lpV, lpTMPBUF, index, IPTRR_V, X_SIZE/2, TableUVBuffer, CoefBuffer, ZigZagTable, V_Scalor, 2, frame);
+index=frame->TempInfo.index;
+IPTRR_V=frame->TempInfo.IPTRR_U;
+
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE,  TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+ 
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE,  TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+
+DecompressYHINoMMX(lpYUV, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+//DecompressYHINoMMX(TempBuffY, lpTMPBUF, index, IPTRR, X_SIZE, TableBuffer, CoefBuffer, ZigZagTable, Y_Scalor, 1, frame);
+index=frame->TempInfo.index;
+IPTRR=frame->TempInfo.IPTRR_U;
+	 } //Stop X
+}// stop Y
+
+
+	return 0;
+}
+
+
+
+
+/* Input (RawFrameBuffer) format is raw isoc. data (with header and packet 
+ * nunber stripped).
+ * Output (FrameBuffer) format is planar YUV420
+ * Returns uncompressed data length if success, or zero if error
+ */
+static ULONG 
+Decompress420(	ULONG SrcWidth,
+		ULONG SrcHeight,
+		PVOID FrameBuffer,
+		ULONG FrameLength,
+		PVOID RawFrameBuffer,
+		ULONG RawFrameLength,
+		ULONG ActualRawFrameLength,
+		struct ov511_frame *frame)
+{
+	ULONG dwLength = SrcWidth * SrcHeight;
+	PUCHAR pBuffer;
+//	PUCHAR pOutBuffer;
+	PUCHAR pYQuanTable;
+	PUCHAR pUVQuanTable;
+	PSHORT pIDCTTable;
+	PULONG pZigAzgTable;
+	NTSTATUS ntStatus = STATUS_SUCCESS;
+//	UCHAR YTable[QUANTABLESIZE], UVTable[QUANTABLESIZE];
+//	UCHAR Temp;
+//	ULONG i;
+
+	PDEBUG(4, "w=%lu h=%lu fb=%p fblen=%lu rawfb=%p rawlen=%lu, actlen=%lu",
+		SrcWidth, SrcHeight, FrameBuffer, FrameLength, RawFrameBuffer,
+		RawFrameLength, ActualRawFrameLength);
+
+//#ifdef OV518
+//	pgFrameContext = m_pCurrentFrameContext;
+////	OVTCAM_KdPrint(MIN_TRACE, ("OVTCAM: YUV=420 \n")); 
+//#endif
+	if(frame->m_pTempBuffer && frame->m_TempBufferLength < dwLength)
+	{
+		FREE(frame->m_pTempBuffer);
+		frame->m_pTempBuffer = NULL;
+		frame->m_TempBufferLength = 0;
+	}
+	if(frame->m_pTempBuffer == NULL)
+	{
+		frame->m_pTempBuffer = ALLOCATE(NonPagedPool, dwLength,0);
+		frame->m_TempBufferLength = dwLength ;
+	}
+	if(frame->m_pTempBuffer == NULL)
+		return 0;
+
+	pBuffer = (PUCHAR)frame->m_pTempBuffer;
+//	pOutBuffer = (PUCHAR)FrameBuffer;
+	pYQuanTable = GetYQuanTable();
+	pUVQuanTable = GetUVQuanTable();
+	pIDCTTable = GetIDCTTable();
+	pZigAzgTable = GetZigZagTable();
+
+//#if 0
+//	//def OV518
+//		for(int i = 0; i < QUANTABLESIZE; i++)
+//		{
+//			Temp = *pYQuanTable++;
+//			if(Temp > 4) YTable[i] = 3;
+//			else YTable[i] = Temp - 2;
+//
+//			Temp = *pUVQuanTable++;
+//			if(Temp > 4) UVTable[i] = 3;
+//			else UVTable[i] = Temp - 2;
+//		}
+//#endif
+
+//	if(m_bSupportMMX)
+//	{
+//#ifdef OV518
+//
+//#ifdef  TABLE_DBG
+//		ntStatus = Decompress420OV518NoMMXTABLE((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)pYQuanTable, (PUCHAR)pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); 
+//#else
+//		ntStatus = Decompress420OV518((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)YTable, (PUCHAR)UVTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); 
+//#endif
+//
+//#else
+//		ntStatus = Decompress420Hi((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, pYQuanTable, pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer);
+//#endif
+//	}
+//	else
+//	{
+
+//#ifdef OV518
+//#ifdef  TABLE_DBG
+//		ntStatus = Decompress420OV518NoMMXTABLE((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)pYQuanTable, (PUCHAR)pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); 
+//#else
+//		ntStatus = Decompress420OV518NoMMX((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, (PUCHAR)YTable, (PUCHAR)UVTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer); 
+//#endif
+//#else
+		ntStatus = Decompress420HiNoMMX((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight, pYQuanTable, pUVQuanTable,pIDCTTable,pZigAzgTable,(PUCHAR) RawFrameBuffer,ActualRawFrameLength,pBuffer,frame);
+//#endif
+
+// Disabled by Mark
+//		//for color saturation 8/22/00
+//		{
+//			LONG u , v;
+//			PUCHAR pU = (PUCHAR) FrameBuffer + dwLength;
+//			PUCHAR pV = pU + dwLength/4;
+//			for(ULONG i = 0; i < dwLength/4; i++)
+//			{
+//				u = (LONG)*pU;
+//				v = (LONG)*pV;
+//				u -= 128;
+//				v -= 128;
+//				u = (u * 577) >> 10;
+//				v = (v * 730) >> 10;
+//				u += 128;
+//				v += 128;
+//				*pU++ = (UCHAR)u;
+//				*pV++ = (UCHAR)v;
+////   u=0.564
+////   v=0.713
+//			}
+//		}
+
+
+//	}
+
+	// Added by Steven 11/06/2000
+	if(ntStatus) return 0;
+
+//	if(m_EnColorCorrect)
+//	{
+//		ColorCorrect420((PUCHAR) FrameBuffer, FrameLength, SrcWidth, SrcHeight);
+//	}
+
+//	OVTCAM_CopyMemory(pOutBuffer, RawFrameBuffer, dwLength * 3 / 2);
+//	if(!NT_SUCCESS(ntStatus))
+//		return 0;
+	return dwLength * 3 / 2;
+}
+#endif
+
+static void ov511_decompress(struct ov511_frame *frame)
+{
+	unsigned long rc;
+
+//	err("Not implemented yet (but given %d bytes)", frame->bytes_recvd);
+
+#ifdef ENABLE_OV511_COMPRESSION
+
+	PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd);
+
+	rc = Decompress420(	frame->width,
+				frame->height,
+				frame->data,		/* Output buffer */
+				MAX_DATA_SIZE,		/* Output buffer len */
+				frame->rawdata,		/* Raw buffer */
+				MAX_RAW_DATA_SIZE,	/* Raw buffer len */
+				frame->bytes_recvd,	/* Actual raw len */
+				frame);
+
+	PDEBUG(4, "DEBUG: Decompress420 returned %ld", rc);
+
+#else
+
+// Enabling this screws up the preprocessor (cpp-2.96-69)
+//	warn("Compression was not compiled in!");
+//	warn("You must load driver with compress=0 or you will get no image!);
+
+#endif
+
+}
+
+/* Converts frame->data from planar YUV420 to RGB24. This is currently only
+ * useful for postprocessing decompressed data, but will eventually replace
+ * ov511_parse_yuv420_to_rgb().
+ *
+ * NOTE: Uses frame->rawdata as a scratch buffer
+ */
+static void ov511_yuv420p_to_rgb(struct ov511_frame *frame, int bits)
+{
+	const int numpix = frame->width * frame->height;
+	const int bytes = bits >> 3;
+	int i, j, y00, y01, y10, y11, u, v;
+	unsigned char *pY = frame->rawdata;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = frame->data;
+
+	/* Move data to a safe place */
+	memmove(frame->rawdata, frame->data, MAX_RAW_DATA_SIZE);
+
+//	/* Clear buffer for testing purposes */
+//	memset(frame->data, 0, MAX_DATA_SIZE);
+
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			y00 = *pY;
+			y01 = *(pY + 1);
+			y10 = *(pY + frame->width);
+			y11 = *(pY + frame->width + 1);
+			u = (*pU++) - 128;
+			v = (*pV++) - 128;
+
+			ov511_move_420_block(y00, y01, y10, y11, u, v,
+					     frame->width, pOut, bits);
+	
+			pY += 2;
+			pOut += 2 * bytes;
+
+		}
+		pY += frame->width;
+		pOut += frame->width * bytes;
+	}
+}
+
+/* Post-processes the specified frame. This consists of:
+ * 	1. Decompress frame (from frame->rawfbuf into frame->data), if necessary
+ * 	2. Convert frame->data from YUV420P to destination format, if necessary
+ * 	3. Fix the RGB offset, if necessary
+ */
+static void ov511_postprocess(struct ov511_frame *frame)
+{
+	if (frame->compressed)
+		ov511_decompress(frame);
+
+	switch (frame->format) {
+	case VIDEO_PALETTE_RGB565:
+		ov511_yuv420p_to_rgb(frame, 16);
+		break;
+	case VIDEO_PALETTE_RGB24:
+		ov511_yuv420p_to_rgb(frame, 24);
+		break;
+	case VIDEO_PALETTE_YUV420:
+	case VIDEO_PALETTE_YUV420P:
+	default:
+		err("Cannot convert data to this format");
+	}
+
+	if (fix_rgb_offset)
+		fixFrameRGBoffset(frame);
+}
+
+/**********************************************************************
+ *
  * OV511 data transfer, IRQ handler
  *
  **********************************************************************/
@@ -1623,10 +4176,20 @@
 	int aPackNum[10];
 	struct ov511_frame *frame;
 	unsigned char *pData;
-	int iPix;
+	int iPix, data_size, pn, num, offset;
+	struct timeval *ts;
 
 	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 +4199,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,44 +4209,114 @@
 			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
 		 */
 
-		/* Check for SOF/EOF packet */
-		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
-		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
-		     (~cdata[8] & 0x08))
-			goto check_middle;
+#if 0
+		{
+			int d;
+			/* Print all data */
+			for (d = data_size; d < data_size; d += 16) {
+				info("%4x: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", d,
+					cdata[d], cdata[d+1], cdata[d+2], cdata[d+3],
+					cdata[d+4], cdata[d+5], cdata[d+6], cdata[d+7],
+					cdata[d+8], cdata[d+9], cdata[d+10], cdata[d+11],
+					cdata[d+12], cdata[d+13], cdata[d+14], cdata[d+15]);
+			}
+		}
+#endif
+
+		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]);
+		}
+
+		if (ov511->bridge == BRG_OV518PLUS) {
+			int trailer = data_size - 9;
+
+			// Could a packet be both SOF and EOF?
+
+			if (compress)
+				trailer -= 64;
+
+			if (printph) {
+				info("trailer: %x %x %x %x %x %x %x %x", 
+					cdata[trailer], cdata[trailer+1], cdata[trailer+2], cdata[trailer+3],
+					cdata[trailer+4], cdata[trailer+5], cdata[trailer+6], cdata[trailer+7]);
+			}
+
+			/* A false positive here is likely, until OVT gives me
+			 * the definitive SOF/EOF format */
+			if (!(cdata[0] | cdata[1] | cdata[2] | cdata[3] |
+			      cdata[5]) && cdata[6]) {
+				goto sof;
+			} else if (!(cdata[trailer] | cdata[trailer+1] |
+				     cdata[trailer+2] | cdata[trailer+3] |
+				     cdata[trailer+5]) && cdata[trailer+6]) {
+				goto eof;
+			} else {
+				goto check_middle;
+			}
+		} else {
+			/* Check for SOF/EOF packet */
+			if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
+			     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
+			     (~cdata[8] & 0x08))
+				goto check_middle;
+		}
 
 		/* Frame end */
 		if (cdata[8] & 0x80) {
-			struct timeval *ts;
-
+eof:
 			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;
 
-				if (fix_rgb_offset)
-					fixFrameRGBoffset(frame);
-				frame->grabstate = FRAME_DONE;
+				if (!ttpp) {
+					if (frame->compressed) {
+						ov511_decompress(frame);
+
+						switch (frame->format) {
+						case VIDEO_PALETTE_RGB565:
+							ov511_yuv420p_to_rgb(frame, 16);
+							break;
+						case VIDEO_PALETTE_RGB24:
+							ov511_yuv420p_to_rgb(frame, 24);
+							break;
+						case VIDEO_PALETTE_YUV420:
+						case VIDEO_PALETTE_YUV420P:
+							memmove(frame->data, frame->rawdata, MAX_RAW_DATA_SIZE);
+							break;
+						default:
+							err("Cannot convert decompressed data to this format");
+						}
+					}
+
+					if (fix_rgb_offset)
+						fixFrameRGBoffset(frame);
+				}
+
+				frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
 
 				if (waitqueue_active(&frame->wq)) {
 					frame->grabstate = FRAME_DONE;
@@ -1711,6 +4345,7 @@
 			 * fixed by carlosf@conectiva.com.br
 			 */
 		} else {
+sof:
 			/* Frame start */
 			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
@@ -1719,7 +4354,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 +4364,9 @@
 
 			frame->scanstate = STATE_LINES;
 			frame->segment = 0;
+			frame->bytes_recvd = 0;
+			frame->output_offset = 0;
+			frame->compressed = cdata[8] & 0x40;
 		}
 
 check_middle:
@@ -1737,6 +4374,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 (frame->compressed) {
+			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,15 +4400,16 @@
 			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;
+			int format;
 			unsigned char *pOut;
 
 			iSegY = iSegUV = frame->segment;
@@ -1767,7 +4424,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);
@@ -1792,13 +4449,20 @@
 			iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
 			iOutUV = iOutUVP * (frame->depth >> 3);
 
-			switch (frame->format) {
+			/* If ttpp is set just decode the frame to YUV420P, and
+			 * post-process it at task-time. Else, do it the old way. */
+			if (ttpp)
+				format = VIDEO_PALETTE_YUV420P;
+			else
+				format = frame->format;
+
+			switch (format) {
 			case VIDEO_PALETTE_GREY:
 				ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
 				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,
@@ -1816,6 +4480,7 @@
 				ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width);
 				break;
 			case VIDEO_PALETTE_YUV420:
+			case VIDEO_PALETTE_YUV420P:
 				ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4,
 					frame->width, frame->height);
 				break;
@@ -1831,8 +4496,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 +4505,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 +4536,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);
@@ -1894,35 +4566,47 @@
 
 	PDEBUG(3, "*** Initializing capture ***");
 
-	ov511->compress = 0;
 	ov511->curframe = -1;
 	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 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 {
+	} 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 {
 		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 +4737,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 +4756,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 +4794,22 @@
 		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;
+
+		/* Allocated by decompressor */
+		FREE(ov511->frame[i].m_pTempBuffer);
+		ov511->frame[i].m_pTempBuffer = NULL;
 	}
 
 	PDEBUG(4, "buffer memory deallocated");
@@ -2182,6 +4889,9 @@
 	}
 
 	ov511->user++;
+	
+	if (ov511->led_policy == LED_AUTO)
+		ov511_led_control(ov511, 1);
 
 out:
 	up(&ov511->lock);
@@ -2203,6 +4913,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 +4987,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 +5114,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;
@@ -2476,7 +5190,6 @@
 		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
 			return -EBUSY;
 
-		/* Don't compress if the size changed */
 		if ((ov511->frame[vm.frame].width != vm.width) ||
 		    (ov511->frame[vm.frame].height != vm.height) ||
 		    (ov511->frame[vm.frame].format != vm.format) ||
@@ -2486,7 +5199,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)
@@ -2572,10 +5285,12 @@
 			if ((ov511->snap_enabled) &&
 			    (ov511->frame[frame].snapshot)) {
 				ov511->frame[frame].snapshot = 0;
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+				ov51x_clear_snapshot(ov511);
 			}
+
+			if (ttpp)
+				ov511_postprocess(&(ov511->frame[frame]));
+
 			break;
 		} /* end switch */
 
@@ -2615,7 +5330,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;
@@ -2688,13 +5403,14 @@
 	}
 
 	/* Clear the snapshot */
-	if (ov511->snap_enabled && frame->snapshot) {
-		frame->snapshot = 0;
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+	if (ov511->snap_enabled && frame->snapshot) {
+		frame->snapshot = 0;
+		ov51x_clear_snapshot(ov511);
 	}
 
+	if (ttpp)
+		ov511_postprocess((struct ov511_frame *) frame);
+
 	PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
 		frame->bytes_read, frame->scanlength);
 
@@ -2775,11 +5491,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 +5574,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)
-		return -1;
-
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 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 +5599,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 mmcclell@bigfoot.com");
+			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);
 		}
 	}
 
@@ -2929,8 +5623,22 @@
 			info("Sensor is an OV7610");
 			ov511->sensor = SEN_OV7610;
 		} else if((rc & 3) == 1) {
-			info("Sensor is an OV7620AE");
-			ov511->sensor = SEN_OV7620AE;
+			
+			/* I don't know what's different about the 76BE yet */
+			if (ov511_i2c_read(dev, 0x15) & 1)
+				info("Sensor is an OV7620AE");
+			else
+				info("Sensor is an OV76BE");
+
+			/* 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 +5689,138 @@
 	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 },
+		/* 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 */
+//		{ OV511_I2C_BUS, 0x12, 0x24 }, /* 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 mmcclell@bigfoot.com");
 		return -1;
+	} else {
+		PDEBUG(1, "OV6xx0 sensor detected");
 	}
 
 	/* Detect sensor if user didn't use override param */
@@ -3058,10 +5830,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 +5852,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 +5873,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 },
@@ -3118,22 +5903,46 @@
 
 	static struct ov511_regvals aRegvalsNorm511[] = {
 		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
 		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
 		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
 		{ 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 mmcclell@bigfoot.com 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 +5955,231 @@
 		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_reg_write(dev, 0x70, phy);
+		ov511_reg_write(dev, 0x71, phuv);
+		ov511_reg_write(dev, 0x72, pvy);
+		ov511_reg_write(dev, 0x73, pvuv);
+		ov511_reg_write(dev, 0x74, qhy);
+		ov511_reg_write(dev, 0x75, qhuv);
+		ov511_reg_write(dev, 0x76, qvy);
+		ov511_reg_write(dev, 0x77, qvuv);
+		if (ov511_upload_quan_tables(dev) < 0)
+			err("Error uploading quantization tables");
+	}
 
 	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;
+
+	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},
+	};
+
+#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 +6195,6 @@
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_ov511 *ov511;
-	int i;
 
 	PDEBUG(1, "probing for device...");
 
@@ -3262,6 +6222,7 @@
 
 	ov511->dev = dev;
 	ov511->iface = interface->bInterfaceNumber;
+	ov511->led_policy = led;
 
 	switch (dev->descriptor.idProduct) {
 	case 0x0511:
@@ -3272,6 +6233,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;
@@ -3279,57 +6244,40 @@
 		ov511->bridge = BRG_OV511PLUS;
 		break;
 	default:
-		err("Unknown product ID");
-		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.");
+		err("Unknown product ID 0x%x", dev->descriptor.idProduct);
 		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_bridge)
+		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;
@@ -3433,7 +6381,7 @@
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_destroy();
-#endif 
+#endif
 }
 
 module_init(usb_ov511_init);
diff -Naur linux-2.4.2-orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux-2.4.2-orig/drivers/usb/ov511.h	Fri Mar  2 03:21:10 2001
+++ linux/drivers/usb/ov511.h	Tue Mar 27 22:24:31 2001
@@ -15,6 +15,40 @@
 #  define PDEBUG(level, fmt, args...) do {} while(0)
 #endif
 
+/* Standard Windows data types, taken from winnt.h at 
+ * http://www.winehq.com/wine/include/ */
+
+#ifndef VOID
+#define VOID void
+#endif
+
+typedef const void			*PCVOID,	*LPCVOID;
+typedef int		BOOL,		*PBOOL,		*LPBOOL;
+typedef unsigned char	BYTE,		*PBYTE,		*LPBYTE;
+typedef unsigned char	UCHAR,		*PUCHAR;
+typedef unsigned short	USHORT,		*PUSHORT,	*LPUSHORT;
+typedef unsigned short	WORD,		*PWORD,		*LPWORD;
+typedef int		INT,		*PINT,		*LPINT;
+typedef unsigned int	UINT,		*PUINT,		*LPUINT;
+typedef unsigned long	DWORD,		*PDWORD,	*LPDWORD;
+typedef unsigned long	ULONG,		*PULONG,	*LPULONG;
+typedef VOID		*PVOID,		*LPVOID;
+typedef BYTE		BOOLEAN,	*PBOOLEAN;
+typedef char		CHAR,		*PCHAR;
+typedef short		SHORT,		*PSHORT;
+typedef long		LONG,		*PLONG,		*LPLONG;
+typedef __s64		LONGLONG,	*PLONGLONG;
+typedef __u64		ULONGLONG,	*PULONGLONG;
+typedef ULONGLONG	DWORDLONG,	*PDWORDLONG;
+
+typedef ULONG		NTSTATUS;
+const NTSTATUS STATUS_SUCCESS 		= 0x00000000;
+const NTSTATUS STATUS_UNSUCCESSFUL 	= 0xC0000001;
+
+/* --------------------------------- */
+/* DEFINES FOR OV511 AND OTHER CHIPS */
+/* --------------------------------- */
+
 /* Camera interface register numbers */
 #define OV511_REG_CAMERA_DELAY_MODE		0x10
 #define OV511_REG_CAMERA_EDGE_MODE		0x11
@@ -52,6 +86,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 +113,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 +162,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 +229,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 +251,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 +282,19 @@
 	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
 };
 
+/* LED options */
+enum {
+	LED_OFF,
+	LED_ON,
+	LED_AUTO,
+};
+
+/* Keeps track of where we are in decompressing a frame */
+typedef struct CompInfo {
+	LONG index;
+	DWORD IPTRR_U;
+} CompInfo;
+
 struct usb_device;
 
 struct ov511_sbuf {
@@ -249,6 +322,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 +333,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 +342,15 @@
 	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 */
+
+	CompInfo TempInfo;	/* Keeps track of decompression state */
+	PUCHAR m_pTempBuffer;	/* Temporary buffer, used by decompressor */
+	ULONG m_TempBufferLength;
 
 	wait_queue_head_t wq;	/* Processes waiting */
 
@@ -299,6 +380,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 +391,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 */
@@ -334,10 +418,14 @@
 
 	int packet_size;	/* Frame size per isoc desc */
 
-				/* proc interface */
 	struct semaphore param_lock;	/* params lock for this camera */
-	struct proc_dir_entry *proc_entry;	/* /proc/ov511/videoX */
-	
+
+	/* /proc entries, relative to /proc/video/ov511/ */
+	struct proc_dir_entry *proc_devdir;   /* Per-device proc directory */
+	struct proc_dir_entry *proc_info;     /* <minor#>/info entry */
+	struct proc_dir_entry *proc_button;   /* <minor#>/button entry */
+	struct proc_dir_entry *proc_control;  /* <minor#>/control entry */
+
 	/* Framebuffer/sbuf management */
 	int buf_state;
 	struct semaphore buf_lock;
@@ -354,7 +442,7 @@
 	char *name;
 };
 
-struct mode_list {
+struct mode_list_511 {
 	int width;
 	int height;
 	int color;		/* 0=grayscale, 1=color */
@@ -366,6 +454,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
 
