Add the OV7670 sensor driver (from Jonathan Corbet)

Andres Salomon dilinger at debian.unroutableorg
Wed Nov 29 11:14:54 EST 2006


Commit:     fcdbcf6204e89da5c0e45588dfbee2daf484ce9a
Parent:     65c0c5e3b42ab8f1099ac843f383b3e5078a3424
commit fcdbcf6204e89da5c0e45588dfbee2daf484ce9a
Author:     Andres Salomon <dilinger at debian.org>
AuthorDate: Fri Oct 6 15:32:18 2006 -0400
Commit:     Andres Salomon <dilinger at debian.org>
CommitDate: Fri Oct 6 15:32:18 2006 -0400

    Add the OV7670 sensor driver (from Jonathan Corbet)
---
 drivers/media/video/ovcamchip/Makefile         |    2 
 drivers/media/video/ovcamchip/ov7670.c         |  599 ++++++++++++++++++++++++
 drivers/media/video/ovcamchip/ovcamchip_core.c |   24 +
 include/media/ovcamchip.h                      |    3 
 4 files changed, 625 insertions(+), 3 deletions(-)

diff --git a/drivers/media/video/ovcamchip/Makefile b/drivers/media/video/ovcamchip/Makefile
index cba4cdf..2db0dfa 100644
--- a/drivers/media/video/ovcamchip/Makefile
+++ b/drivers/media/video/ovcamchip/Makefile
@@ -1,4 +1,4 @@
 ovcamchip-objs     := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \
-		      ov76be.o
+		      ov76be.o ov7670.o
 
 obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o
diff --git a/drivers/media/video/ovcamchip/ov7670.c b/drivers/media/video/ovcamchip/ov7670.c
new file mode 100644
index 0000000..2c679b5
--- /dev/null
+++ b/drivers/media/video/ovcamchip/ov7670.c
@@ -0,0 +1,599 @@
+/* OmniVision OV7670 Camera chip support code
+ *
+ * Liberally stolen from earlier drivers by Jonathan Corbet, thus:
+ *
+ * Copyright (c) 1999-2004 Mark McClelland <mark at alpha.dyndns.org>
+ * http://alpha.dyndns.org/ov511/
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied.
+ */
+
+#define DEBUG
+
+#include <linux/slab.h>
+#include "ovcamchip_priv.h"
+
+/* Registers */
+#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE	0x01	/* blue gain */
+#define REG_RED		0x02	/* red gain */
+#define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1	0x04	/* Control 1 */
+#define  COM1_CCIR656	  0x40  /* CCIR656 enable */
+#define REG_BAVE	0x05	/* U/B Average level */
+#define REG_GbAVE	0x06	/* Y/Gb Average level */
+#define REG_AECHH	0x07	/* AEC MS 5 bits */
+#define REG_RAVE	0x08	/* V/R Average level */
+#define REG_COM2	0x09	/* Control 2 */
+#define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
+#define REG_PID		0x0a	/* Product ID MSB */
+#define REG_VER		0x0b	/* Product ID LSB */
+#define REG_COM3	0x0c	/* Control 3 */
+#define  COM3_SWAP	  0x40	  /* Byte swap */
+#define  COM3_SCALEEN	  0x08	  /* Enable scaling */
+#define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
+#define REG_COM4	0x0d	/* Control 4 */
+#define REG_COM5	0x0e	/* All "reserved" */
+#define REG_COM6	0x0f	/* Control 6 */
+#define REG_AECH	0x10	/* More bits of AEC value */
+#define REG_CLKRC	0x11	/* Clocl control */
+#define   CLK_EXT	  0x40	  /* Use external clock directly */
+#define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
+#define REG_COM7	0x12	/* Control 7 */
+#define   COM7_RESET	  0x80	  /* Register reset */
+#define   COM7_FMT_MASK	  0x38
+#define   COM7_FMT_VGA	  0x00
+#define	  COM7_FMT_CIF	  0x20	  /* CIF format */
+#define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
+#define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
+#define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
+#define	  COM7_YUV	  0x00	  /* YUV */
+#define	  COM7_BAYER	  0x01	  /* Bayer format */
+#define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
+#define REG_COM8	0x13	/* Control 8 */
+#define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
+#define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
+#define   COM8_BFILT	  0x20	  /* Band filter enable */
+#define   COM8_AGC	  0x04	  /* Auto gain enable */
+#define   COM8_AWB	  0x02	  /* White balance enable */
+#define   COM8_AEC	  0x01	  /* Auto exposure enable */
+#define REG_COM9	0x14	/* Control 9  - gain ceiling */
+#define REG_COM10	0x15	/* Control 10 */
+#define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
+#define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
+#define   COM10_HREF_REV  0x08	  /* Reverse HREF */
+#define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
+#define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
+#define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
+#define REG_HSTART	0x17	/* Horiz start high bits */
+#define REG_HSTOP	0x18	/* Horiz stop high bits */
+#define REG_VSTART	0x19	/* Vert start high bits */
+#define REG_VSTOP	0x1a	/* Vert stop high bits */
+#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
+#define REG_MIDH	0x1c	/* Manuf. ID high */
+#define REG_MIDL	0x1d	/* Manuf. ID low */
+#define REG_MVFP	0x1e	/* Mirror / vflip */
+#define   MVFP_MIRROR	  0x20	  /* Mirror image */
+#define   MVFP_FLIP	  0x10	  /* Vertical flip */
+
+#define REG_AEW		0x24	/* AGC upper limit */
+#define REG_AEB		0x25	/* AGC lower limit */
+#define REG_VPT		0x26	/* AGC/AEC fast mode op region */
+#define REG_HSYST	0x30	/* HSYNC rising edge delay */
+#define REG_HSYEN	0x31	/* HSYNC falling edge delay */
+#define REG_HREF	0x32	/* HREF pieces */
+#define REG_TSLB	0x3a	/* lots of stuff */
+#define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
+#define REG_COM11	0x3b	/* Control 11 */
+#define   COM11_NIGHT	  0x80	  /* NIght mode enable */
+#define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
+#define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
+#define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
+#define   COM11_EXP	  0x02
+#define REG_COM12	0x3c	/* Control 12 */
+#define   COM12_HREF	  0x80	  /* HREF always */
+#define REG_COM13	0x3d	/* Control 13 */
+#define   COM13_GAMMA	  0x80	  /* Gamma enable */
+#define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
+#define REG_COM14	0x3e	/* Control 14 */
+#define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
+#define REG_EDGE	0x3f	/* Edge enhancement factor */
+#define REG_COM15	0x40	/* Control 15 */
+#define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
+#define	  COM15_R01FE	  0x80	  /*            01 to FE */
+#define   COM15_R00FF	  0xc0	  /*            00 to FF */
+#define   COM15_RBG565	  0x08	  /* RGB565 output */
+#define   COM15_RGB555	  0x18	  /* RGB555 output */
+#define REG_COM16	0x41	/* Control 16 */
+#define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
+#define REG_COM17	0x42	/* Control 17 */
+#define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
+#define   COM17_CBAR	  0x08	  /* DSP Color bar */
+
+#define REG_BRIGHT	0x55	/* Brightness */
+#define REG_CONTRAS	0x56	/* Contrast control */
+
+#define REG_GFIX	0x69	/* Fix gain control */
+
+#define REG_RGB444	0x8c	/* RGB 444 control */
+#define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
+#define   R444_RGBX	  0x01	  /* Empty nibble at end */
+
+#define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
+#define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
+
+#define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
+#define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
+#define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
+#define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
+#define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
+
+#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
+
+#define REG_BD60MAX	0xab	/* 60hz banding step limit */
+
+
+/* Default control settings. Values are in terms of V4L2 controls. */
+#define OV7120_DFL_BRIGHT     0x60
+#define OV7620_DFL_BRIGHT     0x60
+#define OV7120_DFL_SAT        0xb0
+#define OV7620_DFL_SAT        0xc0
+#define DFL_AUTO_EXP             1
+#define DFL_AUTO_GAIN            1
+#define OV7120_DFL_GAIN       0x00
+#define OV7620_DFL_GAIN       0x00
+/* NOTE: Since autoexposure is the default, these aren't programmed into the
+ * OV7x20 chip. They are just here because V4L2 expects a default */
+#define OV7120_DFL_EXP        0x7f
+#define OV7620_DFL_EXP        0x7f
+
+/*
+ * Window params, from the spec.
+ */
+#define HWSBASE 0x44	/* 7670 spec default - 11 bits */
+#define HWEBASE 0x308	/* spec default */
+#define VWSBASE 0x0c	/* spec default - 10 bits */
+#define VWEBASE 0x1ec	/* spec default */
+
+struct ov7670 {
+	int auto_brt;
+	int auto_exp;
+	int auto_gain;
+	int backlight;
+	int bandfilt;
+	int mirror;
+};
+
+
+/*
+ * Register settings.  This seems awfully short, compared to the others,
+ * there must be a dozen weird tweaks needed.
+ *
+ * "OV" indicates settings from Omnivision.
+ */
+static struct ovcamchip_regvals regvals_init_7670[] = {
+	{ REG_COM7, COM7_RESET },
+/*
+ * Clock scale: 3 = 15fps
+ *              2 = 20fps
+ *              1 = 30fps
+ */
+	{ REG_CLKRC, 0x1 },	/* OV: clock scale (15 fps) */
+	{ REG_TSLB,  0x04 },	/* OV */
+	{ REG_COM7, 0 },	/* VGA */
+/* For now: trying hardcoding OV's H/VSTART numbers, even though they
+ * don't make a lot of sense. */
+	{ REG_HSTART, 0x13 },
+	{ REG_HSTOP, 0x01 },
+	{ REG_HREF, 0xb6 },
+	{ REG_VSTART, 0x02 },
+	{ REG_VSTOP, 0x7a },
+	{ REG_VREF, 0x0a },
+
+	{ REG_COM3, 0 },
+	{ REG_COM14, 0 },
+	/* Mystery scaling numbers */
+	{ 0x70, 0x3a },
+	{ 0x71, 0x35 },
+	{ 0x72, 0x11 },
+	{ 0x72, 0xf0 },  /* "reserved" */
+	{ 0xa2, 0x01 },
+
+/* EXPERIMENT */
+	{ REG_COM10, 0x0 },
+//	{ REG_COM10, 0x40 },
+//	{ 0x30, 0x20 },
+//	{ 0x31, 0x20 },
+	
+	/* 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. */
+	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
+	{ REG_GAIN, 0 },
+	{ REG_AECH, 0 },
+	{ REG_COM4, 0x40 }, /* magic reserved bit */
+	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+	{ REG_BD50MAX, 0x05 },
+	{ REG_BD60MAX, 0x07 },
+	{ REG_AEW, 0x95 },
+	{ REG_AEB, 0x33 },
+	{ REG_VPT, 0xe3 },
+	{ REG_HAECC1, 0x78 },
+	{ REG_HAECC2, 0x68 },
+	{ 0xa1, 0x03 }, /* magic */
+	{ REG_HAECC3, 0xd8 },
+	{ REG_HAECC4, 0xd8 },
+	{ REG_HAECC5, 0xf0 },
+	{ REG_HAECC6, 0x90 },
+	{ REG_HAECC7, 0x94 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
+	
+	/* Almost all of these are magic "reserved" values.  */
+	{ REG_COM5, 0x61 },
+	{ REG_COM6, 0x4b },
+	{ 0x16, 0x02 },
+//	{ REG_MVFP, 0x07 },
+	{ REG_MVFP, 0x07 | MVFP_FLIP }, // Flip needed on dev system
+	{ 0x21, 0x02 },
+	{ 0x22, 0x91 },
+	{ 0x29, 0x07 },
+	{ 0x33, 0x0b },
+	{ 0x35, 0x0b },
+	{ 0x37, 0x1d },
+	{ 0x38, 0x71 },
+	{ 0x39, 0x2a },
+	{ REG_COM12, 0x78 },
+	{ 0x4d, 0x40 },
+	{ 0x4e, 0x20 },
+	{ 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 },
+	{ REG_BLUE, 0x40 },
+	{ REG_RED, 0x60 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
+
+	/* Matrix coefficients */
+	{ 0x4f, 0x80 },
+	{ 0x50, 0x80 },
+	{ 0x51, 0 },
+	{ 0x52, 0x22 },
+	{ 0x53, 0x5e },
+	{ 0x54, 0x80 },
+	{ 0x58, 0x9e },
+
+	{ REG_COM16, COM16_AWBGAIN },
+	{ REG_EDGE, 0 },
+	{ 0x75, 0x05 },
+	{ 0x76, 0xe1 },
+	{ 0x4c, 0 },
+	{ 0x77, 0x01 },
+	{ REG_COM13, 0xc3 }, /* rsvd bits */
+	{ 0x4b, 0x09 },
+	{ 0xc9, 0x60 },
+	{ REG_COM16, 0x38 },
+	{ 0x56, 0x40 },
+
+	{ 0x34, 0x11 },
+	{ REG_COM11, COM11_EXP },
+	{ 0xa4, 0x88 },
+	{ 0x96, 0 },
+	{ 0x97, 0x30 },
+	{ 0x98, 0x20 },
+	{ 0x99, 0x30 },
+	{ 0x9a, 0x84 },
+	{ 0x9b, 0x29 },
+	{ 0x9c, 0x03 },
+	{ 0x9d, 0x4c },
+	{ 0x9e, 0x3f },
+	{ 0x78, 0x04 },
+
+	/* Now for the really weird shit. */
+	{ 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 },
+
+	/* Not sure if these should be here */
+	{ 0xf1, 0x10 },
+	{ 0x0f, 0x1d },
+	{ 0x0f, 0x1f },
+
+
+//	{ REG_COM10, 0 }, /* HREF, don't reverse anything */
+				    /* 7620 sets href negative */
+//	{ REG_PSHFT, 0 },  	/* No pixel delay after href */
+//	{ REG_COM11, COM11_HZAUTO }, /* Auto freq detect */
+//	{ REG_COM13, COM13_GAMMA },
+
+	{ 0xff, 0xff },	/* END MARKER */
+};
+
+
+/* This initializes the OV7670 camera chip and relevant variables. */
+static int ov7670_init(struct i2c_client *c)
+{
+	struct ovcamchip *ov = i2c_get_clientdata(c);
+	struct ov7670 *s;
+	int rc;
+
+	DDEBUG(4, &c->dev, "entered");
+
+	rc = ov_write_regvals(c, regvals_init_7670);
+	if (rc < 0)
+		return rc;
+
+	ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL);
+	if (!s)
+		return -ENOMEM;
+
+	s->auto_brt = 1;
+	s->auto_exp = DFL_AUTO_EXP;
+	s->auto_gain = DFL_AUTO_GAIN;
+
+	return 0;
+}
+
+static int ov7670_free(struct i2c_client *c)
+{
+	struct ovcamchip *ov = i2c_get_clientdata(c);
+
+	kfree(ov->spriv);
+	return 0;
+}
+
+static int ov7670_set_v4l1_control(struct i2c_client *c,
+				   struct ovcamchip_control *ctl)
+{
+	struct ovcamchip *ov = i2c_get_clientdata(c);
+	struct ov7670 *s = ov->spriv;
+	int rc;
+	int v = ctl->value;
+
+	switch (ctl->id) {
+	case OVCAMCHIP_CID_CONT:
+		/* This is a total guess */
+		rc = ov_write(c, REG_CONTRAS, v >> 8);
+		break;
+	case OVCAMCHIP_CID_BRIGHT:
+		/* 7620 doesn't like manual changes when in auto mode */
+    		/* For 7670, guessing again */
+		if (!s->auto_brt)
+			rc = ov_write(c, REG_BRIGHT, v >> 8);
+		else
+			rc = 0;
+		break;
+#if 0 /* Not sure how to do this */
+	case OVCAMCHIP_CID_SAT:
+		rc = ov_write(c, REG_SAT, v >> 8);
+		break;
+#endif
+	case OVCAMCHIP_CID_EXP:
+	    	/*
+		 * Exposure is split among three registers.
+		 */
+	    	if (!s->auto_exp) {
+			rc = ov_write_mask(c, REG_COM1, v, 0x3);
+			rc += ov_write(c, REG_AECH, (v >> 2) & 0xff);
+			rc += ov_write_mask(c, REG_AECHH, v >> 10, 0x3f);
+		}
+		else
+			rc = -EBUSY;
+		break;
+	case OVCAMCHIP_CID_FREQ:
+	    	/*
+		 * Turn off autodetect in the process.
+		 */
+		if (v == 60)
+			rc = ov_write_mask(c, REG_COM11, 0, 0x18);
+		else
+			rc = ov_write_mask(c, REG_COM11, COM11_50HZ, COM11_50HZ);
+		break;
+	case OVCAMCHIP_CID_BANDFILT:
+		rc = ov_write_mask(c, REG_COM8, v ? COM8_BFILT : 0x00, COM8_BFILT);
+		s->bandfilt = v;
+		break;
+	case OVCAMCHIP_CID_AUTOBRIGHT:
+	    	rc = ov_write_mask(c, REG_COM8, v ? COM8_AGC : 0x00, COM8_AGC);
+		s->auto_brt = v;
+		break;
+	case OVCAMCHIP_CID_AUTOEXP:
+	    	rc = ov_write_mask(c, REG_COM8, v ? COM8_AEC : 0x00, COM8_AEC);
+		s->auto_exp = v;
+		break;
+	case OVCAMCHIP_CID_MIRROR:
+		rc = ov_write_mask(c, REG_MVFP, v ? MVFP_MIRROR : 0x00,
+				MVFP_MIRROR);
+		s->mirror = v;
+		break;
+	default:
+		DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
+		return -EPERM;
+	}
+
+	DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc);
+	return rc;
+}
+
+static int ov7670_get_v4l1_control(struct i2c_client *c,
+				   struct ovcamchip_control *ctl)
+{
+	struct ovcamchip *ov = i2c_get_clientdata(c);
+	struct ov7670 *s = ov->spriv;
+	int rc = 0;
+	unsigned char val = 0, v2, v3;
+
+	switch (ctl->id) {
+	case OVCAMCHIP_CID_CONT:
+		rc = ov_read(c, REG_CONTRAS, &val);
+		ctl->value = val << 8;
+		break;
+	case OVCAMCHIP_CID_BRIGHT:
+		rc = ov_read(c, REG_BRIGHT, &val);
+		ctl->value = val << 8;
+		break;
+#if 0
+	case OVCAMCHIP_CID_SAT:
+		rc = ov_read(c, REG_SAT, &val);
+		ctl->value = val << 8;
+		break;
+#endif
+	case OVCAMCHIP_CID_EXP:
+		rc = ov_read(c, REG_COM1, &val);
+		rc += ov_read(c, REG_AECH, &v2);
+		rc += ov_read(c, REG_AECHH, &v3);
+		ctl->value = (val & 0x3) | (v2 << 2) | ((v3 & 0x3f) << 10);
+		break;
+	/*
+	 * Why doesn't it just read these like the rest?
+	 */
+	case OVCAMCHIP_CID_BANDFILT:
+		ctl->value = s->bandfilt;
+		break;
+	case OVCAMCHIP_CID_AUTOBRIGHT:
+		ctl->value = s->auto_brt;
+		break;
+	case OVCAMCHIP_CID_AUTOEXP:
+		ctl->value = s->auto_exp;
+		break;
+	case OVCAMCHIP_CID_BACKLIGHT:
+		ctl->value = s->backlight;
+		break;
+	case OVCAMCHIP_CID_MIRROR:
+		ctl->value = s->mirror;
+		break;
+	default:
+		DDEBUG(2, &c->dev, "control not supported: %d", ctl->id);
+		return -EPERM;
+	}
+
+	DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc);
+	return rc;
+}
+
+
+static int ov7670_set_window(struct i2c_client *c, struct ovcamchip_window *win)
+{
+/* int lsb; */
+
+	if (win->width == 176) 
+		ov_write_mask(c, REG_COM7, COM7_FMT_QCIF, COM7_FMT_MASK);
+	else if (win->width == 320)
+		ov_write_mask(c, REG_COM7, COM7_FMT_QVGA, COM7_FMT_MASK);
+	else if (win->width == 352)
+		ov_write_mask(c, REG_COM7, COM7_FMT_CIF, COM7_FMT_MASK);
+	else
+		ov_write_mask(c, REG_COM7, COM7_FMT_VGA, COM7_FMT_MASK);
+	/*
+	 * 7670 seems to be more rational than some with how the
+	 * sizing parameters are set - no scaling.  The down side is
+	 * that they get split across multiple registers.
+	 */
+#ifdef notdef
+	ov_write(c, REG_HSTART, HWSBASE >> 3); /* deal with x/y later */
+	ov_write(c, REG_HSTOP, (HWSBASE + win->width) >> 3);
+	lsb = (HWSBASE&0x7) | (((HWSBASE + win->width)&0x7) << 3);
+	ov_write_mask(c, REG_HREF, lsb, 0x3f);
+
+	ov_write(c, REG_VSTART, VWSBASE >> 2);
+	ov_write(c, REG_VSTOP, (VWSBASE+win->height) >> 2);
+	lsb = (VWSBASE&0x3) | (((VWSBASE + win->height)&0x3) << 2);
+	ov_write_mask(c, REG_VREF, lsb, 0xf);
+#endif
+/* Try the OV-supplied numbers for now, regardless of whether they make
+   any sense at all */
+
+	return 0;
+}
+
+static int ov7670_command(struct i2c_client *c, unsigned int cmd, void *arg)
+{
+	switch (cmd) {
+	case OVCAMCHIP_CMD_S_CTRL:
+		return ov7670_set_v4l1_control(c, arg);
+	case OVCAMCHIP_CMD_G_CTRL:
+		return ov7670_get_v4l1_control(c, arg);
+	case OVCAMCHIP_CMD_S_MODE:
+		return ov7670_set_window(c, arg);
+	default:
+		DDEBUG(2, &c->dev, "command not supported: %d", cmd);
+		return -ENOIOCTLCMD;
+	}
+}
+
+struct ovcamchip_ops ov7670_ops = {
+	.init    =	ov7670_init,
+	.free    =	ov7670_free,
+	.command =	ov7670_command,
+};
diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c
index 3fe9fa0..7598100 100644
--- a/drivers/media/video/ovcamchip/ovcamchip_core.c
+++ b/drivers/media/video/ovcamchip/ovcamchip_core.c
@@ -46,6 +46,8 @@ MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 /* Registers common to all chips, that are needed for detection */
+#define GENERIC_REG_PID_HIGH	  0x0a	/* Product ID */
+#define GENERIC_REG_PID_LOW	  0x0b
 #define GENERIC_REG_ID_HIGH       0x1C	/* manufacturer ID MSB */
 #define GENERIC_REG_ID_LOW        0x1D	/* manufacturer ID LSB */
 #define GENERIC_REG_COM_I         0x29	/* misc ID bits */
@@ -55,6 +57,7 @@ extern struct ovcamchip_ops ov6x30_ops;
 extern struct ovcamchip_ops ov7x10_ops;
 extern struct ovcamchip_ops ov7x20_ops;
 extern struct ovcamchip_ops ov76be_ops;
+extern struct ovcamchip_ops ov7670_ops;
 
 static char *chip_names[NUM_CC_TYPES] = {
 	[CC_UNKNOWN]	= "Unknown chip",
@@ -66,6 +69,7 @@ static char *chip_names[NUM_CC_TYPES] = 
 	[CC_OV6630]	= "OV6630",
 	[CC_OV6630AE]	= "OV6630AE",
 	[CC_OV6630AF]	= "OV6630AF",
+	[CC_OV7670]	= "OV7670"
 };
 
 /* Forward declarations */
@@ -164,10 +168,27 @@ static int ov7xx0_detect(struct i2c_clie
 {
 	struct ovcamchip *ov = i2c_get_clientdata(c);
 	int rc;
-	unsigned char val;
+	unsigned char val, val2;
 
 	PDEBUG(4, "");
 
+	/*
+	 * Detect the 7670 via the product ID registers.  Not sure why
+	 * they aren't all done this way.
+	 */
+	rc = ov_read(c, GENERIC_REG_PID_HIGH, &val) +
+		ov_read(c, GENERIC_REG_PID_LOW, &val2);
+	if (rc < 0) {
+		PERROR("Error reading 0v7xx0 PID regs");
+		return -1;
+	}
+	if (val == 0x76 && val2 == 0x73) {
+		ov->subtype = CC_OV7670;
+		ov->sops = &ov7670_ops;	
+		PINFO("Camera chip is an OV7670");
+		return 0;
+	}
+
 	/* Detect chip (sub)type */
 	rc = ov_read(c, GENERIC_REG_COM_I, &val);
 	if (rc < 0) {
@@ -300,6 +321,7 @@ static int ovcamchip_attach(struct i2c_a
 	case I2C_HW_SMBUS_OV518:
 	case I2C_HW_SMBUS_OVFX2:
 	case I2C_HW_SMBUS_W9968CF:
+	case I2C_HW_SMBUS_M88ALP01:
 		PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id);
 		break;
 	default:
diff --git a/include/media/ovcamchip.h b/include/media/ovcamchip.h
index 0f43451..6940a01 100644
--- a/include/media/ovcamchip.h
+++ b/include/media/ovcamchip.h
@@ -38,7 +38,7 @@ enum {
 };
 
 /* Chip types */
-#define NUM_CC_TYPES	9
+#define NUM_CC_TYPES	10
 enum {
 	CC_UNKNOWN,
 	CC_OV76BE,
@@ -49,6 +49,7 @@ enum {
 	CC_OV6630,
 	CC_OV6630AE,
 	CC_OV6630AF,
+	CC_OV7670
 };
 
 /* --------------------------------- */


More information about the Commits-kernel mailing list