xref: /openbmc/linux/drivers/media/i2c/ov7670.c (revision cb7a01ac)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * A V4L2 driver for OmniVision OV7670 cameras.
3cb7a01acSMauro Carvalho Chehab  *
4cb7a01acSMauro Carvalho Chehab  * Copyright 2006 One Laptop Per Child Association, Inc.  Written
5cb7a01acSMauro Carvalho Chehab  * by Jonathan Corbet with substantial inspiration from Mark
6cb7a01acSMauro Carvalho Chehab  * McClelland's ovcamchip code.
7cb7a01acSMauro Carvalho Chehab  *
8cb7a01acSMauro Carvalho Chehab  * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
9cb7a01acSMauro Carvalho Chehab  *
10cb7a01acSMauro Carvalho Chehab  * This file may be distributed under the terms of the GNU General
11cb7a01acSMauro Carvalho Chehab  * Public License, version 2.
12cb7a01acSMauro Carvalho Chehab  */
13cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
18cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
19cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
20cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h>
21cb7a01acSMauro Carvalho Chehab #include <media/v4l2-mediabus.h>
22cb7a01acSMauro Carvalho Chehab #include <media/ov7670.h>
23cb7a01acSMauro Carvalho Chehab 
24cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
25cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
26cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
27cb7a01acSMauro Carvalho Chehab 
28cb7a01acSMauro Carvalho Chehab static bool debug;
29cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
30cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
31cb7a01acSMauro Carvalho Chehab 
32cb7a01acSMauro Carvalho Chehab /*
33cb7a01acSMauro Carvalho Chehab  * Basic window sizes.  These probably belong somewhere more globally
34cb7a01acSMauro Carvalho Chehab  * useful.
35cb7a01acSMauro Carvalho Chehab  */
36cb7a01acSMauro Carvalho Chehab #define VGA_WIDTH	640
37cb7a01acSMauro Carvalho Chehab #define VGA_HEIGHT	480
38cb7a01acSMauro Carvalho Chehab #define QVGA_WIDTH	320
39cb7a01acSMauro Carvalho Chehab #define QVGA_HEIGHT	240
40cb7a01acSMauro Carvalho Chehab #define CIF_WIDTH	352
41cb7a01acSMauro Carvalho Chehab #define CIF_HEIGHT	288
42cb7a01acSMauro Carvalho Chehab #define QCIF_WIDTH	176
43cb7a01acSMauro Carvalho Chehab #define	QCIF_HEIGHT	144
44cb7a01acSMauro Carvalho Chehab 
45cb7a01acSMauro Carvalho Chehab /*
46cb7a01acSMauro Carvalho Chehab  * The 7670 sits on i2c with ID 0x42
47cb7a01acSMauro Carvalho Chehab  */
48cb7a01acSMauro Carvalho Chehab #define OV7670_I2C_ADDR 0x42
49cb7a01acSMauro Carvalho Chehab 
50cb7a01acSMauro Carvalho Chehab /* Registers */
51cb7a01acSMauro Carvalho Chehab #define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
52cb7a01acSMauro Carvalho Chehab #define REG_BLUE	0x01	/* blue gain */
53cb7a01acSMauro Carvalho Chehab #define REG_RED		0x02	/* red gain */
54cb7a01acSMauro Carvalho Chehab #define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
55cb7a01acSMauro Carvalho Chehab #define REG_COM1	0x04	/* Control 1 */
56cb7a01acSMauro Carvalho Chehab #define  COM1_CCIR656	  0x40  /* CCIR656 enable */
57cb7a01acSMauro Carvalho Chehab #define REG_BAVE	0x05	/* U/B Average level */
58cb7a01acSMauro Carvalho Chehab #define REG_GbAVE	0x06	/* Y/Gb Average level */
59cb7a01acSMauro Carvalho Chehab #define REG_AECHH	0x07	/* AEC MS 5 bits */
60cb7a01acSMauro Carvalho Chehab #define REG_RAVE	0x08	/* V/R Average level */
61cb7a01acSMauro Carvalho Chehab #define REG_COM2	0x09	/* Control 2 */
62cb7a01acSMauro Carvalho Chehab #define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
63cb7a01acSMauro Carvalho Chehab #define REG_PID		0x0a	/* Product ID MSB */
64cb7a01acSMauro Carvalho Chehab #define REG_VER		0x0b	/* Product ID LSB */
65cb7a01acSMauro Carvalho Chehab #define REG_COM3	0x0c	/* Control 3 */
66cb7a01acSMauro Carvalho Chehab #define  COM3_SWAP	  0x40	  /* Byte swap */
67cb7a01acSMauro Carvalho Chehab #define  COM3_SCALEEN	  0x08	  /* Enable scaling */
68cb7a01acSMauro Carvalho Chehab #define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
69cb7a01acSMauro Carvalho Chehab #define REG_COM4	0x0d	/* Control 4 */
70cb7a01acSMauro Carvalho Chehab #define REG_COM5	0x0e	/* All "reserved" */
71cb7a01acSMauro Carvalho Chehab #define REG_COM6	0x0f	/* Control 6 */
72cb7a01acSMauro Carvalho Chehab #define REG_AECH	0x10	/* More bits of AEC value */
73cb7a01acSMauro Carvalho Chehab #define REG_CLKRC	0x11	/* Clocl control */
74cb7a01acSMauro Carvalho Chehab #define   CLK_EXT	  0x40	  /* Use external clock directly */
75cb7a01acSMauro Carvalho Chehab #define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
76cb7a01acSMauro Carvalho Chehab #define REG_COM7	0x12	/* Control 7 */
77cb7a01acSMauro Carvalho Chehab #define   COM7_RESET	  0x80	  /* Register reset */
78cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_MASK	  0x38
79cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_VGA	  0x00
80cb7a01acSMauro Carvalho Chehab #define	  COM7_FMT_CIF	  0x20	  /* CIF format */
81cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
82cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
83cb7a01acSMauro Carvalho Chehab #define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
84cb7a01acSMauro Carvalho Chehab #define	  COM7_YUV	  0x00	  /* YUV */
85cb7a01acSMauro Carvalho Chehab #define	  COM7_BAYER	  0x01	  /* Bayer format */
86cb7a01acSMauro Carvalho Chehab #define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
87cb7a01acSMauro Carvalho Chehab #define REG_COM8	0x13	/* Control 8 */
88cb7a01acSMauro Carvalho Chehab #define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
89cb7a01acSMauro Carvalho Chehab #define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
90cb7a01acSMauro Carvalho Chehab #define   COM8_BFILT	  0x20	  /* Band filter enable */
91cb7a01acSMauro Carvalho Chehab #define   COM8_AGC	  0x04	  /* Auto gain enable */
92cb7a01acSMauro Carvalho Chehab #define   COM8_AWB	  0x02	  /* White balance enable */
93cb7a01acSMauro Carvalho Chehab #define   COM8_AEC	  0x01	  /* Auto exposure enable */
94cb7a01acSMauro Carvalho Chehab #define REG_COM9	0x14	/* Control 9  - gain ceiling */
95cb7a01acSMauro Carvalho Chehab #define REG_COM10	0x15	/* Control 10 */
96cb7a01acSMauro Carvalho Chehab #define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
97cb7a01acSMauro Carvalho Chehab #define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
98cb7a01acSMauro Carvalho Chehab #define   COM10_HREF_REV  0x08	  /* Reverse HREF */
99cb7a01acSMauro Carvalho Chehab #define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
100cb7a01acSMauro Carvalho Chehab #define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
101cb7a01acSMauro Carvalho Chehab #define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
102cb7a01acSMauro Carvalho Chehab #define REG_HSTART	0x17	/* Horiz start high bits */
103cb7a01acSMauro Carvalho Chehab #define REG_HSTOP	0x18	/* Horiz stop high bits */
104cb7a01acSMauro Carvalho Chehab #define REG_VSTART	0x19	/* Vert start high bits */
105cb7a01acSMauro Carvalho Chehab #define REG_VSTOP	0x1a	/* Vert stop high bits */
106cb7a01acSMauro Carvalho Chehab #define REG_PSHFT	0x1b	/* Pixel delay after HREF */
107cb7a01acSMauro Carvalho Chehab #define REG_MIDH	0x1c	/* Manuf. ID high */
108cb7a01acSMauro Carvalho Chehab #define REG_MIDL	0x1d	/* Manuf. ID low */
109cb7a01acSMauro Carvalho Chehab #define REG_MVFP	0x1e	/* Mirror / vflip */
110cb7a01acSMauro Carvalho Chehab #define   MVFP_MIRROR	  0x20	  /* Mirror image */
111cb7a01acSMauro Carvalho Chehab #define   MVFP_FLIP	  0x10	  /* Vertical flip */
112cb7a01acSMauro Carvalho Chehab 
113cb7a01acSMauro Carvalho Chehab #define REG_AEW		0x24	/* AGC upper limit */
114cb7a01acSMauro Carvalho Chehab #define REG_AEB		0x25	/* AGC lower limit */
115cb7a01acSMauro Carvalho Chehab #define REG_VPT		0x26	/* AGC/AEC fast mode op region */
116cb7a01acSMauro Carvalho Chehab #define REG_HSYST	0x30	/* HSYNC rising edge delay */
117cb7a01acSMauro Carvalho Chehab #define REG_HSYEN	0x31	/* HSYNC falling edge delay */
118cb7a01acSMauro Carvalho Chehab #define REG_HREF	0x32	/* HREF pieces */
119cb7a01acSMauro Carvalho Chehab #define REG_TSLB	0x3a	/* lots of stuff */
120cb7a01acSMauro Carvalho Chehab #define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
121cb7a01acSMauro Carvalho Chehab #define REG_COM11	0x3b	/* Control 11 */
122cb7a01acSMauro Carvalho Chehab #define   COM11_NIGHT	  0x80	  /* NIght mode enable */
123cb7a01acSMauro Carvalho Chehab #define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
124cb7a01acSMauro Carvalho Chehab #define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
125cb7a01acSMauro Carvalho Chehab #define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
126cb7a01acSMauro Carvalho Chehab #define   COM11_EXP	  0x02
127cb7a01acSMauro Carvalho Chehab #define REG_COM12	0x3c	/* Control 12 */
128cb7a01acSMauro Carvalho Chehab #define   COM12_HREF	  0x80	  /* HREF always */
129cb7a01acSMauro Carvalho Chehab #define REG_COM13	0x3d	/* Control 13 */
130cb7a01acSMauro Carvalho Chehab #define   COM13_GAMMA	  0x80	  /* Gamma enable */
131cb7a01acSMauro Carvalho Chehab #define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
132cb7a01acSMauro Carvalho Chehab #define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
133cb7a01acSMauro Carvalho Chehab #define REG_COM14	0x3e	/* Control 14 */
134cb7a01acSMauro Carvalho Chehab #define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
135cb7a01acSMauro Carvalho Chehab #define REG_EDGE	0x3f	/* Edge enhancement factor */
136cb7a01acSMauro Carvalho Chehab #define REG_COM15	0x40	/* Control 15 */
137cb7a01acSMauro Carvalho Chehab #define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
138cb7a01acSMauro Carvalho Chehab #define	  COM15_R01FE	  0x80	  /*            01 to FE */
139cb7a01acSMauro Carvalho Chehab #define   COM15_R00FF	  0xc0	  /*            00 to FF */
140cb7a01acSMauro Carvalho Chehab #define   COM15_RGB565	  0x10	  /* RGB565 output */
141cb7a01acSMauro Carvalho Chehab #define   COM15_RGB555	  0x30	  /* RGB555 output */
142cb7a01acSMauro Carvalho Chehab #define REG_COM16	0x41	/* Control 16 */
143cb7a01acSMauro Carvalho Chehab #define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
144cb7a01acSMauro Carvalho Chehab #define REG_COM17	0x42	/* Control 17 */
145cb7a01acSMauro Carvalho Chehab #define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
146cb7a01acSMauro Carvalho Chehab #define   COM17_CBAR	  0x08	  /* DSP Color bar */
147cb7a01acSMauro Carvalho Chehab 
148cb7a01acSMauro Carvalho Chehab /*
149cb7a01acSMauro Carvalho Chehab  * This matrix defines how the colors are generated, must be
150cb7a01acSMauro Carvalho Chehab  * tweaked to adjust hue and saturation.
151cb7a01acSMauro Carvalho Chehab  *
152cb7a01acSMauro Carvalho Chehab  * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
153cb7a01acSMauro Carvalho Chehab  *
154cb7a01acSMauro Carvalho Chehab  * They are nine-bit signed quantities, with the sign bit
155cb7a01acSMauro Carvalho Chehab  * stored in 0x58.  Sign for v-red is bit 0, and up from there.
156cb7a01acSMauro Carvalho Chehab  */
157cb7a01acSMauro Carvalho Chehab #define	REG_CMATRIX_BASE 0x4f
158cb7a01acSMauro Carvalho Chehab #define   CMATRIX_LEN 6
159cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_SIGN 0x58
160cb7a01acSMauro Carvalho Chehab 
161cb7a01acSMauro Carvalho Chehab 
162cb7a01acSMauro Carvalho Chehab #define REG_BRIGHT	0x55	/* Brightness */
163cb7a01acSMauro Carvalho Chehab #define REG_CONTRAS	0x56	/* Contrast control */
164cb7a01acSMauro Carvalho Chehab 
165cb7a01acSMauro Carvalho Chehab #define REG_GFIX	0x69	/* Fix gain control */
166cb7a01acSMauro Carvalho Chehab 
167cb7a01acSMauro Carvalho Chehab #define REG_REG76	0x76	/* OV's name */
168cb7a01acSMauro Carvalho Chehab #define   R76_BLKPCOR	  0x80	  /* Black pixel correction enable */
169cb7a01acSMauro Carvalho Chehab #define   R76_WHTPCOR	  0x40	  /* White pixel correction enable */
170cb7a01acSMauro Carvalho Chehab 
171cb7a01acSMauro Carvalho Chehab #define REG_RGB444	0x8c	/* RGB 444 control */
172cb7a01acSMauro Carvalho Chehab #define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
173cb7a01acSMauro Carvalho Chehab #define   R444_RGBX	  0x01	  /* Empty nibble at end */
174cb7a01acSMauro Carvalho Chehab 
175cb7a01acSMauro Carvalho Chehab #define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
176cb7a01acSMauro Carvalho Chehab #define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
177cb7a01acSMauro Carvalho Chehab 
178cb7a01acSMauro Carvalho Chehab #define REG_BD50MAX	0xa5	/* 50hz banding step limit */
179cb7a01acSMauro Carvalho Chehab #define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
180cb7a01acSMauro Carvalho Chehab #define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
181cb7a01acSMauro Carvalho Chehab #define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
182cb7a01acSMauro Carvalho Chehab #define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
183cb7a01acSMauro Carvalho Chehab #define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
184cb7a01acSMauro Carvalho Chehab #define REG_BD60MAX	0xab	/* 60hz banding step limit */
185cb7a01acSMauro Carvalho Chehab 
186cb7a01acSMauro Carvalho Chehab 
187cb7a01acSMauro Carvalho Chehab /*
188cb7a01acSMauro Carvalho Chehab  * Information we maintain about a known sensor.
189cb7a01acSMauro Carvalho Chehab  */
190cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct;  /* coming later */
191cb7a01acSMauro Carvalho Chehab struct ov7670_info {
192cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
193cb7a01acSMauro Carvalho Chehab 	struct ov7670_format_struct *fmt;  /* Current format */
194cb7a01acSMauro Carvalho Chehab 	unsigned char sat;		/* Saturation value */
195cb7a01acSMauro Carvalho Chehab 	int hue;			/* Hue value */
196cb7a01acSMauro Carvalho Chehab 	int min_width;			/* Filter out smaller sizes */
197cb7a01acSMauro Carvalho Chehab 	int min_height;			/* Filter out smaller sizes */
198cb7a01acSMauro Carvalho Chehab 	int clock_speed;		/* External clock speed (MHz) */
199cb7a01acSMauro Carvalho Chehab 	u8 clkrc;			/* Clock divider value */
200cb7a01acSMauro Carvalho Chehab 	bool use_smbus;			/* Use smbus I/O instead of I2C */
201cb7a01acSMauro Carvalho Chehab };
202cb7a01acSMauro Carvalho Chehab 
203cb7a01acSMauro Carvalho Chehab static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
204cb7a01acSMauro Carvalho Chehab {
205cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct ov7670_info, sd);
206cb7a01acSMauro Carvalho Chehab }
207cb7a01acSMauro Carvalho Chehab 
208cb7a01acSMauro Carvalho Chehab 
209cb7a01acSMauro Carvalho Chehab 
210cb7a01acSMauro Carvalho Chehab /*
211cb7a01acSMauro Carvalho Chehab  * The default register settings, as obtained from OmniVision.  There
212cb7a01acSMauro Carvalho Chehab  * is really no making sense of most of these - lots of "reserved" values
213cb7a01acSMauro Carvalho Chehab  * and such.
214cb7a01acSMauro Carvalho Chehab  *
215cb7a01acSMauro Carvalho Chehab  * These settings give VGA YUYV.
216cb7a01acSMauro Carvalho Chehab  */
217cb7a01acSMauro Carvalho Chehab 
218cb7a01acSMauro Carvalho Chehab struct regval_list {
219cb7a01acSMauro Carvalho Chehab 	unsigned char reg_num;
220cb7a01acSMauro Carvalho Chehab 	unsigned char value;
221cb7a01acSMauro Carvalho Chehab };
222cb7a01acSMauro Carvalho Chehab 
223cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_default_regs[] = {
224cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RESET },
225cb7a01acSMauro Carvalho Chehab /*
226cb7a01acSMauro Carvalho Chehab  * Clock scale: 3 = 15fps
227cb7a01acSMauro Carvalho Chehab  *              2 = 20fps
228cb7a01acSMauro Carvalho Chehab  *              1 = 30fps
229cb7a01acSMauro Carvalho Chehab  */
230cb7a01acSMauro Carvalho Chehab 	{ REG_CLKRC, 0x1 },	/* OV: clock scale (30 fps) */
231cb7a01acSMauro Carvalho Chehab 	{ REG_TSLB,  0x04 },	/* OV */
232cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, 0 },	/* VGA */
233cb7a01acSMauro Carvalho Chehab 	/*
234cb7a01acSMauro Carvalho Chehab 	 * Set the hardware window.  These values from OV don't entirely
235cb7a01acSMauro Carvalho Chehab 	 * make sense - hstop is less than hstart.  But they work...
236cb7a01acSMauro Carvalho Chehab 	 */
237cb7a01acSMauro Carvalho Chehab 	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
238cb7a01acSMauro Carvalho Chehab 	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
239cb7a01acSMauro Carvalho Chehab 	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
240cb7a01acSMauro Carvalho Chehab 
241cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, 0 },	{ REG_COM14, 0 },
242cb7a01acSMauro Carvalho Chehab 	/* Mystery scaling numbers */
243cb7a01acSMauro Carvalho Chehab 	{ 0x70, 0x3a },		{ 0x71, 0x35 },
244cb7a01acSMauro Carvalho Chehab 	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
245cb7a01acSMauro Carvalho Chehab 	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
246cb7a01acSMauro Carvalho Chehab 
247cb7a01acSMauro Carvalho Chehab 	/* Gamma curve values */
248cb7a01acSMauro Carvalho Chehab 	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
249cb7a01acSMauro Carvalho Chehab 	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
250cb7a01acSMauro Carvalho Chehab 	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
251cb7a01acSMauro Carvalho Chehab 	{ 0x80, 0x76 },		{ 0x81, 0x80 },
252cb7a01acSMauro Carvalho Chehab 	{ 0x82, 0x88 },		{ 0x83, 0x8f },
253cb7a01acSMauro Carvalho Chehab 	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
254cb7a01acSMauro Carvalho Chehab 	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
255cb7a01acSMauro Carvalho Chehab 	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
256cb7a01acSMauro Carvalho Chehab 
257cb7a01acSMauro Carvalho Chehab 	/* AGC and AEC parameters.  Note we start by disabling those features,
258cb7a01acSMauro Carvalho Chehab 	   then turn them only after tweaking the values. */
259cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
260cb7a01acSMauro Carvalho Chehab 	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
261cb7a01acSMauro Carvalho Chehab 	{ REG_COM4, 0x40 }, /* magic reserved bit */
262cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
263cb7a01acSMauro Carvalho Chehab 	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
264cb7a01acSMauro Carvalho Chehab 	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
265cb7a01acSMauro Carvalho Chehab 	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
266cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
267cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
268cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
269cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC7, 0x94 },
270cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
271cb7a01acSMauro Carvalho Chehab 
272cb7a01acSMauro Carvalho Chehab 	/* Almost all of these are magic "reserved" values.  */
273cb7a01acSMauro Carvalho Chehab 	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
274cb7a01acSMauro Carvalho Chehab 	{ 0x16, 0x02 },		{ REG_MVFP, 0x07 },
275cb7a01acSMauro Carvalho Chehab 	{ 0x21, 0x02 },		{ 0x22, 0x91 },
276cb7a01acSMauro Carvalho Chehab 	{ 0x29, 0x07 },		{ 0x33, 0x0b },
277cb7a01acSMauro Carvalho Chehab 	{ 0x35, 0x0b },		{ 0x37, 0x1d },
278cb7a01acSMauro Carvalho Chehab 	{ 0x38, 0x71 },		{ 0x39, 0x2a },
279cb7a01acSMauro Carvalho Chehab 	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
280cb7a01acSMauro Carvalho Chehab 	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
281cb7a01acSMauro Carvalho Chehab 	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
282cb7a01acSMauro Carvalho Chehab 	{ 0x8d, 0x4f },		{ 0x8e, 0 },
283cb7a01acSMauro Carvalho Chehab 	{ 0x8f, 0 },		{ 0x90, 0 },
284cb7a01acSMauro Carvalho Chehab 	{ 0x91, 0 },		{ 0x96, 0 },
285cb7a01acSMauro Carvalho Chehab 	{ 0x9a, 0 },		{ 0xb0, 0x84 },
286cb7a01acSMauro Carvalho Chehab 	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
287cb7a01acSMauro Carvalho Chehab 	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
288cb7a01acSMauro Carvalho Chehab 
289cb7a01acSMauro Carvalho Chehab 	/* More reserved magic, some of which tweaks white balance */
290cb7a01acSMauro Carvalho Chehab 	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
291cb7a01acSMauro Carvalho Chehab 	{ 0x45, 0x34 },		{ 0x46, 0x58 },
292cb7a01acSMauro Carvalho Chehab 	{ 0x47, 0x28 },		{ 0x48, 0x3a },
293cb7a01acSMauro Carvalho Chehab 	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
294cb7a01acSMauro Carvalho Chehab 	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
295cb7a01acSMauro Carvalho Chehab 	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
296cb7a01acSMauro Carvalho Chehab 	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
297cb7a01acSMauro Carvalho Chehab 	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
298cb7a01acSMauro Carvalho Chehab 	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
299cb7a01acSMauro Carvalho Chehab 	{ REG_RED, 0x60 },
300cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
301cb7a01acSMauro Carvalho Chehab 
302cb7a01acSMauro Carvalho Chehab 	/* Matrix coefficients */
303cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
304cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0 },		{ 0x52, 0x22 },
305cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0x5e },		{ 0x54, 0x80 },
306cb7a01acSMauro Carvalho Chehab 	{ 0x58, 0x9e },
307cb7a01acSMauro Carvalho Chehab 
308cb7a01acSMauro Carvalho Chehab 	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
309cb7a01acSMauro Carvalho Chehab 	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
310cb7a01acSMauro Carvalho Chehab 	{ 0x4c, 0 },		{ 0x77, 0x01 },
311cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
312cb7a01acSMauro Carvalho Chehab 	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
313cb7a01acSMauro Carvalho Chehab 	{ 0x56, 0x40 },
314cb7a01acSMauro Carvalho Chehab 
315cb7a01acSMauro Carvalho Chehab 	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
316cb7a01acSMauro Carvalho Chehab 	{ 0xa4, 0x88 },		{ 0x96, 0 },
317cb7a01acSMauro Carvalho Chehab 	{ 0x97, 0x30 },		{ 0x98, 0x20 },
318cb7a01acSMauro Carvalho Chehab 	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
319cb7a01acSMauro Carvalho Chehab 	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
320cb7a01acSMauro Carvalho Chehab 	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
321cb7a01acSMauro Carvalho Chehab 	{ 0x78, 0x04 },
322cb7a01acSMauro Carvalho Chehab 
323cb7a01acSMauro Carvalho Chehab 	/* Extra-weird stuff.  Some sort of multiplexor register */
324cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
325cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
326cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
327cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
328cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
329cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
330cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
331cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
332cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
333cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
334cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
335cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x26 },
336cb7a01acSMauro Carvalho Chehab 
337cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },	/* END MARKER */
338cb7a01acSMauro Carvalho Chehab };
339cb7a01acSMauro Carvalho Chehab 
340cb7a01acSMauro Carvalho Chehab 
341cb7a01acSMauro Carvalho Chehab /*
342cb7a01acSMauro Carvalho Chehab  * Here we'll try to encapsulate the changes for just the output
343cb7a01acSMauro Carvalho Chehab  * video format.
344cb7a01acSMauro Carvalho Chehab  *
345cb7a01acSMauro Carvalho Chehab  * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
346cb7a01acSMauro Carvalho Chehab  *
347cb7a01acSMauro Carvalho Chehab  * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
348cb7a01acSMauro Carvalho Chehab  */
349cb7a01acSMauro Carvalho Chehab 
350cb7a01acSMauro Carvalho Chehab 
351cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_yuv422[] = {
352cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, 0x0 },  /* Selects YUV mode */
353cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, 0 },	/* No RGB444 please */
354cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0 },	/* CCIR601 */
355cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_R00FF },
356cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
357cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
358cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
359cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
360cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
361cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
362cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
363cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
364cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
365cb7a01acSMauro Carvalho Chehab };
366cb7a01acSMauro Carvalho Chehab 
367cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb565[] = {
368cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
369cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, 0 },	/* No RGB444 please */
370cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0x0 },	/* CCIR601 */
371cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_RGB565 },
372cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
373cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
374cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
375cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
376cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
377cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
378cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
379cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
380cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
381cb7a01acSMauro Carvalho Chehab };
382cb7a01acSMauro Carvalho Chehab 
383cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb444[] = {
384cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
385cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
386cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0x0 },	/* CCIR601 */
387cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
388cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
389cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
390cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
391cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
392cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
393cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
394cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
395cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
396cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
397cb7a01acSMauro Carvalho Chehab };
398cb7a01acSMauro Carvalho Chehab 
399cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_raw[] = {
400cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_BAYER },
401cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
402cb7a01acSMauro Carvalho Chehab 	{ REG_COM16, 0x3d }, /* Edge enhancement, denoise */
403cb7a01acSMauro Carvalho Chehab 	{ REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
404cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
405cb7a01acSMauro Carvalho Chehab };
406cb7a01acSMauro Carvalho Chehab 
407cb7a01acSMauro Carvalho Chehab 
408cb7a01acSMauro Carvalho Chehab 
409cb7a01acSMauro Carvalho Chehab /*
410cb7a01acSMauro Carvalho Chehab  * Low-level register I/O.
411cb7a01acSMauro Carvalho Chehab  *
412cb7a01acSMauro Carvalho Chehab  * Note that there are two versions of these.  On the XO 1, the
413cb7a01acSMauro Carvalho Chehab  * i2c controller only does SMBUS, so that's what we use.  The
414cb7a01acSMauro Carvalho Chehab  * ov7670 is not really an SMBUS device, though, so the communication
415cb7a01acSMauro Carvalho Chehab  * is not always entirely reliable.
416cb7a01acSMauro Carvalho Chehab  */
417cb7a01acSMauro Carvalho Chehab static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
418cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
419cb7a01acSMauro Carvalho Chehab {
420cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
421cb7a01acSMauro Carvalho Chehab 	int ret;
422cb7a01acSMauro Carvalho Chehab 
423cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_read_byte_data(client, reg);
424cb7a01acSMauro Carvalho Chehab 	if (ret >= 0) {
425cb7a01acSMauro Carvalho Chehab 		*value = (unsigned char)ret;
426cb7a01acSMauro Carvalho Chehab 		ret = 0;
427cb7a01acSMauro Carvalho Chehab 	}
428cb7a01acSMauro Carvalho Chehab 	return ret;
429cb7a01acSMauro Carvalho Chehab }
430cb7a01acSMauro Carvalho Chehab 
431cb7a01acSMauro Carvalho Chehab 
432cb7a01acSMauro Carvalho Chehab static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
433cb7a01acSMauro Carvalho Chehab 		unsigned char value)
434cb7a01acSMauro Carvalho Chehab {
435cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
436cb7a01acSMauro Carvalho Chehab 	int ret = i2c_smbus_write_byte_data(client, reg, value);
437cb7a01acSMauro Carvalho Chehab 
438cb7a01acSMauro Carvalho Chehab 	if (reg == REG_COM7 && (value & COM7_RESET))
439cb7a01acSMauro Carvalho Chehab 		msleep(5);  /* Wait for reset to run */
440cb7a01acSMauro Carvalho Chehab 	return ret;
441cb7a01acSMauro Carvalho Chehab }
442cb7a01acSMauro Carvalho Chehab 
443cb7a01acSMauro Carvalho Chehab /*
444cb7a01acSMauro Carvalho Chehab  * On most platforms, we'd rather do straight i2c I/O.
445cb7a01acSMauro Carvalho Chehab  */
446cb7a01acSMauro Carvalho Chehab static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
447cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
448cb7a01acSMauro Carvalho Chehab {
449cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
450cb7a01acSMauro Carvalho Chehab 	u8 data = reg;
451cb7a01acSMauro Carvalho Chehab 	struct i2c_msg msg;
452cb7a01acSMauro Carvalho Chehab 	int ret;
453cb7a01acSMauro Carvalho Chehab 
454cb7a01acSMauro Carvalho Chehab 	/*
455cb7a01acSMauro Carvalho Chehab 	 * Send out the register address...
456cb7a01acSMauro Carvalho Chehab 	 */
457cb7a01acSMauro Carvalho Chehab 	msg.addr = client->addr;
458cb7a01acSMauro Carvalho Chehab 	msg.flags = 0;
459cb7a01acSMauro Carvalho Chehab 	msg.len = 1;
460cb7a01acSMauro Carvalho Chehab 	msg.buf = &data;
461cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
462cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
463cb7a01acSMauro Carvalho Chehab 		printk(KERN_ERR "Error %d on register write\n", ret);
464cb7a01acSMauro Carvalho Chehab 		return ret;
465cb7a01acSMauro Carvalho Chehab 	}
466cb7a01acSMauro Carvalho Chehab 	/*
467cb7a01acSMauro Carvalho Chehab 	 * ...then read back the result.
468cb7a01acSMauro Carvalho Chehab 	 */
469cb7a01acSMauro Carvalho Chehab 	msg.flags = I2C_M_RD;
470cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
471cb7a01acSMauro Carvalho Chehab 	if (ret >= 0) {
472cb7a01acSMauro Carvalho Chehab 		*value = data;
473cb7a01acSMauro Carvalho Chehab 		ret = 0;
474cb7a01acSMauro Carvalho Chehab 	}
475cb7a01acSMauro Carvalho Chehab 	return ret;
476cb7a01acSMauro Carvalho Chehab }
477cb7a01acSMauro Carvalho Chehab 
478cb7a01acSMauro Carvalho Chehab 
479cb7a01acSMauro Carvalho Chehab static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
480cb7a01acSMauro Carvalho Chehab 		unsigned char value)
481cb7a01acSMauro Carvalho Chehab {
482cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
483cb7a01acSMauro Carvalho Chehab 	struct i2c_msg msg;
484cb7a01acSMauro Carvalho Chehab 	unsigned char data[2] = { reg, value };
485cb7a01acSMauro Carvalho Chehab 	int ret;
486cb7a01acSMauro Carvalho Chehab 
487cb7a01acSMauro Carvalho Chehab 	msg.addr = client->addr;
488cb7a01acSMauro Carvalho Chehab 	msg.flags = 0;
489cb7a01acSMauro Carvalho Chehab 	msg.len = 2;
490cb7a01acSMauro Carvalho Chehab 	msg.buf = data;
491cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
492cb7a01acSMauro Carvalho Chehab 	if (ret > 0)
493cb7a01acSMauro Carvalho Chehab 		ret = 0;
494cb7a01acSMauro Carvalho Chehab 	if (reg == REG_COM7 && (value & COM7_RESET))
495cb7a01acSMauro Carvalho Chehab 		msleep(5);  /* Wait for reset to run */
496cb7a01acSMauro Carvalho Chehab 	return ret;
497cb7a01acSMauro Carvalho Chehab }
498cb7a01acSMauro Carvalho Chehab 
499cb7a01acSMauro Carvalho Chehab static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
500cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
501cb7a01acSMauro Carvalho Chehab {
502cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
503cb7a01acSMauro Carvalho Chehab 	if (info->use_smbus)
504cb7a01acSMauro Carvalho Chehab 		return ov7670_read_smbus(sd, reg, value);
505cb7a01acSMauro Carvalho Chehab 	else
506cb7a01acSMauro Carvalho Chehab 		return ov7670_read_i2c(sd, reg, value);
507cb7a01acSMauro Carvalho Chehab }
508cb7a01acSMauro Carvalho Chehab 
509cb7a01acSMauro Carvalho Chehab static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
510cb7a01acSMauro Carvalho Chehab 		unsigned char value)
511cb7a01acSMauro Carvalho Chehab {
512cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
513cb7a01acSMauro Carvalho Chehab 	if (info->use_smbus)
514cb7a01acSMauro Carvalho Chehab 		return ov7670_write_smbus(sd, reg, value);
515cb7a01acSMauro Carvalho Chehab 	else
516cb7a01acSMauro Carvalho Chehab 		return ov7670_write_i2c(sd, reg, value);
517cb7a01acSMauro Carvalho Chehab }
518cb7a01acSMauro Carvalho Chehab 
519cb7a01acSMauro Carvalho Chehab /*
520cb7a01acSMauro Carvalho Chehab  * Write a list of register settings; ff/ff stops the process.
521cb7a01acSMauro Carvalho Chehab  */
522cb7a01acSMauro Carvalho Chehab static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
523cb7a01acSMauro Carvalho Chehab {
524cb7a01acSMauro Carvalho Chehab 	while (vals->reg_num != 0xff || vals->value != 0xff) {
525cb7a01acSMauro Carvalho Chehab 		int ret = ov7670_write(sd, vals->reg_num, vals->value);
526cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
527cb7a01acSMauro Carvalho Chehab 			return ret;
528cb7a01acSMauro Carvalho Chehab 		vals++;
529cb7a01acSMauro Carvalho Chehab 	}
530cb7a01acSMauro Carvalho Chehab 	return 0;
531cb7a01acSMauro Carvalho Chehab }
532cb7a01acSMauro Carvalho Chehab 
533cb7a01acSMauro Carvalho Chehab 
534cb7a01acSMauro Carvalho Chehab /*
535cb7a01acSMauro Carvalho Chehab  * Stuff that knows about the sensor.
536cb7a01acSMauro Carvalho Chehab  */
537cb7a01acSMauro Carvalho Chehab static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
538cb7a01acSMauro Carvalho Chehab {
539cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM7, COM7_RESET);
540cb7a01acSMauro Carvalho Chehab 	msleep(1);
541cb7a01acSMauro Carvalho Chehab 	return 0;
542cb7a01acSMauro Carvalho Chehab }
543cb7a01acSMauro Carvalho Chehab 
544cb7a01acSMauro Carvalho Chehab 
545cb7a01acSMauro Carvalho Chehab static int ov7670_init(struct v4l2_subdev *sd, u32 val)
546cb7a01acSMauro Carvalho Chehab {
547cb7a01acSMauro Carvalho Chehab 	return ov7670_write_array(sd, ov7670_default_regs);
548cb7a01acSMauro Carvalho Chehab }
549cb7a01acSMauro Carvalho Chehab 
550cb7a01acSMauro Carvalho Chehab 
551cb7a01acSMauro Carvalho Chehab 
552cb7a01acSMauro Carvalho Chehab static int ov7670_detect(struct v4l2_subdev *sd)
553cb7a01acSMauro Carvalho Chehab {
554cb7a01acSMauro Carvalho Chehab 	unsigned char v;
555cb7a01acSMauro Carvalho Chehab 	int ret;
556cb7a01acSMauro Carvalho Chehab 
557cb7a01acSMauro Carvalho Chehab 	ret = ov7670_init(sd, 0);
558cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
559cb7a01acSMauro Carvalho Chehab 		return ret;
560cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MIDH, &v);
561cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
562cb7a01acSMauro Carvalho Chehab 		return ret;
563cb7a01acSMauro Carvalho Chehab 	if (v != 0x7f) /* OV manuf. id. */
564cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
565cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MIDL, &v);
566cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
567cb7a01acSMauro Carvalho Chehab 		return ret;
568cb7a01acSMauro Carvalho Chehab 	if (v != 0xa2)
569cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
570cb7a01acSMauro Carvalho Chehab 	/*
571cb7a01acSMauro Carvalho Chehab 	 * OK, we know we have an OmniVision chip...but which one?
572cb7a01acSMauro Carvalho Chehab 	 */
573cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_PID, &v);
574cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
575cb7a01acSMauro Carvalho Chehab 		return ret;
576cb7a01acSMauro Carvalho Chehab 	if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
577cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
578cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_VER, &v);
579cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
580cb7a01acSMauro Carvalho Chehab 		return ret;
581cb7a01acSMauro Carvalho Chehab 	if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
582cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
583cb7a01acSMauro Carvalho Chehab 	return 0;
584cb7a01acSMauro Carvalho Chehab }
585cb7a01acSMauro Carvalho Chehab 
586cb7a01acSMauro Carvalho Chehab 
587cb7a01acSMauro Carvalho Chehab /*
588cb7a01acSMauro Carvalho Chehab  * Store information about the video data format.  The color matrix
589cb7a01acSMauro Carvalho Chehab  * is deeply tied into the format, so keep the relevant values here.
590cb7a01acSMauro Carvalho Chehab  * The magic matrix numbers come from OmniVision.
591cb7a01acSMauro Carvalho Chehab  */
592cb7a01acSMauro Carvalho Chehab static struct ov7670_format_struct {
593cb7a01acSMauro Carvalho Chehab 	enum v4l2_mbus_pixelcode mbus_code;
594cb7a01acSMauro Carvalho Chehab 	enum v4l2_colorspace colorspace;
595cb7a01acSMauro Carvalho Chehab 	struct regval_list *regs;
596cb7a01acSMauro Carvalho Chehab 	int cmatrix[CMATRIX_LEN];
597cb7a01acSMauro Carvalho Chehab } ov7670_formats[] = {
598cb7a01acSMauro Carvalho Chehab 	{
599cb7a01acSMauro Carvalho Chehab 		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
600cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_JPEG,
601cb7a01acSMauro Carvalho Chehab 		.regs 		= ov7670_fmt_yuv422,
602cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
603cb7a01acSMauro Carvalho Chehab 	},
604cb7a01acSMauro Carvalho Chehab 	{
605cb7a01acSMauro Carvalho Chehab 		.mbus_code	= V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
606cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
607cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_rgb444,
608cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
609cb7a01acSMauro Carvalho Chehab 	},
610cb7a01acSMauro Carvalho Chehab 	{
611cb7a01acSMauro Carvalho Chehab 		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
612cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
613cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_rgb565,
614cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
615cb7a01acSMauro Carvalho Chehab 	},
616cb7a01acSMauro Carvalho Chehab 	{
617cb7a01acSMauro Carvalho Chehab 		.mbus_code	= V4L2_MBUS_FMT_SBGGR8_1X8,
618cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
619cb7a01acSMauro Carvalho Chehab 		.regs 		= ov7670_fmt_raw,
620cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 0, 0, 0, 0, 0, 0 },
621cb7a01acSMauro Carvalho Chehab 	},
622cb7a01acSMauro Carvalho Chehab };
623cb7a01acSMauro Carvalho Chehab #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
624cb7a01acSMauro Carvalho Chehab 
625cb7a01acSMauro Carvalho Chehab 
626cb7a01acSMauro Carvalho Chehab /*
627cb7a01acSMauro Carvalho Chehab  * Then there is the issue of window sizes.  Try to capture the info here.
628cb7a01acSMauro Carvalho Chehab  */
629cb7a01acSMauro Carvalho Chehab 
630cb7a01acSMauro Carvalho Chehab /*
631cb7a01acSMauro Carvalho Chehab  * QCIF mode is done (by OV) in a very strange way - it actually looks like
632cb7a01acSMauro Carvalho Chehab  * VGA with weird scaling options - they do *not* use the canned QCIF mode
633cb7a01acSMauro Carvalho Chehab  * which is allegedly provided by the sensor.  So here's the weird register
634cb7a01acSMauro Carvalho Chehab  * settings.
635cb7a01acSMauro Carvalho Chehab  */
636cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_qcif_regs[] = {
637cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, COM3_SCALEEN|COM3_DCWEN },
638cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, COM3_DCWEN },
639cb7a01acSMauro Carvalho Chehab 	{ REG_COM14, COM14_DCWEN | 0x01},
640cb7a01acSMauro Carvalho Chehab 	{ 0x73, 0xf1 },
641cb7a01acSMauro Carvalho Chehab 	{ 0xa2, 0x52 },
642cb7a01acSMauro Carvalho Chehab 	{ 0x7b, 0x1c },
643cb7a01acSMauro Carvalho Chehab 	{ 0x7c, 0x28 },
644cb7a01acSMauro Carvalho Chehab 	{ 0x7d, 0x3c },
645cb7a01acSMauro Carvalho Chehab 	{ 0x7f, 0x69 },
646cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },
647cb7a01acSMauro Carvalho Chehab 	{ 0xa1, 0x0b },
648cb7a01acSMauro Carvalho Chehab 	{ 0x74, 0x19 },
649cb7a01acSMauro Carvalho Chehab 	{ 0x9a, 0x80 },
650cb7a01acSMauro Carvalho Chehab 	{ 0x43, 0x14 },
651cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0xc0 },
652cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
653cb7a01acSMauro Carvalho Chehab };
654cb7a01acSMauro Carvalho Chehab 
655cb7a01acSMauro Carvalho Chehab static struct ov7670_win_size {
656cb7a01acSMauro Carvalho Chehab 	int	width;
657cb7a01acSMauro Carvalho Chehab 	int	height;
658cb7a01acSMauro Carvalho Chehab 	unsigned char com7_bit;
659cb7a01acSMauro Carvalho Chehab 	int	hstart;		/* Start/stop values for the camera.  Note */
660cb7a01acSMauro Carvalho Chehab 	int	hstop;		/* that they do not always make complete */
661cb7a01acSMauro Carvalho Chehab 	int	vstart;		/* sense to humans, but evidently the sensor */
662cb7a01acSMauro Carvalho Chehab 	int	vstop;		/* will do the right thing... */
663cb7a01acSMauro Carvalho Chehab 	struct regval_list *regs; /* Regs to tweak */
664cb7a01acSMauro Carvalho Chehab /* h/vref stuff */
665cb7a01acSMauro Carvalho Chehab } ov7670_win_sizes[] = {
666cb7a01acSMauro Carvalho Chehab 	/* VGA */
667cb7a01acSMauro Carvalho Chehab 	{
668cb7a01acSMauro Carvalho Chehab 		.width		= VGA_WIDTH,
669cb7a01acSMauro Carvalho Chehab 		.height		= VGA_HEIGHT,
670cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_VGA,
671cb7a01acSMauro Carvalho Chehab 		.hstart		= 158,		/* These values from */
672cb7a01acSMauro Carvalho Chehab 		.hstop		=  14,		/* Omnivision */
673cb7a01acSMauro Carvalho Chehab 		.vstart		=  10,
674cb7a01acSMauro Carvalho Chehab 		.vstop		= 490,
675cb7a01acSMauro Carvalho Chehab 		.regs 		= NULL,
676cb7a01acSMauro Carvalho Chehab 	},
677cb7a01acSMauro Carvalho Chehab 	/* CIF */
678cb7a01acSMauro Carvalho Chehab 	{
679cb7a01acSMauro Carvalho Chehab 		.width		= CIF_WIDTH,
680cb7a01acSMauro Carvalho Chehab 		.height		= CIF_HEIGHT,
681cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_CIF,
682cb7a01acSMauro Carvalho Chehab 		.hstart		= 170,		/* Empirically determined */
683cb7a01acSMauro Carvalho Chehab 		.hstop		=  90,
684cb7a01acSMauro Carvalho Chehab 		.vstart		=  14,
685cb7a01acSMauro Carvalho Chehab 		.vstop		= 494,
686cb7a01acSMauro Carvalho Chehab 		.regs 		= NULL,
687cb7a01acSMauro Carvalho Chehab 	},
688cb7a01acSMauro Carvalho Chehab 	/* QVGA */
689cb7a01acSMauro Carvalho Chehab 	{
690cb7a01acSMauro Carvalho Chehab 		.width		= QVGA_WIDTH,
691cb7a01acSMauro Carvalho Chehab 		.height		= QVGA_HEIGHT,
692cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_QVGA,
693cb7a01acSMauro Carvalho Chehab 		.hstart		= 168,		/* Empirically determined */
694cb7a01acSMauro Carvalho Chehab 		.hstop		=  24,
695cb7a01acSMauro Carvalho Chehab 		.vstart		=  12,
696cb7a01acSMauro Carvalho Chehab 		.vstop		= 492,
697cb7a01acSMauro Carvalho Chehab 		.regs 		= NULL,
698cb7a01acSMauro Carvalho Chehab 	},
699cb7a01acSMauro Carvalho Chehab 	/* QCIF */
700cb7a01acSMauro Carvalho Chehab 	{
701cb7a01acSMauro Carvalho Chehab 		.width		= QCIF_WIDTH,
702cb7a01acSMauro Carvalho Chehab 		.height		= QCIF_HEIGHT,
703cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_VGA, /* see comment above */
704cb7a01acSMauro Carvalho Chehab 		.hstart		= 456,		/* Empirically determined */
705cb7a01acSMauro Carvalho Chehab 		.hstop		=  24,
706cb7a01acSMauro Carvalho Chehab 		.vstart		=  14,
707cb7a01acSMauro Carvalho Chehab 		.vstop		= 494,
708cb7a01acSMauro Carvalho Chehab 		.regs 		= ov7670_qcif_regs,
709cb7a01acSMauro Carvalho Chehab 	},
710cb7a01acSMauro Carvalho Chehab };
711cb7a01acSMauro Carvalho Chehab 
712cb7a01acSMauro Carvalho Chehab #define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes))
713cb7a01acSMauro Carvalho Chehab 
714cb7a01acSMauro Carvalho Chehab 
715cb7a01acSMauro Carvalho Chehab /*
716cb7a01acSMauro Carvalho Chehab  * Store a set of start/stop values into the camera.
717cb7a01acSMauro Carvalho Chehab  */
718cb7a01acSMauro Carvalho Chehab static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
719cb7a01acSMauro Carvalho Chehab 		int vstart, int vstop)
720cb7a01acSMauro Carvalho Chehab {
721cb7a01acSMauro Carvalho Chehab 	int ret;
722cb7a01acSMauro Carvalho Chehab 	unsigned char v;
723cb7a01acSMauro Carvalho Chehab /*
724cb7a01acSMauro Carvalho Chehab  * Horizontal: 11 bits, top 8 live in hstart and hstop.  Bottom 3 of
725cb7a01acSMauro Carvalho Chehab  * hstart are in href[2:0], bottom 3 of hstop in href[5:3].  There is
726cb7a01acSMauro Carvalho Chehab  * a mystery "edge offset" value in the top two bits of href.
727cb7a01acSMauro Carvalho Chehab  */
728cb7a01acSMauro Carvalho Chehab 	ret =  ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
729cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
730cb7a01acSMauro Carvalho Chehab 	ret += ov7670_read(sd, REG_HREF, &v);
731cb7a01acSMauro Carvalho Chehab 	v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
732cb7a01acSMauro Carvalho Chehab 	msleep(10);
733cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_HREF, v);
734cb7a01acSMauro Carvalho Chehab /*
735cb7a01acSMauro Carvalho Chehab  * Vertical: similar arrangement, but only 10 bits.
736cb7a01acSMauro Carvalho Chehab  */
737cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
738cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
739cb7a01acSMauro Carvalho Chehab 	ret += ov7670_read(sd, REG_VREF, &v);
740cb7a01acSMauro Carvalho Chehab 	v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
741cb7a01acSMauro Carvalho Chehab 	msleep(10);
742cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_VREF, v);
743cb7a01acSMauro Carvalho Chehab 	return ret;
744cb7a01acSMauro Carvalho Chehab }
745cb7a01acSMauro Carvalho Chehab 
746cb7a01acSMauro Carvalho Chehab 
747cb7a01acSMauro Carvalho Chehab static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
748cb7a01acSMauro Carvalho Chehab 					enum v4l2_mbus_pixelcode *code)
749cb7a01acSMauro Carvalho Chehab {
750cb7a01acSMauro Carvalho Chehab 	if (index >= N_OV7670_FMTS)
751cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
752cb7a01acSMauro Carvalho Chehab 
753cb7a01acSMauro Carvalho Chehab 	*code = ov7670_formats[index].mbus_code;
754cb7a01acSMauro Carvalho Chehab 	return 0;
755cb7a01acSMauro Carvalho Chehab }
756cb7a01acSMauro Carvalho Chehab 
757cb7a01acSMauro Carvalho Chehab static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
758cb7a01acSMauro Carvalho Chehab 		struct v4l2_mbus_framefmt *fmt,
759cb7a01acSMauro Carvalho Chehab 		struct ov7670_format_struct **ret_fmt,
760cb7a01acSMauro Carvalho Chehab 		struct ov7670_win_size **ret_wsize)
761cb7a01acSMauro Carvalho Chehab {
762cb7a01acSMauro Carvalho Chehab 	int index;
763cb7a01acSMauro Carvalho Chehab 	struct ov7670_win_size *wsize;
764cb7a01acSMauro Carvalho Chehab 
765cb7a01acSMauro Carvalho Chehab 	for (index = 0; index < N_OV7670_FMTS; index++)
766cb7a01acSMauro Carvalho Chehab 		if (ov7670_formats[index].mbus_code == fmt->code)
767cb7a01acSMauro Carvalho Chehab 			break;
768cb7a01acSMauro Carvalho Chehab 	if (index >= N_OV7670_FMTS) {
769cb7a01acSMauro Carvalho Chehab 		/* default to first format */
770cb7a01acSMauro Carvalho Chehab 		index = 0;
771cb7a01acSMauro Carvalho Chehab 		fmt->code = ov7670_formats[0].mbus_code;
772cb7a01acSMauro Carvalho Chehab 	}
773cb7a01acSMauro Carvalho Chehab 	if (ret_fmt != NULL)
774cb7a01acSMauro Carvalho Chehab 		*ret_fmt = ov7670_formats + index;
775cb7a01acSMauro Carvalho Chehab 	/*
776cb7a01acSMauro Carvalho Chehab 	 * Fields: the OV devices claim to be progressive.
777cb7a01acSMauro Carvalho Chehab 	 */
778cb7a01acSMauro Carvalho Chehab 	fmt->field = V4L2_FIELD_NONE;
779cb7a01acSMauro Carvalho Chehab 	/*
780cb7a01acSMauro Carvalho Chehab 	 * Round requested image size down to the nearest
781cb7a01acSMauro Carvalho Chehab 	 * we support, but not below the smallest.
782cb7a01acSMauro Carvalho Chehab 	 */
783cb7a01acSMauro Carvalho Chehab 	for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES;
784cb7a01acSMauro Carvalho Chehab 	     wsize++)
785cb7a01acSMauro Carvalho Chehab 		if (fmt->width >= wsize->width && fmt->height >= wsize->height)
786cb7a01acSMauro Carvalho Chehab 			break;
787cb7a01acSMauro Carvalho Chehab 	if (wsize >= ov7670_win_sizes + N_WIN_SIZES)
788cb7a01acSMauro Carvalho Chehab 		wsize--;   /* Take the smallest one */
789cb7a01acSMauro Carvalho Chehab 	if (ret_wsize != NULL)
790cb7a01acSMauro Carvalho Chehab 		*ret_wsize = wsize;
791cb7a01acSMauro Carvalho Chehab 	/*
792cb7a01acSMauro Carvalho Chehab 	 * Note the size we'll actually handle.
793cb7a01acSMauro Carvalho Chehab 	 */
794cb7a01acSMauro Carvalho Chehab 	fmt->width = wsize->width;
795cb7a01acSMauro Carvalho Chehab 	fmt->height = wsize->height;
796cb7a01acSMauro Carvalho Chehab 	fmt->colorspace = ov7670_formats[index].colorspace;
797cb7a01acSMauro Carvalho Chehab 	return 0;
798cb7a01acSMauro Carvalho Chehab }
799cb7a01acSMauro Carvalho Chehab 
800cb7a01acSMauro Carvalho Chehab static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd,
801cb7a01acSMauro Carvalho Chehab 			    struct v4l2_mbus_framefmt *fmt)
802cb7a01acSMauro Carvalho Chehab {
803cb7a01acSMauro Carvalho Chehab 	return ov7670_try_fmt_internal(sd, fmt, NULL, NULL);
804cb7a01acSMauro Carvalho Chehab }
805cb7a01acSMauro Carvalho Chehab 
806cb7a01acSMauro Carvalho Chehab /*
807cb7a01acSMauro Carvalho Chehab  * Set a format.
808cb7a01acSMauro Carvalho Chehab  */
809cb7a01acSMauro Carvalho Chehab static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd,
810cb7a01acSMauro Carvalho Chehab 			  struct v4l2_mbus_framefmt *fmt)
811cb7a01acSMauro Carvalho Chehab {
812cb7a01acSMauro Carvalho Chehab 	struct ov7670_format_struct *ovfmt;
813cb7a01acSMauro Carvalho Chehab 	struct ov7670_win_size *wsize;
814cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
815cb7a01acSMauro Carvalho Chehab 	unsigned char com7;
816cb7a01acSMauro Carvalho Chehab 	int ret;
817cb7a01acSMauro Carvalho Chehab 
818cb7a01acSMauro Carvalho Chehab 	ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize);
819cb7a01acSMauro Carvalho Chehab 
820cb7a01acSMauro Carvalho Chehab 	if (ret)
821cb7a01acSMauro Carvalho Chehab 		return ret;
822cb7a01acSMauro Carvalho Chehab 	/*
823cb7a01acSMauro Carvalho Chehab 	 * COM7 is a pain in the ass, it doesn't like to be read then
824cb7a01acSMauro Carvalho Chehab 	 * quickly written afterward.  But we have everything we need
825cb7a01acSMauro Carvalho Chehab 	 * to set it absolutely here, as long as the format-specific
826cb7a01acSMauro Carvalho Chehab 	 * register sets list it first.
827cb7a01acSMauro Carvalho Chehab 	 */
828cb7a01acSMauro Carvalho Chehab 	com7 = ovfmt->regs[0].value;
829cb7a01acSMauro Carvalho Chehab 	com7 |= wsize->com7_bit;
830cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM7, com7);
831cb7a01acSMauro Carvalho Chehab 	/*
832cb7a01acSMauro Carvalho Chehab 	 * Now write the rest of the array.  Also store start/stops
833cb7a01acSMauro Carvalho Chehab 	 */
834cb7a01acSMauro Carvalho Chehab 	ov7670_write_array(sd, ovfmt->regs + 1);
835cb7a01acSMauro Carvalho Chehab 	ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
836cb7a01acSMauro Carvalho Chehab 			wsize->vstop);
837cb7a01acSMauro Carvalho Chehab 	ret = 0;
838cb7a01acSMauro Carvalho Chehab 	if (wsize->regs)
839cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write_array(sd, wsize->regs);
840cb7a01acSMauro Carvalho Chehab 	info->fmt = ovfmt;
841cb7a01acSMauro Carvalho Chehab 
842cb7a01acSMauro Carvalho Chehab 	/*
843cb7a01acSMauro Carvalho Chehab 	 * If we're running RGB565, we must rewrite clkrc after setting
844cb7a01acSMauro Carvalho Chehab 	 * the other parameters or the image looks poor.  If we're *not*
845cb7a01acSMauro Carvalho Chehab 	 * doing RGB565, we must not rewrite clkrc or the image looks
846cb7a01acSMauro Carvalho Chehab 	 * *really* poor.
847cb7a01acSMauro Carvalho Chehab 	 *
848cb7a01acSMauro Carvalho Chehab 	 * (Update) Now that we retain clkrc state, we should be able
849cb7a01acSMauro Carvalho Chehab 	 * to write it unconditionally, and that will make the frame
850cb7a01acSMauro Carvalho Chehab 	 * rate persistent too.
851cb7a01acSMauro Carvalho Chehab 	 */
852cb7a01acSMauro Carvalho Chehab 	if (ret == 0)
853cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
854cb7a01acSMauro Carvalho Chehab 	return 0;
855cb7a01acSMauro Carvalho Chehab }
856cb7a01acSMauro Carvalho Chehab 
857cb7a01acSMauro Carvalho Chehab /*
858cb7a01acSMauro Carvalho Chehab  * Implement G/S_PARM.  There is a "high quality" mode we could try
859cb7a01acSMauro Carvalho Chehab  * to do someday; for now, we just do the frame rate tweak.
860cb7a01acSMauro Carvalho Chehab  */
861cb7a01acSMauro Carvalho Chehab static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
862cb7a01acSMauro Carvalho Chehab {
863cb7a01acSMauro Carvalho Chehab 	struct v4l2_captureparm *cp = &parms->parm.capture;
864cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
865cb7a01acSMauro Carvalho Chehab 
866cb7a01acSMauro Carvalho Chehab 	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
867cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
868cb7a01acSMauro Carvalho Chehab 
869cb7a01acSMauro Carvalho Chehab 	memset(cp, 0, sizeof(struct v4l2_captureparm));
870cb7a01acSMauro Carvalho Chehab 	cp->capability = V4L2_CAP_TIMEPERFRAME;
871cb7a01acSMauro Carvalho Chehab 	cp->timeperframe.numerator = 1;
872cb7a01acSMauro Carvalho Chehab 	cp->timeperframe.denominator = info->clock_speed;
873cb7a01acSMauro Carvalho Chehab 	if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
874cb7a01acSMauro Carvalho Chehab 		cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE);
875cb7a01acSMauro Carvalho Chehab 	return 0;
876cb7a01acSMauro Carvalho Chehab }
877cb7a01acSMauro Carvalho Chehab 
878cb7a01acSMauro Carvalho Chehab static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
879cb7a01acSMauro Carvalho Chehab {
880cb7a01acSMauro Carvalho Chehab 	struct v4l2_captureparm *cp = &parms->parm.capture;
881cb7a01acSMauro Carvalho Chehab 	struct v4l2_fract *tpf = &cp->timeperframe;
882cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
883cb7a01acSMauro Carvalho Chehab 	int div;
884cb7a01acSMauro Carvalho Chehab 
885cb7a01acSMauro Carvalho Chehab 	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
886cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
887cb7a01acSMauro Carvalho Chehab 	if (cp->extendedmode != 0)
888cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
889cb7a01acSMauro Carvalho Chehab 
890cb7a01acSMauro Carvalho Chehab 	if (tpf->numerator == 0 || tpf->denominator == 0)
891cb7a01acSMauro Carvalho Chehab 		div = 1;  /* Reset to full rate */
892cb7a01acSMauro Carvalho Chehab 	else
893cb7a01acSMauro Carvalho Chehab 		div = (tpf->numerator * info->clock_speed) / tpf->denominator;
894cb7a01acSMauro Carvalho Chehab 	if (div == 0)
895cb7a01acSMauro Carvalho Chehab 		div = 1;
896cb7a01acSMauro Carvalho Chehab 	else if (div > CLK_SCALE)
897cb7a01acSMauro Carvalho Chehab 		div = CLK_SCALE;
898cb7a01acSMauro Carvalho Chehab 	info->clkrc = (info->clkrc & 0x80) | div;
899cb7a01acSMauro Carvalho Chehab 	tpf->numerator = 1;
900cb7a01acSMauro Carvalho Chehab 	tpf->denominator = info->clock_speed / div;
901cb7a01acSMauro Carvalho Chehab 	return ov7670_write(sd, REG_CLKRC, info->clkrc);
902cb7a01acSMauro Carvalho Chehab }
903cb7a01acSMauro Carvalho Chehab 
904cb7a01acSMauro Carvalho Chehab 
905cb7a01acSMauro Carvalho Chehab /*
906cb7a01acSMauro Carvalho Chehab  * Frame intervals.  Since frame rates are controlled with the clock
907cb7a01acSMauro Carvalho Chehab  * divider, we can only do 30/n for integer n values.  So no continuous
908cb7a01acSMauro Carvalho Chehab  * or stepwise options.  Here we just pick a handful of logical values.
909cb7a01acSMauro Carvalho Chehab  */
910cb7a01acSMauro Carvalho Chehab 
911cb7a01acSMauro Carvalho Chehab static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
912cb7a01acSMauro Carvalho Chehab 
913cb7a01acSMauro Carvalho Chehab static int ov7670_enum_frameintervals(struct v4l2_subdev *sd,
914cb7a01acSMauro Carvalho Chehab 		struct v4l2_frmivalenum *interval)
915cb7a01acSMauro Carvalho Chehab {
916cb7a01acSMauro Carvalho Chehab 	if (interval->index >= ARRAY_SIZE(ov7670_frame_rates))
917cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
918cb7a01acSMauro Carvalho Chehab 	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
919cb7a01acSMauro Carvalho Chehab 	interval->discrete.numerator = 1;
920cb7a01acSMauro Carvalho Chehab 	interval->discrete.denominator = ov7670_frame_rates[interval->index];
921cb7a01acSMauro Carvalho Chehab 	return 0;
922cb7a01acSMauro Carvalho Chehab }
923cb7a01acSMauro Carvalho Chehab 
924cb7a01acSMauro Carvalho Chehab /*
925cb7a01acSMauro Carvalho Chehab  * Frame size enumeration
926cb7a01acSMauro Carvalho Chehab  */
927cb7a01acSMauro Carvalho Chehab static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
928cb7a01acSMauro Carvalho Chehab 		struct v4l2_frmsizeenum *fsize)
929cb7a01acSMauro Carvalho Chehab {
930cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
931cb7a01acSMauro Carvalho Chehab 	int i;
932cb7a01acSMauro Carvalho Chehab 	int num_valid = -1;
933cb7a01acSMauro Carvalho Chehab 	__u32 index = fsize->index;
934cb7a01acSMauro Carvalho Chehab 
935cb7a01acSMauro Carvalho Chehab 	/*
936cb7a01acSMauro Carvalho Chehab 	 * If a minimum width/height was requested, filter out the capture
937cb7a01acSMauro Carvalho Chehab 	 * windows that fall outside that.
938cb7a01acSMauro Carvalho Chehab 	 */
939cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < N_WIN_SIZES; i++) {
940cb7a01acSMauro Carvalho Chehab 		struct ov7670_win_size *win = &ov7670_win_sizes[index];
941cb7a01acSMauro Carvalho Chehab 		if (info->min_width && win->width < info->min_width)
942cb7a01acSMauro Carvalho Chehab 			continue;
943cb7a01acSMauro Carvalho Chehab 		if (info->min_height && win->height < info->min_height)
944cb7a01acSMauro Carvalho Chehab 			continue;
945cb7a01acSMauro Carvalho Chehab 		if (index == ++num_valid) {
946cb7a01acSMauro Carvalho Chehab 			fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
947cb7a01acSMauro Carvalho Chehab 			fsize->discrete.width = win->width;
948cb7a01acSMauro Carvalho Chehab 			fsize->discrete.height = win->height;
949cb7a01acSMauro Carvalho Chehab 			return 0;
950cb7a01acSMauro Carvalho Chehab 		}
951cb7a01acSMauro Carvalho Chehab 	}
952cb7a01acSMauro Carvalho Chehab 
953cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
954cb7a01acSMauro Carvalho Chehab }
955cb7a01acSMauro Carvalho Chehab 
956cb7a01acSMauro Carvalho Chehab /*
957cb7a01acSMauro Carvalho Chehab  * Code for dealing with controls.
958cb7a01acSMauro Carvalho Chehab  */
959cb7a01acSMauro Carvalho Chehab 
960cb7a01acSMauro Carvalho Chehab static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
961cb7a01acSMauro Carvalho Chehab 		int matrix[CMATRIX_LEN])
962cb7a01acSMauro Carvalho Chehab {
963cb7a01acSMauro Carvalho Chehab 	int i, ret;
964cb7a01acSMauro Carvalho Chehab 	unsigned char signbits = 0;
965cb7a01acSMauro Carvalho Chehab 
966cb7a01acSMauro Carvalho Chehab 	/*
967cb7a01acSMauro Carvalho Chehab 	 * Weird crap seems to exist in the upper part of
968cb7a01acSMauro Carvalho Chehab 	 * the sign bits register, so let's preserve it.
969cb7a01acSMauro Carvalho Chehab 	 */
970cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
971cb7a01acSMauro Carvalho Chehab 	signbits &= 0xc0;
972cb7a01acSMauro Carvalho Chehab 
973cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < CMATRIX_LEN; i++) {
974cb7a01acSMauro Carvalho Chehab 		unsigned char raw;
975cb7a01acSMauro Carvalho Chehab 
976cb7a01acSMauro Carvalho Chehab 		if (matrix[i] < 0) {
977cb7a01acSMauro Carvalho Chehab 			signbits |= (1 << i);
978cb7a01acSMauro Carvalho Chehab 			if (matrix[i] < -255)
979cb7a01acSMauro Carvalho Chehab 				raw = 0xff;
980cb7a01acSMauro Carvalho Chehab 			else
981cb7a01acSMauro Carvalho Chehab 				raw = (-1 * matrix[i]) & 0xff;
982cb7a01acSMauro Carvalho Chehab 		}
983cb7a01acSMauro Carvalho Chehab 		else {
984cb7a01acSMauro Carvalho Chehab 			if (matrix[i] > 255)
985cb7a01acSMauro Carvalho Chehab 				raw = 0xff;
986cb7a01acSMauro Carvalho Chehab 			else
987cb7a01acSMauro Carvalho Chehab 				raw = matrix[i] & 0xff;
988cb7a01acSMauro Carvalho Chehab 		}
989cb7a01acSMauro Carvalho Chehab 		ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
990cb7a01acSMauro Carvalho Chehab 	}
991cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
992cb7a01acSMauro Carvalho Chehab 	return ret;
993cb7a01acSMauro Carvalho Chehab }
994cb7a01acSMauro Carvalho Chehab 
995cb7a01acSMauro Carvalho Chehab 
996cb7a01acSMauro Carvalho Chehab /*
997cb7a01acSMauro Carvalho Chehab  * Hue also requires messing with the color matrix.  It also requires
998cb7a01acSMauro Carvalho Chehab  * trig functions, which tend not to be well supported in the kernel.
999cb7a01acSMauro Carvalho Chehab  * So here is a simple table of sine values, 0-90 degrees, in steps
1000cb7a01acSMauro Carvalho Chehab  * of five degrees.  Values are multiplied by 1000.
1001cb7a01acSMauro Carvalho Chehab  *
1002cb7a01acSMauro Carvalho Chehab  * The following naive approximate trig functions require an argument
1003cb7a01acSMauro Carvalho Chehab  * carefully limited to -180 <= theta <= 180.
1004cb7a01acSMauro Carvalho Chehab  */
1005cb7a01acSMauro Carvalho Chehab #define SIN_STEP 5
1006cb7a01acSMauro Carvalho Chehab static const int ov7670_sin_table[] = {
1007cb7a01acSMauro Carvalho Chehab 	   0,	 87,   173,   258,   342,   422,
1008cb7a01acSMauro Carvalho Chehab 	 499,	573,   642,   707,   766,   819,
1009cb7a01acSMauro Carvalho Chehab 	 866,	906,   939,   965,   984,   996,
1010cb7a01acSMauro Carvalho Chehab 	1000
1011cb7a01acSMauro Carvalho Chehab };
1012cb7a01acSMauro Carvalho Chehab 
1013cb7a01acSMauro Carvalho Chehab static int ov7670_sine(int theta)
1014cb7a01acSMauro Carvalho Chehab {
1015cb7a01acSMauro Carvalho Chehab 	int chs = 1;
1016cb7a01acSMauro Carvalho Chehab 	int sine;
1017cb7a01acSMauro Carvalho Chehab 
1018cb7a01acSMauro Carvalho Chehab 	if (theta < 0) {
1019cb7a01acSMauro Carvalho Chehab 		theta = -theta;
1020cb7a01acSMauro Carvalho Chehab 		chs = -1;
1021cb7a01acSMauro Carvalho Chehab 	}
1022cb7a01acSMauro Carvalho Chehab 	if (theta <= 90)
1023cb7a01acSMauro Carvalho Chehab 		sine = ov7670_sin_table[theta/SIN_STEP];
1024cb7a01acSMauro Carvalho Chehab 	else {
1025cb7a01acSMauro Carvalho Chehab 		theta -= 90;
1026cb7a01acSMauro Carvalho Chehab 		sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
1027cb7a01acSMauro Carvalho Chehab 	}
1028cb7a01acSMauro Carvalho Chehab 	return sine*chs;
1029cb7a01acSMauro Carvalho Chehab }
1030cb7a01acSMauro Carvalho Chehab 
1031cb7a01acSMauro Carvalho Chehab static int ov7670_cosine(int theta)
1032cb7a01acSMauro Carvalho Chehab {
1033cb7a01acSMauro Carvalho Chehab 	theta = 90 - theta;
1034cb7a01acSMauro Carvalho Chehab 	if (theta > 180)
1035cb7a01acSMauro Carvalho Chehab 		theta -= 360;
1036cb7a01acSMauro Carvalho Chehab 	else if (theta < -180)
1037cb7a01acSMauro Carvalho Chehab 		theta += 360;
1038cb7a01acSMauro Carvalho Chehab 	return ov7670_sine(theta);
1039cb7a01acSMauro Carvalho Chehab }
1040cb7a01acSMauro Carvalho Chehab 
1041cb7a01acSMauro Carvalho Chehab 
1042cb7a01acSMauro Carvalho Chehab 
1043cb7a01acSMauro Carvalho Chehab 
1044cb7a01acSMauro Carvalho Chehab static void ov7670_calc_cmatrix(struct ov7670_info *info,
1045cb7a01acSMauro Carvalho Chehab 		int matrix[CMATRIX_LEN])
1046cb7a01acSMauro Carvalho Chehab {
1047cb7a01acSMauro Carvalho Chehab 	int i;
1048cb7a01acSMauro Carvalho Chehab 	/*
1049cb7a01acSMauro Carvalho Chehab 	 * Apply the current saturation setting first.
1050cb7a01acSMauro Carvalho Chehab 	 */
1051cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < CMATRIX_LEN; i++)
1052cb7a01acSMauro Carvalho Chehab 		matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7;
1053cb7a01acSMauro Carvalho Chehab 	/*
1054cb7a01acSMauro Carvalho Chehab 	 * Then, if need be, rotate the hue value.
1055cb7a01acSMauro Carvalho Chehab 	 */
1056cb7a01acSMauro Carvalho Chehab 	if (info->hue != 0) {
1057cb7a01acSMauro Carvalho Chehab 		int sinth, costh, tmpmatrix[CMATRIX_LEN];
1058cb7a01acSMauro Carvalho Chehab 
1059cb7a01acSMauro Carvalho Chehab 		memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
1060cb7a01acSMauro Carvalho Chehab 		sinth = ov7670_sine(info->hue);
1061cb7a01acSMauro Carvalho Chehab 		costh = ov7670_cosine(info->hue);
1062cb7a01acSMauro Carvalho Chehab 
1063cb7a01acSMauro Carvalho Chehab 		matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
1064cb7a01acSMauro Carvalho Chehab 		matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
1065cb7a01acSMauro Carvalho Chehab 		matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
1066cb7a01acSMauro Carvalho Chehab 		matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
1067cb7a01acSMauro Carvalho Chehab 		matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
1068cb7a01acSMauro Carvalho Chehab 		matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
1069cb7a01acSMauro Carvalho Chehab 	}
1070cb7a01acSMauro Carvalho Chehab }
1071cb7a01acSMauro Carvalho Chehab 
1072cb7a01acSMauro Carvalho Chehab 
1073cb7a01acSMauro Carvalho Chehab 
1074cb7a01acSMauro Carvalho Chehab static int ov7670_s_sat(struct v4l2_subdev *sd, int value)
1075cb7a01acSMauro Carvalho Chehab {
1076cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1077cb7a01acSMauro Carvalho Chehab 	int matrix[CMATRIX_LEN];
1078cb7a01acSMauro Carvalho Chehab 	int ret;
1079cb7a01acSMauro Carvalho Chehab 
1080cb7a01acSMauro Carvalho Chehab 	info->sat = value;
1081cb7a01acSMauro Carvalho Chehab 	ov7670_calc_cmatrix(info, matrix);
1082cb7a01acSMauro Carvalho Chehab 	ret = ov7670_store_cmatrix(sd, matrix);
1083cb7a01acSMauro Carvalho Chehab 	return ret;
1084cb7a01acSMauro Carvalho Chehab }
1085cb7a01acSMauro Carvalho Chehab 
1086cb7a01acSMauro Carvalho Chehab static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value)
1087cb7a01acSMauro Carvalho Chehab {
1088cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1089cb7a01acSMauro Carvalho Chehab 
1090cb7a01acSMauro Carvalho Chehab 	*value = info->sat;
1091cb7a01acSMauro Carvalho Chehab 	return 0;
1092cb7a01acSMauro Carvalho Chehab }
1093cb7a01acSMauro Carvalho Chehab 
1094cb7a01acSMauro Carvalho Chehab static int ov7670_s_hue(struct v4l2_subdev *sd, int value)
1095cb7a01acSMauro Carvalho Chehab {
1096cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1097cb7a01acSMauro Carvalho Chehab 	int matrix[CMATRIX_LEN];
1098cb7a01acSMauro Carvalho Chehab 	int ret;
1099cb7a01acSMauro Carvalho Chehab 
1100cb7a01acSMauro Carvalho Chehab 	if (value < -180 || value > 180)
1101cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1102cb7a01acSMauro Carvalho Chehab 	info->hue = value;
1103cb7a01acSMauro Carvalho Chehab 	ov7670_calc_cmatrix(info, matrix);
1104cb7a01acSMauro Carvalho Chehab 	ret = ov7670_store_cmatrix(sd, matrix);
1105cb7a01acSMauro Carvalho Chehab 	return ret;
1106cb7a01acSMauro Carvalho Chehab }
1107cb7a01acSMauro Carvalho Chehab 
1108cb7a01acSMauro Carvalho Chehab 
1109cb7a01acSMauro Carvalho Chehab static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value)
1110cb7a01acSMauro Carvalho Chehab {
1111cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1112cb7a01acSMauro Carvalho Chehab 
1113cb7a01acSMauro Carvalho Chehab 	*value = info->hue;
1114cb7a01acSMauro Carvalho Chehab 	return 0;
1115cb7a01acSMauro Carvalho Chehab }
1116cb7a01acSMauro Carvalho Chehab 
1117cb7a01acSMauro Carvalho Chehab 
1118cb7a01acSMauro Carvalho Chehab /*
1119cb7a01acSMauro Carvalho Chehab  * Some weird registers seem to store values in a sign/magnitude format!
1120cb7a01acSMauro Carvalho Chehab  */
1121cb7a01acSMauro Carvalho Chehab static unsigned char ov7670_sm_to_abs(unsigned char v)
1122cb7a01acSMauro Carvalho Chehab {
1123cb7a01acSMauro Carvalho Chehab 	if ((v & 0x80) == 0)
1124cb7a01acSMauro Carvalho Chehab 		return v + 128;
1125cb7a01acSMauro Carvalho Chehab 	return 128 - (v & 0x7f);
1126cb7a01acSMauro Carvalho Chehab }
1127cb7a01acSMauro Carvalho Chehab 
1128cb7a01acSMauro Carvalho Chehab 
1129cb7a01acSMauro Carvalho Chehab static unsigned char ov7670_abs_to_sm(unsigned char v)
1130cb7a01acSMauro Carvalho Chehab {
1131cb7a01acSMauro Carvalho Chehab 	if (v > 127)
1132cb7a01acSMauro Carvalho Chehab 		return v & 0x7f;
1133cb7a01acSMauro Carvalho Chehab 	return (128 - v) | 0x80;
1134cb7a01acSMauro Carvalho Chehab }
1135cb7a01acSMauro Carvalho Chehab 
1136cb7a01acSMauro Carvalho Chehab static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
1137cb7a01acSMauro Carvalho Chehab {
1138cb7a01acSMauro Carvalho Chehab 	unsigned char com8 = 0, v;
1139cb7a01acSMauro Carvalho Chehab 	int ret;
1140cb7a01acSMauro Carvalho Chehab 
1141cb7a01acSMauro Carvalho Chehab 	ov7670_read(sd, REG_COM8, &com8);
1142cb7a01acSMauro Carvalho Chehab 	com8 &= ~COM8_AEC;
1143cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM8, com8);
1144cb7a01acSMauro Carvalho Chehab 	v = ov7670_abs_to_sm(value);
1145cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_BRIGHT, v);
1146cb7a01acSMauro Carvalho Chehab 	return ret;
1147cb7a01acSMauro Carvalho Chehab }
1148cb7a01acSMauro Carvalho Chehab 
1149cb7a01acSMauro Carvalho Chehab static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value)
1150cb7a01acSMauro Carvalho Chehab {
1151cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1152cb7a01acSMauro Carvalho Chehab 	int ret = ov7670_read(sd, REG_BRIGHT, &v);
1153cb7a01acSMauro Carvalho Chehab 
1154cb7a01acSMauro Carvalho Chehab 	*value = ov7670_sm_to_abs(v);
1155cb7a01acSMauro Carvalho Chehab 	return ret;
1156cb7a01acSMauro Carvalho Chehab }
1157cb7a01acSMauro Carvalho Chehab 
1158cb7a01acSMauro Carvalho Chehab static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
1159cb7a01acSMauro Carvalho Chehab {
1160cb7a01acSMauro Carvalho Chehab 	return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
1161cb7a01acSMauro Carvalho Chehab }
1162cb7a01acSMauro Carvalho Chehab 
1163cb7a01acSMauro Carvalho Chehab static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value)
1164cb7a01acSMauro Carvalho Chehab {
1165cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1166cb7a01acSMauro Carvalho Chehab 	int ret = ov7670_read(sd, REG_CONTRAS, &v);
1167cb7a01acSMauro Carvalho Chehab 
1168cb7a01acSMauro Carvalho Chehab 	*value = v;
1169cb7a01acSMauro Carvalho Chehab 	return ret;
1170cb7a01acSMauro Carvalho Chehab }
1171cb7a01acSMauro Carvalho Chehab 
1172cb7a01acSMauro Carvalho Chehab static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value)
1173cb7a01acSMauro Carvalho Chehab {
1174cb7a01acSMauro Carvalho Chehab 	int ret;
1175cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1176cb7a01acSMauro Carvalho Chehab 
1177cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
1178cb7a01acSMauro Carvalho Chehab 	*value = (v & MVFP_MIRROR) == MVFP_MIRROR;
1179cb7a01acSMauro Carvalho Chehab 	return ret;
1180cb7a01acSMauro Carvalho Chehab }
1181cb7a01acSMauro Carvalho Chehab 
1182cb7a01acSMauro Carvalho Chehab 
1183cb7a01acSMauro Carvalho Chehab static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
1184cb7a01acSMauro Carvalho Chehab {
1185cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1186cb7a01acSMauro Carvalho Chehab 	int ret;
1187cb7a01acSMauro Carvalho Chehab 
1188cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
1189cb7a01acSMauro Carvalho Chehab 	if (value)
1190cb7a01acSMauro Carvalho Chehab 		v |= MVFP_MIRROR;
1191cb7a01acSMauro Carvalho Chehab 	else
1192cb7a01acSMauro Carvalho Chehab 		v &= ~MVFP_MIRROR;
1193cb7a01acSMauro Carvalho Chehab 	msleep(10);  /* FIXME */
1194cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_MVFP, v);
1195cb7a01acSMauro Carvalho Chehab 	return ret;
1196cb7a01acSMauro Carvalho Chehab }
1197cb7a01acSMauro Carvalho Chehab 
1198cb7a01acSMauro Carvalho Chehab 
1199cb7a01acSMauro Carvalho Chehab 
1200cb7a01acSMauro Carvalho Chehab static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value)
1201cb7a01acSMauro Carvalho Chehab {
1202cb7a01acSMauro Carvalho Chehab 	int ret;
1203cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1204cb7a01acSMauro Carvalho Chehab 
1205cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
1206cb7a01acSMauro Carvalho Chehab 	*value = (v & MVFP_FLIP) == MVFP_FLIP;
1207cb7a01acSMauro Carvalho Chehab 	return ret;
1208cb7a01acSMauro Carvalho Chehab }
1209cb7a01acSMauro Carvalho Chehab 
1210cb7a01acSMauro Carvalho Chehab 
1211cb7a01acSMauro Carvalho Chehab static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
1212cb7a01acSMauro Carvalho Chehab {
1213cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1214cb7a01acSMauro Carvalho Chehab 	int ret;
1215cb7a01acSMauro Carvalho Chehab 
1216cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
1217cb7a01acSMauro Carvalho Chehab 	if (value)
1218cb7a01acSMauro Carvalho Chehab 		v |= MVFP_FLIP;
1219cb7a01acSMauro Carvalho Chehab 	else
1220cb7a01acSMauro Carvalho Chehab 		v &= ~MVFP_FLIP;
1221cb7a01acSMauro Carvalho Chehab 	msleep(10);  /* FIXME */
1222cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_MVFP, v);
1223cb7a01acSMauro Carvalho Chehab 	return ret;
1224cb7a01acSMauro Carvalho Chehab }
1225cb7a01acSMauro Carvalho Chehab 
1226cb7a01acSMauro Carvalho Chehab /*
1227cb7a01acSMauro Carvalho Chehab  * GAIN is split between REG_GAIN and REG_VREF[7:6].  If one believes
1228cb7a01acSMauro Carvalho Chehab  * the data sheet, the VREF parts should be the most significant, but
1229cb7a01acSMauro Carvalho Chehab  * experience shows otherwise.  There seems to be little value in
1230cb7a01acSMauro Carvalho Chehab  * messing with the VREF bits, so we leave them alone.
1231cb7a01acSMauro Carvalho Chehab  */
1232cb7a01acSMauro Carvalho Chehab static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
1233cb7a01acSMauro Carvalho Chehab {
1234cb7a01acSMauro Carvalho Chehab 	int ret;
1235cb7a01acSMauro Carvalho Chehab 	unsigned char gain;
1236cb7a01acSMauro Carvalho Chehab 
1237cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_GAIN, &gain);
1238cb7a01acSMauro Carvalho Chehab 	*value = gain;
1239cb7a01acSMauro Carvalho Chehab 	return ret;
1240cb7a01acSMauro Carvalho Chehab }
1241cb7a01acSMauro Carvalho Chehab 
1242cb7a01acSMauro Carvalho Chehab static int ov7670_s_gain(struct v4l2_subdev *sd, int value)
1243cb7a01acSMauro Carvalho Chehab {
1244cb7a01acSMauro Carvalho Chehab 	int ret;
1245cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1246cb7a01acSMauro Carvalho Chehab 
1247cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_GAIN, value & 0xff);
1248cb7a01acSMauro Carvalho Chehab 	/* Have to turn off AGC as well */
1249cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1250cb7a01acSMauro Carvalho Chehab 		ret = ov7670_read(sd, REG_COM8, &com8);
1251cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC);
1252cb7a01acSMauro Carvalho Chehab 	}
1253cb7a01acSMauro Carvalho Chehab 	return ret;
1254cb7a01acSMauro Carvalho Chehab }
1255cb7a01acSMauro Carvalho Chehab 
1256cb7a01acSMauro Carvalho Chehab /*
1257cb7a01acSMauro Carvalho Chehab  * Tweak autogain.
1258cb7a01acSMauro Carvalho Chehab  */
1259cb7a01acSMauro Carvalho Chehab static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value)
1260cb7a01acSMauro Carvalho Chehab {
1261cb7a01acSMauro Carvalho Chehab 	int ret;
1262cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1263cb7a01acSMauro Carvalho Chehab 
1264cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1265cb7a01acSMauro Carvalho Chehab 	*value = (com8 & COM8_AGC) != 0;
1266cb7a01acSMauro Carvalho Chehab 	return ret;
1267cb7a01acSMauro Carvalho Chehab }
1268cb7a01acSMauro Carvalho Chehab 
1269cb7a01acSMauro Carvalho Chehab static int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
1270cb7a01acSMauro Carvalho Chehab {
1271cb7a01acSMauro Carvalho Chehab 	int ret;
1272cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1273cb7a01acSMauro Carvalho Chehab 
1274cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1275cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1276cb7a01acSMauro Carvalho Chehab 		if (value)
1277cb7a01acSMauro Carvalho Chehab 			com8 |= COM8_AGC;
1278cb7a01acSMauro Carvalho Chehab 		else
1279cb7a01acSMauro Carvalho Chehab 			com8 &= ~COM8_AGC;
1280cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8);
1281cb7a01acSMauro Carvalho Chehab 	}
1282cb7a01acSMauro Carvalho Chehab 	return ret;
1283cb7a01acSMauro Carvalho Chehab }
1284cb7a01acSMauro Carvalho Chehab 
1285cb7a01acSMauro Carvalho Chehab /*
1286cb7a01acSMauro Carvalho Chehab  * Exposure is spread all over the place: top 6 bits in AECHH, middle
1287cb7a01acSMauro Carvalho Chehab  * 8 in AECH, and two stashed in COM1 just for the hell of it.
1288cb7a01acSMauro Carvalho Chehab  */
1289cb7a01acSMauro Carvalho Chehab static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value)
1290cb7a01acSMauro Carvalho Chehab {
1291cb7a01acSMauro Carvalho Chehab 	int ret;
1292cb7a01acSMauro Carvalho Chehab 	unsigned char com1, aech, aechh;
1293cb7a01acSMauro Carvalho Chehab 
1294cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM1, &com1) +
1295cb7a01acSMauro Carvalho Chehab 		ov7670_read(sd, REG_AECH, &aech) +
1296cb7a01acSMauro Carvalho Chehab 		ov7670_read(sd, REG_AECHH, &aechh);
1297cb7a01acSMauro Carvalho Chehab 	*value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03);
1298cb7a01acSMauro Carvalho Chehab 	return ret;
1299cb7a01acSMauro Carvalho Chehab }
1300cb7a01acSMauro Carvalho Chehab 
1301cb7a01acSMauro Carvalho Chehab static int ov7670_s_exp(struct v4l2_subdev *sd, int value)
1302cb7a01acSMauro Carvalho Chehab {
1303cb7a01acSMauro Carvalho Chehab 	int ret;
1304cb7a01acSMauro Carvalho Chehab 	unsigned char com1, com8, aech, aechh;
1305cb7a01acSMauro Carvalho Chehab 
1306cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM1, &com1) +
1307cb7a01acSMauro Carvalho Chehab 		ov7670_read(sd, REG_COM8, &com8);
1308cb7a01acSMauro Carvalho Chehab 		ov7670_read(sd, REG_AECHH, &aechh);
1309cb7a01acSMauro Carvalho Chehab 	if (ret)
1310cb7a01acSMauro Carvalho Chehab 		return ret;
1311cb7a01acSMauro Carvalho Chehab 
1312cb7a01acSMauro Carvalho Chehab 	com1 = (com1 & 0xfc) | (value & 0x03);
1313cb7a01acSMauro Carvalho Chehab 	aech = (value >> 2) & 0xff;
1314cb7a01acSMauro Carvalho Chehab 	aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f);
1315cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_COM1, com1) +
1316cb7a01acSMauro Carvalho Chehab 		ov7670_write(sd, REG_AECH, aech) +
1317cb7a01acSMauro Carvalho Chehab 		ov7670_write(sd, REG_AECHH, aechh);
1318cb7a01acSMauro Carvalho Chehab 	/* Have to turn off AEC as well */
1319cb7a01acSMauro Carvalho Chehab 	if (ret == 0)
1320cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC);
1321cb7a01acSMauro Carvalho Chehab 	return ret;
1322cb7a01acSMauro Carvalho Chehab }
1323cb7a01acSMauro Carvalho Chehab 
1324cb7a01acSMauro Carvalho Chehab /*
1325cb7a01acSMauro Carvalho Chehab  * Tweak autoexposure.
1326cb7a01acSMauro Carvalho Chehab  */
1327cb7a01acSMauro Carvalho Chehab static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value)
1328cb7a01acSMauro Carvalho Chehab {
1329cb7a01acSMauro Carvalho Chehab 	int ret;
1330cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1331cb7a01acSMauro Carvalho Chehab 	enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value;
1332cb7a01acSMauro Carvalho Chehab 
1333cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1334cb7a01acSMauro Carvalho Chehab 	if (com8 & COM8_AEC)
1335cb7a01acSMauro Carvalho Chehab 		*atype = V4L2_EXPOSURE_AUTO;
1336cb7a01acSMauro Carvalho Chehab 	else
1337cb7a01acSMauro Carvalho Chehab 		*atype = V4L2_EXPOSURE_MANUAL;
1338cb7a01acSMauro Carvalho Chehab 	return ret;
1339cb7a01acSMauro Carvalho Chehab }
1340cb7a01acSMauro Carvalho Chehab 
1341cb7a01acSMauro Carvalho Chehab static int ov7670_s_autoexp(struct v4l2_subdev *sd,
1342cb7a01acSMauro Carvalho Chehab 		enum v4l2_exposure_auto_type value)
1343cb7a01acSMauro Carvalho Chehab {
1344cb7a01acSMauro Carvalho Chehab 	int ret;
1345cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1346cb7a01acSMauro Carvalho Chehab 
1347cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1348cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1349cb7a01acSMauro Carvalho Chehab 		if (value == V4L2_EXPOSURE_AUTO)
1350cb7a01acSMauro Carvalho Chehab 			com8 |= COM8_AEC;
1351cb7a01acSMauro Carvalho Chehab 		else
1352cb7a01acSMauro Carvalho Chehab 			com8 &= ~COM8_AEC;
1353cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8);
1354cb7a01acSMauro Carvalho Chehab 	}
1355cb7a01acSMauro Carvalho Chehab 	return ret;
1356cb7a01acSMauro Carvalho Chehab }
1357cb7a01acSMauro Carvalho Chehab 
1358cb7a01acSMauro Carvalho Chehab 
1359cb7a01acSMauro Carvalho Chehab 
1360cb7a01acSMauro Carvalho Chehab static int ov7670_queryctrl(struct v4l2_subdev *sd,
1361cb7a01acSMauro Carvalho Chehab 		struct v4l2_queryctrl *qc)
1362cb7a01acSMauro Carvalho Chehab {
1363cb7a01acSMauro Carvalho Chehab 	/* Fill in min, max, step and default value for these controls. */
1364cb7a01acSMauro Carvalho Chehab 	switch (qc->id) {
1365cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1366cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
1367cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1368cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
1369cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
1370cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
1371cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
1372cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1373cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128);
1374cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
1375cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0);
1376cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
1377cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
1378cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1379cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
1380cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
1381cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500);
1382cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE_AUTO:
1383cb7a01acSMauro Carvalho Chehab 		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
1384cb7a01acSMauro Carvalho Chehab 	}
1385cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1386cb7a01acSMauro Carvalho Chehab }
1387cb7a01acSMauro Carvalho Chehab 
1388cb7a01acSMauro Carvalho Chehab static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
1389cb7a01acSMauro Carvalho Chehab {
1390cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
1391cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1392cb7a01acSMauro Carvalho Chehab 		return ov7670_g_brightness(sd, &ctrl->value);
1393cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1394cb7a01acSMauro Carvalho Chehab 		return ov7670_g_contrast(sd, &ctrl->value);
1395cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1396cb7a01acSMauro Carvalho Chehab 		return ov7670_g_sat(sd, &ctrl->value);
1397cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
1398cb7a01acSMauro Carvalho Chehab 		return ov7670_g_hue(sd, &ctrl->value);
1399cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
1400cb7a01acSMauro Carvalho Chehab 		return ov7670_g_vflip(sd, &ctrl->value);
1401cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
1402cb7a01acSMauro Carvalho Chehab 		return ov7670_g_hflip(sd, &ctrl->value);
1403cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
1404cb7a01acSMauro Carvalho Chehab 		return ov7670_g_gain(sd, &ctrl->value);
1405cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1406cb7a01acSMauro Carvalho Chehab 		return ov7670_g_autogain(sd, &ctrl->value);
1407cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
1408cb7a01acSMauro Carvalho Chehab 		return ov7670_g_exp(sd, &ctrl->value);
1409cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE_AUTO:
1410cb7a01acSMauro Carvalho Chehab 		return ov7670_g_autoexp(sd, &ctrl->value);
1411cb7a01acSMauro Carvalho Chehab 	}
1412cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1413cb7a01acSMauro Carvalho Chehab }
1414cb7a01acSMauro Carvalho Chehab 
1415cb7a01acSMauro Carvalho Chehab static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
1416cb7a01acSMauro Carvalho Chehab {
1417cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
1418cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1419cb7a01acSMauro Carvalho Chehab 		return ov7670_s_brightness(sd, ctrl->value);
1420cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1421cb7a01acSMauro Carvalho Chehab 		return ov7670_s_contrast(sd, ctrl->value);
1422cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1423cb7a01acSMauro Carvalho Chehab 		return ov7670_s_sat(sd, ctrl->value);
1424cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HUE:
1425cb7a01acSMauro Carvalho Chehab 		return ov7670_s_hue(sd, ctrl->value);
1426cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
1427cb7a01acSMauro Carvalho Chehab 		return ov7670_s_vflip(sd, ctrl->value);
1428cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
1429cb7a01acSMauro Carvalho Chehab 		return ov7670_s_hflip(sd, ctrl->value);
1430cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_GAIN:
1431cb7a01acSMauro Carvalho Chehab 		return ov7670_s_gain(sd, ctrl->value);
1432cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1433cb7a01acSMauro Carvalho Chehab 		return ov7670_s_autogain(sd, ctrl->value);
1434cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE:
1435cb7a01acSMauro Carvalho Chehab 		return ov7670_s_exp(sd, ctrl->value);
1436cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE_AUTO:
1437cb7a01acSMauro Carvalho Chehab 		return ov7670_s_autoexp(sd,
1438cb7a01acSMauro Carvalho Chehab 				(enum v4l2_exposure_auto_type) ctrl->value);
1439cb7a01acSMauro Carvalho Chehab 	}
1440cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1441cb7a01acSMauro Carvalho Chehab }
1442cb7a01acSMauro Carvalho Chehab 
1443cb7a01acSMauro Carvalho Chehab static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
1444cb7a01acSMauro Carvalho Chehab 		struct v4l2_dbg_chip_ident *chip)
1445cb7a01acSMauro Carvalho Chehab {
1446cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1447cb7a01acSMauro Carvalho Chehab 
1448cb7a01acSMauro Carvalho Chehab 	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
1449cb7a01acSMauro Carvalho Chehab }
1450cb7a01acSMauro Carvalho Chehab 
1451cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1452cb7a01acSMauro Carvalho Chehab static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1453cb7a01acSMauro Carvalho Chehab {
1454cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1455cb7a01acSMauro Carvalho Chehab 	unsigned char val = 0;
1456cb7a01acSMauro Carvalho Chehab 	int ret;
1457cb7a01acSMauro Carvalho Chehab 
1458cb7a01acSMauro Carvalho Chehab 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
1459cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1460cb7a01acSMauro Carvalho Chehab 	if (!capable(CAP_SYS_ADMIN))
1461cb7a01acSMauro Carvalho Chehab 		return -EPERM;
1462cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, reg->reg & 0xff, &val);
1463cb7a01acSMauro Carvalho Chehab 	reg->val = val;
1464cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
1465cb7a01acSMauro Carvalho Chehab 	return ret;
1466cb7a01acSMauro Carvalho Chehab }
1467cb7a01acSMauro Carvalho Chehab 
1468cb7a01acSMauro Carvalho Chehab static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1469cb7a01acSMauro Carvalho Chehab {
1470cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
1471cb7a01acSMauro Carvalho Chehab 
1472cb7a01acSMauro Carvalho Chehab 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
1473cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
1474cb7a01acSMauro Carvalho Chehab 	if (!capable(CAP_SYS_ADMIN))
1475cb7a01acSMauro Carvalho Chehab 		return -EPERM;
1476cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
1477cb7a01acSMauro Carvalho Chehab 	return 0;
1478cb7a01acSMauro Carvalho Chehab }
1479cb7a01acSMauro Carvalho Chehab #endif
1480cb7a01acSMauro Carvalho Chehab 
1481cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1482cb7a01acSMauro Carvalho Chehab 
1483cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ov7670_core_ops = {
1484cb7a01acSMauro Carvalho Chehab 	.g_chip_ident = ov7670_g_chip_ident,
1485cb7a01acSMauro Carvalho Chehab 	.g_ctrl = ov7670_g_ctrl,
1486cb7a01acSMauro Carvalho Chehab 	.s_ctrl = ov7670_s_ctrl,
1487cb7a01acSMauro Carvalho Chehab 	.queryctrl = ov7670_queryctrl,
1488cb7a01acSMauro Carvalho Chehab 	.reset = ov7670_reset,
1489cb7a01acSMauro Carvalho Chehab 	.init = ov7670_init,
1490cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1491cb7a01acSMauro Carvalho Chehab 	.g_register = ov7670_g_register,
1492cb7a01acSMauro Carvalho Chehab 	.s_register = ov7670_s_register,
1493cb7a01acSMauro Carvalho Chehab #endif
1494cb7a01acSMauro Carvalho Chehab };
1495cb7a01acSMauro Carvalho Chehab 
1496cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ov7670_video_ops = {
1497cb7a01acSMauro Carvalho Chehab 	.enum_mbus_fmt = ov7670_enum_mbus_fmt,
1498cb7a01acSMauro Carvalho Chehab 	.try_mbus_fmt = ov7670_try_mbus_fmt,
1499cb7a01acSMauro Carvalho Chehab 	.s_mbus_fmt = ov7670_s_mbus_fmt,
1500cb7a01acSMauro Carvalho Chehab 	.s_parm = ov7670_s_parm,
1501cb7a01acSMauro Carvalho Chehab 	.g_parm = ov7670_g_parm,
1502cb7a01acSMauro Carvalho Chehab 	.enum_frameintervals = ov7670_enum_frameintervals,
1503cb7a01acSMauro Carvalho Chehab 	.enum_framesizes = ov7670_enum_framesizes,
1504cb7a01acSMauro Carvalho Chehab };
1505cb7a01acSMauro Carvalho Chehab 
1506cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ov7670_ops = {
1507cb7a01acSMauro Carvalho Chehab 	.core = &ov7670_core_ops,
1508cb7a01acSMauro Carvalho Chehab 	.video = &ov7670_video_ops,
1509cb7a01acSMauro Carvalho Chehab };
1510cb7a01acSMauro Carvalho Chehab 
1511cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1512cb7a01acSMauro Carvalho Chehab 
1513cb7a01acSMauro Carvalho Chehab static int ov7670_probe(struct i2c_client *client,
1514cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
1515cb7a01acSMauro Carvalho Chehab {
1516cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1517cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info;
1518cb7a01acSMauro Carvalho Chehab 	int ret;
1519cb7a01acSMauro Carvalho Chehab 
1520cb7a01acSMauro Carvalho Chehab 	info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
1521cb7a01acSMauro Carvalho Chehab 	if (info == NULL)
1522cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1523cb7a01acSMauro Carvalho Chehab 	sd = &info->sd;
1524cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
1525cb7a01acSMauro Carvalho Chehab 
1526cb7a01acSMauro Carvalho Chehab 	info->clock_speed = 30; /* default: a guess */
1527cb7a01acSMauro Carvalho Chehab 	if (client->dev.platform_data) {
1528cb7a01acSMauro Carvalho Chehab 		struct ov7670_config *config = client->dev.platform_data;
1529cb7a01acSMauro Carvalho Chehab 
1530cb7a01acSMauro Carvalho Chehab 		/*
1531cb7a01acSMauro Carvalho Chehab 		 * Must apply configuration before initializing device, because it
1532cb7a01acSMauro Carvalho Chehab 		 * selects I/O method.
1533cb7a01acSMauro Carvalho Chehab 		 */
1534cb7a01acSMauro Carvalho Chehab 		info->min_width = config->min_width;
1535cb7a01acSMauro Carvalho Chehab 		info->min_height = config->min_height;
1536cb7a01acSMauro Carvalho Chehab 		info->use_smbus = config->use_smbus;
1537cb7a01acSMauro Carvalho Chehab 
1538cb7a01acSMauro Carvalho Chehab 		if (config->clock_speed)
1539cb7a01acSMauro Carvalho Chehab 			info->clock_speed = config->clock_speed;
1540cb7a01acSMauro Carvalho Chehab 	}
1541cb7a01acSMauro Carvalho Chehab 
1542cb7a01acSMauro Carvalho Chehab 	/* Make sure it's an ov7670 */
1543cb7a01acSMauro Carvalho Chehab 	ret = ov7670_detect(sd);
1544cb7a01acSMauro Carvalho Chehab 	if (ret) {
1545cb7a01acSMauro Carvalho Chehab 		v4l_dbg(1, debug, client,
1546cb7a01acSMauro Carvalho Chehab 			"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
1547cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
1548cb7a01acSMauro Carvalho Chehab 		kfree(info);
1549cb7a01acSMauro Carvalho Chehab 		return ret;
1550cb7a01acSMauro Carvalho Chehab 	}
1551cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1552cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
1553cb7a01acSMauro Carvalho Chehab 
1554cb7a01acSMauro Carvalho Chehab 	info->fmt = &ov7670_formats[0];
1555cb7a01acSMauro Carvalho Chehab 	info->sat = 128;	/* Review this */
1556cb7a01acSMauro Carvalho Chehab 	info->clkrc = info->clock_speed / 30;
1557cb7a01acSMauro Carvalho Chehab 	return 0;
1558cb7a01acSMauro Carvalho Chehab }
1559cb7a01acSMauro Carvalho Chehab 
1560cb7a01acSMauro Carvalho Chehab 
1561cb7a01acSMauro Carvalho Chehab static int ov7670_remove(struct i2c_client *client)
1562cb7a01acSMauro Carvalho Chehab {
1563cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1564cb7a01acSMauro Carvalho Chehab 
1565cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
1566cb7a01acSMauro Carvalho Chehab 	kfree(to_state(sd));
1567cb7a01acSMauro Carvalho Chehab 	return 0;
1568cb7a01acSMauro Carvalho Chehab }
1569cb7a01acSMauro Carvalho Chehab 
1570cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ov7670_id[] = {
1571cb7a01acSMauro Carvalho Chehab 	{ "ov7670", 0 },
1572cb7a01acSMauro Carvalho Chehab 	{ }
1573cb7a01acSMauro Carvalho Chehab };
1574cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ov7670_id);
1575cb7a01acSMauro Carvalho Chehab 
1576cb7a01acSMauro Carvalho Chehab static struct i2c_driver ov7670_driver = {
1577cb7a01acSMauro Carvalho Chehab 	.driver = {
1578cb7a01acSMauro Carvalho Chehab 		.owner	= THIS_MODULE,
1579cb7a01acSMauro Carvalho Chehab 		.name	= "ov7670",
1580cb7a01acSMauro Carvalho Chehab 	},
1581cb7a01acSMauro Carvalho Chehab 	.probe		= ov7670_probe,
1582cb7a01acSMauro Carvalho Chehab 	.remove		= ov7670_remove,
1583cb7a01acSMauro Carvalho Chehab 	.id_table	= ov7670_id,
1584cb7a01acSMauro Carvalho Chehab };
1585cb7a01acSMauro Carvalho Chehab 
1586cb7a01acSMauro Carvalho Chehab module_i2c_driver(ov7670_driver);
1587