• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/media/video/sn9c102/
1/***************************************************************************
2 * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera      *
3 * Controllers                                                             *
4 *                                                                         *
5 * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it>  *
6 *                                                                         *
7 * This program is free software; you can redistribute it and/or modify    *
8 * it under the terms of the GNU General Public License as published by    *
9 * the Free Software Foundation; either version 2 of the License, or       *
10 * (at your option) any later version.                                     *
11 *                                                                         *
12 * This program is distributed in the hope that it will be useful,         *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
15 * GNU General Public License for more details.                            *
16 *                                                                         *
17 * You should have received a copy of the GNU General Public License       *
18 * along with this program; if not, write to the Free Software             *
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
20 ***************************************************************************/
21
22#include "sn9c102_sensor.h"
23#include "sn9c102_devtable.h"
24
25
26static int ov7630_init(struct sn9c102_device* cam)
27{
28	int err = 0;
29
30	switch (sn9c102_get_bridge(cam)) {
31	case BRIDGE_SN9C101:
32	case BRIDGE_SN9C102:
33		err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17},
34					       {0x0f, 0x18}, {0x50, 0x19});
35
36		err += sn9c102_i2c_write(cam, 0x12, 0x8d);
37		err += sn9c102_i2c_write(cam, 0x12, 0x0d);
38		err += sn9c102_i2c_write(cam, 0x11, 0x00);
39		err += sn9c102_i2c_write(cam, 0x15, 0x35);
40		err += sn9c102_i2c_write(cam, 0x16, 0x03);
41		err += sn9c102_i2c_write(cam, 0x17, 0x1c);
42		err += sn9c102_i2c_write(cam, 0x18, 0xbd);
43		err += sn9c102_i2c_write(cam, 0x19, 0x06);
44		err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
45		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
46		err += sn9c102_i2c_write(cam, 0x20, 0x44);
47		err += sn9c102_i2c_write(cam, 0x23, 0xee);
48		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
49		err += sn9c102_i2c_write(cam, 0x27, 0x9a);
50		err += sn9c102_i2c_write(cam, 0x28, 0x20);
51		err += sn9c102_i2c_write(cam, 0x29, 0x30);
52		err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
53		err += sn9c102_i2c_write(cam, 0x30, 0x24);
54		err += sn9c102_i2c_write(cam, 0x32, 0x86);
55		err += sn9c102_i2c_write(cam, 0x60, 0xa9);
56		err += sn9c102_i2c_write(cam, 0x61, 0x42);
57		err += sn9c102_i2c_write(cam, 0x65, 0x00);
58		err += sn9c102_i2c_write(cam, 0x69, 0x38);
59		err += sn9c102_i2c_write(cam, 0x6f, 0x88);
60		err += sn9c102_i2c_write(cam, 0x70, 0x0b);
61		err += sn9c102_i2c_write(cam, 0x71, 0x00);
62		err += sn9c102_i2c_write(cam, 0x74, 0x21);
63		err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
64		break;
65	case BRIDGE_SN9C103:
66		err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03},
67					       {0x1a, 0x04}, {0x20, 0x05},
68					       {0x20, 0x06}, {0x20, 0x07},
69					       {0x03, 0x10}, {0x0a, 0x14},
70					       {0x60, 0x17}, {0x0f, 0x18},
71					       {0x50, 0x19}, {0x1d, 0x1a},
72					       {0x10, 0x1b}, {0x02, 0x1c},
73					       {0x03, 0x1d}, {0x0f, 0x1e},
74					       {0x0c, 0x1f}, {0x00, 0x20},
75					       {0x10, 0x21}, {0x20, 0x22},
76					       {0x30, 0x23}, {0x40, 0x24},
77					       {0x50, 0x25}, {0x60, 0x26},
78					       {0x70, 0x27}, {0x80, 0x28},
79					       {0x90, 0x29}, {0xa0, 0x2a},
80					       {0xb0, 0x2b}, {0xc0, 0x2c},
81					       {0xd0, 0x2d}, {0xe0, 0x2e},
82					       {0xf0, 0x2f}, {0xff, 0x30});
83
84		err += sn9c102_i2c_write(cam, 0x12, 0x8d);
85		err += sn9c102_i2c_write(cam, 0x12, 0x0d);
86		err += sn9c102_i2c_write(cam, 0x15, 0x34);
87		err += sn9c102_i2c_write(cam, 0x11, 0x01);
88		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
89		err += sn9c102_i2c_write(cam, 0x20, 0x44);
90		err += sn9c102_i2c_write(cam, 0x23, 0xee);
91		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
92		err += sn9c102_i2c_write(cam, 0x27, 0x9a);
93		err += sn9c102_i2c_write(cam, 0x28, 0x20);
94		err += sn9c102_i2c_write(cam, 0x29, 0x30);
95		err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
96		err += sn9c102_i2c_write(cam, 0x30, 0x24);
97		err += sn9c102_i2c_write(cam, 0x32, 0x86);
98		err += sn9c102_i2c_write(cam, 0x60, 0xa9);
99		err += sn9c102_i2c_write(cam, 0x61, 0x42);
100		err += sn9c102_i2c_write(cam, 0x65, 0x00);
101		err += sn9c102_i2c_write(cam, 0x69, 0x38);
102		err += sn9c102_i2c_write(cam, 0x6f, 0x88);
103		err += sn9c102_i2c_write(cam, 0x70, 0x0b);
104		err += sn9c102_i2c_write(cam, 0x71, 0x00);
105		err += sn9c102_i2c_write(cam, 0x74, 0x21);
106		err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
107		break;
108	case BRIDGE_SN9C105:
109	case BRIDGE_SN9C120:
110	err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03},
111				       {0x1a, 0x04}, {0x03, 0x10},
112				       {0x0a, 0x14}, {0xe2, 0x17},
113				       {0x0b, 0x18}, {0x00, 0x19},
114				       {0x1d, 0x1a}, {0x10, 0x1b},
115				       {0x02, 0x1c}, {0x03, 0x1d},
116				       {0x0f, 0x1e}, {0x0c, 0x1f},
117				       {0x00, 0x20}, {0x24, 0x21},
118				       {0x3b, 0x22}, {0x47, 0x23},
119				       {0x60, 0x24}, {0x71, 0x25},
120				       {0x80, 0x26}, {0x8f, 0x27},
121				       {0x9d, 0x28}, {0xaa, 0x29},
122				       {0xb8, 0x2a}, {0xc4, 0x2b},
123				       {0xd1, 0x2c}, {0xdd, 0x2d},
124				       {0xe8, 0x2e}, {0xf4, 0x2f},
125				       {0xff, 0x30}, {0x00, 0x3f},
126				       {0xc7, 0x40}, {0x01, 0x41},
127				       {0x44, 0x42}, {0x00, 0x43},
128				       {0x44, 0x44}, {0x00, 0x45},
129				       {0x44, 0x46}, {0x00, 0x47},
130				       {0xc7, 0x48}, {0x01, 0x49},
131				       {0xc7, 0x4a}, {0x01, 0x4b},
132				       {0xc7, 0x4c}, {0x01, 0x4d},
133				       {0x44, 0x4e}, {0x00, 0x4f},
134				       {0x44, 0x50}, {0x00, 0x51},
135				       {0x44, 0x52}, {0x00, 0x53},
136				       {0xc7, 0x54}, {0x01, 0x55},
137				       {0xc7, 0x56}, {0x01, 0x57},
138				       {0xc7, 0x58}, {0x01, 0x59},
139				       {0x44, 0x5a}, {0x00, 0x5b},
140				       {0x44, 0x5c}, {0x00, 0x5d},
141				       {0x44, 0x5e}, {0x00, 0x5f},
142				       {0xc7, 0x60}, {0x01, 0x61},
143				       {0xc7, 0x62}, {0x01, 0x63},
144				       {0xc7, 0x64}, {0x01, 0x65},
145				       {0x44, 0x66}, {0x00, 0x67},
146				       {0x44, 0x68}, {0x00, 0x69},
147				       {0x44, 0x6a}, {0x00, 0x6b},
148				       {0xc7, 0x6c}, {0x01, 0x6d},
149				       {0xc7, 0x6e}, {0x01, 0x6f},
150				       {0xc7, 0x70}, {0x01, 0x71},
151				       {0x44, 0x72}, {0x00, 0x73},
152				       {0x44, 0x74}, {0x00, 0x75},
153				       {0x44, 0x76}, {0x00, 0x77},
154				       {0xc7, 0x78}, {0x01, 0x79},
155				       {0xc7, 0x7a}, {0x01, 0x7b},
156				       {0xc7, 0x7c}, {0x01, 0x7d},
157				       {0x44, 0x7e}, {0x00, 0x7f},
158				       {0x17, 0x84}, {0x00, 0x85},
159				       {0x2e, 0x86}, {0x00, 0x87},
160				       {0x09, 0x88}, {0x00, 0x89},
161				       {0xe8, 0x8a}, {0x0f, 0x8b},
162				       {0xda, 0x8c}, {0x0f, 0x8d},
163				       {0x40, 0x8e}, {0x00, 0x8f},
164				       {0x37, 0x90}, {0x00, 0x91},
165				       {0xcf, 0x92}, {0x0f, 0x93},
166				       {0xfa, 0x94}, {0x0f, 0x95},
167				       {0x00, 0x96}, {0x00, 0x97},
168				       {0x00, 0x98}, {0x66, 0x99},
169				       {0x00, 0x9a}, {0x40, 0x9b},
170				       {0x20, 0x9c}, {0x00, 0x9d},
171				       {0x00, 0x9e}, {0x00, 0x9f},
172				       {0x2d, 0xc0}, {0x2d, 0xc1},
173				       {0x3a, 0xc2}, {0x00, 0xc3},
174				       {0x04, 0xc4}, {0x3f, 0xc5},
175				       {0x00, 0xc6}, {0x00, 0xc7},
176				       {0x50, 0xc8}, {0x3c, 0xc9},
177				       {0x28, 0xca}, {0xd8, 0xcb},
178				       {0x14, 0xcc}, {0xec, 0xcd},
179				       {0x32, 0xce}, {0xdd, 0xcf},
180				       {0x32, 0xd0}, {0xdd, 0xd1},
181				       {0x6a, 0xd2}, {0x50, 0xd3},
182				       {0x60, 0xd4}, {0x00, 0xd5},
183				       {0x00, 0xd6});
184
185		err += sn9c102_i2c_write(cam, 0x12, 0x80);
186		err += sn9c102_i2c_write(cam, 0x12, 0x48);
187		err += sn9c102_i2c_write(cam, 0x01, 0x80);
188		err += sn9c102_i2c_write(cam, 0x02, 0x80);
189		err += sn9c102_i2c_write(cam, 0x03, 0x80);
190		err += sn9c102_i2c_write(cam, 0x04, 0x10);
191		err += sn9c102_i2c_write(cam, 0x05, 0x20);
192		err += sn9c102_i2c_write(cam, 0x06, 0x80);
193		err += sn9c102_i2c_write(cam, 0x11, 0x00);
194		err += sn9c102_i2c_write(cam, 0x0c, 0x20);
195		err += sn9c102_i2c_write(cam, 0x0d, 0x20);
196		err += sn9c102_i2c_write(cam, 0x15, 0x80);
197		err += sn9c102_i2c_write(cam, 0x16, 0x03);
198		err += sn9c102_i2c_write(cam, 0x17, 0x1b);
199		err += sn9c102_i2c_write(cam, 0x18, 0xbd);
200		err += sn9c102_i2c_write(cam, 0x19, 0x05);
201		err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
202		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
203		err += sn9c102_i2c_write(cam, 0x21, 0x1b);
204		err += sn9c102_i2c_write(cam, 0x22, 0x00);
205		err += sn9c102_i2c_write(cam, 0x23, 0xde);
206		err += sn9c102_i2c_write(cam, 0x24, 0x10);
207		err += sn9c102_i2c_write(cam, 0x25, 0x8a);
208		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
209		err += sn9c102_i2c_write(cam, 0x27, 0xca);
210		err += sn9c102_i2c_write(cam, 0x28, 0xa2);
211		err += sn9c102_i2c_write(cam, 0x29, 0x74);
212		err += sn9c102_i2c_write(cam, 0x2a, 0x88);
213		err += sn9c102_i2c_write(cam, 0x2b, 0x34);
214		err += sn9c102_i2c_write(cam, 0x2c, 0x88);
215		err += sn9c102_i2c_write(cam, 0x2e, 0x00);
216		err += sn9c102_i2c_write(cam, 0x2f, 0x00);
217		err += sn9c102_i2c_write(cam, 0x30, 0x00);
218		err += sn9c102_i2c_write(cam, 0x32, 0xc2);
219		err += sn9c102_i2c_write(cam, 0x33, 0x08);
220		err += sn9c102_i2c_write(cam, 0x4c, 0x40);
221		err += sn9c102_i2c_write(cam, 0x4d, 0xf3);
222		err += sn9c102_i2c_write(cam, 0x60, 0x05);
223		err += sn9c102_i2c_write(cam, 0x61, 0x40);
224		err += sn9c102_i2c_write(cam, 0x62, 0x12);
225		err += sn9c102_i2c_write(cam, 0x63, 0x57);
226		err += sn9c102_i2c_write(cam, 0x64, 0x73);
227		err += sn9c102_i2c_write(cam, 0x65, 0x00);
228		err += sn9c102_i2c_write(cam, 0x66, 0x55);
229		err += sn9c102_i2c_write(cam, 0x67, 0x01);
230		err += sn9c102_i2c_write(cam, 0x68, 0xac);
231		err += sn9c102_i2c_write(cam, 0x69, 0x38);
232		err += sn9c102_i2c_write(cam, 0x6f, 0x1f);
233		err += sn9c102_i2c_write(cam, 0x70, 0x01);
234		err += sn9c102_i2c_write(cam, 0x71, 0x00);
235		err += sn9c102_i2c_write(cam, 0x72, 0x10);
236		err += sn9c102_i2c_write(cam, 0x73, 0x50);
237		err += sn9c102_i2c_write(cam, 0x74, 0x20);
238		err += sn9c102_i2c_write(cam, 0x76, 0x01);
239		err += sn9c102_i2c_write(cam, 0x77, 0xf3);
240		err += sn9c102_i2c_write(cam, 0x78, 0x90);
241		err += sn9c102_i2c_write(cam, 0x79, 0x98);
242		err += sn9c102_i2c_write(cam, 0x7a, 0x98);
243		err += sn9c102_i2c_write(cam, 0x7b, 0x00);
244		err += sn9c102_i2c_write(cam, 0x7c, 0x38);
245		err += sn9c102_i2c_write(cam, 0x7d, 0xff);
246		break;
247	default:
248		break;
249	}
250
251	return err;
252}
253
254
255static int ov7630_get_ctrl(struct sn9c102_device* cam,
256			   struct v4l2_control* ctrl)
257{
258	enum sn9c102_bridge bridge = sn9c102_get_bridge(cam);
259	int err = 0;
260
261	switch (ctrl->id) {
262	case V4L2_CID_EXPOSURE:
263		if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
264			return -EIO;
265		break;
266	case V4L2_CID_RED_BALANCE:
267		if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
268			ctrl->value = sn9c102_pread_reg(cam, 0x05);
269		else
270			ctrl->value = sn9c102_pread_reg(cam, 0x07);
271		break;
272	case V4L2_CID_BLUE_BALANCE:
273		ctrl->value = sn9c102_pread_reg(cam, 0x06);
274		break;
275	case SN9C102_V4L2_CID_GREEN_BALANCE:
276		if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
277			ctrl->value = sn9c102_pread_reg(cam, 0x07);
278		else
279			ctrl->value = sn9c102_pread_reg(cam, 0x05);
280		break;
281		break;
282	case V4L2_CID_GAIN:
283		if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0)
284			return -EIO;
285		ctrl->value &= 0x3f;
286		break;
287	case V4L2_CID_DO_WHITE_BALANCE:
288		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
289			return -EIO;
290		ctrl->value &= 0x3f;
291		break;
292	case V4L2_CID_WHITENESS:
293		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0)
294			return -EIO;
295		ctrl->value &= 0x3f;
296		break;
297	case V4L2_CID_AUTOGAIN:
298		if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0)
299			return -EIO;
300		ctrl->value &= 0x01;
301		break;
302	case V4L2_CID_VFLIP:
303		if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0)
304			return -EIO;
305		ctrl->value = (ctrl->value & 0x80) ? 1 : 0;
306		break;
307	case SN9C102_V4L2_CID_GAMMA:
308		if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0)
309			return -EIO;
310		ctrl->value = (ctrl->value & 0x02) ? 1 : 0;
311		break;
312	case SN9C102_V4L2_CID_BAND_FILTER:
313		if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0)
314			return -EIO;
315		ctrl->value = (ctrl->value & 0x02) ? 1 : 0;
316		break;
317	default:
318		return -EINVAL;
319	}
320
321	return err ? -EIO : 0;
322}
323
324
325static int ov7630_set_ctrl(struct sn9c102_device* cam,
326			   const struct v4l2_control* ctrl)
327{
328	enum sn9c102_bridge bridge = sn9c102_get_bridge(cam);
329	int err = 0;
330
331	switch (ctrl->id) {
332	case V4L2_CID_EXPOSURE:
333		err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
334		break;
335	case V4L2_CID_RED_BALANCE:
336		if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
337			err += sn9c102_write_reg(cam, ctrl->value, 0x05);
338		else
339			err += sn9c102_write_reg(cam, ctrl->value, 0x07);
340		break;
341	case V4L2_CID_BLUE_BALANCE:
342		err += sn9c102_write_reg(cam, ctrl->value, 0x06);
343		break;
344	case SN9C102_V4L2_CID_GREEN_BALANCE:
345		if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120)
346			err += sn9c102_write_reg(cam, ctrl->value, 0x07);
347		else
348			err += sn9c102_write_reg(cam, ctrl->value, 0x05);
349		break;
350	case V4L2_CID_GAIN:
351		err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
352		break;
353	case V4L2_CID_DO_WHITE_BALANCE:
354		err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
355		break;
356	case V4L2_CID_WHITENESS:
357		err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
358		break;
359	case V4L2_CID_AUTOGAIN:
360		err += sn9c102_i2c_write(cam, 0x13, ctrl->value |
361						    (ctrl->value << 1));
362		break;
363	case V4L2_CID_VFLIP:
364		err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7));
365		break;
366	case SN9C102_V4L2_CID_GAMMA:
367		err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2);
368		break;
369	case SN9C102_V4L2_CID_BAND_FILTER:
370		err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2);
371		break;
372	default:
373		return -EINVAL;
374	}
375
376	return err ? -EIO : 0;
377}
378
379
380static int ov7630_set_crop(struct sn9c102_device* cam,
381			   const struct v4l2_rect* rect)
382{
383	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
384	int err = 0;
385	u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
386
387	switch (sn9c102_get_bridge(cam)) {
388	case BRIDGE_SN9C101:
389	case BRIDGE_SN9C102:
390	case BRIDGE_SN9C103:
391		h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1;
392		break;
393	case BRIDGE_SN9C105:
394	case BRIDGE_SN9C120:
395		h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4;
396		break;
397	default:
398		break;
399	}
400
401	err += sn9c102_write_reg(cam, h_start, 0x12);
402	err += sn9c102_write_reg(cam, v_start, 0x13);
403
404	return err;
405}
406
407
408static int ov7630_set_pix_format(struct sn9c102_device* cam,
409				 const struct v4l2_pix_format* pix)
410{
411	int err = 0;
412
413	switch (sn9c102_get_bridge(cam)) {
414	case BRIDGE_SN9C101:
415	case BRIDGE_SN9C102:
416	case BRIDGE_SN9C103:
417		if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8)
418			err += sn9c102_write_reg(cam, 0x50, 0x19);
419		else
420			err += sn9c102_write_reg(cam, 0x20, 0x19);
421		break;
422	case BRIDGE_SN9C105:
423	case BRIDGE_SN9C120:
424		if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) {
425			err += sn9c102_write_reg(cam, 0xe5, 0x17);
426			err += sn9c102_i2c_write(cam, 0x11, 0x04);
427		} else {
428			err += sn9c102_write_reg(cam, 0xe2, 0x17);
429			err += sn9c102_i2c_write(cam, 0x11, 0x02);
430		}
431		break;
432	default:
433		break;
434	}
435
436	return err;
437}
438
439
440static const struct sn9c102_sensor ov7630 = {
441	.name = "OV7630",
442	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
443	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 |
444			    BRIDGE_SN9C105 | BRIDGE_SN9C120,
445	.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
446	.frequency = SN9C102_I2C_100KHZ,
447	.interface = SN9C102_I2C_2WIRES,
448	.i2c_slave_id = 0x21,
449	.init = &ov7630_init,
450	.qctrl = {
451		{
452			.id = V4L2_CID_GAIN,
453			.type = V4L2_CTRL_TYPE_INTEGER,
454			.name = "global gain",
455			.minimum = 0x00,
456			.maximum = 0x3f,
457			.step = 0x01,
458			.default_value = 0x14,
459			.flags = 0,
460		},
461		{
462			.id = V4L2_CID_EXPOSURE,
463			.type = V4L2_CTRL_TYPE_INTEGER,
464			.name = "exposure",
465			.minimum = 0x00,
466			.maximum = 0xff,
467			.step = 0x01,
468			.default_value = 0x60,
469			.flags = 0,
470		},
471		{
472			.id = V4L2_CID_WHITENESS,
473			.type = V4L2_CTRL_TYPE_INTEGER,
474			.name = "white balance background: red",
475			.minimum = 0x00,
476			.maximum = 0x3f,
477			.step = 0x01,
478			.default_value = 0x20,
479			.flags = 0,
480		},
481		{
482			.id = V4L2_CID_DO_WHITE_BALANCE,
483			.type = V4L2_CTRL_TYPE_INTEGER,
484			.name = "white balance background: blue",
485			.minimum = 0x00,
486			.maximum = 0x3f,
487			.step = 0x01,
488			.default_value = 0x20,
489			.flags = 0,
490		},
491		{
492			.id = V4L2_CID_RED_BALANCE,
493			.type = V4L2_CTRL_TYPE_INTEGER,
494			.name = "red balance",
495			.minimum = 0x00,
496			.maximum = 0x7f,
497			.step = 0x01,
498			.default_value = 0x20,
499			.flags = 0,
500		},
501		{
502			.id = V4L2_CID_BLUE_BALANCE,
503			.type = V4L2_CTRL_TYPE_INTEGER,
504			.name = "blue balance",
505			.minimum = 0x00,
506			.maximum = 0x7f,
507			.step = 0x01,
508			.default_value = 0x20,
509			.flags = 0,
510		},
511		{
512			.id = V4L2_CID_AUTOGAIN,
513			.type = V4L2_CTRL_TYPE_BOOLEAN,
514			.name = "auto adjust",
515			.minimum = 0x00,
516			.maximum = 0x01,
517			.step = 0x01,
518			.default_value = 0x00,
519			.flags = 0,
520		},
521		{
522			.id = V4L2_CID_VFLIP,
523			.type = V4L2_CTRL_TYPE_BOOLEAN,
524			.name = "vertical flip",
525			.minimum = 0x00,
526			.maximum = 0x01,
527			.step = 0x01,
528			.default_value = 0x01,
529			.flags = 0,
530		},
531		{
532			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
533			.type = V4L2_CTRL_TYPE_INTEGER,
534			.name = "green balance",
535			.minimum = 0x00,
536			.maximum = 0x7f,
537			.step = 0x01,
538			.default_value = 0x20,
539			.flags = 0,
540		},
541		{
542			.id = SN9C102_V4L2_CID_BAND_FILTER,
543			.type = V4L2_CTRL_TYPE_BOOLEAN,
544			.name = "band filter",
545			.minimum = 0x00,
546			.maximum = 0x01,
547			.step = 0x01,
548			.default_value = 0x00,
549			.flags = 0,
550		},
551		{
552			.id = SN9C102_V4L2_CID_GAMMA,
553			.type = V4L2_CTRL_TYPE_BOOLEAN,
554			.name = "rgb gamma",
555			.minimum = 0x00,
556			.maximum = 0x01,
557			.step = 0x01,
558			.default_value = 0x00,
559			.flags = 0,
560		},
561	},
562	.get_ctrl = &ov7630_get_ctrl,
563	.set_ctrl = &ov7630_set_ctrl,
564	.cropcap = {
565		.bounds = {
566			.left = 0,
567			.top = 0,
568			.width = 640,
569			.height = 480,
570		},
571		.defrect = {
572			.left = 0,
573			.top = 0,
574			.width = 640,
575			.height = 480,
576		},
577	},
578	.set_crop = &ov7630_set_crop,
579	.pix_format = {
580		.width = 640,
581		.height = 480,
582		.pixelformat = V4L2_PIX_FMT_SN9C10X,
583		.priv = 8,
584	},
585	.set_pix_format = &ov7630_set_pix_format
586};
587
588
589int sn9c102_probe_ov7630(struct sn9c102_device* cam)
590{
591	int pid, ver, err = 0;
592
593	switch (sn9c102_get_bridge(cam)) {
594	case BRIDGE_SN9C101:
595	case BRIDGE_SN9C102:
596		err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
597					       {0x28, 0x17});
598		break;
599	case BRIDGE_SN9C103: /* do _not_ change anything! */
600		err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01},
601					       {0x28, 0x17}, {0x44, 0x02});
602		pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
603		if (err || pid < 0) /* try a different initialization */
604			err += sn9c102_write_const_regs(cam, {0x01, 0x01},
605							{0x00, 0x01});
606		break;
607	case BRIDGE_SN9C105:
608	case BRIDGE_SN9C120:
609		err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1},
610					       {0x29, 0x01}, {0x74, 0x02},
611					       {0x0e, 0x01}, {0x44, 0x01});
612		break;
613	default:
614		break;
615	}
616
617	pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
618	ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b);
619	if (err || pid < 0 || ver < 0)
620		return -EIO;
621	if (pid != 0x76 || ver != 0x31)
622		return -ENODEV;
623	sn9c102_attach_sensor(cam, &ov7630);
624
625	return 0;
626}
627