Merge branch 'core/xen' into x86/xen
[safe/jmp/linux-2.6] / drivers / media / video / gspca / ov519.c
index 08d99c3..4df4eec 100644 (file)
@@ -24,9 +24,6 @@
 
 #include "gspca.h"
 
-#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 1, 7)
-static const char version[] = "2.1.7";
-
 MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("OV519 USB Camera Driver");
 MODULE_LICENSE("GPL");
@@ -43,14 +40,15 @@ struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
        /* Determined by sensor type */
-       short maxwidth;
-       short maxheight;
+       char sif;
 
        unsigned char primary_i2c_slave;        /* I2C write id of sensor */
 
        unsigned char brightness;
        unsigned char contrast;
        unsigned char colors;
+       __u8 hflip;
+       __u8 vflip;
 
        char compress;          /* Should the next frame be compressed? */
        char compress_inited;   /* Are compression params uploaded? */
@@ -65,11 +63,10 @@ struct sd {
 #define SEN_OV6630 2
 #define SEN_OV7610 3
 #define SEN_OV7620 4
-#define SEN_OV7630 5
-#define SEN_OV7640 6
-#define SEN_OV7670 7
-#define SEN_OV76BE 8
-#define SEN_OV8610 9
+#define SEN_OV7640 5
+#define SEN_OV7670 6
+#define SEN_OV76BE 7
+#define SEN_OV8610 8
 
 };
 
@@ -80,9 +77,12 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
 
 static struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
        {
            {
                .id      = V4L2_CID_BRIGHTNESS,
@@ -91,12 +91,12 @@ static struct ctrl sd_ctrls[] = {
                .minimum = 0,
                .maximum = 255,
                .step    = 1,
-               .default_value = 127,
+#define BRIGHTNESS_DEF 127
+               .default_value = BRIGHTNESS_DEF,
            },
            .set = sd_setbrightness,
            .get = sd_getbrightness,
        },
-#define SD_CONTRAST 1
        {
            {
                .id      = V4L2_CID_CONTRAST,
@@ -105,31 +105,63 @@ static struct ctrl sd_ctrls[] = {
                .minimum = 0,
                .maximum = 255,
                .step    = 1,
-               .default_value = 127,
+#define CONTRAST_DEF 127
+               .default_value = CONTRAST_DEF,
            },
            .set = sd_setcontrast,
            .get = sd_getcontrast,
        },
-#define SD_COLOR 2
        {
            {
                .id      = V4L2_CID_SATURATION,
                .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
+               .name    = "Color",
                .minimum = 0,
                .maximum = 255,
                .step    = 1,
-               .default_value = 127,
+#define COLOR_DEF 127
+               .default_value = COLOR_DEF,
            },
            .set = sd_setcolors,
            .get = sd_getcolors,
        },
+/* next controls work with ov7670 only */
+#define HFLIP_IDX 3
+       {
+           {
+               .id      = V4L2_CID_HFLIP,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Mirror",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define HFLIP_DEF 0
+               .default_value = HFLIP_DEF,
+           },
+           .set = sd_sethflip,
+           .get = sd_gethflip,
+       },
+#define VFLIP_IDX 4
+       {
+           {
+               .id      = V4L2_CID_VFLIP,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Vflip",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define VFLIP_DEF 0
+               .default_value = VFLIP_DEF,
+           },
+           .set = sd_setvflip,
+           .get = sd_getvflip,
+       },
 };
 
 static struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
-               .sizeimage = 320 * 240 * 3 / 8 + 589,
+               .sizeimage = 320 * 240 * 3 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 1},
        {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -141,12 +173,12 @@ static struct v4l2_pix_format vga_mode[] = {
 static struct v4l2_pix_format sif_mode[] = {
        {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 176,
-               .sizeimage = 176 * 144 * 3 / 8 + 589,
+               .sizeimage = 176 * 144 * 3 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 1},
        {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 352,
-               .sizeimage = 352 * 288 * 3 / 8 + 589,
+               .sizeimage = 352 * 288 * 3 / 8 + 590,
                .colorspace = V4L2_COLORSPACE_JPEG,
                .priv = 0},
 };
@@ -228,6 +260,7 @@ static struct v4l2_pix_format sif_mode[] = {
 #define OV7670_REG_VSTART      0x19    /* Vert start high bits */
 #define OV7670_REG_VSTOP       0x1a    /* Vert stop high bits */
 #define OV7670_REG_MVFP        0x1e    /* Mirror / vflip */
+#define   OV7670_MVFP_VFLIP     0x10    /* vertical flip */
 #define   OV7670_MVFP_MIRROR     0x20    /* Mirror image */
 #define OV7670_REG_AEW         0x24    /* AGC upper limit */
 #define OV7670_REG_AEB         0x25    /* AGC lower limit */
@@ -261,14 +294,539 @@ static struct v4l2_pix_format sif_mode[] = {
 #define OV7670_REG_HAECC7      0xaa    /* Hist AEC/AGC control 7 */
 #define OV7670_REG_BD60MAX     0xab    /* 60hz banding step limit */
 
-struct ovsensor_window {
-       short x;
-       short y;
-       short width;
-       short height;
-/*     int format; */
-       short quarter;          /* Scale width and height down 2x */
-       short clockdiv;         /* Clock divisor setting */
+struct ov_regvals {
+       __u8 reg;
+       __u8 val;
+};
+struct ov_i2c_regvals {
+       __u8 reg;
+       __u8 val;
+};
+
+static const struct ov_i2c_regvals norm_6x20[] = {
+       { 0x12, 0x80 }, /* reset */
+       { 0x11, 0x01 },
+       { 0x03, 0x60 },
+       { 0x05, 0x7f }, /* For when autoadjust is off */
+       { 0x07, 0xa8 },
+       /* The ratio of 0x0c and 0x0d  controls the white point */
+       { 0x0c, 0x24 },
+       { 0x0d, 0x24 },
+       { 0x0f, 0x15 }, /* COMS */
+       { 0x10, 0x75 }, /* AEC Exposure time */
+       { 0x12, 0x24 }, /* Enable AGC */
+       { 0x14, 0x04 },
+       /* 0x16: 0x06 helps frame stability with moving objects */
+       { 0x16, 0x06 },
+/*     { 0x20, 0x30 },  * Aperture correction enable */
+       { 0x26, 0xb2 }, /* BLC enable */
+       /* 0x28: 0x05 Selects RGB format if RGB on */
+       { 0x28, 0x05 },
+       { 0x2a, 0x04 }, /* Disable framerate adjust */
+/*     { 0x2b, 0xac },  * Framerate; Set 2a[7] first */
+       { 0x2d, 0x99 },
+       { 0x33, 0xa0 }, /* Color Processing Parameter */
+       { 0x34, 0xd2 }, /* Max A/D range */
+       { 0x38, 0x8b },
+       { 0x39, 0x40 },
+
+       { 0x3c, 0x39 }, /* Enable AEC mode changing */
+       { 0x3c, 0x3c }, /* Change AEC mode */
+       { 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+       { 0x3d, 0x80 },
+       /* These next two registers (0x4a, 0x4b) are undocumented.
+        * They control the color balance */
+       { 0x4a, 0x80 },
+       { 0x4b, 0x80 },
+       { 0x4d, 0xd2 }, /* This reduces noise a bit */
+       { 0x4e, 0xc1 },
+       { 0x4f, 0x04 },
+/* Do 50-53 have any effect? */
+/* Toggle 0x12[2] off and on here? */
+};
+
+static const struct ov_i2c_regvals norm_6x30[] = {
+       { 0x12, 0x80 }, /* Reset */
+       { 0x00, 0x1f }, /* Gain */
+       { 0x01, 0x99 }, /* Blue gain */
+       { 0x02, 0x7c }, /* Red gain */
+       { 0x03, 0xc0 }, /* Saturation */
+       { 0x05, 0x0a }, /* Contrast */
+       { 0x06, 0x95 }, /* Brightness */
+       { 0x07, 0x2d }, /* Sharpness */
+       { 0x0c, 0x20 },
+       { 0x0d, 0x20 },
+       { 0x0e, 0x20 },
+       { 0x0f, 0x05 },
+       { 0x10, 0x9a },
+       { 0x11, 0x00 }, /* Pixel clock = fastest */
+       { 0x12, 0x24 }, /* Enable AGC and AWB */
+       { 0x13, 0x21 },
+       { 0x14, 0x80 },
+       { 0x15, 0x01 },
+       { 0x16, 0x03 },
+       { 0x17, 0x38 },
+       { 0x18, 0xea },
+       { 0x19, 0x04 },
+       { 0x1a, 0x93 },
+       { 0x1b, 0x00 },
+       { 0x1e, 0xc4 },
+       { 0x1f, 0x04 },
+       { 0x20, 0x20 },
+       { 0x21, 0x10 },
+       { 0x22, 0x88 },
+       { 0x23, 0xc0 }, /* Crystal circuit power level */
+       { 0x25, 0x9a }, /* Increase AEC black ratio */
+       { 0x26, 0xb2 }, /* BLC enable */
+       { 0x27, 0xa2 },
+       { 0x28, 0x00 },
+       { 0x29, 0x00 },
+       { 0x2a, 0x84 }, /* 60 Hz power */
+       { 0x2b, 0xa8 }, /* 60 Hz power */
+       { 0x2c, 0xa0 },
+       { 0x2d, 0x95 }, /* Enable auto-brightness */
+       { 0x2e, 0x88 },
+       { 0x33, 0x26 },
+       { 0x34, 0x03 },
+       { 0x36, 0x8f },
+       { 0x37, 0x80 },
+       { 0x38, 0x83 },
+       { 0x39, 0x80 },
+       { 0x3a, 0x0f },
+       { 0x3b, 0x3c },
+       { 0x3c, 0x1a },
+       { 0x3d, 0x80 },
+       { 0x3e, 0x80 },
+       { 0x3f, 0x0e },
+       { 0x40, 0x00 }, /* White bal */
+       { 0x41, 0x00 }, /* White bal */
+       { 0x42, 0x80 },
+       { 0x43, 0x3f }, /* White bal */
+       { 0x44, 0x80 },
+       { 0x45, 0x20 },
+       { 0x46, 0x20 },
+       { 0x47, 0x80 },
+       { 0x48, 0x7f },
+       { 0x49, 0x00 },
+       { 0x4a, 0x00 },
+       { 0x4b, 0x80 },
+       { 0x4c, 0xd0 },
+       { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+       { 0x4e, 0x40 },
+       { 0x4f, 0x07 }, /* UV avg., col. killer: max */
+       { 0x50, 0xff },
+       { 0x54, 0x23 }, /* Max AGC gain: 18dB */
+       { 0x55, 0xff },
+       { 0x56, 0x12 },
+       { 0x57, 0x81 },
+       { 0x58, 0x75 },
+       { 0x59, 0x01 }, /* AGC dark current comp.: +1 */
+       { 0x5a, 0x2c },
+       { 0x5b, 0x0f }, /* AWB chrominance levels */
+       { 0x5c, 0x10 },
+       { 0x3d, 0x80 },
+       { 0x27, 0xa6 },
+       { 0x12, 0x20 }, /* Toggle AWB */
+       { 0x12, 0x24 },
+};
+
+/* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ *     shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ *     contrast
+ */
+static const struct ov_i2c_regvals norm_7610[] = {
+       { 0x10, 0xff },
+       { 0x16, 0x06 },
+       { 0x28, 0x24 },
+       { 0x2b, 0xac },
+       { 0x12, 0x00 },
+       { 0x38, 0x81 },
+       { 0x28, 0x24 }, /* 0c */
+       { 0x0f, 0x85 }, /* lg's setting */
+       { 0x15, 0x01 },
+       { 0x20, 0x1c },
+       { 0x23, 0x2a },
+       { 0x24, 0x10 },
+       { 0x25, 0x8a },
+       { 0x26, 0xa2 },
+       { 0x27, 0xc2 },
+       { 0x2a, 0x04 },
+       { 0x2c, 0xfe },
+       { 0x2d, 0x93 },
+       { 0x30, 0x71 },
+       { 0x31, 0x60 },
+       { 0x32, 0x26 },
+       { 0x33, 0x20 },
+       { 0x34, 0x48 },
+       { 0x12, 0x24 },
+       { 0x11, 0x01 },
+       { 0x0c, 0x24 },
+       { 0x0d, 0x24 },
+};
+
+static const struct ov_i2c_regvals norm_7620[] = {
+       { 0x00, 0x00 },         /* gain */
+       { 0x01, 0x80 },         /* blue gain */
+       { 0x02, 0x80 },         /* red gain */
+       { 0x03, 0xc0 },         /* OV7670_REG_VREF */
+       { 0x06, 0x60 },
+       { 0x07, 0x00 },
+       { 0x0c, 0x24 },
+       { 0x0c, 0x24 },
+       { 0x0d, 0x24 },
+       { 0x11, 0x01 },
+       { 0x12, 0x24 },
+       { 0x13, 0x01 },
+       { 0x14, 0x84 },
+       { 0x15, 0x01 },
+       { 0x16, 0x03 },
+       { 0x17, 0x2f },
+       { 0x18, 0xcf },
+       { 0x19, 0x06 },
+       { 0x1a, 0xf5 },
+       { 0x1b, 0x00 },
+       { 0x20, 0x18 },
+       { 0x21, 0x80 },
+       { 0x22, 0x80 },
+       { 0x23, 0x00 },
+       { 0x26, 0xa2 },
+       { 0x27, 0xea },
+       { 0x28, 0x20 },
+       { 0x29, 0x00 },
+       { 0x2a, 0x10 },
+       { 0x2b, 0x00 },
+       { 0x2c, 0x88 },
+       { 0x2d, 0x91 },
+       { 0x2e, 0x80 },
+       { 0x2f, 0x44 },
+       { 0x60, 0x27 },
+       { 0x61, 0x02 },
+       { 0x62, 0x5f },
+       { 0x63, 0xd5 },
+       { 0x64, 0x57 },
+       { 0x65, 0x83 },
+       { 0x66, 0x55 },
+       { 0x67, 0x92 },
+       { 0x68, 0xcf },
+       { 0x69, 0x76 },
+       { 0x6a, 0x22 },
+       { 0x6b, 0x00 },
+       { 0x6c, 0x02 },
+       { 0x6d, 0x44 },
+       { 0x6e, 0x80 },
+       { 0x6f, 0x1d },
+       { 0x70, 0x8b },
+       { 0x71, 0x00 },
+       { 0x72, 0x14 },
+       { 0x73, 0x54 },
+       { 0x74, 0x00 },
+       { 0x75, 0x8e },
+       { 0x76, 0x00 },
+       { 0x77, 0xff },
+       { 0x78, 0x80 },
+       { 0x79, 0x80 },
+       { 0x7a, 0x80 },
+       { 0x7b, 0xe2 },
+       { 0x7c, 0x00 },
+};
+
+/* 7640 and 7648. The defaults should be OK for most registers. */
+static const struct ov_i2c_regvals norm_7640[] = {
+       { 0x12, 0x80 },
+       { 0x12, 0x14 },
+};
+
+/* 7670. Defaults taken from OmniVision provided data,
+*  as provided by Jonathan Corbet of OLPC              */
+static const struct ov_i2c_regvals norm_7670[] = {
+       { OV7670_REG_COM7, OV7670_COM7_RESET },
+       { OV7670_REG_TSLB, 0x04 },              /* OV */
+       { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
+       { OV7670_REG_CLKRC, 0x01 },
+/*
+ * Set the hardware window.  These values from OV don't entirely
+ * make sense - hstop is less than hstart.  But they work...
+ */
+       { OV7670_REG_HSTART, 0x13 },
+       { OV7670_REG_HSTOP, 0x01 },
+       { OV7670_REG_HREF, 0xb6 },
+       { OV7670_REG_VSTART, 0x02 },
+       { OV7670_REG_VSTOP, 0x7a },
+       { OV7670_REG_VREF, 0x0a },
+
+       { OV7670_REG_COM3, 0 },
+       { OV7670_REG_COM14, 0 },
+/* Mystery scaling numbers */
+       { 0x70, 0x3a },
+       { 0x71, 0x35 },
+       { 0x72, 0x11 },
+       { 0x73, 0xf0 },
+       { 0xa2, 0x02 },
+/*     { OV7670_REG_COM10, 0x0 }, */
+
+/* Gamma curve values */
+       { 0x7a, 0x20 },
+       { 0x7b, 0x10 },
+       { 0x7c, 0x1e },
+       { 0x7d, 0x35 },
+       { 0x7e, 0x5a },
+       { 0x7f, 0x69 },
+       { 0x80, 0x76 },
+       { 0x81, 0x80 },
+       { 0x82, 0x88 },
+       { 0x83, 0x8f },
+       { 0x84, 0x96 },
+       { 0x85, 0xa3 },
+       { 0x86, 0xaf },
+       { 0x87, 0xc4 },
+       { 0x88, 0xd7 },
+       { 0x89, 0xe8 },
+
+/* AGC and AEC parameters.  Note we start by disabling those features,
+   then turn them only after tweaking the values. */
+       { OV7670_REG_COM8, OV7670_COM8_FASTAEC
+                        | OV7670_COM8_AECSTEP
+                        | OV7670_COM8_BFILT },
+       { OV7670_REG_GAIN, 0 },
+       { OV7670_REG_AECH, 0 },
+       { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */
+       { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+       { OV7670_REG_BD50MAX, 0x05 },
+       { OV7670_REG_BD60MAX, 0x07 },
+       { OV7670_REG_AEW, 0x95 },
+       { OV7670_REG_AEB, 0x33 },
+       { OV7670_REG_VPT, 0xe3 },
+       { OV7670_REG_HAECC1, 0x78 },
+       { OV7670_REG_HAECC2, 0x68 },
+       { 0xa1, 0x03 }, /* magic */
+       { OV7670_REG_HAECC3, 0xd8 },
+       { OV7670_REG_HAECC4, 0xd8 },
+       { OV7670_REG_HAECC5, 0xf0 },
+       { OV7670_REG_HAECC6, 0x90 },
+       { OV7670_REG_HAECC7, 0x94 },
+       { OV7670_REG_COM8, OV7670_COM8_FASTAEC
+                       | OV7670_COM8_AECSTEP
+                       | OV7670_COM8_BFILT
+                       | OV7670_COM8_AGC
+                       | OV7670_COM8_AEC },
+
+/* Almost all of these are magic "reserved" values.  */
+       { OV7670_REG_COM5, 0x61 },
+       { OV7670_REG_COM6, 0x4b },
+       { 0x16, 0x02 },
+       { OV7670_REG_MVFP, 0x07 },
+       { 0x21, 0x02 },
+       { 0x22, 0x91 },
+       { 0x29, 0x07 },
+       { 0x33, 0x0b },
+       { 0x35, 0x0b },
+       { 0x37, 0x1d },
+       { 0x38, 0x71 },
+       { 0x39, 0x2a },
+       { OV7670_REG_COM12, 0x78 },
+       { 0x4d, 0x40 },
+       { 0x4e, 0x20 },
+       { OV7670_REG_GFIX, 0 },
+       { 0x6b, 0x4a },
+       { 0x74, 0x10 },
+       { 0x8d, 0x4f },
+       { 0x8e, 0 },
+       { 0x8f, 0 },
+       { 0x90, 0 },
+       { 0x91, 0 },
+       { 0x96, 0 },
+       { 0x9a, 0 },
+       { 0xb0, 0x84 },
+       { 0xb1, 0x0c },
+       { 0xb2, 0x0e },
+       { 0xb3, 0x82 },
+       { 0xb8, 0x0a },
+
+/* More reserved magic, some of which tweaks white balance */
+       { 0x43, 0x0a },
+       { 0x44, 0xf0 },
+       { 0x45, 0x34 },
+       { 0x46, 0x58 },
+       { 0x47, 0x28 },
+       { 0x48, 0x3a },
+       { 0x59, 0x88 },
+       { 0x5a, 0x88 },
+       { 0x5b, 0x44 },
+       { 0x5c, 0x67 },
+       { 0x5d, 0x49 },
+       { 0x5e, 0x0e },
+       { 0x6c, 0x0a },
+       { 0x6d, 0x55 },
+       { 0x6e, 0x11 },
+       { 0x6f, 0x9f },
+                                       /* "9e for advance AWB" */
+       { 0x6a, 0x40 },
+       { OV7670_REG_BLUE, 0x40 },
+       { OV7670_REG_RED, 0x60 },
+       { OV7670_REG_COM8, OV7670_COM8_FASTAEC
+                       | OV7670_COM8_AECSTEP
+                       | OV7670_COM8_BFILT
+                       | OV7670_COM8_AGC
+                       | OV7670_COM8_AEC
+                       | OV7670_COM8_AWB },
+
+/* Matrix coefficients */
+       { 0x4f, 0x80 },
+       { 0x50, 0x80 },
+       { 0x51, 0 },
+       { 0x52, 0x22 },
+       { 0x53, 0x5e },
+       { 0x54, 0x80 },
+       { 0x58, 0x9e },
+
+       { OV7670_REG_COM16, OV7670_COM16_AWBGAIN },
+       { OV7670_REG_EDGE, 0 },
+       { 0x75, 0x05 },
+       { 0x76, 0xe1 },
+       { 0x4c, 0 },
+       { 0x77, 0x01 },
+       { OV7670_REG_COM13, OV7670_COM13_GAMMA
+                         | OV7670_COM13_UVSAT
+                         | 2},         /* was 3 */
+       { 0x4b, 0x09 },
+       { 0xc9, 0x60 },
+       { OV7670_REG_COM16, 0x38 },
+       { 0x56, 0x40 },
+
+       { 0x34, 0x11 },
+       { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
+       { 0xa4, 0x88 },
+       { 0x96, 0 },
+       { 0x97, 0x30 },
+       { 0x98, 0x20 },
+       { 0x99, 0x30 },
+       { 0x9a, 0x84 },
+       { 0x9b, 0x29 },
+       { 0x9c, 0x03 },
+       { 0x9d, 0x4c },
+       { 0x9e, 0x3f },
+       { 0x78, 0x04 },
+
+/* Extra-weird stuff.  Some sort of multiplexor register */
+       { 0x79, 0x01 },
+       { 0xc8, 0xf0 },
+       { 0x79, 0x0f },
+       { 0xc8, 0x00 },
+       { 0x79, 0x10 },
+       { 0xc8, 0x7e },
+       { 0x79, 0x0a },
+       { 0xc8, 0x80 },
+       { 0x79, 0x0b },
+       { 0xc8, 0x01 },
+       { 0x79, 0x0c },
+       { 0xc8, 0x0f },
+       { 0x79, 0x0d },
+       { 0xc8, 0x20 },
+       { 0x79, 0x09 },
+       { 0xc8, 0x80 },
+       { 0x79, 0x02 },
+       { 0xc8, 0xc0 },
+       { 0x79, 0x03 },
+       { 0xc8, 0x40 },
+       { 0x79, 0x05 },
+       { 0xc8, 0x30 },
+       { 0x79, 0x26 },
+};
+
+static const struct ov_i2c_regvals norm_8610[] = {
+       { 0x12, 0x80 },
+       { 0x00, 0x00 },
+       { 0x01, 0x80 },
+       { 0x02, 0x80 },
+       { 0x03, 0xc0 },
+       { 0x04, 0x30 },
+       { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */
+       { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */
+       { 0x0a, 0x86 },
+       { 0x0b, 0xb0 },
+       { 0x0c, 0x20 },
+       { 0x0d, 0x20 },
+       { 0x11, 0x01 },
+       { 0x12, 0x25 },
+       { 0x13, 0x01 },
+       { 0x14, 0x04 },
+       { 0x15, 0x01 }, /* Lin and Win think different about UV order */
+       { 0x16, 0x03 },
+       { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */
+       { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */
+       { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */
+       { 0x1a, 0xf5 },
+       { 0x1b, 0x00 },
+       { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */
+       { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */
+       { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */
+       { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */
+       { 0x26, 0xa2 },
+       { 0x27, 0xea },
+       { 0x28, 0x00 },
+       { 0x29, 0x00 },
+       { 0x2a, 0x80 },
+       { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */
+       { 0x2c, 0xac },
+       { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */
+       { 0x2e, 0x80 },
+       { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */
+       { 0x4c, 0x00 },
+       { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */
+       { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */
+       { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */
+       { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
+       { 0x63, 0xff },
+       { 0x64, 0x53 }, /* new windrv 090403 says 0x57,
+                        * maybe thats wrong */
+       { 0x65, 0x00 },
+       { 0x66, 0x55 },
+       { 0x67, 0xb0 },
+       { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */
+       { 0x69, 0x02 },
+       { 0x6a, 0x22 },
+       { 0x6b, 0x00 },
+       { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but
+                        * deleting bit7 colors the first images red */
+       { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */
+       { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */
+       { 0x6f, 0x01 },
+       { 0x70, 0x8b },
+       { 0x71, 0x00 },
+       { 0x72, 0x14 },
+       { 0x73, 0x54 },
+       { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */
+       { 0x75, 0x0e },
+       { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */
+       { 0x77, 0xff },
+       { 0x78, 0x80 },
+       { 0x79, 0x80 },
+       { 0x7a, 0x80 },
+       { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */
+       { 0x7c, 0x00 },
+       { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */
+       { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */
+       { 0x7f, 0xfb },
+       { 0x80, 0x28 },
+       { 0x81, 0x00 },
+       { 0x82, 0x23 },
+       { 0x83, 0x0b },
+       { 0x84, 0x00 },
+       { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */
+       { 0x86, 0xc9 },
+       { 0x87, 0x00 },
+       { 0x88, 0x00 },
+       { 0x89, 0x01 },
+       { 0x12, 0x20 },
+       { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */
 };
 
 static unsigned char ov7670_abs_to_sm(unsigned char v)
@@ -502,19 +1060,6 @@ static int init_ov_sensor(struct sd *sd)
        return 0;
 }
 
-/* Switch on standard JPEG compression. Returns 0 for success. */
-static int ov519_init_compression(struct sd *sd)
-{
-       if (!sd->compress_inited) {
-               if (reg_w_mask(sd, OV519_SYS_EN_CLK1, 1 << 2, 1 << 2) < 0) {
-                       PDEBUG(D_ERR, "Error switching to compressed mode");
-                       return -EIO;
-               }
-               sd->compress_inited = 1;
-       }
-       return 0;
-}
-
 /* Set the read and write slave IDs. The "slave" argument is the write slave,
  * and the read slave will be set to (slave + 1).
  * This should not be called from outside the i2c I/O functions.
@@ -528,18 +1073,10 @@ static int ov51x_set_slave_ids(struct sd *sd,
        rc = reg_w(sd, R51x_I2C_W_SID, slave);
        if (rc < 0)
                return rc;
+       sd->primary_i2c_slave = slave;
        return reg_w(sd, R51x_I2C_R_SID, slave + 1);
 }
 
-struct ov_regvals {
-       __u8 reg;
-       __u8 val;
-};
-struct ov_i2c_regvals {
-       __u8 reg;
-       __u8 val;
-};
-
 static int write_regvals(struct sd *sd,
                         const struct ov_regvals *regvals,
                         int n)
@@ -582,101 +1119,9 @@ static int write_i2c_regvals(struct sd *sd,
 static int ov8xx0_configure(struct sd *sd)
 {
        int rc;
-       static const struct ov_i2c_regvals norm_8610[] = {
-               { 0x12, 0x80 },
-               { 0x00, 0x00 },
-               { 0x01, 0x80 },
-               { 0x02, 0x80 },
-               { 0x03, 0xc0 },
-               { 0x04, 0x30 },
-               { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */
-               { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */
-               { 0x0a, 0x86 },
-               { 0x0b, 0xb0 },
-               { 0x0c, 0x20 },
-               { 0x0d, 0x20 },
-               { 0x11, 0x01 },
-               { 0x12, 0x25 },
-               { 0x13, 0x01 },
-               { 0x14, 0x04 },
-               { 0x15, 0x01 }, /* Lin and Win think different about UV order */
-               { 0x16, 0x03 },
-               { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */
-               { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */
-               { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */
-               { 0x1a, 0xf5 },
-               { 0x1b, 0x00 },
-               { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */
-               { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */
-               { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */
-               { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */
-               { 0x26, 0xa2 },
-               { 0x27, 0xea },
-               { 0x28, 0x00 },
-               { 0x29, 0x00 },
-               { 0x2a, 0x80 },
-               { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */
-               { 0x2c, 0xac },
-               { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */
-               { 0x2e, 0x80 },
-               { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */
-               { 0x4c, 0x00 },
-               { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */
-               { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */
-               { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */
-               { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
-               { 0x63, 0xff },
-               { 0x64, 0x53 }, /* new windrv 090403 says 0x57,
-                                * maybe thats wrong */
-               { 0x65, 0x00 },
-               { 0x66, 0x55 },
-               { 0x67, 0xb0 },
-               { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */
-               { 0x69, 0x02 },
-               { 0x6a, 0x22 },
-               { 0x6b, 0x00 },
-               { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but
-                                  deleting bit7 colors the first images red */
-               { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */
-               { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */
-               { 0x6f, 0x01 },
-               { 0x70, 0x8b },
-               { 0x71, 0x00 },
-               { 0x72, 0x14 },
-               { 0x73, 0x54 },
-               { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */
-               { 0x75, 0x0e },
-               { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */
-               { 0x77, 0xff },
-               { 0x78, 0x80 },
-               { 0x79, 0x80 },
-               { 0x7a, 0x80 },
-               { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */
-               { 0x7c, 0x00 },
-               { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */
-               { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */
-               { 0x7f, 0xfb },
-               { 0x80, 0x28 },
-               { 0x81, 0x00 },
-               { 0x82, 0x23 },
-               { 0x83, 0x0b },
-               { 0x84, 0x00 },
-               { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */
-               { 0x86, 0xc9 },
-               { 0x87, 0x00 },
-               { 0x88, 0x00 },
-               { 0x89, 0x01 },
-               { 0x12, 0x20 },
-               { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */
-       };
 
        PDEBUG(D_PROBE, "starting ov8xx0 configuration");
 
-       if (init_ov_sensor(sd) < 0)
-               PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID");
-       else
-               PDEBUG(D_PROBE, "OV86x0 initialized");
-
        /* Detect sensor (sub)type */
        rc = i2c_r(sd, OV7610_REG_COM_I);
        if (rc < 0) {
@@ -684,21 +1129,14 @@ static int ov8xx0_configure(struct sd *sd)
                return -1;
        }
        if ((rc & 3) == 1) {
-               PDEBUG(D_PROBE, "Sensor is an OV8610");
                sd->sensor = SEN_OV8610;
        } else {
                PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3);
                return -1;
        }
-       PDEBUG(D_PROBE, "Writing 8610 registers");
-       if (write_i2c_regvals(sd,
-                       norm_8610,
-                       sizeof norm_8610 / sizeof norm_8610[0]))
-               return -1;
 
        /* Set sensor-specific vars */
-       sd->maxwidth = 640;
-       sd->maxheight = 480;
+/*     sd->sif = 0;            already done */
        return 0;
 }
 
@@ -709,280 +1147,9 @@ static int ov7xx0_configure(struct sd *sd)
 {
        int rc, high, low;
 
-       /* Lawrence Glaister <lg@jfm.bc.ca> reports:
-        *
-        * Register 0x0f in the 7610 has the following effects:
-        *
-        * 0x85 (AEC method 1): Best overall, good contrast range
-        * 0x45 (AEC method 2): Very overexposed
-        * 0xa5 (spec sheet default): Ok, but the black level is
-        *      shifted resulting in loss of contrast
-        * 0x05 (old driver setting): very overexposed, too much
-        *      contrast
-        */
-       static const struct ov_i2c_regvals norm_7610[] = {
-               { 0x10, 0xff },
-               { 0x16, 0x06 },
-               { 0x28, 0x24 },
-               { 0x2b, 0xac },
-               { 0x12, 0x00 },
-               { 0x38, 0x81 },
-               { 0x28, 0x24 }, /* 0c */
-               { 0x0f, 0x85 }, /* lg's setting */
-               { 0x15, 0x01 },
-               { 0x20, 0x1c },
-               { 0x23, 0x2a },
-               { 0x24, 0x10 },
-               { 0x25, 0x8a },
-               { 0x26, 0xa2 },
-               { 0x27, 0xc2 },
-               { 0x2a, 0x04 },
-               { 0x2c, 0xfe },
-               { 0x2d, 0x93 },
-               { 0x30, 0x71 },
-               { 0x31, 0x60 },
-               { 0x32, 0x26 },
-               { 0x33, 0x20 },
-               { 0x34, 0x48 },
-               { 0x12, 0x24 },
-               { 0x11, 0x01 },
-               { 0x0c, 0x24 },
-               { 0x0d, 0x24 },
-       };
-
-       static const struct ov_i2c_regvals norm_7620[] = {
-               { 0x00, 0x00 },         /* gain */
-               { 0x01, 0x80 },         /* blue gain */
-               { 0x02, 0x80 },         /* red gain */
-               { 0x03, 0xc0 },         /* OV7670_REG_VREF */
-               { 0x06, 0x60 },
-               { 0x07, 0x00 },
-               { 0x0c, 0x24 },
-               { 0x0c, 0x24 },
-               { 0x0d, 0x24 },
-               { 0x11, 0x01 },
-               { 0x12, 0x24 },
-               { 0x13, 0x01 },
-               { 0x14, 0x84 },
-               { 0x15, 0x01 },
-               { 0x16, 0x03 },
-               { 0x17, 0x2f },
-               { 0x18, 0xcf },
-               { 0x19, 0x06 },
-               { 0x1a, 0xf5 },
-               { 0x1b, 0x00 },
-               { 0x20, 0x18 },
-               { 0x21, 0x80 },
-               { 0x22, 0x80 },
-               { 0x23, 0x00 },
-               { 0x26, 0xa2 },
-               { 0x27, 0xea },
-               { 0x28, 0x20 },
-               { 0x29, 0x00 },
-               { 0x2a, 0x10 },
-               { 0x2b, 0x00 },
-               { 0x2c, 0x88 },
-               { 0x2d, 0x91 },
-               { 0x2e, 0x80 },
-               { 0x2f, 0x44 },
-               { 0x60, 0x27 },
-               { 0x61, 0x02 },
-               { 0x62, 0x5f },
-               { 0x63, 0xd5 },
-               { 0x64, 0x57 },
-               { 0x65, 0x83 },
-               { 0x66, 0x55 },
-               { 0x67, 0x92 },
-               { 0x68, 0xcf },
-               { 0x69, 0x76 },
-               { 0x6a, 0x22 },
-               { 0x6b, 0x00 },
-               { 0x6c, 0x02 },
-               { 0x6d, 0x44 },
-               { 0x6e, 0x80 },
-               { 0x6f, 0x1d },
-               { 0x70, 0x8b },
-               { 0x71, 0x00 },
-               { 0x72, 0x14 },
-               { 0x73, 0x54 },
-               { 0x74, 0x00 },
-               { 0x75, 0x8e },
-               { 0x76, 0x00 },
-               { 0x77, 0xff },
-               { 0x78, 0x80 },
-               { 0x79, 0x80 },
-               { 0x7a, 0x80 },
-               { 0x7b, 0xe2 },
-               { 0x7c, 0x00 },
-       };
-
-       /* 7640 and 7648. The defaults should be OK for most registers. */
-       static const struct ov_i2c_regvals norm_7640[] = {
-               { 0x12, 0x80 },
-               { 0x12, 0x14 },
-       };
-
-       /* 7670. Defaults taken from OmniVision provided data,
-       *  as provided by Jonathan Corbet of OLPC               */
-       static const struct ov_i2c_regvals norm_7670[] = {
-               { OV7670_REG_COM7, OV7670_COM7_RESET },
-               { OV7670_REG_TSLB, 0x04 },              /* OV */
-               { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
-               { OV7670_REG_CLKRC, 0x1 },
-       /*
-        * Set the hardware window.  These values from OV don't entirely
-        * make sense - hstop is less than hstart.  But they work...
-        */
-               { OV7670_REG_HSTART, 0x13 },    { OV7670_REG_HSTOP, 0x01 },
-               { OV7670_REG_HREF, 0xb6 },      { OV7670_REG_VSTART, 0x02 },
-               { OV7670_REG_VSTOP, 0x7a },     { OV7670_REG_VREF, 0x0a },
-
-               { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 },
-       /* Mystery scaling numbers */
-               { 0x70, 0x3a },         { 0x71, 0x35 },
-               { 0x72, 0x11 },         { 0x73, 0xf0 },
-               { 0xa2, 0x02 },
-/* jfm */
-/* { OV7670_REG_COM10, 0x0 }, */
-
-       /* Gamma curve values */
-               { 0x7a, 0x20 },
-/* jfm:win 7b=1c */
-               { 0x7b, 0x10 },
-/* jfm:win 7c=28 */
-               { 0x7c, 0x1e },
-/* jfm:win 7d=3c */
-               { 0x7d, 0x35 },
-               { 0x7e, 0x5a },         { 0x7f, 0x69 },
-               { 0x80, 0x76 },         { 0x81, 0x80 },
-               { 0x82, 0x88 },         { 0x83, 0x8f },
-               { 0x84, 0x96 },         { 0x85, 0xa3 },
-               { 0x86, 0xaf },         { 0x87, 0xc4 },
-               { 0x88, 0xd7 },         { 0x89, 0xe8 },
-
-       /* AGC and AEC parameters.  Note we start by disabling those features,
-          then turn them only after tweaking the values. */
-               { OV7670_REG_COM8, OV7670_COM8_FASTAEC
-                                | OV7670_COM8_AECSTEP
-                                | OV7670_COM8_BFILT },
-               { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 },
-               { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */
-/* jfm:win 14=38 */
-               { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
-               { OV7670_REG_BD50MAX, 0x05 },   { OV7670_REG_BD60MAX, 0x07 },
-               { OV7670_REG_AEW, 0x95 },       { OV7670_REG_AEB, 0x33 },
-               { OV7670_REG_VPT, 0xe3 },       { OV7670_REG_HAECC1, 0x78 },
-               { OV7670_REG_HAECC2, 0x68 },
-/* jfm:win a1=0b */
-               { 0xa1, 0x03 }, /* magic */
-               { OV7670_REG_HAECC3, 0xd8 },    { OV7670_REG_HAECC4, 0xd8 },
-               { OV7670_REG_HAECC5, 0xf0 },    { OV7670_REG_HAECC6, 0x90 },
-               { OV7670_REG_HAECC7, 0x94 },
-               { OV7670_REG_COM8, OV7670_COM8_FASTAEC
-                               | OV7670_COM8_AECSTEP
-                               | OV7670_COM8_BFILT
-                               | OV7670_COM8_AGC
-                               | OV7670_COM8_AEC },
-
-       /* Almost all of these are magic "reserved" values.  */
-               { OV7670_REG_COM5, 0x61 },      { OV7670_REG_COM6, 0x4b },
-               { 0x16, 0x02 },
-/* jfm */
-/*             { OV7670_REG_MVFP, 0x07|OV7670_MVFP_MIRROR }, */
-               { OV7670_REG_MVFP, 0x07 },
-               { 0x21, 0x02 },         { 0x22, 0x91 },
-               { 0x29, 0x07 },         { 0x33, 0x0b },
-               { 0x35, 0x0b },         { 0x37, 0x1d },
-               { 0x38, 0x71 },         { 0x39, 0x2a },
-               { OV7670_REG_COM12, 0x78 },     { 0x4d, 0x40 },
-               { 0x4e, 0x20 },         { OV7670_REG_GFIX, 0 },
-               { 0x6b, 0x4a },         { 0x74, 0x10 },
-               { 0x8d, 0x4f },         { 0x8e, 0 },
-               { 0x8f, 0 },            { 0x90, 0 },
-               { 0x91, 0 },            { 0x96, 0 },
-               { 0x9a, 0 },            { 0xb0, 0x84 },
-               { 0xb1, 0x0c },         { 0xb2, 0x0e },
-               { 0xb3, 0x82 },         { 0xb8, 0x0a },
-
-       /* More reserved magic, some of which tweaks white balance */
-               { 0x43, 0x0a },         { 0x44, 0xf0 },
-               { 0x45, 0x34 },         { 0x46, 0x58 },
-               { 0x47, 0x28 },         { 0x48, 0x3a },
-               { 0x59, 0x88 },         { 0x5a, 0x88 },
-               { 0x5b, 0x44 },         { 0x5c, 0x67 },
-               { 0x5d, 0x49 },         { 0x5e, 0x0e },
-               { 0x6c, 0x0a },         { 0x6d, 0x55 },
-               { 0x6e, 0x11 },         { 0x6f, 0x9f },
-                                               /* "9e for advance AWB" */
-               { 0x6a, 0x40 },         { OV7670_REG_BLUE, 0x40 },
-               { OV7670_REG_RED, 0x60 },
-               { OV7670_REG_COM8, OV7670_COM8_FASTAEC
-                               | OV7670_COM8_AECSTEP
-                               | OV7670_COM8_BFILT
-                               | OV7670_COM8_AGC
-                               | OV7670_COM8_AEC
-                               | OV7670_COM8_AWB },
-
-       /* Matrix coefficients */
-               { 0x4f, 0x80 },         { 0x50, 0x80 },
-               { 0x51, 0 },            { 0x52, 0x22 },
-               { 0x53, 0x5e },         { 0x54, 0x80 },
-               { 0x58, 0x9e },
-
-               { OV7670_REG_COM16, OV7670_COM16_AWBGAIN },
-               { OV7670_REG_EDGE, 0 },
-               { 0x75, 0x05 },         { 0x76, 0xe1 },
-               { 0x4c, 0 },            { 0x77, 0x01 },
-               { OV7670_REG_COM13, 0xc3 },     { 0x4b, 0x09 },
-               { 0xc9, 0x60 },         { OV7670_REG_COM16, 0x38 },
-               { 0x56, 0x40 },
-
-               { 0x34, 0x11 },
-               { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
-               { 0xa4, 0x88 },         { 0x96, 0 },
-               { 0x97, 0x30 },         { 0x98, 0x20 },
-               { 0x99, 0x30 },         { 0x9a, 0x84 },
-               { 0x9b, 0x29 },         { 0x9c, 0x03 },
-               { 0x9d, 0x4c },         { 0x9e, 0x3f },
-               { 0x78, 0x04 },
-
-       /* Extra-weird stuff.  Some sort of multiplexor register */
-               { 0x79, 0x01 },         { 0xc8, 0xf0 },
-               { 0x79, 0x0f },         { 0xc8, 0x00 },
-               { 0x79, 0x10 },         { 0xc8, 0x7e },
-               { 0x79, 0x0a },         { 0xc8, 0x80 },
-               { 0x79, 0x0b },         { 0xc8, 0x01 },
-               { 0x79, 0x0c },         { 0xc8, 0x0f },
-               { 0x79, 0x0d },         { 0xc8, 0x20 },
-               { 0x79, 0x09 },         { 0xc8, 0x80 },
-               { 0x79, 0x02 },         { 0xc8, 0xc0 },
-               { 0x79, 0x03 },         { 0xc8, 0x40 },
-               { 0x79, 0x05 },         { 0xc8, 0x30 },
-               { 0x79, 0x26 },
-
-       /* Format YUV422 */
-               { OV7670_REG_COM7, OV7670_COM7_YUV },  /* Selects YUV mode */
-               { OV7670_REG_RGB444, 0 },       /* No RGB444 please */
-               { OV7670_REG_COM1, 0 },
-               { OV7670_REG_COM15, OV7670_COM15_R00FF },
-               { OV7670_REG_COM9, 0x18 },
-                               /* 4x gain ceiling; 0x8 is reserved bit */
-               { 0x4f, 0x80 },         /* "matrix coefficient 1" */
-               { 0x50, 0x80 },         /* "matrix coefficient 2" */
-               { 0x52, 0x22 },         /* "matrix coefficient 4" */
-               { 0x53, 0x5e },         /* "matrix coefficient 5" */
-               { 0x54, 0x80 },         /* "matrix coefficient 6" */
-               { OV7670_REG_COM13, OV7670_COM13_GAMMA|OV7670_COM13_UVSAT },
-};
 
        PDEBUG(D_PROBE, "starting OV7xx0 configuration");
 
-/* jfm:already done? */
-       if (init_ov_sensor(sd) < 0)
-               PDEBUG(D_ERR, "Failed to read sensor ID");
-       else
-               PDEBUG(D_PROBE, "OV7xx0 initialized");
-
        /* Detect sensor (sub)type */
        rc = i2c_r(sd, OV7610_REG_COM_I);
 
@@ -1028,20 +1195,26 @@ static int ov7xx0_configure(struct sd *sd)
                        return low;
                }
                if (high == 0x76) {
-                       if (low == 0x30) {
+                       switch (low) {
+                       case 0x30:
                                PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635");
-                               sd->sensor = SEN_OV7630;
-                       } else if (low == 0x40) {
+                               PDEBUG(D_ERR,
+                                     "7630 is not supported by this driver");
+                               return -1;
+                       case 0x40:
                                PDEBUG(D_PROBE, "Sensor is an OV7645");
                                sd->sensor = SEN_OV7640; /* FIXME */
-                       } else if (low == 0x45) {
+                               break;
+                       case 0x45:
                                PDEBUG(D_PROBE, "Sensor is an OV7645B");
                                sd->sensor = SEN_OV7640; /* FIXME */
-                       } else if (low == 0x48) {
+                               break;
+                       case 0x48:
                                PDEBUG(D_PROBE, "Sensor is an OV7648");
                                sd->sensor = SEN_OV7640; /* FIXME */
-                       } else {
-                               PDEBUG(D_PROBE, "Unknown sensor: 0x76%X", low);
+                               break;
+                       default:
+                               PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
                                return -1;
                        }
                } else {
@@ -1053,34 +1226,8 @@ static int ov7xx0_configure(struct sd *sd)
                return -1;
        }
 
-       if (sd->sensor == SEN_OV7620) {
-               PDEBUG(D_PROBE, "Writing 7620 registers");
-               if (write_i2c_regvals(sd, norm_7620,
-                               sizeof norm_7620 / sizeof norm_7620[0]))
-                       return -1;
-       } else if (sd->sensor == SEN_OV7630) {
-               PDEBUG(D_ERR, "7630 is not supported by this driver version");
-               return -1;
-       } else if (sd->sensor == SEN_OV7640) {
-               PDEBUG(D_PROBE, "Writing 7640 registers");
-               if (write_i2c_regvals(sd, norm_7640,
-                               sizeof norm_7640 / sizeof norm_7640[0]))
-                       return -1;
-       } else if (sd->sensor == SEN_OV7670) {
-               PDEBUG(D_PROBE, "Writing 7670 registers");
-               if (write_i2c_regvals(sd, norm_7670,
-                               sizeof norm_7670 / sizeof norm_7670[0]))
-                       return -1;
-       } else {
-               PDEBUG(D_PROBE, "Writing 7610 registers");
-               if (write_i2c_regvals(sd, norm_7610,
-                               sizeof norm_7610 / sizeof norm_7610[0]))
-                       return -1;
-       }
-
        /* Set sensor-specific vars */
-       sd->maxwidth = 640;
-       sd->maxheight = 480;
+/*     sd->sif = 0;            already done */
        return 0;
 }
 
@@ -1088,141 +1235,7 @@ static int ov7xx0_configure(struct sd *sd)
 static int ov6xx0_configure(struct sd *sd)
 {
        int rc;
-       static const struct ov_i2c_regvals norm_6x20[] = {
-               { 0x12, 0x80 }, /* reset */
-               { 0x11, 0x01 },
-               { 0x03, 0x60 },
-               { 0x05, 0x7f }, /* For when autoadjust is off */
-               { 0x07, 0xa8 },
-               /* The ratio of 0x0c and 0x0d  controls the white point */
-               { 0x0c, 0x24 },
-               { 0x0d, 0x24 },
-               { 0x0f, 0x15 }, /* COMS */
-               { 0x10, 0x75 }, /* AEC Exposure time */
-               { 0x12, 0x24 }, /* Enable AGC */
-               { 0x14, 0x04 },
-               /* 0x16: 0x06 helps frame stability with moving objects */
-               { 0x16, 0x06 },
-/*             { 0x20, 0x30 },  * Aperture correction enable */
-               { 0x26, 0xb2 }, /* BLC enable */
-               /* 0x28: 0x05 Selects RGB format if RGB on */
-               { 0x28, 0x05 },
-               { 0x2a, 0x04 }, /* Disable framerate adjust */
-/*             { 0x2b, 0xac },  * Framerate; Set 2a[7] first */
-               { 0x2d, 0x99 },
-               { 0x33, 0xa0 }, /* Color Processing Parameter */
-               { 0x34, 0xd2 }, /* Max A/D range */
-               { 0x38, 0x8b },
-               { 0x39, 0x40 },
-
-               { 0x3c, 0x39 }, /* Enable AEC mode changing */
-               { 0x3c, 0x3c }, /* Change AEC mode */
-               { 0x3c, 0x24 }, /* Disable AEC mode changing */
-
-               { 0x3d, 0x80 },
-               /* These next two registers (0x4a, 0x4b) are undocumented.
-                * They control the color balance */
-               { 0x4a, 0x80 },
-               { 0x4b, 0x80 },
-               { 0x4d, 0xd2 }, /* This reduces noise a bit */
-               { 0x4e, 0xc1 },
-               { 0x4f, 0x04 },
-/* Do 50-53 have any effect? */
-/* Toggle 0x12[2] off and on here? */
-       };
-
-       static const struct ov_i2c_regvals norm_6x30[] = {
-               { 0x12, 0x80 }, /* Reset */
-               { 0x00, 0x1f }, /* Gain */
-               { 0x01, 0x99 }, /* Blue gain */
-               { 0x02, 0x7c }, /* Red gain */
-               { 0x03, 0xc0 }, /* Saturation */
-               { 0x05, 0x0a }, /* Contrast */
-               { 0x06, 0x95 }, /* Brightness */
-               { 0x07, 0x2d }, /* Sharpness */
-               { 0x0c, 0x20 },
-               { 0x0d, 0x20 },
-               { 0x0e, 0x20 },
-               { 0x0f, 0x05 },
-               { 0x10, 0x9a },
-               { 0x11, 0x00 }, /* Pixel clock = fastest */
-               { 0x12, 0x24 }, /* Enable AGC and AWB */
-               { 0x13, 0x21 },
-               { 0x14, 0x80 },
-               { 0x15, 0x01 },
-               { 0x16, 0x03 },
-               { 0x17, 0x38 },
-               { 0x18, 0xea },
-               { 0x19, 0x04 },
-               { 0x1a, 0x93 },
-               { 0x1b, 0x00 },
-               { 0x1e, 0xc4 },
-               { 0x1f, 0x04 },
-               { 0x20, 0x20 },
-               { 0x21, 0x10 },
-               { 0x22, 0x88 },
-               { 0x23, 0xc0 }, /* Crystal circuit power level */
-               { 0x25, 0x9a }, /* Increase AEC black ratio */
-               { 0x26, 0xb2 }, /* BLC enable */
-               { 0x27, 0xa2 },
-               { 0x28, 0x00 },
-               { 0x29, 0x00 },
-               { 0x2a, 0x84 }, /* 60 Hz power */
-               { 0x2b, 0xa8 }, /* 60 Hz power */
-               { 0x2c, 0xa0 },
-               { 0x2d, 0x95 }, /* Enable auto-brightness */
-               { 0x2e, 0x88 },
-               { 0x33, 0x26 },
-               { 0x34, 0x03 },
-               { 0x36, 0x8f },
-               { 0x37, 0x80 },
-               { 0x38, 0x83 },
-               { 0x39, 0x80 },
-               { 0x3a, 0x0f },
-               { 0x3b, 0x3c },
-               { 0x3c, 0x1a },
-               { 0x3d, 0x80 },
-               { 0x3e, 0x80 },
-               { 0x3f, 0x0e },
-               { 0x40, 0x00 }, /* White bal */
-               { 0x41, 0x00 }, /* White bal */
-               { 0x42, 0x80 },
-               { 0x43, 0x3f }, /* White bal */
-               { 0x44, 0x80 },
-               { 0x45, 0x20 },
-               { 0x46, 0x20 },
-               { 0x47, 0x80 },
-               { 0x48, 0x7f },
-               { 0x49, 0x00 },
-               { 0x4a, 0x00 },
-               { 0x4b, 0x80 },
-               { 0x4c, 0xd0 },
-               { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
-               { 0x4e, 0x40 },
-               { 0x4f, 0x07 }, /* UV avg., col. killer: max */
-               { 0x50, 0xff },
-               { 0x54, 0x23 }, /* Max AGC gain: 18dB */
-               { 0x55, 0xff },
-               { 0x56, 0x12 },
-               { 0x57, 0x81 },
-               { 0x58, 0x75 },
-               { 0x59, 0x01 }, /* AGC dark current comp.: +1 */
-               { 0x5a, 0x2c },
-               { 0x5b, 0x0f }, /* AWB chrominance levels */
-               { 0x5c, 0x10 },
-               { 0x3d, 0x80 },
-               { 0x27, 0xa6 },
-               { 0x12, 0x20 }, /* Toggle AWB */
-               { 0x12, 0x24 },
-       };
-
-       PDEBUG(D_PROBE, "starting sensor configuration");
-
-       if (init_ov_sensor(sd) < 0) {
-               PDEBUG(D_ERR, "Failed to read sensor ID.");
-               return -1;
-       }
-       PDEBUG(D_PROBE, "OV6xx0 sensor detected");
+       PDEBUG(D_PROBE, "starting OV6xx0 configuration");
 
        /* Detect sensor (sub)type */
        rc = i2c_r(sd, OV7610_REG_COM_I);
@@ -1234,59 +1247,46 @@ static int ov6xx0_configure(struct sd *sd)
        /* Ugh. The first two bits are the version bits, but
         * the entire register value must be used. I guess OVT
         * underestimated how many variants they would make. */
-       if (rc == 0x00) {
+       switch (rc) {
+       case 0x00:
                sd->sensor = SEN_OV6630;
                PDEBUG(D_ERR,
                        "WARNING: Sensor is an OV66308. Your camera may have");
                PDEBUG(D_ERR, "been misdetected in previous driver versions.");
-       } else if (rc == 0x01) {
+               break;
+       case 0x01:
                sd->sensor = SEN_OV6620;
-               PDEBUG(D_PROBE, "Sensor is an OV6620");
-       } else if (rc == 0x02) {
+               break;
+       case 0x02:
                sd->sensor = SEN_OV6630;
                PDEBUG(D_PROBE, "Sensor is an OV66308AE");
-       } else if (rc == 0x03) {
+               break;
+       case 0x03:
                sd->sensor = SEN_OV6630;
                PDEBUG(D_PROBE, "Sensor is an OV66308AF");
-       } else if (rc == 0x90) {
+               break;
+       case 0x90:
                sd->sensor = SEN_OV6630;
                PDEBUG(D_ERR,
                        "WARNING: Sensor is an OV66307. Your camera may have");
                PDEBUG(D_ERR, "been misdetected in previous driver versions.");
-       } else {
+               break;
+       default:
                PDEBUG(D_ERR, "FATAL: Unknown sensor version: 0x%02x", rc);
                return -1;
        }
 
        /* Set sensor-specific vars */
-       sd->maxwidth = 352;
-       sd->maxheight = 288;
+       sd->sif = 1;
 
-       if (sd->sensor == SEN_OV6620) {
-               PDEBUG(D_PROBE, "Writing 6x20 registers");
-               if (write_i2c_regvals(sd, norm_6x20,
-                               sizeof norm_6x20 / sizeof norm_6x20[0]))
-                       return -1;
-       } else {
-               PDEBUG(D_PROBE, "Writing 6x30 registers");
-               if (write_i2c_regvals(sd, norm_6x30,
-                               sizeof norm_6x30 / sizeof norm_6x30[0]))
-                       return -1;
-       }
        return 0;
 }
 
 /* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
 static void ov51x_led_control(struct sd *sd, int on)
 {
-       PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off");
-
-/*     if (sd->bridge == BRG_OV511PLUS) */
-/*             reg_w(sd, R511_SYS_LED_CTL, on ? 1 : 0); */
-/*     else if (sd->bridge == BRG_OV519) */
-               reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1);   /* 0 / 1 */
-/*     else if (sd->bclass == BCL_OV518) */
-/*             reg_w_mask(sd, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); */
+/*     PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); */
+       reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1);   /* 0 / 1 */
 }
 
 /* this function is called at probe time */
@@ -1296,11 +1296,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
        struct sd *sd = (struct sd *) gspca_dev;
        struct cam *cam;
 
-/* (from ov519_configure) */
        static const struct ov_regvals init_519[] = {
                { 0x5a,  0x6d }, /* EnableSystem */
-/* jfm trace usbsnoop3-1.txt */
-/* jfm 53 = fb */
                { 0x53,  0x9b },
                { 0x54,  0xff }, /* set bit2 to enable jpeg */
                { 0x5d,  0x03 },
@@ -1317,28 +1314,34 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        if (write_regvals(sd, init_519, ARRAY_SIZE(init_519)))
                goto error;
-/* jfm: not seen in windows trace */
-       if (ov519_init_compression(sd))
-               goto error;
        ov51x_led_control(sd, 0);       /* turn LED off */
 
        /* Test for 76xx */
-       sd->primary_i2c_slave = OV7xx0_SID;
        if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0)
                goto error;
 
        /* The OV519 must be more aggressive about sensor detection since
         * I2C write will never fail if the sensor is not present. We have
         * to try to initialize the sensor to detect its presence */
-       if (init_ov_sensor(sd) < 0) {
+       if (init_ov_sensor(sd) >= 0) {
+               if (ov7xx0_configure(sd) < 0) {
+                       PDEBUG(D_ERR, "Failed to configure OV7xx0");
+                       goto error;
+               }
+       } else {
+
                /* Test for 6xx0 */
-               sd->primary_i2c_slave = OV6xx0_SID;
                if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0)
                        goto error;
 
-               if (init_ov_sensor(sd) < 0) {
+               if (init_ov_sensor(sd) >= 0) {
+                       if (ov6xx0_configure(sd) < 0) {
+                               PDEBUG(D_ERR, "Failed to configure OV6xx0");
+                               goto error;
+                       }
+               } else {
+
                        /* Test for 8xx0 */
-                       sd->primary_i2c_slave = OV8xx0_SID;
                        if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0)
                                goto error;
 
@@ -1346,48 +1349,76 @@ static int sd_config(struct gspca_dev *gspca_dev,
                                PDEBUG(D_ERR,
                                        "Can't determine sensor slave IDs");
                                goto error;
-                       } else {
-                               if (ov8xx0_configure(sd) < 0) {
-                                       PDEBUG(D_ERR,
-                                          "Failed to configure OV8xx0 sensor");
-                                       goto error;
-                               }
                        }
-               } else {
-                       if (ov6xx0_configure(sd) < 0) {
-                               PDEBUG(D_ERR, "Failed to configure OV6xx0");
+                       if (ov8xx0_configure(sd) < 0) {
+                               PDEBUG(D_ERR,
+                                  "Failed to configure OV8xx0 sensor");
                                goto error;
                        }
                }
-       } else {
-               if (ov7xx0_configure(sd) < 0) {
-                       PDEBUG(D_ERR, "Failed to configure OV7xx0");
-                       goto error;
-               }
        }
 
        cam = &gspca_dev->cam;
        cam->epaddr = OV511_ENDPOINT_ADDRESS;
-       if (sd->maxwidth == 640) {
+       if (!sd->sif) {
                cam->cam_mode = vga_mode;
-               cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+               cam->nmodes = ARRAY_SIZE(vga_mode);
        } else {
                cam->cam_mode = sif_mode;
-               cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+               cam->nmodes = ARRAY_SIZE(sif_mode);
        }
-       cam->dev_name = (char *) id->driver_info;
-       sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
-       sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
-       sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->hflip = HFLIP_DEF;
+       sd->vflip = VFLIP_DEF;
+       if (sd->sensor != SEN_OV7670)
+               gspca_dev->ctrl_dis = (1 << HFLIP_IDX)
+                                       | (1 << VFLIP_IDX);
        return 0;
 error:
        PDEBUG(D_ERR, "OV519 Config failed");
        return -EBUSY;
 }
 
-/* this function is called at open time */
-static int sd_open(struct gspca_dev *gspca_dev)
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* initialize the sensor */
+       switch (sd->sensor) {
+       case SEN_OV6620:
+               if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20)))
+                       return -EIO;
+               break;
+       case SEN_OV6630:
+               if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30)))
+                       return -EIO;
+               break;
+       default:
+/*     case SEN_OV7610: */
+/*     case SEN_OV76BE: */
+               if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610)))
+                       return -EIO;
+               break;
+       case SEN_OV7620:
+               if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620)))
+                       return -EIO;
+               break;
+       case SEN_OV7640:
+               if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640)))
+                       return -EIO;
+               break;
+       case SEN_OV7670:
+               if (write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670)))
+                       return -EIO;
+               break;
+       case SEN_OV8610:
+               if (write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610)))
+                       return -EIO;
+               break;
+       }
        return 0;
 }
 
@@ -1398,8 +1429,7 @@ static int sd_open(struct gspca_dev *gspca_dev)
  *
  * Do not put any sensor-specific code in here (including I2C I/O functions)
  */
-static int ov519_mode_init_regs(struct sd *sd,
-                               int width, int height)
+static int ov519_mode_init_regs(struct sd *sd)
 {
        static const struct ov_regvals mode_init_519_ov7670[] = {
                { 0x5d, 0x03 }, /* Turn off suspend mode */
@@ -1445,36 +1475,23 @@ static int ov519_mode_init_regs(struct sd *sd,
                /* windows reads 0x55 at this point, why? */
        };
 
-/* int hi_res; */
-
-       PDEBUG(D_CONF, "mode init %dx%d", width, height);
-
-/*     if (width >= 800 && height >= 600)
-               hi_res = 1;
-       else
-               hi_res = 0; */
-
-/*     if (ov51x_stop(sd) < 0)
-               return -EIO; */
-
        /******** Set the mode ********/
        if (sd->sensor != SEN_OV7670) {
                if (write_regvals(sd, mode_init_519,
                                  ARRAY_SIZE(mode_init_519)))
                        return -EIO;
+               if (sd->sensor == SEN_OV7640) {
+                       /* Select 8-bit input mode */
+                       reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10);
+               }
        } else {
                if (write_regvals(sd, mode_init_519_ov7670,
                                  ARRAY_SIZE(mode_init_519_ov7670)))
                        return -EIO;
        }
 
-       if (sd->sensor == SEN_OV7640) {
-               /* Select 8-bit input mode */
-               reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10);
-       }
-
-       reg_w(sd, OV519_CAM_H_SIZE,     width >> 4);
-       reg_w(sd, OV519_CAM_V_SIZE,     height >> 3);
+       reg_w(sd, OV519_CAM_H_SIZE,     sd->gspca_dev.width >> 4);
+       reg_w(sd, OV519_CAM_V_SIZE,     sd->gspca_dev.height >> 3);
        reg_w(sd, OV519_CAM_X_OFFSETL,  0x00);
        reg_w(sd, OV519_CAM_X_OFFSETH,  0x00);
        reg_w(sd, OV519_CAM_Y_OFFSETL,  0x00);
@@ -1489,9 +1506,10 @@ static int ov519_mode_init_regs(struct sd *sd,
 
 /* FIXME: These are only valid at the max resolution. */
        sd->clockdiv = 0;
-       if (sd->sensor == SEN_OV7640) {
+       switch (sd->sensor) {
+       case SEN_OV7640:
                switch (sd->frame_rate) {
-/*jfm: default was 30 fps */
+/*fixme: default was 30 fps */
                case 30:
                        reg_w(sd, 0xa4, 0x0c);
                        reg_w(sd, 0x23, 0xff);
@@ -1521,7 +1539,8 @@ static int ov519_mode_init_regs(struct sd *sd,
                        sd->clockdiv = 1;
                        break;
                }
-       } else if (sd->sensor == SEN_OV8610) {
+               break;
+       case SEN_OV8610:
                switch (sd->frame_rate) {
                default:        /* 15 fps */
 /*             case 15: */
@@ -1537,41 +1556,37 @@ static int ov519_mode_init_regs(struct sd *sd,
                        reg_w(sd, 0x23, 0x1b);
                        break;
                }
-               sd->clockdiv = 0;
-       } else if (sd->sensor == SEN_OV7670) { /* guesses, based on 7640 */
+               break;
+       case SEN_OV7670:                /* guesses, based on 7640 */
                PDEBUG(D_STREAM, "Setting framerate to %d fps",
                                 (sd->frame_rate == 0) ? 15 : sd->frame_rate);
+               reg_w(sd, 0xa4, 0x10);
                switch (sd->frame_rate) {
                case 30:
-                       reg_w(sd, 0xa4, 0x10);
                        reg_w(sd, 0x23, 0xff);
                        break;
                case 20:
-                       reg_w(sd, 0xa4, 0x10);
                        reg_w(sd, 0x23, 0x1b);
                        break;
-               default: /* 15 fps */
-/*                     case 15: */
-                       reg_w(sd, 0xa4, 0x10);
+               default:
+/*             case 15: */
                        reg_w(sd, 0x23, 0xff);
                        sd->clockdiv = 1;
                        break;
                }
+               break;
        }
 
-/*     if (ov51x_restart(sd) < 0)
-               return -EIO; */
-
-       /* Reset it just for good measure */
-/*     if (ov51x_reset(sd, OV511_RESET_NOREGS) < 0)
-               return -EIO; */
        return 0;
 }
 
-static int mode_init_ov_sensor_regs(struct sd *sd,
-                               struct ovsensor_window *win)
+static int mode_init_ov_sensor_regs(struct sd *sd)
 {
-       int qvga = win->quarter;
+       struct gspca_dev *gspca_dev;
+       int qvga;
+
+       gspca_dev = &sd->gspca_dev;
+       qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
 
        /******** Mode (VGA/QVGA) and sensor specific regs ********/
        switch (sd->sensor) {
@@ -1615,8 +1630,6 @@ static int mode_init_ov_sensor_regs(struct sd *sd,
                         OV7670_COM7_FMT_MASK);
                break;
        case SEN_OV6620:
-               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
-               break;
        case SEN_OV6630:
                i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
                break;
@@ -1625,24 +1638,21 @@ static int mode_init_ov_sensor_regs(struct sd *sd,
        }
 
        /******** Palette-specific regs ********/
-/* Need to do work here for the OV7670 */
-
-               if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
-                       /* not valid on the OV6620/OV7620/6630? */
-                       i2c_w_mask(sd, 0x0e, 0x00, 0x40);
-               }
+       if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
+               /* not valid on the OV6620/OV7620/6630? */
+               i2c_w_mask(sd, 0x0e, 0x00, 0x40);
+       }
 
-               /* The OV518 needs special treatment. Although both the OV518
-                * and the OV6630 support a 16-bit video bus, only the 8 bit Y
-                * bus is actually used. The UV bus is tied to ground.
-                * Therefore, the OV6630 needs to be in 8-bit multiplexed
-                * output mode */
+       /* The OV518 needs special treatment. Although both the OV518
+        * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+        * bus is actually used. The UV bus is tied to ground.
+        * Therefore, the OV6630 needs to be in 8-bit multiplexed
+        * output mode */
 
-               /* OV7640 is 8-bit only */
+       /* OV7640 is 8-bit only */
 
-               if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640)
-                       i2c_w_mask(sd, 0x13, 0x00, 0x20);
-/*     } */
+       if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640)
+               i2c_w_mask(sd, 0x13, 0x00, 0x20);
 
        /******** Clock programming ********/
        /* The OV6620 needs special handling. This prevents the
@@ -1651,14 +1661,14 @@ static int mode_init_ov_sensor_regs(struct sd *sd,
 
                /* Clock down */
                i2c_w(sd, 0x2a, 0x04);
-               i2c_w(sd, 0x11, win->clockdiv);
+               i2c_w(sd, 0x11, sd->clockdiv);
                i2c_w(sd, 0x2a, 0x84);
                /* This next setting is critical. It seems to improve
                 * the gain or the contrast. The "reserved" bits seem
                 * to have some effect in this case. */
                i2c_w(sd, 0x2d, 0x85);
-       } else if (win->clockdiv >= 0) {
-               i2c_w(sd, 0x11, win->clockdiv);
+       } else if (sd->clockdiv >= 0) {
+               i2c_w(sd, 0x11, sd->clockdiv);
        }
 
        /******** Special Features ********/
@@ -1678,7 +1688,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd,
        /* is fully tested. */
        /* 7620/6620/6630? don't have register 0x35, so play it safe */
        if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
-               if (win->width == 640 /*&& win->height == 480*/)
+               if (!qvga)
                        i2c_w(sd, 0x35, 0x9e);
                else
                        i2c_w(sd, 0x35, 0x1e);
@@ -1686,13 +1696,31 @@ static int mode_init_ov_sensor_regs(struct sd *sd,
        return 0;
 }
 
-static int set_ov_sensor_window(struct sd *sd,
-                               struct ovsensor_window *win)
+static void sethvflip(struct sd *sd)
 {
+       if (sd->sensor != SEN_OV7670)
+               return;
+       if (sd->gspca_dev.streaming)
+               ov51x_stop(sd);
+       i2c_w_mask(sd, OV7670_REG_MVFP,
+               OV7670_MVFP_MIRROR * sd->hflip
+                       | OV7670_MVFP_VFLIP * sd->vflip,
+               OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
+       if (sd->gspca_dev.streaming)
+               ov51x_restart(sd);
+}
+
+static int set_ov_sensor_window(struct sd *sd)
+{
+       struct gspca_dev *gspca_dev;
+       int qvga;
        int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
        int ret, hstart, hstop, vstop, vstart;
        __u8 v;
 
+       gspca_dev = &sd->gspca_dev;
+       qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+
        /* The different sensor ICs handle setting up of window differently.
         * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */
        switch (sd->sensor) {
@@ -1737,7 +1765,7 @@ static int set_ov_sensor_window(struct sd *sd,
        switch (sd->sensor) {
        case SEN_OV6620:
        case SEN_OV6630:
-               if (win->quarter) {     /* QCIF */
+               if (qvga) {             /* QCIF */
                        hwscale = 0;
                        vwscale = 0;
                } else {                /* CIF */
@@ -1747,7 +1775,7 @@ static int set_ov_sensor_window(struct sd *sd,
                }
                break;
        case SEN_OV8610:
-               if (win->quarter) {     /* QSVGA */
+               if (qvga) {             /* QSVGA */
                        hwscale = 1;
                        vwscale = 1;
                } else {                /* SVGA */
@@ -1756,7 +1784,7 @@ static int set_ov_sensor_window(struct sd *sd,
                }
                break;
        default:                        /* SEN_OV7xx0 */
-               if (win->quarter) {     /* QVGA */
+               if (qvga) {             /* QVGA */
                        hwscale = 1;
                        vwscale = 0;
                } else {                /* VGA */
@@ -1765,7 +1793,7 @@ static int set_ov_sensor_window(struct sd *sd,
                }
        }
 
-       ret = mode_init_ov_sensor_regs(sd, win);
+       ret = mode_init_ov_sensor_regs(sd);
        if (ret < 0)
                return ret;
 
@@ -1786,7 +1814,7 @@ static int set_ov_sensor_window(struct sd *sd,
        /* I can hard code this for OV7670s */
        /* Yes, these numbers do look odd, but they're tested and work! */
        if (sd->sensor == SEN_OV7670) {
-               if (win->quarter) {     /* QVGA from ov7670.c by
+               if (qvga) {             /* QVGA from ov7670.c by
                                         * Jonathan Corbet */
                        hstart = 164;
                        hstop = 20;
@@ -1800,75 +1828,45 @@ static int set_ov_sensor_window(struct sd *sd,
                }
                /* OV7670 hardware window registers are split across
                 * multiple locations */
-               i2c_w(sd, OV7670_REG_HSTART, (hstart >> 3) & 0xff);
-               i2c_w(sd, OV7670_REG_HSTOP, (hstop >> 3) & 0xff);
+               i2c_w(sd, OV7670_REG_HSTART, hstart >> 3);
+               i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3);
                v = i2c_r(sd, OV7670_REG_HREF);
                v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07);
                msleep(10);     /* need to sleep between read and write to
                                 * same reg! */
                i2c_w(sd, OV7670_REG_HREF, v);
 
-               i2c_w(sd, OV7670_REG_VSTART, (vstart >> 2) & 0xff);
-               i2c_w(sd, OV7670_REG_VSTOP, (vstop >> 2) & 0xff);
+               i2c_w(sd, OV7670_REG_VSTART, vstart >> 2);
+               i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2);
                v = i2c_r(sd, OV7670_REG_VREF);
                v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03);
                msleep(10);     /* need to sleep between read and write to
                                 * same reg! */
                i2c_w(sd, OV7670_REG_VREF, v);
-
+               sethvflip(sd);
        } else {
-               i2c_w(sd, 0x17, hwsbase + (win->x >> hwscale));
-               i2c_w(sd, 0x18, hwebase + ((win->x + win->width) >> hwscale));
-               i2c_w(sd, 0x19, vwsbase + (win->y >> vwscale));
-               i2c_w(sd, 0x1a, vwebase + ((win->y + win->height) >> vwscale));
+               i2c_w(sd, 0x17, hwsbase);
+               i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale));
+               i2c_w(sd, 0x19, vwsbase);
+               i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale));
        }
        return 0;
 }
 
-static int ov_sensor_mode_setup(struct sd *sd,
-                               int width, int height)
-{
-       struct ovsensor_window win;
-
-/*     win.format = mode; */
-
-       /* Unless subcapture is enabled,
-        * center the image window and downsample
-        * if possible to increase the field of view */
-       /* NOTE: OV518(+) and OV519 does downsampling on its own */
-       win.width = width;
-       win.height = height;
-       if (width == sd->maxwidth)
-               win.quarter = 0;
-       else
-               win.quarter = 1;
-
-       /* Center it */
-       win.x = (win.width - width) / 2;
-       win.y = (win.height - height) / 2;
-
-       /* Clock is determined by OV519 frame rate code */
-       win.clockdiv = sd->clockdiv;
-
-       PDEBUG(D_CONF, "Setting clock divider to %d", win.clockdiv);
-       return set_ov_sensor_window(sd, &win);
-}
-
 /* -- start the camera -- */
 static void sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int ret;
 
-
-       ret = ov519_mode_init_regs(sd, gspca_dev->width, gspca_dev->height);
+       ret = ov519_mode_init_regs(sd);
        if (ret < 0)
                goto out;
-       ret = ov_sensor_mode_setup(sd, gspca_dev->width, gspca_dev->height);
+       ret = set_ov_sensor_window(sd);
        if (ret < 0)
                goto out;
 
-       ret = ov51x_restart((struct sd *) gspca_dev);
+       ret = ov51x_restart(sd);
        if (ret < 0)
                goto out;
        PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
@@ -1884,14 +1882,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
        ov51x_led_control((struct sd *) gspca_dev, 0);
 }
 
-static void sd_stop0(struct gspca_dev *gspca_dev)
-{
-}
-
-static void sd_close(struct gspca_dev *gspca_dev)
-{
-}
-
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        struct gspca_frame *frame,      /* target */
                        __u8 *data,                     /* isoc packet */
@@ -1942,12 +1932,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int val;
-/*     int was_streaming; */
 
        val = sd->brightness;
        PDEBUG(D_CONF, "brightness:%d", val);
-/*     was_streaming = gspca_dev->streaming;
- *     if (was_streaming)
+/*     if (gspca_dev->streaming)
  *             ov51x_stop(sd); */
        switch (sd->sensor) {
        case SEN_OV8610:
@@ -1965,12 +1953,12 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                        i2c_w(sd, OV7610_REG_BRT, val);
                break;
        case SEN_OV7670:
-/*jfm - from windblows
+/*win trace
  *             i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_AEC); */
                i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val));
                break;
        }
-/*     if (was_streaming)
+/*     if (gspca_dev->streaming)
  *             ov51x_restart(sd); */
 }
 
@@ -1978,12 +1966,10 @@ static void setcontrast(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int val;
-/*     int was_streaming; */
 
        val = sd->contrast;
        PDEBUG(D_CONF, "contrast:%d", val);
-/*     was_streaming = gspca_dev->streaming;
-       if (was_streaming)
+/*     if (gspca_dev->streaming)
                ov51x_stop(sd); */
        switch (sd->sensor) {
        case SEN_OV7610:
@@ -2020,7 +2006,7 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                i2c_w(sd, OV7670_REG_CONTRAS, val >> 1);
                break;
        }
-/*     if (was_streaming)
+/*     if (gspca_dev->streaming)
                ov51x_restart(sd); */
 }
 
@@ -2028,12 +2014,10 @@ static void setcolors(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int val;
-/*     int was_streaming; */
 
        val = sd->colors;
        PDEBUG(D_CONF, "saturation:%d", val);
-/*     was_streaming = gspca_dev->streaming;
-       if (was_streaming)
+/*     if (gspca_dev->streaming)
                ov51x_stop(sd); */
        switch (sd->sensor) {
        case SEN_OV8610:
@@ -2059,7 +2043,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
                /* set REG_COM13 values for UV sat auto mode */
                break;
        }
-/*     if (was_streaming)
+/*     if (gspca_dev->streaming)
                ov51x_restart(sd); */
 }
 
@@ -2114,36 +2098,67 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
        return 0;
 }
 
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hflip = val;
+       sethvflip(sd);
+       return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->hflip;
+       return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->vflip = val;
+       sethvflip(sd);
+       return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->vflip;
+       return 0;
+}
+
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
        .ctrls = sd_ctrls,
        .nctrls = ARRAY_SIZE(sd_ctrls),
        .config = sd_config,
-       .open = sd_open,
+       .init = sd_init,
        .start = sd_start,
        .stopN = sd_stopN,
-       .stop0 = sd_stop0,
-       .close = sd_close,
        .pkt_scan = sd_pkt_scan,
 };
 
 /* -- module initialisation -- */
-#define DVNM(name) .driver_info = (kernel_ulong_t) name
 static const __devinitdata struct usb_device_id device_table[] = {
-       {USB_DEVICE(0x041e, 0x4052), DVNM("Creative Live! VISTA IM")},
-       {USB_DEVICE(0x041e, 0x405f), DVNM("Creative Live! VISTA VF0330")},
-       {USB_DEVICE(0x041e, 0x4060), DVNM("Creative Live! VISTA VF0350")},
-       {USB_DEVICE(0x041e, 0x4061), DVNM("Creative Live! VISTA VF0400")},
-       {USB_DEVICE(0x041e, 0x4064), DVNM("Creative Live! VISTA VF0420")},
-       {USB_DEVICE(0x041e, 0x4068), DVNM("Creative Live! VISTA VF0470")},
-       {USB_DEVICE(0x045e, 0x028c), DVNM("Microsoft xbox cam")},
-       {USB_DEVICE(0x054c, 0x0154), DVNM("Sonny toy4")},
-       {USB_DEVICE(0x054c, 0x0155), DVNM("Sonny toy5")},
-       {USB_DEVICE(0x05a9, 0x0519), DVNM("OmniVision")},
-       {USB_DEVICE(0x05a9, 0x0530), DVNM("OmniVision")},
-       {USB_DEVICE(0x05a9, 0x4519), DVNM("OmniVision")},
-       {USB_DEVICE(0x05a9, 0x8519), DVNM("OmniVision")},
+       {USB_DEVICE(0x041e, 0x4052)},
+       {USB_DEVICE(0x041e, 0x405f)},
+       {USB_DEVICE(0x041e, 0x4060)},
+       {USB_DEVICE(0x041e, 0x4061)},
+       {USB_DEVICE(0x041e, 0x4064)},
+       {USB_DEVICE(0x041e, 0x4068)},
+       {USB_DEVICE(0x045e, 0x028c)},
+       {USB_DEVICE(0x054c, 0x0154)},
+       {USB_DEVICE(0x054c, 0x0155)},
+       {USB_DEVICE(0x05a9, 0x0519)},
+       {USB_DEVICE(0x05a9, 0x0530)},
+       {USB_DEVICE(0x05a9, 0x4519)},
+       {USB_DEVICE(0x05a9, 0x8519)},
        {}
 };
 #undef DVNAME
@@ -2162,6 +2177,10 @@ static struct usb_driver sd_driver = {
        .id_table = device_table,
        .probe = sd_probe,
        .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+       .suspend = gspca_suspend,
+       .resume = gspca_resume,
+#endif
 };
 
 /* -- module insert / remove -- */
@@ -2169,7 +2188,7 @@ static int __init sd_mod_init(void)
 {
        if (usb_register(&sd_driver) < 0)
                return -1;
-       PDEBUG(D_PROBE, "v%s registered", version);
+       PDEBUG(D_PROBE, "registered");
        return 0;
 }
 static void __exit sd_mod_exit(void)
@@ -2183,4 +2202,3 @@ module_exit(sd_mod_exit);
 
 module_param(frame_rate, int, 0644);
 MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)");
-