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