]>
Commit | Line | Data |
---|---|---|
4763fa84 EA |
1 | /* |
2 | * Driver for the ov7660 sensor | |
3 | * | |
4 | * Copyright (C) 2009 Erik Andrén | |
5 | * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. | |
6 | * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> | |
7 | * | |
8 | * Portions of code to USB interface and ALi driver software, | |
9 | * Copyright (c) 2006 Willem Duinker | |
10 | * v4l2 interface modeled after the V4L2 driver | |
11 | * for SN9C10x PC Camera Controllers | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or | |
14 | * modify it under the terms of the GNU General Public License as | |
15 | * published by the Free Software Foundation, version 2. | |
16 | * | |
17 | */ | |
18 | ||
bdfe91f4 JP |
19 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
20 | ||
4763fa84 EA |
21 | #include "m5602_ov7660.h" |
22 | ||
c9304e43 EA |
23 | static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); |
24 | static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val); | |
f1f59fe6 EA |
25 | static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, |
26 | __s32 *val); | |
27 | static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, | |
28 | __s32 val); | |
456ebe4e EA |
29 | static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); |
30 | static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); | |
7c7ddf16 EA |
31 | static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); |
32 | static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); | |
a66887d2 EA |
33 | static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); |
34 | static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val); | |
35 | static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); | |
36 | static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val); | |
c9304e43 | 37 | |
f0ecba96 | 38 | static const struct ctrl ov7660_ctrls[] = { |
c9304e43 EA |
39 | #define GAIN_IDX 1 |
40 | { | |
41 | { | |
42 | .id = V4L2_CID_GAIN, | |
43 | .type = V4L2_CTRL_TYPE_INTEGER, | |
44 | .name = "gain", | |
45 | .minimum = 0x00, | |
46 | .maximum = 0xff, | |
47 | .step = 0x1, | |
72b79747 | 48 | .default_value = OV7660_DEFAULT_GAIN, |
c9304e43 EA |
49 | .flags = V4L2_CTRL_FLAG_SLIDER |
50 | }, | |
51 | .set = ov7660_set_gain, | |
52 | .get = ov7660_get_gain | |
53 | }, | |
cbd1f7fb | 54 | #define BLUE_BALANCE_IDX 2 |
68fdb7a5 | 55 | #define RED_BALANCE_IDX 3 |
f1f59fe6 EA |
56 | #define AUTO_WHITE_BALANCE_IDX 4 |
57 | { | |
58 | { | |
780e3121 JFM |
59 | .id = V4L2_CID_AUTO_WHITE_BALANCE, |
60 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
61 | .name = "auto white balance", | |
62 | .minimum = 0, | |
63 | .maximum = 1, | |
64 | .step = 1, | |
65 | .default_value = 1 | |
f1f59fe6 EA |
66 | }, |
67 | .set = ov7660_set_auto_white_balance, | |
68 | .get = ov7660_get_auto_white_balance | |
69 | }, | |
456ebe4e EA |
70 | #define AUTO_GAIN_CTRL_IDX 5 |
71 | { | |
72 | { | |
780e3121 JFM |
73 | .id = V4L2_CID_AUTOGAIN, |
74 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
75 | .name = "auto gain control", | |
76 | .minimum = 0, | |
77 | .maximum = 1, | |
78 | .step = 1, | |
79 | .default_value = 1 | |
456ebe4e EA |
80 | }, |
81 | .set = ov7660_set_auto_gain, | |
82 | .get = ov7660_get_auto_gain | |
83 | }, | |
7c7ddf16 EA |
84 | #define AUTO_EXPOSURE_IDX 6 |
85 | { | |
86 | { | |
780e3121 JFM |
87 | .id = V4L2_CID_EXPOSURE_AUTO, |
88 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
89 | .name = "auto exposure", | |
90 | .minimum = 0, | |
91 | .maximum = 1, | |
92 | .step = 1, | |
93 | .default_value = 1 | |
7c7ddf16 EA |
94 | }, |
95 | .set = ov7660_set_auto_exposure, | |
96 | .get = ov7660_get_auto_exposure | |
a66887d2 EA |
97 | }, |
98 | #define HFLIP_IDX 7 | |
99 | { | |
100 | { | |
780e3121 JFM |
101 | .id = V4L2_CID_HFLIP, |
102 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
103 | .name = "horizontal flip", | |
104 | .minimum = 0, | |
105 | .maximum = 1, | |
106 | .step = 1, | |
107 | .default_value = 0 | |
a66887d2 EA |
108 | }, |
109 | .set = ov7660_set_hflip, | |
110 | .get = ov7660_get_hflip | |
111 | }, | |
112 | #define VFLIP_IDX 8 | |
113 | { | |
114 | { | |
780e3121 JFM |
115 | .id = V4L2_CID_VFLIP, |
116 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
117 | .name = "vertical flip", | |
118 | .minimum = 0, | |
119 | .maximum = 1, | |
120 | .step = 1, | |
121 | .default_value = 0 | |
a66887d2 EA |
122 | }, |
123 | .set = ov7660_set_vflip, | |
124 | .get = ov7660_get_vflip | |
125 | }, | |
126 | ||
c9304e43 | 127 | }; |
4763fa84 EA |
128 | |
129 | static struct v4l2_pix_format ov7660_modes[] = { | |
130 | { | |
131 | 640, | |
132 | 480, | |
133 | V4L2_PIX_FMT_SBGGR8, | |
134 | V4L2_FIELD_NONE, | |
135 | .sizeimage = | |
136 | 640 * 480, | |
137 | .bytesperline = 640, | |
138 | .colorspace = V4L2_COLORSPACE_SRGB, | |
139 | .priv = 0 | |
140 | } | |
141 | }; | |
142 | ||
143 | static void ov7660_dump_registers(struct sd *sd); | |
144 | ||
145 | int ov7660_probe(struct sd *sd) | |
146 | { | |
ea8f74b1 EA |
147 | int err = 0, i; |
148 | u8 prod_id = 0, ver_id = 0; | |
149 | ||
150 | s32 *sensor_settings; | |
151 | ||
152 | if (force_sensor) { | |
153 | if (force_sensor == OV7660_SENSOR) { | |
bdfe91f4 | 154 | pr_info("Forcing an %s sensor\n", ov7660.name); |
ea8f74b1 EA |
155 | goto sensor_found; |
156 | } | |
157 | /* If we want to force another sensor, | |
158 | don't try to probe this one */ | |
159 | return -ENODEV; | |
160 | } | |
161 | ||
162 | /* Do the preinit */ | |
163 | for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) { | |
164 | u8 data[2]; | |
165 | ||
166 | if (preinit_ov7660[i][0] == BRIDGE) { | |
167 | err = m5602_write_bridge(sd, | |
168 | preinit_ov7660[i][1], | |
169 | preinit_ov7660[i][2]); | |
170 | } else { | |
171 | data[0] = preinit_ov7660[i][2]; | |
172 | err = m5602_write_sensor(sd, | |
173 | preinit_ov7660[i][1], data, 1); | |
174 | } | |
175 | } | |
176 | if (err < 0) | |
177 | return err; | |
178 | ||
179 | if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1)) | |
180 | return -ENODEV; | |
181 | ||
182 | if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1)) | |
183 | return -ENODEV; | |
184 | ||
bdfe91f4 | 185 | pr_info("Sensor reported 0x%x%x\n", prod_id, ver_id); |
ea8f74b1 EA |
186 | |
187 | if ((prod_id == 0x76) && (ver_id == 0x60)) { | |
bdfe91f4 | 188 | pr_info("Detected a ov7660 sensor\n"); |
ea8f74b1 EA |
189 | goto sensor_found; |
190 | } | |
4763fa84 | 191 | return -ENODEV; |
ea8f74b1 EA |
192 | |
193 | sensor_found: | |
c9304e43 EA |
194 | sensor_settings = kmalloc( |
195 | ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL); | |
196 | if (!sensor_settings) | |
197 | return -ENOMEM; | |
198 | ||
ea8f74b1 EA |
199 | sd->gspca_dev.cam.cam_mode = ov7660_modes; |
200 | sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes); | |
201 | sd->desc->ctrls = ov7660_ctrls; | |
202 | sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls); | |
203 | ||
204 | for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++) | |
205 | sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value; | |
206 | sd->sensor_priv = sensor_settings; | |
207 | ||
208 | return 0; | |
4763fa84 EA |
209 | } |
210 | ||
211 | int ov7660_init(struct sd *sd) | |
212 | { | |
0364c4ca | 213 | int i, err = 0; |
c9304e43 | 214 | s32 *sensor_settings = sd->sensor_priv; |
0364c4ca EA |
215 | |
216 | /* Init the sensor */ | |
eb3678fb | 217 | for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) { |
0364c4ca EA |
218 | u8 data[2]; |
219 | ||
eb3678fb | 220 | if (init_ov7660[i][0] == BRIDGE) { |
0364c4ca | 221 | err = m5602_write_bridge(sd, |
eb3678fb EA |
222 | init_ov7660[i][1], |
223 | init_ov7660[i][2]); | |
0364c4ca | 224 | } else { |
eb3678fb | 225 | data[0] = init_ov7660[i][2]; |
0364c4ca | 226 | err = m5602_write_sensor(sd, |
eb3678fb | 227 | init_ov7660[i][1], data, 1); |
0364c4ca EA |
228 | } |
229 | } | |
230 | ||
231 | if (dump_sensor) | |
232 | ov7660_dump_registers(sd); | |
233 | ||
c9304e43 EA |
234 | err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); |
235 | if (err < 0) | |
236 | return err; | |
237 | ||
f1f59fe6 EA |
238 | err = ov7660_set_auto_white_balance(&sd->gspca_dev, |
239 | sensor_settings[AUTO_WHITE_BALANCE_IDX]); | |
36e64d5c EA |
240 | if (err < 0) |
241 | return err; | |
242 | ||
456ebe4e EA |
243 | err = ov7660_set_auto_gain(&sd->gspca_dev, |
244 | sensor_settings[AUTO_GAIN_CTRL_IDX]); | |
245 | if (err < 0) | |
246 | return err; | |
247 | ||
7c7ddf16 EA |
248 | err = ov7660_set_auto_exposure(&sd->gspca_dev, |
249 | sensor_settings[AUTO_EXPOSURE_IDX]); | |
250 | if (err < 0) | |
251 | return err; | |
a66887d2 EA |
252 | err = ov7660_set_hflip(&sd->gspca_dev, |
253 | sensor_settings[HFLIP_IDX]); | |
254 | if (err < 0) | |
255 | return err; | |
256 | ||
257 | err = ov7660_set_vflip(&sd->gspca_dev, | |
258 | sensor_settings[VFLIP_IDX]); | |
f1f59fe6 | 259 | |
0364c4ca | 260 | return err; |
4763fa84 EA |
261 | } |
262 | ||
263 | int ov7660_start(struct sd *sd) | |
264 | { | |
265 | return 0; | |
266 | } | |
267 | ||
268 | int ov7660_stop(struct sd *sd) | |
269 | { | |
270 | return 0; | |
271 | } | |
272 | ||
c9304e43 EA |
273 | void ov7660_disconnect(struct sd *sd) |
274 | { | |
275 | ov7660_stop(sd); | |
276 | ||
277 | sd->sensor = NULL; | |
278 | kfree(sd->sensor_priv); | |
279 | } | |
280 | ||
281 | static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | |
282 | { | |
283 | struct sd *sd = (struct sd *) gspca_dev; | |
284 | s32 *sensor_settings = sd->sensor_priv; | |
285 | ||
286 | *val = sensor_settings[GAIN_IDX]; | |
287 | PDEBUG(D_V4L2, "Read gain %d", *val); | |
288 | return 0; | |
289 | } | |
290 | ||
291 | static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) | |
292 | { | |
293 | int err; | |
294 | u8 i2c_data; | |
295 | struct sd *sd = (struct sd *) gspca_dev; | |
296 | s32 *sensor_settings = sd->sensor_priv; | |
297 | ||
298 | PDEBUG(D_V4L2, "Setting gain to %d", val); | |
299 | ||
300 | sensor_settings[GAIN_IDX] = val; | |
301 | ||
302 | err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1); | |
303 | return err; | |
304 | } | |
4763fa84 | 305 | |
68fdb7a5 | 306 | |
f1f59fe6 EA |
307 | static int ov7660_get_auto_white_balance(struct gspca_dev *gspca_dev, |
308 | __s32 *val) | |
309 | { | |
310 | struct sd *sd = (struct sd *) gspca_dev; | |
311 | s32 *sensor_settings = sd->sensor_priv; | |
312 | ||
313 | *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; | |
314 | return 0; | |
315 | } | |
316 | ||
317 | static int ov7660_set_auto_white_balance(struct gspca_dev *gspca_dev, | |
318 | __s32 val) | |
319 | { | |
320 | int err; | |
321 | u8 i2c_data; | |
322 | struct sd *sd = (struct sd *) gspca_dev; | |
323 | s32 *sensor_settings = sd->sensor_priv; | |
324 | ||
325 | PDEBUG(D_V4L2, "Set auto white balance to %d", val); | |
326 | ||
327 | sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; | |
328 | err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); | |
329 | if (err < 0) | |
330 | return err; | |
331 | ||
332 | i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); | |
333 | err = m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); | |
334 | ||
335 | return err; | |
336 | } | |
337 | ||
456ebe4e EA |
338 | static int ov7660_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) |
339 | { | |
340 | struct sd *sd = (struct sd *) gspca_dev; | |
341 | s32 *sensor_settings = sd->sensor_priv; | |
342 | ||
343 | *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; | |
344 | PDEBUG(D_V4L2, "Read auto gain control %d", *val); | |
345 | return 0; | |
346 | } | |
347 | ||
348 | static int ov7660_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) | |
349 | { | |
350 | int err; | |
351 | u8 i2c_data; | |
352 | struct sd *sd = (struct sd *) gspca_dev; | |
353 | s32 *sensor_settings = sd->sensor_priv; | |
354 | ||
355 | PDEBUG(D_V4L2, "Set auto gain control to %d", val); | |
356 | ||
357 | sensor_settings[AUTO_GAIN_CTRL_IDX] = val; | |
358 | err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); | |
359 | if (err < 0) | |
360 | return err; | |
361 | ||
362 | i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); | |
363 | ||
364 | return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); | |
365 | } | |
366 | ||
7c7ddf16 EA |
367 | static int ov7660_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) |
368 | { | |
369 | struct sd *sd = (struct sd *) gspca_dev; | |
370 | s32 *sensor_settings = sd->sensor_priv; | |
371 | ||
372 | *val = sensor_settings[AUTO_EXPOSURE_IDX]; | |
373 | PDEBUG(D_V4L2, "Read auto exposure control %d", *val); | |
374 | return 0; | |
375 | } | |
376 | ||
377 | static int ov7660_set_auto_exposure(struct gspca_dev *gspca_dev, | |
378 | __s32 val) | |
379 | { | |
380 | int err; | |
381 | u8 i2c_data; | |
382 | struct sd *sd = (struct sd *) gspca_dev; | |
383 | s32 *sensor_settings = sd->sensor_priv; | |
384 | ||
385 | PDEBUG(D_V4L2, "Set auto exposure control to %d", val); | |
386 | ||
387 | sensor_settings[AUTO_EXPOSURE_IDX] = val; | |
388 | err = m5602_read_sensor(sd, OV7660_COM8, &i2c_data, 1); | |
389 | if (err < 0) | |
390 | return err; | |
391 | ||
392 | i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); | |
393 | ||
394 | return m5602_write_sensor(sd, OV7660_COM8, &i2c_data, 1); | |
395 | } | |
396 | ||
a66887d2 EA |
397 | static int ov7660_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) |
398 | { | |
399 | struct sd *sd = (struct sd *) gspca_dev; | |
400 | s32 *sensor_settings = sd->sensor_priv; | |
401 | ||
402 | *val = sensor_settings[HFLIP_IDX]; | |
403 | PDEBUG(D_V4L2, "Read horizontal flip %d", *val); | |
404 | return 0; | |
405 | } | |
406 | ||
407 | static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val) | |
408 | { | |
409 | int err; | |
410 | u8 i2c_data; | |
411 | struct sd *sd = (struct sd *) gspca_dev; | |
412 | s32 *sensor_settings = sd->sensor_priv; | |
413 | ||
414 | PDEBUG(D_V4L2, "Set horizontal flip to %d", val); | |
415 | ||
416 | sensor_settings[HFLIP_IDX] = val; | |
417 | ||
418 | i2c_data = ((val & 0x01) << 5) | | |
419 | (sensor_settings[VFLIP_IDX] << 4); | |
420 | ||
421 | err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); | |
422 | ||
423 | return err; | |
424 | } | |
425 | ||
426 | static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) | |
427 | { | |
428 | struct sd *sd = (struct sd *) gspca_dev; | |
429 | s32 *sensor_settings = sd->sensor_priv; | |
430 | ||
431 | *val = sensor_settings[VFLIP_IDX]; | |
432 | PDEBUG(D_V4L2, "Read vertical flip %d", *val); | |
433 | ||
434 | return 0; | |
435 | } | |
436 | ||
437 | static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val) | |
438 | { | |
439 | int err; | |
440 | u8 i2c_data; | |
441 | struct sd *sd = (struct sd *) gspca_dev; | |
442 | s32 *sensor_settings = sd->sensor_priv; | |
443 | ||
444 | PDEBUG(D_V4L2, "Set vertical flip to %d", val); | |
445 | sensor_settings[VFLIP_IDX] = val; | |
446 | ||
447 | i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); | |
448 | err = m5602_write_sensor(sd, OV7660_MVFP, &i2c_data, 1); | |
449 | if (err < 0) | |
450 | return err; | |
451 | ||
452 | /* When vflip is toggled we need to readjust the bridge hsync/vsync */ | |
453 | if (gspca_dev->streaming) | |
454 | err = ov7660_start(sd); | |
455 | ||
456 | return err; | |
457 | } | |
458 | ||
4763fa84 EA |
459 | static void ov7660_dump_registers(struct sd *sd) |
460 | { | |
461 | int address; | |
bdfe91f4 | 462 | pr_info("Dumping the ov7660 register state\n"); |
4763fa84 EA |
463 | for (address = 0; address < 0xa9; address++) { |
464 | u8 value; | |
465 | m5602_read_sensor(sd, address, &value, 1); | |
bdfe91f4 | 466 | pr_info("register 0x%x contains 0x%x\n", address, value); |
4763fa84 EA |
467 | } |
468 | ||
bdfe91f4 | 469 | pr_info("ov7660 register state dump complete\n"); |
4763fa84 | 470 | |
bdfe91f4 | 471 | pr_info("Probing for which registers that are read/write\n"); |
4763fa84 EA |
472 | for (address = 0; address < 0xff; address++) { |
473 | u8 old_value, ctrl_value; | |
474 | u8 test_value[2] = {0xff, 0xff}; | |
475 | ||
476 | m5602_read_sensor(sd, address, &old_value, 1); | |
477 | m5602_write_sensor(sd, address, test_value, 1); | |
478 | m5602_read_sensor(sd, address, &ctrl_value, 1); | |
479 | ||
480 | if (ctrl_value == test_value[0]) | |
bdfe91f4 | 481 | pr_info("register 0x%x is writeable\n", address); |
4763fa84 | 482 | else |
bdfe91f4 | 483 | pr_info("register 0x%x is read only\n", address); |
4763fa84 EA |
484 | |
485 | /* Restore original value */ | |
486 | m5602_write_sensor(sd, address, &old_value, 1); | |
487 | } | |
488 | } |