xref: /openbmc/linux/drivers/media/i2c/ov7670.c (revision ed5c2f5f)
112237550SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cb7a01acSMauro Carvalho Chehab /*
3cb7a01acSMauro Carvalho Chehab  * A V4L2 driver for OmniVision OV7670 cameras.
4cb7a01acSMauro Carvalho Chehab  *
5cb7a01acSMauro Carvalho Chehab  * Copyright 2006 One Laptop Per Child Association, Inc.  Written
6cb7a01acSMauro Carvalho Chehab  * by Jonathan Corbet with substantial inspiration from Mark
7cb7a01acSMauro Carvalho Chehab  * McClelland's ovcamchip code.
8cb7a01acSMauro Carvalho Chehab  *
9cb7a01acSMauro Carvalho Chehab  * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
10cb7a01acSMauro Carvalho Chehab  */
110a024d63SHans Verkuil #include <linux/clk.h>
12cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
13cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
18a0c4164eSHans Verkuil #include <linux/gpio.h>
19a0c4164eSHans Verkuil #include <linux/gpio/consumer.h>
20cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
217852adf8SAkinobu Mita #include <media/v4l2-event.h>
22492959c7SJavier Martin #include <media/v4l2-ctrls.h>
2301b84448SJacopo Mondi #include <media/v4l2-fwnode.h>
24cb7a01acSMauro Carvalho Chehab #include <media/v4l2-mediabus.h>
254721b3ebSAxel Lin #include <media/v4l2-image-sizes.h>
26b5dcee22SMauro Carvalho Chehab #include <media/i2c/ov7670.h>
27cb7a01acSMauro Carvalho Chehab 
28cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
29cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
30cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
31cb7a01acSMauro Carvalho Chehab 
32cb7a01acSMauro Carvalho Chehab static bool debug;
33cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
34cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
35cb7a01acSMauro Carvalho Chehab 
36cb7a01acSMauro Carvalho Chehab /*
37cb7a01acSMauro Carvalho Chehab  * The 7670 sits on i2c with ID 0x42
38cb7a01acSMauro Carvalho Chehab  */
39cb7a01acSMauro Carvalho Chehab #define OV7670_I2C_ADDR 0x42
40cb7a01acSMauro Carvalho Chehab 
41f6dd927fSJavier Martin #define PLL_FACTOR	4
42f6dd927fSJavier Martin 
43cb7a01acSMauro Carvalho Chehab /* Registers */
44cb7a01acSMauro Carvalho Chehab #define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
45cb7a01acSMauro Carvalho Chehab #define REG_BLUE	0x01	/* blue gain */
46cb7a01acSMauro Carvalho Chehab #define REG_RED		0x02	/* red gain */
47cb7a01acSMauro Carvalho Chehab #define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
48cb7a01acSMauro Carvalho Chehab #define REG_COM1	0x04	/* Control 1 */
49cb7a01acSMauro Carvalho Chehab #define  COM1_CCIR656	  0x40  /* CCIR656 enable */
50cb7a01acSMauro Carvalho Chehab #define REG_BAVE	0x05	/* U/B Average level */
51cb7a01acSMauro Carvalho Chehab #define REG_GbAVE	0x06	/* Y/Gb Average level */
52cb7a01acSMauro Carvalho Chehab #define REG_AECHH	0x07	/* AEC MS 5 bits */
53cb7a01acSMauro Carvalho Chehab #define REG_RAVE	0x08	/* V/R Average level */
54cb7a01acSMauro Carvalho Chehab #define REG_COM2	0x09	/* Control 2 */
55cb7a01acSMauro Carvalho Chehab #define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
56cb7a01acSMauro Carvalho Chehab #define REG_PID		0x0a	/* Product ID MSB */
57cb7a01acSMauro Carvalho Chehab #define REG_VER		0x0b	/* Product ID LSB */
58cb7a01acSMauro Carvalho Chehab #define REG_COM3	0x0c	/* Control 3 */
59cb7a01acSMauro Carvalho Chehab #define  COM3_SWAP	  0x40	  /* Byte swap */
60cb7a01acSMauro Carvalho Chehab #define  COM3_SCALEEN	  0x08	  /* Enable scaling */
61cb7a01acSMauro Carvalho Chehab #define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
62cb7a01acSMauro Carvalho Chehab #define REG_COM4	0x0d	/* Control 4 */
63cb7a01acSMauro Carvalho Chehab #define REG_COM5	0x0e	/* All "reserved" */
64cb7a01acSMauro Carvalho Chehab #define REG_COM6	0x0f	/* Control 6 */
65cb7a01acSMauro Carvalho Chehab #define REG_AECH	0x10	/* More bits of AEC value */
66cb7a01acSMauro Carvalho Chehab #define REG_CLKRC	0x11	/* Clocl control */
67cb7a01acSMauro Carvalho Chehab #define   CLK_EXT	  0x40	  /* Use external clock directly */
68cb7a01acSMauro Carvalho Chehab #define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
69cb7a01acSMauro Carvalho Chehab #define REG_COM7	0x12	/* Control 7 */
70cb7a01acSMauro Carvalho Chehab #define   COM7_RESET	  0x80	  /* Register reset */
71cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_MASK	  0x38
72cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_VGA	  0x00
73cb7a01acSMauro Carvalho Chehab #define	  COM7_FMT_CIF	  0x20	  /* CIF format */
74cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
75cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
76cb7a01acSMauro Carvalho Chehab #define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
77cb7a01acSMauro Carvalho Chehab #define	  COM7_YUV	  0x00	  /* YUV */
78cb7a01acSMauro Carvalho Chehab #define	  COM7_BAYER	  0x01	  /* Bayer format */
79cb7a01acSMauro Carvalho Chehab #define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
80cb7a01acSMauro Carvalho Chehab #define REG_COM8	0x13	/* Control 8 */
81cb7a01acSMauro Carvalho Chehab #define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
82cb7a01acSMauro Carvalho Chehab #define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
83cb7a01acSMauro Carvalho Chehab #define   COM8_BFILT	  0x20	  /* Band filter enable */
84cb7a01acSMauro Carvalho Chehab #define   COM8_AGC	  0x04	  /* Auto gain enable */
85cb7a01acSMauro Carvalho Chehab #define   COM8_AWB	  0x02	  /* White balance enable */
86cb7a01acSMauro Carvalho Chehab #define   COM8_AEC	  0x01	  /* Auto exposure enable */
87cb7a01acSMauro Carvalho Chehab #define REG_COM9	0x14	/* Control 9  - gain ceiling */
88cb7a01acSMauro Carvalho Chehab #define REG_COM10	0x15	/* Control 10 */
89cb7a01acSMauro Carvalho Chehab #define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
90cb7a01acSMauro Carvalho Chehab #define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
91cb7a01acSMauro Carvalho Chehab #define   COM10_HREF_REV  0x08	  /* Reverse HREF */
92cb7a01acSMauro Carvalho Chehab #define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
93cb7a01acSMauro Carvalho Chehab #define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
94cb7a01acSMauro Carvalho Chehab #define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
95cb7a01acSMauro Carvalho Chehab #define REG_HSTART	0x17	/* Horiz start high bits */
96cb7a01acSMauro Carvalho Chehab #define REG_HSTOP	0x18	/* Horiz stop high bits */
97cb7a01acSMauro Carvalho Chehab #define REG_VSTART	0x19	/* Vert start high bits */
98cb7a01acSMauro Carvalho Chehab #define REG_VSTOP	0x1a	/* Vert stop high bits */
99cb7a01acSMauro Carvalho Chehab #define REG_PSHFT	0x1b	/* Pixel delay after HREF */
100cb7a01acSMauro Carvalho Chehab #define REG_MIDH	0x1c	/* Manuf. ID high */
101cb7a01acSMauro Carvalho Chehab #define REG_MIDL	0x1d	/* Manuf. ID low */
102cb7a01acSMauro Carvalho Chehab #define REG_MVFP	0x1e	/* Mirror / vflip */
103cb7a01acSMauro Carvalho Chehab #define   MVFP_MIRROR	  0x20	  /* Mirror image */
104cb7a01acSMauro Carvalho Chehab #define   MVFP_FLIP	  0x10	  /* Vertical flip */
105cb7a01acSMauro Carvalho Chehab 
106cb7a01acSMauro Carvalho Chehab #define REG_AEW		0x24	/* AGC upper limit */
107cb7a01acSMauro Carvalho Chehab #define REG_AEB		0x25	/* AGC lower limit */
108cb7a01acSMauro Carvalho Chehab #define REG_VPT		0x26	/* AGC/AEC fast mode op region */
109cb7a01acSMauro Carvalho Chehab #define REG_HSYST	0x30	/* HSYNC rising edge delay */
110cb7a01acSMauro Carvalho Chehab #define REG_HSYEN	0x31	/* HSYNC falling edge delay */
111cb7a01acSMauro Carvalho Chehab #define REG_HREF	0x32	/* HREF pieces */
112cb7a01acSMauro Carvalho Chehab #define REG_TSLB	0x3a	/* lots of stuff */
113cb7a01acSMauro Carvalho Chehab #define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
114cb7a01acSMauro Carvalho Chehab #define REG_COM11	0x3b	/* Control 11 */
115cb7a01acSMauro Carvalho Chehab #define   COM11_NIGHT	  0x80	  /* NIght mode enable */
116cb7a01acSMauro Carvalho Chehab #define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
117cb7a01acSMauro Carvalho Chehab #define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
118cb7a01acSMauro Carvalho Chehab #define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
119cb7a01acSMauro Carvalho Chehab #define   COM11_EXP	  0x02
120cb7a01acSMauro Carvalho Chehab #define REG_COM12	0x3c	/* Control 12 */
121cb7a01acSMauro Carvalho Chehab #define   COM12_HREF	  0x80	  /* HREF always */
122cb7a01acSMauro Carvalho Chehab #define REG_COM13	0x3d	/* Control 13 */
123cb7a01acSMauro Carvalho Chehab #define   COM13_GAMMA	  0x80	  /* Gamma enable */
124cb7a01acSMauro Carvalho Chehab #define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
125cb7a01acSMauro Carvalho Chehab #define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
126cb7a01acSMauro Carvalho Chehab #define REG_COM14	0x3e	/* Control 14 */
127cb7a01acSMauro Carvalho Chehab #define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
128cb7a01acSMauro Carvalho Chehab #define REG_EDGE	0x3f	/* Edge enhancement factor */
129cb7a01acSMauro Carvalho Chehab #define REG_COM15	0x40	/* Control 15 */
130cb7a01acSMauro Carvalho Chehab #define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
131cb7a01acSMauro Carvalho Chehab #define	  COM15_R01FE	  0x80	  /*            01 to FE */
132cb7a01acSMauro Carvalho Chehab #define   COM15_R00FF	  0xc0	  /*            00 to FF */
133cb7a01acSMauro Carvalho Chehab #define   COM15_RGB565	  0x10	  /* RGB565 output */
134cb7a01acSMauro Carvalho Chehab #define   COM15_RGB555	  0x30	  /* RGB555 output */
135cb7a01acSMauro Carvalho Chehab #define REG_COM16	0x41	/* Control 16 */
136cb7a01acSMauro Carvalho Chehab #define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
137cb7a01acSMauro Carvalho Chehab #define REG_COM17	0x42	/* Control 17 */
138cb7a01acSMauro Carvalho Chehab #define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
139cb7a01acSMauro Carvalho Chehab #define   COM17_CBAR	  0x08	  /* DSP Color bar */
140cb7a01acSMauro Carvalho Chehab 
141cb7a01acSMauro Carvalho Chehab /*
142cb7a01acSMauro Carvalho Chehab  * This matrix defines how the colors are generated, must be
143cb7a01acSMauro Carvalho Chehab  * tweaked to adjust hue and saturation.
144cb7a01acSMauro Carvalho Chehab  *
145cb7a01acSMauro Carvalho Chehab  * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
146cb7a01acSMauro Carvalho Chehab  *
147cb7a01acSMauro Carvalho Chehab  * They are nine-bit signed quantities, with the sign bit
148cb7a01acSMauro Carvalho Chehab  * stored in 0x58.  Sign for v-red is bit 0, and up from there.
149cb7a01acSMauro Carvalho Chehab  */
150cb7a01acSMauro Carvalho Chehab #define	REG_CMATRIX_BASE 0x4f
151cb7a01acSMauro Carvalho Chehab #define   CMATRIX_LEN 6
152cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_SIGN 0x58
153cb7a01acSMauro Carvalho Chehab 
154cb7a01acSMauro Carvalho Chehab 
155cb7a01acSMauro Carvalho Chehab #define REG_BRIGHT	0x55	/* Brightness */
156cb7a01acSMauro Carvalho Chehab #define REG_CONTRAS	0x56	/* Contrast control */
157cb7a01acSMauro Carvalho Chehab 
158cb7a01acSMauro Carvalho Chehab #define REG_GFIX	0x69	/* Fix gain control */
159cb7a01acSMauro Carvalho Chehab 
160f6dd927fSJavier Martin #define REG_DBLV	0x6b	/* PLL control an debugging */
16161da76beSJacopo Mondi #define   DBLV_BYPASS	  0x0a	  /* Bypass PLL */
16261da76beSJacopo Mondi #define   DBLV_X4	  0x4a	  /* clock x4 */
16361da76beSJacopo Mondi #define   DBLV_X6	  0x8a	  /* clock x6 */
16461da76beSJacopo Mondi #define   DBLV_X8	  0xca	  /* clock x8 */
165f6dd927fSJavier Martin 
166b48d908dSAkinobu Mita #define REG_SCALING_XSC	0x70	/* Test pattern and horizontal scale factor */
167b48d908dSAkinobu Mita #define   TEST_PATTTERN_0 0x80
168b48d908dSAkinobu Mita #define REG_SCALING_YSC	0x71	/* Test pattern and vertical scale factor */
169b48d908dSAkinobu Mita #define   TEST_PATTTERN_1 0x80
170b48d908dSAkinobu Mita 
171cb7a01acSMauro Carvalho Chehab #define REG_REG76	0x76	/* OV's name */
172cb7a01acSMauro Carvalho Chehab #define   R76_BLKPCOR	  0x80	  /* Black pixel correction enable */
173cb7a01acSMauro Carvalho Chehab #define   R76_WHTPCOR	  0x40	  /* White pixel correction enable */
174cb7a01acSMauro Carvalho Chehab 
175cb7a01acSMauro Carvalho Chehab #define REG_RGB444	0x8c	/* RGB 444 control */
176cb7a01acSMauro Carvalho Chehab #define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
177cb7a01acSMauro Carvalho Chehab #define   R444_RGBX	  0x01	  /* Empty nibble at end */
178cb7a01acSMauro Carvalho Chehab 
179cb7a01acSMauro Carvalho Chehab #define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
180cb7a01acSMauro Carvalho Chehab #define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
181cb7a01acSMauro Carvalho Chehab 
182cb7a01acSMauro Carvalho Chehab #define REG_BD50MAX	0xa5	/* 50hz banding step limit */
183cb7a01acSMauro Carvalho Chehab #define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
184cb7a01acSMauro Carvalho Chehab #define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
185cb7a01acSMauro Carvalho Chehab #define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
186cb7a01acSMauro Carvalho Chehab #define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
187cb7a01acSMauro Carvalho Chehab #define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
188cb7a01acSMauro Carvalho Chehab #define REG_BD60MAX	0xab	/* 60hz banding step limit */
189cb7a01acSMauro Carvalho Chehab 
190d058e237SJavier Martin enum ov7670_model {
191d058e237SJavier Martin 	MODEL_OV7670 = 0,
192d058e237SJavier Martin 	MODEL_OV7675,
193d058e237SJavier Martin };
194d058e237SJavier Martin 
195d058e237SJavier Martin struct ov7670_win_size {
196d058e237SJavier Martin 	int	width;
197d058e237SJavier Martin 	int	height;
198d058e237SJavier Martin 	unsigned char com7_bit;
199d058e237SJavier Martin 	int	hstart;		/* Start/stop values for the camera.  Note */
200d058e237SJavier Martin 	int	hstop;		/* that they do not always make complete */
201d058e237SJavier Martin 	int	vstart;		/* sense to humans, but evidently the sensor */
202d058e237SJavier Martin 	int	vstop;		/* will do the right thing... */
203d058e237SJavier Martin 	struct regval_list *regs; /* Regs to tweak */
204d058e237SJavier Martin };
205d058e237SJavier Martin 
206d058e237SJavier Martin struct ov7670_devtype {
207d058e237SJavier Martin 	/* formats supported for each model */
208d058e237SJavier Martin 	struct ov7670_win_size *win_sizes;
209d058e237SJavier Martin 	unsigned int n_win_sizes;
210f6dd927fSJavier Martin 	/* callbacks for frame rate control */
211f6dd927fSJavier Martin 	int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
212f6dd927fSJavier Martin 	void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
213d058e237SJavier Martin };
214cb7a01acSMauro Carvalho Chehab 
215cb7a01acSMauro Carvalho Chehab /*
216cb7a01acSMauro Carvalho Chehab  * Information we maintain about a known sensor.
217cb7a01acSMauro Carvalho Chehab  */
218cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct;  /* coming later */
219cb7a01acSMauro Carvalho Chehab struct ov7670_info {
220cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
221d94a26f0SWenyou Yang #if defined(CONFIG_MEDIA_CONTROLLER)
222d94a26f0SWenyou Yang 	struct media_pad pad;
223d94a26f0SWenyou Yang #endif
224492959c7SJavier Martin 	struct v4l2_ctrl_handler hdl;
225492959c7SJavier Martin 	struct {
226492959c7SJavier Martin 		/* gain cluster */
227492959c7SJavier Martin 		struct v4l2_ctrl *auto_gain;
228492959c7SJavier Martin 		struct v4l2_ctrl *gain;
229492959c7SJavier Martin 	};
230492959c7SJavier Martin 	struct {
231492959c7SJavier Martin 		/* exposure cluster */
232492959c7SJavier Martin 		struct v4l2_ctrl *auto_exposure;
233492959c7SJavier Martin 		struct v4l2_ctrl *exposure;
234492959c7SJavier Martin 	};
235492959c7SJavier Martin 	struct {
236492959c7SJavier Martin 		/* saturation/hue cluster */
237492959c7SJavier Martin 		struct v4l2_ctrl *saturation;
238492959c7SJavier Martin 		struct v4l2_ctrl *hue;
239492959c7SJavier Martin 	};
240c0662dd4SWenyou Yang 	struct v4l2_mbus_framefmt format;
241cb7a01acSMauro Carvalho Chehab 	struct ov7670_format_struct *fmt;  /* Current format */
2425556ab2aSLubomir Rintel 	struct ov7670_win_size *wsize;
2430a024d63SHans Verkuil 	struct clk *clk;
2443d6a8fe2SLubomir Rintel 	int on;
245a0c4164eSHans Verkuil 	struct gpio_desc *resetb_gpio;
246a0c4164eSHans Verkuil 	struct gpio_desc *pwdn_gpio;
24701b84448SJacopo Mondi 	unsigned int mbus_config;	/* Media bus configuration flags */
248cb7a01acSMauro Carvalho Chehab 	int min_width;			/* Filter out smaller sizes */
249cb7a01acSMauro Carvalho Chehab 	int min_height;			/* Filter out smaller sizes */
250cb7a01acSMauro Carvalho Chehab 	int clock_speed;		/* External clock speed (MHz) */
251cb7a01acSMauro Carvalho Chehab 	u8 clkrc;			/* Clock divider value */
252cb7a01acSMauro Carvalho Chehab 	bool use_smbus;			/* Use smbus I/O instead of I2C */
25304ee6d92SJavier Martin 	bool pll_bypass;
254ee95258eSJavier Martin 	bool pclk_hb_disable;
255d058e237SJavier Martin 	const struct ov7670_devtype *devtype; /* Device specifics */
256cb7a01acSMauro Carvalho Chehab };
257cb7a01acSMauro Carvalho Chehab 
258cb7a01acSMauro Carvalho Chehab static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
259cb7a01acSMauro Carvalho Chehab {
260cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct ov7670_info, sd);
261cb7a01acSMauro Carvalho Chehab }
262cb7a01acSMauro Carvalho Chehab 
263492959c7SJavier Martin static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
264492959c7SJavier Martin {
265492959c7SJavier Martin 	return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd;
266492959c7SJavier Martin }
267492959c7SJavier Martin 
268cb7a01acSMauro Carvalho Chehab 
269cb7a01acSMauro Carvalho Chehab 
270cb7a01acSMauro Carvalho Chehab /*
271cb7a01acSMauro Carvalho Chehab  * The default register settings, as obtained from OmniVision.  There
272cb7a01acSMauro Carvalho Chehab  * is really no making sense of most of these - lots of "reserved" values
273cb7a01acSMauro Carvalho Chehab  * and such.
274cb7a01acSMauro Carvalho Chehab  *
275cb7a01acSMauro Carvalho Chehab  * These settings give VGA YUYV.
276cb7a01acSMauro Carvalho Chehab  */
277cb7a01acSMauro Carvalho Chehab 
278cb7a01acSMauro Carvalho Chehab struct regval_list {
279cb7a01acSMauro Carvalho Chehab 	unsigned char reg_num;
280cb7a01acSMauro Carvalho Chehab 	unsigned char value;
281cb7a01acSMauro Carvalho Chehab };
282cb7a01acSMauro Carvalho Chehab 
283cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_default_regs[] = {
284cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RESET },
285cb7a01acSMauro Carvalho Chehab /*
286cb7a01acSMauro Carvalho Chehab  * Clock scale: 3 = 15fps
287cb7a01acSMauro Carvalho Chehab  *              2 = 20fps
288cb7a01acSMauro Carvalho Chehab  *              1 = 30fps
289cb7a01acSMauro Carvalho Chehab  */
290cb7a01acSMauro Carvalho Chehab 	{ REG_CLKRC, 0x1 },	/* OV: clock scale (30 fps) */
291cb7a01acSMauro Carvalho Chehab 	{ REG_TSLB,  0x04 },	/* OV */
292cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, 0 },	/* VGA */
293cb7a01acSMauro Carvalho Chehab 	/*
294cb7a01acSMauro Carvalho Chehab 	 * Set the hardware window.  These values from OV don't entirely
295cb7a01acSMauro Carvalho Chehab 	 * make sense - hstop is less than hstart.  But they work...
296cb7a01acSMauro Carvalho Chehab 	 */
297cb7a01acSMauro Carvalho Chehab 	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
298cb7a01acSMauro Carvalho Chehab 	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
299cb7a01acSMauro Carvalho Chehab 	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
300cb7a01acSMauro Carvalho Chehab 
301cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, 0 },	{ REG_COM14, 0 },
302cb7a01acSMauro Carvalho Chehab 	/* Mystery scaling numbers */
303b48d908dSAkinobu Mita 	{ REG_SCALING_XSC, 0x3a },
304b48d908dSAkinobu Mita 	{ REG_SCALING_YSC, 0x35 },
305cb7a01acSMauro Carvalho Chehab 	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
306cb7a01acSMauro Carvalho Chehab 	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
307cb7a01acSMauro Carvalho Chehab 
308cb7a01acSMauro Carvalho Chehab 	/* Gamma curve values */
309cb7a01acSMauro Carvalho Chehab 	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
310cb7a01acSMauro Carvalho Chehab 	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
311cb7a01acSMauro Carvalho Chehab 	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
312cb7a01acSMauro Carvalho Chehab 	{ 0x80, 0x76 },		{ 0x81, 0x80 },
313cb7a01acSMauro Carvalho Chehab 	{ 0x82, 0x88 },		{ 0x83, 0x8f },
314cb7a01acSMauro Carvalho Chehab 	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
315cb7a01acSMauro Carvalho Chehab 	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
316cb7a01acSMauro Carvalho Chehab 	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
317cb7a01acSMauro Carvalho Chehab 
318cb7a01acSMauro Carvalho Chehab 	/* AGC and AEC parameters.  Note we start by disabling those features,
319cb7a01acSMauro Carvalho Chehab 	   then turn them only after tweaking the values. */
320cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
321cb7a01acSMauro Carvalho Chehab 	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
322cb7a01acSMauro Carvalho Chehab 	{ REG_COM4, 0x40 }, /* magic reserved bit */
323cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
324cb7a01acSMauro Carvalho Chehab 	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
325cb7a01acSMauro Carvalho Chehab 	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
326cb7a01acSMauro Carvalho Chehab 	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
327cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
328cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
329cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
330cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC7, 0x94 },
331cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
332cb7a01acSMauro Carvalho Chehab 
333cb7a01acSMauro Carvalho Chehab 	/* Almost all of these are magic "reserved" values.  */
334cb7a01acSMauro Carvalho Chehab 	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
335cb7a01acSMauro Carvalho Chehab 	{ 0x16, 0x02 },		{ REG_MVFP, 0x07 },
336cb7a01acSMauro Carvalho Chehab 	{ 0x21, 0x02 },		{ 0x22, 0x91 },
337cb7a01acSMauro Carvalho Chehab 	{ 0x29, 0x07 },		{ 0x33, 0x0b },
338cb7a01acSMauro Carvalho Chehab 	{ 0x35, 0x0b },		{ 0x37, 0x1d },
339cb7a01acSMauro Carvalho Chehab 	{ 0x38, 0x71 },		{ 0x39, 0x2a },
340cb7a01acSMauro Carvalho Chehab 	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
341cb7a01acSMauro Carvalho Chehab 	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
342cb7a01acSMauro Carvalho Chehab 	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
343cb7a01acSMauro Carvalho Chehab 	{ 0x8d, 0x4f },		{ 0x8e, 0 },
344cb7a01acSMauro Carvalho Chehab 	{ 0x8f, 0 },		{ 0x90, 0 },
345cb7a01acSMauro Carvalho Chehab 	{ 0x91, 0 },		{ 0x96, 0 },
346cb7a01acSMauro Carvalho Chehab 	{ 0x9a, 0 },		{ 0xb0, 0x84 },
347cb7a01acSMauro Carvalho Chehab 	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
348cb7a01acSMauro Carvalho Chehab 	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
349cb7a01acSMauro Carvalho Chehab 
350cb7a01acSMauro Carvalho Chehab 	/* More reserved magic, some of which tweaks white balance */
351cb7a01acSMauro Carvalho Chehab 	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
352cb7a01acSMauro Carvalho Chehab 	{ 0x45, 0x34 },		{ 0x46, 0x58 },
353cb7a01acSMauro Carvalho Chehab 	{ 0x47, 0x28 },		{ 0x48, 0x3a },
354cb7a01acSMauro Carvalho Chehab 	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
355cb7a01acSMauro Carvalho Chehab 	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
356cb7a01acSMauro Carvalho Chehab 	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
357cb7a01acSMauro Carvalho Chehab 	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
358cb7a01acSMauro Carvalho Chehab 	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
359cb7a01acSMauro Carvalho Chehab 	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
360cb7a01acSMauro Carvalho Chehab 	{ REG_RED, 0x60 },
361cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
362cb7a01acSMauro Carvalho Chehab 
363cb7a01acSMauro Carvalho Chehab 	/* Matrix coefficients */
364cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
365cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0 },		{ 0x52, 0x22 },
366cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0x5e },		{ 0x54, 0x80 },
367cb7a01acSMauro Carvalho Chehab 	{ 0x58, 0x9e },
368cb7a01acSMauro Carvalho Chehab 
369cb7a01acSMauro Carvalho Chehab 	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
370cb7a01acSMauro Carvalho Chehab 	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
371cb7a01acSMauro Carvalho Chehab 	{ 0x4c, 0 },		{ 0x77, 0x01 },
372cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
373cb7a01acSMauro Carvalho Chehab 	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
374cb7a01acSMauro Carvalho Chehab 	{ 0x56, 0x40 },
375cb7a01acSMauro Carvalho Chehab 
376cb7a01acSMauro Carvalho Chehab 	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
377cb7a01acSMauro Carvalho Chehab 	{ 0xa4, 0x88 },		{ 0x96, 0 },
378cb7a01acSMauro Carvalho Chehab 	{ 0x97, 0x30 },		{ 0x98, 0x20 },
379cb7a01acSMauro Carvalho Chehab 	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
380cb7a01acSMauro Carvalho Chehab 	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
381cb7a01acSMauro Carvalho Chehab 	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
382cb7a01acSMauro Carvalho Chehab 	{ 0x78, 0x04 },
383cb7a01acSMauro Carvalho Chehab 
384cb7a01acSMauro Carvalho Chehab 	/* Extra-weird stuff.  Some sort of multiplexor register */
385cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
386cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
387cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
388cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
389cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
390cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
391cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
392cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
393cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
394cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
395cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
396cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x26 },
397cb7a01acSMauro Carvalho Chehab 
398cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },	/* END MARKER */
399cb7a01acSMauro Carvalho Chehab };
400cb7a01acSMauro Carvalho Chehab 
401cb7a01acSMauro Carvalho Chehab 
402cb7a01acSMauro Carvalho Chehab /*
403cb7a01acSMauro Carvalho Chehab  * Here we'll try to encapsulate the changes for just the output
404cb7a01acSMauro Carvalho Chehab  * video format.
405cb7a01acSMauro Carvalho Chehab  *
406cb7a01acSMauro Carvalho Chehab  * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
407cb7a01acSMauro Carvalho Chehab  *
408cb7a01acSMauro Carvalho Chehab  * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
409cb7a01acSMauro Carvalho Chehab  */
410cb7a01acSMauro Carvalho Chehab 
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_yuv422[] = {
413cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, 0x0 },  /* Selects YUV mode */
414cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, 0 },	/* No RGB444 please */
415cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0 },	/* CCIR601 */
416cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_R00FF },
417c01b7429SJavier Martin 	{ REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */
418cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0x80 },		/* "matrix coefficient 1" */
419cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0x80 },		/* "matrix coefficient 2" */
420cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
421cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x22 },		/* "matrix coefficient 4" */
422cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0x5e },		/* "matrix coefficient 5" */
423cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0x80 },		/* "matrix coefficient 6" */
424cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
425cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
426cb7a01acSMauro Carvalho Chehab };
427cb7a01acSMauro Carvalho Chehab 
428cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb565[] = {
429cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
430cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, 0 },	/* No RGB444 please */
431cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0x0 },	/* CCIR601 */
432cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_RGB565 },
433cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },	/* 16x gain ceiling; 0x8 is reserved bit */
434cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0xb3 },		/* "matrix coefficient 1" */
435cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0xb3 },		/* "matrix coefficient 2" */
436cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
437cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x3d },		/* "matrix coefficient 4" */
438cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0xa7 },		/* "matrix coefficient 5" */
439cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0xe4 },		/* "matrix coefficient 6" */
440cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
441cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
442cb7a01acSMauro Carvalho Chehab };
443cb7a01acSMauro Carvalho Chehab 
444cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb444[] = {
445cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
446cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
447cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0x0 },	/* CCIR601 */
448cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
449cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },	/* 16x gain ceiling; 0x8 is reserved bit */
450cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0xb3 },		/* "matrix coefficient 1" */
451cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0xb3 },		/* "matrix coefficient 2" */
452cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
453cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x3d },		/* "matrix coefficient 4" */
454cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0xa7 },		/* "matrix coefficient 5" */
455cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0xe4 },		/* "matrix coefficient 6" */
456cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
457cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
458cb7a01acSMauro Carvalho Chehab };
459cb7a01acSMauro Carvalho Chehab 
460cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_raw[] = {
461cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_BAYER },
462cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
463cb7a01acSMauro Carvalho Chehab 	{ REG_COM16, 0x3d }, /* Edge enhancement, denoise */
464cb7a01acSMauro Carvalho Chehab 	{ REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
465cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
466cb7a01acSMauro Carvalho Chehab };
467cb7a01acSMauro Carvalho Chehab 
468cb7a01acSMauro Carvalho Chehab 
469cb7a01acSMauro Carvalho Chehab 
470cb7a01acSMauro Carvalho Chehab /*
471cb7a01acSMauro Carvalho Chehab  * Low-level register I/O.
472cb7a01acSMauro Carvalho Chehab  *
473cb7a01acSMauro Carvalho Chehab  * Note that there are two versions of these.  On the XO 1, the
474cb7a01acSMauro Carvalho Chehab  * i2c controller only does SMBUS, so that's what we use.  The
475cb7a01acSMauro Carvalho Chehab  * ov7670 is not really an SMBUS device, though, so the communication
476cb7a01acSMauro Carvalho Chehab  * is not always entirely reliable.
477cb7a01acSMauro Carvalho Chehab  */
478cb7a01acSMauro Carvalho Chehab static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
479cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
480cb7a01acSMauro Carvalho Chehab {
481cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
482cb7a01acSMauro Carvalho Chehab 	int ret;
483cb7a01acSMauro Carvalho Chehab 
484cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_read_byte_data(client, reg);
485cb7a01acSMauro Carvalho Chehab 	if (ret >= 0) {
486cb7a01acSMauro Carvalho Chehab 		*value = (unsigned char)ret;
487cb7a01acSMauro Carvalho Chehab 		ret = 0;
488cb7a01acSMauro Carvalho Chehab 	}
489cb7a01acSMauro Carvalho Chehab 	return ret;
490cb7a01acSMauro Carvalho Chehab }
491cb7a01acSMauro Carvalho Chehab 
492cb7a01acSMauro Carvalho Chehab 
493cb7a01acSMauro Carvalho Chehab static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
494cb7a01acSMauro Carvalho Chehab 		unsigned char value)
495cb7a01acSMauro Carvalho Chehab {
496cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
497cb7a01acSMauro Carvalho Chehab 	int ret = i2c_smbus_write_byte_data(client, reg, value);
498cb7a01acSMauro Carvalho Chehab 
499cb7a01acSMauro Carvalho Chehab 	if (reg == REG_COM7 && (value & COM7_RESET))
500cb7a01acSMauro Carvalho Chehab 		msleep(5);  /* Wait for reset to run */
501cb7a01acSMauro Carvalho Chehab 	return ret;
502cb7a01acSMauro Carvalho Chehab }
503cb7a01acSMauro Carvalho Chehab 
504cb7a01acSMauro Carvalho Chehab /*
505cb7a01acSMauro Carvalho Chehab  * On most platforms, we'd rather do straight i2c I/O.
506cb7a01acSMauro Carvalho Chehab  */
507cb7a01acSMauro Carvalho Chehab static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
508cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
509cb7a01acSMauro Carvalho Chehab {
510cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
511cb7a01acSMauro Carvalho Chehab 	u8 data = reg;
512cb7a01acSMauro Carvalho Chehab 	struct i2c_msg msg;
513cb7a01acSMauro Carvalho Chehab 	int ret;
514cb7a01acSMauro Carvalho Chehab 
515cb7a01acSMauro Carvalho Chehab 	/*
516cb7a01acSMauro Carvalho Chehab 	 * Send out the register address...
517cb7a01acSMauro Carvalho Chehab 	 */
518cb7a01acSMauro Carvalho Chehab 	msg.addr = client->addr;
519cb7a01acSMauro Carvalho Chehab 	msg.flags = 0;
520cb7a01acSMauro Carvalho Chehab 	msg.len = 1;
521cb7a01acSMauro Carvalho Chehab 	msg.buf = &data;
522cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
523cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
524cb7a01acSMauro Carvalho Chehab 		printk(KERN_ERR "Error %d on register write\n", ret);
525cb7a01acSMauro Carvalho Chehab 		return ret;
526cb7a01acSMauro Carvalho Chehab 	}
527cb7a01acSMauro Carvalho Chehab 	/*
528cb7a01acSMauro Carvalho Chehab 	 * ...then read back the result.
529cb7a01acSMauro Carvalho Chehab 	 */
530cb7a01acSMauro Carvalho Chehab 	msg.flags = I2C_M_RD;
531cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
532cb7a01acSMauro Carvalho Chehab 	if (ret >= 0) {
533cb7a01acSMauro Carvalho Chehab 		*value = data;
534cb7a01acSMauro Carvalho Chehab 		ret = 0;
535cb7a01acSMauro Carvalho Chehab 	}
536cb7a01acSMauro Carvalho Chehab 	return ret;
537cb7a01acSMauro Carvalho Chehab }
538cb7a01acSMauro Carvalho Chehab 
539cb7a01acSMauro Carvalho Chehab 
540cb7a01acSMauro Carvalho Chehab static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
541cb7a01acSMauro Carvalho Chehab 		unsigned char value)
542cb7a01acSMauro Carvalho Chehab {
543cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
544cb7a01acSMauro Carvalho Chehab 	struct i2c_msg msg;
545cb7a01acSMauro Carvalho Chehab 	unsigned char data[2] = { reg, value };
546cb7a01acSMauro Carvalho Chehab 	int ret;
547cb7a01acSMauro Carvalho Chehab 
548cb7a01acSMauro Carvalho Chehab 	msg.addr = client->addr;
549cb7a01acSMauro Carvalho Chehab 	msg.flags = 0;
550cb7a01acSMauro Carvalho Chehab 	msg.len = 2;
551cb7a01acSMauro Carvalho Chehab 	msg.buf = data;
552cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
553cb7a01acSMauro Carvalho Chehab 	if (ret > 0)
554cb7a01acSMauro Carvalho Chehab 		ret = 0;
555cb7a01acSMauro Carvalho Chehab 	if (reg == REG_COM7 && (value & COM7_RESET))
556cb7a01acSMauro Carvalho Chehab 		msleep(5);  /* Wait for reset to run */
557cb7a01acSMauro Carvalho Chehab 	return ret;
558cb7a01acSMauro Carvalho Chehab }
559cb7a01acSMauro Carvalho Chehab 
560cb7a01acSMauro Carvalho Chehab static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
561cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
562cb7a01acSMauro Carvalho Chehab {
563cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
5643abafaf4STom Rix 
565cb7a01acSMauro Carvalho Chehab 	if (info->use_smbus)
566cb7a01acSMauro Carvalho Chehab 		return ov7670_read_smbus(sd, reg, value);
567cb7a01acSMauro Carvalho Chehab 	else
568cb7a01acSMauro Carvalho Chehab 		return ov7670_read_i2c(sd, reg, value);
569cb7a01acSMauro Carvalho Chehab }
570cb7a01acSMauro Carvalho Chehab 
571cb7a01acSMauro Carvalho Chehab static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
572cb7a01acSMauro Carvalho Chehab 		unsigned char value)
573cb7a01acSMauro Carvalho Chehab {
574cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
5753abafaf4STom Rix 
576cb7a01acSMauro Carvalho Chehab 	if (info->use_smbus)
577cb7a01acSMauro Carvalho Chehab 		return ov7670_write_smbus(sd, reg, value);
578cb7a01acSMauro Carvalho Chehab 	else
579cb7a01acSMauro Carvalho Chehab 		return ov7670_write_i2c(sd, reg, value);
580cb7a01acSMauro Carvalho Chehab }
581cb7a01acSMauro Carvalho Chehab 
582b48d908dSAkinobu Mita static int ov7670_update_bits(struct v4l2_subdev *sd, unsigned char reg,
583b48d908dSAkinobu Mita 		unsigned char mask, unsigned char value)
584b48d908dSAkinobu Mita {
585b48d908dSAkinobu Mita 	unsigned char orig;
586b48d908dSAkinobu Mita 	int ret;
587b48d908dSAkinobu Mita 
588b48d908dSAkinobu Mita 	ret = ov7670_read(sd, reg, &orig);
589b48d908dSAkinobu Mita 	if (ret)
590b48d908dSAkinobu Mita 		return ret;
591b48d908dSAkinobu Mita 
592b48d908dSAkinobu Mita 	return ov7670_write(sd, reg, (orig & ~mask) | (value & mask));
593b48d908dSAkinobu Mita }
594b48d908dSAkinobu Mita 
595cb7a01acSMauro Carvalho Chehab /*
596cb7a01acSMauro Carvalho Chehab  * Write a list of register settings; ff/ff stops the process.
597cb7a01acSMauro Carvalho Chehab  */
598cb7a01acSMauro Carvalho Chehab static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
599cb7a01acSMauro Carvalho Chehab {
600cb7a01acSMauro Carvalho Chehab 	while (vals->reg_num != 0xff || vals->value != 0xff) {
601cb7a01acSMauro Carvalho Chehab 		int ret = ov7670_write(sd, vals->reg_num, vals->value);
6023abafaf4STom Rix 
603cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
604cb7a01acSMauro Carvalho Chehab 			return ret;
605cb7a01acSMauro Carvalho Chehab 		vals++;
606cb7a01acSMauro Carvalho Chehab 	}
607cb7a01acSMauro Carvalho Chehab 	return 0;
608cb7a01acSMauro Carvalho Chehab }
609cb7a01acSMauro Carvalho Chehab 
610cb7a01acSMauro Carvalho Chehab 
611cb7a01acSMauro Carvalho Chehab /*
612cb7a01acSMauro Carvalho Chehab  * Stuff that knows about the sensor.
613cb7a01acSMauro Carvalho Chehab  */
614cb7a01acSMauro Carvalho Chehab static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
615cb7a01acSMauro Carvalho Chehab {
616cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM7, COM7_RESET);
617cb7a01acSMauro Carvalho Chehab 	msleep(1);
618cb7a01acSMauro Carvalho Chehab 	return 0;
619cb7a01acSMauro Carvalho Chehab }
620cb7a01acSMauro Carvalho Chehab 
621cb7a01acSMauro Carvalho Chehab 
622cb7a01acSMauro Carvalho Chehab static int ov7670_init(struct v4l2_subdev *sd, u32 val)
623cb7a01acSMauro Carvalho Chehab {
624cb7a01acSMauro Carvalho Chehab 	return ov7670_write_array(sd, ov7670_default_regs);
625cb7a01acSMauro Carvalho Chehab }
626cb7a01acSMauro Carvalho Chehab 
627cb7a01acSMauro Carvalho Chehab static int ov7670_detect(struct v4l2_subdev *sd)
628cb7a01acSMauro Carvalho Chehab {
629cb7a01acSMauro Carvalho Chehab 	unsigned char v;
630cb7a01acSMauro Carvalho Chehab 	int ret;
631cb7a01acSMauro Carvalho Chehab 
632cb7a01acSMauro Carvalho Chehab 	ret = ov7670_init(sd, 0);
633cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
634cb7a01acSMauro Carvalho Chehab 		return ret;
635cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MIDH, &v);
636cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
637cb7a01acSMauro Carvalho Chehab 		return ret;
638cb7a01acSMauro Carvalho Chehab 	if (v != 0x7f) /* OV manuf. id. */
639cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
640cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MIDL, &v);
641cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
642cb7a01acSMauro Carvalho Chehab 		return ret;
643cb7a01acSMauro Carvalho Chehab 	if (v != 0xa2)
644cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
645cb7a01acSMauro Carvalho Chehab 	/*
646cb7a01acSMauro Carvalho Chehab 	 * OK, we know we have an OmniVision chip...but which one?
647cb7a01acSMauro Carvalho Chehab 	 */
648cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_PID, &v);
649cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
650cb7a01acSMauro Carvalho Chehab 		return ret;
651cb7a01acSMauro Carvalho Chehab 	if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
652cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
653cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_VER, &v);
654cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
655cb7a01acSMauro Carvalho Chehab 		return ret;
656cb7a01acSMauro Carvalho Chehab 	if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
657cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
658cb7a01acSMauro Carvalho Chehab 	return 0;
659cb7a01acSMauro Carvalho Chehab }
660cb7a01acSMauro Carvalho Chehab 
661cb7a01acSMauro Carvalho Chehab 
662cb7a01acSMauro Carvalho Chehab /*
663cb7a01acSMauro Carvalho Chehab  * Store information about the video data format.  The color matrix
664cb7a01acSMauro Carvalho Chehab  * is deeply tied into the format, so keep the relevant values here.
665cb7a01acSMauro Carvalho Chehab  * The magic matrix numbers come from OmniVision.
666cb7a01acSMauro Carvalho Chehab  */
667cb7a01acSMauro Carvalho Chehab static struct ov7670_format_struct {
668f5fe58fdSBoris BREZILLON 	u32 mbus_code;
669cb7a01acSMauro Carvalho Chehab 	enum v4l2_colorspace colorspace;
670cb7a01acSMauro Carvalho Chehab 	struct regval_list *regs;
671cb7a01acSMauro Carvalho Chehab 	int cmatrix[CMATRIX_LEN];
672cb7a01acSMauro Carvalho Chehab } ov7670_formats[] = {
673cb7a01acSMauro Carvalho Chehab 	{
674f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
67575eb9847SHans Verkuil 		.colorspace	= V4L2_COLORSPACE_SRGB,
676cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_yuv422,
677cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
678cb7a01acSMauro Carvalho Chehab 	},
679cb7a01acSMauro Carvalho Chehab 	{
680f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
681cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
682cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_rgb444,
683cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
684cb7a01acSMauro Carvalho Chehab 	},
685cb7a01acSMauro Carvalho Chehab 	{
686f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
687cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
688cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_rgb565,
689cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
690cb7a01acSMauro Carvalho Chehab 	},
691cb7a01acSMauro Carvalho Chehab 	{
692f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
693cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
694cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_raw,
695cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 0, 0, 0, 0, 0, 0 },
696cb7a01acSMauro Carvalho Chehab 	},
697cb7a01acSMauro Carvalho Chehab };
698cb7a01acSMauro Carvalho Chehab #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
699cb7a01acSMauro Carvalho Chehab 
700cb7a01acSMauro Carvalho Chehab 
701cb7a01acSMauro Carvalho Chehab /*
702cb7a01acSMauro Carvalho Chehab  * Then there is the issue of window sizes.  Try to capture the info here.
703cb7a01acSMauro Carvalho Chehab  */
704cb7a01acSMauro Carvalho Chehab 
705cb7a01acSMauro Carvalho Chehab /*
706cb7a01acSMauro Carvalho Chehab  * QCIF mode is done (by OV) in a very strange way - it actually looks like
707cb7a01acSMauro Carvalho Chehab  * VGA with weird scaling options - they do *not* use the canned QCIF mode
708cb7a01acSMauro Carvalho Chehab  * which is allegedly provided by the sensor.  So here's the weird register
709cb7a01acSMauro Carvalho Chehab  * settings.
710cb7a01acSMauro Carvalho Chehab  */
711cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_qcif_regs[] = {
712cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, COM3_SCALEEN|COM3_DCWEN },
713cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, COM3_DCWEN },
714cb7a01acSMauro Carvalho Chehab 	{ REG_COM14, COM14_DCWEN | 0x01},
715cb7a01acSMauro Carvalho Chehab 	{ 0x73, 0xf1 },
716cb7a01acSMauro Carvalho Chehab 	{ 0xa2, 0x52 },
717cb7a01acSMauro Carvalho Chehab 	{ 0x7b, 0x1c },
718cb7a01acSMauro Carvalho Chehab 	{ 0x7c, 0x28 },
719cb7a01acSMauro Carvalho Chehab 	{ 0x7d, 0x3c },
720cb7a01acSMauro Carvalho Chehab 	{ 0x7f, 0x69 },
721cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },
722cb7a01acSMauro Carvalho Chehab 	{ 0xa1, 0x0b },
723cb7a01acSMauro Carvalho Chehab 	{ 0x74, 0x19 },
724cb7a01acSMauro Carvalho Chehab 	{ 0x9a, 0x80 },
725cb7a01acSMauro Carvalho Chehab 	{ 0x43, 0x14 },
726cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0xc0 },
727cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
728cb7a01acSMauro Carvalho Chehab };
729cb7a01acSMauro Carvalho Chehab 
730d058e237SJavier Martin static struct ov7670_win_size ov7670_win_sizes[] = {
731cb7a01acSMauro Carvalho Chehab 	/* VGA */
732cb7a01acSMauro Carvalho Chehab 	{
733cb7a01acSMauro Carvalho Chehab 		.width		= VGA_WIDTH,
734cb7a01acSMauro Carvalho Chehab 		.height		= VGA_HEIGHT,
735cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_VGA,
736cb7a01acSMauro Carvalho Chehab 		.hstart		= 158,	/* These values from */
737cb7a01acSMauro Carvalho Chehab 		.hstop		=  14,	/* Omnivision */
738cb7a01acSMauro Carvalho Chehab 		.vstart		=  10,
739cb7a01acSMauro Carvalho Chehab 		.vstop		= 490,
740cb7a01acSMauro Carvalho Chehab 		.regs		= NULL,
741cb7a01acSMauro Carvalho Chehab 	},
742cb7a01acSMauro Carvalho Chehab 	/* CIF */
743cb7a01acSMauro Carvalho Chehab 	{
744cb7a01acSMauro Carvalho Chehab 		.width		= CIF_WIDTH,
745cb7a01acSMauro Carvalho Chehab 		.height		= CIF_HEIGHT,
746cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_CIF,
747cb7a01acSMauro Carvalho Chehab 		.hstart		= 170,	/* Empirically determined */
748cb7a01acSMauro Carvalho Chehab 		.hstop		=  90,
749cb7a01acSMauro Carvalho Chehab 		.vstart		=  14,
750cb7a01acSMauro Carvalho Chehab 		.vstop		= 494,
751cb7a01acSMauro Carvalho Chehab 		.regs		= NULL,
752cb7a01acSMauro Carvalho Chehab 	},
753cb7a01acSMauro Carvalho Chehab 	/* QVGA */
754cb7a01acSMauro Carvalho Chehab 	{
755cb7a01acSMauro Carvalho Chehab 		.width		= QVGA_WIDTH,
756cb7a01acSMauro Carvalho Chehab 		.height		= QVGA_HEIGHT,
757cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_QVGA,
758cb7a01acSMauro Carvalho Chehab 		.hstart		= 168,	/* Empirically determined */
759cb7a01acSMauro Carvalho Chehab 		.hstop		=  24,
760cb7a01acSMauro Carvalho Chehab 		.vstart		=  12,
761cb7a01acSMauro Carvalho Chehab 		.vstop		= 492,
762cb7a01acSMauro Carvalho Chehab 		.regs		= NULL,
763cb7a01acSMauro Carvalho Chehab 	},
764cb7a01acSMauro Carvalho Chehab 	/* QCIF */
765cb7a01acSMauro Carvalho Chehab 	{
766cb7a01acSMauro Carvalho Chehab 		.width		= QCIF_WIDTH,
767cb7a01acSMauro Carvalho Chehab 		.height		= QCIF_HEIGHT,
768cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_VGA, /* see comment above */
769cb7a01acSMauro Carvalho Chehab 		.hstart		= 456,	/* Empirically determined */
770cb7a01acSMauro Carvalho Chehab 		.hstop		=  24,
771cb7a01acSMauro Carvalho Chehab 		.vstart		=  14,
772cb7a01acSMauro Carvalho Chehab 		.vstop		= 494,
773cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_qcif_regs,
774d058e237SJavier Martin 	}
775cb7a01acSMauro Carvalho Chehab };
776cb7a01acSMauro Carvalho Chehab 
777d058e237SJavier Martin static struct ov7670_win_size ov7675_win_sizes[] = {
778d058e237SJavier Martin 	/*
779d058e237SJavier Martin 	 * Currently, only VGA is supported. Theoretically it could be possible
780d058e237SJavier Martin 	 * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a
781d058e237SJavier Martin 	 * base and tweak them empirically could be required.
782d058e237SJavier Martin 	 */
783d058e237SJavier Martin 	{
784d058e237SJavier Martin 		.width		= VGA_WIDTH,
785d058e237SJavier Martin 		.height		= VGA_HEIGHT,
786d058e237SJavier Martin 		.com7_bit	= COM7_FMT_VGA,
787d058e237SJavier Martin 		.hstart		= 158,	/* These values from */
788d058e237SJavier Martin 		.hstop		=  14,	/* Omnivision */
789d058e237SJavier Martin 		.vstart		=  14,  /* Empirically determined */
790d058e237SJavier Martin 		.vstop		= 494,
791d058e237SJavier Martin 		.regs		= NULL,
792d058e237SJavier Martin 	}
793d058e237SJavier Martin };
794cb7a01acSMauro Carvalho Chehab 
795f6dd927fSJavier Martin static void ov7675_get_framerate(struct v4l2_subdev *sd,
796f6dd927fSJavier Martin 				 struct v4l2_fract *tpf)
797f6dd927fSJavier Martin {
798f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
799f6dd927fSJavier Martin 	u32 clkrc = info->clkrc;
80004ee6d92SJavier Martin 	int pll_factor;
80104ee6d92SJavier Martin 
80204ee6d92SJavier Martin 	if (info->pll_bypass)
80304ee6d92SJavier Martin 		pll_factor = 1;
80404ee6d92SJavier Martin 	else
80504ee6d92SJavier Martin 		pll_factor = PLL_FACTOR;
806f6dd927fSJavier Martin 
807f6dd927fSJavier Martin 	clkrc++;
808f5fe58fdSBoris BREZILLON 	if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
809f6dd927fSJavier Martin 		clkrc = (clkrc >> 1);
810f6dd927fSJavier Martin 
811f6dd927fSJavier Martin 	tpf->numerator = 1;
812f6dd927fSJavier Martin 	tpf->denominator = (5 * pll_factor * info->clock_speed) /
813f6dd927fSJavier Martin 			(4 * clkrc);
814f6dd927fSJavier Martin }
815f6dd927fSJavier Martin 
81640012cd5SLubomir Rintel static int ov7675_apply_framerate(struct v4l2_subdev *sd)
81740012cd5SLubomir Rintel {
81840012cd5SLubomir Rintel 	struct ov7670_info *info = to_state(sd);
81940012cd5SLubomir Rintel 	int ret;
82040012cd5SLubomir Rintel 
82140012cd5SLubomir Rintel 	ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
82240012cd5SLubomir Rintel 	if (ret < 0)
82340012cd5SLubomir Rintel 		return ret;
82440012cd5SLubomir Rintel 
82540012cd5SLubomir Rintel 	return ov7670_write(sd, REG_DBLV,
82640012cd5SLubomir Rintel 			    info->pll_bypass ? DBLV_BYPASS : DBLV_X4);
82740012cd5SLubomir Rintel }
82840012cd5SLubomir Rintel 
829f6dd927fSJavier Martin static int ov7675_set_framerate(struct v4l2_subdev *sd,
830f6dd927fSJavier Martin 				 struct v4l2_fract *tpf)
831f6dd927fSJavier Martin {
832f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
833f6dd927fSJavier Martin 	u32 clkrc;
83404ee6d92SJavier Martin 	int pll_factor;
835f6dd927fSJavier Martin 
836f6dd927fSJavier Martin 	/*
837f6dd927fSJavier Martin 	 * The formula is fps = 5/4*pixclk for YUV/RGB and
838f6dd927fSJavier Martin 	 * fps = 5/2*pixclk for RAW.
839f6dd927fSJavier Martin 	 *
840f6dd927fSJavier Martin 	 * pixclk = clock_speed / (clkrc + 1) * PLLfactor
841f6dd927fSJavier Martin 	 *
842f6dd927fSJavier Martin 	 */
843f6dd927fSJavier Martin 	if (tpf->numerator == 0 || tpf->denominator == 0) {
844f6dd927fSJavier Martin 		clkrc = 0;
845f6dd927fSJavier Martin 	} else {
84640012cd5SLubomir Rintel 		pll_factor = info->pll_bypass ? 1 : PLL_FACTOR;
847f6dd927fSJavier Martin 		clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) /
848f6dd927fSJavier Martin 			(4 * tpf->denominator);
849f5fe58fdSBoris BREZILLON 		if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
850f6dd927fSJavier Martin 			clkrc = (clkrc << 1);
851f6dd927fSJavier Martin 		clkrc--;
852f6dd927fSJavier Martin 	}
853f6dd927fSJavier Martin 
854f6dd927fSJavier Martin 	/*
855f6dd927fSJavier Martin 	 * The datasheet claims that clkrc = 0 will divide the input clock by 1
856f6dd927fSJavier Martin 	 * but we've checked with an oscilloscope that it divides by 2 instead.
857f6dd927fSJavier Martin 	 * So, if clkrc = 0 just bypass the divider.
858f6dd927fSJavier Martin 	 */
859f6dd927fSJavier Martin 	if (clkrc <= 0)
860f6dd927fSJavier Martin 		clkrc = CLK_EXT;
861f6dd927fSJavier Martin 	else if (clkrc > CLK_SCALE)
862f6dd927fSJavier Martin 		clkrc = CLK_SCALE;
863f6dd927fSJavier Martin 	info->clkrc = clkrc;
864f6dd927fSJavier Martin 
865f6dd927fSJavier Martin 	/* Recalculate frame rate */
866f6dd927fSJavier Martin 	ov7675_get_framerate(sd, tpf);
867f6dd927fSJavier Martin 
86812f6153dSAkinobu Mita 	/*
86912f6153dSAkinobu Mita 	 * If the device is not powered up by the host driver do
87012f6153dSAkinobu Mita 	 * not apply any changes to H/W at this time. Instead
87112f6153dSAkinobu Mita 	 * the framerate will be restored right after power-up.
87212f6153dSAkinobu Mita 	 */
87312f6153dSAkinobu Mita 	if (info->on)
87440012cd5SLubomir Rintel 		return ov7675_apply_framerate(sd);
87512f6153dSAkinobu Mita 
87612f6153dSAkinobu Mita 	return 0;
877f6dd927fSJavier Martin }
878f6dd927fSJavier Martin 
879f6dd927fSJavier Martin static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd,
880f6dd927fSJavier Martin 				 struct v4l2_fract *tpf)
881f6dd927fSJavier Martin {
882f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
883f6dd927fSJavier Martin 
884f6dd927fSJavier Martin 	tpf->numerator = 1;
885f6dd927fSJavier Martin 	tpf->denominator = info->clock_speed;
886f6dd927fSJavier Martin 	if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
887f6dd927fSJavier Martin 		tpf->denominator /= (info->clkrc & CLK_SCALE);
888f6dd927fSJavier Martin }
889f6dd927fSJavier Martin 
890f6dd927fSJavier Martin static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd,
891f6dd927fSJavier Martin 					struct v4l2_fract *tpf)
892f6dd927fSJavier Martin {
893f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
894f6dd927fSJavier Martin 	int div;
895f6dd927fSJavier Martin 
896f6dd927fSJavier Martin 	if (tpf->numerator == 0 || tpf->denominator == 0)
897f6dd927fSJavier Martin 		div = 1;  /* Reset to full rate */
898f6dd927fSJavier Martin 	else
899f6dd927fSJavier Martin 		div = (tpf->numerator * info->clock_speed) / tpf->denominator;
900f6dd927fSJavier Martin 	if (div == 0)
901f6dd927fSJavier Martin 		div = 1;
902f6dd927fSJavier Martin 	else if (div > CLK_SCALE)
903f6dd927fSJavier Martin 		div = CLK_SCALE;
904f6dd927fSJavier Martin 	info->clkrc = (info->clkrc & 0x80) | div;
905f6dd927fSJavier Martin 	tpf->numerator = 1;
906f6dd927fSJavier Martin 	tpf->denominator = info->clock_speed / div;
90712f6153dSAkinobu Mita 
90812f6153dSAkinobu Mita 	/*
90912f6153dSAkinobu Mita 	 * If the device is not powered up by the host driver do
91012f6153dSAkinobu Mita 	 * not apply any changes to H/W at this time. Instead
91112f6153dSAkinobu Mita 	 * the framerate will be restored right after power-up.
91212f6153dSAkinobu Mita 	 */
91312f6153dSAkinobu Mita 	if (info->on)
914f6dd927fSJavier Martin 		return ov7670_write(sd, REG_CLKRC, info->clkrc);
91512f6153dSAkinobu Mita 
91612f6153dSAkinobu Mita 	return 0;
917f6dd927fSJavier Martin }
918f6dd927fSJavier Martin 
919cb7a01acSMauro Carvalho Chehab /*
920cb7a01acSMauro Carvalho Chehab  * Store a set of start/stop values into the camera.
921cb7a01acSMauro Carvalho Chehab  */
922cb7a01acSMauro Carvalho Chehab static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
923cb7a01acSMauro Carvalho Chehab 		int vstart, int vstop)
924cb7a01acSMauro Carvalho Chehab {
925cb7a01acSMauro Carvalho Chehab 	int ret;
926cb7a01acSMauro Carvalho Chehab 	unsigned char v;
927cb7a01acSMauro Carvalho Chehab 	/*
928cb7a01acSMauro Carvalho Chehab 	 * Horizontal: 11 bits, top 8 live in hstart and hstop.  Bottom 3 of
929cb7a01acSMauro Carvalho Chehab 	 * hstart are in href[2:0], bottom 3 of hstop in href[5:3].  There is
930cb7a01acSMauro Carvalho Chehab 	 * a mystery "edge offset" value in the top two bits of href.
931cb7a01acSMauro Carvalho Chehab 	 */
932cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
9333abafaf4STom Rix 	if (ret)
9343abafaf4STom Rix 		return ret;
9353abafaf4STom Rix 	ret = ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
9363abafaf4STom Rix 	if (ret)
9373abafaf4STom Rix 		return ret;
9383abafaf4STom Rix 	ret = ov7670_read(sd, REG_HREF, &v);
9393abafaf4STom Rix 	if (ret)
9403abafaf4STom Rix 		return ret;
941cb7a01acSMauro Carvalho Chehab 	v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
942cb7a01acSMauro Carvalho Chehab 	msleep(10);
9433abafaf4STom Rix 	ret = ov7670_write(sd, REG_HREF, v);
9443abafaf4STom Rix 	if (ret)
9453abafaf4STom Rix 		return ret;
9463abafaf4STom Rix 	/* Vertical: similar arrangement, but only 10 bits. */
9473abafaf4STom Rix 	ret = ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
9483abafaf4STom Rix 	if (ret)
9493abafaf4STom Rix 		return ret;
9503abafaf4STom Rix 	ret = ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
9513abafaf4STom Rix 	if (ret)
9523abafaf4STom Rix 		return ret;
9533abafaf4STom Rix 	ret = ov7670_read(sd, REG_VREF, &v);
9543abafaf4STom Rix 	if (ret)
9553abafaf4STom Rix 		return ret;
956cb7a01acSMauro Carvalho Chehab 	v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
957cb7a01acSMauro Carvalho Chehab 	msleep(10);
9583abafaf4STom Rix 	return ov7670_write(sd, REG_VREF, v);
959cb7a01acSMauro Carvalho Chehab }
960cb7a01acSMauro Carvalho Chehab 
961cb7a01acSMauro Carvalho Chehab 
962ebcff5fcSHans Verkuil static int ov7670_enum_mbus_code(struct v4l2_subdev *sd,
9630d346d2aSTomi Valkeinen 		struct v4l2_subdev_state *sd_state,
964ebcff5fcSHans Verkuil 		struct v4l2_subdev_mbus_code_enum *code)
965cb7a01acSMauro Carvalho Chehab {
966ebcff5fcSHans Verkuil 	if (code->pad || code->index >= N_OV7670_FMTS)
967cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
968cb7a01acSMauro Carvalho Chehab 
969ebcff5fcSHans Verkuil 	code->code = ov7670_formats[code->index].mbus_code;
970cb7a01acSMauro Carvalho Chehab 	return 0;
971cb7a01acSMauro Carvalho Chehab }
972cb7a01acSMauro Carvalho Chehab 
973cb7a01acSMauro Carvalho Chehab static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
974cb7a01acSMauro Carvalho Chehab 		struct v4l2_mbus_framefmt *fmt,
975cb7a01acSMauro Carvalho Chehab 		struct ov7670_format_struct **ret_fmt,
976cb7a01acSMauro Carvalho Chehab 		struct ov7670_win_size **ret_wsize)
977cb7a01acSMauro Carvalho Chehab {
978f748cd3eSJavier Martin 	int index, i;
979cb7a01acSMauro Carvalho Chehab 	struct ov7670_win_size *wsize;
980d058e237SJavier Martin 	struct ov7670_info *info = to_state(sd);
981d058e237SJavier Martin 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
982f748cd3eSJavier Martin 	unsigned int win_sizes_limit = n_win_sizes;
983cb7a01acSMauro Carvalho Chehab 
984cb7a01acSMauro Carvalho Chehab 	for (index = 0; index < N_OV7670_FMTS; index++)
985cb7a01acSMauro Carvalho Chehab 		if (ov7670_formats[index].mbus_code == fmt->code)
986cb7a01acSMauro Carvalho Chehab 			break;
987cb7a01acSMauro Carvalho Chehab 	if (index >= N_OV7670_FMTS) {
988cb7a01acSMauro Carvalho Chehab 		/* default to first format */
989cb7a01acSMauro Carvalho Chehab 		index = 0;
990cb7a01acSMauro Carvalho Chehab 		fmt->code = ov7670_formats[0].mbus_code;
991cb7a01acSMauro Carvalho Chehab 	}
992cb7a01acSMauro Carvalho Chehab 	if (ret_fmt != NULL)
993cb7a01acSMauro Carvalho Chehab 		*ret_fmt = ov7670_formats + index;
994cb7a01acSMauro Carvalho Chehab 	/*
995cb7a01acSMauro Carvalho Chehab 	 * Fields: the OV devices claim to be progressive.
996cb7a01acSMauro Carvalho Chehab 	 */
997cb7a01acSMauro Carvalho Chehab 	fmt->field = V4L2_FIELD_NONE;
998f748cd3eSJavier Martin 
999f748cd3eSJavier Martin 	/*
1000f748cd3eSJavier Martin 	 * Don't consider values that don't match min_height and min_width
1001f748cd3eSJavier Martin 	 * constraints.
1002f748cd3eSJavier Martin 	 */
1003f748cd3eSJavier Martin 	if (info->min_width || info->min_height)
1004f748cd3eSJavier Martin 		for (i = 0; i < n_win_sizes; i++) {
1005f748cd3eSJavier Martin 			wsize = info->devtype->win_sizes + i;
1006f748cd3eSJavier Martin 
1007f748cd3eSJavier Martin 			if (wsize->width < info->min_width ||
1008f748cd3eSJavier Martin 				wsize->height < info->min_height) {
1009f748cd3eSJavier Martin 				win_sizes_limit = i;
1010f748cd3eSJavier Martin 				break;
1011f748cd3eSJavier Martin 			}
1012f748cd3eSJavier Martin 		}
1013cb7a01acSMauro Carvalho Chehab 	/*
1014cb7a01acSMauro Carvalho Chehab 	 * Round requested image size down to the nearest
1015cb7a01acSMauro Carvalho Chehab 	 * we support, but not below the smallest.
1016cb7a01acSMauro Carvalho Chehab 	 */
1017d058e237SJavier Martin 	for (wsize = info->devtype->win_sizes;
1018f748cd3eSJavier Martin 	     wsize < info->devtype->win_sizes + win_sizes_limit; wsize++)
1019cb7a01acSMauro Carvalho Chehab 		if (fmt->width >= wsize->width && fmt->height >= wsize->height)
1020cb7a01acSMauro Carvalho Chehab 			break;
1021f748cd3eSJavier Martin 	if (wsize >= info->devtype->win_sizes + win_sizes_limit)
1022cb7a01acSMauro Carvalho Chehab 		wsize--;   /* Take the smallest one */
1023cb7a01acSMauro Carvalho Chehab 	if (ret_wsize != NULL)
1024cb7a01acSMauro Carvalho Chehab 		*ret_wsize = wsize;
1025cb7a01acSMauro Carvalho Chehab 	/*
1026cb7a01acSMauro Carvalho Chehab 	 * Note the size we'll actually handle.
1027cb7a01acSMauro Carvalho Chehab 	 */
1028cb7a01acSMauro Carvalho Chehab 	fmt->width = wsize->width;
1029cb7a01acSMauro Carvalho Chehab 	fmt->height = wsize->height;
1030cb7a01acSMauro Carvalho Chehab 	fmt->colorspace = ov7670_formats[index].colorspace;
1031c0662dd4SWenyou Yang 
1032c0662dd4SWenyou Yang 	info->format = *fmt;
1033c0662dd4SWenyou Yang 
1034cb7a01acSMauro Carvalho Chehab 	return 0;
1035cb7a01acSMauro Carvalho Chehab }
1036cb7a01acSMauro Carvalho Chehab 
10375556ab2aSLubomir Rintel static int ov7670_apply_fmt(struct v4l2_subdev *sd)
1038cb7a01acSMauro Carvalho Chehab {
1039cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
10405556ab2aSLubomir Rintel 	struct ov7670_win_size *wsize = info->wsize;
104101b84448SJacopo Mondi 	unsigned char com7, com10 = 0;
1042cb7a01acSMauro Carvalho Chehab 	int ret;
1043cb7a01acSMauro Carvalho Chehab 
1044cb7a01acSMauro Carvalho Chehab 	/*
1045cb7a01acSMauro Carvalho Chehab 	 * COM7 is a pain in the ass, it doesn't like to be read then
1046cb7a01acSMauro Carvalho Chehab 	 * quickly written afterward.  But we have everything we need
1047cb7a01acSMauro Carvalho Chehab 	 * to set it absolutely here, as long as the format-specific
1048cb7a01acSMauro Carvalho Chehab 	 * register sets list it first.
1049cb7a01acSMauro Carvalho Chehab 	 */
10505556ab2aSLubomir Rintel 	com7 = info->fmt->regs[0].value;
1051cb7a01acSMauro Carvalho Chehab 	com7 |= wsize->com7_bit;
105201b84448SJacopo Mondi 	ret = ov7670_write(sd, REG_COM7, com7);
105301b84448SJacopo Mondi 	if (ret)
105401b84448SJacopo Mondi 		return ret;
105501b84448SJacopo Mondi 
105601b84448SJacopo Mondi 	/*
105701b84448SJacopo Mondi 	 * Configure the media bus through COM10 register
105801b84448SJacopo Mondi 	 */
105901b84448SJacopo Mondi 	if (info->mbus_config & V4L2_MBUS_VSYNC_ACTIVE_LOW)
106001b84448SJacopo Mondi 		com10 |= COM10_VS_NEG;
106101b84448SJacopo Mondi 	if (info->mbus_config & V4L2_MBUS_HSYNC_ACTIVE_LOW)
106201b84448SJacopo Mondi 		com10 |= COM10_HREF_REV;
106301b84448SJacopo Mondi 	if (info->pclk_hb_disable)
106401b84448SJacopo Mondi 		com10 |= COM10_PCLK_HB;
106501b84448SJacopo Mondi 	ret = ov7670_write(sd, REG_COM10, com10);
106601b84448SJacopo Mondi 	if (ret)
106701b84448SJacopo Mondi 		return ret;
106801b84448SJacopo Mondi 
1069cb7a01acSMauro Carvalho Chehab 	/*
1070cb7a01acSMauro Carvalho Chehab 	 * Now write the rest of the array.  Also store start/stops
1071cb7a01acSMauro Carvalho Chehab 	 */
10725556ab2aSLubomir Rintel 	ret = ov7670_write_array(sd, info->fmt->regs + 1);
107301b84448SJacopo Mondi 	if (ret)
107401b84448SJacopo Mondi 		return ret;
107501b84448SJacopo Mondi 
107601b84448SJacopo Mondi 	ret = ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
1077cb7a01acSMauro Carvalho Chehab 			    wsize->vstop);
107801b84448SJacopo Mondi 	if (ret)
107901b84448SJacopo Mondi 		return ret;
108001b84448SJacopo Mondi 
108101b84448SJacopo Mondi 	if (wsize->regs) {
1082cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write_array(sd, wsize->regs);
108301b84448SJacopo Mondi 		if (ret)
108401b84448SJacopo Mondi 			return ret;
108501b84448SJacopo Mondi 	}
108601b84448SJacopo Mondi 
1087cb7a01acSMauro Carvalho Chehab 	/*
1088cb7a01acSMauro Carvalho Chehab 	 * If we're running RGB565, we must rewrite clkrc after setting
1089cb7a01acSMauro Carvalho Chehab 	 * the other parameters or the image looks poor.  If we're *not*
1090cb7a01acSMauro Carvalho Chehab 	 * doing RGB565, we must not rewrite clkrc or the image looks
1091cb7a01acSMauro Carvalho Chehab 	 * *really* poor.
1092cb7a01acSMauro Carvalho Chehab 	 *
1093cb7a01acSMauro Carvalho Chehab 	 * (Update) Now that we retain clkrc state, we should be able
1094cb7a01acSMauro Carvalho Chehab 	 * to write it unconditionally, and that will make the frame
1095cb7a01acSMauro Carvalho Chehab 	 * rate persistent too.
1096cb7a01acSMauro Carvalho Chehab 	 */
1097cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
109801b84448SJacopo Mondi 	if (ret)
109901b84448SJacopo Mondi 		return ret;
110001b84448SJacopo Mondi 
1101cb7a01acSMauro Carvalho Chehab 	return 0;
1102cb7a01acSMauro Carvalho Chehab }
1103cb7a01acSMauro Carvalho Chehab 
11045556ab2aSLubomir Rintel /*
11055556ab2aSLubomir Rintel  * Set a format.
11065556ab2aSLubomir Rintel  */
11075556ab2aSLubomir Rintel static int ov7670_set_fmt(struct v4l2_subdev *sd,
11080d346d2aSTomi Valkeinen 		struct v4l2_subdev_state *sd_state,
11095556ab2aSLubomir Rintel 		struct v4l2_subdev_format *format)
11105556ab2aSLubomir Rintel {
11115556ab2aSLubomir Rintel 	struct ov7670_info *info = to_state(sd);
11125556ab2aSLubomir Rintel #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
11135556ab2aSLubomir Rintel 	struct v4l2_mbus_framefmt *mbus_fmt;
11145556ab2aSLubomir Rintel #endif
11155556ab2aSLubomir Rintel 	int ret;
11165556ab2aSLubomir Rintel 
11175556ab2aSLubomir Rintel 	if (format->pad)
11185556ab2aSLubomir Rintel 		return -EINVAL;
11195556ab2aSLubomir Rintel 
11205556ab2aSLubomir Rintel 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
11215556ab2aSLubomir Rintel 		ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL);
11225556ab2aSLubomir Rintel 		if (ret)
11235556ab2aSLubomir Rintel 			return ret;
11245556ab2aSLubomir Rintel #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
11250d346d2aSTomi Valkeinen 		mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state,
11260d346d2aSTomi Valkeinen 						      format->pad);
11275556ab2aSLubomir Rintel 		*mbus_fmt = format->format;
11285556ab2aSLubomir Rintel #endif
1129fa564e90SHans Verkuil 		return 0;
11305556ab2aSLubomir Rintel 	}
11315556ab2aSLubomir Rintel 
11325556ab2aSLubomir Rintel 	ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize);
11335556ab2aSLubomir Rintel 	if (ret)
11345556ab2aSLubomir Rintel 		return ret;
11355556ab2aSLubomir Rintel 
113612f6153dSAkinobu Mita 	/*
113712f6153dSAkinobu Mita 	 * If the device is not powered up by the host driver do
113812f6153dSAkinobu Mita 	 * not apply any changes to H/W at this time. Instead
113912f6153dSAkinobu Mita 	 * the frame format will be restored right after power-up.
114012f6153dSAkinobu Mita 	 */
114112f6153dSAkinobu Mita 	if (info->on)
114212f6153dSAkinobu Mita 		return ov7670_apply_fmt(sd);
11435556ab2aSLubomir Rintel 
11445556ab2aSLubomir Rintel 	return 0;
11455556ab2aSLubomir Rintel }
11465556ab2aSLubomir Rintel 
1147c0662dd4SWenyou Yang static int ov7670_get_fmt(struct v4l2_subdev *sd,
11480d346d2aSTomi Valkeinen 			  struct v4l2_subdev_state *sd_state,
1149c0662dd4SWenyou Yang 			  struct v4l2_subdev_format *format)
1150c0662dd4SWenyou Yang {
1151c0662dd4SWenyou Yang 	struct ov7670_info *info = to_state(sd);
1152c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1153c0662dd4SWenyou Yang 	struct v4l2_mbus_framefmt *mbus_fmt;
1154c0662dd4SWenyou Yang #endif
1155c0662dd4SWenyou Yang 
1156c0662dd4SWenyou Yang 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1157c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
11580d346d2aSTomi Valkeinen 		mbus_fmt = v4l2_subdev_get_try_format(sd, sd_state, 0);
1159c0662dd4SWenyou Yang 		format->format = *mbus_fmt;
1160c0662dd4SWenyou Yang 		return 0;
1161c0662dd4SWenyou Yang #else
1162fa564e90SHans Verkuil 		return -EINVAL;
1163c0662dd4SWenyou Yang #endif
1164c0662dd4SWenyou Yang 	} else {
1165c0662dd4SWenyou Yang 		format->format = info->format;
1166c0662dd4SWenyou Yang 	}
1167c0662dd4SWenyou Yang 
1168c0662dd4SWenyou Yang 	return 0;
1169c0662dd4SWenyou Yang }
1170c0662dd4SWenyou Yang 
1171cb7a01acSMauro Carvalho Chehab /*
1172cb7a01acSMauro Carvalho Chehab  * Implement G/S_PARM.  There is a "high quality" mode we could try
1173cb7a01acSMauro Carvalho Chehab  * to do someday; for now, we just do the frame rate tweak.
1174cb7a01acSMauro Carvalho Chehab  */
11754471109eSHans Verkuil static int ov7670_g_frame_interval(struct v4l2_subdev *sd,
11764471109eSHans Verkuil 				   struct v4l2_subdev_frame_interval *ival)
1177cb7a01acSMauro Carvalho Chehab {
1178cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1179cb7a01acSMauro Carvalho Chehab 
1180cb7a01acSMauro Carvalho Chehab 
11814471109eSHans Verkuil 	info->devtype->get_framerate(sd, &ival->interval);
1182f6dd927fSJavier Martin 
1183cb7a01acSMauro Carvalho Chehab 	return 0;
1184cb7a01acSMauro Carvalho Chehab }
1185cb7a01acSMauro Carvalho Chehab 
11864471109eSHans Verkuil static int ov7670_s_frame_interval(struct v4l2_subdev *sd,
11874471109eSHans Verkuil 				   struct v4l2_subdev_frame_interval *ival)
1188cb7a01acSMauro Carvalho Chehab {
11894471109eSHans Verkuil 	struct v4l2_fract *tpf = &ival->interval;
1190cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1191cb7a01acSMauro Carvalho Chehab 
1192cb7a01acSMauro Carvalho Chehab 
1193f6dd927fSJavier Martin 	return info->devtype->set_framerate(sd, tpf);
1194cb7a01acSMauro Carvalho Chehab }
1195cb7a01acSMauro Carvalho Chehab 
1196cb7a01acSMauro Carvalho Chehab 
1197cb7a01acSMauro Carvalho Chehab /*
1198cb7a01acSMauro Carvalho Chehab  * Frame intervals.  Since frame rates are controlled with the clock
1199cb7a01acSMauro Carvalho Chehab  * divider, we can only do 30/n for integer n values.  So no continuous
1200cb7a01acSMauro Carvalho Chehab  * or stepwise options.  Here we just pick a handful of logical values.
1201cb7a01acSMauro Carvalho Chehab  */
1202cb7a01acSMauro Carvalho Chehab 
1203cb7a01acSMauro Carvalho Chehab static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
1204cb7a01acSMauro Carvalho Chehab 
120517bef885SHans Verkuil static int ov7670_enum_frame_interval(struct v4l2_subdev *sd,
12060d346d2aSTomi Valkeinen 				      struct v4l2_subdev_state *sd_state,
120717bef885SHans Verkuil 				      struct v4l2_subdev_frame_interval_enum *fie)
1208cb7a01acSMauro Carvalho Chehab {
1209b8cc79fdSHans Verkuil 	struct ov7670_info *info = to_state(sd);
1210b8cc79fdSHans Verkuil 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
1211b8cc79fdSHans Verkuil 	int i;
1212b8cc79fdSHans Verkuil 
121317bef885SHans Verkuil 	if (fie->pad)
1214cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
121517bef885SHans Verkuil 	if (fie->index >= ARRAY_SIZE(ov7670_frame_rates))
121617bef885SHans Verkuil 		return -EINVAL;
1217b8cc79fdSHans Verkuil 
1218b8cc79fdSHans Verkuil 	/*
1219b8cc79fdSHans Verkuil 	 * Check if the width/height is valid.
1220b8cc79fdSHans Verkuil 	 *
1221b8cc79fdSHans Verkuil 	 * If a minimum width/height was requested, filter out the capture
1222b8cc79fdSHans Verkuil 	 * windows that fall outside that.
1223b8cc79fdSHans Verkuil 	 */
1224b8cc79fdSHans Verkuil 	for (i = 0; i < n_win_sizes; i++) {
1225b8cc79fdSHans Verkuil 		struct ov7670_win_size *win = &info->devtype->win_sizes[i];
1226b8cc79fdSHans Verkuil 
1227b8cc79fdSHans Verkuil 		if (info->min_width && win->width < info->min_width)
1228b8cc79fdSHans Verkuil 			continue;
1229b8cc79fdSHans Verkuil 		if (info->min_height && win->height < info->min_height)
1230b8cc79fdSHans Verkuil 			continue;
1231b8cc79fdSHans Verkuil 		if (fie->width == win->width && fie->height == win->height)
1232b8cc79fdSHans Verkuil 			break;
1233b8cc79fdSHans Verkuil 	}
1234b8cc79fdSHans Verkuil 	if (i == n_win_sizes)
1235b8cc79fdSHans Verkuil 		return -EINVAL;
123617bef885SHans Verkuil 	fie->interval.numerator = 1;
123717bef885SHans Verkuil 	fie->interval.denominator = ov7670_frame_rates[fie->index];
1238cb7a01acSMauro Carvalho Chehab 	return 0;
1239cb7a01acSMauro Carvalho Chehab }
1240cb7a01acSMauro Carvalho Chehab 
1241cb7a01acSMauro Carvalho Chehab /*
1242cb7a01acSMauro Carvalho Chehab  * Frame size enumeration
1243cb7a01acSMauro Carvalho Chehab  */
124417bef885SHans Verkuil static int ov7670_enum_frame_size(struct v4l2_subdev *sd,
12450d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
124617bef885SHans Verkuil 				  struct v4l2_subdev_frame_size_enum *fse)
1247cb7a01acSMauro Carvalho Chehab {
1248cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1249cb7a01acSMauro Carvalho Chehab 	int i;
1250cb7a01acSMauro Carvalho Chehab 	int num_valid = -1;
125117bef885SHans Verkuil 	__u32 index = fse->index;
1252d058e237SJavier Martin 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
1253cb7a01acSMauro Carvalho Chehab 
125417bef885SHans Verkuil 	if (fse->pad)
125517bef885SHans Verkuil 		return -EINVAL;
125617bef885SHans Verkuil 
1257cb7a01acSMauro Carvalho Chehab 	/*
1258cb7a01acSMauro Carvalho Chehab 	 * If a minimum width/height was requested, filter out the capture
1259cb7a01acSMauro Carvalho Chehab 	 * windows that fall outside that.
1260cb7a01acSMauro Carvalho Chehab 	 */
1261d058e237SJavier Martin 	for (i = 0; i < n_win_sizes; i++) {
1262322e6d19SGuennadi Liakhovetski 		struct ov7670_win_size *win = &info->devtype->win_sizes[i];
12633abafaf4STom Rix 
1264cb7a01acSMauro Carvalho Chehab 		if (info->min_width && win->width < info->min_width)
1265cb7a01acSMauro Carvalho Chehab 			continue;
1266cb7a01acSMauro Carvalho Chehab 		if (info->min_height && win->height < info->min_height)
1267cb7a01acSMauro Carvalho Chehab 			continue;
1268cb7a01acSMauro Carvalho Chehab 		if (index == ++num_valid) {
126917bef885SHans Verkuil 			fse->min_width = fse->max_width = win->width;
127017bef885SHans Verkuil 			fse->min_height = fse->max_height = win->height;
1271cb7a01acSMauro Carvalho Chehab 			return 0;
1272cb7a01acSMauro Carvalho Chehab 		}
1273cb7a01acSMauro Carvalho Chehab 	}
1274cb7a01acSMauro Carvalho Chehab 
1275cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1276cb7a01acSMauro Carvalho Chehab }
1277cb7a01acSMauro Carvalho Chehab 
1278cb7a01acSMauro Carvalho Chehab /*
1279cb7a01acSMauro Carvalho Chehab  * Code for dealing with controls.
1280cb7a01acSMauro Carvalho Chehab  */
1281cb7a01acSMauro Carvalho Chehab 
1282cb7a01acSMauro Carvalho Chehab static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
1283cb7a01acSMauro Carvalho Chehab 		int matrix[CMATRIX_LEN])
1284cb7a01acSMauro Carvalho Chehab {
1285cb7a01acSMauro Carvalho Chehab 	int i, ret;
1286cb7a01acSMauro Carvalho Chehab 	unsigned char signbits = 0;
1287cb7a01acSMauro Carvalho Chehab 
1288cb7a01acSMauro Carvalho Chehab 	/*
1289cb7a01acSMauro Carvalho Chehab 	 * Weird crap seems to exist in the upper part of
1290cb7a01acSMauro Carvalho Chehab 	 * the sign bits register, so let's preserve it.
1291cb7a01acSMauro Carvalho Chehab 	 */
1292cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
1293cb7a01acSMauro Carvalho Chehab 	signbits &= 0xc0;
1294cb7a01acSMauro Carvalho Chehab 
1295cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < CMATRIX_LEN; i++) {
1296cb7a01acSMauro Carvalho Chehab 		unsigned char raw;
1297cb7a01acSMauro Carvalho Chehab 
1298cb7a01acSMauro Carvalho Chehab 		if (matrix[i] < 0) {
1299cb7a01acSMauro Carvalho Chehab 			signbits |= (1 << i);
1300cb7a01acSMauro Carvalho Chehab 			if (matrix[i] < -255)
1301cb7a01acSMauro Carvalho Chehab 				raw = 0xff;
1302cb7a01acSMauro Carvalho Chehab 			else
1303cb7a01acSMauro Carvalho Chehab 				raw = (-1 * matrix[i]) & 0xff;
13043abafaf4STom Rix 		} else {
1305cb7a01acSMauro Carvalho Chehab 			if (matrix[i] > 255)
1306cb7a01acSMauro Carvalho Chehab 				raw = 0xff;
1307cb7a01acSMauro Carvalho Chehab 			else
1308cb7a01acSMauro Carvalho Chehab 				raw = matrix[i] & 0xff;
1309cb7a01acSMauro Carvalho Chehab 		}
13103abafaf4STom Rix 		ret = ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
13113abafaf4STom Rix 		if (ret)
1312cb7a01acSMauro Carvalho Chehab 			return ret;
1313cb7a01acSMauro Carvalho Chehab 	}
13143abafaf4STom Rix 	return ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
13153abafaf4STom Rix }
1316cb7a01acSMauro Carvalho Chehab 
1317cb7a01acSMauro Carvalho Chehab 
1318cb7a01acSMauro Carvalho Chehab /*
1319cb7a01acSMauro Carvalho Chehab  * Hue also requires messing with the color matrix.  It also requires
1320cb7a01acSMauro Carvalho Chehab  * trig functions, which tend not to be well supported in the kernel.
1321cb7a01acSMauro Carvalho Chehab  * So here is a simple table of sine values, 0-90 degrees, in steps
1322cb7a01acSMauro Carvalho Chehab  * of five degrees.  Values are multiplied by 1000.
1323cb7a01acSMauro Carvalho Chehab  *
1324cb7a01acSMauro Carvalho Chehab  * The following naive approximate trig functions require an argument
1325cb7a01acSMauro Carvalho Chehab  * carefully limited to -180 <= theta <= 180.
1326cb7a01acSMauro Carvalho Chehab  */
1327cb7a01acSMauro Carvalho Chehab #define SIN_STEP 5
1328cb7a01acSMauro Carvalho Chehab static const int ov7670_sin_table[] = {
1329cb7a01acSMauro Carvalho Chehab 	   0,	 87,   173,   258,   342,   422,
1330cb7a01acSMauro Carvalho Chehab 	 499,	573,   642,   707,   766,   819,
1331cb7a01acSMauro Carvalho Chehab 	 866,	906,   939,   965,   984,   996,
1332cb7a01acSMauro Carvalho Chehab 	1000
1333cb7a01acSMauro Carvalho Chehab };
1334cb7a01acSMauro Carvalho Chehab 
1335cb7a01acSMauro Carvalho Chehab static int ov7670_sine(int theta)
1336cb7a01acSMauro Carvalho Chehab {
1337cb7a01acSMauro Carvalho Chehab 	int chs = 1;
1338cb7a01acSMauro Carvalho Chehab 	int sine;
1339cb7a01acSMauro Carvalho Chehab 
1340cb7a01acSMauro Carvalho Chehab 	if (theta < 0) {
1341cb7a01acSMauro Carvalho Chehab 		theta = -theta;
1342cb7a01acSMauro Carvalho Chehab 		chs = -1;
1343cb7a01acSMauro Carvalho Chehab 	}
1344cb7a01acSMauro Carvalho Chehab 	if (theta <= 90)
1345cb7a01acSMauro Carvalho Chehab 		sine = ov7670_sin_table[theta/SIN_STEP];
1346cb7a01acSMauro Carvalho Chehab 	else {
1347cb7a01acSMauro Carvalho Chehab 		theta -= 90;
1348cb7a01acSMauro Carvalho Chehab 		sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
1349cb7a01acSMauro Carvalho Chehab 	}
1350cb7a01acSMauro Carvalho Chehab 	return sine*chs;
1351cb7a01acSMauro Carvalho Chehab }
1352cb7a01acSMauro Carvalho Chehab 
1353cb7a01acSMauro Carvalho Chehab static int ov7670_cosine(int theta)
1354cb7a01acSMauro Carvalho Chehab {
1355cb7a01acSMauro Carvalho Chehab 	theta = 90 - theta;
1356cb7a01acSMauro Carvalho Chehab 	if (theta > 180)
1357cb7a01acSMauro Carvalho Chehab 		theta -= 360;
1358cb7a01acSMauro Carvalho Chehab 	else if (theta < -180)
1359cb7a01acSMauro Carvalho Chehab 		theta += 360;
1360cb7a01acSMauro Carvalho Chehab 	return ov7670_sine(theta);
1361cb7a01acSMauro Carvalho Chehab }
1362cb7a01acSMauro Carvalho Chehab 
1363cb7a01acSMauro Carvalho Chehab 
1364cb7a01acSMauro Carvalho Chehab 
1365cb7a01acSMauro Carvalho Chehab 
1366cb7a01acSMauro Carvalho Chehab static void ov7670_calc_cmatrix(struct ov7670_info *info,
1367492959c7SJavier Martin 		int matrix[CMATRIX_LEN], int sat, int hue)
1368cb7a01acSMauro Carvalho Chehab {
1369cb7a01acSMauro Carvalho Chehab 	int i;
1370cb7a01acSMauro Carvalho Chehab 	/*
1371cb7a01acSMauro Carvalho Chehab 	 * Apply the current saturation setting first.
1372cb7a01acSMauro Carvalho Chehab 	 */
1373cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < CMATRIX_LEN; i++)
1374492959c7SJavier Martin 		matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7;
1375cb7a01acSMauro Carvalho Chehab 	/*
1376cb7a01acSMauro Carvalho Chehab 	 * Then, if need be, rotate the hue value.
1377cb7a01acSMauro Carvalho Chehab 	 */
1378492959c7SJavier Martin 	if (hue != 0) {
1379cb7a01acSMauro Carvalho Chehab 		int sinth, costh, tmpmatrix[CMATRIX_LEN];
1380cb7a01acSMauro Carvalho Chehab 
1381cb7a01acSMauro Carvalho Chehab 		memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
1382492959c7SJavier Martin 		sinth = ov7670_sine(hue);
1383492959c7SJavier Martin 		costh = ov7670_cosine(hue);
1384cb7a01acSMauro Carvalho Chehab 
1385cb7a01acSMauro Carvalho Chehab 		matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
1386cb7a01acSMauro Carvalho Chehab 		matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
1387cb7a01acSMauro Carvalho Chehab 		matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
1388cb7a01acSMauro Carvalho Chehab 		matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
1389cb7a01acSMauro Carvalho Chehab 		matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
1390cb7a01acSMauro Carvalho Chehab 		matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
1391cb7a01acSMauro Carvalho Chehab 	}
1392cb7a01acSMauro Carvalho Chehab }
1393cb7a01acSMauro Carvalho Chehab 
1394cb7a01acSMauro Carvalho Chehab 
1395cb7a01acSMauro Carvalho Chehab 
1396492959c7SJavier Martin static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue)
1397cb7a01acSMauro Carvalho Chehab {
1398cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1399cb7a01acSMauro Carvalho Chehab 	int matrix[CMATRIX_LEN];
1400cb7a01acSMauro Carvalho Chehab 
1401492959c7SJavier Martin 	ov7670_calc_cmatrix(info, matrix, sat, hue);
14023abafaf4STom Rix 	return ov7670_store_cmatrix(sd, matrix);
1403cb7a01acSMauro Carvalho Chehab }
1404cb7a01acSMauro Carvalho Chehab 
1405cb7a01acSMauro Carvalho Chehab 
1406cb7a01acSMauro Carvalho Chehab /*
1407cb7a01acSMauro Carvalho Chehab  * Some weird registers seem to store values in a sign/magnitude format!
1408cb7a01acSMauro Carvalho Chehab  */
1409cb7a01acSMauro Carvalho Chehab 
1410cb7a01acSMauro Carvalho Chehab static unsigned char ov7670_abs_to_sm(unsigned char v)
1411cb7a01acSMauro Carvalho Chehab {
1412cb7a01acSMauro Carvalho Chehab 	if (v > 127)
1413cb7a01acSMauro Carvalho Chehab 		return v & 0x7f;
1414cb7a01acSMauro Carvalho Chehab 	return (128 - v) | 0x80;
1415cb7a01acSMauro Carvalho Chehab }
1416cb7a01acSMauro Carvalho Chehab 
1417cb7a01acSMauro Carvalho Chehab static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
1418cb7a01acSMauro Carvalho Chehab {
1419cb7a01acSMauro Carvalho Chehab 	unsigned char com8 = 0, v;
1420cb7a01acSMauro Carvalho Chehab 
1421cb7a01acSMauro Carvalho Chehab 	ov7670_read(sd, REG_COM8, &com8);
1422cb7a01acSMauro Carvalho Chehab 	com8 &= ~COM8_AEC;
1423cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM8, com8);
1424cb7a01acSMauro Carvalho Chehab 	v = ov7670_abs_to_sm(value);
14253abafaf4STom Rix 	return ov7670_write(sd, REG_BRIGHT, v);
1426cb7a01acSMauro Carvalho Chehab }
1427cb7a01acSMauro Carvalho Chehab 
1428cb7a01acSMauro Carvalho Chehab static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
1429cb7a01acSMauro Carvalho Chehab {
1430cb7a01acSMauro Carvalho Chehab 	return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
1431cb7a01acSMauro Carvalho Chehab }
1432cb7a01acSMauro Carvalho Chehab 
1433cb7a01acSMauro Carvalho Chehab static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
1434cb7a01acSMauro Carvalho Chehab {
1435cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1436cb7a01acSMauro Carvalho Chehab 	int ret;
1437cb7a01acSMauro Carvalho Chehab 
1438cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
14393abafaf4STom Rix 	if (ret)
14403abafaf4STom Rix 		return ret;
1441cb7a01acSMauro Carvalho Chehab 	if (value)
1442cb7a01acSMauro Carvalho Chehab 		v |= MVFP_MIRROR;
1443cb7a01acSMauro Carvalho Chehab 	else
1444cb7a01acSMauro Carvalho Chehab 		v &= ~MVFP_MIRROR;
1445cb7a01acSMauro Carvalho Chehab 	msleep(10);  /* FIXME */
14463abafaf4STom Rix 	return ov7670_write(sd, REG_MVFP, v);
1447cb7a01acSMauro Carvalho Chehab }
1448cb7a01acSMauro Carvalho Chehab 
1449cb7a01acSMauro Carvalho Chehab static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
1450cb7a01acSMauro Carvalho Chehab {
1451cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1452cb7a01acSMauro Carvalho Chehab 	int ret;
1453cb7a01acSMauro Carvalho Chehab 
1454cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
14553abafaf4STom Rix 	if (ret)
14563abafaf4STom Rix 		return ret;
1457cb7a01acSMauro Carvalho Chehab 	if (value)
1458cb7a01acSMauro Carvalho Chehab 		v |= MVFP_FLIP;
1459cb7a01acSMauro Carvalho Chehab 	else
1460cb7a01acSMauro Carvalho Chehab 		v &= ~MVFP_FLIP;
1461cb7a01acSMauro Carvalho Chehab 	msleep(10);  /* FIXME */
14623abafaf4STom Rix 	return ov7670_write(sd, REG_MVFP, v);
1463cb7a01acSMauro Carvalho Chehab }
1464cb7a01acSMauro Carvalho Chehab 
1465cb7a01acSMauro Carvalho Chehab /*
1466cb7a01acSMauro Carvalho Chehab  * GAIN is split between REG_GAIN and REG_VREF[7:6].  If one believes
1467cb7a01acSMauro Carvalho Chehab  * the data sheet, the VREF parts should be the most significant, but
1468cb7a01acSMauro Carvalho Chehab  * experience shows otherwise.  There seems to be little value in
1469cb7a01acSMauro Carvalho Chehab  * messing with the VREF bits, so we leave them alone.
1470cb7a01acSMauro Carvalho Chehab  */
1471cb7a01acSMauro Carvalho Chehab static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
1472cb7a01acSMauro Carvalho Chehab {
1473cb7a01acSMauro Carvalho Chehab 	int ret;
1474cb7a01acSMauro Carvalho Chehab 	unsigned char gain;
1475cb7a01acSMauro Carvalho Chehab 
1476cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_GAIN, &gain);
14773abafaf4STom Rix 	if (ret)
1478cb7a01acSMauro Carvalho Chehab 		return ret;
14793abafaf4STom Rix 	*value = gain;
14803abafaf4STom Rix 	return 0;
1481cb7a01acSMauro Carvalho Chehab }
1482cb7a01acSMauro Carvalho Chehab 
1483cb7a01acSMauro Carvalho Chehab static int ov7670_s_gain(struct v4l2_subdev *sd, int value)
1484cb7a01acSMauro Carvalho Chehab {
1485cb7a01acSMauro Carvalho Chehab 	int ret;
1486cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1487cb7a01acSMauro Carvalho Chehab 
1488cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_GAIN, value & 0xff);
14893abafaf4STom Rix 	if (ret)
1490cb7a01acSMauro Carvalho Chehab 		return ret;
14913abafaf4STom Rix 	/* Have to turn off AGC as well */
14923abafaf4STom Rix 	ret = ov7670_read(sd, REG_COM8, &com8);
14933abafaf4STom Rix 	if (ret)
14943abafaf4STom Rix 		return ret;
14953abafaf4STom Rix 	return ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC);
1496cb7a01acSMauro Carvalho Chehab }
1497cb7a01acSMauro Carvalho Chehab 
1498cb7a01acSMauro Carvalho Chehab /*
1499cb7a01acSMauro Carvalho Chehab  * Tweak autogain.
1500cb7a01acSMauro Carvalho Chehab  */
1501cb7a01acSMauro Carvalho Chehab static int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
1502cb7a01acSMauro Carvalho Chehab {
1503cb7a01acSMauro Carvalho Chehab 	int ret;
1504cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1505cb7a01acSMauro Carvalho Chehab 
1506cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1507cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1508cb7a01acSMauro Carvalho Chehab 		if (value)
1509cb7a01acSMauro Carvalho Chehab 			com8 |= COM8_AGC;
1510cb7a01acSMauro Carvalho Chehab 		else
1511cb7a01acSMauro Carvalho Chehab 			com8 &= ~COM8_AGC;
1512cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8);
1513cb7a01acSMauro Carvalho Chehab 	}
1514cb7a01acSMauro Carvalho Chehab 	return ret;
1515cb7a01acSMauro Carvalho Chehab }
1516cb7a01acSMauro Carvalho Chehab 
1517cb7a01acSMauro Carvalho Chehab static int ov7670_s_exp(struct v4l2_subdev *sd, int value)
1518cb7a01acSMauro Carvalho Chehab {
1519cb7a01acSMauro Carvalho Chehab 	int ret;
1520cb7a01acSMauro Carvalho Chehab 	unsigned char com1, com8, aech, aechh;
1521cb7a01acSMauro Carvalho Chehab 
1522cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM1, &com1) +
1523d487df9eSMauro Carvalho Chehab 		ov7670_read(sd, REG_COM8, &com8) +
1524cb7a01acSMauro Carvalho Chehab 		ov7670_read(sd, REG_AECHH, &aechh);
1525cb7a01acSMauro Carvalho Chehab 	if (ret)
1526cb7a01acSMauro Carvalho Chehab 		return ret;
1527cb7a01acSMauro Carvalho Chehab 
1528cb7a01acSMauro Carvalho Chehab 	com1 = (com1 & 0xfc) | (value & 0x03);
1529cb7a01acSMauro Carvalho Chehab 	aech = (value >> 2) & 0xff;
1530cb7a01acSMauro Carvalho Chehab 	aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f);
1531cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_COM1, com1) +
1532cb7a01acSMauro Carvalho Chehab 		ov7670_write(sd, REG_AECH, aech) +
1533cb7a01acSMauro Carvalho Chehab 		ov7670_write(sd, REG_AECHH, aechh);
1534cb7a01acSMauro Carvalho Chehab 	/* Have to turn off AEC as well */
1535cb7a01acSMauro Carvalho Chehab 	if (ret == 0)
1536cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC);
1537cb7a01acSMauro Carvalho Chehab 	return ret;
1538cb7a01acSMauro Carvalho Chehab }
1539cb7a01acSMauro Carvalho Chehab 
1540cb7a01acSMauro Carvalho Chehab /*
1541cb7a01acSMauro Carvalho Chehab  * Tweak autoexposure.
1542cb7a01acSMauro Carvalho Chehab  */
1543cb7a01acSMauro Carvalho Chehab static int ov7670_s_autoexp(struct v4l2_subdev *sd,
1544cb7a01acSMauro Carvalho Chehab 		enum v4l2_exposure_auto_type value)
1545cb7a01acSMauro Carvalho Chehab {
1546cb7a01acSMauro Carvalho Chehab 	int ret;
1547cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1548cb7a01acSMauro Carvalho Chehab 
1549cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1550cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1551cb7a01acSMauro Carvalho Chehab 		if (value == V4L2_EXPOSURE_AUTO)
1552cb7a01acSMauro Carvalho Chehab 			com8 |= COM8_AEC;
1553cb7a01acSMauro Carvalho Chehab 		else
1554cb7a01acSMauro Carvalho Chehab 			com8 &= ~COM8_AEC;
1555cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8);
1556cb7a01acSMauro Carvalho Chehab 	}
1557cb7a01acSMauro Carvalho Chehab 	return ret;
1558cb7a01acSMauro Carvalho Chehab }
1559cb7a01acSMauro Carvalho Chehab 
1560b48d908dSAkinobu Mita static const char * const ov7670_test_pattern_menu[] = {
1561b48d908dSAkinobu Mita 	"No test output",
1562b48d908dSAkinobu Mita 	"Shifting \"1\"",
1563b48d908dSAkinobu Mita 	"8-bar color bar",
1564b48d908dSAkinobu Mita 	"Fade to gray color bar",
1565b48d908dSAkinobu Mita };
1566b48d908dSAkinobu Mita 
1567b48d908dSAkinobu Mita static int ov7670_s_test_pattern(struct v4l2_subdev *sd, int value)
1568b48d908dSAkinobu Mita {
1569b48d908dSAkinobu Mita 	int ret;
1570b48d908dSAkinobu Mita 
1571b48d908dSAkinobu Mita 	ret = ov7670_update_bits(sd, REG_SCALING_XSC, TEST_PATTTERN_0,
1572b48d908dSAkinobu Mita 				value & BIT(0) ? TEST_PATTTERN_0 : 0);
1573b48d908dSAkinobu Mita 	if (ret)
1574b48d908dSAkinobu Mita 		return ret;
1575b48d908dSAkinobu Mita 
1576b48d908dSAkinobu Mita 	return ov7670_update_bits(sd, REG_SCALING_YSC, TEST_PATTTERN_1,
1577b48d908dSAkinobu Mita 				value & BIT(1) ? TEST_PATTTERN_1 : 0);
1578b48d908dSAkinobu Mita }
1579cb7a01acSMauro Carvalho Chehab 
1580492959c7SJavier Martin static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
1581cb7a01acSMauro Carvalho Chehab {
1582492959c7SJavier Martin 	struct v4l2_subdev *sd = to_sd(ctrl);
1583492959c7SJavier Martin 	struct ov7670_info *info = to_state(sd);
1584492959c7SJavier Martin 
1585492959c7SJavier Martin 	switch (ctrl->id) {
1586cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1587492959c7SJavier Martin 		return ov7670_g_gain(sd, &info->gain->val);
1588cb7a01acSMauro Carvalho Chehab 	}
1589cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1590cb7a01acSMauro Carvalho Chehab }
1591cb7a01acSMauro Carvalho Chehab 
1592492959c7SJavier Martin static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl)
1593cb7a01acSMauro Carvalho Chehab {
1594492959c7SJavier Martin 	struct v4l2_subdev *sd = to_sd(ctrl);
1595492959c7SJavier Martin 	struct ov7670_info *info = to_state(sd);
1596492959c7SJavier Martin 
1597cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
1598cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1599492959c7SJavier Martin 		return ov7670_s_brightness(sd, ctrl->val);
1600cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1601492959c7SJavier Martin 		return ov7670_s_contrast(sd, ctrl->val);
1602cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1603492959c7SJavier Martin 		return ov7670_s_sat_hue(sd,
1604492959c7SJavier Martin 				info->saturation->val, info->hue->val);
1605cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
1606492959c7SJavier Martin 		return ov7670_s_vflip(sd, ctrl->val);
1607cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
1608492959c7SJavier Martin 		return ov7670_s_hflip(sd, ctrl->val);
1609cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1610492959c7SJavier Martin 		/* Only set manual gain if auto gain is not explicitly
1611492959c7SJavier Martin 		   turned on. */
1612492959c7SJavier Martin 		if (!ctrl->val) {
1613492959c7SJavier Martin 			/* ov7670_s_gain turns off auto gain */
1614492959c7SJavier Martin 			return ov7670_s_gain(sd, info->gain->val);
1615492959c7SJavier Martin 		}
1616492959c7SJavier Martin 		return ov7670_s_autogain(sd, ctrl->val);
1617cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE_AUTO:
1618492959c7SJavier Martin 		/* Only set manual exposure if auto exposure is not explicitly
1619492959c7SJavier Martin 		   turned on. */
1620492959c7SJavier Martin 		if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
1621492959c7SJavier Martin 			/* ov7670_s_exp turns off auto exposure */
1622492959c7SJavier Martin 			return ov7670_s_exp(sd, info->exposure->val);
1623492959c7SJavier Martin 		}
1624492959c7SJavier Martin 		return ov7670_s_autoexp(sd, ctrl->val);
1625b48d908dSAkinobu Mita 	case V4L2_CID_TEST_PATTERN:
1626b48d908dSAkinobu Mita 		return ov7670_s_test_pattern(sd, ctrl->val);
1627cb7a01acSMauro Carvalho Chehab 	}
1628cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1629cb7a01acSMauro Carvalho Chehab }
1630cb7a01acSMauro Carvalho Chehab 
1631492959c7SJavier Martin static const struct v4l2_ctrl_ops ov7670_ctrl_ops = {
1632492959c7SJavier Martin 	.s_ctrl = ov7670_s_ctrl,
1633492959c7SJavier Martin 	.g_volatile_ctrl = ov7670_g_volatile_ctrl,
1634492959c7SJavier Martin };
1635cb7a01acSMauro Carvalho Chehab 
1636cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1637cb7a01acSMauro Carvalho Chehab static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1638cb7a01acSMauro Carvalho Chehab {
1639cb7a01acSMauro Carvalho Chehab 	unsigned char val = 0;
1640cb7a01acSMauro Carvalho Chehab 	int ret;
1641cb7a01acSMauro Carvalho Chehab 
1642cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, reg->reg & 0xff, &val);
1643cb7a01acSMauro Carvalho Chehab 	reg->val = val;
1644cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
1645cb7a01acSMauro Carvalho Chehab 	return ret;
1646cb7a01acSMauro Carvalho Chehab }
1647cb7a01acSMauro Carvalho Chehab 
1648977ba3b1SHans Verkuil static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
1649cb7a01acSMauro Carvalho Chehab {
1650cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
1651cb7a01acSMauro Carvalho Chehab 	return 0;
1652cb7a01acSMauro Carvalho Chehab }
1653cb7a01acSMauro Carvalho Chehab #endif
1654cb7a01acSMauro Carvalho Chehab 
16553d6a8fe2SLubomir Rintel static void ov7670_power_on(struct v4l2_subdev *sd)
165671862f63SWenyou Yang {
165771862f63SWenyou Yang 	struct ov7670_info *info = to_state(sd);
165871862f63SWenyou Yang 
16593d6a8fe2SLubomir Rintel 	if (info->on)
16603d6a8fe2SLubomir Rintel 		return;
16613d6a8fe2SLubomir Rintel 
1662030f9f68SLubomir Rintel 	clk_prepare_enable(info->clk);
1663030f9f68SLubomir Rintel 
166471862f63SWenyou Yang 	if (info->pwdn_gpio)
16653d6a8fe2SLubomir Rintel 		gpiod_set_value(info->pwdn_gpio, 0);
16663d6a8fe2SLubomir Rintel 	if (info->resetb_gpio) {
166771862f63SWenyou Yang 		gpiod_set_value(info->resetb_gpio, 1);
166871862f63SWenyou Yang 		usleep_range(500, 1000);
166971862f63SWenyou Yang 		gpiod_set_value(info->resetb_gpio, 0);
167071862f63SWenyou Yang 	}
1671030f9f68SLubomir Rintel 	if (info->pwdn_gpio || info->resetb_gpio || info->clk)
1672030f9f68SLubomir Rintel 		usleep_range(3000, 5000);
167371862f63SWenyou Yang 
16743d6a8fe2SLubomir Rintel 	info->on = true;
16753d6a8fe2SLubomir Rintel }
16763d6a8fe2SLubomir Rintel 
16773d6a8fe2SLubomir Rintel static void ov7670_power_off(struct v4l2_subdev *sd)
16783d6a8fe2SLubomir Rintel {
16793d6a8fe2SLubomir Rintel 	struct ov7670_info *info = to_state(sd);
16803d6a8fe2SLubomir Rintel 
16813d6a8fe2SLubomir Rintel 	if (!info->on)
16823d6a8fe2SLubomir Rintel 		return;
16833d6a8fe2SLubomir Rintel 
1684030f9f68SLubomir Rintel 	clk_disable_unprepare(info->clk);
1685030f9f68SLubomir Rintel 
16863d6a8fe2SLubomir Rintel 	if (info->pwdn_gpio)
16873d6a8fe2SLubomir Rintel 		gpiod_set_value(info->pwdn_gpio, 1);
16883d6a8fe2SLubomir Rintel 
16893d6a8fe2SLubomir Rintel 	info->on = false;
16903d6a8fe2SLubomir Rintel }
16913d6a8fe2SLubomir Rintel 
16923d6a8fe2SLubomir Rintel static int ov7670_s_power(struct v4l2_subdev *sd, int on)
16933d6a8fe2SLubomir Rintel {
16943d6a8fe2SLubomir Rintel 	struct ov7670_info *info = to_state(sd);
16953d6a8fe2SLubomir Rintel 
16963d6a8fe2SLubomir Rintel 	if (info->on == on)
16973d6a8fe2SLubomir Rintel 		return 0;
16983d6a8fe2SLubomir Rintel 
16993d6a8fe2SLubomir Rintel 	if (on) {
17003d6a8fe2SLubomir Rintel 		ov7670_power_on(sd);
170132ab688bSAkinobu Mita 		ov7670_init(sd, 0);
17023d6a8fe2SLubomir Rintel 		ov7670_apply_fmt(sd);
17033d6a8fe2SLubomir Rintel 		ov7675_apply_framerate(sd);
17043d6a8fe2SLubomir Rintel 		v4l2_ctrl_handler_setup(&info->hdl);
17053d6a8fe2SLubomir Rintel 	} else {
17063d6a8fe2SLubomir Rintel 		ov7670_power_off(sd);
17073d6a8fe2SLubomir Rintel 	}
17083d6a8fe2SLubomir Rintel 
170971862f63SWenyou Yang 	return 0;
171071862f63SWenyou Yang }
171171862f63SWenyou Yang 
1712c0662dd4SWenyou Yang static void ov7670_get_default_format(struct v4l2_subdev *sd,
1713c0662dd4SWenyou Yang 				      struct v4l2_mbus_framefmt *format)
1714c0662dd4SWenyou Yang {
1715c0662dd4SWenyou Yang 	struct ov7670_info *info = to_state(sd);
1716c0662dd4SWenyou Yang 
1717c0662dd4SWenyou Yang 	format->width = info->devtype->win_sizes[0].width;
1718c0662dd4SWenyou Yang 	format->height = info->devtype->win_sizes[0].height;
1719c0662dd4SWenyou Yang 	format->colorspace = info->fmt->colorspace;
1720c0662dd4SWenyou Yang 	format->code = info->fmt->mbus_code;
1721c0662dd4SWenyou Yang 	format->field = V4L2_FIELD_NONE;
1722c0662dd4SWenyou Yang }
1723c0662dd4SWenyou Yang 
1724c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1725c0662dd4SWenyou Yang static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
1726c0662dd4SWenyou Yang {
1727c0662dd4SWenyou Yang 	struct v4l2_mbus_framefmt *format =
17280d346d2aSTomi Valkeinen 				v4l2_subdev_get_try_format(sd, fh->state, 0);
1729c0662dd4SWenyou Yang 
1730c0662dd4SWenyou Yang 	ov7670_get_default_format(sd, format);
1731c0662dd4SWenyou Yang 
1732c0662dd4SWenyou Yang 	return 0;
1733c0662dd4SWenyou Yang }
1734c0662dd4SWenyou Yang #endif
1735c0662dd4SWenyou Yang 
1736cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1737cb7a01acSMauro Carvalho Chehab 
1738cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ov7670_core_ops = {
1739cb7a01acSMauro Carvalho Chehab 	.reset = ov7670_reset,
1740cb7a01acSMauro Carvalho Chehab 	.init = ov7670_init,
17413d6a8fe2SLubomir Rintel 	.s_power = ov7670_s_power,
17427852adf8SAkinobu Mita 	.log_status = v4l2_ctrl_subdev_log_status,
17437852adf8SAkinobu Mita 	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
17447852adf8SAkinobu Mita 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
1745cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1746cb7a01acSMauro Carvalho Chehab 	.g_register = ov7670_g_register,
1747cb7a01acSMauro Carvalho Chehab 	.s_register = ov7670_s_register,
1748cb7a01acSMauro Carvalho Chehab #endif
1749cb7a01acSMauro Carvalho Chehab };
1750cb7a01acSMauro Carvalho Chehab 
1751cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ov7670_video_ops = {
17524471109eSHans Verkuil 	.s_frame_interval = ov7670_s_frame_interval,
17534471109eSHans Verkuil 	.g_frame_interval = ov7670_g_frame_interval,
175417bef885SHans Verkuil };
175517bef885SHans Verkuil 
175617bef885SHans Verkuil static const struct v4l2_subdev_pad_ops ov7670_pad_ops = {
175717bef885SHans Verkuil 	.enum_frame_interval = ov7670_enum_frame_interval,
175817bef885SHans Verkuil 	.enum_frame_size = ov7670_enum_frame_size,
1759ebcff5fcSHans Verkuil 	.enum_mbus_code = ov7670_enum_mbus_code,
1760c0662dd4SWenyou Yang 	.get_fmt = ov7670_get_fmt,
1761717fd5b4SHans Verkuil 	.set_fmt = ov7670_set_fmt,
1762cb7a01acSMauro Carvalho Chehab };
1763cb7a01acSMauro Carvalho Chehab 
1764cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ov7670_ops = {
1765cb7a01acSMauro Carvalho Chehab 	.core = &ov7670_core_ops,
1766cb7a01acSMauro Carvalho Chehab 	.video = &ov7670_video_ops,
176717bef885SHans Verkuil 	.pad = &ov7670_pad_ops,
1768cb7a01acSMauro Carvalho Chehab };
1769cb7a01acSMauro Carvalho Chehab 
1770c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1771c0662dd4SWenyou Yang static const struct v4l2_subdev_internal_ops ov7670_subdev_internal_ops = {
1772c0662dd4SWenyou Yang 	.open = ov7670_open,
1773c0662dd4SWenyou Yang };
1774c0662dd4SWenyou Yang #endif
1775c0662dd4SWenyou Yang 
1776cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1777cb7a01acSMauro Carvalho Chehab 
1778d058e237SJavier Martin static const struct ov7670_devtype ov7670_devdata[] = {
1779d058e237SJavier Martin 	[MODEL_OV7670] = {
1780d058e237SJavier Martin 		.win_sizes = ov7670_win_sizes,
1781d058e237SJavier Martin 		.n_win_sizes = ARRAY_SIZE(ov7670_win_sizes),
1782f6dd927fSJavier Martin 		.set_framerate = ov7670_set_framerate_legacy,
1783f6dd927fSJavier Martin 		.get_framerate = ov7670_get_framerate_legacy,
1784d058e237SJavier Martin 	},
1785d058e237SJavier Martin 	[MODEL_OV7675] = {
1786d058e237SJavier Martin 		.win_sizes = ov7675_win_sizes,
1787d058e237SJavier Martin 		.n_win_sizes = ARRAY_SIZE(ov7675_win_sizes),
1788f6dd927fSJavier Martin 		.set_framerate = ov7675_set_framerate,
1789f6dd927fSJavier Martin 		.get_framerate = ov7675_get_framerate,
1790d058e237SJavier Martin 	},
1791d058e237SJavier Martin };
1792d058e237SJavier Martin 
1793a0c4164eSHans Verkuil static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info)
1794a0c4164eSHans Verkuil {
1795a0c4164eSHans Verkuil 	info->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
1796a0c4164eSHans Verkuil 			GPIOD_OUT_LOW);
1797a0c4164eSHans Verkuil 	if (IS_ERR(info->pwdn_gpio)) {
1798a0c4164eSHans Verkuil 		dev_info(&client->dev, "can't get %s GPIO\n", "powerdown");
1799a0c4164eSHans Verkuil 		return PTR_ERR(info->pwdn_gpio);
1800a0c4164eSHans Verkuil 	}
1801a0c4164eSHans Verkuil 
1802a0c4164eSHans Verkuil 	info->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
1803a0c4164eSHans Verkuil 			GPIOD_OUT_LOW);
1804a0c4164eSHans Verkuil 	if (IS_ERR(info->resetb_gpio)) {
1805a0c4164eSHans Verkuil 		dev_info(&client->dev, "can't get %s GPIO\n", "reset");
1806a0c4164eSHans Verkuil 		return PTR_ERR(info->resetb_gpio);
1807a0c4164eSHans Verkuil 	}
1808a0c4164eSHans Verkuil 
1809a0c4164eSHans Verkuil 	usleep_range(3000, 5000);
1810a0c4164eSHans Verkuil 
1811a0c4164eSHans Verkuil 	return 0;
1812a0c4164eSHans Verkuil }
1813a0c4164eSHans Verkuil 
181401b84448SJacopo Mondi /*
181501b84448SJacopo Mondi  * ov7670_parse_dt() - Parse device tree to collect mbus configuration
181601b84448SJacopo Mondi  *			properties
181701b84448SJacopo Mondi  */
181801b84448SJacopo Mondi static int ov7670_parse_dt(struct device *dev,
181901b84448SJacopo Mondi 			   struct ov7670_info *info)
182001b84448SJacopo Mondi {
182101b84448SJacopo Mondi 	struct fwnode_handle *fwnode = dev_fwnode(dev);
182260359a28SSakari Ailus 	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
182301b84448SJacopo Mondi 	struct fwnode_handle *ep;
182401b84448SJacopo Mondi 	int ret;
182501b84448SJacopo Mondi 
182601b84448SJacopo Mondi 	if (!fwnode)
182701b84448SJacopo Mondi 		return -EINVAL;
182801b84448SJacopo Mondi 
182901b84448SJacopo Mondi 	info->pclk_hb_disable = false;
183001b84448SJacopo Mondi 	if (fwnode_property_present(fwnode, "ov7670,pclk-hb-disable"))
183101b84448SJacopo Mondi 		info->pclk_hb_disable = true;
183201b84448SJacopo Mondi 
183301b84448SJacopo Mondi 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
183401b84448SJacopo Mondi 	if (!ep)
183501b84448SJacopo Mondi 		return -EINVAL;
183601b84448SJacopo Mondi 
183701b84448SJacopo Mondi 	ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg);
183801b84448SJacopo Mondi 	fwnode_handle_put(ep);
183909a48f74SJacopo Mondi 	if (ret)
184001b84448SJacopo Mondi 		return ret;
184101b84448SJacopo Mondi 
184201b84448SJacopo Mondi 	if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) {
184301b84448SJacopo Mondi 		dev_err(dev, "Unsupported media bus type\n");
184401b84448SJacopo Mondi 		return ret;
184501b84448SJacopo Mondi 	}
184601b84448SJacopo Mondi 	info->mbus_config = bus_cfg.bus.parallel.flags;
184701b84448SJacopo Mondi 
184801b84448SJacopo Mondi 	return 0;
184901b84448SJacopo Mondi }
185001b84448SJacopo Mondi 
1851cb7a01acSMauro Carvalho Chehab static int ov7670_probe(struct i2c_client *client,
1852cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
1853cb7a01acSMauro Carvalho Chehab {
1854f6dd927fSJavier Martin 	struct v4l2_fract tpf;
1855cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1856cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info;
1857cb7a01acSMauro Carvalho Chehab 	int ret;
1858cb7a01acSMauro Carvalho Chehab 
1859c02b211dSLaurent Pinchart 	info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
1860cb7a01acSMauro Carvalho Chehab 	if (info == NULL)
1861cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1862cb7a01acSMauro Carvalho Chehab 	sd = &info->sd;
1863cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
1864cb7a01acSMauro Carvalho Chehab 
1865c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1866c0662dd4SWenyou Yang 	sd->internal_ops = &ov7670_subdev_internal_ops;
18677852adf8SAkinobu Mita 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
1868c0662dd4SWenyou Yang #endif
1869c0662dd4SWenyou Yang 
1870cb7a01acSMauro Carvalho Chehab 	info->clock_speed = 30; /* default: a guess */
187101b84448SJacopo Mondi 
187201b84448SJacopo Mondi 	if (dev_fwnode(&client->dev)) {
187301b84448SJacopo Mondi 		ret = ov7670_parse_dt(&client->dev, info);
187401b84448SJacopo Mondi 		if (ret)
187501b84448SJacopo Mondi 			return ret;
187601b84448SJacopo Mondi 
187701b84448SJacopo Mondi 	} else if (client->dev.platform_data) {
1878cb7a01acSMauro Carvalho Chehab 		struct ov7670_config *config = client->dev.platform_data;
1879cb7a01acSMauro Carvalho Chehab 
1880cb7a01acSMauro Carvalho Chehab 		/*
1881cb7a01acSMauro Carvalho Chehab 		 * Must apply configuration before initializing device, because it
1882cb7a01acSMauro Carvalho Chehab 		 * selects I/O method.
1883cb7a01acSMauro Carvalho Chehab 		 */
1884cb7a01acSMauro Carvalho Chehab 		info->min_width = config->min_width;
1885cb7a01acSMauro Carvalho Chehab 		info->min_height = config->min_height;
1886cb7a01acSMauro Carvalho Chehab 		info->use_smbus = config->use_smbus;
1887cb7a01acSMauro Carvalho Chehab 
1888cb7a01acSMauro Carvalho Chehab 		if (config->clock_speed)
1889cb7a01acSMauro Carvalho Chehab 			info->clock_speed = config->clock_speed;
189004ee6d92SJavier Martin 
189161da76beSJacopo Mondi 		if (config->pll_bypass)
189204ee6d92SJavier Martin 			info->pll_bypass = true;
1893ee95258eSJavier Martin 
1894ee95258eSJavier Martin 		if (config->pclk_hb_disable)
1895ee95258eSJavier Martin 			info->pclk_hb_disable = true;
1896cb7a01acSMauro Carvalho Chehab 	}
1897cb7a01acSMauro Carvalho Chehab 
1898786fa584SLubomir Rintel 	info->clk = devm_clk_get(&client->dev, "xclk"); /* optional */
1899786fa584SLubomir Rintel 	if (IS_ERR(info->clk)) {
1900786fa584SLubomir Rintel 		ret = PTR_ERR(info->clk);
1901786fa584SLubomir Rintel 		if (ret == -ENOENT)
1902786fa584SLubomir Rintel 			info->clk = NULL;
1903786fa584SLubomir Rintel 		else
1904786fa584SLubomir Rintel 			return ret;
1905786fa584SLubomir Rintel 	}
19063d6a8fe2SLubomir Rintel 
1907030f9f68SLubomir Rintel 	ret = ov7670_init_gpio(client, info);
19081fc86ad0SFabio Estevam 	if (ret)
19091fc86ad0SFabio Estevam 		return ret;
19100a024d63SHans Verkuil 
1911030f9f68SLubomir Rintel 	ov7670_power_on(sd);
1912030f9f68SLubomir Rintel 
1913030f9f68SLubomir Rintel 	if (info->clk) {
19140a024d63SHans Verkuil 		info->clock_speed = clk_get_rate(info->clk) / 1000000;
19150a024d63SHans Verkuil 		if (info->clock_speed < 10 || info->clock_speed > 48) {
19160a024d63SHans Verkuil 			ret = -EINVAL;
1917030f9f68SLubomir Rintel 			goto power_off;
19180a024d63SHans Verkuil 		}
1919786fa584SLubomir Rintel 	}
19200a024d63SHans Verkuil 
1921cb7a01acSMauro Carvalho Chehab 	/* Make sure it's an ov7670 */
1922cb7a01acSMauro Carvalho Chehab 	ret = ov7670_detect(sd);
1923cb7a01acSMauro Carvalho Chehab 	if (ret) {
1924cb7a01acSMauro Carvalho Chehab 		v4l_dbg(1, debug, client,
1925cb7a01acSMauro Carvalho Chehab 			"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
1926cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
192771862f63SWenyou Yang 		goto power_off;
1928cb7a01acSMauro Carvalho Chehab 	}
1929cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1930cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
1931cb7a01acSMauro Carvalho Chehab 
1932d058e237SJavier Martin 	info->devtype = &ov7670_devdata[id->driver_data];
1933cb7a01acSMauro Carvalho Chehab 	info->fmt = &ov7670_formats[0];
19345556ab2aSLubomir Rintel 	info->wsize = &info->devtype->win_sizes[0];
1935c0662dd4SWenyou Yang 
1936c0662dd4SWenyou Yang 	ov7670_get_default_format(sd, &info->format);
1937c0662dd4SWenyou Yang 
1938f6dd927fSJavier Martin 	info->clkrc = 0;
1939f6dd927fSJavier Martin 
1940f6dd927fSJavier Martin 	/* Set default frame rate to 30 fps */
1941f6dd927fSJavier Martin 	tpf.numerator = 1;
1942f6dd927fSJavier Martin 	tpf.denominator = 30;
1943f6dd927fSJavier Martin 	info->devtype->set_framerate(sd, &tpf);
1944f6dd927fSJavier Martin 
1945492959c7SJavier Martin 	v4l2_ctrl_handler_init(&info->hdl, 10);
1946492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1947492959c7SJavier Martin 			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
1948492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1949492959c7SJavier Martin 			V4L2_CID_CONTRAST, 0, 127, 1, 64);
1950492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1951492959c7SJavier Martin 			V4L2_CID_VFLIP, 0, 1, 1, 0);
1952492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1953492959c7SJavier Martin 			V4L2_CID_HFLIP, 0, 1, 1, 0);
1954492959c7SJavier Martin 	info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1955492959c7SJavier Martin 			V4L2_CID_SATURATION, 0, 256, 1, 128);
1956492959c7SJavier Martin 	info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1957492959c7SJavier Martin 			V4L2_CID_HUE, -180, 180, 5, 0);
1958492959c7SJavier Martin 	info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1959492959c7SJavier Martin 			V4L2_CID_GAIN, 0, 255, 1, 128);
1960492959c7SJavier Martin 	info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1961492959c7SJavier Martin 			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
1962492959c7SJavier Martin 	info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1963492959c7SJavier Martin 			V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
1964492959c7SJavier Martin 	info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops,
1965492959c7SJavier Martin 			V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
1966492959c7SJavier Martin 			V4L2_EXPOSURE_AUTO);
1967b48d908dSAkinobu Mita 	v4l2_ctrl_new_std_menu_items(&info->hdl, &ov7670_ctrl_ops,
1968b48d908dSAkinobu Mita 			V4L2_CID_TEST_PATTERN,
1969b48d908dSAkinobu Mita 			ARRAY_SIZE(ov7670_test_pattern_menu) - 1, 0, 0,
1970b48d908dSAkinobu Mita 			ov7670_test_pattern_menu);
1971492959c7SJavier Martin 	sd->ctrl_handler = &info->hdl;
1972492959c7SJavier Martin 	if (info->hdl.error) {
19737d1b8619SHans Verkuil 		ret = info->hdl.error;
1974492959c7SJavier Martin 
19757d1b8619SHans Verkuil 		goto hdl_free;
1976492959c7SJavier Martin 	}
1977492959c7SJavier Martin 	/*
1978492959c7SJavier Martin 	 * We have checked empirically that hw allows to read back the gain
1979492959c7SJavier Martin 	 * value chosen by auto gain but that's not the case for auto exposure.
1980492959c7SJavier Martin 	 */
1981492959c7SJavier Martin 	v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true);
1982492959c7SJavier Martin 	v4l2_ctrl_auto_cluster(2, &info->auto_exposure,
1983492959c7SJavier Martin 			       V4L2_EXPOSURE_MANUAL, false);
1984492959c7SJavier Martin 	v4l2_ctrl_cluster(2, &info->saturation);
1985d94a26f0SWenyou Yang 
1986d94a26f0SWenyou Yang #if defined(CONFIG_MEDIA_CONTROLLER)
1987d94a26f0SWenyou Yang 	info->pad.flags = MEDIA_PAD_FL_SOURCE;
1988d94a26f0SWenyou Yang 	info->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1989d94a26f0SWenyou Yang 	ret = media_entity_pads_init(&info->sd.entity, 1, &info->pad);
1990d94a26f0SWenyou Yang 	if (ret < 0)
1991d94a26f0SWenyou Yang 		goto hdl_free;
1992d94a26f0SWenyou Yang #endif
1993d94a26f0SWenyou Yang 
1994492959c7SJavier Martin 	v4l2_ctrl_handler_setup(&info->hdl);
1995492959c7SJavier Martin 
19967d1b8619SHans Verkuil 	ret = v4l2_async_register_subdev(&info->sd);
19977d1b8619SHans Verkuil 	if (ret < 0)
1998d94a26f0SWenyou Yang 		goto entity_cleanup;
19997d1b8619SHans Verkuil 
2000030f9f68SLubomir Rintel 	ov7670_power_off(sd);
2001cb7a01acSMauro Carvalho Chehab 	return 0;
20027d1b8619SHans Verkuil 
2003d94a26f0SWenyou Yang entity_cleanup:
2004d94a26f0SWenyou Yang 	media_entity_cleanup(&info->sd.entity);
20057d1b8619SHans Verkuil hdl_free:
20067d1b8619SHans Verkuil 	v4l2_ctrl_handler_free(&info->hdl);
200771862f63SWenyou Yang power_off:
20083d6a8fe2SLubomir Rintel 	ov7670_power_off(sd);
20097d1b8619SHans Verkuil 	return ret;
2010cb7a01acSMauro Carvalho Chehab }
2011cb7a01acSMauro Carvalho Chehab 
2012*ed5c2f5fSUwe Kleine-König static void ov7670_remove(struct i2c_client *client)
2013cb7a01acSMauro Carvalho Chehab {
2014cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
2015492959c7SJavier Martin 	struct ov7670_info *info = to_state(sd);
2016cb7a01acSMauro Carvalho Chehab 
2017344aa836SAkinobu Mita 	v4l2_async_unregister_subdev(sd);
2018492959c7SJavier Martin 	v4l2_ctrl_handler_free(&info->hdl);
2019d94a26f0SWenyou Yang 	media_entity_cleanup(&info->sd.entity);
2020cb7a01acSMauro Carvalho Chehab }
2021cb7a01acSMauro Carvalho Chehab 
2022cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ov7670_id[] = {
2023d058e237SJavier Martin 	{ "ov7670", MODEL_OV7670 },
2024d058e237SJavier Martin 	{ "ov7675", MODEL_OV7675 },
2025cb7a01acSMauro Carvalho Chehab 	{ }
2026cb7a01acSMauro Carvalho Chehab };
2027cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ov7670_id);
2028cb7a01acSMauro Carvalho Chehab 
2029a0c4164eSHans Verkuil #if IS_ENABLED(CONFIG_OF)
2030a0c4164eSHans Verkuil static const struct of_device_id ov7670_of_match[] = {
2031a0c4164eSHans Verkuil 	{ .compatible = "ovti,ov7670", },
2032a0c4164eSHans Verkuil 	{ /* sentinel */ },
2033a0c4164eSHans Verkuil };
2034a0c4164eSHans Verkuil MODULE_DEVICE_TABLE(of, ov7670_of_match);
2035a0c4164eSHans Verkuil #endif
2036a0c4164eSHans Verkuil 
2037cb7a01acSMauro Carvalho Chehab static struct i2c_driver ov7670_driver = {
2038cb7a01acSMauro Carvalho Chehab 	.driver = {
2039cb7a01acSMauro Carvalho Chehab 		.name	= "ov7670",
2040a0c4164eSHans Verkuil 		.of_match_table = of_match_ptr(ov7670_of_match),
2041cb7a01acSMauro Carvalho Chehab 	},
2042cb7a01acSMauro Carvalho Chehab 	.probe		= ov7670_probe,
2043cb7a01acSMauro Carvalho Chehab 	.remove		= ov7670_remove,
2044cb7a01acSMauro Carvalho Chehab 	.id_table	= ov7670_id,
2045cb7a01acSMauro Carvalho Chehab };
2046cb7a01acSMauro Carvalho Chehab 
2047cb7a01acSMauro Carvalho Chehab module_i2c_driver(ov7670_driver);
2048