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