xref: /openbmc/linux/drivers/media/i2c/ov7670.c (revision 5556ab2a)
1cb7a01acSMauro Carvalho Chehab /*
2cb7a01acSMauro Carvalho Chehab  * A V4L2 driver for OmniVision OV7670 cameras.
3cb7a01acSMauro Carvalho Chehab  *
4cb7a01acSMauro Carvalho Chehab  * Copyright 2006 One Laptop Per Child Association, Inc.  Written
5cb7a01acSMauro Carvalho Chehab  * by Jonathan Corbet with substantial inspiration from Mark
6cb7a01acSMauro Carvalho Chehab  * McClelland's ovcamchip code.
7cb7a01acSMauro Carvalho Chehab  *
8cb7a01acSMauro Carvalho Chehab  * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
9cb7a01acSMauro Carvalho Chehab  *
10cb7a01acSMauro Carvalho Chehab  * This file may be distributed under the terms of the GNU General
11cb7a01acSMauro Carvalho Chehab  * Public License, version 2.
12cb7a01acSMauro Carvalho Chehab  */
130a024d63SHans Verkuil #include <linux/clk.h>
14cb7a01acSMauro Carvalho Chehab #include <linux/init.h>
15cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
16cb7a01acSMauro Carvalho Chehab #include <linux/slab.h>
17cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
18cb7a01acSMauro Carvalho Chehab #include <linux/delay.h>
19cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h>
20a0c4164eSHans Verkuil #include <linux/gpio.h>
21a0c4164eSHans Verkuil #include <linux/gpio/consumer.h>
22cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h>
237852adf8SAkinobu Mita #include <media/v4l2-event.h>
24492959c7SJavier Martin #include <media/v4l2-ctrls.h>
2501b84448SJacopo Mondi #include <media/v4l2-fwnode.h>
26cb7a01acSMauro Carvalho Chehab #include <media/v4l2-mediabus.h>
274721b3ebSAxel Lin #include <media/v4l2-image-sizes.h>
28b5dcee22SMauro Carvalho Chehab #include <media/i2c/ov7670.h>
29cb7a01acSMauro Carvalho Chehab 
30cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
31cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
32cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
33cb7a01acSMauro Carvalho Chehab 
34cb7a01acSMauro Carvalho Chehab static bool debug;
35cb7a01acSMauro Carvalho Chehab module_param(debug, bool, 0644);
36cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)");
37cb7a01acSMauro Carvalho Chehab 
38cb7a01acSMauro Carvalho Chehab /*
39cb7a01acSMauro Carvalho Chehab  * The 7670 sits on i2c with ID 0x42
40cb7a01acSMauro Carvalho Chehab  */
41cb7a01acSMauro Carvalho Chehab #define OV7670_I2C_ADDR 0x42
42cb7a01acSMauro Carvalho Chehab 
43f6dd927fSJavier Martin #define PLL_FACTOR	4
44f6dd927fSJavier Martin 
45cb7a01acSMauro Carvalho Chehab /* Registers */
46cb7a01acSMauro Carvalho Chehab #define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
47cb7a01acSMauro Carvalho Chehab #define REG_BLUE	0x01	/* blue gain */
48cb7a01acSMauro Carvalho Chehab #define REG_RED		0x02	/* red gain */
49cb7a01acSMauro Carvalho Chehab #define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
50cb7a01acSMauro Carvalho Chehab #define REG_COM1	0x04	/* Control 1 */
51cb7a01acSMauro Carvalho Chehab #define  COM1_CCIR656	  0x40  /* CCIR656 enable */
52cb7a01acSMauro Carvalho Chehab #define REG_BAVE	0x05	/* U/B Average level */
53cb7a01acSMauro Carvalho Chehab #define REG_GbAVE	0x06	/* Y/Gb Average level */
54cb7a01acSMauro Carvalho Chehab #define REG_AECHH	0x07	/* AEC MS 5 bits */
55cb7a01acSMauro Carvalho Chehab #define REG_RAVE	0x08	/* V/R Average level */
56cb7a01acSMauro Carvalho Chehab #define REG_COM2	0x09	/* Control 2 */
57cb7a01acSMauro Carvalho Chehab #define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
58cb7a01acSMauro Carvalho Chehab #define REG_PID		0x0a	/* Product ID MSB */
59cb7a01acSMauro Carvalho Chehab #define REG_VER		0x0b	/* Product ID LSB */
60cb7a01acSMauro Carvalho Chehab #define REG_COM3	0x0c	/* Control 3 */
61cb7a01acSMauro Carvalho Chehab #define  COM3_SWAP	  0x40	  /* Byte swap */
62cb7a01acSMauro Carvalho Chehab #define  COM3_SCALEEN	  0x08	  /* Enable scaling */
63cb7a01acSMauro Carvalho Chehab #define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
64cb7a01acSMauro Carvalho Chehab #define REG_COM4	0x0d	/* Control 4 */
65cb7a01acSMauro Carvalho Chehab #define REG_COM5	0x0e	/* All "reserved" */
66cb7a01acSMauro Carvalho Chehab #define REG_COM6	0x0f	/* Control 6 */
67cb7a01acSMauro Carvalho Chehab #define REG_AECH	0x10	/* More bits of AEC value */
68cb7a01acSMauro Carvalho Chehab #define REG_CLKRC	0x11	/* Clocl control */
69cb7a01acSMauro Carvalho Chehab #define   CLK_EXT	  0x40	  /* Use external clock directly */
70cb7a01acSMauro Carvalho Chehab #define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
71cb7a01acSMauro Carvalho Chehab #define REG_COM7	0x12	/* Control 7 */
72cb7a01acSMauro Carvalho Chehab #define   COM7_RESET	  0x80	  /* Register reset */
73cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_MASK	  0x38
74cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_VGA	  0x00
75cb7a01acSMauro Carvalho Chehab #define	  COM7_FMT_CIF	  0x20	  /* CIF format */
76cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
77cb7a01acSMauro Carvalho Chehab #define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
78cb7a01acSMauro Carvalho Chehab #define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
79cb7a01acSMauro Carvalho Chehab #define	  COM7_YUV	  0x00	  /* YUV */
80cb7a01acSMauro Carvalho Chehab #define	  COM7_BAYER	  0x01	  /* Bayer format */
81cb7a01acSMauro Carvalho Chehab #define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
82cb7a01acSMauro Carvalho Chehab #define REG_COM8	0x13	/* Control 8 */
83cb7a01acSMauro Carvalho Chehab #define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
84cb7a01acSMauro Carvalho Chehab #define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
85cb7a01acSMauro Carvalho Chehab #define   COM8_BFILT	  0x20	  /* Band filter enable */
86cb7a01acSMauro Carvalho Chehab #define   COM8_AGC	  0x04	  /* Auto gain enable */
87cb7a01acSMauro Carvalho Chehab #define   COM8_AWB	  0x02	  /* White balance enable */
88cb7a01acSMauro Carvalho Chehab #define   COM8_AEC	  0x01	  /* Auto exposure enable */
89cb7a01acSMauro Carvalho Chehab #define REG_COM9	0x14	/* Control 9  - gain ceiling */
90cb7a01acSMauro Carvalho Chehab #define REG_COM10	0x15	/* Control 10 */
91cb7a01acSMauro Carvalho Chehab #define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
92cb7a01acSMauro Carvalho Chehab #define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
93cb7a01acSMauro Carvalho Chehab #define   COM10_HREF_REV  0x08	  /* Reverse HREF */
94cb7a01acSMauro Carvalho Chehab #define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
95cb7a01acSMauro Carvalho Chehab #define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
96cb7a01acSMauro Carvalho Chehab #define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
97cb7a01acSMauro Carvalho Chehab #define REG_HSTART	0x17	/* Horiz start high bits */
98cb7a01acSMauro Carvalho Chehab #define REG_HSTOP	0x18	/* Horiz stop high bits */
99cb7a01acSMauro Carvalho Chehab #define REG_VSTART	0x19	/* Vert start high bits */
100cb7a01acSMauro Carvalho Chehab #define REG_VSTOP	0x1a	/* Vert stop high bits */
101cb7a01acSMauro Carvalho Chehab #define REG_PSHFT	0x1b	/* Pixel delay after HREF */
102cb7a01acSMauro Carvalho Chehab #define REG_MIDH	0x1c	/* Manuf. ID high */
103cb7a01acSMauro Carvalho Chehab #define REG_MIDL	0x1d	/* Manuf. ID low */
104cb7a01acSMauro Carvalho Chehab #define REG_MVFP	0x1e	/* Mirror / vflip */
105cb7a01acSMauro Carvalho Chehab #define   MVFP_MIRROR	  0x20	  /* Mirror image */
106cb7a01acSMauro Carvalho Chehab #define   MVFP_FLIP	  0x10	  /* Vertical flip */
107cb7a01acSMauro Carvalho Chehab 
108cb7a01acSMauro Carvalho Chehab #define REG_AEW		0x24	/* AGC upper limit */
109cb7a01acSMauro Carvalho Chehab #define REG_AEB		0x25	/* AGC lower limit */
110cb7a01acSMauro Carvalho Chehab #define REG_VPT		0x26	/* AGC/AEC fast mode op region */
111cb7a01acSMauro Carvalho Chehab #define REG_HSYST	0x30	/* HSYNC rising edge delay */
112cb7a01acSMauro Carvalho Chehab #define REG_HSYEN	0x31	/* HSYNC falling edge delay */
113cb7a01acSMauro Carvalho Chehab #define REG_HREF	0x32	/* HREF pieces */
114cb7a01acSMauro Carvalho Chehab #define REG_TSLB	0x3a	/* lots of stuff */
115cb7a01acSMauro Carvalho Chehab #define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
116cb7a01acSMauro Carvalho Chehab #define REG_COM11	0x3b	/* Control 11 */
117cb7a01acSMauro Carvalho Chehab #define   COM11_NIGHT	  0x80	  /* NIght mode enable */
118cb7a01acSMauro Carvalho Chehab #define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
119cb7a01acSMauro Carvalho Chehab #define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
120cb7a01acSMauro Carvalho Chehab #define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
121cb7a01acSMauro Carvalho Chehab #define   COM11_EXP	  0x02
122cb7a01acSMauro Carvalho Chehab #define REG_COM12	0x3c	/* Control 12 */
123cb7a01acSMauro Carvalho Chehab #define   COM12_HREF	  0x80	  /* HREF always */
124cb7a01acSMauro Carvalho Chehab #define REG_COM13	0x3d	/* Control 13 */
125cb7a01acSMauro Carvalho Chehab #define   COM13_GAMMA	  0x80	  /* Gamma enable */
126cb7a01acSMauro Carvalho Chehab #define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
127cb7a01acSMauro Carvalho Chehab #define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
128cb7a01acSMauro Carvalho Chehab #define REG_COM14	0x3e	/* Control 14 */
129cb7a01acSMauro Carvalho Chehab #define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
130cb7a01acSMauro Carvalho Chehab #define REG_EDGE	0x3f	/* Edge enhancement factor */
131cb7a01acSMauro Carvalho Chehab #define REG_COM15	0x40	/* Control 15 */
132cb7a01acSMauro Carvalho Chehab #define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
133cb7a01acSMauro Carvalho Chehab #define	  COM15_R01FE	  0x80	  /*            01 to FE */
134cb7a01acSMauro Carvalho Chehab #define   COM15_R00FF	  0xc0	  /*            00 to FF */
135cb7a01acSMauro Carvalho Chehab #define   COM15_RGB565	  0x10	  /* RGB565 output */
136cb7a01acSMauro Carvalho Chehab #define   COM15_RGB555	  0x30	  /* RGB555 output */
137cb7a01acSMauro Carvalho Chehab #define REG_COM16	0x41	/* Control 16 */
138cb7a01acSMauro Carvalho Chehab #define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
139cb7a01acSMauro Carvalho Chehab #define REG_COM17	0x42	/* Control 17 */
140cb7a01acSMauro Carvalho Chehab #define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
141cb7a01acSMauro Carvalho Chehab #define   COM17_CBAR	  0x08	  /* DSP Color bar */
142cb7a01acSMauro Carvalho Chehab 
143cb7a01acSMauro Carvalho Chehab /*
144cb7a01acSMauro Carvalho Chehab  * This matrix defines how the colors are generated, must be
145cb7a01acSMauro Carvalho Chehab  * tweaked to adjust hue and saturation.
146cb7a01acSMauro Carvalho Chehab  *
147cb7a01acSMauro Carvalho Chehab  * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
148cb7a01acSMauro Carvalho Chehab  *
149cb7a01acSMauro Carvalho Chehab  * They are nine-bit signed quantities, with the sign bit
150cb7a01acSMauro Carvalho Chehab  * stored in 0x58.  Sign for v-red is bit 0, and up from there.
151cb7a01acSMauro Carvalho Chehab  */
152cb7a01acSMauro Carvalho Chehab #define	REG_CMATRIX_BASE 0x4f
153cb7a01acSMauro Carvalho Chehab #define   CMATRIX_LEN 6
154cb7a01acSMauro Carvalho Chehab #define REG_CMATRIX_SIGN 0x58
155cb7a01acSMauro Carvalho Chehab 
156cb7a01acSMauro Carvalho Chehab 
157cb7a01acSMauro Carvalho Chehab #define REG_BRIGHT	0x55	/* Brightness */
158cb7a01acSMauro Carvalho Chehab #define REG_CONTRAS	0x56	/* Contrast control */
159cb7a01acSMauro Carvalho Chehab 
160cb7a01acSMauro Carvalho Chehab #define REG_GFIX	0x69	/* Fix gain control */
161cb7a01acSMauro Carvalho Chehab 
162f6dd927fSJavier Martin #define REG_DBLV	0x6b	/* PLL control an debugging */
16361da76beSJacopo Mondi #define   DBLV_BYPASS	  0x0a	  /* Bypass PLL */
16461da76beSJacopo Mondi #define   DBLV_X4	  0x4a	  /* clock x4 */
16561da76beSJacopo Mondi #define   DBLV_X6	  0x8a	  /* clock x6 */
16661da76beSJacopo Mondi #define   DBLV_X8	  0xca	  /* clock x8 */
167f6dd927fSJavier Martin 
168b48d908dSAkinobu Mita #define REG_SCALING_XSC	0x70	/* Test pattern and horizontal scale factor */
169b48d908dSAkinobu Mita #define   TEST_PATTTERN_0 0x80
170b48d908dSAkinobu Mita #define REG_SCALING_YSC	0x71	/* Test pattern and vertical scale factor */
171b48d908dSAkinobu Mita #define   TEST_PATTTERN_1 0x80
172b48d908dSAkinobu Mita 
173cb7a01acSMauro Carvalho Chehab #define REG_REG76	0x76	/* OV's name */
174cb7a01acSMauro Carvalho Chehab #define   R76_BLKPCOR	  0x80	  /* Black pixel correction enable */
175cb7a01acSMauro Carvalho Chehab #define   R76_WHTPCOR	  0x40	  /* White pixel correction enable */
176cb7a01acSMauro Carvalho Chehab 
177cb7a01acSMauro Carvalho Chehab #define REG_RGB444	0x8c	/* RGB 444 control */
178cb7a01acSMauro Carvalho Chehab #define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
179cb7a01acSMauro Carvalho Chehab #define   R444_RGBX	  0x01	  /* Empty nibble at end */
180cb7a01acSMauro Carvalho Chehab 
181cb7a01acSMauro Carvalho Chehab #define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
182cb7a01acSMauro Carvalho Chehab #define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
183cb7a01acSMauro Carvalho Chehab 
184cb7a01acSMauro Carvalho Chehab #define REG_BD50MAX	0xa5	/* 50hz banding step limit */
185cb7a01acSMauro Carvalho Chehab #define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
186cb7a01acSMauro Carvalho Chehab #define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
187cb7a01acSMauro Carvalho Chehab #define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
188cb7a01acSMauro Carvalho Chehab #define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
189cb7a01acSMauro Carvalho Chehab #define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
190cb7a01acSMauro Carvalho Chehab #define REG_BD60MAX	0xab	/* 60hz banding step limit */
191cb7a01acSMauro Carvalho Chehab 
192d058e237SJavier Martin enum ov7670_model {
193d058e237SJavier Martin 	MODEL_OV7670 = 0,
194d058e237SJavier Martin 	MODEL_OV7675,
195d058e237SJavier Martin };
196d058e237SJavier Martin 
197d058e237SJavier Martin struct ov7670_win_size {
198d058e237SJavier Martin 	int	width;
199d058e237SJavier Martin 	int	height;
200d058e237SJavier Martin 	unsigned char com7_bit;
201d058e237SJavier Martin 	int	hstart;		/* Start/stop values for the camera.  Note */
202d058e237SJavier Martin 	int	hstop;		/* that they do not always make complete */
203d058e237SJavier Martin 	int	vstart;		/* sense to humans, but evidently the sensor */
204d058e237SJavier Martin 	int	vstop;		/* will do the right thing... */
205d058e237SJavier Martin 	struct regval_list *regs; /* Regs to tweak */
206d058e237SJavier Martin };
207d058e237SJavier Martin 
208d058e237SJavier Martin struct ov7670_devtype {
209d058e237SJavier Martin 	/* formats supported for each model */
210d058e237SJavier Martin 	struct ov7670_win_size *win_sizes;
211d058e237SJavier Martin 	unsigned int n_win_sizes;
212f6dd927fSJavier Martin 	/* callbacks for frame rate control */
213f6dd927fSJavier Martin 	int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
214f6dd927fSJavier Martin 	void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
215d058e237SJavier Martin };
216cb7a01acSMauro Carvalho Chehab 
217cb7a01acSMauro Carvalho Chehab /*
218cb7a01acSMauro Carvalho Chehab  * Information we maintain about a known sensor.
219cb7a01acSMauro Carvalho Chehab  */
220cb7a01acSMauro Carvalho Chehab struct ov7670_format_struct;  /* coming later */
221cb7a01acSMauro Carvalho Chehab struct ov7670_info {
222cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev sd;
223d94a26f0SWenyou Yang #if defined(CONFIG_MEDIA_CONTROLLER)
224d94a26f0SWenyou Yang 	struct media_pad pad;
225d94a26f0SWenyou Yang #endif
226492959c7SJavier Martin 	struct v4l2_ctrl_handler hdl;
227492959c7SJavier Martin 	struct {
228492959c7SJavier Martin 		/* gain cluster */
229492959c7SJavier Martin 		struct v4l2_ctrl *auto_gain;
230492959c7SJavier Martin 		struct v4l2_ctrl *gain;
231492959c7SJavier Martin 	};
232492959c7SJavier Martin 	struct {
233492959c7SJavier Martin 		/* exposure cluster */
234492959c7SJavier Martin 		struct v4l2_ctrl *auto_exposure;
235492959c7SJavier Martin 		struct v4l2_ctrl *exposure;
236492959c7SJavier Martin 	};
237492959c7SJavier Martin 	struct {
238492959c7SJavier Martin 		/* saturation/hue cluster */
239492959c7SJavier Martin 		struct v4l2_ctrl *saturation;
240492959c7SJavier Martin 		struct v4l2_ctrl *hue;
241492959c7SJavier Martin 	};
242c0662dd4SWenyou Yang 	struct v4l2_mbus_framefmt format;
243cb7a01acSMauro Carvalho Chehab 	struct ov7670_format_struct *fmt;  /* Current format */
2445556ab2aSLubomir Rintel 	struct ov7670_win_size *wsize;
2450a024d63SHans Verkuil 	struct clk *clk;
246a0c4164eSHans Verkuil 	struct gpio_desc *resetb_gpio;
247a0c4164eSHans Verkuil 	struct gpio_desc *pwdn_gpio;
24801b84448SJacopo Mondi 	unsigned int mbus_config;	/* Media bus configuration flags */
249cb7a01acSMauro Carvalho Chehab 	int min_width;			/* Filter out smaller sizes */
250cb7a01acSMauro Carvalho Chehab 	int min_height;			/* Filter out smaller sizes */
251cb7a01acSMauro Carvalho Chehab 	int clock_speed;		/* External clock speed (MHz) */
252cb7a01acSMauro Carvalho Chehab 	u8 clkrc;			/* Clock divider value */
253cb7a01acSMauro Carvalho Chehab 	bool use_smbus;			/* Use smbus I/O instead of I2C */
25404ee6d92SJavier Martin 	bool pll_bypass;
255ee95258eSJavier Martin 	bool pclk_hb_disable;
256d058e237SJavier Martin 	const struct ov7670_devtype *devtype; /* Device specifics */
257cb7a01acSMauro Carvalho Chehab };
258cb7a01acSMauro Carvalho Chehab 
259cb7a01acSMauro Carvalho Chehab static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
260cb7a01acSMauro Carvalho Chehab {
261cb7a01acSMauro Carvalho Chehab 	return container_of(sd, struct ov7670_info, sd);
262cb7a01acSMauro Carvalho Chehab }
263cb7a01acSMauro Carvalho Chehab 
264492959c7SJavier Martin static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
265492959c7SJavier Martin {
266492959c7SJavier Martin 	return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd;
267492959c7SJavier Martin }
268492959c7SJavier Martin 
269cb7a01acSMauro Carvalho Chehab 
270cb7a01acSMauro Carvalho Chehab 
271cb7a01acSMauro Carvalho Chehab /*
272cb7a01acSMauro Carvalho Chehab  * The default register settings, as obtained from OmniVision.  There
273cb7a01acSMauro Carvalho Chehab  * is really no making sense of most of these - lots of "reserved" values
274cb7a01acSMauro Carvalho Chehab  * and such.
275cb7a01acSMauro Carvalho Chehab  *
276cb7a01acSMauro Carvalho Chehab  * These settings give VGA YUYV.
277cb7a01acSMauro Carvalho Chehab  */
278cb7a01acSMauro Carvalho Chehab 
279cb7a01acSMauro Carvalho Chehab struct regval_list {
280cb7a01acSMauro Carvalho Chehab 	unsigned char reg_num;
281cb7a01acSMauro Carvalho Chehab 	unsigned char value;
282cb7a01acSMauro Carvalho Chehab };
283cb7a01acSMauro Carvalho Chehab 
284cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_default_regs[] = {
285cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RESET },
286cb7a01acSMauro Carvalho Chehab /*
287cb7a01acSMauro Carvalho Chehab  * Clock scale: 3 = 15fps
288cb7a01acSMauro Carvalho Chehab  *              2 = 20fps
289cb7a01acSMauro Carvalho Chehab  *              1 = 30fps
290cb7a01acSMauro Carvalho Chehab  */
291cb7a01acSMauro Carvalho Chehab 	{ REG_CLKRC, 0x1 },	/* OV: clock scale (30 fps) */
292cb7a01acSMauro Carvalho Chehab 	{ REG_TSLB,  0x04 },	/* OV */
293cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, 0 },	/* VGA */
294cb7a01acSMauro Carvalho Chehab 	/*
295cb7a01acSMauro Carvalho Chehab 	 * Set the hardware window.  These values from OV don't entirely
296cb7a01acSMauro Carvalho Chehab 	 * make sense - hstop is less than hstart.  But they work...
297cb7a01acSMauro Carvalho Chehab 	 */
298cb7a01acSMauro Carvalho Chehab 	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
299cb7a01acSMauro Carvalho Chehab 	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
300cb7a01acSMauro Carvalho Chehab 	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
301cb7a01acSMauro Carvalho Chehab 
302cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, 0 },	{ REG_COM14, 0 },
303cb7a01acSMauro Carvalho Chehab 	/* Mystery scaling numbers */
304b48d908dSAkinobu Mita 	{ REG_SCALING_XSC, 0x3a },
305b48d908dSAkinobu Mita 	{ REG_SCALING_YSC, 0x35 },
306cb7a01acSMauro Carvalho Chehab 	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
307cb7a01acSMauro Carvalho Chehab 	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
308cb7a01acSMauro Carvalho Chehab 
309cb7a01acSMauro Carvalho Chehab 	/* Gamma curve values */
310cb7a01acSMauro Carvalho Chehab 	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
311cb7a01acSMauro Carvalho Chehab 	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
312cb7a01acSMauro Carvalho Chehab 	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
313cb7a01acSMauro Carvalho Chehab 	{ 0x80, 0x76 },		{ 0x81, 0x80 },
314cb7a01acSMauro Carvalho Chehab 	{ 0x82, 0x88 },		{ 0x83, 0x8f },
315cb7a01acSMauro Carvalho Chehab 	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
316cb7a01acSMauro Carvalho Chehab 	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
317cb7a01acSMauro Carvalho Chehab 	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
318cb7a01acSMauro Carvalho Chehab 
319cb7a01acSMauro Carvalho Chehab 	/* AGC and AEC parameters.  Note we start by disabling those features,
320cb7a01acSMauro Carvalho Chehab 	   then turn them only after tweaking the values. */
321cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
322cb7a01acSMauro Carvalho Chehab 	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
323cb7a01acSMauro Carvalho Chehab 	{ REG_COM4, 0x40 }, /* magic reserved bit */
324cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
325cb7a01acSMauro Carvalho Chehab 	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
326cb7a01acSMauro Carvalho Chehab 	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
327cb7a01acSMauro Carvalho Chehab 	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
328cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
329cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
330cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
331cb7a01acSMauro Carvalho Chehab 	{ REG_HAECC7, 0x94 },
332cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
333cb7a01acSMauro Carvalho Chehab 
334cb7a01acSMauro Carvalho Chehab 	/* Almost all of these are magic "reserved" values.  */
335cb7a01acSMauro Carvalho Chehab 	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
336cb7a01acSMauro Carvalho Chehab 	{ 0x16, 0x02 },		{ REG_MVFP, 0x07 },
337cb7a01acSMauro Carvalho Chehab 	{ 0x21, 0x02 },		{ 0x22, 0x91 },
338cb7a01acSMauro Carvalho Chehab 	{ 0x29, 0x07 },		{ 0x33, 0x0b },
339cb7a01acSMauro Carvalho Chehab 	{ 0x35, 0x0b },		{ 0x37, 0x1d },
340cb7a01acSMauro Carvalho Chehab 	{ 0x38, 0x71 },		{ 0x39, 0x2a },
341cb7a01acSMauro Carvalho Chehab 	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
342cb7a01acSMauro Carvalho Chehab 	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
343cb7a01acSMauro Carvalho Chehab 	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
344cb7a01acSMauro Carvalho Chehab 	{ 0x8d, 0x4f },		{ 0x8e, 0 },
345cb7a01acSMauro Carvalho Chehab 	{ 0x8f, 0 },		{ 0x90, 0 },
346cb7a01acSMauro Carvalho Chehab 	{ 0x91, 0 },		{ 0x96, 0 },
347cb7a01acSMauro Carvalho Chehab 	{ 0x9a, 0 },		{ 0xb0, 0x84 },
348cb7a01acSMauro Carvalho Chehab 	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
349cb7a01acSMauro Carvalho Chehab 	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
350cb7a01acSMauro Carvalho Chehab 
351cb7a01acSMauro Carvalho Chehab 	/* More reserved magic, some of which tweaks white balance */
352cb7a01acSMauro Carvalho Chehab 	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
353cb7a01acSMauro Carvalho Chehab 	{ 0x45, 0x34 },		{ 0x46, 0x58 },
354cb7a01acSMauro Carvalho Chehab 	{ 0x47, 0x28 },		{ 0x48, 0x3a },
355cb7a01acSMauro Carvalho Chehab 	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
356cb7a01acSMauro Carvalho Chehab 	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
357cb7a01acSMauro Carvalho Chehab 	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
358cb7a01acSMauro Carvalho Chehab 	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
359cb7a01acSMauro Carvalho Chehab 	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
360cb7a01acSMauro Carvalho Chehab 	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
361cb7a01acSMauro Carvalho Chehab 	{ REG_RED, 0x60 },
362cb7a01acSMauro Carvalho Chehab 	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
363cb7a01acSMauro Carvalho Chehab 
364cb7a01acSMauro Carvalho Chehab 	/* Matrix coefficients */
365cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
366cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0 },		{ 0x52, 0x22 },
367cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0x5e },		{ 0x54, 0x80 },
368cb7a01acSMauro Carvalho Chehab 	{ 0x58, 0x9e },
369cb7a01acSMauro Carvalho Chehab 
370cb7a01acSMauro Carvalho Chehab 	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
371cb7a01acSMauro Carvalho Chehab 	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
372cb7a01acSMauro Carvalho Chehab 	{ 0x4c, 0 },		{ 0x77, 0x01 },
373cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
374cb7a01acSMauro Carvalho Chehab 	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
375cb7a01acSMauro Carvalho Chehab 	{ 0x56, 0x40 },
376cb7a01acSMauro Carvalho Chehab 
377cb7a01acSMauro Carvalho Chehab 	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
378cb7a01acSMauro Carvalho Chehab 	{ 0xa4, 0x88 },		{ 0x96, 0 },
379cb7a01acSMauro Carvalho Chehab 	{ 0x97, 0x30 },		{ 0x98, 0x20 },
380cb7a01acSMauro Carvalho Chehab 	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
381cb7a01acSMauro Carvalho Chehab 	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
382cb7a01acSMauro Carvalho Chehab 	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
383cb7a01acSMauro Carvalho Chehab 	{ 0x78, 0x04 },
384cb7a01acSMauro Carvalho Chehab 
385cb7a01acSMauro Carvalho Chehab 	/* Extra-weird stuff.  Some sort of multiplexor register */
386cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
387cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
388cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
389cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
390cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
391cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
392cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
393cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
394cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
395cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
396cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
397cb7a01acSMauro Carvalho Chehab 	{ 0x79, 0x26 },
398cb7a01acSMauro Carvalho Chehab 
399cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },	/* END MARKER */
400cb7a01acSMauro Carvalho Chehab };
401cb7a01acSMauro Carvalho Chehab 
402cb7a01acSMauro Carvalho Chehab 
403cb7a01acSMauro Carvalho Chehab /*
404cb7a01acSMauro Carvalho Chehab  * Here we'll try to encapsulate the changes for just the output
405cb7a01acSMauro Carvalho Chehab  * video format.
406cb7a01acSMauro Carvalho Chehab  *
407cb7a01acSMauro Carvalho Chehab  * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
408cb7a01acSMauro Carvalho Chehab  *
409cb7a01acSMauro Carvalho Chehab  * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
410cb7a01acSMauro Carvalho Chehab  */
411cb7a01acSMauro Carvalho Chehab 
412cb7a01acSMauro Carvalho Chehab 
413cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_yuv422[] = {
414cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, 0x0 },  /* Selects YUV mode */
415cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, 0 },	/* No RGB444 please */
416cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0 },	/* CCIR601 */
417cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_R00FF },
418c01b7429SJavier Martin 	{ REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */
419cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0x80 },		/* "matrix coefficient 1" */
420cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0x80 },		/* "matrix coefficient 2" */
421cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
422cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x22 },		/* "matrix coefficient 4" */
423cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0x5e },		/* "matrix coefficient 5" */
424cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0x80 },		/* "matrix coefficient 6" */
425cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
426cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
427cb7a01acSMauro Carvalho Chehab };
428cb7a01acSMauro Carvalho Chehab 
429cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb565[] = {
430cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
431cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, 0 },	/* No RGB444 please */
432cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0x0 },	/* CCIR601 */
433cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_RGB565 },
434cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },	/* 16x gain ceiling; 0x8 is reserved bit */
435cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0xb3 },		/* "matrix coefficient 1" */
436cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0xb3 },		/* "matrix coefficient 2" */
437cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
438cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x3d },		/* "matrix coefficient 4" */
439cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0xa7 },		/* "matrix coefficient 5" */
440cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0xe4 },		/* "matrix coefficient 6" */
441cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
442cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
443cb7a01acSMauro Carvalho Chehab };
444cb7a01acSMauro Carvalho Chehab 
445cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_rgb444[] = {
446cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
447cb7a01acSMauro Carvalho Chehab 	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
448cb7a01acSMauro Carvalho Chehab 	{ REG_COM1, 0x0 },	/* CCIR601 */
449cb7a01acSMauro Carvalho Chehab 	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
450cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },	/* 16x gain ceiling; 0x8 is reserved bit */
451cb7a01acSMauro Carvalho Chehab 	{ 0x4f, 0xb3 },		/* "matrix coefficient 1" */
452cb7a01acSMauro Carvalho Chehab 	{ 0x50, 0xb3 },		/* "matrix coefficient 2" */
453cb7a01acSMauro Carvalho Chehab 	{ 0x51, 0    },		/* vb */
454cb7a01acSMauro Carvalho Chehab 	{ 0x52, 0x3d },		/* "matrix coefficient 4" */
455cb7a01acSMauro Carvalho Chehab 	{ 0x53, 0xa7 },		/* "matrix coefficient 5" */
456cb7a01acSMauro Carvalho Chehab 	{ 0x54, 0xe4 },		/* "matrix coefficient 6" */
457cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
458cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
459cb7a01acSMauro Carvalho Chehab };
460cb7a01acSMauro Carvalho Chehab 
461cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_fmt_raw[] = {
462cb7a01acSMauro Carvalho Chehab 	{ REG_COM7, COM7_BAYER },
463cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
464cb7a01acSMauro Carvalho Chehab 	{ REG_COM16, 0x3d }, /* Edge enhancement, denoise */
465cb7a01acSMauro Carvalho Chehab 	{ REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
466cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
467cb7a01acSMauro Carvalho Chehab };
468cb7a01acSMauro Carvalho Chehab 
469cb7a01acSMauro Carvalho Chehab 
470cb7a01acSMauro Carvalho Chehab 
471cb7a01acSMauro Carvalho Chehab /*
472cb7a01acSMauro Carvalho Chehab  * Low-level register I/O.
473cb7a01acSMauro Carvalho Chehab  *
474cb7a01acSMauro Carvalho Chehab  * Note that there are two versions of these.  On the XO 1, the
475cb7a01acSMauro Carvalho Chehab  * i2c controller only does SMBUS, so that's what we use.  The
476cb7a01acSMauro Carvalho Chehab  * ov7670 is not really an SMBUS device, though, so the communication
477cb7a01acSMauro Carvalho Chehab  * is not always entirely reliable.
478cb7a01acSMauro Carvalho Chehab  */
479cb7a01acSMauro Carvalho Chehab static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
480cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
481cb7a01acSMauro Carvalho Chehab {
482cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
483cb7a01acSMauro Carvalho Chehab 	int ret;
484cb7a01acSMauro Carvalho Chehab 
485cb7a01acSMauro Carvalho Chehab 	ret = i2c_smbus_read_byte_data(client, reg);
486cb7a01acSMauro Carvalho Chehab 	if (ret >= 0) {
487cb7a01acSMauro Carvalho Chehab 		*value = (unsigned char)ret;
488cb7a01acSMauro Carvalho Chehab 		ret = 0;
489cb7a01acSMauro Carvalho Chehab 	}
490cb7a01acSMauro Carvalho Chehab 	return ret;
491cb7a01acSMauro Carvalho Chehab }
492cb7a01acSMauro Carvalho Chehab 
493cb7a01acSMauro Carvalho Chehab 
494cb7a01acSMauro Carvalho Chehab static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
495cb7a01acSMauro Carvalho Chehab 		unsigned char value)
496cb7a01acSMauro Carvalho Chehab {
497cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
498cb7a01acSMauro Carvalho Chehab 	int ret = i2c_smbus_write_byte_data(client, reg, value);
499cb7a01acSMauro Carvalho Chehab 
500cb7a01acSMauro Carvalho Chehab 	if (reg == REG_COM7 && (value & COM7_RESET))
501cb7a01acSMauro Carvalho Chehab 		msleep(5);  /* Wait for reset to run */
502cb7a01acSMauro Carvalho Chehab 	return ret;
503cb7a01acSMauro Carvalho Chehab }
504cb7a01acSMauro Carvalho Chehab 
505cb7a01acSMauro Carvalho Chehab /*
506cb7a01acSMauro Carvalho Chehab  * On most platforms, we'd rather do straight i2c I/O.
507cb7a01acSMauro Carvalho Chehab  */
508cb7a01acSMauro Carvalho Chehab static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
509cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
510cb7a01acSMauro Carvalho Chehab {
511cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
512cb7a01acSMauro Carvalho Chehab 	u8 data = reg;
513cb7a01acSMauro Carvalho Chehab 	struct i2c_msg msg;
514cb7a01acSMauro Carvalho Chehab 	int ret;
515cb7a01acSMauro Carvalho Chehab 
516cb7a01acSMauro Carvalho Chehab 	/*
517cb7a01acSMauro Carvalho Chehab 	 * Send out the register address...
518cb7a01acSMauro Carvalho Chehab 	 */
519cb7a01acSMauro Carvalho Chehab 	msg.addr = client->addr;
520cb7a01acSMauro Carvalho Chehab 	msg.flags = 0;
521cb7a01acSMauro Carvalho Chehab 	msg.len = 1;
522cb7a01acSMauro Carvalho Chehab 	msg.buf = &data;
523cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
524cb7a01acSMauro Carvalho Chehab 	if (ret < 0) {
525cb7a01acSMauro Carvalho Chehab 		printk(KERN_ERR "Error %d on register write\n", ret);
526cb7a01acSMauro Carvalho Chehab 		return ret;
527cb7a01acSMauro Carvalho Chehab 	}
528cb7a01acSMauro Carvalho Chehab 	/*
529cb7a01acSMauro Carvalho Chehab 	 * ...then read back the result.
530cb7a01acSMauro Carvalho Chehab 	 */
531cb7a01acSMauro Carvalho Chehab 	msg.flags = I2C_M_RD;
532cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
533cb7a01acSMauro Carvalho Chehab 	if (ret >= 0) {
534cb7a01acSMauro Carvalho Chehab 		*value = data;
535cb7a01acSMauro Carvalho Chehab 		ret = 0;
536cb7a01acSMauro Carvalho Chehab 	}
537cb7a01acSMauro Carvalho Chehab 	return ret;
538cb7a01acSMauro Carvalho Chehab }
539cb7a01acSMauro Carvalho Chehab 
540cb7a01acSMauro Carvalho Chehab 
541cb7a01acSMauro Carvalho Chehab static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
542cb7a01acSMauro Carvalho Chehab 		unsigned char value)
543cb7a01acSMauro Carvalho Chehab {
544cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
545cb7a01acSMauro Carvalho Chehab 	struct i2c_msg msg;
546cb7a01acSMauro Carvalho Chehab 	unsigned char data[2] = { reg, value };
547cb7a01acSMauro Carvalho Chehab 	int ret;
548cb7a01acSMauro Carvalho Chehab 
549cb7a01acSMauro Carvalho Chehab 	msg.addr = client->addr;
550cb7a01acSMauro Carvalho Chehab 	msg.flags = 0;
551cb7a01acSMauro Carvalho Chehab 	msg.len = 2;
552cb7a01acSMauro Carvalho Chehab 	msg.buf = data;
553cb7a01acSMauro Carvalho Chehab 	ret = i2c_transfer(client->adapter, &msg, 1);
554cb7a01acSMauro Carvalho Chehab 	if (ret > 0)
555cb7a01acSMauro Carvalho Chehab 		ret = 0;
556cb7a01acSMauro Carvalho Chehab 	if (reg == REG_COM7 && (value & COM7_RESET))
557cb7a01acSMauro Carvalho Chehab 		msleep(5);  /* Wait for reset to run */
558cb7a01acSMauro Carvalho Chehab 	return ret;
559cb7a01acSMauro Carvalho Chehab }
560cb7a01acSMauro Carvalho Chehab 
561cb7a01acSMauro Carvalho Chehab static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
562cb7a01acSMauro Carvalho Chehab 		unsigned char *value)
563cb7a01acSMauro Carvalho Chehab {
564cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
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);
575cb7a01acSMauro Carvalho Chehab 	if (info->use_smbus)
576cb7a01acSMauro Carvalho Chehab 		return ov7670_write_smbus(sd, reg, value);
577cb7a01acSMauro Carvalho Chehab 	else
578cb7a01acSMauro Carvalho Chehab 		return ov7670_write_i2c(sd, reg, value);
579cb7a01acSMauro Carvalho Chehab }
580cb7a01acSMauro Carvalho Chehab 
581b48d908dSAkinobu Mita static int ov7670_update_bits(struct v4l2_subdev *sd, unsigned char reg,
582b48d908dSAkinobu Mita 		unsigned char mask, unsigned char value)
583b48d908dSAkinobu Mita {
584b48d908dSAkinobu Mita 	unsigned char orig;
585b48d908dSAkinobu Mita 	int ret;
586b48d908dSAkinobu Mita 
587b48d908dSAkinobu Mita 	ret = ov7670_read(sd, reg, &orig);
588b48d908dSAkinobu Mita 	if (ret)
589b48d908dSAkinobu Mita 		return ret;
590b48d908dSAkinobu Mita 
591b48d908dSAkinobu Mita 	return ov7670_write(sd, reg, (orig & ~mask) | (value & mask));
592b48d908dSAkinobu Mita }
593b48d908dSAkinobu Mita 
594cb7a01acSMauro Carvalho Chehab /*
595cb7a01acSMauro Carvalho Chehab  * Write a list of register settings; ff/ff stops the process.
596cb7a01acSMauro Carvalho Chehab  */
597cb7a01acSMauro Carvalho Chehab static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
598cb7a01acSMauro Carvalho Chehab {
599cb7a01acSMauro Carvalho Chehab 	while (vals->reg_num != 0xff || vals->value != 0xff) {
600cb7a01acSMauro Carvalho Chehab 		int ret = ov7670_write(sd, vals->reg_num, vals->value);
601cb7a01acSMauro Carvalho Chehab 		if (ret < 0)
602cb7a01acSMauro Carvalho Chehab 			return ret;
603cb7a01acSMauro Carvalho Chehab 		vals++;
604cb7a01acSMauro Carvalho Chehab 	}
605cb7a01acSMauro Carvalho Chehab 	return 0;
606cb7a01acSMauro Carvalho Chehab }
607cb7a01acSMauro Carvalho Chehab 
608cb7a01acSMauro Carvalho Chehab 
609cb7a01acSMauro Carvalho Chehab /*
610cb7a01acSMauro Carvalho Chehab  * Stuff that knows about the sensor.
611cb7a01acSMauro Carvalho Chehab  */
612cb7a01acSMauro Carvalho Chehab static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
613cb7a01acSMauro Carvalho Chehab {
614cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM7, COM7_RESET);
615cb7a01acSMauro Carvalho Chehab 	msleep(1);
616cb7a01acSMauro Carvalho Chehab 	return 0;
617cb7a01acSMauro Carvalho Chehab }
618cb7a01acSMauro Carvalho Chehab 
619cb7a01acSMauro Carvalho Chehab 
620cb7a01acSMauro Carvalho Chehab static int ov7670_init(struct v4l2_subdev *sd, u32 val)
621cb7a01acSMauro Carvalho Chehab {
622cb7a01acSMauro Carvalho Chehab 	return ov7670_write_array(sd, ov7670_default_regs);
623cb7a01acSMauro Carvalho Chehab }
624cb7a01acSMauro Carvalho Chehab 
625cb7a01acSMauro Carvalho Chehab static int ov7670_detect(struct v4l2_subdev *sd)
626cb7a01acSMauro Carvalho Chehab {
627cb7a01acSMauro Carvalho Chehab 	unsigned char v;
628cb7a01acSMauro Carvalho Chehab 	int ret;
629cb7a01acSMauro Carvalho Chehab 
630cb7a01acSMauro Carvalho Chehab 	ret = ov7670_init(sd, 0);
631cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
632cb7a01acSMauro Carvalho Chehab 		return ret;
633cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MIDH, &v);
634cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
635cb7a01acSMauro Carvalho Chehab 		return ret;
636cb7a01acSMauro Carvalho Chehab 	if (v != 0x7f) /* OV manuf. id. */
637cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
638cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MIDL, &v);
639cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
640cb7a01acSMauro Carvalho Chehab 		return ret;
641cb7a01acSMauro Carvalho Chehab 	if (v != 0xa2)
642cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
643cb7a01acSMauro Carvalho Chehab 	/*
644cb7a01acSMauro Carvalho Chehab 	 * OK, we know we have an OmniVision chip...but which one?
645cb7a01acSMauro Carvalho Chehab 	 */
646cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_PID, &v);
647cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
648cb7a01acSMauro Carvalho Chehab 		return ret;
649cb7a01acSMauro Carvalho Chehab 	if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
650cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
651cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_VER, &v);
652cb7a01acSMauro Carvalho Chehab 	if (ret < 0)
653cb7a01acSMauro Carvalho Chehab 		return ret;
654cb7a01acSMauro Carvalho Chehab 	if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
655cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
656cb7a01acSMauro Carvalho Chehab 	return 0;
657cb7a01acSMauro Carvalho Chehab }
658cb7a01acSMauro Carvalho Chehab 
659cb7a01acSMauro Carvalho Chehab 
660cb7a01acSMauro Carvalho Chehab /*
661cb7a01acSMauro Carvalho Chehab  * Store information about the video data format.  The color matrix
662cb7a01acSMauro Carvalho Chehab  * is deeply tied into the format, so keep the relevant values here.
663cb7a01acSMauro Carvalho Chehab  * The magic matrix numbers come from OmniVision.
664cb7a01acSMauro Carvalho Chehab  */
665cb7a01acSMauro Carvalho Chehab static struct ov7670_format_struct {
666f5fe58fdSBoris BREZILLON 	u32 mbus_code;
667cb7a01acSMauro Carvalho Chehab 	enum v4l2_colorspace colorspace;
668cb7a01acSMauro Carvalho Chehab 	struct regval_list *regs;
669cb7a01acSMauro Carvalho Chehab 	int cmatrix[CMATRIX_LEN];
670cb7a01acSMauro Carvalho Chehab } ov7670_formats[] = {
671cb7a01acSMauro Carvalho Chehab 	{
672f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_YUYV8_2X8,
67375eb9847SHans Verkuil 		.colorspace	= V4L2_COLORSPACE_SRGB,
674cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_yuv422,
675cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
676cb7a01acSMauro Carvalho Chehab 	},
677cb7a01acSMauro Carvalho Chehab 	{
678f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
679cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
680cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_rgb444,
681cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
682cb7a01acSMauro Carvalho Chehab 	},
683cb7a01acSMauro Carvalho Chehab 	{
684f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_RGB565_2X8_LE,
685cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
686cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_rgb565,
687cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
688cb7a01acSMauro Carvalho Chehab 	},
689cb7a01acSMauro Carvalho Chehab 	{
690f5fe58fdSBoris BREZILLON 		.mbus_code	= MEDIA_BUS_FMT_SBGGR8_1X8,
691cb7a01acSMauro Carvalho Chehab 		.colorspace	= V4L2_COLORSPACE_SRGB,
692cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_fmt_raw,
693cb7a01acSMauro Carvalho Chehab 		.cmatrix	= { 0, 0, 0, 0, 0, 0 },
694cb7a01acSMauro Carvalho Chehab 	},
695cb7a01acSMauro Carvalho Chehab };
696cb7a01acSMauro Carvalho Chehab #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
697cb7a01acSMauro Carvalho Chehab 
698cb7a01acSMauro Carvalho Chehab 
699cb7a01acSMauro Carvalho Chehab /*
700cb7a01acSMauro Carvalho Chehab  * Then there is the issue of window sizes.  Try to capture the info here.
701cb7a01acSMauro Carvalho Chehab  */
702cb7a01acSMauro Carvalho Chehab 
703cb7a01acSMauro Carvalho Chehab /*
704cb7a01acSMauro Carvalho Chehab  * QCIF mode is done (by OV) in a very strange way - it actually looks like
705cb7a01acSMauro Carvalho Chehab  * VGA with weird scaling options - they do *not* use the canned QCIF mode
706cb7a01acSMauro Carvalho Chehab  * which is allegedly provided by the sensor.  So here's the weird register
707cb7a01acSMauro Carvalho Chehab  * settings.
708cb7a01acSMauro Carvalho Chehab  */
709cb7a01acSMauro Carvalho Chehab static struct regval_list ov7670_qcif_regs[] = {
710cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, COM3_SCALEEN|COM3_DCWEN },
711cb7a01acSMauro Carvalho Chehab 	{ REG_COM3, COM3_DCWEN },
712cb7a01acSMauro Carvalho Chehab 	{ REG_COM14, COM14_DCWEN | 0x01},
713cb7a01acSMauro Carvalho Chehab 	{ 0x73, 0xf1 },
714cb7a01acSMauro Carvalho Chehab 	{ 0xa2, 0x52 },
715cb7a01acSMauro Carvalho Chehab 	{ 0x7b, 0x1c },
716cb7a01acSMauro Carvalho Chehab 	{ 0x7c, 0x28 },
717cb7a01acSMauro Carvalho Chehab 	{ 0x7d, 0x3c },
718cb7a01acSMauro Carvalho Chehab 	{ 0x7f, 0x69 },
719cb7a01acSMauro Carvalho Chehab 	{ REG_COM9, 0x38 },
720cb7a01acSMauro Carvalho Chehab 	{ 0xa1, 0x0b },
721cb7a01acSMauro Carvalho Chehab 	{ 0x74, 0x19 },
722cb7a01acSMauro Carvalho Chehab 	{ 0x9a, 0x80 },
723cb7a01acSMauro Carvalho Chehab 	{ 0x43, 0x14 },
724cb7a01acSMauro Carvalho Chehab 	{ REG_COM13, 0xc0 },
725cb7a01acSMauro Carvalho Chehab 	{ 0xff, 0xff },
726cb7a01acSMauro Carvalho Chehab };
727cb7a01acSMauro Carvalho Chehab 
728d058e237SJavier Martin static struct ov7670_win_size ov7670_win_sizes[] = {
729cb7a01acSMauro Carvalho Chehab 	/* VGA */
730cb7a01acSMauro Carvalho Chehab 	{
731cb7a01acSMauro Carvalho Chehab 		.width		= VGA_WIDTH,
732cb7a01acSMauro Carvalho Chehab 		.height		= VGA_HEIGHT,
733cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_VGA,
734cb7a01acSMauro Carvalho Chehab 		.hstart		= 158,	/* These values from */
735cb7a01acSMauro Carvalho Chehab 		.hstop		=  14,	/* Omnivision */
736cb7a01acSMauro Carvalho Chehab 		.vstart		=  10,
737cb7a01acSMauro Carvalho Chehab 		.vstop		= 490,
738cb7a01acSMauro Carvalho Chehab 		.regs		= NULL,
739cb7a01acSMauro Carvalho Chehab 	},
740cb7a01acSMauro Carvalho Chehab 	/* CIF */
741cb7a01acSMauro Carvalho Chehab 	{
742cb7a01acSMauro Carvalho Chehab 		.width		= CIF_WIDTH,
743cb7a01acSMauro Carvalho Chehab 		.height		= CIF_HEIGHT,
744cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_CIF,
745cb7a01acSMauro Carvalho Chehab 		.hstart		= 170,	/* Empirically determined */
746cb7a01acSMauro Carvalho Chehab 		.hstop		=  90,
747cb7a01acSMauro Carvalho Chehab 		.vstart		=  14,
748cb7a01acSMauro Carvalho Chehab 		.vstop		= 494,
749cb7a01acSMauro Carvalho Chehab 		.regs		= NULL,
750cb7a01acSMauro Carvalho Chehab 	},
751cb7a01acSMauro Carvalho Chehab 	/* QVGA */
752cb7a01acSMauro Carvalho Chehab 	{
753cb7a01acSMauro Carvalho Chehab 		.width		= QVGA_WIDTH,
754cb7a01acSMauro Carvalho Chehab 		.height		= QVGA_HEIGHT,
755cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_QVGA,
756cb7a01acSMauro Carvalho Chehab 		.hstart		= 168,	/* Empirically determined */
757cb7a01acSMauro Carvalho Chehab 		.hstop		=  24,
758cb7a01acSMauro Carvalho Chehab 		.vstart		=  12,
759cb7a01acSMauro Carvalho Chehab 		.vstop		= 492,
760cb7a01acSMauro Carvalho Chehab 		.regs		= NULL,
761cb7a01acSMauro Carvalho Chehab 	},
762cb7a01acSMauro Carvalho Chehab 	/* QCIF */
763cb7a01acSMauro Carvalho Chehab 	{
764cb7a01acSMauro Carvalho Chehab 		.width		= QCIF_WIDTH,
765cb7a01acSMauro Carvalho Chehab 		.height		= QCIF_HEIGHT,
766cb7a01acSMauro Carvalho Chehab 		.com7_bit	= COM7_FMT_VGA, /* see comment above */
767cb7a01acSMauro Carvalho Chehab 		.hstart		= 456,	/* Empirically determined */
768cb7a01acSMauro Carvalho Chehab 		.hstop		=  24,
769cb7a01acSMauro Carvalho Chehab 		.vstart		=  14,
770cb7a01acSMauro Carvalho Chehab 		.vstop		= 494,
771cb7a01acSMauro Carvalho Chehab 		.regs		= ov7670_qcif_regs,
772d058e237SJavier Martin 	}
773cb7a01acSMauro Carvalho Chehab };
774cb7a01acSMauro Carvalho Chehab 
775d058e237SJavier Martin static struct ov7670_win_size ov7675_win_sizes[] = {
776d058e237SJavier Martin 	/*
777d058e237SJavier Martin 	 * Currently, only VGA is supported. Theoretically it could be possible
778d058e237SJavier Martin 	 * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a
779d058e237SJavier Martin 	 * base and tweak them empirically could be required.
780d058e237SJavier Martin 	 */
781d058e237SJavier Martin 	{
782d058e237SJavier Martin 		.width		= VGA_WIDTH,
783d058e237SJavier Martin 		.height		= VGA_HEIGHT,
784d058e237SJavier Martin 		.com7_bit	= COM7_FMT_VGA,
785d058e237SJavier Martin 		.hstart		= 158,	/* These values from */
786d058e237SJavier Martin 		.hstop		=  14,	/* Omnivision */
787d058e237SJavier Martin 		.vstart		=  14,  /* Empirically determined */
788d058e237SJavier Martin 		.vstop		= 494,
789d058e237SJavier Martin 		.regs		= NULL,
790d058e237SJavier Martin 	}
791d058e237SJavier Martin };
792cb7a01acSMauro Carvalho Chehab 
793f6dd927fSJavier Martin static void ov7675_get_framerate(struct v4l2_subdev *sd,
794f6dd927fSJavier Martin 				 struct v4l2_fract *tpf)
795f6dd927fSJavier Martin {
796f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
797f6dd927fSJavier Martin 	u32 clkrc = info->clkrc;
79804ee6d92SJavier Martin 	int pll_factor;
79904ee6d92SJavier Martin 
80004ee6d92SJavier Martin 	if (info->pll_bypass)
80104ee6d92SJavier Martin 		pll_factor = 1;
80204ee6d92SJavier Martin 	else
80304ee6d92SJavier Martin 		pll_factor = PLL_FACTOR;
804f6dd927fSJavier Martin 
805f6dd927fSJavier Martin 	clkrc++;
806f5fe58fdSBoris BREZILLON 	if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
807f6dd927fSJavier Martin 		clkrc = (clkrc >> 1);
808f6dd927fSJavier Martin 
809f6dd927fSJavier Martin 	tpf->numerator = 1;
810f6dd927fSJavier Martin 	tpf->denominator = (5 * pll_factor * info->clock_speed) /
811f6dd927fSJavier Martin 			(4 * clkrc);
812f6dd927fSJavier Martin }
813f6dd927fSJavier Martin 
814f6dd927fSJavier Martin static int ov7675_set_framerate(struct v4l2_subdev *sd,
815f6dd927fSJavier Martin 				 struct v4l2_fract *tpf)
816f6dd927fSJavier Martin {
817f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
818f6dd927fSJavier Martin 	u32 clkrc;
81904ee6d92SJavier Martin 	int pll_factor;
820f6dd927fSJavier Martin 	int ret;
821f6dd927fSJavier Martin 
822f6dd927fSJavier Martin 	/*
823f6dd927fSJavier Martin 	 * The formula is fps = 5/4*pixclk for YUV/RGB and
824f6dd927fSJavier Martin 	 * fps = 5/2*pixclk for RAW.
825f6dd927fSJavier Martin 	 *
826f6dd927fSJavier Martin 	 * pixclk = clock_speed / (clkrc + 1) * PLLfactor
827f6dd927fSJavier Martin 	 *
828f6dd927fSJavier Martin 	 */
82904ee6d92SJavier Martin 	if (info->pll_bypass) {
83004ee6d92SJavier Martin 		pll_factor = 1;
83104ee6d92SJavier Martin 		ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS);
83204ee6d92SJavier Martin 	} else {
83304ee6d92SJavier Martin 		pll_factor = PLL_FACTOR;
83404ee6d92SJavier Martin 		ret = ov7670_write(sd, REG_DBLV, DBLV_X4);
83504ee6d92SJavier Martin 	}
83604ee6d92SJavier Martin 	if (ret < 0)
83704ee6d92SJavier Martin 		return ret;
83804ee6d92SJavier Martin 
839f6dd927fSJavier Martin 	if (tpf->numerator == 0 || tpf->denominator == 0) {
840f6dd927fSJavier Martin 		clkrc = 0;
841f6dd927fSJavier Martin 	} else {
842f6dd927fSJavier Martin 		clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) /
843f6dd927fSJavier Martin 			(4 * tpf->denominator);
844f5fe58fdSBoris BREZILLON 		if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
845f6dd927fSJavier Martin 			clkrc = (clkrc << 1);
846f6dd927fSJavier Martin 		clkrc--;
847f6dd927fSJavier Martin 	}
848f6dd927fSJavier Martin 
849f6dd927fSJavier Martin 	/*
850f6dd927fSJavier Martin 	 * The datasheet claims that clkrc = 0 will divide the input clock by 1
851f6dd927fSJavier Martin 	 * but we've checked with an oscilloscope that it divides by 2 instead.
852f6dd927fSJavier Martin 	 * So, if clkrc = 0 just bypass the divider.
853f6dd927fSJavier Martin 	 */
854f6dd927fSJavier Martin 	if (clkrc <= 0)
855f6dd927fSJavier Martin 		clkrc = CLK_EXT;
856f6dd927fSJavier Martin 	else if (clkrc > CLK_SCALE)
857f6dd927fSJavier Martin 		clkrc = CLK_SCALE;
858f6dd927fSJavier Martin 	info->clkrc = clkrc;
859f6dd927fSJavier Martin 
860f6dd927fSJavier Martin 	/* Recalculate frame rate */
861f6dd927fSJavier Martin 	ov7675_get_framerate(sd, tpf);
862f6dd927fSJavier Martin 
863f1fb0855SSakari Ailus 	return ov7670_write(sd, REG_CLKRC, info->clkrc);
864f6dd927fSJavier Martin }
865f6dd927fSJavier Martin 
866f6dd927fSJavier Martin static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd,
867f6dd927fSJavier Martin 				 struct v4l2_fract *tpf)
868f6dd927fSJavier Martin {
869f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
870f6dd927fSJavier Martin 
871f6dd927fSJavier Martin 	tpf->numerator = 1;
872f6dd927fSJavier Martin 	tpf->denominator = info->clock_speed;
873f6dd927fSJavier Martin 	if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
874f6dd927fSJavier Martin 		tpf->denominator /= (info->clkrc & CLK_SCALE);
875f6dd927fSJavier Martin }
876f6dd927fSJavier Martin 
877f6dd927fSJavier Martin static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd,
878f6dd927fSJavier Martin 					struct v4l2_fract *tpf)
879f6dd927fSJavier Martin {
880f6dd927fSJavier Martin 	struct ov7670_info *info = to_state(sd);
881f6dd927fSJavier Martin 	int div;
882f6dd927fSJavier Martin 
883f6dd927fSJavier Martin 	if (tpf->numerator == 0 || tpf->denominator == 0)
884f6dd927fSJavier Martin 		div = 1;  /* Reset to full rate */
885f6dd927fSJavier Martin 	else
886f6dd927fSJavier Martin 		div = (tpf->numerator * info->clock_speed) / tpf->denominator;
887f6dd927fSJavier Martin 	if (div == 0)
888f6dd927fSJavier Martin 		div = 1;
889f6dd927fSJavier Martin 	else if (div > CLK_SCALE)
890f6dd927fSJavier Martin 		div = CLK_SCALE;
891f6dd927fSJavier Martin 	info->clkrc = (info->clkrc & 0x80) | div;
892f6dd927fSJavier Martin 	tpf->numerator = 1;
893f6dd927fSJavier Martin 	tpf->denominator = info->clock_speed / div;
894f6dd927fSJavier Martin 	return ov7670_write(sd, REG_CLKRC, info->clkrc);
895f6dd927fSJavier Martin }
896f6dd927fSJavier Martin 
897cb7a01acSMauro Carvalho Chehab /*
898cb7a01acSMauro Carvalho Chehab  * Store a set of start/stop values into the camera.
899cb7a01acSMauro Carvalho Chehab  */
900cb7a01acSMauro Carvalho Chehab static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
901cb7a01acSMauro Carvalho Chehab 		int vstart, int vstop)
902cb7a01acSMauro Carvalho Chehab {
903cb7a01acSMauro Carvalho Chehab 	int ret;
904cb7a01acSMauro Carvalho Chehab 	unsigned char v;
905cb7a01acSMauro Carvalho Chehab /*
906cb7a01acSMauro Carvalho Chehab  * Horizontal: 11 bits, top 8 live in hstart and hstop.  Bottom 3 of
907cb7a01acSMauro Carvalho Chehab  * hstart are in href[2:0], bottom 3 of hstop in href[5:3].  There is
908cb7a01acSMauro Carvalho Chehab  * a mystery "edge offset" value in the top two bits of href.
909cb7a01acSMauro Carvalho Chehab  */
910cb7a01acSMauro Carvalho Chehab 	ret =  ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
911cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
912cb7a01acSMauro Carvalho Chehab 	ret += ov7670_read(sd, REG_HREF, &v);
913cb7a01acSMauro Carvalho Chehab 	v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
914cb7a01acSMauro Carvalho Chehab 	msleep(10);
915cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_HREF, v);
916cb7a01acSMauro Carvalho Chehab /*
917cb7a01acSMauro Carvalho Chehab  * Vertical: similar arrangement, but only 10 bits.
918cb7a01acSMauro Carvalho Chehab  */
919cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
920cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
921cb7a01acSMauro Carvalho Chehab 	ret += ov7670_read(sd, REG_VREF, &v);
922cb7a01acSMauro Carvalho Chehab 	v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
923cb7a01acSMauro Carvalho Chehab 	msleep(10);
924cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_VREF, v);
925cb7a01acSMauro Carvalho Chehab 	return ret;
926cb7a01acSMauro Carvalho Chehab }
927cb7a01acSMauro Carvalho Chehab 
928cb7a01acSMauro Carvalho Chehab 
929ebcff5fcSHans Verkuil static int ov7670_enum_mbus_code(struct v4l2_subdev *sd,
930ebcff5fcSHans Verkuil 		struct v4l2_subdev_pad_config *cfg,
931ebcff5fcSHans Verkuil 		struct v4l2_subdev_mbus_code_enum *code)
932cb7a01acSMauro Carvalho Chehab {
933ebcff5fcSHans Verkuil 	if (code->pad || code->index >= N_OV7670_FMTS)
934cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
935cb7a01acSMauro Carvalho Chehab 
936ebcff5fcSHans Verkuil 	code->code = ov7670_formats[code->index].mbus_code;
937cb7a01acSMauro Carvalho Chehab 	return 0;
938cb7a01acSMauro Carvalho Chehab }
939cb7a01acSMauro Carvalho Chehab 
940cb7a01acSMauro Carvalho Chehab static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
941cb7a01acSMauro Carvalho Chehab 		struct v4l2_mbus_framefmt *fmt,
942cb7a01acSMauro Carvalho Chehab 		struct ov7670_format_struct **ret_fmt,
943cb7a01acSMauro Carvalho Chehab 		struct ov7670_win_size **ret_wsize)
944cb7a01acSMauro Carvalho Chehab {
945f748cd3eSJavier Martin 	int index, i;
946cb7a01acSMauro Carvalho Chehab 	struct ov7670_win_size *wsize;
947d058e237SJavier Martin 	struct ov7670_info *info = to_state(sd);
948d058e237SJavier Martin 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
949f748cd3eSJavier Martin 	unsigned int win_sizes_limit = n_win_sizes;
950cb7a01acSMauro Carvalho Chehab 
951cb7a01acSMauro Carvalho Chehab 	for (index = 0; index < N_OV7670_FMTS; index++)
952cb7a01acSMauro Carvalho Chehab 		if (ov7670_formats[index].mbus_code == fmt->code)
953cb7a01acSMauro Carvalho Chehab 			break;
954cb7a01acSMauro Carvalho Chehab 	if (index >= N_OV7670_FMTS) {
955cb7a01acSMauro Carvalho Chehab 		/* default to first format */
956cb7a01acSMauro Carvalho Chehab 		index = 0;
957cb7a01acSMauro Carvalho Chehab 		fmt->code = ov7670_formats[0].mbus_code;
958cb7a01acSMauro Carvalho Chehab 	}
959cb7a01acSMauro Carvalho Chehab 	if (ret_fmt != NULL)
960cb7a01acSMauro Carvalho Chehab 		*ret_fmt = ov7670_formats + index;
961cb7a01acSMauro Carvalho Chehab 	/*
962cb7a01acSMauro Carvalho Chehab 	 * Fields: the OV devices claim to be progressive.
963cb7a01acSMauro Carvalho Chehab 	 */
964cb7a01acSMauro Carvalho Chehab 	fmt->field = V4L2_FIELD_NONE;
965f748cd3eSJavier Martin 
966f748cd3eSJavier Martin 	/*
967f748cd3eSJavier Martin 	 * Don't consider values that don't match min_height and min_width
968f748cd3eSJavier Martin 	 * constraints.
969f748cd3eSJavier Martin 	 */
970f748cd3eSJavier Martin 	if (info->min_width || info->min_height)
971f748cd3eSJavier Martin 		for (i = 0; i < n_win_sizes; i++) {
972f748cd3eSJavier Martin 			wsize = info->devtype->win_sizes + i;
973f748cd3eSJavier Martin 
974f748cd3eSJavier Martin 			if (wsize->width < info->min_width ||
975f748cd3eSJavier Martin 				wsize->height < info->min_height) {
976f748cd3eSJavier Martin 				win_sizes_limit = i;
977f748cd3eSJavier Martin 				break;
978f748cd3eSJavier Martin 			}
979f748cd3eSJavier Martin 		}
980cb7a01acSMauro Carvalho Chehab 	/*
981cb7a01acSMauro Carvalho Chehab 	 * Round requested image size down to the nearest
982cb7a01acSMauro Carvalho Chehab 	 * we support, but not below the smallest.
983cb7a01acSMauro Carvalho Chehab 	 */
984d058e237SJavier Martin 	for (wsize = info->devtype->win_sizes;
985f748cd3eSJavier Martin 	     wsize < info->devtype->win_sizes + win_sizes_limit; wsize++)
986cb7a01acSMauro Carvalho Chehab 		if (fmt->width >= wsize->width && fmt->height >= wsize->height)
987cb7a01acSMauro Carvalho Chehab 			break;
988f748cd3eSJavier Martin 	if (wsize >= info->devtype->win_sizes + win_sizes_limit)
989cb7a01acSMauro Carvalho Chehab 		wsize--;   /* Take the smallest one */
990cb7a01acSMauro Carvalho Chehab 	if (ret_wsize != NULL)
991cb7a01acSMauro Carvalho Chehab 		*ret_wsize = wsize;
992cb7a01acSMauro Carvalho Chehab 	/*
993cb7a01acSMauro Carvalho Chehab 	 * Note the size we'll actually handle.
994cb7a01acSMauro Carvalho Chehab 	 */
995cb7a01acSMauro Carvalho Chehab 	fmt->width = wsize->width;
996cb7a01acSMauro Carvalho Chehab 	fmt->height = wsize->height;
997cb7a01acSMauro Carvalho Chehab 	fmt->colorspace = ov7670_formats[index].colorspace;
998c0662dd4SWenyou Yang 
999c0662dd4SWenyou Yang 	info->format = *fmt;
1000c0662dd4SWenyou Yang 
1001cb7a01acSMauro Carvalho Chehab 	return 0;
1002cb7a01acSMauro Carvalho Chehab }
1003cb7a01acSMauro Carvalho Chehab 
10045556ab2aSLubomir Rintel static int ov7670_apply_fmt(struct v4l2_subdev *sd)
1005cb7a01acSMauro Carvalho Chehab {
1006cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
10075556ab2aSLubomir Rintel 	struct ov7670_win_size *wsize = info->wsize;
100801b84448SJacopo Mondi 	unsigned char com7, com10 = 0;
1009cb7a01acSMauro Carvalho Chehab 	int ret;
1010cb7a01acSMauro Carvalho Chehab 
1011cb7a01acSMauro Carvalho Chehab 	/*
1012cb7a01acSMauro Carvalho Chehab 	 * COM7 is a pain in the ass, it doesn't like to be read then
1013cb7a01acSMauro Carvalho Chehab 	 * quickly written afterward.  But we have everything we need
1014cb7a01acSMauro Carvalho Chehab 	 * to set it absolutely here, as long as the format-specific
1015cb7a01acSMauro Carvalho Chehab 	 * register sets list it first.
1016cb7a01acSMauro Carvalho Chehab 	 */
10175556ab2aSLubomir Rintel 	com7 = info->fmt->regs[0].value;
1018cb7a01acSMauro Carvalho Chehab 	com7 |= wsize->com7_bit;
101901b84448SJacopo Mondi 	ret = ov7670_write(sd, REG_COM7, com7);
102001b84448SJacopo Mondi 	if (ret)
102101b84448SJacopo Mondi 		return ret;
102201b84448SJacopo Mondi 
102301b84448SJacopo Mondi 	/*
102401b84448SJacopo Mondi 	 * Configure the media bus through COM10 register
102501b84448SJacopo Mondi 	 */
102601b84448SJacopo Mondi 	if (info->mbus_config & V4L2_MBUS_VSYNC_ACTIVE_LOW)
102701b84448SJacopo Mondi 		com10 |= COM10_VS_NEG;
102801b84448SJacopo Mondi 	if (info->mbus_config & V4L2_MBUS_HSYNC_ACTIVE_LOW)
102901b84448SJacopo Mondi 		com10 |= COM10_HREF_REV;
103001b84448SJacopo Mondi 	if (info->pclk_hb_disable)
103101b84448SJacopo Mondi 		com10 |= COM10_PCLK_HB;
103201b84448SJacopo Mondi 	ret = ov7670_write(sd, REG_COM10, com10);
103301b84448SJacopo Mondi 	if (ret)
103401b84448SJacopo Mondi 		return ret;
103501b84448SJacopo Mondi 
1036cb7a01acSMauro Carvalho Chehab 	/*
1037cb7a01acSMauro Carvalho Chehab 	 * Now write the rest of the array.  Also store start/stops
1038cb7a01acSMauro Carvalho Chehab 	 */
10395556ab2aSLubomir Rintel 	ret = ov7670_write_array(sd, info->fmt->regs + 1);
104001b84448SJacopo Mondi 	if (ret)
104101b84448SJacopo Mondi 		return ret;
104201b84448SJacopo Mondi 
104301b84448SJacopo Mondi 	ret = ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
1044cb7a01acSMauro Carvalho Chehab 			    wsize->vstop);
104501b84448SJacopo Mondi 	if (ret)
104601b84448SJacopo Mondi 		return ret;
104701b84448SJacopo Mondi 
104801b84448SJacopo Mondi 	if (wsize->regs) {
1049cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write_array(sd, wsize->regs);
105001b84448SJacopo Mondi 		if (ret)
105101b84448SJacopo Mondi 			return ret;
105201b84448SJacopo Mondi 	}
105301b84448SJacopo Mondi 
1054cb7a01acSMauro Carvalho Chehab 	/*
1055cb7a01acSMauro Carvalho Chehab 	 * If we're running RGB565, we must rewrite clkrc after setting
1056cb7a01acSMauro Carvalho Chehab 	 * the other parameters or the image looks poor.  If we're *not*
1057cb7a01acSMauro Carvalho Chehab 	 * doing RGB565, we must not rewrite clkrc or the image looks
1058cb7a01acSMauro Carvalho Chehab 	 * *really* poor.
1059cb7a01acSMauro Carvalho Chehab 	 *
1060cb7a01acSMauro Carvalho Chehab 	 * (Update) Now that we retain clkrc state, we should be able
1061cb7a01acSMauro Carvalho Chehab 	 * to write it unconditionally, and that will make the frame
1062cb7a01acSMauro Carvalho Chehab 	 * rate persistent too.
1063cb7a01acSMauro Carvalho Chehab 	 */
1064cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
106501b84448SJacopo Mondi 	if (ret)
106601b84448SJacopo Mondi 		return ret;
106701b84448SJacopo Mondi 
1068cb7a01acSMauro Carvalho Chehab 	return 0;
1069cb7a01acSMauro Carvalho Chehab }
1070cb7a01acSMauro Carvalho Chehab 
10715556ab2aSLubomir Rintel /*
10725556ab2aSLubomir Rintel  * Set a format.
10735556ab2aSLubomir Rintel  */
10745556ab2aSLubomir Rintel static int ov7670_set_fmt(struct v4l2_subdev *sd,
10755556ab2aSLubomir Rintel 		struct v4l2_subdev_pad_config *cfg,
10765556ab2aSLubomir Rintel 		struct v4l2_subdev_format *format)
10775556ab2aSLubomir Rintel {
10785556ab2aSLubomir Rintel 	struct ov7670_info *info = to_state(sd);
10795556ab2aSLubomir Rintel #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
10805556ab2aSLubomir Rintel 	struct v4l2_mbus_framefmt *mbus_fmt;
10815556ab2aSLubomir Rintel #endif
10825556ab2aSLubomir Rintel 	int ret;
10835556ab2aSLubomir Rintel 
10845556ab2aSLubomir Rintel 	if (format->pad)
10855556ab2aSLubomir Rintel 		return -EINVAL;
10865556ab2aSLubomir Rintel 
10875556ab2aSLubomir Rintel 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
10885556ab2aSLubomir Rintel 		ret = ov7670_try_fmt_internal(sd, &format->format, NULL, NULL);
10895556ab2aSLubomir Rintel 		if (ret)
10905556ab2aSLubomir Rintel 			return ret;
10915556ab2aSLubomir Rintel #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
10925556ab2aSLubomir Rintel 		mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
10935556ab2aSLubomir Rintel 		*mbus_fmt = format->format;
10945556ab2aSLubomir Rintel 		return 0;
10955556ab2aSLubomir Rintel #else
10965556ab2aSLubomir Rintel 		return -ENOTTY;
10975556ab2aSLubomir Rintel #endif
10985556ab2aSLubomir Rintel 	}
10995556ab2aSLubomir Rintel 
11005556ab2aSLubomir Rintel 	ret = ov7670_try_fmt_internal(sd, &format->format, &info->fmt, &info->wsize);
11015556ab2aSLubomir Rintel 	if (ret)
11025556ab2aSLubomir Rintel 		return ret;
11035556ab2aSLubomir Rintel 
11045556ab2aSLubomir Rintel 	ret = ov7670_apply_fmt(sd);
11055556ab2aSLubomir Rintel 	if (ret)
11065556ab2aSLubomir Rintel 		return ret;
11075556ab2aSLubomir Rintel 
11085556ab2aSLubomir Rintel 	return 0;
11095556ab2aSLubomir Rintel }
11105556ab2aSLubomir Rintel 
1111c0662dd4SWenyou Yang static int ov7670_get_fmt(struct v4l2_subdev *sd,
1112c0662dd4SWenyou Yang 			  struct v4l2_subdev_pad_config *cfg,
1113c0662dd4SWenyou Yang 			  struct v4l2_subdev_format *format)
1114c0662dd4SWenyou Yang {
1115c0662dd4SWenyou Yang 	struct ov7670_info *info = to_state(sd);
1116c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1117c0662dd4SWenyou Yang 	struct v4l2_mbus_framefmt *mbus_fmt;
1118c0662dd4SWenyou Yang #endif
1119c0662dd4SWenyou Yang 
1120c0662dd4SWenyou Yang 	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
1121c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1122c0662dd4SWenyou Yang 		mbus_fmt = v4l2_subdev_get_try_format(sd, cfg, 0);
1123c0662dd4SWenyou Yang 		format->format = *mbus_fmt;
1124c0662dd4SWenyou Yang 		return 0;
1125c0662dd4SWenyou Yang #else
1126c0662dd4SWenyou Yang 		return -ENOTTY;
1127c0662dd4SWenyou Yang #endif
1128c0662dd4SWenyou Yang 	} else {
1129c0662dd4SWenyou Yang 		format->format = info->format;
1130c0662dd4SWenyou Yang 	}
1131c0662dd4SWenyou Yang 
1132c0662dd4SWenyou Yang 	return 0;
1133c0662dd4SWenyou Yang }
1134c0662dd4SWenyou Yang 
1135cb7a01acSMauro Carvalho Chehab /*
1136cb7a01acSMauro Carvalho Chehab  * Implement G/S_PARM.  There is a "high quality" mode we could try
1137cb7a01acSMauro Carvalho Chehab  * to do someday; for now, we just do the frame rate tweak.
1138cb7a01acSMauro Carvalho Chehab  */
11394471109eSHans Verkuil static int ov7670_g_frame_interval(struct v4l2_subdev *sd,
11404471109eSHans Verkuil 				   struct v4l2_subdev_frame_interval *ival)
1141cb7a01acSMauro Carvalho Chehab {
1142cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1143cb7a01acSMauro Carvalho Chehab 
1144cb7a01acSMauro Carvalho Chehab 
11454471109eSHans Verkuil 	info->devtype->get_framerate(sd, &ival->interval);
1146f6dd927fSJavier Martin 
1147cb7a01acSMauro Carvalho Chehab 	return 0;
1148cb7a01acSMauro Carvalho Chehab }
1149cb7a01acSMauro Carvalho Chehab 
11504471109eSHans Verkuil static int ov7670_s_frame_interval(struct v4l2_subdev *sd,
11514471109eSHans Verkuil 				   struct v4l2_subdev_frame_interval *ival)
1152cb7a01acSMauro Carvalho Chehab {
11534471109eSHans Verkuil 	struct v4l2_fract *tpf = &ival->interval;
1154cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1155cb7a01acSMauro Carvalho Chehab 
1156cb7a01acSMauro Carvalho Chehab 
1157f6dd927fSJavier Martin 	return info->devtype->set_framerate(sd, tpf);
1158cb7a01acSMauro Carvalho Chehab }
1159cb7a01acSMauro Carvalho Chehab 
1160cb7a01acSMauro Carvalho Chehab 
1161cb7a01acSMauro Carvalho Chehab /*
1162cb7a01acSMauro Carvalho Chehab  * Frame intervals.  Since frame rates are controlled with the clock
1163cb7a01acSMauro Carvalho Chehab  * divider, we can only do 30/n for integer n values.  So no continuous
1164cb7a01acSMauro Carvalho Chehab  * or stepwise options.  Here we just pick a handful of logical values.
1165cb7a01acSMauro Carvalho Chehab  */
1166cb7a01acSMauro Carvalho Chehab 
1167cb7a01acSMauro Carvalho Chehab static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
1168cb7a01acSMauro Carvalho Chehab 
116917bef885SHans Verkuil static int ov7670_enum_frame_interval(struct v4l2_subdev *sd,
117017bef885SHans Verkuil 				      struct v4l2_subdev_pad_config *cfg,
117117bef885SHans Verkuil 				      struct v4l2_subdev_frame_interval_enum *fie)
1172cb7a01acSMauro Carvalho Chehab {
1173b8cc79fdSHans Verkuil 	struct ov7670_info *info = to_state(sd);
1174b8cc79fdSHans Verkuil 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
1175b8cc79fdSHans Verkuil 	int i;
1176b8cc79fdSHans Verkuil 
117717bef885SHans Verkuil 	if (fie->pad)
1178cb7a01acSMauro Carvalho Chehab 		return -EINVAL;
117917bef885SHans Verkuil 	if (fie->index >= ARRAY_SIZE(ov7670_frame_rates))
118017bef885SHans Verkuil 		return -EINVAL;
1181b8cc79fdSHans Verkuil 
1182b8cc79fdSHans Verkuil 	/*
1183b8cc79fdSHans Verkuil 	 * Check if the width/height is valid.
1184b8cc79fdSHans Verkuil 	 *
1185b8cc79fdSHans Verkuil 	 * If a minimum width/height was requested, filter out the capture
1186b8cc79fdSHans Verkuil 	 * windows that fall outside that.
1187b8cc79fdSHans Verkuil 	 */
1188b8cc79fdSHans Verkuil 	for (i = 0; i < n_win_sizes; i++) {
1189b8cc79fdSHans Verkuil 		struct ov7670_win_size *win = &info->devtype->win_sizes[i];
1190b8cc79fdSHans Verkuil 
1191b8cc79fdSHans Verkuil 		if (info->min_width && win->width < info->min_width)
1192b8cc79fdSHans Verkuil 			continue;
1193b8cc79fdSHans Verkuil 		if (info->min_height && win->height < info->min_height)
1194b8cc79fdSHans Verkuil 			continue;
1195b8cc79fdSHans Verkuil 		if (fie->width == win->width && fie->height == win->height)
1196b8cc79fdSHans Verkuil 			break;
1197b8cc79fdSHans Verkuil 	}
1198b8cc79fdSHans Verkuil 	if (i == n_win_sizes)
1199b8cc79fdSHans Verkuil 		return -EINVAL;
120017bef885SHans Verkuil 	fie->interval.numerator = 1;
120117bef885SHans Verkuil 	fie->interval.denominator = ov7670_frame_rates[fie->index];
1202cb7a01acSMauro Carvalho Chehab 	return 0;
1203cb7a01acSMauro Carvalho Chehab }
1204cb7a01acSMauro Carvalho Chehab 
1205cb7a01acSMauro Carvalho Chehab /*
1206cb7a01acSMauro Carvalho Chehab  * Frame size enumeration
1207cb7a01acSMauro Carvalho Chehab  */
120817bef885SHans Verkuil static int ov7670_enum_frame_size(struct v4l2_subdev *sd,
120917bef885SHans Verkuil 				  struct v4l2_subdev_pad_config *cfg,
121017bef885SHans Verkuil 				  struct v4l2_subdev_frame_size_enum *fse)
1211cb7a01acSMauro Carvalho Chehab {
1212cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1213cb7a01acSMauro Carvalho Chehab 	int i;
1214cb7a01acSMauro Carvalho Chehab 	int num_valid = -1;
121517bef885SHans Verkuil 	__u32 index = fse->index;
1216d058e237SJavier Martin 	unsigned int n_win_sizes = info->devtype->n_win_sizes;
1217cb7a01acSMauro Carvalho Chehab 
121817bef885SHans Verkuil 	if (fse->pad)
121917bef885SHans Verkuil 		return -EINVAL;
122017bef885SHans Verkuil 
1221cb7a01acSMauro Carvalho Chehab 	/*
1222cb7a01acSMauro Carvalho Chehab 	 * If a minimum width/height was requested, filter out the capture
1223cb7a01acSMauro Carvalho Chehab 	 * windows that fall outside that.
1224cb7a01acSMauro Carvalho Chehab 	 */
1225d058e237SJavier Martin 	for (i = 0; i < n_win_sizes; i++) {
1226322e6d19SGuennadi Liakhovetski 		struct ov7670_win_size *win = &info->devtype->win_sizes[i];
1227cb7a01acSMauro Carvalho Chehab 		if (info->min_width && win->width < info->min_width)
1228cb7a01acSMauro Carvalho Chehab 			continue;
1229cb7a01acSMauro Carvalho Chehab 		if (info->min_height && win->height < info->min_height)
1230cb7a01acSMauro Carvalho Chehab 			continue;
1231cb7a01acSMauro Carvalho Chehab 		if (index == ++num_valid) {
123217bef885SHans Verkuil 			fse->min_width = fse->max_width = win->width;
123317bef885SHans Verkuil 			fse->min_height = fse->max_height = win->height;
1234cb7a01acSMauro Carvalho Chehab 			return 0;
1235cb7a01acSMauro Carvalho Chehab 		}
1236cb7a01acSMauro Carvalho Chehab 	}
1237cb7a01acSMauro Carvalho Chehab 
1238cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1239cb7a01acSMauro Carvalho Chehab }
1240cb7a01acSMauro Carvalho Chehab 
1241cb7a01acSMauro Carvalho Chehab /*
1242cb7a01acSMauro Carvalho Chehab  * Code for dealing with controls.
1243cb7a01acSMauro Carvalho Chehab  */
1244cb7a01acSMauro Carvalho Chehab 
1245cb7a01acSMauro Carvalho Chehab static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
1246cb7a01acSMauro Carvalho Chehab 		int matrix[CMATRIX_LEN])
1247cb7a01acSMauro Carvalho Chehab {
1248cb7a01acSMauro Carvalho Chehab 	int i, ret;
1249cb7a01acSMauro Carvalho Chehab 	unsigned char signbits = 0;
1250cb7a01acSMauro Carvalho Chehab 
1251cb7a01acSMauro Carvalho Chehab 	/*
1252cb7a01acSMauro Carvalho Chehab 	 * Weird crap seems to exist in the upper part of
1253cb7a01acSMauro Carvalho Chehab 	 * the sign bits register, so let's preserve it.
1254cb7a01acSMauro Carvalho Chehab 	 */
1255cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
1256cb7a01acSMauro Carvalho Chehab 	signbits &= 0xc0;
1257cb7a01acSMauro Carvalho Chehab 
1258cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < CMATRIX_LEN; i++) {
1259cb7a01acSMauro Carvalho Chehab 		unsigned char raw;
1260cb7a01acSMauro Carvalho Chehab 
1261cb7a01acSMauro Carvalho Chehab 		if (matrix[i] < 0) {
1262cb7a01acSMauro Carvalho Chehab 			signbits |= (1 << i);
1263cb7a01acSMauro Carvalho Chehab 			if (matrix[i] < -255)
1264cb7a01acSMauro Carvalho Chehab 				raw = 0xff;
1265cb7a01acSMauro Carvalho Chehab 			else
1266cb7a01acSMauro Carvalho Chehab 				raw = (-1 * matrix[i]) & 0xff;
1267cb7a01acSMauro Carvalho Chehab 		}
1268cb7a01acSMauro Carvalho Chehab 		else {
1269cb7a01acSMauro Carvalho Chehab 			if (matrix[i] > 255)
1270cb7a01acSMauro Carvalho Chehab 				raw = 0xff;
1271cb7a01acSMauro Carvalho Chehab 			else
1272cb7a01acSMauro Carvalho Chehab 				raw = matrix[i] & 0xff;
1273cb7a01acSMauro Carvalho Chehab 		}
1274cb7a01acSMauro Carvalho Chehab 		ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
1275cb7a01acSMauro Carvalho Chehab 	}
1276cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
1277cb7a01acSMauro Carvalho Chehab 	return ret;
1278cb7a01acSMauro Carvalho Chehab }
1279cb7a01acSMauro Carvalho Chehab 
1280cb7a01acSMauro Carvalho Chehab 
1281cb7a01acSMauro Carvalho Chehab /*
1282cb7a01acSMauro Carvalho Chehab  * Hue also requires messing with the color matrix.  It also requires
1283cb7a01acSMauro Carvalho Chehab  * trig functions, which tend not to be well supported in the kernel.
1284cb7a01acSMauro Carvalho Chehab  * So here is a simple table of sine values, 0-90 degrees, in steps
1285cb7a01acSMauro Carvalho Chehab  * of five degrees.  Values are multiplied by 1000.
1286cb7a01acSMauro Carvalho Chehab  *
1287cb7a01acSMauro Carvalho Chehab  * The following naive approximate trig functions require an argument
1288cb7a01acSMauro Carvalho Chehab  * carefully limited to -180 <= theta <= 180.
1289cb7a01acSMauro Carvalho Chehab  */
1290cb7a01acSMauro Carvalho Chehab #define SIN_STEP 5
1291cb7a01acSMauro Carvalho Chehab static const int ov7670_sin_table[] = {
1292cb7a01acSMauro Carvalho Chehab 	   0,	 87,   173,   258,   342,   422,
1293cb7a01acSMauro Carvalho Chehab 	 499,	573,   642,   707,   766,   819,
1294cb7a01acSMauro Carvalho Chehab 	 866,	906,   939,   965,   984,   996,
1295cb7a01acSMauro Carvalho Chehab 	1000
1296cb7a01acSMauro Carvalho Chehab };
1297cb7a01acSMauro Carvalho Chehab 
1298cb7a01acSMauro Carvalho Chehab static int ov7670_sine(int theta)
1299cb7a01acSMauro Carvalho Chehab {
1300cb7a01acSMauro Carvalho Chehab 	int chs = 1;
1301cb7a01acSMauro Carvalho Chehab 	int sine;
1302cb7a01acSMauro Carvalho Chehab 
1303cb7a01acSMauro Carvalho Chehab 	if (theta < 0) {
1304cb7a01acSMauro Carvalho Chehab 		theta = -theta;
1305cb7a01acSMauro Carvalho Chehab 		chs = -1;
1306cb7a01acSMauro Carvalho Chehab 	}
1307cb7a01acSMauro Carvalho Chehab 	if (theta <= 90)
1308cb7a01acSMauro Carvalho Chehab 		sine = ov7670_sin_table[theta/SIN_STEP];
1309cb7a01acSMauro Carvalho Chehab 	else {
1310cb7a01acSMauro Carvalho Chehab 		theta -= 90;
1311cb7a01acSMauro Carvalho Chehab 		sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
1312cb7a01acSMauro Carvalho Chehab 	}
1313cb7a01acSMauro Carvalho Chehab 	return sine*chs;
1314cb7a01acSMauro Carvalho Chehab }
1315cb7a01acSMauro Carvalho Chehab 
1316cb7a01acSMauro Carvalho Chehab static int ov7670_cosine(int theta)
1317cb7a01acSMauro Carvalho Chehab {
1318cb7a01acSMauro Carvalho Chehab 	theta = 90 - theta;
1319cb7a01acSMauro Carvalho Chehab 	if (theta > 180)
1320cb7a01acSMauro Carvalho Chehab 		theta -= 360;
1321cb7a01acSMauro Carvalho Chehab 	else if (theta < -180)
1322cb7a01acSMauro Carvalho Chehab 		theta += 360;
1323cb7a01acSMauro Carvalho Chehab 	return ov7670_sine(theta);
1324cb7a01acSMauro Carvalho Chehab }
1325cb7a01acSMauro Carvalho Chehab 
1326cb7a01acSMauro Carvalho Chehab 
1327cb7a01acSMauro Carvalho Chehab 
1328cb7a01acSMauro Carvalho Chehab 
1329cb7a01acSMauro Carvalho Chehab static void ov7670_calc_cmatrix(struct ov7670_info *info,
1330492959c7SJavier Martin 		int matrix[CMATRIX_LEN], int sat, int hue)
1331cb7a01acSMauro Carvalho Chehab {
1332cb7a01acSMauro Carvalho Chehab 	int i;
1333cb7a01acSMauro Carvalho Chehab 	/*
1334cb7a01acSMauro Carvalho Chehab 	 * Apply the current saturation setting first.
1335cb7a01acSMauro Carvalho Chehab 	 */
1336cb7a01acSMauro Carvalho Chehab 	for (i = 0; i < CMATRIX_LEN; i++)
1337492959c7SJavier Martin 		matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7;
1338cb7a01acSMauro Carvalho Chehab 	/*
1339cb7a01acSMauro Carvalho Chehab 	 * Then, if need be, rotate the hue value.
1340cb7a01acSMauro Carvalho Chehab 	 */
1341492959c7SJavier Martin 	if (hue != 0) {
1342cb7a01acSMauro Carvalho Chehab 		int sinth, costh, tmpmatrix[CMATRIX_LEN];
1343cb7a01acSMauro Carvalho Chehab 
1344cb7a01acSMauro Carvalho Chehab 		memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
1345492959c7SJavier Martin 		sinth = ov7670_sine(hue);
1346492959c7SJavier Martin 		costh = ov7670_cosine(hue);
1347cb7a01acSMauro Carvalho Chehab 
1348cb7a01acSMauro Carvalho Chehab 		matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
1349cb7a01acSMauro Carvalho Chehab 		matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
1350cb7a01acSMauro Carvalho Chehab 		matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
1351cb7a01acSMauro Carvalho Chehab 		matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
1352cb7a01acSMauro Carvalho Chehab 		matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
1353cb7a01acSMauro Carvalho Chehab 		matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
1354cb7a01acSMauro Carvalho Chehab 	}
1355cb7a01acSMauro Carvalho Chehab }
1356cb7a01acSMauro Carvalho Chehab 
1357cb7a01acSMauro Carvalho Chehab 
1358cb7a01acSMauro Carvalho Chehab 
1359492959c7SJavier Martin static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue)
1360cb7a01acSMauro Carvalho Chehab {
1361cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info = to_state(sd);
1362cb7a01acSMauro Carvalho Chehab 	int matrix[CMATRIX_LEN];
1363cb7a01acSMauro Carvalho Chehab 	int ret;
1364cb7a01acSMauro Carvalho Chehab 
1365492959c7SJavier Martin 	ov7670_calc_cmatrix(info, matrix, sat, hue);
1366cb7a01acSMauro Carvalho Chehab 	ret = ov7670_store_cmatrix(sd, matrix);
1367cb7a01acSMauro Carvalho Chehab 	return ret;
1368cb7a01acSMauro Carvalho Chehab }
1369cb7a01acSMauro Carvalho Chehab 
1370cb7a01acSMauro Carvalho Chehab 
1371cb7a01acSMauro Carvalho Chehab /*
1372cb7a01acSMauro Carvalho Chehab  * Some weird registers seem to store values in a sign/magnitude format!
1373cb7a01acSMauro Carvalho Chehab  */
1374cb7a01acSMauro Carvalho Chehab 
1375cb7a01acSMauro Carvalho Chehab static unsigned char ov7670_abs_to_sm(unsigned char v)
1376cb7a01acSMauro Carvalho Chehab {
1377cb7a01acSMauro Carvalho Chehab 	if (v > 127)
1378cb7a01acSMauro Carvalho Chehab 		return v & 0x7f;
1379cb7a01acSMauro Carvalho Chehab 	return (128 - v) | 0x80;
1380cb7a01acSMauro Carvalho Chehab }
1381cb7a01acSMauro Carvalho Chehab 
1382cb7a01acSMauro Carvalho Chehab static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
1383cb7a01acSMauro Carvalho Chehab {
1384cb7a01acSMauro Carvalho Chehab 	unsigned char com8 = 0, v;
1385cb7a01acSMauro Carvalho Chehab 	int ret;
1386cb7a01acSMauro Carvalho Chehab 
1387cb7a01acSMauro Carvalho Chehab 	ov7670_read(sd, REG_COM8, &com8);
1388cb7a01acSMauro Carvalho Chehab 	com8 &= ~COM8_AEC;
1389cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, REG_COM8, com8);
1390cb7a01acSMauro Carvalho Chehab 	v = ov7670_abs_to_sm(value);
1391cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_BRIGHT, v);
1392cb7a01acSMauro Carvalho Chehab 	return ret;
1393cb7a01acSMauro Carvalho Chehab }
1394cb7a01acSMauro Carvalho Chehab 
1395cb7a01acSMauro Carvalho Chehab static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
1396cb7a01acSMauro Carvalho Chehab {
1397cb7a01acSMauro Carvalho Chehab 	return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
1398cb7a01acSMauro Carvalho Chehab }
1399cb7a01acSMauro Carvalho Chehab 
1400cb7a01acSMauro Carvalho Chehab static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
1401cb7a01acSMauro Carvalho Chehab {
1402cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1403cb7a01acSMauro Carvalho Chehab 	int ret;
1404cb7a01acSMauro Carvalho Chehab 
1405cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
1406cb7a01acSMauro Carvalho Chehab 	if (value)
1407cb7a01acSMauro Carvalho Chehab 		v |= MVFP_MIRROR;
1408cb7a01acSMauro Carvalho Chehab 	else
1409cb7a01acSMauro Carvalho Chehab 		v &= ~MVFP_MIRROR;
1410cb7a01acSMauro Carvalho Chehab 	msleep(10);  /* FIXME */
1411cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_MVFP, v);
1412cb7a01acSMauro Carvalho Chehab 	return ret;
1413cb7a01acSMauro Carvalho Chehab }
1414cb7a01acSMauro Carvalho Chehab 
1415cb7a01acSMauro Carvalho Chehab static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
1416cb7a01acSMauro Carvalho Chehab {
1417cb7a01acSMauro Carvalho Chehab 	unsigned char v = 0;
1418cb7a01acSMauro Carvalho Chehab 	int ret;
1419cb7a01acSMauro Carvalho Chehab 
1420cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_MVFP, &v);
1421cb7a01acSMauro Carvalho Chehab 	if (value)
1422cb7a01acSMauro Carvalho Chehab 		v |= MVFP_FLIP;
1423cb7a01acSMauro Carvalho Chehab 	else
1424cb7a01acSMauro Carvalho Chehab 		v &= ~MVFP_FLIP;
1425cb7a01acSMauro Carvalho Chehab 	msleep(10);  /* FIXME */
1426cb7a01acSMauro Carvalho Chehab 	ret += ov7670_write(sd, REG_MVFP, v);
1427cb7a01acSMauro Carvalho Chehab 	return ret;
1428cb7a01acSMauro Carvalho Chehab }
1429cb7a01acSMauro Carvalho Chehab 
1430cb7a01acSMauro Carvalho Chehab /*
1431cb7a01acSMauro Carvalho Chehab  * GAIN is split between REG_GAIN and REG_VREF[7:6].  If one believes
1432cb7a01acSMauro Carvalho Chehab  * the data sheet, the VREF parts should be the most significant, but
1433cb7a01acSMauro Carvalho Chehab  * experience shows otherwise.  There seems to be little value in
1434cb7a01acSMauro Carvalho Chehab  * messing with the VREF bits, so we leave them alone.
1435cb7a01acSMauro Carvalho Chehab  */
1436cb7a01acSMauro Carvalho Chehab static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
1437cb7a01acSMauro Carvalho Chehab {
1438cb7a01acSMauro Carvalho Chehab 	int ret;
1439cb7a01acSMauro Carvalho Chehab 	unsigned char gain;
1440cb7a01acSMauro Carvalho Chehab 
1441cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_GAIN, &gain);
1442cb7a01acSMauro Carvalho Chehab 	*value = gain;
1443cb7a01acSMauro Carvalho Chehab 	return ret;
1444cb7a01acSMauro Carvalho Chehab }
1445cb7a01acSMauro Carvalho Chehab 
1446cb7a01acSMauro Carvalho Chehab static int ov7670_s_gain(struct v4l2_subdev *sd, int value)
1447cb7a01acSMauro Carvalho Chehab {
1448cb7a01acSMauro Carvalho Chehab 	int ret;
1449cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1450cb7a01acSMauro Carvalho Chehab 
1451cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_GAIN, value & 0xff);
1452cb7a01acSMauro Carvalho Chehab 	/* Have to turn off AGC as well */
1453cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1454cb7a01acSMauro Carvalho Chehab 		ret = ov7670_read(sd, REG_COM8, &com8);
1455cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC);
1456cb7a01acSMauro Carvalho Chehab 	}
1457cb7a01acSMauro Carvalho Chehab 	return ret;
1458cb7a01acSMauro Carvalho Chehab }
1459cb7a01acSMauro Carvalho Chehab 
1460cb7a01acSMauro Carvalho Chehab /*
1461cb7a01acSMauro Carvalho Chehab  * Tweak autogain.
1462cb7a01acSMauro Carvalho Chehab  */
1463cb7a01acSMauro Carvalho Chehab static int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
1464cb7a01acSMauro Carvalho Chehab {
1465cb7a01acSMauro Carvalho Chehab 	int ret;
1466cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1467cb7a01acSMauro Carvalho Chehab 
1468cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1469cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1470cb7a01acSMauro Carvalho Chehab 		if (value)
1471cb7a01acSMauro Carvalho Chehab 			com8 |= COM8_AGC;
1472cb7a01acSMauro Carvalho Chehab 		else
1473cb7a01acSMauro Carvalho Chehab 			com8 &= ~COM8_AGC;
1474cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8);
1475cb7a01acSMauro Carvalho Chehab 	}
1476cb7a01acSMauro Carvalho Chehab 	return ret;
1477cb7a01acSMauro Carvalho Chehab }
1478cb7a01acSMauro Carvalho Chehab 
1479cb7a01acSMauro Carvalho Chehab static int ov7670_s_exp(struct v4l2_subdev *sd, int value)
1480cb7a01acSMauro Carvalho Chehab {
1481cb7a01acSMauro Carvalho Chehab 	int ret;
1482cb7a01acSMauro Carvalho Chehab 	unsigned char com1, com8, aech, aechh;
1483cb7a01acSMauro Carvalho Chehab 
1484cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM1, &com1) +
1485d487df9eSMauro Carvalho Chehab 		ov7670_read(sd, REG_COM8, &com8) +
1486cb7a01acSMauro Carvalho Chehab 		ov7670_read(sd, REG_AECHH, &aechh);
1487cb7a01acSMauro Carvalho Chehab 	if (ret)
1488cb7a01acSMauro Carvalho Chehab 		return ret;
1489cb7a01acSMauro Carvalho Chehab 
1490cb7a01acSMauro Carvalho Chehab 	com1 = (com1 & 0xfc) | (value & 0x03);
1491cb7a01acSMauro Carvalho Chehab 	aech = (value >> 2) & 0xff;
1492cb7a01acSMauro Carvalho Chehab 	aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f);
1493cb7a01acSMauro Carvalho Chehab 	ret = ov7670_write(sd, REG_COM1, com1) +
1494cb7a01acSMauro Carvalho Chehab 		ov7670_write(sd, REG_AECH, aech) +
1495cb7a01acSMauro Carvalho Chehab 		ov7670_write(sd, REG_AECHH, aechh);
1496cb7a01acSMauro Carvalho Chehab 	/* Have to turn off AEC as well */
1497cb7a01acSMauro Carvalho Chehab 	if (ret == 0)
1498cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC);
1499cb7a01acSMauro Carvalho Chehab 	return ret;
1500cb7a01acSMauro Carvalho Chehab }
1501cb7a01acSMauro Carvalho Chehab 
1502cb7a01acSMauro Carvalho Chehab /*
1503cb7a01acSMauro Carvalho Chehab  * Tweak autoexposure.
1504cb7a01acSMauro Carvalho Chehab  */
1505cb7a01acSMauro Carvalho Chehab static int ov7670_s_autoexp(struct v4l2_subdev *sd,
1506cb7a01acSMauro Carvalho Chehab 		enum v4l2_exposure_auto_type value)
1507cb7a01acSMauro Carvalho Chehab {
1508cb7a01acSMauro Carvalho Chehab 	int ret;
1509cb7a01acSMauro Carvalho Chehab 	unsigned char com8;
1510cb7a01acSMauro Carvalho Chehab 
1511cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, REG_COM8, &com8);
1512cb7a01acSMauro Carvalho Chehab 	if (ret == 0) {
1513cb7a01acSMauro Carvalho Chehab 		if (value == V4L2_EXPOSURE_AUTO)
1514cb7a01acSMauro Carvalho Chehab 			com8 |= COM8_AEC;
1515cb7a01acSMauro Carvalho Chehab 		else
1516cb7a01acSMauro Carvalho Chehab 			com8 &= ~COM8_AEC;
1517cb7a01acSMauro Carvalho Chehab 		ret = ov7670_write(sd, REG_COM8, com8);
1518cb7a01acSMauro Carvalho Chehab 	}
1519cb7a01acSMauro Carvalho Chehab 	return ret;
1520cb7a01acSMauro Carvalho Chehab }
1521cb7a01acSMauro Carvalho Chehab 
1522b48d908dSAkinobu Mita static const char * const ov7670_test_pattern_menu[] = {
1523b48d908dSAkinobu Mita 	"No test output",
1524b48d908dSAkinobu Mita 	"Shifting \"1\"",
1525b48d908dSAkinobu Mita 	"8-bar color bar",
1526b48d908dSAkinobu Mita 	"Fade to gray color bar",
1527b48d908dSAkinobu Mita };
1528b48d908dSAkinobu Mita 
1529b48d908dSAkinobu Mita static int ov7670_s_test_pattern(struct v4l2_subdev *sd, int value)
1530b48d908dSAkinobu Mita {
1531b48d908dSAkinobu Mita 	int ret;
1532b48d908dSAkinobu Mita 
1533b48d908dSAkinobu Mita 	ret = ov7670_update_bits(sd, REG_SCALING_XSC, TEST_PATTTERN_0,
1534b48d908dSAkinobu Mita 				value & BIT(0) ? TEST_PATTTERN_0 : 0);
1535b48d908dSAkinobu Mita 	if (ret)
1536b48d908dSAkinobu Mita 		return ret;
1537b48d908dSAkinobu Mita 
1538b48d908dSAkinobu Mita 	return ov7670_update_bits(sd, REG_SCALING_YSC, TEST_PATTTERN_1,
1539b48d908dSAkinobu Mita 				value & BIT(1) ? TEST_PATTTERN_1 : 0);
1540b48d908dSAkinobu Mita }
1541cb7a01acSMauro Carvalho Chehab 
1542492959c7SJavier Martin static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
1543cb7a01acSMauro Carvalho Chehab {
1544492959c7SJavier Martin 	struct v4l2_subdev *sd = to_sd(ctrl);
1545492959c7SJavier Martin 	struct ov7670_info *info = to_state(sd);
1546492959c7SJavier Martin 
1547492959c7SJavier Martin 	switch (ctrl->id) {
1548cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1549492959c7SJavier Martin 		return ov7670_g_gain(sd, &info->gain->val);
1550cb7a01acSMauro Carvalho Chehab 	}
1551cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1552cb7a01acSMauro Carvalho Chehab }
1553cb7a01acSMauro Carvalho Chehab 
1554492959c7SJavier Martin static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl)
1555cb7a01acSMauro Carvalho Chehab {
1556492959c7SJavier Martin 	struct v4l2_subdev *sd = to_sd(ctrl);
1557492959c7SJavier Martin 	struct ov7670_info *info = to_state(sd);
1558492959c7SJavier Martin 
1559cb7a01acSMauro Carvalho Chehab 	switch (ctrl->id) {
1560cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
1561492959c7SJavier Martin 		return ov7670_s_brightness(sd, ctrl->val);
1562cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
1563492959c7SJavier Martin 		return ov7670_s_contrast(sd, ctrl->val);
1564cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
1565492959c7SJavier Martin 		return ov7670_s_sat_hue(sd,
1566492959c7SJavier Martin 				info->saturation->val, info->hue->val);
1567cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_VFLIP:
1568492959c7SJavier Martin 		return ov7670_s_vflip(sd, ctrl->val);
1569cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_HFLIP:
1570492959c7SJavier Martin 		return ov7670_s_hflip(sd, ctrl->val);
1571cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_AUTOGAIN:
1572492959c7SJavier Martin 		/* Only set manual gain if auto gain is not explicitly
1573492959c7SJavier Martin 		   turned on. */
1574492959c7SJavier Martin 		if (!ctrl->val) {
1575492959c7SJavier Martin 			/* ov7670_s_gain turns off auto gain */
1576492959c7SJavier Martin 			return ov7670_s_gain(sd, info->gain->val);
1577492959c7SJavier Martin 		}
1578492959c7SJavier Martin 		return ov7670_s_autogain(sd, ctrl->val);
1579cb7a01acSMauro Carvalho Chehab 	case V4L2_CID_EXPOSURE_AUTO:
1580492959c7SJavier Martin 		/* Only set manual exposure if auto exposure is not explicitly
1581492959c7SJavier Martin 		   turned on. */
1582492959c7SJavier Martin 		if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
1583492959c7SJavier Martin 			/* ov7670_s_exp turns off auto exposure */
1584492959c7SJavier Martin 			return ov7670_s_exp(sd, info->exposure->val);
1585492959c7SJavier Martin 		}
1586492959c7SJavier Martin 		return ov7670_s_autoexp(sd, ctrl->val);
1587b48d908dSAkinobu Mita 	case V4L2_CID_TEST_PATTERN:
1588b48d908dSAkinobu Mita 		return ov7670_s_test_pattern(sd, ctrl->val);
1589cb7a01acSMauro Carvalho Chehab 	}
1590cb7a01acSMauro Carvalho Chehab 	return -EINVAL;
1591cb7a01acSMauro Carvalho Chehab }
1592cb7a01acSMauro Carvalho Chehab 
1593492959c7SJavier Martin static const struct v4l2_ctrl_ops ov7670_ctrl_ops = {
1594492959c7SJavier Martin 	.s_ctrl = ov7670_s_ctrl,
1595492959c7SJavier Martin 	.g_volatile_ctrl = ov7670_g_volatile_ctrl,
1596492959c7SJavier Martin };
1597cb7a01acSMauro Carvalho Chehab 
1598cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1599cb7a01acSMauro Carvalho Chehab static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1600cb7a01acSMauro Carvalho Chehab {
1601cb7a01acSMauro Carvalho Chehab 	unsigned char val = 0;
1602cb7a01acSMauro Carvalho Chehab 	int ret;
1603cb7a01acSMauro Carvalho Chehab 
1604cb7a01acSMauro Carvalho Chehab 	ret = ov7670_read(sd, reg->reg & 0xff, &val);
1605cb7a01acSMauro Carvalho Chehab 	reg->val = val;
1606cb7a01acSMauro Carvalho Chehab 	reg->size = 1;
1607cb7a01acSMauro Carvalho Chehab 	return ret;
1608cb7a01acSMauro Carvalho Chehab }
1609cb7a01acSMauro Carvalho Chehab 
1610977ba3b1SHans Verkuil static int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
1611cb7a01acSMauro Carvalho Chehab {
1612cb7a01acSMauro Carvalho Chehab 	ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
1613cb7a01acSMauro Carvalho Chehab 	return 0;
1614cb7a01acSMauro Carvalho Chehab }
1615cb7a01acSMauro Carvalho Chehab #endif
1616cb7a01acSMauro Carvalho Chehab 
161771862f63SWenyou Yang static int ov7670_s_power(struct v4l2_subdev *sd, int on)
161871862f63SWenyou Yang {
161971862f63SWenyou Yang 	struct ov7670_info *info = to_state(sd);
162071862f63SWenyou Yang 
162171862f63SWenyou Yang 	if (info->pwdn_gpio)
162271862f63SWenyou Yang 		gpiod_set_value(info->pwdn_gpio, !on);
162371862f63SWenyou Yang 	if (on && info->resetb_gpio) {
162471862f63SWenyou Yang 		gpiod_set_value(info->resetb_gpio, 1);
162571862f63SWenyou Yang 		usleep_range(500, 1000);
162671862f63SWenyou Yang 		gpiod_set_value(info->resetb_gpio, 0);
162771862f63SWenyou Yang 		usleep_range(3000, 5000);
162871862f63SWenyou Yang 	}
162971862f63SWenyou Yang 
163071862f63SWenyou Yang 	return 0;
163171862f63SWenyou Yang }
163271862f63SWenyou Yang 
1633c0662dd4SWenyou Yang static void ov7670_get_default_format(struct v4l2_subdev *sd,
1634c0662dd4SWenyou Yang 				      struct v4l2_mbus_framefmt *format)
1635c0662dd4SWenyou Yang {
1636c0662dd4SWenyou Yang 	struct ov7670_info *info = to_state(sd);
1637c0662dd4SWenyou Yang 
1638c0662dd4SWenyou Yang 	format->width = info->devtype->win_sizes[0].width;
1639c0662dd4SWenyou Yang 	format->height = info->devtype->win_sizes[0].height;
1640c0662dd4SWenyou Yang 	format->colorspace = info->fmt->colorspace;
1641c0662dd4SWenyou Yang 	format->code = info->fmt->mbus_code;
1642c0662dd4SWenyou Yang 	format->field = V4L2_FIELD_NONE;
1643c0662dd4SWenyou Yang }
1644c0662dd4SWenyou Yang 
1645c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1646c0662dd4SWenyou Yang static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
1647c0662dd4SWenyou Yang {
1648c0662dd4SWenyou Yang 	struct v4l2_mbus_framefmt *format =
1649c0662dd4SWenyou Yang 				v4l2_subdev_get_try_format(sd, fh->pad, 0);
1650c0662dd4SWenyou Yang 
1651c0662dd4SWenyou Yang 	ov7670_get_default_format(sd, format);
1652c0662dd4SWenyou Yang 
1653c0662dd4SWenyou Yang 	return 0;
1654c0662dd4SWenyou Yang }
1655c0662dd4SWenyou Yang #endif
1656c0662dd4SWenyou Yang 
1657cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1658cb7a01acSMauro Carvalho Chehab 
1659cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ov7670_core_ops = {
1660cb7a01acSMauro Carvalho Chehab 	.reset = ov7670_reset,
1661cb7a01acSMauro Carvalho Chehab 	.init = ov7670_init,
16627852adf8SAkinobu Mita 	.log_status = v4l2_ctrl_subdev_log_status,
16637852adf8SAkinobu Mita 	.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
16647852adf8SAkinobu Mita 	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
1665cb7a01acSMauro Carvalho Chehab #ifdef CONFIG_VIDEO_ADV_DEBUG
1666cb7a01acSMauro Carvalho Chehab 	.g_register = ov7670_g_register,
1667cb7a01acSMauro Carvalho Chehab 	.s_register = ov7670_s_register,
1668cb7a01acSMauro Carvalho Chehab #endif
1669cb7a01acSMauro Carvalho Chehab };
1670cb7a01acSMauro Carvalho Chehab 
1671cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ov7670_video_ops = {
16724471109eSHans Verkuil 	.s_frame_interval = ov7670_s_frame_interval,
16734471109eSHans Verkuil 	.g_frame_interval = ov7670_g_frame_interval,
167417bef885SHans Verkuil };
167517bef885SHans Verkuil 
167617bef885SHans Verkuil static const struct v4l2_subdev_pad_ops ov7670_pad_ops = {
167717bef885SHans Verkuil 	.enum_frame_interval = ov7670_enum_frame_interval,
167817bef885SHans Verkuil 	.enum_frame_size = ov7670_enum_frame_size,
1679ebcff5fcSHans Verkuil 	.enum_mbus_code = ov7670_enum_mbus_code,
1680c0662dd4SWenyou Yang 	.get_fmt = ov7670_get_fmt,
1681717fd5b4SHans Verkuil 	.set_fmt = ov7670_set_fmt,
1682cb7a01acSMauro Carvalho Chehab };
1683cb7a01acSMauro Carvalho Chehab 
1684cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ov7670_ops = {
1685cb7a01acSMauro Carvalho Chehab 	.core = &ov7670_core_ops,
1686cb7a01acSMauro Carvalho Chehab 	.video = &ov7670_video_ops,
168717bef885SHans Verkuil 	.pad = &ov7670_pad_ops,
1688cb7a01acSMauro Carvalho Chehab };
1689cb7a01acSMauro Carvalho Chehab 
1690c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1691c0662dd4SWenyou Yang static const struct v4l2_subdev_internal_ops ov7670_subdev_internal_ops = {
1692c0662dd4SWenyou Yang 	.open = ov7670_open,
1693c0662dd4SWenyou Yang };
1694c0662dd4SWenyou Yang #endif
1695c0662dd4SWenyou Yang 
1696cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */
1697cb7a01acSMauro Carvalho Chehab 
1698d058e237SJavier Martin static const struct ov7670_devtype ov7670_devdata[] = {
1699d058e237SJavier Martin 	[MODEL_OV7670] = {
1700d058e237SJavier Martin 		.win_sizes = ov7670_win_sizes,
1701d058e237SJavier Martin 		.n_win_sizes = ARRAY_SIZE(ov7670_win_sizes),
1702f6dd927fSJavier Martin 		.set_framerate = ov7670_set_framerate_legacy,
1703f6dd927fSJavier Martin 		.get_framerate = ov7670_get_framerate_legacy,
1704d058e237SJavier Martin 	},
1705d058e237SJavier Martin 	[MODEL_OV7675] = {
1706d058e237SJavier Martin 		.win_sizes = ov7675_win_sizes,
1707d058e237SJavier Martin 		.n_win_sizes = ARRAY_SIZE(ov7675_win_sizes),
1708f6dd927fSJavier Martin 		.set_framerate = ov7675_set_framerate,
1709f6dd927fSJavier Martin 		.get_framerate = ov7675_get_framerate,
1710d058e237SJavier Martin 	},
1711d058e237SJavier Martin };
1712d058e237SJavier Martin 
1713a0c4164eSHans Verkuil static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info)
1714a0c4164eSHans Verkuil {
1715a0c4164eSHans Verkuil 	info->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
1716a0c4164eSHans Verkuil 			GPIOD_OUT_LOW);
1717a0c4164eSHans Verkuil 	if (IS_ERR(info->pwdn_gpio)) {
1718a0c4164eSHans Verkuil 		dev_info(&client->dev, "can't get %s GPIO\n", "powerdown");
1719a0c4164eSHans Verkuil 		return PTR_ERR(info->pwdn_gpio);
1720a0c4164eSHans Verkuil 	}
1721a0c4164eSHans Verkuil 
1722a0c4164eSHans Verkuil 	info->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset",
1723a0c4164eSHans Verkuil 			GPIOD_OUT_LOW);
1724a0c4164eSHans Verkuil 	if (IS_ERR(info->resetb_gpio)) {
1725a0c4164eSHans Verkuil 		dev_info(&client->dev, "can't get %s GPIO\n", "reset");
1726a0c4164eSHans Verkuil 		return PTR_ERR(info->resetb_gpio);
1727a0c4164eSHans Verkuil 	}
1728a0c4164eSHans Verkuil 
1729a0c4164eSHans Verkuil 	usleep_range(3000, 5000);
1730a0c4164eSHans Verkuil 
1731a0c4164eSHans Verkuil 	return 0;
1732a0c4164eSHans Verkuil }
1733a0c4164eSHans Verkuil 
173401b84448SJacopo Mondi /*
173501b84448SJacopo Mondi  * ov7670_parse_dt() - Parse device tree to collect mbus configuration
173601b84448SJacopo Mondi  *			properties
173701b84448SJacopo Mondi  */
173801b84448SJacopo Mondi static int ov7670_parse_dt(struct device *dev,
173901b84448SJacopo Mondi 			   struct ov7670_info *info)
174001b84448SJacopo Mondi {
174101b84448SJacopo Mondi 	struct fwnode_handle *fwnode = dev_fwnode(dev);
174260359a28SSakari Ailus 	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
174301b84448SJacopo Mondi 	struct fwnode_handle *ep;
174401b84448SJacopo Mondi 	int ret;
174501b84448SJacopo Mondi 
174601b84448SJacopo Mondi 	if (!fwnode)
174701b84448SJacopo Mondi 		return -EINVAL;
174801b84448SJacopo Mondi 
174901b84448SJacopo Mondi 	info->pclk_hb_disable = false;
175001b84448SJacopo Mondi 	if (fwnode_property_present(fwnode, "ov7670,pclk-hb-disable"))
175101b84448SJacopo Mondi 		info->pclk_hb_disable = true;
175201b84448SJacopo Mondi 
175301b84448SJacopo Mondi 	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
175401b84448SJacopo Mondi 	if (!ep)
175501b84448SJacopo Mondi 		return -EINVAL;
175601b84448SJacopo Mondi 
175701b84448SJacopo Mondi 	ret = v4l2_fwnode_endpoint_parse(ep, &bus_cfg);
175801b84448SJacopo Mondi 	fwnode_handle_put(ep);
175909a48f74SJacopo Mondi 	if (ret)
176001b84448SJacopo Mondi 		return ret;
176101b84448SJacopo Mondi 
176201b84448SJacopo Mondi 	if (bus_cfg.bus_type != V4L2_MBUS_PARALLEL) {
176301b84448SJacopo Mondi 		dev_err(dev, "Unsupported media bus type\n");
176401b84448SJacopo Mondi 		return ret;
176501b84448SJacopo Mondi 	}
176601b84448SJacopo Mondi 	info->mbus_config = bus_cfg.bus.parallel.flags;
176701b84448SJacopo Mondi 
176801b84448SJacopo Mondi 	return 0;
176901b84448SJacopo Mondi }
177001b84448SJacopo Mondi 
1771cb7a01acSMauro Carvalho Chehab static int ov7670_probe(struct i2c_client *client,
1772cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
1773cb7a01acSMauro Carvalho Chehab {
1774f6dd927fSJavier Martin 	struct v4l2_fract tpf;
1775cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
1776cb7a01acSMauro Carvalho Chehab 	struct ov7670_info *info;
1777cb7a01acSMauro Carvalho Chehab 	int ret;
1778cb7a01acSMauro Carvalho Chehab 
1779c02b211dSLaurent Pinchart 	info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
1780cb7a01acSMauro Carvalho Chehab 	if (info == NULL)
1781cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
1782cb7a01acSMauro Carvalho Chehab 	sd = &info->sd;
1783cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
1784cb7a01acSMauro Carvalho Chehab 
1785c0662dd4SWenyou Yang #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
1786c0662dd4SWenyou Yang 	sd->internal_ops = &ov7670_subdev_internal_ops;
17877852adf8SAkinobu Mita 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
1788c0662dd4SWenyou Yang #endif
1789c0662dd4SWenyou Yang 
1790cb7a01acSMauro Carvalho Chehab 	info->clock_speed = 30; /* default: a guess */
179101b84448SJacopo Mondi 
179201b84448SJacopo Mondi 	if (dev_fwnode(&client->dev)) {
179301b84448SJacopo Mondi 		ret = ov7670_parse_dt(&client->dev, info);
179401b84448SJacopo Mondi 		if (ret)
179501b84448SJacopo Mondi 			return ret;
179601b84448SJacopo Mondi 
179701b84448SJacopo Mondi 	} else if (client->dev.platform_data) {
1798cb7a01acSMauro Carvalho Chehab 		struct ov7670_config *config = client->dev.platform_data;
1799cb7a01acSMauro Carvalho Chehab 
1800cb7a01acSMauro Carvalho Chehab 		/*
1801cb7a01acSMauro Carvalho Chehab 		 * Must apply configuration before initializing device, because it
1802cb7a01acSMauro Carvalho Chehab 		 * selects I/O method.
1803cb7a01acSMauro Carvalho Chehab 		 */
1804cb7a01acSMauro Carvalho Chehab 		info->min_width = config->min_width;
1805cb7a01acSMauro Carvalho Chehab 		info->min_height = config->min_height;
1806cb7a01acSMauro Carvalho Chehab 		info->use_smbus = config->use_smbus;
1807cb7a01acSMauro Carvalho Chehab 
1808cb7a01acSMauro Carvalho Chehab 		if (config->clock_speed)
1809cb7a01acSMauro Carvalho Chehab 			info->clock_speed = config->clock_speed;
181004ee6d92SJavier Martin 
181161da76beSJacopo Mondi 		if (config->pll_bypass)
181204ee6d92SJavier Martin 			info->pll_bypass = true;
1813ee95258eSJavier Martin 
1814ee95258eSJavier Martin 		if (config->pclk_hb_disable)
1815ee95258eSJavier Martin 			info->pclk_hb_disable = true;
1816cb7a01acSMauro Carvalho Chehab 	}
1817cb7a01acSMauro Carvalho Chehab 
1818786fa584SLubomir Rintel 	info->clk = devm_clk_get(&client->dev, "xclk"); /* optional */
1819786fa584SLubomir Rintel 	if (IS_ERR(info->clk)) {
1820786fa584SLubomir Rintel 		ret = PTR_ERR(info->clk);
1821786fa584SLubomir Rintel 		if (ret == -ENOENT)
1822786fa584SLubomir Rintel 			info->clk = NULL;
1823786fa584SLubomir Rintel 		else
1824786fa584SLubomir Rintel 			return ret;
1825786fa584SLubomir Rintel 	}
1826786fa584SLubomir Rintel 	if (info->clk) {
18271fc86ad0SFabio Estevam 		ret = clk_prepare_enable(info->clk);
18281fc86ad0SFabio Estevam 		if (ret)
18291fc86ad0SFabio Estevam 			return ret;
18300a024d63SHans Verkuil 
18310a024d63SHans Verkuil 		info->clock_speed = clk_get_rate(info->clk) / 1000000;
18320a024d63SHans Verkuil 		if (info->clock_speed < 10 || info->clock_speed > 48) {
18330a024d63SHans Verkuil 			ret = -EINVAL;
18340a024d63SHans Verkuil 			goto clk_disable;
18350a024d63SHans Verkuil 		}
1836786fa584SLubomir Rintel 	}
18370a024d63SHans Verkuil 
183871862f63SWenyou Yang 	ret = ov7670_init_gpio(client, info);
183971862f63SWenyou Yang 	if (ret)
184071862f63SWenyou Yang 		goto clk_disable;
184171862f63SWenyou Yang 
184271862f63SWenyou Yang 	ov7670_s_power(sd, 1);
184371862f63SWenyou Yang 
1844cb7a01acSMauro Carvalho Chehab 	/* Make sure it's an ov7670 */
1845cb7a01acSMauro Carvalho Chehab 	ret = ov7670_detect(sd);
1846cb7a01acSMauro Carvalho Chehab 	if (ret) {
1847cb7a01acSMauro Carvalho Chehab 		v4l_dbg(1, debug, client,
1848cb7a01acSMauro Carvalho Chehab 			"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
1849cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
185071862f63SWenyou Yang 		goto power_off;
1851cb7a01acSMauro Carvalho Chehab 	}
1852cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
1853cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
1854cb7a01acSMauro Carvalho Chehab 
1855d058e237SJavier Martin 	info->devtype = &ov7670_devdata[id->driver_data];
1856cb7a01acSMauro Carvalho Chehab 	info->fmt = &ov7670_formats[0];
18575556ab2aSLubomir Rintel 	info->wsize = &info->devtype->win_sizes[0];
1858c0662dd4SWenyou Yang 
1859c0662dd4SWenyou Yang 	ov7670_get_default_format(sd, &info->format);
1860c0662dd4SWenyou Yang 
1861f6dd927fSJavier Martin 	info->clkrc = 0;
1862f6dd927fSJavier Martin 
1863f6dd927fSJavier Martin 	/* Set default frame rate to 30 fps */
1864f6dd927fSJavier Martin 	tpf.numerator = 1;
1865f6dd927fSJavier Martin 	tpf.denominator = 30;
1866f6dd927fSJavier Martin 	info->devtype->set_framerate(sd, &tpf);
1867f6dd927fSJavier Martin 
1868492959c7SJavier Martin 	v4l2_ctrl_handler_init(&info->hdl, 10);
1869492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1870492959c7SJavier Martin 			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
1871492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1872492959c7SJavier Martin 			V4L2_CID_CONTRAST, 0, 127, 1, 64);
1873492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1874492959c7SJavier Martin 			V4L2_CID_VFLIP, 0, 1, 1, 0);
1875492959c7SJavier Martin 	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1876492959c7SJavier Martin 			V4L2_CID_HFLIP, 0, 1, 1, 0);
1877492959c7SJavier Martin 	info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1878492959c7SJavier Martin 			V4L2_CID_SATURATION, 0, 256, 1, 128);
1879492959c7SJavier Martin 	info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1880492959c7SJavier Martin 			V4L2_CID_HUE, -180, 180, 5, 0);
1881492959c7SJavier Martin 	info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1882492959c7SJavier Martin 			V4L2_CID_GAIN, 0, 255, 1, 128);
1883492959c7SJavier Martin 	info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1884492959c7SJavier Martin 			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
1885492959c7SJavier Martin 	info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1886492959c7SJavier Martin 			V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
1887492959c7SJavier Martin 	info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops,
1888492959c7SJavier Martin 			V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
1889492959c7SJavier Martin 			V4L2_EXPOSURE_AUTO);
1890b48d908dSAkinobu Mita 	v4l2_ctrl_new_std_menu_items(&info->hdl, &ov7670_ctrl_ops,
1891b48d908dSAkinobu Mita 			V4L2_CID_TEST_PATTERN,
1892b48d908dSAkinobu Mita 			ARRAY_SIZE(ov7670_test_pattern_menu) - 1, 0, 0,
1893b48d908dSAkinobu Mita 			ov7670_test_pattern_menu);
1894492959c7SJavier Martin 	sd->ctrl_handler = &info->hdl;
1895492959c7SJavier Martin 	if (info->hdl.error) {
18967d1b8619SHans Verkuil 		ret = info->hdl.error;
1897492959c7SJavier Martin 
18987d1b8619SHans Verkuil 		goto hdl_free;
1899492959c7SJavier Martin 	}
1900492959c7SJavier Martin 	/*
1901492959c7SJavier Martin 	 * We have checked empirically that hw allows to read back the gain
1902492959c7SJavier Martin 	 * value chosen by auto gain but that's not the case for auto exposure.
1903492959c7SJavier Martin 	 */
1904492959c7SJavier Martin 	v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true);
1905492959c7SJavier Martin 	v4l2_ctrl_auto_cluster(2, &info->auto_exposure,
1906492959c7SJavier Martin 			       V4L2_EXPOSURE_MANUAL, false);
1907492959c7SJavier Martin 	v4l2_ctrl_cluster(2, &info->saturation);
1908d94a26f0SWenyou Yang 
1909d94a26f0SWenyou Yang #if defined(CONFIG_MEDIA_CONTROLLER)
1910d94a26f0SWenyou Yang 	info->pad.flags = MEDIA_PAD_FL_SOURCE;
1911d94a26f0SWenyou Yang 	info->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
1912d94a26f0SWenyou Yang 	ret = media_entity_pads_init(&info->sd.entity, 1, &info->pad);
1913d94a26f0SWenyou Yang 	if (ret < 0)
1914d94a26f0SWenyou Yang 		goto hdl_free;
1915d94a26f0SWenyou Yang #endif
1916d94a26f0SWenyou Yang 
1917492959c7SJavier Martin 	v4l2_ctrl_handler_setup(&info->hdl);
1918492959c7SJavier Martin 
19197d1b8619SHans Verkuil 	ret = v4l2_async_register_subdev(&info->sd);
19207d1b8619SHans Verkuil 	if (ret < 0)
1921d94a26f0SWenyou Yang 		goto entity_cleanup;
19227d1b8619SHans Verkuil 
1923cb7a01acSMauro Carvalho Chehab 	return 0;
19247d1b8619SHans Verkuil 
1925d94a26f0SWenyou Yang entity_cleanup:
1926d94a26f0SWenyou Yang 	media_entity_cleanup(&info->sd.entity);
19277d1b8619SHans Verkuil hdl_free:
19287d1b8619SHans Verkuil 	v4l2_ctrl_handler_free(&info->hdl);
192971862f63SWenyou Yang power_off:
193071862f63SWenyou Yang 	ov7670_s_power(sd, 0);
19310a024d63SHans Verkuil clk_disable:
19320a024d63SHans Verkuil 	clk_disable_unprepare(info->clk);
19337d1b8619SHans Verkuil 	return ret;
1934cb7a01acSMauro Carvalho Chehab }
1935cb7a01acSMauro Carvalho Chehab 
1936cb7a01acSMauro Carvalho Chehab 
1937cb7a01acSMauro Carvalho Chehab static int ov7670_remove(struct i2c_client *client)
1938cb7a01acSMauro Carvalho Chehab {
1939cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1940492959c7SJavier Martin 	struct ov7670_info *info = to_state(sd);
1941cb7a01acSMauro Carvalho Chehab 
1942344aa836SAkinobu Mita 	v4l2_async_unregister_subdev(sd);
1943492959c7SJavier Martin 	v4l2_ctrl_handler_free(&info->hdl);
19440a024d63SHans Verkuil 	clk_disable_unprepare(info->clk);
1945d94a26f0SWenyou Yang 	media_entity_cleanup(&info->sd.entity);
194671862f63SWenyou Yang 	ov7670_s_power(sd, 0);
1947cb7a01acSMauro Carvalho Chehab 	return 0;
1948cb7a01acSMauro Carvalho Chehab }
1949cb7a01acSMauro Carvalho Chehab 
1950cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ov7670_id[] = {
1951d058e237SJavier Martin 	{ "ov7670", MODEL_OV7670 },
1952d058e237SJavier Martin 	{ "ov7675", MODEL_OV7675 },
1953cb7a01acSMauro Carvalho Chehab 	{ }
1954cb7a01acSMauro Carvalho Chehab };
1955cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ov7670_id);
1956cb7a01acSMauro Carvalho Chehab 
1957a0c4164eSHans Verkuil #if IS_ENABLED(CONFIG_OF)
1958a0c4164eSHans Verkuil static const struct of_device_id ov7670_of_match[] = {
1959a0c4164eSHans Verkuil 	{ .compatible = "ovti,ov7670", },
1960a0c4164eSHans Verkuil 	{ /* sentinel */ },
1961a0c4164eSHans Verkuil };
1962a0c4164eSHans Verkuil MODULE_DEVICE_TABLE(of, ov7670_of_match);
1963a0c4164eSHans Verkuil #endif
1964a0c4164eSHans Verkuil 
1965cb7a01acSMauro Carvalho Chehab static struct i2c_driver ov7670_driver = {
1966cb7a01acSMauro Carvalho Chehab 	.driver = {
1967cb7a01acSMauro Carvalho Chehab 		.name	= "ov7670",
1968a0c4164eSHans Verkuil 		.of_match_table = of_match_ptr(ov7670_of_match),
1969cb7a01acSMauro Carvalho Chehab 	},
1970cb7a01acSMauro Carvalho Chehab 	.probe		= ov7670_probe,
1971cb7a01acSMauro Carvalho Chehab 	.remove		= ov7670_remove,
1972cb7a01acSMauro Carvalho Chehab 	.id_table	= ov7670_id,
1973cb7a01acSMauro Carvalho Chehab };
1974cb7a01acSMauro Carvalho Chehab 
1975cb7a01acSMauro Carvalho Chehab module_i2c_driver(ov7670_driver);
1976