vserver 2.0 rc7
[linux-2.6.git] / drivers / usb / media / sn9c102_core.c
index bcd9fbd..31d5740 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************************
- * V4L2 driver for SN9C10[12] PC Camera Controllers                        *
+ * V4L2 driver for SN9C10x PC Camera Controllers                           *
  *                                                                         *
- * Copyright (C) 2004 by Luca Risolia <luca.risolia@studio.unibo.it>       *
+ * Copyright (C) 2004-2005 by Luca Risolia <luca.risolia@studio.unibo.it>  *
  *                                                                         *
  * This program is free software; you can redistribute it and/or modify    *
  * it under the terms of the GNU General Public License as published by    *
 #include <linux/string.h>
 #include <linux/device.h>
 #include <linux/fs.h>
-#include <linux/time.h>
 #include <linux/delay.h>
 #include <linux/stddef.h>
+#include <linux/compiler.h>
 #include <linux/ioctl.h>
 #include <linux/poll.h>
 #include <linux/stat.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <linux/page-flags.h>
+#include <linux/byteorder/generic.h>
 #include <asm/page.h>
 #include <asm/uaccess.h>
 
@@ -52,8 +53,7 @@ MODULE_VERSION(SN9C102_MODULE_VERSION);
 MODULE_LICENSE(SN9C102_MODULE_LICENSE);
 
 static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1};
-static unsigned int nv;
-module_param_array(video_nr, short, nv, 0444);
+module_param_array(video_nr, short, NULL, 0444);
 MODULE_PARM_DESC(video_nr,
                  "\n<-1|n[,...]> Specify V4L2 minor mode number."
                  "\n -1 = use next available (default)"
@@ -66,6 +66,20 @@ MODULE_PARM_DESC(video_nr,
                  "\none and for every other camera."
                  "\n");
 
+static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = 
+                               SN9C102_FORCE_MUNMAP};
+module_param_array(force_munmap, bool, NULL, 0444);
+MODULE_PARM_DESC(force_munmap,
+                 "\n<0|1[,...]> Force the application to unmap previously "
+                 "\nmapped buffer memory before calling any VIDIOC_S_CROP or "
+                 "\nVIDIOC_S_FMT ioctl's. Not all the applications support "
+                 "\nthis feature. This parameter is specific for each "
+                 "\ndetected camera."
+                 "\n 0 = do not force memory unmapping"
+                 "\n 1 = force memory unmapping (save memory)"
+                 "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"."
+                 "\n");
+
 #ifdef SN9C102_DEBUG
 static unsigned short debug = SN9C102_DEBUG_LEVEL;
 module_param(debug, ushort, 0644);
@@ -83,16 +97,11 @@ MODULE_PARM_DESC(debug,
 
 /*****************************************************************************/
 
-typedef char sn9c102_sof_header_t[7];
-typedef char sn9c102_eof_header_t[4];
-
 static sn9c102_sof_header_t sn9c102_sof_header[] = {
        {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00},
        {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01},
 };
 
-/* Number of random bytes that complete the SOF above headers */
-#define SN9C102_SOFLEN 5
 
 static sn9c102_eof_header_t sn9c102_eof_header[] = {
        {0x00, 0x00, 0x00, 0x00},
@@ -103,17 +112,6 @@ static sn9c102_eof_header_t sn9c102_eof_header[] = {
 
 /*****************************************************************************/
 
-static inline unsigned long kvirt_to_pa(unsigned long adr)
-{
-       unsigned long kva, ret;
-
-       kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
-       kva |= adr & (PAGE_SIZE-1);
-       ret = __pa(kva);
-       return ret;
-}
-
-
 static void* rvmalloc(size_t size)
 {
        void* mem;
@@ -158,10 +156,16 @@ static void rvfree(void* mem, size_t size)
 }
 
 
-static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
+static u32 
+sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, 
+                        enum sn9c102_io_method io)
 {
        struct v4l2_pix_format* p = &(cam->sensor->pix_format);
-       const size_t imagesize = (p->width * p->height * p->priv)/8;
+       struct v4l2_rect* r = &(cam->sensor->cropcap.bounds);
+       const size_t imagesize = cam->module_param.force_munmap ||
+                                io == IO_READ ?
+                                (p->width * p->height * p->priv) / 8 :
+                                (r->width * r->height * p->priv) / 8;
        void* buff = NULL;
        u32 i;
 
@@ -170,15 +174,15 @@ static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count)
 
        cam->nbuffers = count;
        while (cam->nbuffers > 0) {
-               if ((buff = rvmalloc(cam->nbuffers * imagesize)))
+               if ((buff = rvmalloc(cam->nbuffers * PAGE_ALIGN(imagesize))))
                        break;
                cam->nbuffers--;
        }
 
        for (i = 0; i < cam->nbuffers; i++) {
-               cam->frame[i].bufmem = buff + i*imagesize;
+               cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
                cam->frame[i].buf.index = i;
-               cam->frame[i].buf.m.offset = i*imagesize;
+               cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
                cam->frame[i].buf.length = imagesize;
                cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                cam->frame[i].buf.sequence = 0;
@@ -237,9 +241,6 @@ int sn9c102_write_reg(struct sn9c102_device* cam, u8 value, u16 index)
        u8* buff = cam->control_buffer;
        int res;
 
-       if (index == 0x18)
-               value = (value & 0xcf) | (cam->reg[0x18] & 0x30);
-
        *buff = value;
 
        res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
@@ -294,9 +295,9 @@ sn9c102_i2c_wait(struct sn9c102_device* cam, struct sn9c102_sensor* sensor)
                if (r & 0x04)
                        return 0;
                if (sensor->frequency & SN9C102_I2C_400KHZ)
-                       udelay(5*8);
+                       udelay(5*16);
                else
-                       udelay(16*8);
+                       udelay(16*16);
        }
        return -EBUSY;
 }
@@ -323,18 +324,19 @@ sn9c102_i2c_detect_write_error(struct sn9c102_device* cam,
 
 
 int 
-sn9c102_i2c_try_read(struct sn9c102_device* cam,
-                     struct sn9c102_sensor* sensor, u8 address)
+sn9c102_i2c_try_raw_read(struct sn9c102_device* cam,
+                         struct sn9c102_sensor* sensor, u8 data0, u8 data1,
+                         u8 n, u8 buffer[])
 {
        struct usb_device* udev = cam->usbdev;
        u8* data = cam->control_buffer;
        int err = 0, res;
 
-       /* Write cycle - address */
+       /* Write cycle */
        data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
                  ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) | 0x10;
-       data[1] = sensor->slave_write_id;
-       data[2] = address;
+       data[1] = data0; /* I2C slave id */
+       data[2] = data1; /* address */
        data[7] = 0x10;
        res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
                              0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
@@ -343,11 +345,11 @@ sn9c102_i2c_try_read(struct sn9c102_device* cam,
 
        err += sn9c102_i2c_wait(cam, sensor);
 
-       /* Read cycle - 1 byte */
+       /* Read cycle - n bytes */
        data[0] = ((sensor->interface == SN9C102_I2C_2WIRES) ? 0x80 : 0) |
                  ((sensor->frequency & SN9C102_I2C_400KHZ) ? 0x01 : 0) |
-                 0x10 | 0x02;
-       data[1] = sensor->slave_read_id;
+                 (n << 4) | 0x02;
+       data[1] = data0;
        data[7] = 0x10;
        res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
                              0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
@@ -356,7 +358,7 @@ sn9c102_i2c_try_read(struct sn9c102_device* cam,
 
        err += sn9c102_i2c_wait(cam, sensor);
 
-       /* The read byte will be placed in data[4] */
+       /* The first read byte will be placed in data[4] */
        res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
                              0x0a, 0, data, 5, SN9C102_CTRL_TIMEOUT);
        if (res < 0)
@@ -364,12 +366,18 @@ sn9c102_i2c_try_read(struct sn9c102_device* cam,
 
        err += sn9c102_i2c_detect_read_error(cam, sensor);
 
-       if (err)
+       PDBGG("I2C read: address 0x%02X, first read byte: 0x%02X", data1,
+             data[4])
+
+       if (err) {
                DBG(3, "I2C read failed for %s image sensor", sensor->name)
+               return -1;
+       }
 
-       PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[4])
+       if (buffer)
+               memcpy(buffer, data, sizeof(buffer));
 
-       return err ? -1 : (int)data[4];
+       return (int)data[4];
 }
 
 
@@ -392,7 +400,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
        data[4] = data3;
        data[5] = data4;
        data[6] = data5;
-       data[7] = 0x10;
+       data[7] = 0x14;
        res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41,
                              0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT);
        if (res < 0)
@@ -404,7 +412,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
        if (err)
                DBG(3, "I2C write failed for %s image sensor", sensor->name)
 
-       PDBGG("I2C write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
+       PDBGG("I2C raw write: %u bytes, data0 = 0x%02X, data1 = 0x%02X, "
              "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X",
              n, data0, data1, data2, data3, data4, data5)
 
@@ -412,12 +420,21 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam,
 }
 
 
-int 
+int
+sn9c102_i2c_try_read(struct sn9c102_device* cam,
+                     struct sn9c102_sensor* sensor, u8 address)
+{
+       return sn9c102_i2c_try_raw_read(cam, sensor, sensor->i2c_slave_id,
+                                       address, 1, NULL);
+}
+
+
+static int 
 sn9c102_i2c_try_write(struct sn9c102_device* cam,
                       struct sn9c102_sensor* sensor, u8 address, u8 value)
 {
        return sn9c102_i2c_try_raw_write(cam, sensor, 3, 
-                                        sensor->slave_write_id, address,
+                                        sensor->i2c_slave_id, address,
                                         value, 0, 0, 0);
 }
 
@@ -441,26 +458,34 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value)
 
 /*****************************************************************************/
 
-static void* sn9c102_find_sof_header(void* mem, size_t len)
+static void*
+sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len)
 {
-       size_t soflen=sizeof(sn9c102_sof_header_t), SOFLEN=SN9C102_SOFLEN, i;
+       size_t soflen = sizeof(sn9c102_sof_header_t), i;
        u8 j, n = sizeof(sn9c102_sof_header) / soflen;
 
-       for (i = 0; (len >= soflen+SOFLEN) && (i <= len-soflen-SOFLEN); i++)
+       for (i = 0; (len >= soflen) && (i <= len - soflen); i++)
                for (j = 0; j < n; j++)
-                       if (!memcmp(mem + i, sn9c102_sof_header[j], soflen))
-                               /* Skips the header */
-                               return mem + i + soflen + SOFLEN;
+                       /* It's enough to compare 7 bytes */
+                       if (!memcmp(mem + i, sn9c102_sof_header[j], 7)) {
+                               memcpy(cam->sof_header, mem + i, soflen);
+                               /* Skip the header */
+                               return mem + i + soflen;
+                       }
 
        return NULL;
 }
 
 
-static void* sn9c102_find_eof_header(void* mem, size_t len)
+static void*
+sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len)
 {
        size_t eoflen = sizeof(sn9c102_eof_header_t), i;
        unsigned j, n = sizeof(sn9c102_eof_header) / eoflen;
 
+       if (cam->sensor->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+               return NULL; /* EOF header does not exist in compressed data */
+
        for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++)
                for (j = 0; j < n; j++)
                        if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen))
@@ -474,6 +499,7 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
 {
        struct sn9c102_device* cam = urb->context;
        struct sn9c102_frame_t** f;
+       size_t imagesize;
        unsigned long lock_flags;
        u8 i;
        int err = 0;
@@ -491,9 +517,14 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
                wake_up_interruptible(&cam->wait_stream);
        }
 
-       if ((cam->state & DEV_DISCONNECTED)||(cam->state & DEV_MISCONFIGURED))
+       if (cam->state & DEV_DISCONNECTED)
                return;
 
+       if (cam->state & DEV_MISCONFIGURED) {
+               wake_up_interruptible(&cam->wait_frame);
+               return;
+       }
+
        if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
                goto resubmit_urb;
 
@@ -501,6 +532,10 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
                (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t,
                                  frame);
 
+       imagesize = (cam->sensor->pix_format.width *
+                    cam->sensor->pix_format.height *
+                    cam->sensor->pix_format.priv) / 8;
+
        for (i = 0; i < urb->number_of_packets; i++) {
                unsigned int img, len, status;
                void *pos, *sof, *eof;
@@ -517,15 +552,17 @@ static void sn9c102_urb_complete(struct urb *urb, struct pt_regs* regs)
 
                PDBGG("Isochrnous frame: length %u, #%u i", len, i)
 
-               /* NOTE: It is probably correct to assume that SOF and EOF
+               /*
+                  NOTE: It is probably correct to assume that SOF and EOF
                         headers do not occur between two consecutive packets,
                         but who knows..Whatever is the truth, this assumption
-                        doesn't introduce bugs. */
+                        doesn't introduce bugs.
+               */
 
 redo:
-               sof = sn9c102_find_sof_header(pos, len);
+               sof = sn9c102_find_sof_header(cam, pos, len);
                if (!sof) {
-                       eof = sn9c102_find_eof_header(pos, len);
+                       eof = sn9c102_find_eof_header(cam, pos, len);
                        if ((*f)->state == F_GRABBING) {
 end_of_frame:
                                img = len;
@@ -533,11 +570,10 @@ end_of_frame:
                                if (eof)
                                        img = (eof > pos) ? eof - pos - 1 : 0;
 
-                               if ((*f)->buf.bytesused+img>(*f)->buf.length) {
+                               if ((*f)->buf.bytesused+img > imagesize) {
                                        u32 b = (*f)->buf.bytesused + img -
-                                               (*f)->buf.length;
-                                       img = (*f)->buf.length - 
-                                             (*f)->buf.bytesused;
+                                               imagesize;
+                                       img = imagesize - (*f)->buf.bytesused;
                                        DBG(3, "Expected EOF not found: "
                                               "video frame cut")
                                        if (eof)
@@ -553,7 +589,9 @@ end_of_frame:
 
                                (*f)->buf.bytesused += img;
 
-                               if ((*f)->buf.bytesused == (*f)->buf.length) {
+                               if ((*f)->buf.bytesused == imagesize ||
+                                   (cam->sensor->pix_format.pixelformat ==
+                                               V4L2_PIX_FMT_SN9C10X && eof)) {
                                        u32 b = (*f)->buf.bytesused;
                                        (*f)->state = F_DONE;
                                        (*f)->buf.sequence= ++cam->frame_count;
@@ -570,6 +608,9 @@ end_of_frame:
                                                (*f) = NULL;
                                        spin_unlock_irqrestore(&cam->queue_lock
                                                               , lock_flags);
+                                       memcpy(cam->sysfs.frame_header,
+                                              cam->sof_header,
+                                              sizeof(sn9c102_sof_header_t));
                                        DBG(3, "Video frame captured: "
                                               "%lu bytes", (unsigned long)(b))
 
@@ -606,14 +647,20 @@ start_of_frame:
                                goto redo;
 
                } else if ((*f)->state == F_GRABBING) {
-                       eof = sn9c102_find_eof_header(pos, len);
+                       eof = sn9c102_find_eof_header(cam, pos, len);
                        if (eof && eof < sof)
                                goto end_of_frame; /* (1) */
                        else {
-                               DBG(3, "SOF before expected EOF after %lu "
-                                      "bytes of image data", 
-                                   (unsigned long)((*f)->buf.bytesused))
-                               goto start_of_frame;
+                               if (cam->sensor->pix_format.pixelformat ==
+                                   V4L2_PIX_FMT_SN9C10X) {
+                                       eof = sof-sizeof(sn9c102_sof_header_t);
+                                       goto end_of_frame;
+                               } else {
+                                       DBG(3, "SOF before expected EOF after "
+                                              "%lu bytes of image data", 
+                                         (unsigned long)((*f)->buf.bytesused))
+                                       goto start_of_frame;
+                               }
                        }
                }
        }
@@ -635,7 +682,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam)
        struct usb_device *udev = cam->usbdev;
        struct urb* urb;
        const unsigned int wMaxPacketSize[] = {0, 128, 256, 384, 512,
-                                               680, 800, 900, 1023};
+                                              680, 800, 900, 1023};
        const unsigned int psz = wMaxPacketSize[SN9C102_ALTERNATE_SETTING];
        s8 i, j;
        int err = 0;
@@ -737,6 +784,28 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
        return err;
 }
 
+
+static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
+{
+       int err = 0;
+
+       cam->stream = STREAM_INTERRUPT;
+       err = wait_event_timeout(cam->wait_stream,
+                                (cam->stream == STREAM_OFF) ||
+                                (cam->state & DEV_DISCONNECTED),
+                                SN9C102_URB_TIMEOUT);
+       if (cam->state & DEV_DISCONNECTED)
+               return -ENODEV;
+       else if (err) {
+               cam->state |= DEV_MISCONFIGURED;
+               DBG(1, "The camera is misconfigured. To use it, close and "
+                      "open /dev/video%d again.", cam->v4ldev->minor)
+               return err;
+       }
+
+       return 0;
+}
+
 /*****************************************************************************/
 
 static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
@@ -764,9 +833,11 @@ static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count)
        return (u8)val;
 }
 
-/* NOTE 1: being inside one of the following methods implies that the v4l
+/*
+   NOTE 1: being inside one of the following methods implies that the v4l
            device exists for sure (see kobjects and reference counters)
-   NOTE 2: buffers are PAGE_SIZE long */
+   NOTE 2: buffers are PAGE_SIZE long
+*/
 
 static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf)
 {
@@ -913,7 +984,7 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf)
        up(&sn9c102_sysfs_lock);
 
        return count;
-} 
+}
 
 
 static ssize_t 
@@ -964,6 +1035,11 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf)
                return -ENODEV;
        }
 
+       if (!(cam->sensor->sysfs_ops & SN9C102_I2C_READ)) {
+               up(&sn9c102_sysfs_lock);
+               return -ENOSYS;
+       }
+
        if ((val = sn9c102_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
                up(&sn9c102_sysfs_lock);
                return -EIO;
@@ -996,6 +1072,11 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
                return -ENODEV;
        }
 
+       if (!(cam->sensor->sysfs_ops & SN9C102_I2C_WRITE)) {
+               up(&sn9c102_sysfs_lock);
+               return -ENOSYS;
+       }
+
        value = sn9c102_strtou8(buf, len, &count);
        if (!count) {
                up(&sn9c102_sysfs_lock);
@@ -1019,17 +1100,63 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len)
 
 
 static ssize_t
-sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len)
+sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
 {
+       struct sn9c102_device* cam;
+       enum sn9c102_bridge bridge;
        ssize_t res = 0;
        u8 value;
        ssize_t count;
 
+       if (down_interruptible(&sn9c102_sysfs_lock))
+               return -ERESTARTSYS;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam) {
+               up(&sn9c102_sysfs_lock);
+               return -ENODEV;
+       }
+
+       bridge = cam->bridge;
+
+       up(&sn9c102_sysfs_lock);
+
        value = sn9c102_strtou8(buf, len, &count);
        if (!count)
                return -EINVAL;
 
-       if ((res = sn9c102_store_reg(cd, "0x10", 4)) >= 0)
+       switch (bridge) {
+       case BRIDGE_SN9C101:
+       case BRIDGE_SN9C102:
+               if (value > 0x0f)
+                       return -EINVAL;
+               if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+                       res = sn9c102_store_val(cd, buf, len);
+               break;
+       case BRIDGE_SN9C103:
+               if (value > 0x7f)
+                       return -EINVAL;
+               if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0)
+                       res = sn9c102_store_val(cd, buf, len);
+               break;
+       }
+
+       return res;
+}
+
+
+static ssize_t
+sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len)
+{
+       ssize_t res = 0;
+       u8 value;
+       ssize_t count;
+
+       value = sn9c102_strtou8(buf, len, &count);
+       if (!count || value > 0x7f)
+               return -EINVAL;
+
+       if ((res = sn9c102_store_reg(cd, "0x06", 4)) >= 0)
                res = sn9c102_store_val(cd, buf, len);
 
        return res;
@@ -1037,23 +1164,41 @@ sn9c102_store_redblue(struct class_device* cd, const char* buf, size_t len)
 
 
 static ssize_t
-sn9c102_store_green(struct class_device* cd, const char* buf, size_t len)
+sn9c102_store_red(struct class_device* cd, const char* buf, size_t len)
 {
        ssize_t res = 0;
        u8 value;
        ssize_t count;
 
        value = sn9c102_strtou8(buf, len, &count);
-       if (!count || value > 0x0f)
+       if (!count || value > 0x7f)
                return -EINVAL;
 
-       if ((res = sn9c102_store_reg(cd, "0x11", 4)) >= 0)
+       if ((res = sn9c102_store_reg(cd, "0x05", 4)) >= 0)
                res = sn9c102_store_val(cd, buf, len);
 
        return res;
 }
 
 
+static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf)
+{
+       struct sn9c102_device* cam;
+       ssize_t count;
+
+       cam = video_get_drvdata(to_video_device(cd));
+       if (!cam)
+               return -ENODEV;
+
+       count = sizeof(cam->sysfs.frame_header);
+       memcpy(buf, cam->sysfs.frame_header, count);
+
+       DBG(3, "Frame header, read bytes: %zd", count)
+
+       return count;
+} 
+
+
 static CLASS_DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
                          sn9c102_show_reg, sn9c102_store_reg);
 static CLASS_DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
@@ -1062,8 +1207,11 @@ static CLASS_DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
                          sn9c102_show_i2c_reg, sn9c102_store_i2c_reg);
 static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
                          sn9c102_show_i2c_val, sn9c102_store_i2c_val);
-static CLASS_DEVICE_ATTR(redblue, S_IWUGO, NULL, sn9c102_store_redblue);
 static CLASS_DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green);
+static CLASS_DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue);
+static CLASS_DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red);
+static CLASS_DEVICE_ATTR(frame_header, S_IRUGO,
+                         sn9c102_show_frame_header, NULL);
 
 
 static void sn9c102_create_sysfs(struct sn9c102_device* cam)
@@ -1072,9 +1220,14 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
 
        video_device_create_file(v4ldev, &class_device_attr_reg);
        video_device_create_file(v4ldev, &class_device_attr_val);
-       video_device_create_file(v4ldev, &class_device_attr_redblue);
-       video_device_create_file(v4ldev, &class_device_attr_green);
-       if (cam->sensor->slave_write_id && cam->sensor->slave_read_id) {
+       video_device_create_file(v4ldev, &class_device_attr_frame_header);
+       if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102)
+               video_device_create_file(v4ldev, &class_device_attr_green);
+       else if (cam->bridge == BRIDGE_SN9C103) {
+               video_device_create_file(v4ldev, &class_device_attr_blue);
+               video_device_create_file(v4ldev, &class_device_attr_red);
+       }
+       if (cam->sensor->sysfs_ops) {
                video_device_create_file(v4ldev, &class_device_attr_i2c_reg);
                video_device_create_file(v4ldev, &class_device_attr_i2c_val);
        }
@@ -1082,6 +1235,35 @@ static void sn9c102_create_sysfs(struct sn9c102_device* cam)
 
 /*****************************************************************************/
 
+static int
+sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix)
+{
+       int err = 0;
+
+       if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+               err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18);
+       else
+               err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18);
+
+       return err ? -EIO : 0;
+}
+
+
+static int
+sn9c102_set_compression(struct sn9c102_device* cam,
+                        struct v4l2_jpegcompression* compression)
+{
+       int err = 0;
+
+       if (compression->quality == 0)
+               err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17);
+       else if (compression->quality == 1)
+               err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17);
+
+       return err ? -EIO : 0;
+}
+
+
 static int sn9c102_set_scale(struct sn9c102_device* cam, u8 scale)
 {
        u8 r = 0;
@@ -1111,31 +1293,18 @@ static int sn9c102_set_crop(struct sn9c102_device* cam, struct v4l2_rect* rect)
        u8 h_start = (u8)(rect->left - s->cropcap.bounds.left),
           v_start = (u8)(rect->top - s->cropcap.bounds.top),
           h_size = (u8)(rect->width / 16),
-          v_size = (u8)(rect->height / 16),
-          ae_strx = 0x00,
-          ae_stry = 0x00,
-          ae_endx = h_size / 2,
-          ae_endy = v_size / 2;
+          v_size = (u8)(rect->height / 16);
        int err = 0;
 
-       /* These are a sort of stroboscopic signal for some sensors */
-       err += sn9c102_write_reg(cam, h_size, 0x1a);
-       err += sn9c102_write_reg(cam, v_size, 0x1b);
-
        err += sn9c102_write_reg(cam, h_start, 0x12);
        err += sn9c102_write_reg(cam, v_start, 0x13);
        err += sn9c102_write_reg(cam, h_size, 0x15);
        err += sn9c102_write_reg(cam, v_size, 0x16);
-       err += sn9c102_write_reg(cam, ae_strx, 0x1c);
-       err += sn9c102_write_reg(cam, ae_stry, 0x1d);
-       err += sn9c102_write_reg(cam, ae_endx, 0x1e);
-       err += sn9c102_write_reg(cam, ae_endy, 0x1f);
        if (err)
                return -EIO;
 
        PDBGG("h_start, v_start, h_size, v_size, ho_size, vo_size "
-             "%u %u %u %u %u %u", h_start, v_start, h_size, v_size, ho_size,
-             vo_size)
+             "%u %u %u %u", h_start, v_start, h_size, v_size)
 
        return 0;
 }
@@ -1172,6 +1341,22 @@ static int sn9c102_init(struct sn9c102_device* cam)
                }
        }
 
+       if (!(cam->state & DEV_INITIALIZED))
+               cam->compression.quality =  cam->reg[0x17] & 0x01 ? 0 : 1;
+       else
+               err += sn9c102_set_compression(cam, &cam->compression);
+       err += sn9c102_set_pix_format(cam, &s->pix_format);
+       if (s->set_pix_format)
+               err += s->set_pix_format(cam, &s->pix_format);
+       if (err)
+               return err;
+
+       if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X)
+               DBG(3, "Compressed video format is active, quality %d", 
+                   cam->compression.quality)
+       else
+               DBG(3, "Uncompressed video format is active")
+
        if (s->set_crop)
                if ((err = s->set_crop(cam, rect))) {
                        DBG(3, "set_crop() failed")
@@ -1187,9 +1372,12 @@ static int sn9c102_init(struct sn9c102_device* cam)
                                ctrl.value = qctrl[i].default_value;
                                err = s->set_ctrl(cam, &ctrl);
                                if (err) {
-                                       DBG(3, "Set control failed")
+                                       DBG(3, "Set %s control failed",
+                                           s->qctrl[i].name)
                                        return err;
                                }
+                               DBG(3, "Image sensor supports '%s' control",
+                                   s->qctrl[i].name)
                        }
        }
 
@@ -1198,6 +1386,7 @@ static int sn9c102_init(struct sn9c102_device* cam)
                spin_lock_init(&cam->queue_lock);
                init_waitqueue_head(&cam->wait_frame);
                init_waitqueue_head(&cam->wait_stream);
+               cam->nreadbuffers = 2;
                memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
                memcpy(&(s->_rect), &(s->cropcap.defrect), 
                       sizeof(struct v4l2_rect));
@@ -1229,7 +1418,10 @@ static int sn9c102_open(struct inode* inode, struct file* filp)
        struct sn9c102_device* cam;
        int err = 0;
 
-       /* This the only safe way to prevent race conditions with disconnect */
+       /*
+          This is the only safe way to prevent race conditions with
+          disconnect
+       */
        if (!down_read_trylock(&sn9c102_disconnect))
                return -ERESTARTSYS;
 
@@ -1352,7 +1544,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
        }
 
        if (cam->io == IO_NONE) {
-               if (!sn9c102_request_buffers(cam, 2)) {
+               if (!sn9c102_request_buffers(cam,cam->nreadbuffers, IO_READ)) {
                        DBG(1, "read() failed, not enough memory")
                        up(&cam->fileop_sem);
                        return -ENOMEM;
@@ -1375,7 +1567,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
                err = wait_event_interruptible
                      ( cam->wait_frame, 
                        (!list_empty(&cam->outqueue)) ||
-                       (cam->state & DEV_DISCONNECTED) );
+                       (cam->state & DEV_DISCONNECTED) ||
+                       (cam->state & DEV_MISCONFIGURED) );
                if (err) {
                        up(&cam->fileop_sem);
                        return err;
@@ -1384,6 +1577,10 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
                        up(&cam->fileop_sem);
                        return -ENODEV;
                }
+               if (cam->state & DEV_MISCONFIGURED) {
+                       up(&cam->fileop_sem);
+                       return -EIO;
+               }
        }
 
        f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame);
@@ -1396,8 +1593,8 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
 
        sn9c102_queue_unusedframes(cam);
 
-       if (count > f->buf.length)
-               count = f->buf.length;
+       if (count > f->buf.bytesused)
+               count = f->buf.bytesused;
 
        if (copy_to_user(buf, f->bufmem, count)) {
                up(&cam->fileop_sem);
@@ -1432,7 +1629,8 @@ static unsigned int sn9c102_poll(struct file *filp, poll_table *wait)
        }
 
        if (cam->io == IO_NONE) {
-               if (!sn9c102_request_buffers(cam, 2)) {
+               if (!sn9c102_request_buffers(cam, cam->nreadbuffers,
+                                            IO_READ)) {
                        DBG(1, "poll() failed, not enough memory")
                        goto error;
                }
@@ -1518,11 +1716,15 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
                return -EINVAL;
        }
 
+       /* VM_IO is eventually going to replace PageReserved altogether */
+       vma->vm_flags |= VM_IO;
+       vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+
        pos = (unsigned long)cam->frame[i].bufmem;
        while (size > 0) { /* size is page-aligned */
-               page = kvirt_to_pa(pos);
-               if (remap_page_range(vma, start, page, PAGE_SIZE, 
-                                    vma->vm_page_prot)) {
+               page = vmalloc_to_pfn((void *)pos);
+               if (remap_pfn_range(vma, start, page, PAGE_SIZE,
+                                   vma->vm_page_prot)) {
                        up(&cam->fileop_sem);
                        return -EAGAIN;
                }
@@ -1532,8 +1734,6 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
        }
 
        vma->vm_ops = &sn9c102_vm_ops;
-       vma->vm_flags &= ~VM_IO; /* not I/O memory */
-       vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
        vma->vm_private_data = &cam->frame[i];
 
        sn9c102_vm_open(vma);
@@ -1544,7 +1744,7 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma)
 }
 
 
-static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
+static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
                               unsigned int cmd, void __user * arg)
 {
        struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));
@@ -1558,11 +1758,14 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                        .version = SN9C102_MODULE_VERSION_CODE,
                        .capabilities = V4L2_CAP_VIDEO_CAPTURE | 
                                        V4L2_CAP_READWRITE |
-                                       V4L2_CAP_STREAMING,
+                                       V4L2_CAP_STREAMING,
                };
 
                strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
-               strlcpy(cap.bus_info, cam->dev.bus_id, sizeof(cap.bus_info));
+               if (usb_make_path(cam->usbdev, cap.bus_info,
+                   sizeof(cap.bus_info)) < 0)
+                       strlcpy(cap.bus_info, cam->dev.bus_id,
+                               sizeof(cap.bus_info));
 
                if (copy_to_user(arg, &cap, sizeof(cap)))
                        return -EFAULT;
@@ -1644,6 +1847,7 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                return err;
        }
 
+       case VIDIOC_S_CTRL_OLD:
        case VIDIOC_S_CTRL:
        {
                struct sn9c102_sensor* s = cam->sensor;
@@ -1657,16 +1861,24 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
                        return -EFAULT;
 
-               if ((err = s->set_ctrl(cam, &ctrl)))
-                       return err;
-
                n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
                for (i = 0; i < n; i++)
                        if (ctrl.id == s->qctrl[i].id) {
-                               s->_qctrl[i].default_value = ctrl.value;
+                               if (ctrl.value < s->qctrl[i].minimum ||
+                                   ctrl.value > s->qctrl[i].maximum)
+                                       return -ERANGE;
+                               ctrl.value -= ctrl.value % s->qctrl[i].step;
                                break;
                        }
 
+               if ((err = s->set_ctrl(cam, &ctrl)))
+                       return err;
+
+               s->_qctrl[i].default_value = ctrl.value;
+
+               PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
+                     (unsigned long)ctrl.id, (unsigned long)ctrl.value)
+
                return 0;
        }
 
@@ -1720,12 +1932,19 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                        return -EINVAL;
 
-               for (i = 0; i < cam->nbuffers; i++)
-                       if (cam->frame[i].vma_use_count) {
-                               DBG(3, "VIDIOC_S_CROP failed. "
-                                      "Unmap the buffers first.")
-                               return -EINVAL;
-                       }
+               if (cam->module_param.force_munmap)
+                       for (i = 0; i < cam->nbuffers; i++)
+                               if (cam->frame[i].vma_use_count) {
+                                       DBG(3, "VIDIOC_S_CROP failed. "
+                                              "Unmap the buffers first.")
+                                       return -EINVAL;
+                               }
+
+               /* Preserve R,G or B origin */
+               rect->left = (s->_rect.left & 1L) ?
+                            rect->left | 1L : rect->left & ~1L;
+               rect->top = (s->_rect.top & 1L) ?
+                           rect->top | 1L : rect->top & ~1L;
 
                if (rect->width < 16)
                        rect->width = 16;
@@ -1747,37 +1966,27 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                rect->width &= ~15L;
                rect->height &= ~15L;
 
-               { /* calculate the scaling factor */
+               if (SN9C102_PRESERVE_IMGSCALE) {
+                       /* Calculate the actual scaling factor */
                        u32 a, b;
                        a = rect->width * rect->height;
                        b = pix_format->width * pix_format->height;
-                       scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 :
-                                   ((a / b) > 4 ? 4 : (a / b)))) : 1;
-               }
+                       scale = b ? (u8)((a / b) < 4 ? 1 :
+                                       ((a / b) < 16 ? 2 : 4)) : 1;
+               } else
+                       scale = 1;
 
-               if (cam->stream == STREAM_ON) {
-                       cam->stream = STREAM_INTERRUPT;
-                       err = wait_event_interruptible
-                             ( cam->wait_stream, 
-                               (cam->stream == STREAM_OFF) ||
-                               (cam->state & DEV_DISCONNECTED) );
-                       if (err) {
-                               cam->state |= DEV_MISCONFIGURED;
-                               DBG(1, "The camera is misconfigured. To use "
-                                      "it, close and open /dev/video%d "
-                                      "again.", cam->v4ldev->minor)
+               if (cam->stream == STREAM_ON)
+                       if ((err = sn9c102_stream_interrupt(cam)))
                                return err;
-                       }
-                       if (cam->state & DEV_DISCONNECTED)
-                               return -ENODEV;
-               }
 
                if (copy_to_user(arg, &crop, sizeof(crop))) {
                        cam->stream = stream;
                        return -EFAULT;
                }
 
-               sn9c102_release_buffers(cam);
+               if (cam->module_param.force_munmap || cam->io == IO_READ)
+                       sn9c102_release_buffers(cam);
 
                err = sn9c102_set_crop(cam, rect);
                if (s->set_crop)
@@ -1789,14 +1998,16 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                        DBG(1, "VIDIOC_S_CROP failed because of hardware "
                               "problems. To use the camera, close and open "
                               "/dev/video%d again.", cam->v4ldev->minor)
-                       return err;
+                       return -EIO;
                }
 
                s->pix_format.width = rect->width/scale;
                s->pix_format.height = rect->height/scale;
                memcpy(&(s->_rect), rect, sizeof(*rect));
 
-               if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+               if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
+                   nbuffers != sn9c102_request_buffers(cam, nbuffers,
+                                                       cam->io)) {
                        cam->state |= DEV_MISCONFIGURED;
                        DBG(1, "VIDIOC_S_CROP failed because of not enough "
                               "memory. To use the camera, close and open "
@@ -1811,20 +2022,23 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
 
        case VIDIOC_ENUM_FMT:
        {
-               struct sn9c102_sensor* s = cam->sensor;
                struct v4l2_fmtdesc fmtd;
 
                if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
                        return -EFAULT;
 
-               if (fmtd.index != 0)
+               if (fmtd.index == 0) {
+                       strcpy(fmtd.description, "bayer rgb");
+                       fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
+               } else if (fmtd.index == 1) {
+                       strcpy(fmtd.description, "compressed");
+                       fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
+                       fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
+               } else
                        return -EINVAL;
 
-               memset(&fmtd, 0, sizeof(fmtd));
-
                fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-               strcpy(fmtd.description, "bayer rgb");
-               fmtd.pixelformat = s->pix_format.pixelformat;
+               memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
 
                if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
                        return -EFAULT;
@@ -1843,8 +2057,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                        return -EINVAL;
 
-               pfmt->bytesperline = (pfmt->width * pfmt->priv) / 8;
-               pfmt->sizeimage = pfmt->height * pfmt->bytesperline;
+               pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
+                                    ? 0 : (pfmt->width * pfmt->priv) / 8;
+               pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
                pfmt->field = V4L2_FIELD_NONE;
                memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
 
@@ -1879,12 +2094,12 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
 
                memcpy(&rect, &(s->_rect), sizeof(rect));
 
-               { /* calculate the scaling factor */
+               { /* calculate the actual scaling factor */
                        u32 a, b;
                        a = rect.width * rect.height;
                        b = pix->width * pix->height;
-                       scale = b ? (u8)((a / b) <= 1 ? 1 : ((a / b) == 3 ? 2 :
-                                   ((a / b) > 4 ? 4 : (a / b)))) : 1;
+                       scale = b ? (u8)((a / b) < 4 ? 1 :
+                                       ((a / b) < 16 ? 2 : 4)) : 1;
                }
 
                rect.width = scale * pix->width;
@@ -1895,58 +2110,64 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                if (rect.height < 16)
                        rect.height = 16;
                if (rect.width > bounds->left + bounds->width - rect.left)
-                       rect.width = bounds->left+bounds->width - rect.left;
+                       rect.width = bounds->left + bounds->width - rect.left;
                if (rect.height > bounds->top + bounds->height - rect.top)
                        rect.height = bounds->top + bounds->height - rect.top;
 
                rect.width &= ~15L;
                rect.height &= ~15L;
 
+               { /* adjust the scaling factor */
+                       u32 a, b;
+                       a = rect.width * rect.height;
+                       b = pix->width * pix->height;
+                       scale = b ? (u8)((a / b) < 4 ? 1 :
+                                       ((a / b) < 16 ? 2 : 4)) : 1;
+               }
+
                pix->width = rect.width / scale;
                pix->height = rect.height / scale;
 
-               pix->pixelformat = pfmt->pixelformat;
+               if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
+                   pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
+                       pix->pixelformat = pfmt->pixelformat;
                pix->priv = pfmt->priv; /* bpp */
                pix->colorspace = pfmt->colorspace;
-               pix->bytesperline = (pix->width * pix->priv) / 8;
-               pix->sizeimage = pix->height * pix->bytesperline;
+               pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
+                                   ? 0 : (pix->width * pix->priv) / 8;
+               pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
                pix->field = V4L2_FIELD_NONE;
 
-               if (cmd == VIDIOC_TRY_FMT)
+               if (cmd == VIDIOC_TRY_FMT) {
+                       if (copy_to_user(arg, &format, sizeof(format)))
+                               return -EFAULT;
                        return 0;
+               }
 
-               for (i = 0; i < cam->nbuffers; i++)
-                       if (cam->frame[i].vma_use_count) {
-                               DBG(3, "VIDIOC_S_FMT failed. "
-                                      "Unmap the buffers first.")
-                               return -EINVAL;
-                       }
+               if (cam->module_param.force_munmap)
+                       for (i = 0; i < cam->nbuffers; i++)
+                               if (cam->frame[i].vma_use_count) {
+                                       DBG(3, "VIDIOC_S_FMT failed. "
+                                              "Unmap the buffers first.")
+                                       return -EINVAL;
+                               }
 
-               if (cam->stream == STREAM_ON) {
-                       cam->stream = STREAM_INTERRUPT;
-                       err = wait_event_interruptible
-                             ( cam->wait_stream, 
-                               (cam->stream == STREAM_OFF) ||
-                               (cam->state & DEV_DISCONNECTED) );
-                       if (err) {
-                               cam->state |= DEV_MISCONFIGURED;
-                               DBG(1, "The camera is misconfigured. To use "
-                                      "it, close and open /dev/video%d "
-                                      "again.", cam->v4ldev->minor)
+               if (cam->stream == STREAM_ON)
+                       if ((err = sn9c102_stream_interrupt(cam)))
                                return err;
-                       }
-                       if (cam->state & DEV_DISCONNECTED)
-                               return -ENODEV;
-               }
 
                if (copy_to_user(arg, &format, sizeof(format))) {
                        cam->stream = stream;
                        return -EFAULT;
                }
 
-               sn9c102_release_buffers(cam);
+               if (cam->module_param.force_munmap  || cam->io == IO_READ)
+                       sn9c102_release_buffers(cam);
 
-               err = sn9c102_set_crop(cam, &rect);
+               err += sn9c102_set_pix_format(cam, pix);
+               err += sn9c102_set_crop(cam, &rect);
+               if (s->set_pix_format)
+                       err += s->set_pix_format(cam, pix);
                if (s->set_crop)
                        err += s->set_crop(cam, &rect);
                err += sn9c102_set_scale(cam, scale);
@@ -1956,13 +2177,15 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                        DBG(1, "VIDIOC_S_FMT failed because of hardware "
                               "problems. To use the camera, close and open "
                               "/dev/video%d again.", cam->v4ldev->minor)
-                       return err;
+                       return -EIO;
                }
 
                memcpy(pfmt, pix, sizeof(*pix));
                memcpy(&(s->_rect), &rect, sizeof(rect));
 
-               if (nbuffers != sn9c102_request_buffers(cam, nbuffers)) {
+               if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
+                   nbuffers != sn9c102_request_buffers(cam, nbuffers,
+                                                       cam->io)) {
                        cam->state |= DEV_MISCONFIGURED;
                        DBG(1, "VIDIOC_S_FMT failed because of not enough "
                               "memory. To use the camera, close and open "
@@ -1975,6 +2198,47 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                return 0;
        }
 
+       case VIDIOC_G_JPEGCOMP:
+       {
+               if (copy_to_user(arg, &cam->compression,
+                                sizeof(cam->compression)))
+                       return -EFAULT;
+
+               return 0;
+       }
+
+       case VIDIOC_S_JPEGCOMP:
+       {
+               struct v4l2_jpegcompression jc;
+               const enum sn9c102_stream_state stream = cam->stream;
+               int err = 0;
+
+               if (copy_from_user(&jc, arg, sizeof(jc)))
+                       return -EFAULT;
+
+               if (jc.quality != 0 && jc.quality != 1)
+                       return -EINVAL;
+
+               if (cam->stream == STREAM_ON)
+                       if ((err = sn9c102_stream_interrupt(cam)))
+                               return err;
+
+               err += sn9c102_set_compression(cam, &jc);
+               if (err) { /* atomic, no rollback in ioctl() */
+                       cam->state |= DEV_MISCONFIGURED;
+                       DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
+                              "problems. To use the camera, close and open "
+                              "/dev/video%d again.", cam->v4ldev->minor)
+                       return -EIO;
+               }
+
+               cam->compression.quality = jc.quality;
+
+               cam->stream = stream;
+
+               return 0;
+       }
+
        case VIDIOC_REQBUFS:
        {
                struct v4l2_requestbuffers rb;
@@ -2001,28 +2265,16 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                                return -EINVAL;
                        }
 
-               if (cam->stream == STREAM_ON) {
-                       cam->stream = STREAM_INTERRUPT;
-                       err = wait_event_interruptible
-                             ( cam->wait_stream, 
-                               (cam->stream == STREAM_OFF) ||
-                               (cam->state & DEV_DISCONNECTED) );
-                       if (err) {
-                               cam->state |= DEV_MISCONFIGURED;
-                               DBG(1, "The camera is misconfigured. To use "
-                                      "it, close and open /dev/video%d "
-                                      "again.", cam->v4ldev->minor)
+               if (cam->stream == STREAM_ON)
+                       if ((err = sn9c102_stream_interrupt(cam)))
                                return err;
-                       }
-                       if (cam->state & DEV_DISCONNECTED)
-                               return -ENODEV;
-               }
 
                sn9c102_empty_framequeues(cam);
 
                sn9c102_release_buffers(cam);
                if (rb.count)
-                       rb.count = sn9c102_request_buffers(cam, rb.count);
+                       rb.count = sn9c102_request_buffers(cam, rb.count,
+                                                          IO_MMAP);
 
                if (copy_to_user(arg, &rb, sizeof(rb))) {
                        sn9c102_release_buffers(cam);
@@ -2109,17 +2361,20 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                        err = wait_event_interruptible
                              ( cam->wait_frame, 
                                (!list_empty(&cam->outqueue)) ||
-                               (cam->state & DEV_DISCONNECTED) );
+                               (cam->state & DEV_DISCONNECTED) ||
+                               (cam->state & DEV_MISCONFIGURED) );
                        if (err)
                                return err;
                        if (cam->state & DEV_DISCONNECTED)
                                return -ENODEV;
+                       if (cam->state & DEV_MISCONFIGURED)
+                               return -EIO;
                }
 
                spin_lock_irqsave(&cam->queue_lock, lock_flags);
                f = list_entry(cam->outqueue.next, struct sn9c102_frame_t,
                               frame);
-               list_del(&cam->outqueue);
+               list_del(cam->outqueue.next);
                spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
 
                f->state = F_UNUSED;
@@ -2166,22 +2421,9 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
                        return -EINVAL;
 
-               if (cam->stream == STREAM_ON) {
-                       cam->stream = STREAM_INTERRUPT;
-                       err = wait_event_interruptible
-                             ( cam->wait_stream, 
-                               (cam->stream == STREAM_OFF) ||
-                               (cam->state & DEV_DISCONNECTED) );
-                       if (err) {
-                               cam->state |= DEV_MISCONFIGURED;
-                               DBG(1, "The camera is misconfigured. To use "
-                                      "it, close and open /dev/video%d "
-                                      "again.", cam->v4ldev->minor)
+               if (cam->stream == STREAM_ON)
+                       if ((err = sn9c102_stream_interrupt(cam)))
                                return err;
-                       }
-                       if (cam->state & DEV_DISCONNECTED)
-                               return -ENODEV;
-               }
 
                sn9c102_empty_framequeues(cam);
 
@@ -2190,13 +2432,57 @@ static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp,
                return 0;
        }
 
+       case VIDIOC_G_PARM:
+       {
+               struct v4l2_streamparm sp;
+
+               if (copy_from_user(&sp, arg, sizeof(sp)))
+                       return -EFAULT;
+
+               if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+
+               sp.parm.capture.extendedmode = 0;
+               sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+               if (copy_to_user(arg, &sp, sizeof(sp)))
+                       return -EFAULT;
+
+               return 0;
+       }
+
+       case VIDIOC_S_PARM_OLD:
+       case VIDIOC_S_PARM:
+       {
+               struct v4l2_streamparm sp;
+
+               if (copy_from_user(&sp, arg, sizeof(sp)))
+                       return -EFAULT;
+
+               if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+
+               sp.parm.capture.extendedmode = 0;
+
+               if (sp.parm.capture.readbuffers == 0)
+                       sp.parm.capture.readbuffers = cam->nreadbuffers;
+
+               if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
+                       sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;
+
+               if (copy_to_user(arg, &sp, sizeof(sp)))
+                       return -EFAULT;
+
+               cam->nreadbuffers = sp.parm.capture.readbuffers;
+
+               return 0;
+       }
+
        case VIDIOC_G_STD:
        case VIDIOC_S_STD:
        case VIDIOC_QUERYSTD:
        case VIDIOC_ENUMSTD:
        case VIDIOC_QUERYMENU:
-       case VIDIOC_G_PARM:
-       case VIDIOC_S_PARM:
                return -EINVAL;
 
        default:
@@ -2227,7 +2513,7 @@ static int sn9c102_ioctl(struct inode* inode, struct file* filp,
                return -EIO;
        }
 
-       err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg);
+       err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg);
 
        up(&cam->fileop_sem);
 
@@ -2260,8 +2546,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
 
        n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]);
        for (i = 0; i < n-1; i++)
-               if (udev->descriptor.idVendor==sn9c102_id_table[i].idVendor &&
-                   udev->descriptor.idProduct==sn9c102_id_table[i].idProduct)
+               if (le16_to_cpu(udev->descriptor.idVendor) == 
+                   sn9c102_id_table[i].idVendor &&
+                   le16_to_cpu(udev->descriptor.idProduct) ==
+                   sn9c102_id_table[i].idProduct)
                        break;
        if (i == n-1)
                return -ENODEV;
@@ -2291,16 +2579,28 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
 
        r = sn9c102_read_reg(cam, 0x00);
        if (r < 0 || r != 0x10) {
-               DBG(1, "Sorry, this is not a SN9C10[12] based camera "
+               DBG(1, "Sorry, this is not a SN9C10x based camera "
                       "(vid/pid 0x%04X/0x%04X)",
                    sn9c102_id_table[i].idVendor,sn9c102_id_table[i].idProduct)
                err = -ENODEV;
                goto fail;
        }
 
-       DBG(2, "SN9C10[12] PC Camera Controller detected "
-              "(vid/pid 0x%04X/0x%04X)",
-           sn9c102_id_table[i].idVendor, sn9c102_id_table[i].idProduct)
+       cam->bridge = (sn9c102_id_table[i].idProduct & 0xffc0) == 0x6080 ?
+                     BRIDGE_SN9C103 : BRIDGE_SN9C102;
+       switch (cam->bridge) {
+       case BRIDGE_SN9C101:
+       case BRIDGE_SN9C102:
+               DBG(2, "SN9C10[12] PC Camera Controller detected "
+                      "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+                   sn9c102_id_table[i].idProduct)
+               break;
+       case BRIDGE_SN9C103:
+               DBG(2, "SN9C103 PC Camera Controller detected "
+                      "(vid/pid 0x%04X/0x%04X)", sn9c102_id_table[i].idVendor,
+                   sn9c102_id_table[i].idProduct)
+               break;
+       }
 
        for  (i = 0; sn9c102_sensor_table[i]; i++) {
                err = sn9c102_sensor_table[i](cam);
@@ -2323,7 +2623,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
                cam->state |= DEV_MISCONFIGURED;
        }
 
-       strcpy(cam->v4ldev->name, "SN9C10[12] PC Camera");
+       strcpy(cam->v4ldev->name, "SN9C10x PC Camera");
        cam->v4ldev->owner = THIS_MODULE;
        cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
        cam->v4ldev->hardware = VID_HARDWARE_SN9C102;
@@ -2348,7 +2648,12 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
 
        DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor)
 
+       cam->module_param.force_munmap = force_munmap[dev_nr];
+
+       dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
        sn9c102_create_sysfs(cam);
+       DBG(2, "Optional device control through 'sysfs' interface ready")
 
        usb_set_intfdata(intf, cam);