## Automatically generated incremental diff
## From:   linux-2.2.18-pre7
## To:     linux-2.2.18-pre8
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.2.18-pre7/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux-2.2.18-pre7/Documentation/usb/ov511.txt	Mon Dec  3 22:13:46 2001
+++ linux/Documentation/usb/ov511.txt	Mon Dec  3 22:13:53 2001
@@ -6,12 +6,16 @@
 Homepage: http://alpha.dyndns.org/ov511
 
 NEW IN THIS VERSION:
- o Race conditions and other bugs fixed
+ o Stability improvements
+ o Support for hue control
+ o 160x120 mostly working
+ o OV6620 color problems fixed
+ o MoreWebCam 3 detection improvements
 
 INTRODUCTION:
 
 This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
-Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work.It 
+Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It 
 supports streaming and capture of color or monochrome video via the Video4Linux
 API. Most V4L apps are compatible with it, but a few videoconferencing programs
 do not work yet. The following resolutions are supported: 640x480, 448x336,
@@ -156,7 +160,7 @@
   DEFAULT: 5
   DESC: This is the number of times the driver will try to sync and detect the
         internal i2c bus (which connects the OV511 and sensor). If you are
-        getting intermittant detection failures ("Failed to read sensor ID...")
+        getting intermittent detection failures ("Failed to read sensor ID...")
         you should increase this by a modest amount. If setting it to 20 or so
         doesn't fix things, look elsewhere for the cause of the problem.
 
@@ -174,19 +178,47 @@
         programs that expect RGB data (e.g. gqcam) to work with this driver. If
         your colors look VERY wrong, you may want to change this.
 
+  NAME: buf_timeout
+  TYPE: integer
+  DEFAULT: 5 (seconds)
+  DESC: Number of seconds before unused frame buffers are deallocated.
+        Previously, memory was allocated upon open() and deallocated upon
+        close(). Deallocation now occurs only if the driver is closed and this
+        timeout is reached. If you are capturing frames less frequently than
+        the default timeout, increase this. This will not make any difference
+        with programs that capture multiple frames during an open/close cycle.
+
+  NAME: cams
+  TYPE: integer (1-4 for OV511, 1-31 for OV511+)
+  DEFAULT: 1
+  DESC: Number of cameras allowed to stream simultaneously on a single bus.
+        Values higher than 1 reduce the data rate of each camera, allowing two
+        or more to be used at once. If you have a complicated setup involving
+        both OV511 and OV511+ cameras, trial-and-error may be necessary for
+        finding the optimum setting.
+
+  NAME: retry_sync
+  TYPE: boolean
+  DEFAULT: 0
+  DESC: Prevent apps from timing out if frame is not done in time. This is
+        useful if you are having problems with Xawtv getting "stuck" on a frame
+        when your system is under heavy load.
+ 
 WORKING FEATURES:
- o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
- o YUV420 color
+ o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, 320x240, and
+   160x120
+ o RGB24, YUV420, YUV422, YUYV, and YUV422P color
  o Monochrome
- o Setting/getting of saturation, contrast and brightness (no hue yet; only
-   works with OV7610, not the OV7620 or OV7620AE)
+ o Setting/getting of saturation, contrast, brightness, and hue (only some of
+   them work the OV7620 and OV7620AE)
  o proc status reporting
 
 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 read() support
+ o OV6620 sensor support
+ o GBR422 parsing
 
 TODO:
  o Fix the noise / grainy image problem.
@@ -194,26 +226,23 @@
    frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
    so we can't really work on that yet. Please kindly inform OmniVision that you
    would like them to release their specifications to the Linux community.
- o Get 160x120 working
- o YUV422 (and other color modes)
+ o YUV422
  o Get snapshot mode working with mmap().
  o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
- o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture()
-   and ov511_set_picture())
  o Get autoadjust disable working
  o V4L2 support (Probably not until it goes into the kernel)
- o Fix I2C initialization. Some people are reporting problems with reading the
-   7610 registers. This could be due to timing differences, an excessive I2C
-   clock rate, or a problem with ov511_i2c_read().
+ o Creative WebCam III has problems initializing its sensor. This should be
+   fixed now, but if you still have problems let me know.
  o Get rid of the memory management functions (put them in videodev.c??)
- o Setting of contrast and brightness not working with 7620
+ o Setting of contrast and brightness not working with 7620/7620AE
  o Driver/camera state save/restore for when USB supports suspend/resume
- o Multiple cameras reportedly do not work simultaneously
- o Problems with OHCI
+ o Unstable on SMP systems
+ o OV7620/OV6620 experience frame corruption with moving objects
+ o OV6620 is too dark
 
 HOW TO CONTACT ME:
 
-You can email me at mmcclelland@delphi.com . Please prefix the subject line
+You can email me at mwm@i.am . Please prefix the subject line
 with "OV511: " so that I am certain to notice your message.
 
 CREDITS:
diff -urN linux-2.2.18-pre7/Makefile linux/Makefile
--- linux-2.2.18-pre7/Makefile	Mon Dec  3 22:13:46 2001
+++ linux/Makefile	Mon Dec  3 22:13:53 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 18
-EXTRAVERSION = pre7
+EXTRAVERSION = pre8
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -urN linux-2.2.18-pre7/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- linux-2.2.18-pre7/arch/alpha/kernel/bios32.c	Wed Jun  7 14:26:42 2000
+++ linux/arch/alpha/kernel/bios32.c	Mon Dec  3 22:13:53 2001
@@ -828,6 +828,7 @@
 
 	for (dev = bus->devices; dev; dev = dev->sibling) {
 		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
+		    (dev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
 		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
 			disable_dev(dev);
 		}
@@ -840,6 +841,7 @@
 
 	for (dev = bus->devices; dev; dev = dev->sibling) {
 		if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) ||
+		    (dev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
 		    (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) {
 			layout_dev(dev);
 		}
@@ -1081,6 +1083,7 @@
 	 */
 	for (dev = pci_devices; dev; dev = dev->next) {
 		if ((dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) &&
+		    (dev->class >> 8 != PCI_CLASS_BRIDGE_OTHER) &&
 		    (dev->class >> 8 != PCI_CLASS_BRIDGE_PCMCIA))
 			continue;
 
diff -urN linux-2.2.18-pre7/arch/alpha/vmlinux.lds linux/arch/alpha/vmlinux.lds
--- linux-2.2.18-pre7/arch/alpha/vmlinux.lds	Mon Dec  3 22:13:46 2001
+++ linux/arch/alpha/vmlinux.lds	Mon Dec  3 22:13:53 2001
@@ -32,7 +32,7 @@
   .setup.init : { *(.setup.init) }
   __setup_end = .;
 
-  . = ALIGN(8)
+  . = ALIGN(8);
   __initcall_start = .;		/* the init functions to be called */
   .initcall.init : { *(.initcall.init) }
   __initcall_end = .;
diff -urN linux-2.2.18-pre7/arch/i386/kernel/mtrr.c linux/arch/i386/kernel/mtrr.c
--- linux-2.2.18-pre7/arch/i386/kernel/mtrr.c	Mon Dec  3 22:13:46 2001
+++ linux/arch/i386/kernel/mtrr.c	Mon Dec  3 22:13:53 2001
@@ -442,10 +442,10 @@
 	/*  Cyrix have 8 ARRs  */
       case X86_VENDOR_CENTAUR:
         /*  and Centaur has 8 MCR's  */
-	if(boot_cpu.x86==5)
+	if(boot_cpu_data.x86==5)
 		return 8;
 	/*  the cyrix III has intel compatible MTRR */
-	if(boot_cpu.x86==6)
+	if(boot_cpu_data.x86==6)
 	{
 		rdmsr (MTRRcap_MSR, config, dummy);
 		return (config & 0xff);
@@ -474,7 +474,6 @@
 	return (config & (1<<10));
 	/*break;*/
       case X86_VENDOR_CYRIX:
-      case X86_VENDOR_CENTAUR:
 	return 1;
 	/*break;*/
     }
diff -urN linux-2.2.18-pre7/arch/sparc64/kernel/sys_sparc32.c linux/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.2.18-pre7/arch/sparc64/kernel/sys_sparc32.c	Mon Dec  3 22:13:46 2001
+++ linux/arch/sparc64/kernel/sys_sparc32.c	Mon Dec  3 22:13:53 2001
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.107.2.12 2000/09/02 01:59:51 davem Exp $
+/* $Id: sys_sparc32.c,v 1.107.2.13 2000/09/14 10:39:24 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -1380,7 +1380,7 @@
 
 static void *do_ncp_super_data_conv(void *raw_data)
 {
-	struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data;
+	struct ncp_mount_data news, *n = &news; 
 	struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data;
 
 	n->dir_mode = n32->dir_mode;
@@ -1390,6 +1390,7 @@
 	memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int)));
 	n->wdog_pid = n32->wdog_pid;
 	n->mounted_uid = n32->mounted_uid;
+	memcpy(raw_data, n, sizeof(struct ncp_mount_data)); 
 	return raw_data;
 }
 
@@ -1404,7 +1405,7 @@
 
 static void *do_smb_super_data_conv(void *raw_data)
 {
-	struct smb_mount_data *s = (struct smb_mount_data *)raw_data;
+	struct smb_mount_data news, *s = &news;
 	struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data;
 
 	s->version = s32->version;
@@ -1413,6 +1414,7 @@
 	s->gid = s32->gid;
 	s->file_mode = s32->file_mode;
 	s->dir_mode = s32->dir_mode;
+	memcpy(raw_data, s, sizeof(struct smb_mount_data)); 
 	return raw_data;
 }
 
diff -urN linux-2.2.18-pre7/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- linux-2.2.18-pre7/drivers/block/ide-cd.c	Mon Sep  4 10:39:16 2000
+++ linux/drivers/block/ide-cd.c	Mon Dec  3 22:13:53 2001
@@ -1827,6 +1827,20 @@
 	return cdrom_queue_packet_command(drive, &pc);
 }
 
+static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end)
+{
+	struct request_sense sense;
+	struct packet_command pc;
+
+	memset(&pc, 0, sizeof (pc));
+	pc.sense = &sense;
+
+	pc.c[0] = GPCMD_PLAY_AUDIO_10;
+	put_unaligned(cpu_to_be32(lba_start), (unsigned int *) &pc.c[2]);
+	put_unaligned(cpu_to_be16(lba_end - lba_start), (unsigned int *) &pc.c[7]);
+
+	return cdrom_queue_packet_command(drive, &pc);
+}
 
 static int cdrom_get_toc_entry(ide_drive_t *drive, int track,
 				struct atapi_toc_entry **ent)
@@ -1932,6 +1946,34 @@
 	struct cdrom_info *info = drive->driver_data;
 
 	switch (cmd) {
+	/*
+	 * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_10, since
+	 * atapi doesn't support it
+	 */
+	case CDROMPLAYTRKIND: {
+		int stat, lba_start, lba_end;
+		struct cdrom_ti *ti = (struct cdrom_ti *)arg;
+		struct atapi_toc_entry *first_toc, *last_toc;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk0, &first_toc);
+		if (stat)
+			return stat;
+
+		stat = cdrom_get_toc_entry(drive, ti->cdti_trk1, &last_toc);
+		if (stat)
+			return stat;
+
+		if (ti->cdti_trk1 != CDROM_LEADOUT)
+			++last_toc;
+		lba_start = first_toc->addr.lba;
+		lba_end   = last_toc->addr.lba;
+
+		if (lba_end <= lba_start)
+			return -EINVAL;
+
+		return cdrom_play_audio(drive, lba_start, lba_end);
+	}
+
 	case CDROMREADTOCHDR: {
 		int stat;
 		struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
@@ -2275,7 +2317,7 @@
 		CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1;
 	if (cap.audio_play)
 		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
-	if (cap.mechtype == 0)
+	if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup)
 		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
 
 #if ! STANDARD_ATAPI
diff -urN linux-2.2.18-pre7/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c
--- linux-2.2.18-pre7/drivers/cdrom/cdrom.c	Mon Sep  4 10:39:17 2000
+++ linux/drivers/cdrom/cdrom.c	Mon Dec  3 22:13:53 2001
@@ -1604,7 +1604,7 @@
 			return -EDRIVE_CANT_DO_THIS;
 		keeplocked = arg ? 1 : 0;
 		/* don't unlock the door on multiple opens */
-		if ((cdi->use_count != 1) && !arg)
+		if ((cdi->use_count != 1) && !arg && !capable(CAP_SYS_ADMIN))
 			return -EBUSY;
 		return cdo->lock_door(cdi, arg);
 		}
@@ -1965,19 +1965,6 @@
 		IOCTL_OUT(arg, struct cdrom_subchnl, q);
 		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
 		return 0;
-		}
-	case CDROMPLAYTRKIND: {
-		struct cdrom_ti ti;
-
-		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n");
-		IOCTL_IN(arg, struct cdrom_ti, ti);
-
-		cgc.cmd[0] = GPCMD_PLAY_AUDIO_TI;
-		cgc.cmd[4] = ti.cdti_trk0;
-		cgc.cmd[5] = ti.cdti_ind0;
-		cgc.cmd[7] = ti.cdti_trk1;
-		cgc.cmd[8] = ti.cdti_ind1;
-		return cdo->generic_packet(cdi, &cgc);
 		}
 	case CDROMPLAYMSF: {
 		struct cdrom_msf msf;
diff -urN linux-2.2.18-pre7/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- linux-2.2.18-pre7/drivers/scsi/sr_ioctl.c	Mon Sep  4 10:39:21 2000
+++ linux/drivers/scsi/sr_ioctl.c	Mon Dec  3 22:13:53 2001
@@ -314,6 +314,8 @@
     u_char  sr_cmd[10];    
     int result, target  = MINOR(cdi->dev);
     unsigned char buffer[32];
+
+    memset(sr_cmd, 0, sizeof(sr_cmd));
     
     switch (cmd) 
     {
@@ -324,10 +326,7 @@
 	sr_cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
 	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
-	sr_cmd[6] = 0;
-	sr_cmd[7] = 0;              /* MSB of length (12) */
 	sr_cmd[8] = 12;             /* LSB of length */
-	sr_cmd[9] = 0;
 	
 	result = sr_do_ioctl(target, sr_cmd, buffer, 12, 1);
 	
@@ -346,9 +345,7 @@
           (tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
 	sr_cmd[6] = tocentry->cdte_track;
-	sr_cmd[7] = 0;             /* MSB of length (12)  */
 	sr_cmd[8] = 12;            /* LSB of length */
-	sr_cmd[9] = 0;
 	
 	result = sr_do_ioctl (target, sr_cmd, buffer, 12, 0);
 	
@@ -373,9 +370,7 @@
 		sr_cmd[0] = MODE_SENSE;
 		sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
 		sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
-		sr_cmd[3] = 0;
 		sr_cmd[4] = 28;
-		sr_cmd[5] = 0;
 	
 		if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL)
 			return -ENOMEM;
@@ -436,9 +431,7 @@
 		sr_cmd[0] = MODE_SENSE;
 		sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
 		sr_cmd[2] = 0xe;    /* Want mode page 0xe, CDROM audio params */
-		sr_cmd[3] = 0;
 		sr_cmd[4] = 28;
-		sr_cmd[5] = 0;
 	
 		if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL)
 			return -ENOMEM;
@@ -455,6 +448,20 @@
 		volctrl->channel3 = buffer[27];
 
 		scsi_free(buffer, 512);
+		break;
+	}
+
+    case CDROMPLAYTRKIND: {
+		struct cdrom_ti* ti = (struct cdrom_ti*)arg;
+
+		sr_cmd[0] = GPCMD_PLAYAUDIO_TI;
+		sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+		sr_cmd[4] = ti->cdti_trk0;
+		sr_cmd[5] = ti->cdti_ind0;
+		sr_cmd[7] = ti->cdti_trk1;
+		sr_cmd[8] = ti->cdti_ind1;
+	
+		result = sr_do_ioctl(target, sr_cmd, NULL, 255, 0);
 		break;
 	}
 
diff -urN linux-2.2.18-pre7/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux-2.2.18-pre7/drivers/usb/Config.in	Mon Dec  3 22:13:47 2001
+++ linux/drivers/usb/Config.in	Mon Dec  3 22:13:54 2001
@@ -83,7 +83,9 @@
    fi
    dep_tristate '  Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB
    dep_tristate '  Logitech WingMan Force joystick support' CONFIG_USB_WMFORCE $CONFIG_USB
-   dep_tristate '  Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB
+   if [ "$CONFIG_VT" = "y" ]; then
+      dep_tristate '  Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB 
+   fi
    dep_tristate '  Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB
    if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then
       int '   Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
diff -urN linux-2.2.18-pre7/drivers/usb/devio.c linux/drivers/usb/devio.c
--- linux-2.2.18-pre7/drivers/usb/devio.c	Mon Dec  3 22:13:47 2001
+++ linux/drivers/usb/devio.c	Mon Dec  3 22:13:54 2001
@@ -962,19 +962,22 @@
 		if (copy_to_user(as->userbuffer, as->urb.transfer_buffer, as->urb.transfer_buffer_length))
 			return -EFAULT;
 	if (put_user(as->urb.status,
-		     &((struct usbdevfs_urb *)as->userurb)->status) ||
-	    __put_user(as->urb.actual_length,
-		       &((struct usbdevfs_urb *)as->userurb)->actual_length) ||
-	    __put_user(as->urb.error_count,
-		       &((struct usbdevfs_urb *)as->userurb)->error_count))
+		     &((struct usbdevfs_urb *)as->userurb)->status))
+		return -EFAULT;
+	if (put_user(as->urb.actual_length,
+		     &((struct usbdevfs_urb *)as->userurb)->actual_length))
+		return -EFAULT;
+	if (put_user(as->urb.error_count,
+		     &((struct usbdevfs_urb *)as->userurb)->error_count))
 		return -EFAULT;
 	if (!(usb_pipeisoc(as->urb.pipe)))
 		return 0;
 	for (i = 0; i < as->urb.number_of_packets; i++) {
 		if (put_user(as->urb.iso_frame_desc[i].actual_length, 
-			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length) ||
-		    __put_user(as->urb.iso_frame_desc[i].status, 
-			       &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status))
+			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length))
+			return -EFAULT;
+		if (put_user(as->urb.iso_frame_desc[i].status, 
+			     &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status))
 			return -EFAULT;
 	}
 	return 0;
diff -urN linux-2.2.18-pre7/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux-2.2.18-pre7/drivers/usb/ov511.c	Mon Dec  3 22:13:47 2001
+++ linux/drivers/usb/ov511.c	Mon Dec  3 22:13:54 2001
@@ -30,7 +30,7 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-static const char version[] = "1.17";
+static const char version[] = "1.25";
 
 #define __NO_VERSION__
 
@@ -63,11 +63,9 @@
 #define MAX_FRAME_SIZE (640 * 480 * 3)
 #define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
 
-#define DEFAULT_WIDTH 640
-#define DEFAULT_HEIGHT 480
-
 #define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
-#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24)
+#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : \
+		     ((p) == VIDEO_PALETTE_YUV422 ? 16 : 24))
 
 /* PARAMETER VARIABLES: */
 static int autoadjust = 1;    /* CCD dynamically changes exposure, etc... */
@@ -102,22 +100,59 @@
  * programs that expect RGB data (e.g. gqcam) to work with this driver. */
 static int force_rgb = 0;
 
+/* Number of seconds before inactive buffers are deallocated */
+static int buf_timeout = 5;
+
+/* Number of cameras to stream from simultaneously */
+static int cams = 1;
+
+/* Prevent apps from timing out if frame is not done in time */
+static int retry_sync = 0;
+
+/* Enable compression. This is for experimentation only; compressed images
+ * still cannot be decoded yet. */
+static int compress = 0;
+
+/* Display test pattern - doesn't work yet either */
+static int testpat = 0;
+
 MODULE_PARM(autoadjust, "i");
+MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
 MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=init/detection, 2=warning, 3=config/control, 4=function call, 5=max");
 MODULE_PARM(fix_rgb_offset, "i");
+MODULE_PARM_DESC(fix_rgb_offset, "Fix vertical misalignment of red and blue at 640x480");
 MODULE_PARM(snapshot, "i");
+MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
 MODULE_PARM(sensor, "i");
+MODULE_PARM_DESC(sensor, "Override sensor detection");
 MODULE_PARM(i2c_detect_tries, "i");
+MODULE_PARM_DESC(i2c_detect_tries, "Number of tries to detect sensor");
 MODULE_PARM(aperture, "i");
+MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs");
 MODULE_PARM(force_rgb, "i");
+MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
+MODULE_PARM(buf_timeout, "i");
+MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
+MODULE_PARM(cams, "i");
+MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
+MODULE_PARM(retry_sync, "i");
+MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out");
+MODULE_PARM(compress, "i");
+MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
+MODULE_PARM(testpat, "i");
+MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)");
 
-MODULE_AUTHOR("Mark McClelland <mmcclelland@delphi.com> & 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 <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>");
 MODULE_DESCRIPTION("OV511 USB Camera Driver");
 
 char kernel_version[] = UTS_RELEASE;
 
 static struct usb_driver ov511_driver;
 
+/* I know, I know, global variables suck. This is only a temporary hack */
+int output_offset;
+
 /**********************************************************************
  * List of known OV511-based cameras
  **********************************************************************/
@@ -136,6 +171,7 @@
 	{  -1, NULL }
 };
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 static struct palette_list plist[] = {
 	{ VIDEO_PALETTE_GREY,	"GREY" },
 	{ VIDEO_PALETTE_HI240,  "HI240" },
@@ -155,6 +191,7 @@
 	{ VIDEO_PALETTE_YUV410P,"YUV410P" },
 	{ -1, NULL }
 };
+#endif
 
 /**********************************************************************
  *
@@ -436,7 +473,7 @@
 	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
 
 	if (rc < 0)
-		err("ov511_reg_write: error %d", rc);
+		err("reg write: error %d", rc);
 
 	return rc;
 }
@@ -456,7 +493,7 @@
 	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
 	
 	if (rc < 0) {
-		err("ov511_reg_read: error %d", rc);
+		err("reg read: error %d", rc);
 		return rc;
 	} else {
 		return buffer[0];	
@@ -505,7 +542,7 @@
 	return 0;
 
 error:
-	err("ov511_i2c_write: error %d", rc);
+	err("i2c write: error %d", rc);
 	return rc;
 }
 
@@ -577,7 +614,7 @@
 	return value;
 
 error:
-	err("ov511_i2c_read: error %d", rc);
+	err("i2c read: error %d", rc);
 	return rc;
 }
 
@@ -605,7 +642,7 @@
 	return 0;
 
 error:
-	err("ov511_write_regvals: error %d", rc);
+	err("write regvals: error %d", rc);
 	return rc;
 }
 
@@ -623,9 +660,10 @@
 static void ov511_dump_i2c_regs(struct usb_device *dev)
 {
 	PDEBUG(3, "I2C REGS");
-	ov511_dump_i2c_range(dev, 0x00, 0x38);
+	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;
@@ -636,7 +674,6 @@
 	}
 }
 
-#if 0
 static void ov511_dump_regs(struct usb_device *dev)
 {
 	PDEBUG(1, "CAMERA INTERFACE REGS");
@@ -651,7 +688,7 @@
 	PDEBUG(1, "I2C REGS");
 	ov511_dump_reg_range(dev, 0x40, 0x49);
 	PDEBUG(1, "SYSTEM CONTROL REGS");
-	ov511_dump_reg_range(dev, 0x50, 0x53);
+	ov511_dump_reg_range(dev, 0x50, 0x55);
 	ov511_dump_reg_range(dev, 0x5e, 0x5f);
 	PDEBUG(1, "OmniCE REGS");
 	ov511_dump_reg_range(dev, 0x70, 0x79);
@@ -703,11 +740,8 @@
 	if (ov511->bridge == BRG_OV511) {
 		if (size == 0) alt = OV511_ALT_SIZE_0;
 		else if (size == 257) alt = OV511_ALT_SIZE_257;
-//		else if (size == 512) alt = OV511_ALT_SIZE_512;
 		else if (size == 513) alt = OV511_ALT_SIZE_513;
-//		else if (size == 768) alt = OV511_ALT_SIZE_768;
 		else if (size == 769) alt = OV511_ALT_SIZE_769;
-//		else if (size == 992) alt = OV511_ALT_SIZE_992;
 		else if (size == 993) alt = OV511_ALT_SIZE_993;
 		else {
 			err("Set packet size: invalid size (%d)", size);
@@ -778,15 +812,22 @@
 	if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
 		return -EIO;
 #endif
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE)
-		if(ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
+	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE
+		|| ov511->sensor == SEN_OV6620)
+		if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
+			return -EIO;
+
+	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) {
+		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+			return -EIO;
+
+		if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
 			return -EIO;
 
-	if (ov511->sensor == SEN_OV7610) {
-		if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+		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)
+		if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
 			return -EIO;
 	} else if ((ov511->sensor == SEN_OV7620) 
 	         || (ov511->sensor == SEN_OV7620AE)) {
@@ -797,7 +838,7 @@
 	        PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT),
 	         ov511_i2c_read(dev, OV7610_REG_BRT)); 
 
-		if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
 			return -EIO;
 #endif
 	}
@@ -819,16 +860,19 @@
 	if (ov511_stop(dev) < 0)
 		return -EIO;
 
-	if((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
 	p->colour = ret << 8;
 
-	if((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
 	p->contrast = ret << 8;
 
-	if((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
 	p->brightness = ret << 8;
 
-	p->hue = 0x8000;
+	/* This may not be the best way to do it */
+	if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO;
+	p->hue = ret << 8;
+
 	p->whiteness = 105 << 8;
 
 	/* Can we get these from frame[0]? -claudio? */
@@ -841,20 +885,23 @@
 	return 0;
 }
 
-/* FIXME: 176x144, 160x140 */
 /* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
 static struct mode_list mlist[] = {
-	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COMC  COML */
-	{ 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e },
-	{ 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e },
-	{ 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e },
-	{ 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e },
-	{ 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
-	{ 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
-	{ 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
-	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
-	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
-	{ 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e },
+	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COML */
+	{ 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
+	{ 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
+	{ 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
+	{ 176, 144, 1, 0x15, 0x12, 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 }
 };
 
@@ -864,7 +911,8 @@
 {
 	int i;
 	struct usb_device *dev = ov511->dev;
-	int hwsbase = 0, hwebase = 0;
+	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
+	int hwscale = 0, vwscale = 0;
 
 	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
 	       width, height, mode, sub_flag);
@@ -876,7 +924,7 @@
 		ov511_reg_write(dev, 0x16, 0x00);
 		if (ov511->sensor == SEN_OV7610
 		    || ov511->sensor == SEN_OV7620AE) {
-			/* these aren't valid on the OV7620 */
+			/* these aren't valid on the OV6620/OV7620 */
 			ov511_i2c_write(dev, 0x0e, 0x44);
 		}
 		ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
@@ -888,7 +936,7 @@
 		ov511_reg_write(dev, 0x16, 0x01);
 		if (ov511->sensor == SEN_OV7610
 		    || ov511->sensor == SEN_OV7620AE) {
-			/* not valid on the OV7620 */
+			/* not valid on the OV6620/OV7620 */
 			ov511_i2c_write(dev, 0x0e, 0x04);
 		}
 		ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
@@ -904,27 +952,64 @@
 	case SEN_OV7620AE:
 		hwsbase = 0x38;
 		hwebase = 0x3a;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV6620:
+		hwsbase = 0x38;
+		hwebase = 0x39;
+		vwsbase = 0x03;
+		vwebase = 0x04;
 		break;
 	case SEN_OV7620:
 		hwsbase = 0x2c;
 		hwebase = 0x2d;
+		vwsbase = vwebase = 0x05;
 		break;
 	default:
-		hwsbase = 0;
-		hwebase = 0;
-		break;
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	/* Bit 5 of COM C register varies with sensor */ 
+	if (ov511->sensor == SEN_OV6620) {
+		if (width > 176 && height > 144) {  /* CIF */
+			ov511_i2c_write(dev, 0x14, 0x04);
+			hwscale = 1;
+			vwscale = 1;  /* The datasheet says 0; it's wrong */
+			hwsize = 352;
+			vwsize = 288;
+		} else {			    /* QCIF */
+			ov511_i2c_write(dev, 0x14, 0x24);
+			hwsize = 176;
+			vwsize = 144;
+		}
+	}
+	else {
+		if (width > 320 && height > 240) {  /* VGA */
+			ov511_i2c_write(dev, 0x14, 0x04);
+			hwscale = 2;
+			vwscale = 1;
+			hwsize = 640;
+			vwsize = 480;
+		} else {			    /* QVGA */
+			ov511_i2c_write(dev, 0x14, 0x24);
+			hwscale = 1;
+			hwsize = 320;
+			vwsize = 240;
+		}	
 	}
 
+	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
 	if (sub_flag) {
-		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2));
-		ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2));
-		ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
-		ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1));
+		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale));
+		ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale));
+		ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale));
 	} else {
 		ov511_i2c_write(dev, 0x17, hwsbase);
-		ov511_i2c_write(dev, 0x18, hwebase + (640>>2));
-		ov511_i2c_write(dev, 0x19, 0x5);
-		ov511_i2c_write(dev, 0x1a, 5 + (480>>1));
+		ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale));
+		ov511_i2c_write(dev, 0x19, vwsbase);
+		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
 	}
 
 	for (i = 0; mlist[i].width; i++) {
@@ -962,17 +1047,22 @@
 #ifdef OV511_GBR422
 		ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08);
 #else
-		ov511_i2c_write(dev, 0x12, mlist[i].common_A);
+		ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
 #endif
-		ov511_i2c_write(dev, 0x14, mlist[i].common_C);
 
-		/* 7620 doesn't have register 0x35, so play it safe */
-		if (ov511->sensor != SEN_OV7620)
+		/* 7620/6620 don't have register 0x35, so play it safe */
+		if (ov511->sensor == SEN_OV7610 ||
+		    ov511->sensor == SEN_OV7620AE)
 			ov511_i2c_write(dev, 0x35, mlist[i].common_L);
 
 		break;
 	}
 
+	if (compress) {
+		ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
+		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
+	}
+
 	if (ov511_restart(ov511->dev) < 0)
 		return -EIO;
 
@@ -1009,6 +1099,16 @@
  *
  * To avoid floating point arithmetic, the color conversion
  * coefficients are scaled into 16.16 fixed-point integers.
+ * They were determined as follows:
+ *
+ *	double brightness = 1.0;  (0->black; 1->full scale) 
+ *	double saturation = 1.0;  (0->greyscale; 1->full color)
+ *	double fixScale = brightness * 256 * 256;
+ *	int rvScale = (int)(1.402 * saturation * fixScale);
+ *	int guScale = (int)(-0.344136 * saturation * fixScale);
+ *	int gvScale = (int)(-0.714136 * saturation * fixScale);
+ *	int buScale = (int)(1.772 * saturation * fixScale);
+ *	int yScale = (int)(fixScale);	
  */
 
 /* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
@@ -1018,11 +1118,11 @@
 ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, 
 	int rowPixels, unsigned char * rgb)
 {
-	const int rvScale = (1402000 * 1024) / 16525; /* 1.402    */
-	const int guScale = (-344136 * 1024) / 16525; /* -.344136 */
-	const int gvScale = (-714136 * 1024) / 16525; /* -.714136 */
-	const int buScale = (1772000 * 1024) / 16525; /* 1.772    */
-	const int yScale  = (1000000 * 1024) / 16525; /* 1.0      */
+	const int rvScale = 91881;
+	const int guScale = -22553;
+	const int gvScale = -46801;
+	const int buScale = 116129;
+	const int yScale  = 65536;
 	int r, g, b;
 
 	g = guScale * u + gvScale * v;
@@ -1082,10 +1182,13 @@
 
 #undef OV511_DUMPPIX
 
+/* #define this and OV511_DUMPPIX to disable parsing of UV data */
+#undef OV511_FORCE_MONO
+
 #ifdef OV511_GBR422
 static void
 ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth)    			    
+		       int iOutY, int iOutUV, int iHalf, int iWidth)
 {
 	int k, l, m;
 	unsigned char *pIn;
@@ -1135,7 +1238,7 @@
 
 static void
 ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth)    			    
+		       int iOutY, int iOutUV, int iHalf, int iWidth)
 {
 #ifndef OV511_DUMPPIX
 	int k, l, m;
@@ -1212,6 +1315,8 @@
 		}
 	}
 #else
+
+#ifndef OV511_FORCE_MONO
 	/* Just dump pix data straight out for debug */
 	int i, j;
 
@@ -1224,10 +1329,172 @@
 		}
 		pOut0 += (iWidth - WDIV) * 3;
 	}
+#else
+
+#if 1
+	/* This converts the Y data to "black-and-white" RGB data */
+	/* Useful for experimenting with compression */
+	int k, l, m;
+	unsigned char *pIn, *pOut, *pOut1;
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++) {
+				*pOut1++ = *pIn;
+				*pOut1++ = *pIn;
+				*pOut1++ = *pIn++;
+			}
+			pOut1 += (iWidth - 8) * 3;
+		}
+		pOut += 8 * 3;
+	}
+#else
+	/* This will dump the Y channel data stream as-is */ 
+	int count;
+	unsigned char *pIn, *pOut;
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + output_offset;
+	for (count = 0; count < 256; count++) {
+		*pOut++ = *pIn;
+		*pOut++ = *pIn;
+		*pOut++ = *pIn++;
+		output_offset += 3;
+	}	
+#endif
+
+#endif
+
 #endif
 }
 #endif
 
+/* This converts YUV420 segments to YUYV */
+static void
+ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
+		        int iOutY, int iOutUV, int iWidth)
+{
+	int k, l, m;
+	unsigned char *pIn, *pOut, *pOut1;
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++) {
+				*pOut1 = (*pIn++);
+				pOut1 += 2;
+			}
+			pOut1 += (iWidth - 8) * 2;
+		}
+		pOut += 8 * 2;
+	}
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + 1;
+	for (l = 0; l < 8; l++) {
+		for (m=0; m<8; m++) {
+			int v   = *(pIn+64);
+			int u   = *pIn++;
+			
+			*pOut = u;
+			*(pOut+2) = v;
+			*(pOut+iWidth) = u;
+			*(pOut+iWidth+2) = v;
+			pOut += 4;
+		}
+		pOut += (iWidth*4 - 32);
+	}
+}
+
+static void
+ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0,
+		        int iOutY, int iOutUV, int iWidth, int iHeight)
+{
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+	unsigned a = iWidth * iHeight;
+	unsigned w = iWidth / 2;
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + a;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+		pOut += w;
+	}
+
+	pIn = pIn0 + 64;
+	pOut = pOut0 + iOutUV + a + a/4;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+		pOut += w;
+	}
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++)
+				*pOut1++ =*pIn++;
+			pOut1 += iWidth - 8;
+		}
+		pOut += 8;
+	}
+}
+
+static void
+ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0,
+		       int iOutY, int iOutUV, int iWidth, int iHeight)
+{
+	int k, l, m;
+	unsigned char *pIn;
+	unsigned char *pOut, *pOut1;
+	unsigned a = iWidth * iHeight;
+	unsigned w = iWidth / 2;
+
+	pIn = pIn0;
+	pOut = pOut0 + iOutUV + a;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			*pOut1 = *(pOut1 + w) = *pIn++;
+			pOut1++;
+		}
+		pOut += iWidth;
+	}
+
+	pIn = pIn0 + 64;
+	pOut = pOut0 + iOutUV + a + a/2;
+	for (k = 0; k < 8; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			*pOut1 = *(pOut1 + w) = *pIn++;
+			pOut1++;
+		}
+		pOut += iWidth;
+	}
+
+	pIn = pIn0 + 128;
+	pOut = pOut0 + iOutY;
+	for (k = 0; k < 4; k++) {
+		pOut1 = pOut;
+		for (l = 0; l < 8; l++) {
+			for (m = 0; m < 8; m++)
+				*pOut1++ =*pIn++;
+			pOut1 += iWidth - 8;
+		}
+		pOut += 8;
+	}
+}
+
 /*
  * For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
  * The segments represent 4 squares of 8x8 pixels as follows:
@@ -1398,6 +1665,8 @@
 			/* Frame start */
 			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
+			output_offset = 0;
+
 			/* Check to see if it's a snapshot frame */
 			/* FIXME?? Should the snapshot reset go here? Performance? */
 			if (cdata[8] & 0x02) {
@@ -1429,7 +1698,7 @@
 		    frame->segment < frame->width * frame->height / 256) {
 			int iSegY, iSegUV;
 			int iY, jY, iUV, jUV;
-			int iOutY, iOutUV;
+			int iOutY, iOutYP, iOutUV, iOutUVP;
 			unsigned char *pOut;
 
 			iSegY = iSegUV = frame->segment;
@@ -1462,10 +1731,12 @@
 			 */
 			iY = iSegY / (frame->width / WDIV);
 			jY = iSegY - iY * (frame->width / WDIV);
-			iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3);
+			iOutYP = iY*HDIV*frame->width + jY*WDIV;
+			iOutY = iOutYP * (frame->depth >> 3);
 			iUV = iSegUV / (frame->width / WDIV * 2);
 			jUV = iSegUV - iUV * (frame->width / WDIV * 2);
-			iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3);
+			iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
+			iOutUV = iOutUVP * (frame->depth >> 3);
 
 			switch (frame->format) {
 			case VIDEO_PALETTE_GREY:
@@ -1475,6 +1746,20 @@
 				ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV,
 					iY & 1, frame->width);
 				break;
+			case VIDEO_PALETTE_YUV422:
+			case VIDEO_PALETTE_YUYV:
+				ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width);
+				break;
+			case VIDEO_PALETTE_YUV420:
+				ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4,
+					frame->width, frame->height);
+				break;
+			case VIDEO_PALETTE_YUV422P:
+				ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2,
+					frame->width, frame->height);
+				break;
+			default:
+				err("Unsupported format: %d", frame->format);
 			}
 
 			pData = &cdata[iPix];
@@ -1507,7 +1792,7 @@
 		return;
 
 	if (!ov511->streaming) {
-		PDEBUG(2, "hmmm... not streaming, but got interrupt");
+		PDEBUG(4, "hmmm... not streaming, but got interrupt");
 		return;
 	}
 	
@@ -1529,7 +1814,7 @@
 static int ov511_init_isoc(struct usb_ov511 *ov511)
 {
 	urb_t *urb;
-	int fx, err;
+	int fx, err, n, size;
 
 	PDEBUG(3, "*** Initializing capture ***");
 
@@ -1539,61 +1824,61 @@
 	ov511->scratchlen = 0;
 
 	if (ov511->bridge == BRG_OV511)
-		ov511_set_packet_size(ov511, 993);
+		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)
-		ov511_set_packet_size(ov511, 961);
-	else
+		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 {
 		err("invalid bridge type");
+		return -1;
+	}
+
+	ov511_set_packet_size(ov511, size);
 
-	/* We double buffer the Iso lists */
-	urb = usb_alloc_urb(FRAMES_PER_DESC);
+	for (n = 0; n < OV511_NUMSBUF; n++) {
+		urb = usb_alloc_urb(FRAMES_PER_DESC);
 	
-	if (!urb) {
-		err("ov511_init_isoc: usb_alloc_urb ret. NULL");
-		return -ENOMEM;
-	}
-	ov511->sbuf[0].urb = urb;
-	urb->dev = ov511->dev;
-	urb->context = ov511;
-	urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
-	urb->transfer_flags = USB_ISO_ASAP;
-	urb->transfer_buffer = ov511->sbuf[0].data;
-	urb->complete = ov511_isoc_irq;
-	urb->number_of_packets = FRAMES_PER_DESC;
-	urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
-	for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-		urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
-		urb->iso_frame_desc[fx].length = ov511->packet_size;
-	}
-
-	urb = usb_alloc_urb(FRAMES_PER_DESC);
-	if (!urb) {
-		err("ov511_init_isoc: usb_alloc_urb ret. NULL");
-		return -ENOMEM;
-	}
-	ov511->sbuf[1].urb = urb;
-	urb->dev = ov511->dev;
-	urb->context = ov511;
-	urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
-	urb->transfer_flags = USB_ISO_ASAP;
-	urb->transfer_buffer = ov511->sbuf[1].data;
-	urb->complete = ov511_isoc_irq;
-	urb->number_of_packets = FRAMES_PER_DESC;
-	urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
-	for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-		urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
-		urb->iso_frame_desc[fx].length = ov511->packet_size;
+		if (!urb) {
+			err("init isoc: usb_alloc_urb ret. NULL");
+			return -ENOMEM;
+		}
+		ov511->sbuf[n].urb = urb;
+		urb->dev = ov511->dev;
+		urb->context = ov511;
+		urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS);
+		urb->transfer_flags = USB_ISO_ASAP;
+		urb->transfer_buffer = ov511->sbuf[n].data;
+		urb->complete = ov511_isoc_irq;
+		urb->number_of_packets = FRAMES_PER_DESC;
+		urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+			urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+			urb->iso_frame_desc[fx].length = ov511->packet_size;
+		}
 	}
 
-	ov511->sbuf[1].urb->next = ov511->sbuf[0].urb;
-	ov511->sbuf[0].urb->next = ov511->sbuf[1].urb;
-
-	err = usb_submit_urb(ov511->sbuf[0].urb);
-	if (err)
-		err("ov511_init_isoc: usb_submit_urb(0) ret %d", err);
-	err = usb_submit_urb(ov511->sbuf[1].urb);
-	if (err)
-		err("ov511_init_isoc: usb_submit_urb(1) ret %d", err);
+	ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb;
+	for (n = 0; n < OV511_NUMSBUF - 1; n++)
+		ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
+
+	for (n = 0; n < OV511_NUMSBUF; n++) {
+		err = usb_submit_urb(ov511->sbuf[n].urb);
+		if (err)
+			err("init isoc: usb_submit_urb(%d) ret %d", n, err);
+	}
 
 	ov511->streaming = 1;
 
@@ -1602,6 +1887,8 @@
 
 static void ov511_stop_isoc(struct usb_ov511 *ov511)
 {
+	int n;
+
 	if (!ov511->streaming || !ov511->dev)
 		return;
 
@@ -1612,24 +1899,19 @@
 	ov511->streaming = 0;
 
 	/* Unschedule all of the iso td's */
-	if (ov511->sbuf[1].urb) {
-		ov511->sbuf[1].urb->next = NULL;
-		usb_unlink_urb(ov511->sbuf[1].urb);
-		usb_free_urb(ov511->sbuf[1].urb);
-		ov511->sbuf[1].urb = NULL;
-	}
-	if (ov511->sbuf[0].urb) {
-		ov511->sbuf[0].urb->next = NULL;
-		usb_unlink_urb(ov511->sbuf[0].urb);
-		usb_free_urb(ov511->sbuf[0].urb);
-		ov511->sbuf[0].urb = NULL;
+	for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
+		if (ov511->sbuf[n].urb) {
+			ov511->sbuf[n].urb->next = NULL;
+			usb_unlink_urb(ov511->sbuf[n].urb);
+			usb_free_urb(ov511->sbuf[n].urb);
+			ov511->sbuf[n].urb = NULL;
+		}
 	}
 }
 
 static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
 {
 	struct ov511_frame *frame;
-	int width, height;
 
 	PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe,
 		framenum);
@@ -1645,11 +1927,9 @@
 		return 0;
 
 	frame = &ov511->frame[framenum];
-	width = frame->width;
-	height = frame->height;
 
-	PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, width,
-		height);
+	PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, 
+		frame->width, frame->height);
 
 	frame->grabstate = FRAME_GRABBING;
 	frame->scanstate = STATE_SCANNING;
@@ -1659,21 +1939,142 @@
 	ov511->curframe = framenum;
 
 	/* Make sure it's not too big */
-	if (width > DEFAULT_WIDTH)
-		width = DEFAULT_WIDTH;
+	if (frame->width > ov511->maxwidth)
+		frame->width = ov511->maxwidth;
 
-	width &= ~7L;			/* Multiple of 8 */
+	frame->width &= ~7L;		/* Multiple of 8 */
 
-	if (height > DEFAULT_HEIGHT)
-		height = DEFAULT_HEIGHT;
+	if (frame->height > ov511->maxheight)
+		frame->height = ov511->maxheight;
 
-	width &= ~3L;			/* Multiple of 4 */
+	frame->height &= ~3L;		/* Multiple of 4 */
 
 	return 0;
 }
 
 /****************************************************************************
  *
+ * Buffer management
+ *
+ ***************************************************************************/
+static int ov511_alloc(struct usb_ov511 *ov511)
+{
+	int i;
+
+	PDEBUG(4, "entered");
+	down(&ov511->buf_lock);
+
+	if (ov511->buf_state == BUF_PEND_DEALLOC) {
+		ov511->buf_state = BUF_ALLOCATED;
+		del_timer(&ov511->buf_timer);
+	}
+
+	if (ov511->buf_state == BUF_ALLOCATED)
+		goto out;
+
+	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
+	if (!ov511->fbuf)
+		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;
+		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+
+		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
+			MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
+		if (!ov511->sbuf[i].data) {
+			while (--i) {
+				kfree(ov511->sbuf[i].data);
+				ov511->sbuf[i].data = NULL;
+			}
+			rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+			ov511->fbuf = NULL;
+			goto error;
+		}
+		PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
+	}
+	ov511->buf_state = BUF_ALLOCATED;
+out:
+	up(&ov511->buf_lock);
+	PDEBUG(4, "leaving");
+	return 0;
+error:
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+	up(&ov511->buf_lock);
+	PDEBUG(4, "errored");
+	return -ENOMEM;
+}
+
+/* 
+ * - You must acquire buf_lock before entering this function.
+ * - Because this code will free any non-null pointer, you must be sure to null
+ *   them if you explicitly free them somewhere else!
+ */
+static void ov511_do_dealloc(struct usb_ov511 *ov511)
+{
+	int i;
+	PDEBUG(4, "entered");
+
+	if (ov511->fbuf) {
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+		ov511->fbuf = NULL;
+	}
+
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		if (ov511->sbuf[i].data) {
+			kfree(ov511->sbuf[i].data);
+			ov511->sbuf[i].data = NULL;
+		}
+	}
+
+	PDEBUG(4, "buffer memory deallocated");
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+	PDEBUG(4, "leaving");
+}
+
+static void ov511_buf_callback(unsigned long data)
+{
+	struct usb_ov511 *ov511 = (struct usb_ov511 *)data;
+	PDEBUG(4, "entered");
+	down(&ov511->buf_lock);
+
+	if (ov511->buf_state == BUF_PEND_DEALLOC)
+		ov511_do_dealloc(ov511);
+
+	up(&ov511->buf_lock);
+	PDEBUG(4, "leaving");
+}
+
+static void ov511_dealloc(struct usb_ov511 *ov511, int now)
+{
+	struct timer_list *bt = &(ov511->buf_timer);
+	PDEBUG(4, "entered");
+	down(&ov511->buf_lock);
+
+	PDEBUG(4, "deallocating buffer memory %s", now ? "now" : "later");
+
+	if (ov511->buf_state == BUF_PEND_DEALLOC) {
+		ov511->buf_state = BUF_ALLOCATED;
+		del_timer(bt);
+	}
+
+	if (now)
+		ov511_do_dealloc(ov511);
+	else {
+		ov511->buf_state = BUF_PEND_DEALLOC;
+		init_timer(bt);
+		bt->function = ov511_buf_callback;
+		bt->data = (unsigned long)ov511;
+		bt->expires = jiffies + buf_timeout * HZ;
+		add_timer(bt);
+	}
+	up(&ov511->buf_lock);
+	PDEBUG(4, "leaving");
+}
+
+/****************************************************************************
+ *
  * V4L API
  *
  ***************************************************************************/
@@ -1681,7 +2082,7 @@
 static int ov511_open(struct video_device *dev, int flags)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int i, err = 0;
+	int err = 0;
 
 	MOD_INC_USE_COUNT;
 	PDEBUG(4, "opening");
@@ -1692,33 +2093,16 @@
 		goto out;
 
 	err = -ENOMEM;
-
-	/* Allocate memory for the frame buffers */
-	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
-	if (!ov511->fbuf)
+	if (ov511_alloc(ov511))
 		goto out;
 
 	ov511->sub_flag = 0;
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].grabstate = FRAME_UNUSED;
-		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE;
-		PDEBUG(4, "frame [%d] @ %p", i, ov511->frame[0].data);
-
-		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
-			MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
-		if (!ov511->sbuf[i].data) {
-open_free_ret:
-			while (--i) kfree(ov511->sbuf[i].data);
-			rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE);
-			goto out;
-		}	
-		PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
-	}
-
 	err = ov511_init_isoc(ov511);
-	if (err)
-		goto open_free_ret;
+	if (err) {
+		ov511_dealloc(ov511, 0);
+		goto out;
+	}
 
 	ov511->user++;
 
@@ -1734,28 +2118,27 @@
 static void ov511_close(struct video_device *dev)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int i;
 
 	PDEBUG(4, "ov511_close");
 	
-	down(&ov511->lock);	
-	ov511->user--;
+	down(&ov511->lock);
 
+	ov511->user--;
 	ov511_stop_isoc(ov511);
 
-	rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
-	for (i = 0; i < OV511_NUMFRAMES; i++)
-		kfree(ov511->sbuf[i].data);
+	if (ov511->dev)
+		ov511_dealloc(ov511, 0);
 
 	up(&ov511->lock);
 
 	if (!ov511->dev) {
+		ov511_dealloc(ov511, 1);
 		video_unregister_device(&ov511->vdev);
 		kfree(ov511);
+		ov511 = NULL;
 	}
 
 	MOD_DEC_USE_COUNT;
-
 }
 
 static int ov511_init_done(struct video_device *dev)
@@ -1792,8 +2175,8 @@
 		b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
 		b.channels = 1;
 		b.audios = 0;
-		b.maxwidth = DEFAULT_WIDTH;
-		b.maxheight = DEFAULT_HEIGHT;
+		b.maxwidth = ov511->maxwidth;
+		b.maxheight = ov511->maxheight;
 		b.minwidth = 32;
 		b.minheight = 16;
 
@@ -1857,11 +2240,17 @@
 		if (copy_from_user(&p, arg, sizeof(p)))
 			return -EFAULT;
 
+		if (p.palette != VIDEO_PALETTE_GREY &&
+		    p.palette != VIDEO_PALETTE_RGB24 &&
+		    p.palette != VIDEO_PALETTE_YUV422 &&
+		    p.palette != VIDEO_PALETTE_YUYV &&
+		    p.palette != VIDEO_PALETTE_YUV420 &&
+		    p.palette != VIDEO_PALETTE_YUV422P)
+			return -EINVAL;
+			
 		if (ov7610_set_picture(ov511, &p))
 			return -EIO;
 
-		/* FIXME: check validity */
-
 		PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette);
 		for (i = 0; i < OV511_NUMFRAMES; i++) {
 			ov511->frame[i].depth = p.depth;
@@ -1928,9 +2317,9 @@
 			return -EINVAL;
 		if (vw.clipcount)
 			return -EINVAL;
-		if (vw.height != DEFAULT_HEIGHT)
+		if (vw.height != ov511->maxheight)
 			return -EINVAL;
-		if (vw.width != DEFAULT_WIDTH)
+		if (vw.width != ov511->maxwidth)
 			return -EINVAL;
 #endif
 
@@ -1974,8 +2363,8 @@
 		struct video_mbuf vm;
 
 		memset(&vm, 0, sizeof(vm));
-		vm.size = 2 * MAX_DATA_SIZE;
-		vm.frames = 2;
+		vm.size = OV511_NUMFRAMES * MAX_DATA_SIZE;
+		vm.frames = OV511_NUMFRAMES;
 		vm.offsets[0] = 0;
 		vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval);
 
@@ -1992,17 +2381,24 @@
 		if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
 			return -EFAULT;
 
-		PDEBUG(4, "MCAPTURE");
+		PDEBUG(4, "CMCAPTURE");
 		PDEBUG(4, "frame: %d, size: %dx%d, format: %d",
 			vm.frame, vm.width, vm.height, vm.format);
 
 		if (vm.format != VIDEO_PALETTE_RGB24 &&
+		    vm.format != VIDEO_PALETTE_YUV422 &&
+		    vm.format != VIDEO_PALETTE_YUYV &&
+		    vm.format != VIDEO_PALETTE_YUV420 &&
+		    vm.format != VIDEO_PALETTE_YUV422P &&
 		    vm.format != VIDEO_PALETTE_GREY)
 			return -EINVAL;
 
 		if ((vm.frame != 0) && (vm.frame != 1))
 			return -EINVAL;
 				
+		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight)
+			return -EINVAL;
+
 		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
 			return -EBUSY;
 
@@ -2061,8 +2457,23 @@
 				init_waitqueue_head(&ov511->frame[frame].wq);
 #endif
 				interruptible_sleep_on(&ov511->frame[frame].wq);
-				if (signal_pending(current))
-					return -EINTR;
+				if (signal_pending(current)) {
+					if (retry_sync) {
+						PDEBUG(3, "***retry sync***");
+
+						/* Polling apps will destroy frames with that! */
+						ov511_new_frame(ov511, frame);
+						ov511->curframe = -1;
+
+						/* This will request another frame. */
+						if (waitqueue_active(&ov511->frame[frame].wq))
+							wake_up_interruptible(&ov511->frame[frame].wq);
+
+						return 0;
+ 					} else {
+						return -EINTR;
+					}
+				}
 			} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
 
 			if (ov511->frame[frame].grabstate == FRAME_ERROR) {
@@ -2102,14 +2513,14 @@
 		return 0;
 	}
 	case VIDIOCKEY:
-		return 0; 		
+		return 0;
 	case VIDIOCCAPTURE:
 		return -EINVAL;
 	case VIDIOCSFBUF:
 		return -EINVAL;
 	case VIDIOCGTUNER:
 	case VIDIOCSTUNER:
-		return -EINVAL;			
+		return -EINVAL;
 	case VIDIOCGFREQ:
 	case VIDIOCSFREQ:
 		return -EINVAL;
@@ -2180,7 +2591,7 @@
 		frame->bytes_read = 0;
 		err("** ick! ** Errored frame %d", ov511->curframe);
 		if (ov511_new_frame(ov511, frmx))
-			err("ov511_read: ov511_new_frame error");
+			err("read: ov511_new_frame error");
 		goto restart;
 	}
 
@@ -2309,12 +2720,9 @@
 		{ OV511_I2C_BUS, 0x16, 0x06 },
 		{ OV511_I2C_BUS, 0x28, 0x24 },
 		{ OV511_I2C_BUS, 0x2b, 0xac },
-		{ OV511_I2C_BUS, 0x05, 0x00 },
-		{ OV511_I2C_BUS, 0x06, 0x00 },
 		{ OV511_I2C_BUS, 0x12, 0x00 },
 		{ OV511_I2C_BUS, 0x38, 0x81 },
 		{ OV511_I2C_BUS, 0x28, 0x24 },	/* 0c */
-		{ OV511_I2C_BUS, 0x05, 0x00 },
 		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
 		{ OV511_I2C_BUS, 0x15, 0x01 },
 		{ OV511_I2C_BUS, 0x20, 0x1c },
@@ -2322,7 +2730,6 @@
 		{ OV511_I2C_BUS, 0x24, 0x10 },
 		{ OV511_I2C_BUS, 0x25, 0x8a },
 		{ OV511_I2C_BUS, 0x27, 0xc2 },
-		{ OV511_I2C_BUS, 0x29, 0x03 },	/* 91 */
 		{ OV511_I2C_BUS, 0x2a, 0x04 },
 		{ OV511_I2C_BUS, 0x2c, 0xfe },
 		{ OV511_I2C_BUS, 0x30, 0x71 },
@@ -2344,14 +2751,12 @@
 		{ OV511_I2C_BUS, 0x2b, 0xac },
 		{ OV511_I2C_BUS, 0x12, 0x00 },
 		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x05, 0x00 },
 		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
 		{ OV511_I2C_BUS, 0x15, 0x01 },
 		{ OV511_I2C_BUS, 0x23, 0x00 },
 		{ OV511_I2C_BUS, 0x24, 0x10 },
 		{ OV511_I2C_BUS, 0x25, 0x8a },
 		{ OV511_I2C_BUS, 0x27, 0xe2 },
-		{ OV511_I2C_BUS, 0x29, 0x03 },
 		{ OV511_I2C_BUS, 0x2a, 0x00 },
 		{ OV511_I2C_BUS, 0x2c, 0xfe },
 		{ OV511_I2C_BUS, 0x30, 0x71 },
@@ -2368,38 +2773,68 @@
 
 	PDEBUG (4, "starting configuration");
 
-	if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                        OV7610_I2C_WRITE_ID) < 0)
+	/* This looks redundant, but is necessary for WebCam 3 */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
+	                    OV7610_I2C_WRITE_ID) < 0)
 		return -1;
 
-	if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                        OV7610_I2C_READ_ID) < 0)
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
+	                    OV7610_I2C_READ_ID) < 0)
 		return -1;
 
 	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
 		return -1;
-	
+
 	/* Reset the 76xx */ 
 	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
 
+	/* 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))
+		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
 			success = 1;
+			continue;
+		}
 
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 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);
+		/* 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);
+		PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i);
 	} else {
-		err("Failed to read sensor ID. You might not have an OV76xx,");
-		err("or it may be not responding. Report this to");
-		err("mmcclelland@delphi.com");
-		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);
+
+		i = 0;
+		success = 0;
+		while (i <= i2c_detect_tries) {
+			if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
+			    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+				success = 1;
+				break;
+			} else {
+				i++;
+			}
+		}
+
+		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;
+		} else {
+			PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1);
+		}
 	}
 
 	/* Detect sensor if user didn't use override param */
@@ -2437,6 +2872,128 @@
 			return -1;
 	}
 
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+
+	if (aperture < 0) {          /* go with the default */
+		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
+	} else if (aperture <= 0xf) {  /* user overrode default */
+		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
+			return -1;
+	} else {
+		err("Invalid setting for aperture; legal value: 0 - 15");
+		return -1;
+	}
+
+	if (autoadjust) {
+		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 
+		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	} else {
+		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
+		if (ov511_i2c_write(dev, 0x2d, 
+		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
+		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+	}
+
+	return 0;
+}
+
+static int ov6xx0_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int i, success, rc;
+
+	static struct ov511_regvals aRegvalsNorm6x20[] = {
+		{ OV511_I2C_BUS, 0x12, 0x80 },  /* reset */
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x03, 0xd0 },
+		{ OV511_I2C_BUS, 0x05, 0x7f },
+		{ OV511_I2C_BUS, 0x07, 0xa8 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x10, 0xff },  /* ? */
+		{ OV511_I2C_BUS, 0x14, 0x04 },
+		{ OV511_I2C_BUS, 0x16, 0x06 },  /* ? */
+		{ OV511_I2C_BUS, 0x19, 0x04 },
+		{ OV511_I2C_BUS, 0x1a, 0x93 },
+		{ OV511_I2C_BUS, 0x20, 0x28 },
+		{ OV511_I2C_BUS, 0x27, 0xa2 },
+		{ OV511_I2C_BUS, 0x28, 0x24 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 },  /* 84? */
+		{ OV511_I2C_BUS, 0x2b, 0xac },  /* a8? */
+		{ OV511_I2C_BUS, 0x2d, 0x95 },
+		{ OV511_I2C_BUS, 0x33, 0x28 },
+		{ OV511_I2C_BUS, 0x34, 0xc7 },
+		{ OV511_I2C_BUS, 0x38, 0x8b },
+		{ OV511_I2C_BUS, 0x3c, 0x5c },
+		{ OV511_I2C_BUS, 0x3d, 0x80 },
+		{ OV511_I2C_BUS, 0x3f, 0x00 },
+		{ OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */
+		{ OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */
+		{ OV511_I2C_BUS, 0x4d, 0xd2 },
+		{ OV511_I2C_BUS, 0x4e, 0xc1 },
+		{ OV511_I2C_BUS, 0x4f, 0x04 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	PDEBUG (4, "starting sensor configuration");
+	
+	/* Reset the 6xx0 */ 
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+
+	/* Wait for it to initialize */ 
+	schedule_timeout (1 + 150 * HZ / 1000);
+
+	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
+		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+			success = 1;
+			continue;
+		}
+
+		/* Reset the 6xx0 */ 
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+		/* Wait for it to initialize */ 
+		schedule_timeout (1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+	}
+
+	if (success) {
+		PDEBUG(1, "I2C synced in %d attempt(s)", i);
+	} else {
+		err("Failed to read sensor ID. You might not have an OV6xx0,");
+		err("or it may be not responding. Report this to");
+		err("mwm@i.am");
+		return -1;
+	}
+
+	/* Detect sensor if user didn't use override param */
+	if (sensor == 0) {
+		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+
+		if (rc < 0) {
+			err("Error detecting sensor type");
+			return -1;
+		} else {
+			info("Sensor is an OV6xx0 (version %d)", rc & 3);
+			ov511->sensor = SEN_OV6620;
+		}
+	} else {	/* sensor != 0; user overrode detection */
+		ov511->sensor = sensor;
+		info("Sensor set to type %d", ov511->sensor);
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 352;
+	ov511->maxheight = 288;
+
+	PDEBUG(4, "Writing 6x20 registers");
+	if (ov511_write_regvals(dev, aRegvalsNorm6x20))
+		return -1;
+
 	if (aperture < 0) {          /* go with the default */
 		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
 	} else if (aperture <= 0xf) {  /* user overrode default */
@@ -2482,10 +3039,10 @@
 		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
 		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
 		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
-		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, /* 0f */
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x3f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x3f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 },
+		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 },
 		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 },
 		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 },
 		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
@@ -2498,8 +3055,9 @@
 
 	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
 
-	init_waitqueue_head(&ov511->frame[0].wq);
-	init_waitqueue_head(&ov511->frame[1].wq);
+	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) == -1) {
@@ -2514,11 +3072,52 @@
 
 	ov511->snap_enabled = snapshot;	
 
+	/* Test for 76xx */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
+	                   OV7610_I2C_WRITE_ID) < 0)
+		goto error;
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
+	                   OV7610_I2C_READ_ID) < 0)
+		goto error;
+
+	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+		goto error;
+
+	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+		/* Test for 6xx0 */
+		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
+		                    OV6xx0_I2C_WRITE_ID) < 0)
+			goto error;
+
+		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
+		                    OV6xx0_I2C_READ_ID) < 0)
+			goto error;
+
+		if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+			goto error;
+
+		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+			err("Can't determine sensor slave IDs");
+			goto error;
+		}
+		
+		if(ov6xx0_configure(ov511) < 0) {
+			err("failed to configure OV6xx0");
+ 			goto error;
+		}
+	} else {
+		if(ov76xx_configure(ov511) < 0) {
+			err("failed to configure OV76xx");
+	 		goto error;
+		}
+	}
+	
 	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
 	 * (using read() instead). */
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].width = DEFAULT_WIDTH;
-		ov511->frame[i].height = DEFAULT_HEIGHT;
+		ov511->frame[i].width = ov511->maxwidth;
+		ov511->frame[i].height = ov511->maxheight;
 		ov511->frame[i].depth = 24;
 		ov511->frame[i].bytes_read = 0;
 		ov511->frame[i].segment = 0;
@@ -2526,14 +3125,8 @@
 		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
 	}
 
-	/* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */
-
-	if(ov76xx_configure(ov511) < 0) {
-		err("failed to configure OV76xx");
- 		goto error;	
-	}
-
-	if (ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT,
+	/* Initialize to max width/height, RGB24 */
+	if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
 				 VIDEO_PALETTE_RGB24, 0) < 0)
 		goto error;
 
@@ -2569,10 +3162,12 @@
 	interface = &dev->actconfig->interface[ifnum].altsetting[0];
 
 	/* Is it an OV511/OV511+? */
-	if (dev->descriptor.idVendor != 0x05a9)
+	if (dev->descriptor.idVendor != 0x05a9 
+	 && dev->descriptor.idVendor != 0x0813)
 		return NULL;
 	if (dev->descriptor.idProduct != 0x0511
-	 && dev->descriptor.idProduct != 0xA511)
+	 && dev->descriptor.idProduct != 0xA511
+	 && dev->descriptor.idProduct != 0x0002)
 		return NULL;
 
 	/* Checking vendor/product should be enough, but what the hell */
@@ -2603,6 +3198,15 @@
 		info("USB OV511+ camera found");
 		ov511->bridge = BRG_OV511PLUS;
 		break;
+	case 0x0002:
+		if (dev->descriptor.idVendor != 0x0813)
+			goto error;
+		info("Intel Play Me2Cam (OV511+) found");
+		ov511->bridge = BRG_OV511PLUS;
+		break;
+	default:
+		err("Unknown product ID");
+		goto error;
 	}
 
 	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
@@ -2629,7 +3233,7 @@
 
 	if (clist[i].id == -1) {
 		err("Camera type (%d) not recognized", ov511->customid);
-		err("Please contact mmcclelland@delphi.com to request");
+		err("Please contact mwm@i.am to request");
 		err("support for your camera.");
 	}
 
@@ -2641,6 +3245,8 @@
 	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;
 	} else {
 		err("Failed to configure camera");
 		goto error;
@@ -2663,51 +3269,53 @@
 static void ov511_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
+	int n;
 
 	MOD_INC_USE_COUNT;
 
+	PDEBUG(3, "");
+
 	/* We don't want people trying to open up the device */
 	if (!ov511->user)
 		video_unregister_device(&ov511->vdev);
+	else
+		PDEBUG(3, "Device open...deferring video_unregister_device");
 
-	usb_driver_release_interface(&ov511_driver,
-		&ov511->dev->actconfig->interface[ov511->iface]);
+	for (n = 0; n < OV511_NUMFRAMES; n++)
+		ov511->frame[n].grabstate = FRAME_ERROR;
 
-	ov511->dev = NULL;
-	ov511->frame[0].grabstate = FRAME_ERROR;
-	ov511->frame[1].grabstate = FRAME_ERROR;
 	ov511->curframe = -1;
 
 	/* This will cause the process to request another frame */
-	if (waitqueue_active(&ov511->frame[0].wq))
-		wake_up_interruptible(&ov511->frame[0].wq);
-	if (waitqueue_active(&ov511->frame[1].wq))
-		wake_up_interruptible(&ov511->frame[1].wq);
+	for (n = 0; n < OV511_NUMFRAMES; n++)
+		if (waitqueue_active(&ov511->frame[n].wq))
+			wake_up_interruptible(&ov511->frame[n].wq);
 	if (waitqueue_active(&ov511->wq))
 		wake_up_interruptible(&ov511->wq);
 
 	ov511->streaming = 0;
 
 	/* Unschedule all of the iso td's */
-	if (ov511->sbuf[1].urb) {
-		ov511->sbuf[1].urb->next = NULL;
-		usb_unlink_urb(ov511->sbuf[1].urb);
-		usb_free_urb(ov511->sbuf[1].urb);
-		ov511->sbuf[1].urb = NULL;
-	}
-	if (ov511->sbuf[0].urb) {
-		ov511->sbuf[0].urb->next = NULL;
-		usb_unlink_urb(ov511->sbuf[0].urb);
-		usb_free_urb(ov511->sbuf[0].urb);
-		ov511->sbuf[0].urb = NULL;
+	for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
+		if (ov511->sbuf[n].urb) {
+			ov511->sbuf[n].urb->next = NULL;
+			usb_unlink_urb(ov511->sbuf[n].urb);
+			usb_free_urb(ov511->sbuf[n].urb);
+			ov511->sbuf[n].urb = NULL;
+		}
 	}
 
+	usb_driver_release_interface(&ov511_driver,
+		&ov511->dev->actconfig->interface[ov511->iface]);
+	ov511->dev = NULL;
+
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         destroy_proc_ov511_cam(ov511);
 #endif
 
 	/* Free the memory */
 	if (ov511 && !ov511->user) {
+		ov511_dealloc(ov511, 1);
 		kfree(ov511);
 		ov511 = NULL;
 	}
@@ -2746,7 +3354,7 @@
 static void __exit usb_ov511_exit(void)
 {
 	usb_deregister(&ov511_driver);
-	info("ov511 driver deregistered");
+	info("driver deregistered");
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_destroy();
diff -urN linux-2.2.18-pre7/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux-2.2.18-pre7/drivers/usb/ov511.h	Mon Dec  3 22:13:47 2001
+++ linux/drivers/usb/ov511.h	Mon Dec  3 22:13:54 2001
@@ -171,9 +171,7 @@
 #define OV7610_REG_COM_K         0x38	/* misc registers */
 
 
-#define STREAM_BUF_SIZE	(PAGE_SIZE * 4)
-
-#define SCRATCH_BUF_SIZE 384
+#define SCRATCH_BUF_SIZE 512
 
 #define FRAMES_PER_DESC		10	/* FIXME - What should this be? */
 #define FRAME_SIZE_PER_DESC	993	/* FIXME - Deprecated */
@@ -185,6 +183,9 @@
 // FIXME - these can vary between specific models
 #define OV7610_I2C_WRITE_ID 0x42
 #define OV7610_I2C_READ_ID  0x43
+#define OV6xx0_I2C_WRITE_ID 0xC0
+#define OV6xx0_I2C_READ_ID  0xC1
+
 #define OV511_I2C_CLOCK_PRESCALER 0x03
 
 /* Prototypes */
@@ -205,6 +206,7 @@
 	SEN_OV7610,
 	SEN_OV7620,
 	SEN_OV7620AE,
+	SEN_OV6620,
 };
 
 enum {
@@ -213,6 +215,13 @@
 	STATE_LINES,		/* Parsing lines */
 };
 
+/* Buffer states */
+enum {
+	BUF_NOT_ALLOCATED,
+	BUF_ALLOCATED,
+	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
+};
+
 struct usb_device;
 
 struct ov511_sbuf {
@@ -249,7 +258,7 @@
 	int hdrheight;		/* Height */
 
 	int sub_flag;		/* Sub-capture mode for this frame? */
-	int format;		/* Format for this frame */
+	unsigned int format;	/* Format for this frame */
 	int segsize;		/* How big is each segment from the camera? */
 
 	volatile int grabstate;	/* State of grabbing */
@@ -280,6 +289,10 @@
 	int desc;
 	unsigned char iface;
 
+	/* Determined by sensor type */
+	int maxwidth;
+	int maxheight;
+
 	int brightness;
 	int colour;
 	int contrast;
@@ -324,6 +337,11 @@
 				/* proc interface */
 	struct semaphore param_lock;	/* params lock for this camera */
 	struct proc_dir_entry *proc_entry;	/* /proc/ov511/videoX */
+	
+	/* Framebuffer/sbuf management */
+	int buf_state;
+	struct semaphore buf_lock;
+	struct timer_list buf_timer;
 };
 
 struct cam_list {
@@ -346,7 +364,6 @@
 	u8 lndv;		/* line divisor */
 	u8 m420;
 	u8 common_A;
-	u8 common_C;
 	u8 common_L;
 };
 
diff -urN linux-2.2.18-pre7/include/linux/tasks.h linux/include/linux/tasks.h
--- linux-2.2.18-pre7/include/linux/tasks.h	Tue May 11 10:35:42 1999
+++ linux/include/linux/tasks.h	Mon Dec  3 22:13:54 2001
@@ -11,7 +11,7 @@
 #define NR_CPUS 1
 #endif
 
-#define NR_TASKS	512	/* On x86 Max 4092, or 4090 w/APM configured. */
+#define NR_TASKS	512	/* On x86 Max about 4000 */
 
 #define MAX_TASKS_PER_USER (NR_TASKS/2)
 #define MIN_TASKS_LEFT_FOR_ROOT 4
