Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42e142f6 authored by Jean-François Moine's avatar Jean-François Moine Committed by Mauro Carvalho Chehab
Browse files

[media] gspca - ov519: New sensor ov7660 with bridge ov530 (ov519)



[mchehab@redhat.com: Some CodingStyle fixes]
Tested-by: default avatarAnca Emanuel <anca.emanuel@gmail.com>
Signed-off-by: default avatarJean-François Moine <moinejf@free.fr>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 7491f785
Loading
Loading
Loading
Loading
+386 −8
Original line number Diff line number Diff line
@@ -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);