10c0d06caSMauro Carvalho Chehab /*
20c0d06caSMauro Carvalho Chehab  * Driver for the ov7660 sensor
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  * Copyright (C) 2009 Erik Andrén
50c0d06caSMauro Carvalho Chehab  * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
60c0d06caSMauro Carvalho Chehab  * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
70c0d06caSMauro Carvalho Chehab  *
80c0d06caSMauro Carvalho Chehab  * Portions of code to USB interface and ALi driver software,
90c0d06caSMauro Carvalho Chehab  * Copyright (c) 2006 Willem Duinker
100c0d06caSMauro Carvalho Chehab  * v4l2 interface modeled after the V4L2 driver
110c0d06caSMauro Carvalho Chehab  * for SN9C10x PC Camera Controllers
120c0d06caSMauro Carvalho Chehab  *
130c0d06caSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
140c0d06caSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License as
150c0d06caSMauro Carvalho Chehab  * published by the Free Software Foundation, version 2.
160c0d06caSMauro Carvalho Chehab  *
170c0d06caSMauro Carvalho Chehab  */
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
200c0d06caSMauro Carvalho Chehab 
210c0d06caSMauro Carvalho Chehab #include "m5602_ov7660.h"
220c0d06caSMauro Carvalho Chehab 
23c84e412fSHans de Goede static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl);
24c84e412fSHans de Goede static void ov7660_dump_registers(struct sd *sd);
250c0d06caSMauro Carvalho Chehab 
26123818eeSMauro Carvalho Chehab static const unsigned char preinit_ov7660[][4] = {
27123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
28123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
29123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
30123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
31123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
32123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
33123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
34123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
35123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x03},
36123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
37123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
38123818eeSMauro Carvalho Chehab 
39123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_OFON, 0x0c},
40123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM2, 0x11},
41123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM7, 0x05},
42123818eeSMauro Carvalho Chehab 
43123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
44123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
45123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
46123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
47123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
48123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
49123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
50123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
51123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
52123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
53123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
54123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
55123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
56123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
57123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00}
58123818eeSMauro Carvalho Chehab };
59123818eeSMauro Carvalho Chehab 
60123818eeSMauro Carvalho Chehab static const unsigned char init_ov7660[][4] = {
61123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
62123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
63123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
64123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
65123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
66123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d},
67123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
68123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
69123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
70123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
71123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
72123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
73123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
74123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
75123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
76123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
77123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
78123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM7, 0x80},
79123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_CLKRC, 0x80},
80123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM9, 0x4c},
81123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_OFON, 0x43},
82123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM12, 0x28},
83123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM8, 0x00},
84123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM10, 0x40},
85123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_HSTART, 0x0c},
86123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_HSTOP, 0x61},
87123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_HREF, 0xa4},
88123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_PSHFT, 0x0b},
89123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_VSTART, 0x01},
90123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_VSTOP, 0x7a},
91123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_VSTOP, 0x00},
92123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM7, 0x05},
93123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM6, 0x42},
94123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_BBIAS, 0x94},
95123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_GbBIAS, 0x94},
96123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_RSVD29, 0x94},
97123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_RBIAS, 0x94},
98123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM1, 0x00},
99123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_AECH, 0x00},
100123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_AECHH, 0x00},
101123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_ADC, 0x05},
102123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM13, 0x00},
103123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_RSVDA1, 0x23},
104123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_TSLB, 0x0d},
105123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_HV, 0x80},
106123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_LCC1, 0x00},
107123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_LCC2, 0x00},
108123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_LCC3, 0x10},
109123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_LCC4, 0x40},
110123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_LCC5, 0x01},
111123818eeSMauro Carvalho Chehab 
112123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_AECH, 0x20},
113123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM1, 0x00},
114123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_OFON, 0x0c},
115123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM2, 0x11},
116123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM7, 0x05},
117123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
118123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
119123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
120123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
121123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
122123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
123123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
124123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
125123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
126123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
127123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
128123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
129123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
130123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
131123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
132123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_AECH, 0x5f},
133123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM1, 0x03},
134123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_OFON, 0x0c},
135123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM2, 0x11},
136123818eeSMauro Carvalho Chehab 	{SENSOR, OV7660_COM7, 0x05},
137123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x01},
138123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
139123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
140123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
141123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00},
142123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08},
143123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
144123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
145123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
146123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
147123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
148123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
149123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
150123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
151123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
152123818eeSMauro Carvalho Chehab 
153123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06},
154123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
155123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
156123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
157123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81},
158123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82},
159123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x01},
160123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
161123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x08},
162123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
163123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
164123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01},
165123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0xec},
166123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
167123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00},
168123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x00},
169123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x02},
170123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00},
171123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x27},
172123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02},
173123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_HSYNC_PARA, 0xa7},
174123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SIG_INI, 0x00},
175123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
176123818eeSMauro Carvalho Chehab 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
177123818eeSMauro Carvalho Chehab };
178123818eeSMauro Carvalho Chehab 
1790c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format ov7660_modes[] = {
1800c0d06caSMauro Carvalho Chehab 	{
1810c0d06caSMauro Carvalho Chehab 		640,
1820c0d06caSMauro Carvalho Chehab 		480,
1830c0d06caSMauro Carvalho Chehab 		V4L2_PIX_FMT_SBGGR8,
1840c0d06caSMauro Carvalho Chehab 		V4L2_FIELD_NONE,
1850c0d06caSMauro Carvalho Chehab 		.sizeimage =
1860c0d06caSMauro Carvalho Chehab 			640 * 480,
1870c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
1880c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
1890c0d06caSMauro Carvalho Chehab 		.priv = 0
1900c0d06caSMauro Carvalho Chehab 	}
1910c0d06caSMauro Carvalho Chehab };
1920c0d06caSMauro Carvalho Chehab 
193c84e412fSHans de Goede static const struct v4l2_ctrl_ops ov7660_ctrl_ops = {
194c84e412fSHans de Goede 	.s_ctrl = ov7660_s_ctrl,
195c84e412fSHans de Goede };
1960c0d06caSMauro Carvalho Chehab 
1970c0d06caSMauro Carvalho Chehab int ov7660_probe(struct sd *sd)
1980c0d06caSMauro Carvalho Chehab {
1990c0d06caSMauro Carvalho Chehab 	int err = 0, i;
2000c0d06caSMauro Carvalho Chehab 	u8 prod_id = 0, ver_id = 0;
2010c0d06caSMauro Carvalho Chehab 
2020c0d06caSMauro Carvalho Chehab 	if (force_sensor) {
2030c0d06caSMauro Carvalho Chehab 		if (force_sensor == OV7660_SENSOR) {
2040c0d06caSMauro Carvalho Chehab 			pr_info("Forcing an %s sensor\n", ov7660.name);
2050c0d06caSMauro Carvalho Chehab 			goto sensor_found;
2060c0d06caSMauro Carvalho Chehab 		}
2070c0d06caSMauro Carvalho Chehab 		/* If we want to force another sensor,
2080c0d06caSMauro Carvalho Chehab 		don't try to probe this one */
2090c0d06caSMauro Carvalho Chehab 		return -ENODEV;
2100c0d06caSMauro Carvalho Chehab 	}
2110c0d06caSMauro Carvalho Chehab 
2120c0d06caSMauro Carvalho Chehab 	/* Do the preinit */
2130c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) {
2140c0d06caSMauro Carvalho Chehab 		u8 data[2];
2150c0d06caSMauro Carvalho Chehab 
2160c0d06caSMauro Carvalho Chehab 		if (preinit_ov7660[i][0] == BRIDGE) {
2170c0d06caSMauro Carvalho Chehab 			err = m5602_write_bridge(sd,
2180c0d06caSMauro Carvalho Chehab 				preinit_ov7660[i][1],
2190c0d06caSMauro Carvalho Chehab 				preinit_ov7660[i][2]);
2200c0d06caSMauro Carvalho Chehab 		} else {
2210c0d06caSMauro Carvalho Chehab 			data[0] = preinit_ov7660[i][2];
2220c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
2230c0d06caSMauro Carvalho Chehab 				preinit_ov7660[i][1], data, 1);
2240c0d06caSMauro Carvalho Chehab 		}
2250c0d06caSMauro Carvalho Chehab 	}
2260c0d06caSMauro Carvalho Chehab 	if (err < 0)
2270c0d06caSMauro Carvalho Chehab 		return err;
2280c0d06caSMauro Carvalho Chehab 
2290c0d06caSMauro Carvalho Chehab 	if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1))
2300c0d06caSMauro Carvalho Chehab 		return -ENODEV;
2310c0d06caSMauro Carvalho Chehab 
2320c0d06caSMauro Carvalho Chehab 	if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1))
2330c0d06caSMauro Carvalho Chehab 		return -ENODEV;
2340c0d06caSMauro Carvalho Chehab 
2350c0d06caSMauro Carvalho Chehab 	pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id);
2360c0d06caSMauro Carvalho Chehab 
2370c0d06caSMauro Carvalho Chehab 	if ((prod_id == 0x76) && (ver_id == 0x60)) {
2380c0d06caSMauro Carvalho Chehab 		pr_info("Detected a ov7660 sensor\n");
2390c0d06caSMauro Carvalho Chehab 		goto sensor_found;
2400c0d06caSMauro Carvalho Chehab 	}
2410c0d06caSMauro Carvalho Chehab 	return -ENODEV;
2420c0d06caSMauro Carvalho Chehab 
2430c0d06caSMauro Carvalho Chehab sensor_found:
2440c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.cam_mode = ov7660_modes;
2450c0d06caSMauro Carvalho Chehab 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes);
2460c0d06caSMauro Carvalho Chehab 
2470c0d06caSMauro Carvalho Chehab 	return 0;
2480c0d06caSMauro Carvalho Chehab }
2490c0d06caSMauro Carvalho Chehab 
2500c0d06caSMauro Carvalho Chehab int ov7660_init(struct sd *sd)
2510c0d06caSMauro Carvalho Chehab {
2529dc033f1SMauro Carvalho Chehab 	int i, err;
2530c0d06caSMauro Carvalho Chehab 
2540c0d06caSMauro Carvalho Chehab 	/* Init the sensor */
2550c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) {
2560c0d06caSMauro Carvalho Chehab 		u8 data[2];
2570c0d06caSMauro Carvalho Chehab 
2580c0d06caSMauro Carvalho Chehab 		if (init_ov7660[i][0] == BRIDGE) {
2590c0d06caSMauro Carvalho Chehab 			err = m5602_write_bridge(sd,
2600c0d06caSMauro Carvalho Chehab 				init_ov7660[i][1],
2610c0d06caSMauro Carvalho Chehab 				init_ov7660[i][2]);
2620c0d06caSMauro Carvalho Chehab 		} else {
2630c0d06caSMauro Carvalho Chehab 			data[0] = init_ov7660[i][2];
2640c0d06caSMauro Carvalho Chehab 			err = m5602_write_sensor(sd,
2650c0d06caSMauro Carvalho Chehab 				init_ov7660[i][1], data, 1);
2660c0d06caSMauro Carvalho Chehab 		}
2679dc033f1SMauro Carvalho Chehab 		if (err < 0)
2689dc033f1SMauro Carvalho Chehab 			return err;
2690c0d06caSMauro Carvalho Chehab 	}
2700c0d06caSMauro Carvalho Chehab 
2710c0d06caSMauro Carvalho Chehab 	if (dump_sensor)
2720c0d06caSMauro Carvalho Chehab 		ov7660_dump_registers(sd);
2730c0d06caSMauro Carvalho Chehab 
274c84e412fSHans de Goede 	return 0;
275c84e412fSHans de Goede }
2760c0d06caSMauro Carvalho Chehab 
277c84e412fSHans de Goede int ov7660_init_controls(struct sd *sd)
278c84e412fSHans de Goede {
279c84e412fSHans de Goede 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
2800c0d06caSMauro Carvalho Chehab 
281c84e412fSHans de Goede 	sd->gspca_dev.vdev.ctrl_handler = hdl;
282c84e412fSHans de Goede 	v4l2_ctrl_handler_init(hdl, 6);
2830c0d06caSMauro Carvalho Chehab 
284c84e412fSHans de Goede 	v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
285c84e412fSHans de Goede 			  0, 1, 1, 1);
286c84e412fSHans de Goede 	v4l2_ctrl_new_std_menu(hdl, &ov7660_ctrl_ops,
287c84e412fSHans de Goede 			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);
2880c0d06caSMauro Carvalho Chehab 
289c84e412fSHans de Goede 	sd->autogain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops,
290c84e412fSHans de Goede 					 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
291c84e412fSHans de Goede 	sd->gain = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_GAIN, 0,
292c84e412fSHans de Goede 				     255, 1, OV7660_DEFAULT_GAIN);
2930c0d06caSMauro Carvalho Chehab 
294c84e412fSHans de Goede 	sd->hflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_HFLIP,
295c84e412fSHans de Goede 				      0, 1, 1, 0);
296c84e412fSHans de Goede 	sd->vflip = v4l2_ctrl_new_std(hdl, &ov7660_ctrl_ops, V4L2_CID_VFLIP,
297c84e412fSHans de Goede 				      0, 1, 1, 0);
298c84e412fSHans de Goede 
299c84e412fSHans de Goede 	if (hdl->error) {
300c84e412fSHans de Goede 		pr_err("Could not initialize controls\n");
301c84e412fSHans de Goede 		return hdl->error;
302c84e412fSHans de Goede 	}
303c84e412fSHans de Goede 
304c84e412fSHans de Goede 	v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
305c84e412fSHans de Goede 	v4l2_ctrl_cluster(2, &sd->hflip);
306c84e412fSHans de Goede 
307c84e412fSHans de Goede 	return 0;
3080c0d06caSMauro Carvalho Chehab }
3090c0d06caSMauro Carvalho Chehab 
3100c0d06caSMauro Carvalho Chehab int ov7660_start(struct sd *sd)
3110c0d06caSMauro Carvalho Chehab {
3120c0d06caSMauro Carvalho Chehab 	return 0;
3130c0d06caSMauro Carvalho Chehab }
3140c0d06caSMauro Carvalho Chehab 
3150c0d06caSMauro Carvalho Chehab int ov7660_stop(struct sd *sd)
3160c0d06caSMauro Carvalho Chehab {
3170c0d06caSMauro Carvalho Chehab 	return 0;
3180c0d06caSMauro Carvalho Chehab }
3190c0d06caSMauro Carvalho Chehab 
3200c0d06caSMauro Carvalho Chehab void ov7660_disconnect(struct sd *sd)
3210c0d06caSMauro Carvalho Chehab {
3220c0d06caSMauro Carvalho Chehab 	ov7660_stop(sd);
3230c0d06caSMauro Carvalho Chehab 
3240c0d06caSMauro Carvalho Chehab 	sd->sensor = NULL;
3250c0d06caSMauro Carvalho Chehab }
3260c0d06caSMauro Carvalho Chehab 
3270c0d06caSMauro Carvalho Chehab static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val)
3280c0d06caSMauro Carvalho Chehab {
3290c0d06caSMauro Carvalho Chehab 	int err;
330c84e412fSHans de Goede 	u8 i2c_data = val;
3310c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3320c0d06caSMauro Carvalho Chehab 
333c93396e1STheodore Kilgore 	PDEBUG(D_CONF, "Setting gain to %d", val);
3340c0d06caSMauro Carvalho Chehab 
3350c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1);
3360c0d06caSMauro Carvalho Chehab 	return err;
3370c0d06caSMauro Carvalho Chehab }
3380c0d06caSMauro Carvalho Chehab 
3390c0d06caSMauro Carvalho Chehab static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev,
3400c0d06caSMauro Carvalho Chehab 					 __s32 val)
3410c0d06caSMauro Carvalho Chehab {
3420c0d06caSMauro Carvalho Chehab 	int err;
3430c0d06caSMauro Carvalho Chehab 	u8 i2c_data;
3440c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3450c0d06caSMauro Carvalho Chehab 
346c93396e1STheodore Kilgore 	PDEBUG(D_CONF, "Set auto white balance to %d", val);
3470c0d06caSMauro Carvalho Chehab 
3480c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
3490c0d06caSMauro Carvalho Chehab 	if (err < 0)
3500c0d06caSMauro Carvalho Chehab 		return err;
3510c0d06caSMauro Carvalho Chehab 
3520c0d06caSMauro Carvalho Chehab 	i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
3530c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
3540c0d06caSMauro Carvalho Chehab 
3550c0d06caSMauro Carvalho Chehab 	return err;
3560c0d06caSMauro Carvalho Chehab }
3570c0d06caSMauro Carvalho Chehab 
3580c0d06caSMauro Carvalho Chehab static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
3590c0d06caSMauro Carvalho Chehab {
3600c0d06caSMauro Carvalho Chehab 	int err;
3610c0d06caSMauro Carvalho Chehab 	u8 i2c_data;
3620c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3630c0d06caSMauro Carvalho Chehab 
364c93396e1STheodore Kilgore 	PDEBUG(D_CONF, "Set auto gain control to %d", val);
3650c0d06caSMauro Carvalho Chehab 
3660c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
3670c0d06caSMauro Carvalho Chehab 	if (err < 0)
3680c0d06caSMauro Carvalho Chehab 		return err;
3690c0d06caSMauro Carvalho Chehab 
3700c0d06caSMauro Carvalho Chehab 	i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
3710c0d06caSMauro Carvalho Chehab 
3720c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
3730c0d06caSMauro Carvalho Chehab }
3740c0d06caSMauro Carvalho Chehab 
3750c0d06caSMauro Carvalho Chehab static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev,
3760c0d06caSMauro Carvalho Chehab 				    __s32 val)
3770c0d06caSMauro Carvalho Chehab {
3780c0d06caSMauro Carvalho Chehab 	int err;
3790c0d06caSMauro Carvalho Chehab 	u8 i2c_data;
3800c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3810c0d06caSMauro Carvalho Chehab 
382c93396e1STheodore Kilgore 	PDEBUG(D_CONF, "Set auto exposure control to %d", val);
3830c0d06caSMauro Carvalho Chehab 
3840c0d06caSMauro Carvalho Chehab 	err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1);
3850c0d06caSMauro Carvalho Chehab 	if (err < 0)
3860c0d06caSMauro Carvalho Chehab 		return err;
3870c0d06caSMauro Carvalho Chehab 
388c84e412fSHans de Goede 	val = (val == V4L2_EXPOSURE_AUTO);
3890c0d06caSMauro Carvalho Chehab 	i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
3900c0d06caSMauro Carvalho Chehab 
3910c0d06caSMauro Carvalho Chehab 	return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1);
3920c0d06caSMauro Carvalho Chehab }
3930c0d06caSMauro Carvalho Chehab 
394c84e412fSHans de Goede static int ov7660_set_hvflip(struct gspca_dev *gspca_dev)
3950c0d06caSMauro Carvalho Chehab {
3960c0d06caSMauro Carvalho Chehab 	int err;
3970c0d06caSMauro Carvalho Chehab 	u8 i2c_data;
3980c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
3990c0d06caSMauro Carvalho Chehab 
400c93396e1STheodore Kilgore 	PDEBUG(D_CONF, "Set hvflip to %d, %d", sd->hflip->val, sd->vflip->val);
4010c0d06caSMauro Carvalho Chehab 
402c84e412fSHans de Goede 	i2c_data = (sd->hflip->val << 5) | (sd->vflip->val << 4);
4030c0d06caSMauro Carvalho Chehab 
4040c0d06caSMauro Carvalho Chehab 	err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1);
4050c0d06caSMauro Carvalho Chehab 
4060c0d06caSMauro Carvalho Chehab 	return err;
4070c0d06caSMauro Carvalho Chehab }
4080c0d06caSMauro Carvalho Chehab 
409c84e412fSHans de Goede static int ov7660_s_ctrl(struct v4l2_ctrl *ctrl)
4100c0d06caSMauro Carvalho Chehab {
411c84e412fSHans de Goede 	struct gspca_dev *gspca_dev =
412c84e412fSHans de Goede 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
4130c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4140c0d06caSMauro Carvalho Chehab 	int err;
4150c0d06caSMauro Carvalho Chehab 
416c84e412fSHans de Goede 	if (!gspca_dev->streaming)
417c84e412fSHans de Goede 		return 0;
4180c0d06caSMauro Carvalho Chehab 
419c84e412fSHans de Goede 	switch (ctrl->id) {
420c84e412fSHans de Goede 	case V4L2_CID_AUTO_WHITE_BALANCE:
421c84e412fSHans de Goede 		err = ov7660_set_auto_white_balance(gspca_dev, ctrl->val);
422c84e412fSHans de Goede 		break;
423c84e412fSHans de Goede 	case V4L2_CID_EXPOSURE_AUTO:
424c84e412fSHans de Goede 		err = ov7660_set_auto_exposure(gspca_dev, ctrl->val);
425c84e412fSHans de Goede 		break;
426c84e412fSHans de Goede 	case V4L2_CID_AUTOGAIN:
427c84e412fSHans de Goede 		err = ov7660_set_auto_gain(gspca_dev, ctrl->val);
428c84e412fSHans de Goede 		if (err || ctrl->val)
4290c0d06caSMauro Carvalho Chehab 			return err;
430c84e412fSHans de Goede 		err = ov7660_set_gain(gspca_dev, sd->gain->val);
431c84e412fSHans de Goede 		break;
432c84e412fSHans de Goede 	case V4L2_CID_HFLIP:
433c84e412fSHans de Goede 		err = ov7660_set_hvflip(gspca_dev);
434c84e412fSHans de Goede 		break;
435c84e412fSHans de Goede 	default:
436c84e412fSHans de Goede 		return -EINVAL;
437c84e412fSHans de Goede 	}
4380c0d06caSMauro Carvalho Chehab 
4390c0d06caSMauro Carvalho Chehab 	return err;
4400c0d06caSMauro Carvalho Chehab }
4410c0d06caSMauro Carvalho Chehab 
4420c0d06caSMauro Carvalho Chehab static void ov7660_dump_registers(struct sd *sd)
4430c0d06caSMauro Carvalho Chehab {
4440c0d06caSMauro Carvalho Chehab 	int address;
4450c0d06caSMauro Carvalho Chehab 	pr_info("Dumping the ov7660 register state\n");
4460c0d06caSMauro Carvalho Chehab 	for (address = 0; address < 0xa9; address++) {
4470c0d06caSMauro Carvalho Chehab 		u8 value;
4480c0d06caSMauro Carvalho Chehab 		m5602_read_sensor(sd, address, &value, 1);
4490c0d06caSMauro Carvalho Chehab 		pr_info("register 0x%x contains 0x%x\n", address, value);
4500c0d06caSMauro Carvalho Chehab 	}
4510c0d06caSMauro Carvalho Chehab 
4520c0d06caSMauro Carvalho Chehab 	pr_info("ov7660 register state dump complete\n");
4530c0d06caSMauro Carvalho Chehab 
4540c0d06caSMauro Carvalho Chehab 	pr_info("Probing for which registers that are read/write\n");
4550c0d06caSMauro Carvalho Chehab 	for (address = 0; address < 0xff; address++) {
4560c0d06caSMauro Carvalho Chehab 		u8 old_value, ctrl_value;
4570c0d06caSMauro Carvalho Chehab 		u8 test_value[2] = {0xff, 0xff};
4580c0d06caSMauro Carvalho Chehab 
4590c0d06caSMauro Carvalho Chehab 		m5602_read_sensor(sd, address, &old_value, 1);
4600c0d06caSMauro Carvalho Chehab 		m5602_write_sensor(sd, address, test_value, 1);
4610c0d06caSMauro Carvalho Chehab 		m5602_read_sensor(sd, address, &ctrl_value, 1);
4620c0d06caSMauro Carvalho Chehab 
4630c0d06caSMauro Carvalho Chehab 		if (ctrl_value == test_value[0])
4640c0d06caSMauro Carvalho Chehab 			pr_info("register 0x%x is writeable\n", address);
4650c0d06caSMauro Carvalho Chehab 		else
4660c0d06caSMauro Carvalho Chehab 			pr_info("register 0x%x is read only\n", address);
4670c0d06caSMauro Carvalho Chehab 
4680c0d06caSMauro Carvalho Chehab 		/* Restore original value */
4690c0d06caSMauro Carvalho Chehab 		m5602_write_sensor(sd, address, &old_value, 1);
4700c0d06caSMauro Carvalho Chehab 	}
4710c0d06caSMauro Carvalho Chehab }
472