]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.2.18pre8 2.2.18pre8
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:22:20 +0000 (15:22 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:22:20 +0000 (15:22 -0500)
o Fix mtrr compile bug (Peter Blomgren)
o Alpha PCI boot up fix (Michal Jaegermann)
o Fix vt/keyboard dependancy in USB config (Arjan van de Ven)
o Fix sound hangs on cs4281 (Tom Woller)
o Fix Alpha vmlinuz.lds (Andrea Arcangeli)
o Fix CDROMPLAYTRKIND bug, allow root to open (Jens Axboe)
the cd door whenver.
o Update ov511 to match 2.4 (Greg Kroah-Hartman)
o Further devio.c fix (Greg Kroah-Hartman)
o Update NR_TASKS comment (Jarkko Kovala)
o Further sparc64 ioctl translator fixes (Andi Kleen)

14 files changed:
Documentation/usb/ov511.txt
Makefile
arch/alpha/kernel/bios32.c
arch/alpha/vmlinux.lds
arch/i386/kernel/mtrr.c
arch/sparc64/kernel/sys_sparc32.c
drivers/block/ide-cd.c
drivers/cdrom/cdrom.c
drivers/scsi/sr_ioctl.c
drivers/usb/Config.in
drivers/usb/devio.c
drivers/usb/ov511.c
drivers/usb/ov511.h
include/linux/tasks.h

index a9245312959ffd3cc0f06bf983581f9c9bb3450d..be425c1187ada394e7dcf3a9d58160b1ae0884ba 100644 (file)
@@ -6,12 +6,16 @@ Author: Mark McClelland
 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 @@ MODULE PARAMETERS:
   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 @@ MODULE PARAMETERS:
         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 @@ TODO:
    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:
index 18fb8e23b826ef8573d7669abc6f7f371165a432..f01bb67cba2381701363355bb788c9f15e515031 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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/)
 
index adc7dcfa377efdd45356108694f381620f8c64d7..03f9e7dc325d002eb6af47561db6f877cee884fa 100644 (file)
@@ -828,6 +828,7 @@ layout_bus(struct pci_bus *bus)
 
        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 @@ layout_bus(struct pci_bus *bus)
 
        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 @@ common_pci_fixup(int (*map_irq)(struct pci_dev *dev, int slot, int pin),
         */
        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;
 
index 3583972b48d2eed36cbdc92b2a693c4c3bc96254..86b204497dbcfdb8fc3fbd036c71eb9847f0683c 100644 (file)
@@ -32,7 +32,7 @@ SECTIONS
   .setup.init : { *(.setup.init) }
   __setup_end = .;
 
-  . = ALIGN(8)
+  . = ALIGN(8);
   __initcall_start = .;                /* the init functions to be called */
   .initcall.init : { *(.initcall.init) }
   __initcall_end = .;
index 2d8270ccfe68bc07c8ca933671e0d2782fb81035..292026e31adc777a58dcb8f41bba5f58d80c3964 100644 (file)
@@ -442,10 +442,10 @@ static unsigned int get_num_var_ranges (void)
        /*  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 @@ static int have_wrcomb (void)
        return (config & (1<<10));
        /*break;*/
       case X86_VENDOR_CYRIX:
-      case X86_VENDOR_CENTAUR:
        return 1;
        /*break;*/
     }
index e1030104ffa376e06fb746b479167f179db13cb5..7e8deda7754d35943c7edd85f6a36a6dc83ca57d 100644 (file)
@@ -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 @@ struct ncp_mount_data32 {
 
 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 @@ static void *do_ncp_super_data_conv(void *raw_data)
        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 @@ struct smb_mount_data32 {
 
 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 @@ static void *do_smb_super_data_conv(void *raw_data)
        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;
 }
 
index 6fd10edd98c2e57076eebf71172a63c52268b2fa..949e9dba0cc5cdf3c51d8689d30e51c44e1b7f17 100644 (file)
@@ -1827,6 +1827,20 @@ static int cdrom_select_speed(ide_drive_t *drive, int speed,
        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 @@ int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
        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 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
                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
index 4c242a73bc4f95b339cbf41f4dcb7672e49cb55a..ddf1f028968fe3058d129a2c3cfc9a2bf4d545b6 100644 (file)
@@ -1604,7 +1604,7 @@ static int cdrom_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
                        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);
                }
@@ -1966,19 +1966,6 @@ static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
                /* 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;
                cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
index 5ddc9d830634cd5026bdbd22ef5a1e18ac744ecb..644544e45451155e3ae873bc6ab45ee277c6260d 100644 (file)
@@ -314,6 +314,8 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
     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 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
        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 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
           (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 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
                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 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
                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;
@@ -458,6 +451,20 @@ int sr_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void* arg)
                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;
+       }
+
     default:
         return -EINVAL;
     }
index fc2a564216a9dd71e75ff6945fd15a402a433d24..6fadec9173ff5b434072a0e2eb23abe114f465ed 100644 (file)
@@ -83,7 +83,9 @@ comment 'USB HID'
    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
index 7435e5a80f7736ff9caf99175afe34aea0264994..115ef51e8cd3fa18c0fa900ba13c8d0b4920841a 100644 (file)
@@ -962,19 +962,22 @@ static int processcompl(struct async *as)
                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;
index 64a89d05357ae7aba688a1dae9c74ab624bda764..8ca0e7934d0396f9ebe982d9aa283b1ec6ce03aa 100644 (file)
@@ -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 @@ static const char version[] = "1.17";
 #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 @@ static int aperture = -1;
  * 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_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_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 <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 @@ static struct cam_list clist[] = {
        {  -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 @@ static struct palette_list plist[] = {
        { VIDEO_PALETTE_YUV410P,"YUV410P" },
        { -1, NULL }
 };
+#endif
 
 /**********************************************************************
  *
@@ -436,7 +473,7 @@ static int ov511_reg_write(struct usb_device *dev,
        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 @@ static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
        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 @@ static int ov511_i2c_write(struct usb_device *dev,
        return 0;
 
 error:
-       err("ov511_i2c_write: error %d", rc);
+       err("i2c write: error %d", rc);
        return rc;
 }
 
@@ -577,7 +614,7 @@ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
        return value;
 
 error:
-       err("ov511_i2c_read: error %d", rc);
+       err("i2c read: error %d", rc);
        return rc;
 }
 
@@ -605,7 +642,7 @@ static int ov511_write_regvals(struct usb_device *dev,
        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_range(struct usb_device *dev, int reg1, int regn)
 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 @@ static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
        }
 }
 
-#if 0
 static void ov511_dump_regs(struct usb_device *dev)
 {
        PDEBUG(1, "CAMERA INTERFACE REGS");
@@ -651,7 +688,7 @@ static void ov511_dump_regs(struct usb_device *dev)
        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 @@ static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
        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 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
        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) {
-               if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+       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_BRT, p->brightness >> 8) < 0)
+               if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
+                       return -EIO;
+
+               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)
                        return -EIO;
        } else if ((ov511->sensor == SEN_OV7620) 
                 || (ov511->sensor == SEN_OV7620AE)) {
@@ -797,7 +838,7 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
                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 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
        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 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
        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 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
 {
        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_mode_init_regs(struct usb_ov511 *ov511,
                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_mode_init_regs(struct usb_ov511 *ov511,
                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 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
        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 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
 #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 @@ ov511_mode_init_regs(struct usb_ov511 *ov511,
  *
  * 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 @@ static inline void
 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 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
 
 #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 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
 
 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 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
                }
        }
 #else
+
+#ifndef OV511_FORCE_MONO
        /* Just dump pix data straight out for debug */
        int i, j;
 
@@ -1224,10 +1329,172 @@ ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0,
                }
                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 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
                        /* 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 @@ check_middle:
                    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 @@ check_middle:
                         */
                        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 @@ check_middle:
                                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 @@ static void ov511_isoc_irq(struct urb *urb)
                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 void ov511_isoc_irq(struct urb *urb)
 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 @@ static int ov511_init_isoc(struct usb_ov511 *ov511)
        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;
+       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;
 
-       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);
+       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 int ov511_init_isoc(struct usb_ov511 *ov511)
 
 static void ov511_stop_isoc(struct usb_ov511 *ov511)
 {
+       int n;
+
        if (!ov511->streaming || !ov511->dev)
                return;
 
@@ -1612,24 +1899,19 @@ static void ov511_stop_isoc(struct usb_ov511 *ov511)
        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 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
                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,19 +1939,140 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
        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_new_frame(struct usb_ov511 *ov511, int framenum)
 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 @@ static int ov511_open(struct video_device *dev, int flags)
                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 @@ out:
 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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                        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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
                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 @@ redo:
                                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 @@ redo:
                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 @@ restart:
                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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
                { 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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
                { 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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
                { 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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
 
        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 @@ static int ov76xx_configure(struct usb_ov511 *ov511)
                        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 @@ static int ov511_configure(struct usb_ov511 *ov511)
                { 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 @@ static int ov511_configure(struct usb_ov511 *ov511)
 
        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 @@ static int ov511_configure(struct usb_ov511 *ov511)
 
        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 @@ static int ov511_configure(struct usb_ov511 *ov511)
                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 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
        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 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
                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 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
 
        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 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum)
        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 @@ error:
 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 int __init usb_ov511_init(void)
 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();
index 92827afb3321d357137a37bb7c54f705bb762039..a4ef7dacde5b8d47090007bc9a17e72444cfa53c 100644 (file)
@@ -171,9 +171,7 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args
 #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 @@ if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args
 // 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 @@ enum {
        SEN_OV7610,
        SEN_OV7620,
        SEN_OV7620AE,
+       SEN_OV6620,
 };
 
 enum {
@@ -213,6 +215,13 @@ enum {
        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 @@ struct ov511_frame {
        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 @@ struct usb_ov511 {
        int desc;
        unsigned char iface;
 
+       /* Determined by sensor type */
+       int maxwidth;
+       int maxheight;
+
        int brightness;
        int colour;
        int contrast;
@@ -324,6 +337,11 @@ struct usb_ov511 {
                                /* 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 @@ struct mode_list {
        u8 lndv;                /* line divisor */
        u8 m420;
        u8 common_A;
-       u8 common_C;
        u8 common_L;
 };
 
index 91b758f49681ced96bc2daa1905ee2b5728bb445..b8489f4a9377880e65738d77106b6e2a8de06155 100644 (file)
@@ -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