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