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