• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/src/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
24
25static int ov7630_init(struct sn9c102_device* cam)
26{
27	int err = 0;
28
29	switch (sn9c102_get_bridge(cam)) {
30	case BRIDGE_SN9C101:
31	case BRIDGE_SN9C102:
32		err = sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17},
33					       {0x0f, 0x18}, {0x50, 0x19});
34
35		err += sn9c102_i2c_write(cam, 0x12, 0x8d);
36		err += sn9c102_i2c_write(cam, 0x12, 0x0d);
37		err += sn9c102_i2c_write(cam, 0x11, 0x00);
38		err += sn9c102_i2c_write(cam, 0x15, 0x35);
39		err += sn9c102_i2c_write(cam, 0x16, 0x03);
40		err += sn9c102_i2c_write(cam, 0x17, 0x1c);
41		err += sn9c102_i2c_write(cam, 0x18, 0xbd);
42		err += sn9c102_i2c_write(cam, 0x19, 0x06);
43		err += sn9c102_i2c_write(cam, 0x1a, 0xf6);
44		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
45		err += sn9c102_i2c_write(cam, 0x20, 0x44);
46		err += sn9c102_i2c_write(cam, 0x23, 0xee);
47		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
48		err += sn9c102_i2c_write(cam, 0x27, 0x9a);
49		err += sn9c102_i2c_write(cam, 0x28, 0x20);
50		err += sn9c102_i2c_write(cam, 0x29, 0x30);
51		err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
52		err += sn9c102_i2c_write(cam, 0x30, 0x24);
53		err += sn9c102_i2c_write(cam, 0x32, 0x86);
54		err += sn9c102_i2c_write(cam, 0x60, 0xa9);
55		err += sn9c102_i2c_write(cam, 0x61, 0x42);
56		err += sn9c102_i2c_write(cam, 0x65, 0x00);
57		err += sn9c102_i2c_write(cam, 0x69, 0x38);
58		err += sn9c102_i2c_write(cam, 0x6f, 0x88);
59		err += sn9c102_i2c_write(cam, 0x70, 0x0b);
60		err += sn9c102_i2c_write(cam, 0x71, 0x00);
61		err += sn9c102_i2c_write(cam, 0x74, 0x21);
62		err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
63		break;
64	case BRIDGE_SN9C103:
65		err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03},
66					       {0x1a, 0x04}, {0x20, 0x05},
67					       {0x20, 0x06}, {0x20, 0x07},
68					       {0x03, 0x10}, {0x0a, 0x14},
69					       {0x60, 0x17}, {0x0f, 0x18},
70					       {0x50, 0x19}, {0x1d, 0x1a},
71					       {0x10, 0x1b}, {0x02, 0x1c},
72					       {0x03, 0x1d}, {0x0f, 0x1e},
73					       {0x0c, 0x1f}, {0x00, 0x20},
74					       {0x10, 0x21}, {0x20, 0x22},
75					       {0x30, 0x23}, {0x40, 0x24},
76					       {0x50, 0x25}, {0x60, 0x26},
77					       {0x70, 0x27}, {0x80, 0x28},
78					       {0x90, 0x29}, {0xa0, 0x2a},
79					       {0xb0, 0x2b}, {0xc0, 0x2c},
80					       {0xd0, 0x2d}, {0xe0, 0x2e},
81					       {0xf0, 0x2f}, {0xff, 0x30});
82
83		err += sn9c102_i2c_write(cam, 0x12, 0x8d);
84		err += sn9c102_i2c_write(cam, 0x12, 0x0d);
85		err += sn9c102_i2c_write(cam, 0x15, 0x34);
86		err += sn9c102_i2c_write(cam, 0x11, 0x01);
87		err += sn9c102_i2c_write(cam, 0x1b, 0x04);
88		err += sn9c102_i2c_write(cam, 0x20, 0x44);
89		err += sn9c102_i2c_write(cam, 0x23, 0xee);
90		err += sn9c102_i2c_write(cam, 0x26, 0xa0);
91		err += sn9c102_i2c_write(cam, 0x27, 0x9a);
92		err += sn9c102_i2c_write(cam, 0x28, 0x20);
93		err += sn9c102_i2c_write(cam, 0x29, 0x30);
94		err += sn9c102_i2c_write(cam, 0x2f, 0x3d);
95		err += sn9c102_i2c_write(cam, 0x30, 0x24);
96		err += sn9c102_i2c_write(cam, 0x32, 0x86);
97		err += sn9c102_i2c_write(cam, 0x60, 0xa9);
98		err += sn9c102_i2c_write(cam, 0x61, 0x42);
99		err += sn9c102_i2c_write(cam, 0x65, 0x00);
100		err += sn9c102_i2c_write(cam, 0x69, 0x38);
101		err += sn9c102_i2c_write(cam, 0x6f, 0x88);
102		err += sn9c102_i2c_write(cam, 0x70, 0x0b);
103		err += sn9c102_i2c_write(cam, 0x71, 0x00);
104		err += sn9c102_i2c_write(cam, 0x74, 0x21);
105		err += sn9c102_i2c_write(cam, 0x7d, 0xf7);
106		break;
107	default:
108		break;
109	}
110
111	return err;
112}
113
114
115static int ov7630_get_ctrl(struct sn9c102_device* cam,
116			   struct v4l2_control* ctrl)
117{
118	int err = 0;
119
120	switch (ctrl->id) {
121	case V4L2_CID_EXPOSURE:
122		if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0)
123			return -EIO;
124		break;
125	case V4L2_CID_RED_BALANCE:
126		ctrl->value = sn9c102_pread_reg(cam, 0x07);
127		break;
128	case V4L2_CID_BLUE_BALANCE:
129		ctrl->value = sn9c102_pread_reg(cam, 0x06);
130		break;
131	case SN9C102_V4L2_CID_GREEN_BALANCE:
132		ctrl->value = sn9c102_pread_reg(cam, 0x05);
133		break;
134	case V4L2_CID_GAIN:
135		if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0)
136			return -EIO;
137		ctrl->value &= 0x3f;
138		break;
139	case V4L2_CID_DO_WHITE_BALANCE:
140		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0)
141			return -EIO;
142		ctrl->value &= 0x3f;
143		break;
144	case V4L2_CID_WHITENESS:
145		if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0)
146			return -EIO;
147		ctrl->value &= 0x3f;
148		break;
149	case V4L2_CID_AUTOGAIN:
150		if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0)
151			return -EIO;
152		ctrl->value &= 0x01;
153		break;
154	case V4L2_CID_VFLIP:
155		if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0)
156			return -EIO;
157		ctrl->value = (ctrl->value & 0x80) ? 1 : 0;
158		break;
159	case SN9C102_V4L2_CID_GAMMA:
160		if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0)
161			return -EIO;
162		ctrl->value = (ctrl->value & 0x02) ? 1 : 0;
163		break;
164	case SN9C102_V4L2_CID_BAND_FILTER:
165		if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0)
166			return -EIO;
167		ctrl->value = (ctrl->value & 0x02) ? 1 : 0;
168		break;
169	default:
170		return -EINVAL;
171	}
172
173	return err ? -EIO : 0;
174}
175
176
177static int ov7630_set_ctrl(struct sn9c102_device* cam,
178			   const struct v4l2_control* ctrl)
179{
180	int err = 0;
181
182	switch (ctrl->id) {
183	case V4L2_CID_EXPOSURE:
184		err += sn9c102_i2c_write(cam, 0x10, ctrl->value);
185		break;
186	case V4L2_CID_RED_BALANCE:
187		err += sn9c102_write_reg(cam, ctrl->value, 0x07);
188		break;
189	case V4L2_CID_BLUE_BALANCE:
190		err += sn9c102_write_reg(cam, ctrl->value, 0x06);
191		break;
192	case SN9C102_V4L2_CID_GREEN_BALANCE:
193		err += sn9c102_write_reg(cam, ctrl->value, 0x05);
194		break;
195	case V4L2_CID_GAIN:
196		err += sn9c102_i2c_write(cam, 0x00, ctrl->value);
197		break;
198	case V4L2_CID_DO_WHITE_BALANCE:
199		err += sn9c102_i2c_write(cam, 0x0c, ctrl->value);
200		break;
201	case V4L2_CID_WHITENESS:
202		err += sn9c102_i2c_write(cam, 0x0d, ctrl->value);
203		break;
204	case V4L2_CID_AUTOGAIN:
205		err += sn9c102_i2c_write(cam, 0x13, ctrl->value |
206						    (ctrl->value << 1));
207		break;
208	case V4L2_CID_VFLIP:
209		err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7));
210		break;
211	case SN9C102_V4L2_CID_GAMMA:
212		err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2);
213		break;
214	case SN9C102_V4L2_CID_BAND_FILTER:
215		err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2);
216		break;
217	default:
218		return -EINVAL;
219	}
220
221	return err ? -EIO : 0;
222}
223
224
225static int ov7630_set_crop(struct sn9c102_device* cam,
226			   const struct v4l2_rect* rect)
227{
228	struct sn9c102_sensor* s = sn9c102_get_sensor(cam);
229	int err = 0;
230	u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1,
231	   v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1;
232
233	err += sn9c102_write_reg(cam, h_start, 0x12);
234	err += sn9c102_write_reg(cam, v_start, 0x13);
235
236	return err;
237}
238
239
240static int ov7630_set_pix_format(struct sn9c102_device* cam,
241				 const struct v4l2_pix_format* pix)
242{
243	int err = 0;
244
245	if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
246		err += sn9c102_write_reg(cam, 0x20, 0x19);
247	else
248		err += sn9c102_write_reg(cam, 0x50, 0x19);
249
250	return err;
251}
252
253
254static const struct sn9c102_sensor ov7630 = {
255	.name = "OV7630",
256	.maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>",
257	.supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103,
258	.sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE,
259	.frequency = SN9C102_I2C_100KHZ,
260	.interface = SN9C102_I2C_2WIRES,
261	.i2c_slave_id = 0x21,
262	.init = &ov7630_init,
263	.qctrl = {
264		{
265			.id = V4L2_CID_GAIN,
266			.type = V4L2_CTRL_TYPE_INTEGER,
267			.name = "global gain",
268			.minimum = 0x00,
269			.maximum = 0x3f,
270			.step = 0x01,
271			.default_value = 0x14,
272			.flags = 0,
273		},
274		{
275			.id = V4L2_CID_EXPOSURE,
276			.type = V4L2_CTRL_TYPE_INTEGER,
277			.name = "exposure",
278			.minimum = 0x00,
279			.maximum = 0xff,
280			.step = 0x01,
281			.default_value = 0x60,
282			.flags = 0,
283		},
284		{
285			.id = V4L2_CID_WHITENESS,
286			.type = V4L2_CTRL_TYPE_INTEGER,
287			.name = "white balance background: red",
288			.minimum = 0x00,
289			.maximum = 0x3f,
290			.step = 0x01,
291			.default_value = 0x20,
292			.flags = 0,
293		},
294		{
295			.id = V4L2_CID_DO_WHITE_BALANCE,
296			.type = V4L2_CTRL_TYPE_INTEGER,
297			.name = "white balance background: blue",
298			.minimum = 0x00,
299			.maximum = 0x3f,
300			.step = 0x01,
301			.default_value = 0x20,
302			.flags = 0,
303		},
304		{
305			.id = V4L2_CID_RED_BALANCE,
306			.type = V4L2_CTRL_TYPE_INTEGER,
307			.name = "red balance",
308			.minimum = 0x00,
309			.maximum = 0x7f,
310			.step = 0x01,
311			.default_value = 0x20,
312			.flags = 0,
313		},
314		{
315			.id = V4L2_CID_BLUE_BALANCE,
316			.type = V4L2_CTRL_TYPE_INTEGER,
317			.name = "blue balance",
318			.minimum = 0x00,
319			.maximum = 0x7f,
320			.step = 0x01,
321			.default_value = 0x20,
322			.flags = 0,
323		},
324		{
325			.id = V4L2_CID_AUTOGAIN,
326			.type = V4L2_CTRL_TYPE_BOOLEAN,
327			.name = "auto adjust",
328			.minimum = 0x00,
329			.maximum = 0x01,
330			.step = 0x01,
331			.default_value = 0x00,
332			.flags = 0,
333		},
334		{
335			.id = V4L2_CID_VFLIP,
336			.type = V4L2_CTRL_TYPE_BOOLEAN,
337			.name = "vertical flip",
338			.minimum = 0x00,
339			.maximum = 0x01,
340			.step = 0x01,
341			.default_value = 0x01,
342			.flags = 0,
343		},
344		{
345			.id = SN9C102_V4L2_CID_GREEN_BALANCE,
346			.type = V4L2_CTRL_TYPE_INTEGER,
347			.name = "green balance",
348			.minimum = 0x00,
349			.maximum = 0x7f,
350			.step = 0x01,
351			.default_value = 0x20,
352			.flags = 0,
353		},
354		{
355			.id = SN9C102_V4L2_CID_BAND_FILTER,
356			.type = V4L2_CTRL_TYPE_BOOLEAN,
357			.name = "band filter",
358			.minimum = 0x00,
359			.maximum = 0x01,
360			.step = 0x01,
361			.default_value = 0x00,
362			.flags = 0,
363		},
364		{
365			.id = SN9C102_V4L2_CID_GAMMA,
366			.type = V4L2_CTRL_TYPE_BOOLEAN,
367			.name = "rgb gamma",
368			.minimum = 0x00,
369			.maximum = 0x01,
370			.step = 0x01,
371			.default_value = 0x00,
372			.flags = 0,
373		},
374	},
375	.get_ctrl = &ov7630_get_ctrl,
376	.set_ctrl = &ov7630_set_ctrl,
377	.cropcap = {
378		.bounds = {
379			.left = 0,
380			.top = 0,
381			.width = 640,
382			.height = 480,
383		},
384		.defrect = {
385			.left = 0,
386			.top = 0,
387			.width = 640,
388			.height = 480,
389		},
390	},
391	.set_crop = &ov7630_set_crop,
392	.pix_format = {
393		.width = 640,
394		.height = 480,
395		.pixelformat = V4L2_PIX_FMT_SN9C10X,
396		.priv = 8,
397	},
398	.set_pix_format = &ov7630_set_pix_format
399};
400
401
402int sn9c102_probe_ov7630(struct sn9c102_device* cam)
403{
404	int pid, ver, err = 0;
405
406	switch (sn9c102_get_bridge(cam)) {
407	case BRIDGE_SN9C101:
408	case BRIDGE_SN9C102:
409		err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01},
410					       {0x28, 0x17});
411		break;
412	case BRIDGE_SN9C103: /* do _not_ change anything! */
413		err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x42, 0x01},
414					       {0x28, 0x17}, {0x44, 0x02});
415		pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
416		if (err || pid < 0) /* try a different initialization */
417			err += sn9c102_write_const_regs(cam, {0x01, 0x01},
418							{0x00, 0x01});
419		break;
420	default:
421		break;
422	}
423
424	pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a);
425	ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b);
426	if (err || pid < 0 || ver < 0)
427		return -EIO;
428	if (pid != 0x76 || ver != 0x31)
429		return -ENODEV;
430	sn9c102_attach_sensor(cam, &ov7630);
431
432	return 0;
433}
434