1111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
2111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * A V4L2 driver for OmniVision OV7670 cameras.
3111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *
4111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Copyright 2006 One Laptop Per Child Association, Inc.  Written
5111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * by Jonathan Corbet with substantial inspiration from Mark
6111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * McClelland's ovcamchip code.
7111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *
877d5140fe78ca628bfc9d2567c35457481f9c63dJonathan Corbet * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
977d5140fe78ca628bfc9d2567c35457481f9c63dJonathan Corbet *
10111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * This file may be distributed under the terms of the GNU General
11111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Public License, version 2.
12111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
13111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#include <linux/init.h>
14111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#include <linux/module.h>
155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
1614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil#include <linux/i2c.h>
17111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#include <linux/delay.h>
187e0a16f6118a297dd467c1e5a0908429fcdf56afMauro Carvalho Chehab#include <linux/videodev2.h>
1914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil#include <media/v4l2-device.h>
20492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin#include <media/v4l2-ctrls.h>
21959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil#include <media/v4l2-mediabus.h>
224721b3eb662ca5ea60a636f0f190f2fd2ac5df14Axel Lin#include <media/v4l2-image-sizes.h>
23f8fc729870ee82662ae6e3a713d59b2fbf3b04c6Jonathan Corbet#include <media/ov7670.h>
24111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
255e614475decfcc852d2c8c50702f81ee34901fe2Dave JonesMODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
26111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan CorbetMODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
27111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan CorbetMODULE_LICENSE("GPL");
28111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
2990ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug;
3014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilmodule_param(debug, bool, 0644);
3114386c2b7793652a656021a3345cff3b0f6771f9Hans VerkuilMODULE_PARM_DESC(debug, "Debug level (0-1)");
3214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil
33111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
34111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * The 7670 sits on i2c with ID 0x42
35111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
36111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define OV7670_I2C_ADDR 0x42
37111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
38f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define PLL_FACTOR	4
39f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
40111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/* Registers */
41111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
42111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BLUE	0x01	/* blue gain */
43111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_RED		0x02	/* red gain */
44111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
45111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM1	0x04	/* Control 1 */
46111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define  COM1_CCIR656	  0x40  /* CCIR656 enable */
47111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BAVE	0x05	/* U/B Average level */
48111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_GbAVE	0x06	/* Y/Gb Average level */
49111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AECHH	0x07	/* AEC MS 5 bits */
50111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_RAVE	0x08	/* V/R Average level */
51111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM2	0x09	/* Control 2 */
52111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
53111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_PID		0x0a	/* Product ID MSB */
54111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VER		0x0b	/* Product ID LSB */
55111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM3	0x0c	/* Control 3 */
56111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define  COM3_SWAP	  0x40	  /* Byte swap */
57111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define  COM3_SCALEEN	  0x08	  /* Enable scaling */
58111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
59111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM4	0x0d	/* Control 4 */
60111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM5	0x0e	/* All "reserved" */
61111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM6	0x0f	/* Control 6 */
62111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AECH	0x10	/* More bits of AEC value */
63111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_CLKRC	0x11	/* Clocl control */
64111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   CLK_EXT	  0x40	  /* Use external clock directly */
65111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
66111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM7	0x12	/* Control 7 */
67111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM7_RESET	  0x80	  /* Register reset */
68111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM7_FMT_MASK	  0x38
69111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM7_FMT_VGA	  0x00
70111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM7_FMT_CIF	  0x20	  /* CIF format */
71111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
72111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
73111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
74111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM7_YUV	  0x00	  /* YUV */
75111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM7_BAYER	  0x01	  /* Bayer format */
76111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
77111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM8	0x13	/* Control 8 */
78111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
79111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
80111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM8_BFILT	  0x20	  /* Band filter enable */
81111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM8_AGC	  0x04	  /* Auto gain enable */
82111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM8_AWB	  0x02	  /* White balance enable */
83111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM8_AEC	  0x01	  /* Auto exposure enable */
84111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM9	0x14	/* Control 9  - gain ceiling */
85111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM10	0x15	/* Control 10 */
86111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
87111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
88111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM10_HREF_REV  0x08	  /* Reverse HREF */
89111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
90111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
91111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
92111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSTART	0x17	/* Horiz start high bits */
93111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSTOP	0x18	/* Horiz stop high bits */
94111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VSTART	0x19	/* Vert start high bits */
95111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VSTOP	0x1a	/* Vert stop high bits */
96111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
97111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_MIDH	0x1c	/* Manuf. ID high */
98111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_MIDL	0x1d	/* Manuf. ID low */
99111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_MVFP	0x1e	/* Mirror / vflip */
100111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   MVFP_MIRROR	  0x20	  /* Mirror image */
101111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   MVFP_FLIP	  0x10	  /* Vertical flip */
102111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
103111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AEW		0x24	/* AGC upper limit */
104111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_AEB		0x25	/* AGC lower limit */
105111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_VPT		0x26	/* AGC/AEC fast mode op region */
106111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSYST	0x30	/* HSYNC rising edge delay */
107111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HSYEN	0x31	/* HSYNC falling edge delay */
108111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HREF	0x32	/* HREF pieces */
109111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_TSLB	0x3a	/* lots of stuff */
110111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
111111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM11	0x3b	/* Control 11 */
112111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM11_NIGHT	  0x80	  /* NIght mode enable */
113111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
114111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
115111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
116111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM11_EXP	  0x02
117111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM12	0x3c	/* Control 12 */
118111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM12_HREF	  0x80	  /* HREF always */
119111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM13	0x3d	/* Control 13 */
120111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM13_GAMMA	  0x80	  /* Gamma enable */
121111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
122111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
123111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM14	0x3e	/* Control 14 */
124111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
125111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_EDGE	0x3f	/* Edge enhancement factor */
126111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM15	0x40	/* Control 15 */
127111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
128111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define	  COM15_R01FE	  0x80	  /*            01 to FE */
129111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM15_R00FF	  0xc0	  /*            00 to FF */
130111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM15_RGB565	  0x10	  /* RGB565 output */
131111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM15_RGB555	  0x30	  /* RGB555 output */
132111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM16	0x41	/* Control 16 */
133111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
134111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_COM17	0x42	/* Control 17 */
135111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
136111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   COM17_CBAR	  0x08	  /* DSP Color bar */
137111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
138f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/*
139f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * This matrix defines how the colors are generated, must be
140f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * tweaked to adjust hue and saturation.
141f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet *
142f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
143f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet *
144f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * They are nine-bit signed quantities, with the sign bit
145f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * stored in 0x58.  Sign for v-red is bit 0, and up from there.
146f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */
147f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define	REG_CMATRIX_BASE 0x4f
148f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define   CMATRIX_LEN 6
149f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define REG_CMATRIX_SIGN 0x58
150f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
151f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
152111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BRIGHT	0x55	/* Brightness */
153111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_CONTRAS	0x56	/* Contrast control */
154111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
155111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_GFIX	0x69	/* Fix gain control */
156111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
157f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define REG_DBLV	0x6b	/* PLL control an debugging */
158f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define   DBLV_BYPASS	  0x00	  /* Bypass PLL */
159f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define   DBLV_X4	  0x01	  /* clock x4 */
160f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define   DBLV_X6	  0x10	  /* clock x6 */
161f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin#define   DBLV_X8	  0x11	  /* clock x8 */
162f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
163585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define REG_REG76	0x76	/* OV's name */
164585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define   R76_BLKPCOR	  0x80	  /* Black pixel correction enable */
165585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define   R76_WHTPCOR	  0x40	  /* White pixel correction enable */
166585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet
167111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_RGB444	0x8c	/* RGB 444 control */
168111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
169111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define   R444_RGBX	  0x01	  /* Empty nibble at end */
170111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
171111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
172111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
173111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
174111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
175111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
176111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
177111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
178111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
179111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
180111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet#define REG_BD60MAX	0xab	/* 60hz banding step limit */
181111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
182d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinenum ov7670_model {
183d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	MODEL_OV7670 = 0,
184d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	MODEL_OV7675,
185d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin};
186d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin
187d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstruct ov7670_win_size {
188d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	int	width;
189d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	int	height;
190d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	unsigned char com7_bit;
191d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	int	hstart;		/* Start/stop values for the camera.  Note */
192d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	int	hstop;		/* that they do not always make complete */
193d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	int	vstart;		/* sense to humans, but evidently the sensor */
194d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	int	vstop;		/* will do the right thing... */
195d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	struct regval_list *regs; /* Regs to tweak */
196d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin};
197d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin
198d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstruct ov7670_devtype {
199d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	/* formats supported for each model */
200d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	struct ov7670_win_size *win_sizes;
201d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	unsigned int n_win_sizes;
202f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	/* callbacks for frame rate control */
203f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
204f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *);
205d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin};
206111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
207111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
208f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Information we maintain about a known sensor.
209f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */
210f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstruct ov7670_format_struct;  /* coming later */
211f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstruct ov7670_info {
21214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct v4l2_subdev sd;
213492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct v4l2_ctrl_handler hdl;
214492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct {
215492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		/* gain cluster */
216492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		struct v4l2_ctrl *auto_gain;
217492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		struct v4l2_ctrl *gain;
218492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	};
219492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct {
220492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		/* exposure cluster */
221492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		struct v4l2_ctrl *auto_exposure;
222492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		struct v4l2_ctrl *exposure;
223492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	};
224492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct {
225492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		/* saturation/hue cluster */
226492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		struct v4l2_ctrl *saturation;
227492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		struct v4l2_ctrl *hue;
228492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	};
229f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	struct ov7670_format_struct *fmt;  /* Current format */
23075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	int min_width;			/* Filter out smaller sizes */
23175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	int min_height;			/* Filter out smaller sizes */
23275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	int clock_speed;		/* External clock speed (MHz) */
233d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	u8 clkrc;			/* Clock divider value */
23475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	bool use_smbus;			/* Use smbus I/O instead of I2C */
23504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	bool pll_bypass;
236ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin	bool pclk_hb_disable;
237d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	const struct ov7670_devtype *devtype; /* Device specifics */
238f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet};
239f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
24014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
24114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil{
24214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return container_of(sd, struct ov7670_info, sd);
24314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil}
244f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
245492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
246492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin{
247492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd;
248492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin}
249492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin
250f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
251f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
252f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/*
253111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * The default register settings, as obtained from OmniVision.  There
254111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * is really no making sense of most of these - lots of "reserved" values
255111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * and such.
256111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *
257111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * These settings give VGA YUYV.
258111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
259111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
260111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstruct regval_list {
261111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	unsigned char reg_num;
262111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	unsigned char value;
263111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
264111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
265111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_default_regs[] = {
266111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM7, COM7_RESET },
267111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
268111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Clock scale: 3 = 15fps
269111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *              2 = 20fps
270111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *              1 = 30fps
271111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
272f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ REG_CLKRC, 0x1 },	/* OV: clock scale (30 fps) */
273111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_TSLB,  0x04 },	/* OV */
274111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM7, 0 },	/* VGA */
275111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
276111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * Set the hardware window.  These values from OV don't entirely
277111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * make sense - hstop is less than hstart.  But they work...
278111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
279111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
280111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
281111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
282111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
283111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM3, 0 },	{ REG_COM14, 0 },
284111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* Mystery scaling numbers */
285111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x70, 0x3a },		{ 0x71, 0x35 },
286111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
287111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
288111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
289111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* Gamma curve values */
290111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
291111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
292111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
293111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x80, 0x76 },		{ 0x81, 0x80 },
294111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x82, 0x88 },		{ 0x83, 0x8f },
295111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
296111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
297111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
298111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
299111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* AGC and AEC parameters.  Note we start by disabling those features,
300111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	   then turn them only after tweaking the values. */
301111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
302111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
303111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM4, 0x40 }, /* magic reserved bit */
304111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
305111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
306111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
307111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
308111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
309111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
310111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
311111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_HAECC7, 0x94 },
312111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
313111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
314111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* Almost all of these are magic "reserved" values.  */
315111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
3167f7b12f09be8e396d159800baeaf922df46e25cfJonathan Corbet	{ 0x16, 0x02 },		{ REG_MVFP, 0x07 },
317111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x21, 0x02 },		{ 0x22, 0x91 },
318111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x29, 0x07 },		{ 0x33, 0x0b },
319111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x35, 0x0b },		{ 0x37, 0x1d },
320111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x38, 0x71 },		{ 0x39, 0x2a },
321111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
322111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
323111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
324111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x8d, 0x4f },		{ 0x8e, 0 },
325111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x8f, 0 },		{ 0x90, 0 },
326111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x91, 0 },		{ 0x96, 0 },
327111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x9a, 0 },		{ 0xb0, 0x84 },
328111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
329111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
330111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
331111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* More reserved magic, some of which tweaks white balance */
332111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
333111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x45, 0x34 },		{ 0x46, 0x58 },
334111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x47, 0x28 },		{ 0x48, 0x3a },
335111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
336111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
337111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
338111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
339111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
340111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
341111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_RED, 0x60 },
342111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
343111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
344111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* Matrix coefficients */
345111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
346111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x51, 0 },		{ 0x52, 0x22 },
347111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x53, 0x5e },		{ 0x54, 0x80 },
348111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x58, 0x9e },
349111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
350111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
351111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
352111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x4c, 0 },		{ 0x77, 0x01 },
353111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
354111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
355111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x56, 0x40 },
356111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
357c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
358111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xa4, 0x88 },		{ 0x96, 0 },
359111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x97, 0x30 },		{ 0x98, 0x20 },
360111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
361111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
362111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
363111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x78, 0x04 },
364111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
365111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* Extra-weird stuff.  Some sort of multiplexor register */
366111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
367111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
368111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
369111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
370111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
371111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
372111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
373111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
374111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
375111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
376111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
377111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x79, 0x26 },
378111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
379111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xff, 0xff },	/* END MARKER */
380111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
381111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
382111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
383111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
384111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Here we'll try to encapsulate the changes for just the output
385111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * video format.
386111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *
387111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
388111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet *
389111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
390111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
391111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
392111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
393111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_fmt_yuv422[] = {
394111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM7, 0x0 },  /* Selects YUV mode */
395111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_RGB444, 0 },	/* No RGB444 please */
39697693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet	{ REG_COM1, 0 },	/* CCIR601 */
397111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM15, COM15_R00FF },
398c01b7429e2b713a3d039cd2b1a94dc1b97cdf321Javier Martin	{ REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */
399111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
400111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
401f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x51, 0    },		/* vb */
402111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
403111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
404111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
405111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
406111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xff, 0xff },
407111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
408111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
409111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_fmt_rgb565[] = {
410111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
411111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_RGB444, 0 },	/* No RGB444 please */
41297693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet	{ REG_COM1, 0x0 },	/* CCIR601 */
413111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM15, COM15_RGB565 },
414111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
415111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
416111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
417f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x51, 0    },		/* vb */
418111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
419111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
420111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
421111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
422111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xff, 0xff },
423111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
424111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
425111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct regval_list ov7670_fmt_rgb444[] = {
426111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
427111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
42897693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet	{ REG_COM1, 0x0 },	/* CCIR601 */
429111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
430111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
431111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
432111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
433f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x51, 0    },		/* vb */
434111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
435111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
436111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
437111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
438111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{ 0xff, 0xff },
439111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
440111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
441585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbetstatic struct regval_list ov7670_fmt_raw[] = {
442585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	{ REG_COM7, COM7_BAYER },
443585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	{ REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */
444585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	{ REG_COM16, 0x3d }, /* Edge enhancement, denoise */
445585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	{ REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */
446585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	{ 0xff, 0xff },
447585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet};
448111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
449111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
450111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
451111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
452111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Low-level register I/O.
453467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet *
454467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * Note that there are two versions of these.  On the XO 1, the
455467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * i2c controller only does SMBUS, so that's what we use.  The
456467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * ov7670 is not really an SMBUS device, though, so the communication
457467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * is not always entirely reliable.
458467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet */
45975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg,
460467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet		unsigned char *value)
461467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet{
462467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	struct i2c_client *client = v4l2_get_subdevdata(sd);
463467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	int ret;
464467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet
465467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	ret = i2c_smbus_read_byte_data(client, reg);
466467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	if (ret >= 0) {
467467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet		*value = (unsigned char)ret;
468467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet		ret = 0;
469467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	}
470467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	return ret;
471467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet}
472467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet
473467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet
47475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg,
475467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet		unsigned char value)
476467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet{
477467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	struct i2c_client *client = v4l2_get_subdevdata(sd);
478467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	int ret = i2c_smbus_write_byte_data(client, reg, value);
479467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet
480467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	if (reg == REG_COM7 && (value & COM7_RESET))
481467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet		msleep(5);  /* Wait for reset to run */
482467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet	return ret;
483467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet}
484467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet
485467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet/*
486467142093de1507833a08c50740d74b3b8c8eacaJonathan Corbet * On most platforms, we'd rather do straight i2c I/O.
487111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
48875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg,
489111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		unsigned char *value)
490111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
49114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct i2c_client *client = v4l2_get_subdevdata(sd);
4922bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	u8 data = reg;
4932bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	struct i2c_msg msg;
494111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	int ret;
495111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
4962bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	/*
4972bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	 * Send out the register address...
4982bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	 */
4992bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.addr = client->addr;
5002bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.flags = 0;
5012bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.len = 1;
5022bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.buf = &data;
5032bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	ret = i2c_transfer(client->adapter, &msg, 1);
5042bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	if (ret < 0) {
5052bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet		printk(KERN_ERR "Error %d on register write\n", ret);
5062bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet		return ret;
5072bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	}
5082bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	/*
5092bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	 * ...then read back the result.
5102bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	 */
5112bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.flags = I2C_M_RD;
5122bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	ret = i2c_transfer(client->adapter, &msg, 1);
513bca5c2c550f16d2dc2d21ffb7b4712bd0a7d32a9Andres Salomon	if (ret >= 0) {
5142bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet		*value = data;
515bca5c2c550f16d2dc2d21ffb7b4712bd0a7d32a9Andres Salomon		ret = 0;
516bca5c2c550f16d2dc2d21ffb7b4712bd0a7d32a9Andres Salomon	}
517111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return ret;
518111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
519111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
520111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
52175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg,
522111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		unsigned char value)
523111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
52414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct i2c_client *client = v4l2_get_subdevdata(sd);
5252bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	struct i2c_msg msg;
5262bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	unsigned char data[2] = { reg, value };
5272bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	int ret;
52814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil
5292bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.addr = client->addr;
5302bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.flags = 0;
5312bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.len = 2;
5322bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	msg.buf = data;
5332bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	ret = i2c_transfer(client->adapter, &msg, 1);
5342bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet	if (ret > 0)
5352bf7de48888fceed8d8e5cddd51f1d474bdbfae6Jonathan Corbet		ret = 0;
5366d77444aca298b43a88086be446f943cd0442ef7Jonathan Corbet	if (reg == REG_COM7 && (value & COM7_RESET))
53797693f9178ba83068f6b4e419a47ffd3d1a20897Jonathan Corbet		msleep(5);  /* Wait for reset to run */
5386d77444aca298b43a88086be446f943cd0442ef7Jonathan Corbet	return ret;
539111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
540111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
54175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
54275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		unsigned char *value)
54375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake{
54475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	struct ov7670_info *info = to_state(sd);
54575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	if (info->use_smbus)
54675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		return ov7670_read_smbus(sd, reg, value);
54775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	else
54875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		return ov7670_read_i2c(sd, reg, value);
54975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake}
55075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake
55175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drakestatic int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
55275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		unsigned char value)
55375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake{
55475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	struct ov7670_info *info = to_state(sd);
55575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	if (info->use_smbus)
55675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		return ov7670_write_smbus(sd, reg, value);
55775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	else
55875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		return ov7670_write_i2c(sd, reg, value);
55975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake}
560111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
561111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
562111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Write a list of register settings; ff/ff stops the process.
563111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
56414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
565111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
566111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	while (vals->reg_num != 0xff || vals->value != 0xff) {
56714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil		int ret = ov7670_write(sd, vals->reg_num, vals->value);
568111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		if (ret < 0)
569111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet			return ret;
570111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		vals++;
571111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	}
572111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return 0;
573111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
574111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
575111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
576111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
577111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Stuff that knows about the sensor.
578111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
57914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_reset(struct v4l2_subdev *sd, u32 val)
580111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
58114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ov7670_write(sd, REG_COM7, COM7_RESET);
582111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	msleep(1);
58314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return 0;
584111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
585111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
586111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
58714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_init(struct v4l2_subdev *sd, u32 val)
588111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
58914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return ov7670_write_array(sd, ov7670_default_regs);
590111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
591111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
592111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
593111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
59414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_detect(struct v4l2_subdev *sd)
595111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
596111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	unsigned char v;
597111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	int ret;
598111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
59914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_init(sd, 0);
600111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret < 0)
601111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return ret;
60214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_MIDH, &v);
603111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret < 0)
604111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return ret;
605111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (v != 0x7f) /* OV manuf. id. */
606111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return -ENODEV;
60714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_MIDL, &v);
608111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret < 0)
609111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return ret;
610111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (v != 0xa2)
611111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return -ENODEV;
612111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
613111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * OK, we know we have an OmniVision chip...but which one?
614111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
61514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_PID, &v);
616111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret < 0)
617111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return ret;
618111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
619111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return -ENODEV;
62014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_VER, &v);
621111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret < 0)
622111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return ret;
623111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
624111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return -ENODEV;
625111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return 0;
626111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
627111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
628111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
629f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/*
630f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Store information about the video data format.  The color matrix
631f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * is deeply tied into the format, so keep the relevant values here.
632959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil * The magic matrix numbers come from OmniVision.
633f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */
634111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic struct ov7670_format_struct {
635959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	enum v4l2_mbus_pixelcode mbus_code;
636959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	enum v4l2_colorspace colorspace;
637111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	struct regval_list *regs;
638f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int cmatrix[CMATRIX_LEN];
639111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet} ov7670_formats[] = {
640111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{
641959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.mbus_code	= V4L2_MBUS_FMT_YUYV8_2X8,
642959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.colorspace	= V4L2_COLORSPACE_JPEG,
643111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.regs 		= ov7670_fmt_yuv422,
644f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
645111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	},
646111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{
647959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.mbus_code	= V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
648959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.colorspace	= V4L2_COLORSPACE_SRGB,
649111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.regs		= ov7670_fmt_rgb444,
650f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
651111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	},
652111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{
653959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.mbus_code	= V4L2_MBUS_FMT_RGB565_2X8_LE,
654959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.colorspace	= V4L2_COLORSPACE_SRGB,
655111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.regs		= ov7670_fmt_rgb565,
656f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
657585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	},
658585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet	{
659959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.mbus_code	= V4L2_MBUS_FMT_SBGGR8_1X8,
660959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		.colorspace	= V4L2_COLORSPACE_SRGB,
661585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet		.regs 		= ov7670_fmt_raw,
662585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet		.cmatrix	= { 0, 0, 0, 0, 0, 0 },
663111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	},
664111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
665585553ecedabf434e5cdc59d05bf64596ceab7bcJonathan Corbet#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats)
666111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
667111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
668111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
669111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Then there is the issue of window sizes.  Try to capture the info here.
670111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
671f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
672f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/*
673f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * QCIF mode is done (by OV) in a very strange way - it actually looks like
674f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * VGA with weird scaling options - they do *not* use the canned QCIF mode
675f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * which is allegedly provided by the sensor.  So here's the weird register
676f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * settings.
677f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */
678f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic struct regval_list ov7670_qcif_regs[] = {
679f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ REG_COM3, COM3_SCALEEN|COM3_DCWEN },
680f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ REG_COM3, COM3_DCWEN },
681f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ REG_COM14, COM14_DCWEN | 0x01},
682f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x73, 0xf1 },
683f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0xa2, 0x52 },
684f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x7b, 0x1c },
685f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x7c, 0x28 },
686f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x7d, 0x3c },
687f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x7f, 0x69 },
688f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ REG_COM9, 0x38 },
689f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0xa1, 0x0b },
690f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x74, 0x19 },
691f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x9a, 0x80 },
692f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0x43, 0x14 },
693f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ REG_COM13, 0xc0 },
694f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{ 0xff, 0xff },
695f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet};
696f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
697d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstatic struct ov7670_win_size ov7670_win_sizes[] = {
698111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* VGA */
699111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{
700111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.width		= VGA_WIDTH,
701111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.height		= VGA_HEIGHT,
702111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.com7_bit	= COM7_FMT_VGA,
703d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstart		= 158,	/* These values from */
704d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstop		=  14,	/* Omnivision */
705111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.vstart		=  10,
706111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.vstop		= 490,
707d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.regs		= NULL,
708111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	},
709111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* CIF */
710111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{
711111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.width		= CIF_WIDTH,
712111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.height		= CIF_HEIGHT,
713111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.com7_bit	= COM7_FMT_CIF,
714d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstart		= 170,	/* Empirically determined */
715111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.hstop		=  90,
716111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.vstart		=  14,
717111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.vstop		= 494,
718d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.regs		= NULL,
719111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	},
720111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/* QVGA */
721111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	{
722111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.width		= QVGA_WIDTH,
723111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.height		= QVGA_HEIGHT,
724111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		.com7_bit	= COM7_FMT_QVGA,
725d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstart		= 168,	/* Empirically determined */
726dc4589c814a3a50a4cfc2077690fc7fd397308c8Daniel Drake		.hstop		=  24,
727dc4589c814a3a50a4cfc2077690fc7fd397308c8Daniel Drake		.vstart		=  12,
728dc4589c814a3a50a4cfc2077690fc7fd397308c8Daniel Drake		.vstop		= 492,
729d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.regs		= NULL,
730f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	},
731f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	/* QCIF */
732f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	{
733f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.width		= QCIF_WIDTH,
734f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.height		= QCIF_HEIGHT,
735f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.com7_bit	= COM7_FMT_VGA, /* see comment above */
736d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstart		= 456,	/* Empirically determined */
737f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.hstop		=  24,
738f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.vstart		=  14,
739f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		.vstop		= 494,
740d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.regs		= ov7670_qcif_regs,
741d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	}
742111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
743111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
744d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstatic struct ov7670_win_size ov7675_win_sizes[] = {
745d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	/*
746d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	 * Currently, only VGA is supported. Theoretically it could be possible
747d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	 * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a
748d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	 * base and tweak them empirically could be required.
749d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	 */
750d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	{
751d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.width		= VGA_WIDTH,
752d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.height		= VGA_HEIGHT,
753d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.com7_bit	= COM7_FMT_VGA,
754d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstart		= 158,	/* These values from */
755d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.hstop		=  14,	/* Omnivision */
756d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.vstart		=  14,  /* Empirically determined */
757d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.vstop		= 494,
758d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.regs		= NULL,
759d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	}
760d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin};
761111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
762f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic void ov7675_get_framerate(struct v4l2_subdev *sd,
763f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin				 struct v4l2_fract *tpf)
764f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{
765f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	struct ov7670_info *info = to_state(sd);
766f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	u32 clkrc = info->clkrc;
76704ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	int pll_factor;
76804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin
76904ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	if (info->pll_bypass)
77004ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		pll_factor = 1;
77104ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	else
77204ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		pll_factor = PLL_FACTOR;
773f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
774f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	clkrc++;
775f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8)
776f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		clkrc = (clkrc >> 1);
777f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
778f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf->numerator = 1;
779f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf->denominator = (5 * pll_factor * info->clock_speed) /
780f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin			(4 * clkrc);
781f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin}
782f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
783f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic int ov7675_set_framerate(struct v4l2_subdev *sd,
784f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin				 struct v4l2_fract *tpf)
785f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{
786f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	struct ov7670_info *info = to_state(sd);
787f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	u32 clkrc;
78804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	int pll_factor;
789f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	int ret;
790f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
791f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	/*
792f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 * The formula is fps = 5/4*pixclk for YUV/RGB and
793f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 * fps = 5/2*pixclk for RAW.
794f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 *
795f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 * pixclk = clock_speed / (clkrc + 1) * PLLfactor
796f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 *
797f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 */
79804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	if (info->pll_bypass) {
79904ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		pll_factor = 1;
80004ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS);
80104ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	} else {
80204ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		pll_factor = PLL_FACTOR;
80304ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		ret = ov7670_write(sd, REG_DBLV, DBLV_X4);
80404ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	}
80504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin	if (ret < 0)
80604ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		return ret;
80704ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin
808f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if (tpf->numerator == 0 || tpf->denominator == 0) {
809f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		clkrc = 0;
810f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	} else {
811f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) /
812f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin			(4 * tpf->denominator);
813f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8)
814f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin			clkrc = (clkrc << 1);
815f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		clkrc--;
816f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	}
817f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
818f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	/*
819f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 * The datasheet claims that clkrc = 0 will divide the input clock by 1
820f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 * but we've checked with an oscilloscope that it divides by 2 instead.
821f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 * So, if clkrc = 0 just bypass the divider.
822f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	 */
823f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if (clkrc <= 0)
824f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		clkrc = CLK_EXT;
825f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	else if (clkrc > CLK_SCALE)
826f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		clkrc = CLK_SCALE;
827f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	info->clkrc = clkrc;
828f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
829f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	/* Recalculate frame rate */
830f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	ov7675_get_framerate(sd, tpf);
831f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
832f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
833f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if (ret < 0)
834f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		return ret;
83504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin
836f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	return ov7670_write(sd, REG_DBLV, DBLV_X4);
837f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin}
838f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
839f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic void ov7670_get_framerate_legacy(struct v4l2_subdev *sd,
840f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin				 struct v4l2_fract *tpf)
841f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{
842f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	struct ov7670_info *info = to_state(sd);
843f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
844f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf->numerator = 1;
845f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf->denominator = info->clock_speed;
846f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1)
847f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		tpf->denominator /= (info->clkrc & CLK_SCALE);
848f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin}
849f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
850f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martinstatic int ov7670_set_framerate_legacy(struct v4l2_subdev *sd,
851f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin					struct v4l2_fract *tpf)
852f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin{
853f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	struct ov7670_info *info = to_state(sd);
854f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	int div;
855f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
856f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if (tpf->numerator == 0 || tpf->denominator == 0)
857f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		div = 1;  /* Reset to full rate */
858f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	else
859f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		div = (tpf->numerator * info->clock_speed) / tpf->denominator;
860f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	if (div == 0)
861f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		div = 1;
862f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	else if (div > CLK_SCALE)
863f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		div = CLK_SCALE;
864f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	info->clkrc = (info->clkrc & 0x80) | div;
865f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf->numerator = 1;
866f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf->denominator = info->clock_speed / div;
867f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	return ov7670_write(sd, REG_CLKRC, info->clkrc);
868f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin}
869f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
870111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
871111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Store a set of start/stop values into the camera.
872111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
87314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
874111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		int vstart, int vstop)
875111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
876111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	int ret;
877111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	unsigned char v;
878111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
879111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Horizontal: 11 bits, top 8 live in hstart and hstop.  Bottom 3 of
880111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * hstart are in href[2:0], bottom 3 of hstop in href[5:3].  There is
881111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * a mystery "edge offset" value in the top two bits of href.
882111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
88314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret =  ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
88414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
88514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_read(sd, REG_HREF, &v);
886111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
887111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	msleep(10);
88814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_HREF, v);
889111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
890111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Vertical: similar arrangement, but only 10 bits.
891111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
89214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
89314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
89414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_read(sd, REG_VREF, &v);
895111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
896111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	msleep(10);
89714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_VREF, v);
898111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return ret;
899111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
900111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
901111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
902959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuilstatic int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
903959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil					enum v4l2_mbus_pixelcode *code)
904959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil{
905959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	if (index >= N_OV7670_FMTS)
906959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		return -EINVAL;
907959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil
908959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	*code = ov7670_formats[index].mbus_code;
909959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	return 0;
910959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil}
911111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
91214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
913959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		struct v4l2_mbus_framefmt *fmt,
914111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		struct ov7670_format_struct **ret_fmt,
915111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		struct ov7670_win_size **ret_wsize)
916111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
917f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	int index, i;
918111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	struct ov7670_win_size *wsize;
919d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	struct ov7670_info *info = to_state(sd);
920d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	unsigned int n_win_sizes = info->devtype->n_win_sizes;
921f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	unsigned int win_sizes_limit = n_win_sizes;
922111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
923111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	for (index = 0; index < N_OV7670_FMTS; index++)
924959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		if (ov7670_formats[index].mbus_code == fmt->code)
925111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet			break;
926cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake	if (index >= N_OV7670_FMTS) {
927cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake		/* default to first format */
928cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake		index = 0;
929959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		fmt->code = ov7670_formats[0].mbus_code;
930cd257a6f4dd908d94e504d2431710f0fcfe62036Daniel Drake	}
931111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret_fmt != NULL)
932111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		*ret_fmt = ov7670_formats + index;
933111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
934111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * Fields: the OV devices claim to be progressive.
935111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
936959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	fmt->field = V4L2_FIELD_NONE;
937f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin
938f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	/*
939f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	 * Don't consider values that don't match min_height and min_width
940f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	 * constraints.
941f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	 */
942f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	if (info->min_width || info->min_height)
943f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin		for (i = 0; i < n_win_sizes; i++) {
944f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin			wsize = info->devtype->win_sizes + i;
945f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin
946f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin			if (wsize->width < info->min_width ||
947f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin				wsize->height < info->min_height) {
948f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin				win_sizes_limit = i;
949f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin				break;
950f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin			}
951f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin		}
952111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
953111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * Round requested image size down to the nearest
954111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * we support, but not below the smallest.
955111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
956d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	for (wsize = info->devtype->win_sizes;
957f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	     wsize < info->devtype->win_sizes + win_sizes_limit; wsize++)
958959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil		if (fmt->width >= wsize->width && fmt->height >= wsize->height)
959111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet			break;
960f748cd3ec8039a01d260ba0d51687afdf93c6e67Javier Martin	if (wsize >= info->devtype->win_sizes + win_sizes_limit)
961111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		wsize--;   /* Take the smallest one */
962111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret_wsize != NULL)
963111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		*ret_wsize = wsize;
964111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
965111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * Note the size we'll actually handle.
966111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
967959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	fmt->width = wsize->width;
968959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	fmt->height = wsize->height;
969959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	fmt->colorspace = ov7670_formats[index].colorspace;
970111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return 0;
971111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
972111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
973959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuilstatic int ov7670_try_mbus_fmt(struct v4l2_subdev *sd,
974959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil			    struct v4l2_mbus_framefmt *fmt)
97514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil{
97614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return ov7670_try_fmt_internal(sd, fmt, NULL, NULL);
97714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil}
97814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil
979111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
980111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Set a format.
981111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
982959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuilstatic int ov7670_s_mbus_fmt(struct v4l2_subdev *sd,
983959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil			  struct v4l2_mbus_framefmt *fmt)
984111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
985111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	struct ov7670_format_struct *ovfmt;
986111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	struct ov7670_win_size *wsize;
98714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct ov7670_info *info = to_state(sd);
988d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	unsigned char com7;
989959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	int ret;
990111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
99114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize);
992959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil
993111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (ret)
994111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return ret;
995111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
996111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * COM7 is a pain in the ass, it doesn't like to be read then
997111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * quickly written afterward.  But we have everything we need
998111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * to set it absolutely here, as long as the format-specific
999111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * register sets list it first.
1000111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
1001111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	com7 = ovfmt->regs[0].value;
1002111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	com7 |= wsize->com7_bit;
100314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ov7670_write(sd, REG_COM7, com7);
1004111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	/*
1005111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 * Now write the rest of the array.  Also store start/stops
1006111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	 */
100714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ov7670_write_array(sd, ovfmt->regs + 1);
100814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
1009111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet			wsize->vstop);
1010f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	ret = 0;
1011f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	if (wsize->regs)
101214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil		ret = ov7670_write_array(sd, wsize->regs);
1013f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	info->fmt = ovfmt;
1014edd75ede2d40eadb98e07d87e88fa970f86ffe9eJonathan Corbet
1015d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	/*
1016d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	 * If we're running RGB565, we must rewrite clkrc after setting
1017d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	 * the other parameters or the image looks poor.  If we're *not*
1018d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	 * doing RGB565, we must not rewrite clkrc or the image looks
1019d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	 * *really* poor.
1020a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet	 *
1021a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet	 * (Update) Now that we retain clkrc state, we should be able
1022a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet	 * to write it unconditionally, and that will make the frame
1023a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet	 * rate persistent too.
1024d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	 */
1025a8e68c37c846236499ac05e95af76dff2e9aa1ebJonathan Corbet	if (ret == 0)
1026d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet		ret = ov7670_write(sd, REG_CLKRC, info->clkrc);
1027959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	return 0;
1028959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil}
1029959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil
1030111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
1031c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet * Implement G/S_PARM.  There is a "high quality" mode we could try
1032c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet * to do someday; for now, we just do the frame rate tweak.
1033c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet */
103414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
1035c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet{
1036c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	struct v4l2_captureparm *cp = &parms->parm.capture;
1037d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	struct ov7670_info *info = to_state(sd);
1038c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet
1039c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1040c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet		return -EINVAL;
1041d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet
1042c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	memset(cp, 0, sizeof(struct v4l2_captureparm));
1043c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	cp->capability = V4L2_CAP_TIMEPERFRAME;
1044f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	info->devtype->get_framerate(sd, &cp->timeperframe);
1045f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
1046c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	return 0;
1047c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet}
1048c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet
104914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
1050c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet{
1051c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	struct v4l2_captureparm *cp = &parms->parm.capture;
1052c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	struct v4l2_fract *tpf = &cp->timeperframe;
1053d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet	struct ov7670_info *info = to_state(sd);
1054c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet
1055c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
1056c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet		return -EINVAL;
1057c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet	if (cp->extendedmode != 0)
1058c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet		return -EINVAL;
1059d8d201552f518370d0a64cb758684f667fdd2012Jonathan Corbet
1060f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	return info->devtype->set_framerate(sd, tpf);
1061c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet}
1062c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet
1063c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet
1064c8f5b2f5607e78c61df229259c539a5d9488a013Jonathan Corbet/*
1065e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * Frame intervals.  Since frame rates are controlled with the clock
1066e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * divider, we can only do 30/n for integer n values.  So no continuous
1067e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * or stepwise options.  Here we just pick a handful of logical values.
1068111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
1069111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1070e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbetstatic int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 };
1071f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1072e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbetstatic int ov7670_enum_frameintervals(struct v4l2_subdev *sd,
1073e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet		struct v4l2_frmivalenum *interval)
1074e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet{
1075e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet	if (interval->index >= ARRAY_SIZE(ov7670_frame_rates))
1076e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet		return -EINVAL;
1077e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet	interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
1078e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet	interval->discrete.numerator = 1;
1079e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet	interval->discrete.denominator = ov7670_frame_rates[interval->index];
1080e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet	return 0;
1081e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet}
1082f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1083e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet/*
1084b0326b7f8de020d70487673123bc93138c091151Daniel Drake * Frame size enumeration
1085b0326b7f8de020d70487673123bc93138c091151Daniel Drake */
1086b0326b7f8de020d70487673123bc93138c091151Daniel Drakestatic int ov7670_enum_framesizes(struct v4l2_subdev *sd,
1087b0326b7f8de020d70487673123bc93138c091151Daniel Drake		struct v4l2_frmsizeenum *fsize)
1088b0326b7f8de020d70487673123bc93138c091151Daniel Drake{
108975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	struct ov7670_info *info = to_state(sd);
109075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	int i;
109175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	int num_valid = -1;
1092b0326b7f8de020d70487673123bc93138c091151Daniel Drake	__u32 index = fsize->index;
1093d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	unsigned int n_win_sizes = info->devtype->n_win_sizes;
1094b0326b7f8de020d70487673123bc93138c091151Daniel Drake
109575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	/*
109675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	 * If a minimum width/height was requested, filter out the capture
109775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	 * windows that fall outside that.
109875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	 */
1099d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	for (i = 0; i < n_win_sizes; i++) {
1100322e6d19ede96ac6c13a132590c435ff17e0827fGuennadi Liakhovetski		struct ov7670_win_size *win = &info->devtype->win_sizes[i];
110175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		if (info->min_width && win->width < info->min_width)
110275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake			continue;
110375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		if (info->min_height && win->height < info->min_height)
110475e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake			continue;
110575e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		if (index == ++num_valid) {
110675e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake			fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
110775e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake			fsize->discrete.width = win->width;
110875e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake			fsize->discrete.height = win->height;
110975e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake			return 0;
111075e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake		}
111175e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	}
111275e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake
111375e2bdad8901a0b599e01a96229be922eef1e488Daniel Drake	return -EINVAL;
1114b0326b7f8de020d70487673123bc93138c091151Daniel Drake}
1115b0326b7f8de020d70487673123bc93138c091151Daniel Drake
1116b0326b7f8de020d70487673123bc93138c091151Daniel Drake/*
1117e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet * Code for dealing with controls.
1118e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet */
1119f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
112014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_store_cmatrix(struct v4l2_subdev *sd,
1121f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		int matrix[CMATRIX_LEN])
1122f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{
1123f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int i, ret;
1124e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil	unsigned char signbits = 0;
1125f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1126f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	/*
1127f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 * Weird crap seems to exist in the upper part of
1128f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 * the sign bits register, so let's preserve it.
1129f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 */
113014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
1131f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	signbits &= 0xc0;
1132f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1133f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	for (i = 0; i < CMATRIX_LEN; i++) {
1134f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		unsigned char raw;
1135f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1136f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		if (matrix[i] < 0) {
1137f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet			signbits |= (1 << i);
1138f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet			if (matrix[i] < -255)
1139f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet				raw = 0xff;
1140f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet			else
1141f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet				raw = (-1 * matrix[i]) & 0xff;
1142f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		}
1143f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		else {
1144f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet			if (matrix[i] > 255)
1145f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet				raw = 0xff;
1146f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet			else
1147f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet				raw = matrix[i] & 0xff;
1148f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		}
114914386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil		ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
1150f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	}
115114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
1152f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	return ret;
1153f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}
1154f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1155f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1156f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet/*
1157f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * Hue also requires messing with the color matrix.  It also requires
1158f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * trig functions, which tend not to be well supported in the kernel.
1159f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * So here is a simple table of sine values, 0-90 degrees, in steps
1160f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * of five degrees.  Values are multiplied by 1000.
1161f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet *
1162f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * The following naive approximate trig functions require an argument
1163f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet * carefully limited to -180 <= theta <= 180.
1164f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet */
1165f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet#define SIN_STEP 5
1166f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic const int ov7670_sin_table[] = {
1167f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	   0,	 87,   173,   258,   342,   422,
1168f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 499,	573,   642,   707,   766,   819,
1169f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 866,	906,   939,   965,   984,   996,
1170f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	1000
1171f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet};
1172f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1173f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic int ov7670_sine(int theta)
1174f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{
1175f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int chs = 1;
1176f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int sine;
1177f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1178f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	if (theta < 0) {
1179f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		theta = -theta;
1180f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		chs = -1;
1181f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	}
1182f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	if (theta <= 90)
1183f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		sine = ov7670_sin_table[theta/SIN_STEP];
1184f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	else {
1185f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		theta -= 90;
1186f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
1187f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	}
1188f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	return sine*chs;
1189f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}
1190f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1191f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic int ov7670_cosine(int theta)
1192f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{
1193f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	theta = 90 - theta;
1194f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	if (theta > 180)
1195f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		theta -= 360;
1196f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	else if (theta < -180)
1197f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		theta += 360;
1198f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	return ov7670_sine(theta);
1199f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}
1200f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1201f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1202f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1203f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1204f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbetstatic void ov7670_calc_cmatrix(struct ov7670_info *info,
1205492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		int matrix[CMATRIX_LEN], int sat, int hue)
1206f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{
1207f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int i;
1208f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	/*
1209f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 * Apply the current saturation setting first.
1210f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 */
1211f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	for (i = 0; i < CMATRIX_LEN; i++)
1212492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7;
1213f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	/*
1214f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 * Then, if need be, rotate the hue value.
1215f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	 */
1216492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	if (hue != 0) {
1217f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		int sinth, costh, tmpmatrix[CMATRIX_LEN];
1218f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1219f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
1220492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		sinth = ov7670_sine(hue);
1221492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		costh = ov7670_cosine(hue);
1222f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1223f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
1224f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
1225f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
1226f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
1227f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
1228f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet		matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
1229f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	}
1230f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}
1231f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1232f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1233f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1234492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue)
1235f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet{
123614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct ov7670_info *info = to_state(sd);
1237f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int matrix[CMATRIX_LEN];
1238f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	int ret;
1239f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1240492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	ov7670_calc_cmatrix(info, matrix, sat, hue);
124114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_store_cmatrix(sd, matrix);
1242f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	return ret;
1243f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet}
1244f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1245f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet
1246111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet/*
1247111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet * Some weird registers seem to store values in a sign/magnitude format!
1248111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet */
1249111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1250111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbetstatic unsigned char ov7670_abs_to_sm(unsigned char v)
1251111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1252111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (v > 127)
1253111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return v & 0x7f;
125414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return (128 - v) | 0x80;
1255111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1256111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1257ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
1258111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1259e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil	unsigned char com8 = 0, v;
1260111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	int ret;
1261111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
126214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ov7670_read(sd, REG_COM8, &com8);
1263111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	com8 &= ~COM8_AEC;
126414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ov7670_write(sd, REG_COM8, com8);
1265f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	v = ov7670_abs_to_sm(value);
126614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_write(sd, REG_BRIGHT, v);
1267111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return ret;
1268111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1269111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1270ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
1271111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
127214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
1273111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1274111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1275ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
1276111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1277e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil	unsigned char v = 0;
1278111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	int ret;
1279111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
128014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_MVFP, &v);
1281111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (value)
1282111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		v |= MVFP_MIRROR;
1283111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	else
1284111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		v &= ~MVFP_MIRROR;
1285111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	msleep(10);  /* FIXME */
128614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_MVFP, v);
1287111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return ret;
1288111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1289111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1290ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuilstatic int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
1291111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1292e3bf20de33b016ac73424a5574177ed46c754be3Hans Verkuil	unsigned char v = 0;
1293111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	int ret;
1294111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
129514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret = ov7670_read(sd, REG_MVFP, &v);
1296111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	if (value)
1297111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		v |= MVFP_FLIP;
1298111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	else
1299111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		v &= ~MVFP_FLIP;
1300111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	msleep(10);  /* FIXME */
130114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	ret += ov7670_write(sd, REG_MVFP, v);
1302111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return ret;
1303111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1304111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
130581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet/*
130681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * GAIN is split between REG_GAIN and REG_VREF[7:6].  If one believes
130781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * the data sheet, the VREF parts should be the most significant, but
130881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * experience shows otherwise.  There seems to be little value in
130981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * messing with the VREF bits, so we leave them alone.
131081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet */
131181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbetstatic int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value)
131281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet{
131381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	int ret;
131481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	unsigned char gain;
131581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
131681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	ret = ov7670_read(sd, REG_GAIN, &gain);
131781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	*value = gain;
131881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	return ret;
131981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet}
132081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
132181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbetstatic int ov7670_s_gain(struct v4l2_subdev *sd, int value)
132281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet{
132381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	int ret;
132481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	unsigned char com8;
132581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
132681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	ret = ov7670_write(sd, REG_GAIN, value & 0xff);
132781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	/* Have to turn off AGC as well */
132881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	if (ret == 0) {
132981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet		ret = ov7670_read(sd, REG_COM8, &com8);
133081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC);
133181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	}
133281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	return ret;
133381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet}
133481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
133581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet/*
133681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet * Tweak autogain.
133781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet */
133881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbetstatic int ov7670_s_autogain(struct v4l2_subdev *sd, int value)
133981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet{
134081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	int ret;
134181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	unsigned char com8;
134281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
134381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	ret = ov7670_read(sd, REG_COM8, &com8);
134481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	if (ret == 0) {
134581898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet		if (value)
134681898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet			com8 |= COM8_AGC;
134781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet		else
134881898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet			com8 &= ~COM8_AGC;
134981898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet		ret = ov7670_write(sd, REG_COM8, com8);
135081898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	}
135181898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	return ret;
135281898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet}
135381898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
1354364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbetstatic int ov7670_s_exp(struct v4l2_subdev *sd, int value)
1355364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet{
1356364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	int ret;
1357364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	unsigned char com1, com8, aech, aechh;
1358364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet
1359364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	ret = ov7670_read(sd, REG_COM1, &com1) +
1360364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		ov7670_read(sd, REG_COM8, &com8);
1361364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		ov7670_read(sd, REG_AECHH, &aechh);
1362364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	if (ret)
1363364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		return ret;
1364364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet
1365364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	com1 = (com1 & 0xfc) | (value & 0x03);
1366364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	aech = (value >> 2) & 0xff;
1367364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f);
1368364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	ret = ov7670_write(sd, REG_COM1, com1) +
1369364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		ov7670_write(sd, REG_AECH, aech) +
1370364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		ov7670_write(sd, REG_AECHH, aechh);
1371364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	/* Have to turn off AEC as well */
1372364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	if (ret == 0)
1373364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC);
1374364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	return ret;
1375364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet}
1376364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet
1377364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet/*
1378364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet * Tweak autoexposure.
1379364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet */
1380364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbetstatic int ov7670_s_autoexp(struct v4l2_subdev *sd,
1381364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		enum v4l2_exposure_auto_type value)
1382364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet{
1383364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	int ret;
1384364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	unsigned char com8;
1385364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet
1386364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	ret = ov7670_read(sd, REG_COM8, &com8);
1387364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	if (ret == 0) {
1388364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		if (value == V4L2_EXPOSURE_AUTO)
1389364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet			com8 |= COM8_AEC;
1390364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		else
1391364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet			com8 &= ~COM8_AEC;
1392364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet		ret = ov7670_write(sd, REG_COM8, com8);
1393364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	}
1394364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	return ret;
1395364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet}
1396364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet
139781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet
1398492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
1399111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1400492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct v4l2_subdev *sd = to_sd(ctrl);
1401492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct ov7670_info *info = to_state(sd);
1402111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1403ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	switch (ctrl->id) {
140481898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	case V4L2_CID_AUTOGAIN:
1405492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_g_gain(sd, &info->gain->val);
1406ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	}
1407ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	return -EINVAL;
1408111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1409111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1410492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic int ov7670_s_ctrl(struct v4l2_ctrl *ctrl)
1411111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1412492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct v4l2_subdev *sd = to_sd(ctrl);
1413492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct ov7670_info *info = to_state(sd);
1414492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin
1415ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	switch (ctrl->id) {
1416ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	case V4L2_CID_BRIGHTNESS:
1417492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_brightness(sd, ctrl->val);
1418ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	case V4L2_CID_CONTRAST:
1419492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_contrast(sd, ctrl->val);
1420ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	case V4L2_CID_SATURATION:
1421492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_sat_hue(sd,
1422492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin				info->saturation->val, info->hue->val);
1423ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	case V4L2_CID_VFLIP:
1424492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_vflip(sd, ctrl->val);
1425ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	case V4L2_CID_HFLIP:
1426492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_hflip(sd, ctrl->val);
142781898671247a6cfa6bfd6a32faee18b3999b6610Jonathan Corbet	case V4L2_CID_AUTOGAIN:
1428492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		/* Only set manual gain if auto gain is not explicitly
1429492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		   turned on. */
1430492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		if (!ctrl->val) {
1431492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			/* ov7670_s_gain turns off auto gain */
1432492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			return ov7670_s_gain(sd, info->gain->val);
1433492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		}
1434492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_autogain(sd, ctrl->val);
1435364e93372fb21ef5de18d0122c78789f065ddbf5Jonathan Corbet	case V4L2_CID_EXPOSURE_AUTO:
1436492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		/* Only set manual exposure if auto exposure is not explicitly
1437492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		   turned on. */
1438492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
1439492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			/* ov7670_s_exp turns off auto exposure */
1440492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			return ov7670_s_exp(sd, info->exposure->val);
1441492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		}
1442492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return ov7670_s_autoexp(sd, ctrl->val);
1443ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	}
1444ca07561ac70b00b5c2b5af727b3d0b6a4f91bee2Hans Verkuil	return -EINVAL;
1445111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1446111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1447492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martinstatic const struct v4l2_ctrl_ops ov7670_ctrl_ops = {
1448492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	.s_ctrl = ov7670_s_ctrl,
1449492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	.g_volatile_ctrl = ov7670_g_volatile_ctrl,
1450492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin};
1451492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin
1452b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#ifdef CONFIG_VIDEO_ADV_DEBUG
1453b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuilstatic int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
1454b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil{
1455b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	unsigned char val = 0;
1456b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	int ret;
1457b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil
1458b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	ret = ov7670_read(sd, reg->reg & 0xff, &val);
1459b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	reg->val = val;
1460b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	reg->size = 1;
1461b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	return ret;
1462b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil}
1463b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil
1464977ba3b1b73f24fae2d0c8bd59d7a4696f1e0cccHans Verkuilstatic int ov7670_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
1465b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil{
1466b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
1467b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	return 0;
1468b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil}
1469b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#endif
1470b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil
147114386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil/* ----------------------------------------------------------------------- */
1472111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
147314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct v4l2_subdev_core_ops ov7670_core_ops = {
147414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	.reset = ov7670_reset,
147514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	.init = ov7670_init,
1476b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#ifdef CONFIG_VIDEO_ADV_DEBUG
1477b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	.g_register = ov7670_g_register,
1478b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil	.s_register = ov7670_s_register,
1479b794aabff01a704df5c0bcf6537e6a7343a08465Hans Verkuil#endif
148014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil};
1481111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
148214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct v4l2_subdev_video_ops ov7670_video_ops = {
1483959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	.enum_mbus_fmt = ov7670_enum_mbus_fmt,
1484959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	.try_mbus_fmt = ov7670_try_mbus_fmt,
1485959f3bdadfefb59e27c53c2e9ab13192119b0e3cHans Verkuil	.s_mbus_fmt = ov7670_s_mbus_fmt,
148614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	.s_parm = ov7670_s_parm,
148714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	.g_parm = ov7670_g_parm,
1488e99dfcf7f68d8dffccfa795d1548790cee2d7395Jonathan Corbet	.enum_frameintervals = ov7670_enum_frameintervals,
1489b0326b7f8de020d70487673123bc93138c091151Daniel Drake	.enum_framesizes = ov7670_enum_framesizes,
149014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil};
1491111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
149214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct v4l2_subdev_ops ov7670_ops = {
149314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	.core = &ov7670_core_ops,
149414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	.video = &ov7670_video_ops,
149514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil};
1496111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
149714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil/* ----------------------------------------------------------------------- */
1498111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1499d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martinstatic const struct ov7670_devtype ov7670_devdata[] = {
1500d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	[MODEL_OV7670] = {
1501d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.win_sizes = ov7670_win_sizes,
1502d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.n_win_sizes = ARRAY_SIZE(ov7670_win_sizes),
1503f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		.set_framerate = ov7670_set_framerate_legacy,
1504f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		.get_framerate = ov7670_get_framerate_legacy,
1505d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	},
1506d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	[MODEL_OV7675] = {
1507d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.win_sizes = ov7675_win_sizes,
1508d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin		.n_win_sizes = ARRAY_SIZE(ov7675_win_sizes),
1509f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		.set_framerate = ov7675_set_framerate,
1510f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin		.get_framerate = ov7675_get_framerate,
1511d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	},
1512d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin};
1513d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin
151414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_probe(struct i2c_client *client,
151514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil			const struct i2c_device_id *id)
1516111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
1517f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	struct v4l2_fract tpf;
151814386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct v4l2_subdev *sd;
1519f9a7615686a854cb94b5252e66b836a0a539ad9eJonathan Corbet	struct ov7670_info *info;
15203c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	int ret;
1521111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1522c02b211df6fc54e51ee554c27a6736a11255a764Laurent Pinchart	info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
152314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	if (info == NULL)
1524111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet		return -ENOMEM;
152514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	sd = &info->sd;
152614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
152714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil
15283c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	info->clock_speed = 30; /* default: a guess */
15293c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	if (client->dev.platform_data) {
15303c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		struct ov7670_config *config = client->dev.platform_data;
15313c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil
15323c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		/*
15333c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		 * Must apply configuration before initializing device, because it
15343c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		 * selects I/O method.
15353c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		 */
15363c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		info->min_width = config->min_width;
15373c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		info->min_height = config->min_height;
15383c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		info->use_smbus = config->use_smbus;
15393c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil
15403c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		if (config->clock_speed)
15413c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil			info->clock_speed = config->clock_speed;
154204ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin
154304ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		/*
154404ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		 * It should be allowed for ov7670 too when it is migrated to
154504ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		 * the new frame rate formula.
154604ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		 */
154704ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin		if (config->pll_bypass && id->driver_data != MODEL_OV7670)
154804ee6d92047e1ac68d4eb615119343f4f0fc57dbJavier Martin			info->pll_bypass = true;
1549ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin
1550ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin		if (config->pclk_hb_disable)
1551ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin			info->pclk_hb_disable = true;
15523c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	}
15533c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil
15543c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	/* Make sure it's an ov7670 */
15553c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	ret = ov7670_detect(sd);
15563c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	if (ret) {
15573c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		v4l_dbg(1, debug, client,
15583c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil			"chip found @ 0x%x (%s) is not an ov7670 chip.\n",
15593c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil			client->addr << 1, client->adapter->name);
15603c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil		return ret;
15613c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	}
15623c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	v4l_info(client, "chip found @ 0x%02x (%s)\n",
15633c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil			client->addr << 1, client->adapter->name);
15643c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil
1565d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	info->devtype = &ov7670_devdata[id->driver_data];
15663c7c9370fb645f4713e0fbbe69425d8db9b47a13Hans Verkuil	info->fmt = &ov7670_formats[0];
1567f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	info->clkrc = 0;
1568f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
1569f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	/* Set default frame rate to 30 fps */
1570f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf.numerator = 1;
1571f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	tpf.denominator = 30;
1572f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin	info->devtype->set_framerate(sd, &tpf);
1573f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5Javier Martin
1574ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin	if (info->pclk_hb_disable)
1575ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin		ov7670_write(sd, REG_COM10, COM10_PCLK_HB);
1576ee95258ed3926f3aa2cf8d62e62cd51be466fe26Javier Martin
1577492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_handler_init(&info->hdl, 10);
1578492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1579492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
1580492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1581492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_CONTRAST, 0, 127, 1, 64);
1582492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1583492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_VFLIP, 0, 1, 1, 0);
1584492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1585492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_HFLIP, 0, 1, 1, 0);
1586492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1587492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_SATURATION, 0, 256, 1, 128);
1588492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1589492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_HUE, -180, 180, 5, 0);
1590492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1591492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_GAIN, 0, 255, 1, 128);
1592492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1593492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
1594492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops,
1595492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_EXPOSURE, 0, 65535, 1, 500);
1596492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops,
1597492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
1598492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			V4L2_EXPOSURE_AUTO);
1599492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	sd->ctrl_handler = &info->hdl;
1600492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	if (info->hdl.error) {
1601492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		int err = info->hdl.error;
1602492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin
1603492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		v4l2_ctrl_handler_free(&info->hdl);
1604492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin		return err;
1605492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	}
1606492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	/*
1607492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	 * We have checked empirically that hw allows to read back the gain
1608492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	 * value chosen by auto gain but that's not the case for auto exposure.
1609492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	 */
1610492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true);
1611492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_auto_cluster(2, &info->auto_exposure,
1612492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin			       V4L2_EXPOSURE_MANUAL, false);
1613492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_cluster(2, &info->saturation);
1614492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_handler_setup(&info->hdl);
1615492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin
1616111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet	return 0;
1617111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1618111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
1619111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
162014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic int ov7670_remove(struct i2c_client *client)
1621111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet{
162214386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	struct v4l2_subdev *sd = i2c_get_clientdata(client);
1623492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	struct ov7670_info *info = to_state(sd);
1624111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
162514386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	v4l2_device_unregister_subdev(sd);
1626492959c77f66c2238298115f4fabf1bb9ca997ebJavier Martin	v4l2_ctrl_handler_free(&info->hdl);
162714386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	return 0;
1628111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet}
1629111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet
163014386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuilstatic const struct i2c_device_id ov7670_id[] = {
1631d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	{ "ov7670", MODEL_OV7670 },
1632d058e23704ad7e0b6876a94b0d8428dcef510b49Javier Martin	{ "ov7675", MODEL_OV7675 },
163314386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil	{ }
163414386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil};
163514386c2b7793652a656021a3345cff3b0f6771f9Hans VerkuilMODULE_DEVICE_TABLE(i2c, ov7670_id);
163614386c2b7793652a656021a3345cff3b0f6771f9Hans Verkuil
1637ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuilstatic struct i2c_driver ov7670_driver = {
1638ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil	.driver = {
1639ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil		.owner	= THIS_MODULE,
1640ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil		.name	= "ov7670",
1641ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil	},
1642ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil	.probe		= ov7670_probe,
1643ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil	.remove		= ov7670_remove,
1644ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil	.id_table	= ov7670_id,
1645111f33564e19b2b5f70e3df9a8f92c08c1c91fd9Jonathan Corbet};
1646ef2ac770cd69909f6387d8a9e045cb41b46aedcfHans Verkuil
1647c6e8d86fffd8edf1bfccbd441b1812ee919fe3d5Axel Linmodule_i2c_driver(ov7670_driver);
1648