[media] gspca - ov519: New sensor ov7660 with bridge ov530 (ov519)
authorJean-François Moine <moinejf@free.fr>
Sat, 13 Nov 2010 08:10:27 +0000 (05:10 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 29 Dec 2010 10:16:44 +0000 (08:16 -0200)
[mchehab@redhat.com: Some CodingStyle fixes]
Tested-by: Anca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/ov519.c

index 08f07c3..0ed21db 100644 (file)
@@ -82,7 +82,7 @@ struct sd {
 #define BRIDGE_OV511PLUS       1
 #define BRIDGE_OV518           2
 #define BRIDGE_OV518PLUS       3
-#define BRIDGE_OV519           4
+#define BRIDGE_OV519           4               /* = ov530 */
 #define BRIDGE_OVFX2           5
 #define BRIDGE_W9968CF         6
 #define BRIDGE_MASK            7
@@ -127,6 +127,7 @@ enum sensors {
        SEN_OV7620AE,
        SEN_OV7640,
        SEN_OV7648,
+       SEN_OV7660,
        SEN_OV7670,
        SEN_OV76BE,
        SEN_OV8610,
@@ -183,7 +184,7 @@ static const struct ctrl sd_ctrls[] = {
            },
            .set_control = setcolors,
        },
-/* The flip controls work with ov7670 only */
+/* The flip controls work for sensors ov7660 and ov7670 only */
 [HFLIP] = {
            {
                .id      = V4L2_CID_HFLIP,
@@ -268,6 +269,8 @@ static const unsigned ctrl_dis[] = {
                        (1 << AUTOBRIGHT) |
                        (1 << CONTRAST),
 
+[SEN_OV7660] =         (1 << AUTOBRIGHT),
+
 [SEN_OV7670] =         (1 << COLORS) |
                        (1 << AUTOBRIGHT),
 
@@ -572,7 +575,7 @@ static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
 #define OV7610_REG_ID_LOW      0x1d    /* manufacturer ID LSB */
 #define OV7610_REG_COM_I       0x29    /* misc settings */
 
-/* OV7670 registers */
+/* OV7660 and OV7670 registers */
 #define OV7670_R00_GAIN                0x00    /* Gain lower 8 bits (rest in vref) */
 #define OV7670_R01_BLUE                0x01    /* blue gain */
 #define OV7670_R02_RED         0x02    /* red gain */
@@ -625,6 +628,7 @@ static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
 /*#define   OV7670_COM15_R00FF  0xc0    *      00 to FF */
 #define OV7670_R41_COM16       0x41    /* Control 16 */
 #define   OV7670_COM16_AWBGAIN  0x08   /* AWB gain enable */
+/* end of ov7660 common registers */
 #define OV7670_R55_BRIGHT      0x55    /* Brightness */
 #define OV7670_R56_CONTRAS     0x56    /* Contrast control */
 #define OV7670_R69_GFIX                0x69    /* Fix gain control */
@@ -1577,6 +1581,150 @@ static const struct ov_i2c_regvals norm_7640[] = {
        { 0x12, 0x14 },
 };
 
+static const struct ov_regvals init_519_ov7660[] = {
+       { 0x5d, 0x03 }, /* Turn off suspend mode */
+       { 0x53, 0x9b }, /* 0x9f enables the (unused) microcontroller */
+       { 0x54, 0x0f }, /* bit2 (jpeg enable) */
+       { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+       { 0xa3, 0x18 },
+       { 0xa4, 0x04 },
+       { 0xa5, 0x28 },
+       { 0x37, 0x00 }, /* SetUsbInit */
+       { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+       /* Enable both fields, YUV Input, disable defect comp (why?) */
+       { 0x20, 0x0c }, /* 0x0d does U <-> V swap */
+       { 0x21, 0x38 },
+       { 0x22, 0x1d },
+       { 0x17, 0x50 }, /* undocumented */
+       { 0x37, 0x00 }, /* undocumented */
+       { 0x40, 0xff }, /* I2C timeout counter */
+       { 0x46, 0x00 }, /* I2C clock prescaler */
+};
+static const struct ov_i2c_regvals norm_7660[] = {
+       {OV7670_R12_COM7, OV7670_COM7_RESET},
+       {OV7670_R11_CLKRC, 0x81},
+       {0x92, 0x00},                   /* DM_LNL */
+       {0x93, 0x00},                   /* DM_LNH */
+       {0x9d, 0x4c},                   /* BD50ST */
+       {0x9e, 0x3f},                   /* BD60ST */
+       {OV7670_R3B_COM11, 0x02},
+       {OV7670_R13_COM8, 0xf5},
+       {OV7670_R10_AECH, 0x00},
+       {OV7670_R00_GAIN, 0x00},
+       {OV7670_R01_BLUE, 0x7c},
+       {OV7670_R02_RED, 0x9d},
+       {OV7670_R12_COM7, 0x00},
+       {OV7670_R04_COM1, 00},
+       {OV7670_R18_HSTOP, 0x01},
+       {OV7670_R17_HSTART, 0x13},
+       {OV7670_R32_HREF, 0x92},
+       {OV7670_R19_VSTART, 0x02},
+       {OV7670_R1A_VSTOP, 0x7a},
+       {OV7670_R03_VREF, 0x00},
+       {OV7670_R0E_COM5, 0x04},
+       {OV7670_R0F_COM6, 0x62},
+       {OV7670_R15_COM10, 0x00},
+       {0x16, 0x02},                   /* RSVD */
+       {0x1b, 0x00},                   /* PSHFT */
+       {OV7670_R1E_MVFP, 0x01},
+       {0x29, 0x3c},                   /* RSVD */
+       {0x33, 0x00},                   /* CHLF */
+       {0x34, 0x07},                   /* ARBLM */
+       {0x35, 0x84},                   /* RSVD */
+       {0x36, 0x00},                   /* RSVD */
+       {0x37, 0x04},                   /* ADC */
+       {0x39, 0x43},                   /* OFON */
+       {OV7670_R3A_TSLB, 0x00},
+       {OV7670_R3C_COM12, 0x6c},
+       {OV7670_R3D_COM13, 0x98},
+       {OV7670_R3F_EDGE, 0x23},
+       {OV7670_R40_COM15, 0xc1},
+       {OV7670_R41_COM16, 0x22},
+       {0x6b, 0x0a},                   /* DBLV */
+       {0xa1, 0x08},                   /* RSVD */
+       {0x69, 0x80},                   /* HV */
+       {0x43, 0xf0},                   /* RSVD.. */
+       {0x44, 0x10},
+       {0x45, 0x78},
+       {0x46, 0xa8},
+       {0x47, 0x60},
+       {0x48, 0x80},
+       {0x59, 0xba},
+       {0x5a, 0x9a},
+       {0x5b, 0x22},
+       {0x5c, 0xb9},
+       {0x5d, 0x9b},
+       {0x5e, 0x10},
+       {0x5f, 0xe0},
+       {0x60, 0x85},
+       {0x61, 0x60},
+       {0x9f, 0x9d},                   /* RSVD */
+       {0xa0, 0xa0},                   /* DSPC2 */
+       {0x4f, 0x60},                   /* matrix */
+       {0x50, 0x64},
+       {0x51, 0x04},
+       {0x52, 0x18},
+       {0x53, 0x3c},
+       {0x54, 0x54},
+       {0x55, 0x40},
+       {0x56, 0x40},
+       {0x57, 0x40},
+       {0x58, 0x0d},                   /* matrix sign */
+       {0x8b, 0xcc},                   /* RSVD */
+       {0x8c, 0xcc},
+       {0x8d, 0xcf},
+       {0x6c, 0x40},                   /* gamma curve */
+       {0x6d, 0xe0},
+       {0x6e, 0xa0},
+       {0x6f, 0x80},
+       {0x70, 0x70},
+       {0x71, 0x80},
+       {0x72, 0x60},
+       {0x73, 0x60},
+       {0x74, 0x50},
+       {0x75, 0x40},
+       {0x76, 0x38},
+       {0x77, 0x3c},
+       {0x78, 0x32},
+       {0x79, 0x1a},
+       {0x7a, 0x28},
+       {0x7b, 0x24},
+       {0x7c, 0x04},                   /* gamma curve */
+       {0x7d, 0x12},
+       {0x7e, 0x26},
+       {0x7f, 0x46},
+       {0x80, 0x54},
+       {0x81, 0x64},
+       {0x82, 0x70},
+       {0x83, 0x7c},
+       {0x84, 0x86},
+       {0x85, 0x8e},
+       {0x86, 0x9c},
+       {0x87, 0xab},
+       {0x88, 0xc4},
+       {0x89, 0xd1},
+       {0x8a, 0xe5},
+       {OV7670_R14_COM9, 0x1e},
+       {OV7670_R24_AEW, 0x80},
+       {OV7670_R25_AEB, 0x72},
+       {OV7670_R26_VPT, 0xb3},
+       {0x62, 0x80},                   /* LCC1 */
+       {0x63, 0x80},                   /* LCC2 */
+       {0x64, 0x06},                   /* LCC3 */
+       {0x65, 0x00},                   /* LCC4 */
+       {0x66, 0x01},                   /* LCC5 */
+       {0x94, 0x0e},                   /* RSVD.. */
+       {0x95, 0x14},
+       {OV7670_R13_COM8, OV7670_COM8_FASTAEC
+                       | OV7670_COM8_AECSTEP
+                       | OV7670_COM8_BFILT
+                       | 0x10
+                       | OV7670_COM8_AGC
+                       | OV7670_COM8_AWB
+                       | OV7670_COM8_AEC},
+       {0xa1, 0xc8}
+};
+
 /* 7670. Defaults taken from OmniVision provided data,
 *  as provided by Jonathan Corbet of OLPC              */
 static const struct ov_i2c_regvals norm_7670[] = {
@@ -2574,6 +2722,11 @@ static void ov7xx0_configure(struct sd *sd)
                                PDEBUG(D_PROBE, "Sensor is an OV7648");
                                sd->sensor = SEN_OV7648;
                                break;
+                       case 0x60:
+                               PDEBUG(D_PROBE, "Sensor is a OV7660");
+                               sd->sensor = SEN_OV7660;
+                               sd->invert_led = 0;
+                               break;
                        default:
                                PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
                                return;
@@ -2935,6 +3088,91 @@ static void ovfx2_configure(struct sd *sd)
        write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
 }
 
+/* set the mode */
+/* This function works for ov7660 only */
+static void ov519_set_mode(struct sd *sd)
+{
+       static const struct ov_regvals bridge_ov7660[2][10] = {
+               {{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00},
+                {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+                {0x25, 0x01}, {0x26, 0x00}},
+               {{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00},
+                {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+                {0x25, 0x03}, {0x26, 0x00}}
+       };
+       static const struct ov_i2c_regvals sensor_ov7660[2][3] = {
+               {{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}},
+               {{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}}
+       };
+       static const struct ov_i2c_regvals sensor_ov7660_2[] = {
+               {OV7670_R17_HSTART, 0x13},
+               {OV7670_R18_HSTOP, 0x01},
+               {OV7670_R32_HREF, 0x92},
+               {OV7670_R19_VSTART, 0x02},
+               {OV7670_R1A_VSTOP, 0x7a},
+               {OV7670_R03_VREF, 0x00},
+/*             {0x33, 0x00}, */
+/*             {0x34, 0x07}, */
+/*             {0x36, 0x00}, */
+/*             {0x6b, 0x0a}, */
+       };
+
+       write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode],
+                       ARRAY_SIZE(bridge_ov7660[0]));
+       write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode],
+                       ARRAY_SIZE(sensor_ov7660[0]));
+       write_i2c_regvals(sd, sensor_ov7660_2,
+                       ARRAY_SIZE(sensor_ov7660_2));
+}
+
+/* set the frame rate */
+/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */
+static void ov519_set_fr(struct sd *sd)
+{
+       int fr;
+       u8 clock;
+       /* frame rate table with indices:
+        *      - mode = 0: 320x240, 1: 640x480
+        *      - fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5
+        *      - reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock)
+        */
+       static const u8 fr_tb[2][6][3] = {
+               {{0x04, 0xff, 0x00},
+                {0x04, 0x1f, 0x00},
+                {0x04, 0x1b, 0x00},
+                {0x04, 0x15, 0x00},
+                {0x04, 0x09, 0x00},
+                {0x04, 0x01, 0x00}},
+               {{0x0c, 0xff, 0x00},
+                {0x0c, 0x1f, 0x00},
+                {0x0c, 0x1b, 0x00},
+                {0x04, 0xff, 0x01},
+                {0x04, 0x1f, 0x01},
+                {0x04, 0x1b, 0x01}},
+       };
+
+       if (frame_rate > 0)
+               sd->frame_rate = frame_rate;
+       if (sd->frame_rate >= 30)
+               fr = 0;
+       else if (sd->frame_rate >= 25)
+               fr = 1;
+       else if (sd->frame_rate >= 20)
+               fr = 2;
+       else if (sd->frame_rate >= 15)
+               fr = 3;
+       else if (sd->frame_rate >= 10)
+               fr = 4;
+       else
+               fr = 5;
+       reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]);
+       reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]);
+       clock = fr_tb[sd->gspca_dev.curr_mode][fr][2];
+       if (sd->sensor == SEN_OV7660)
+               clock |= 0x80;          /* enable double clock */
+       ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
+}
+
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
@@ -3118,6 +3356,34 @@ static int sd_init(struct gspca_dev *gspca_dev)
        case SEN_OV7648:
                write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640));
                break;
+       case SEN_OV7660:
+               i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET);
+               msleep(14);
+               reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+               write_regvals(sd, init_519_ov7660,
+                               ARRAY_SIZE(init_519_ov7660));
+               write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660));
+               sd->gspca_dev.curr_mode = 1;    /* 640x480 */
+               sd->frame_rate = 15;
+               ov519_set_mode(sd);
+               ov519_set_fr(sd);
+               sd->ctrls[COLORS].max = 4;      /* 0..4 */
+               sd->ctrls[COLORS].val =
+                       sd->ctrls[COLORS].def = 2;
+               setcolors(gspca_dev);
+               sd->ctrls[CONTRAST].max = 6;    /* 0..6 */
+               sd->ctrls[CONTRAST].val =
+                       sd->ctrls[CONTRAST].def = 3;
+               setcontrast(gspca_dev);
+               sd->ctrls[BRIGHTNESS].max = 6;  /* 0..6 */
+               sd->ctrls[BRIGHTNESS].val =
+                       sd->ctrls[BRIGHTNESS].def = 3;
+               setbrightness(gspca_dev);
+               sd_reset_snapshot(gspca_dev);
+               ov51x_restart(sd);
+               ov51x_stop(sd);                 /* not in win traces */
+               ov51x_led_control(sd, 0);
+               break;
        case SEN_OV7670:
                sd->ctrls[FREQ].max = 3;        /* auto */
                sd->ctrls[FREQ].def = 3;
@@ -3431,16 +3697,21 @@ static void ov519_mode_init_regs(struct sd *sd)
        };
 
        /******** Set the mode ********/
-       if (sd->sensor != SEN_OV7670) {
+       switch (sd->sensor) {
+       default:
                write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519));
                if (sd->sensor == SEN_OV7640 ||
                    sd->sensor == SEN_OV7648) {
                        /* Select 8-bit input mode */
                        reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
                }
-       } else {
+               break;
+       case SEN_OV7660:
+               return;         /* done by ov519_set_mode/fr() */
+       case SEN_OV7670:
                write_regvals(sd, mode_init_519_ov7670,
                                ARRAY_SIZE(mode_init_519_ov7670));
+               break;
        }
 
        reg_w(sd, OV519_R10_H_SIZE,     sd->gspca_dev.width >> 4);
@@ -3682,6 +3953,7 @@ static void mode_init_ov_sensor_regs(struct sd *sd)
        i2c_w(sd, 0x11, sd->clockdiv);
 }
 
+/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
 static void sethvflip(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -3703,11 +3975,18 @@ static void set_ov_sensor_window(struct sd *sd)
        int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
 
        /* mode setup is fully handled in mode_init_ov_sensor_regs for these */
-       if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 ||
-           sd->sensor == SEN_OV7670) {
+       switch (sd->sensor) {
+       case SEN_OV2610:
+       case SEN_OV3610:
+       case SEN_OV7670:
                mode_init_ov_sensor_regs(sd);
                return;
+       case SEN_OV7660:
+               ov519_set_mode(sd);
+               ov519_set_fr(sd);
+               return;
        }
+
        gspca_dev = &sd->gspca_dev;
        qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
        crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2;
@@ -4101,6 +4380,22 @@ static void setbrightness(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int val;
+       static const struct ov_i2c_regvals brit_7660[][7] = {
+               {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
+                       {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
+               {{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1},
+                       {0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}},
+               {{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2},
+                       {0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}},
+               {{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3},
+                       {0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}},
+               {{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3},
+                       {0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}},
+               {{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3},
+                       {0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}},
+               {{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4},
+                       {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
+       };
 
        val = sd->ctrls[BRIGHTNESS].val;
        switch (sd->sensor) {
@@ -4120,6 +4415,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                if (!sd->ctrls[AUTOBRIGHT].val)
                        i2c_w(sd, OV7610_REG_BRT, val);
                break;
+       case SEN_OV7660:
+               write_i2c_regvals(sd, brit_7660[val],
+                               ARRAY_SIZE(brit_7660[0]));
+               break;
        case SEN_OV7670:
 /*win trace
  *             i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */
@@ -4132,6 +4431,64 @@ static void setcontrast(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int val;
+       static const struct ov_i2c_regvals contrast_7660[][31] = {
+               {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
+                {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
+                {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24},
+                {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34},
+                {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65},
+                {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83},
+                {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f},
+                {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}},
+               {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94},
+                {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30},
+                {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24},
+                {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31},
+                {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62},
+                {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81},
+                {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1},
+                {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}},
+               {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84},
+                {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40},
+                {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24},
+                {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34},
+                {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d},
+                {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81},
+                {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e},
+                {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}},
+               {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70},
+                {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48},
+                {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34},
+                {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22},
+                {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58},
+                {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80},
+                {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9},
+                {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}},
+               {{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80},
+                {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60},
+                {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38},
+                {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e},
+                {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46},
+                {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c},
+                {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4},
+                {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}},
+               {{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80},
+                {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30},
+                {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50},
+                {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08},
+                {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a},
+                {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b},
+                {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3},
+                {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}},
+               {{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60},
+                {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8},
+                {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c},
+                {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04},
+                {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22},
+                {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b},
+                {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde},
+                {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
+       };
 
        val = sd->ctrls[CONTRAST].val;
        switch (sd->sensor) {
@@ -4163,6 +4520,10 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                i2c_w(sd, 0x64, ctab[val >> 4]);
                break;
            }
+       case SEN_OV7660:
+               write_i2c_regvals(sd, contrast_7660[val],
+                                       ARRAY_SIZE(contrast_7660[0]));
+               break;
        case SEN_OV7670:
                /* check that this isn't just the same as ov7610 */
                i2c_w(sd, OV7670_R56_CONTRAS, val >> 1);
@@ -4174,6 +4535,18 @@ static void setcolors(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int val;
+       static const struct ov_i2c_regvals colors_7660[][6] = {
+               {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
+                {0x53, 0x19}, {0x54, 0x23}},
+               {{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11},
+                {0x53, 0x2c}, {0x54, 0x3e}},
+               {{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19},
+                {0x53, 0x40}, {0x54, 0x59}},
+               {{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20},
+                {0x53, 0x53}, {0x54, 0x73}},
+               {{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28},
+                {0x53, 0x66}, {0x54, 0x8e}},
+       };
 
        val = sd->ctrls[COLORS].val;
        switch (sd->sensor) {
@@ -4197,6 +4570,10 @@ static void setcolors(struct gspca_dev *gspca_dev)
        case SEN_OV7648:
                i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
                break;
+       case SEN_OV7660:
+               write_i2c_regvals(sd, colors_7660[val],
+                                       ARRAY_SIZE(colors_7660[0]));
+               break;
        case SEN_OV7670:
                /* supported later once I work out how to do it
                 * transparently fail now! */
@@ -4214,7 +4591,8 @@ static void setautobright(struct gspca_dev *gspca_dev)
 
 static void setfreq_i(struct sd *sd)
 {
-       if (sd->sensor == SEN_OV7670) {
+       if (sd->sensor == SEN_OV7660
+        || sd->sensor == SEN_OV7670) {
                switch (sd->ctrls[FREQ].val) {
                case 0: /* Banding filter disabled */
                        i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);