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