Fix new doxygen errors.
[microdia.git] / ov965x.c
blob5de8a4bfcbd4cc15a29603ac3e3ae731a729f553
1 /**
2 * @file ov965x.c
3 * @author Brian Johnson
4 * @date 2008-08-06
6 * @brief Common functions and data for the Omnivision OV965x sensor series.
8 * @note Copyright (C) Brian Johnson
10 * @par Licences
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "microdia.h"
28 #include "sn9c20x.h"
29 #include "ov965x.h"
31 /**
32 * @var ov965x_init
33 * @brief Addresses and values for the initialization of ov965x sensors
36 static __u8 ov965x_init[][2] = {
37 {OV965X_CTL_COM7, OV965X_COM7_SCCB_RESET},
38 {OV965X_CTL_GAIN, 0x00},
39 {OV965X_CTL_BLUE, 0x78},
40 {OV965X_CTL_RED, 0x78},
41 {OV965X_CTL_VREF, OV965X_VREF_VSTOP_LOW3(0x06) |
42 OV965X_VREF_VSTART_LOW3(0x06)},
43 {OV965X_CTL_COM1, 0x03},
44 {OV965X_CTL_BAVE, 0x00}, /* default */
45 {OV965X_CTL_GEAVE, 0x00}, /* default */
46 {OV965X_CTL_RAVE, 0x00}, /* default */
47 {OV965X_CTL_COM2, OV965X_COM2_OUTPUT_DRIVE_CAP_2X},
48 {OV965X_CTL_COM3, 0x00},
49 {OV965X_CTL_COM4, 0x00},
50 {OV965X_CTL_COM5, OV965X_COM5_15FPS_48MHZ_RGB | 0x20},
51 {OV965X_CTL_COM6, OV965X_COM6_TIMING_RESET_ON_FMT_CHANGE | 0x50},
52 {OV965X_CTL_AECH, 0x7c},
53 {OV965X_CTL_CLKRC, OV965X_CLKRC_DBL_CLK_ENABLE},
54 {OV965X_CTL_COM7, OV965X_COM7_OUTPUT_VGA | OV965X_COM7_OUTPUT_RAW_RGB},
55 {OV965X_CTL_COM8, OV965X_COM8_FAST_AGC_AEC |
56 OV965X_COM8_AEC_STEP_SIZE_NOLIMIT |
57 OV965X_COM8_AGC_ENABLE |
58 OV965X_COM8_AEC_ENABLE |
59 OV965X_COM8_AWB_ENABLE},
60 {OV965X_CTL_COM9, OV965X_COM9_MAX_AGC_8X |
61 OV965X_COM9_RELAX_EXPOSURE_TIMING |
62 OV965X_COM9_DROP_VSYNC_ON_FRAME_DROP |
63 OV965X_COM9_DROP_FRAME_ON_BIG_AEC},
64 {OV965X_CTL_COM10, 0x00},
65 {0x16, 0x07}, /* reserved */
66 {OV965X_CTL_HSTART, 0x24},
67 {OV965X_CTL_HSTOP, 0xc5},
68 {OV965X_CTL_VSTRT, 0x00},
69 {OV965X_CTL_VSTOP, 0x3c},
70 {OV965X_CTL_PSHIFT, 0x00}, /* default */
71 {OV965X_CTL_MVFP, 0x04},
72 {OV965X_CTL_LAEC, 0x00}, /* reserved */
73 {OV965X_CTL_AEW, 0x78}, /* default */
74 {OV965X_CTL_AEB, 0x68}, /* default */
75 {OV965X_CTL_VPT, 0xd4}, /* default */
76 {OV965X_CTL_BBIAS, OV965X_BIAS_SUBTRACT}, /* default */
77 {OV965X_CTL_GbBIAS, OV965X_BIAS_SUBTRACT}, /* default */
78 {OV965X_CTL_Gr_COM, OV965X_Gr_COM_BYPASS_ANALOG_BLC |
79 OV965X_Gr_COM_BYPASS_REGULATOR},
80 {OV965X_CTL_EXHCH, 0x00}, /* default */
81 {OV965X_CTL_EXHCL, 0x00}, /* default */
82 {OV965X_CTL_RBIAS, OV965X_BIAS_SUBTRACT}, /* default */
83 {OV965X_CTL_ADVFL, 0x00}, /* default */
84 {OV965X_CTL_ADVFH, 0x00}, /* default */
85 {OV965X_CTL_YAVE, 0x00}, /* default */
86 {OV965X_CTL_HSYST, 0x08}, /* default */
87 {OV965X_CTL_HSYEN, 0x30}, /* default */
88 {OV965X_CTL_HREF, OV965X_HREF_EDGE_OFT_TO_DATA_OUT(2) |
89 OV965X_HREF_HSTOP_LOW3(0) |
90 OV965X_HREF_HSTART_LOW3(4)},
91 {OV965X_CTL_CHLF, 0xe2}, /* reserved */
92 {OV965X_CTL_ARBLM, 0xbf}, /* reserved */
93 {0x35, 0x81}, /* reserved */
94 {0x36, 0xf9}, /* reserved */
95 {OV965X_CTL_ADC, 0x00}, /* reserved */
96 {OV965X_CTL_ACOM, 0x93}, /* reserved */
97 {OV965X_CTL_OFON, 0x50},
98 {OV965X_CTL_TSLB, OV965X_TSLB_OUTPUT_SEQ_UYVY |
99 OV965X_TSLB_DIGITAL_BLC_ENABLE},
100 {OV965X_CTL_COM11, OV965X_COM11_MANUAL_BANDING_FILTER},
101 {OV965X_CTL_COM12, 0x73},
102 {OV965X_CTL_COM13, OV965X_COM13_ENABLE_COLOR_MATRIX |
103 OV965X_COM13_DELAY_Y_CHANNEL |
104 OV965X_COM13_OUTPUT_DELAY(1)},
105 {OV965X_CTL_COM14, OV965X_COM14_YUV_EDGE_ENHANCE |
106 OV965X_COM14_EDGE_ENHANCE_FACTOR_DBL | 0x0b},
107 {OV965X_CTL_EDGE, OV965X_EDGE_EDGE_ENHANCE_LOW4(8) |
108 OV965X_EDGE_EDGE_ENHANCE_FACTOR(8)},
109 {OV965X_CTL_COM15, OV965X_COM15_OUTPUT_RANGE_O0_TO_FF | 0x01},
110 {OV965X_CTL_COM16, 0x00},
111 {OV965X_CTL_COM17, 0x08},
112 {OV965X_CTL_MANU, 0x80}, /* default */
113 {OV965X_CTL_MANV, 0x80}, /* default */
114 {OV965X_CTL_HV, 0x40},
115 {OV965X_CTL_MBD, 0x00}, /* default */
116 {OV965X_CTL_DBLV, 0x0a}, /* reserved */
117 {OV965X_CTL_COM21, 0x06}, /* reserved */
118 {OV965X_CTL_COM22, 0x20},
119 {OV965X_CTL_COM23, 0x00}, /* default */
120 {OV965X_CTL_COM24, 0x00}, /* reserved */
121 {OV965X_CTL_DBLC1, 0xdf},
122 {OV965X_CTL_DM_LNL, 0x00}, /* default */
123 {OV965X_CTL_DM_LNH, 0x00}, /* default */
124 {0x94, 0x88}, /* reserved */
125 {0x95, 0x88}, /* reserved */
126 {0x96, 0x04}, /* reserved */
127 {OV965X_CTL_AECHM, 0x00},
128 {OV965X_CTL_COM26, 0x80}, /* reserved */
129 {0xa8, 0x80}, /* reserved */
130 {0xa9, 0xb8}, /* reserved */
131 {0xaa, 0x92}, /* reserved */
132 {0xab, 0x0a}, /* reserved */
135 struct microdia_video_format ov965x_fmts[] = {
137 .pix_fmt = V4L2_PIX_FMT_SBGGR8,
138 .desc = "Bayer 8bit (BGGR)",
139 .bpp = 1,
140 .set_format = sn9c20x_set_raw
143 .pix_fmt = V4L2_PIX_FMT_JPEG,
144 .desc = "JPEG (YUV 4:2:2)",
145 .bpp = 2,
146 .set_format = sn9c20x_set_jpeg
150 struct microdia_video_resolution ov965x_resolutions[] = {
152 .width = 160,
153 .height = 120,
154 .scale = SN9C20X_1_4_SCALE,
155 .window = {0, 17, 640, 480}
158 .width = 320,
159 .height = 240,
160 .scale = SN9C20X_1_2_SCALE,
161 .window = {0, 7, 640, 480}
164 .width = 640,
165 .height = 480,
166 .scale = SN9C20X_NO_SCALE,
167 .window = {0, 7, 640, 480}
171 struct microdia_video_resolution soi968_resolutions[] = {
173 .width = 160,
174 .height = 120,
175 .scale = SN9C20X_1_4_SCALE,
176 .window = {60, 17, 640, 480}
179 .width = 320,
180 .height = 240,
181 .scale = SN9C20X_1_2_SCALE,
182 .window = {60, 11, 640, 480}
185 .width = 640,
186 .height = 480,
187 .scale = SN9C20X_NO_SCALE,
188 .window = {60, 11, 640, 480}
192 int ov965x_probe(struct usb_microdia *dev)
194 int ret;
195 __u8 buf[2];
196 dev->camera.sensor_slave_address = 0x30;
197 ret = sn9c20x_read_i2c_data(dev, 2, 0x0a, buf);
198 if (ret == 0) {
199 if (buf[0] != 0x96)
200 return -EINVAL;
201 if (buf[1] == 0x52) {
202 ov965x_initialize(dev);
203 dev->camera.set_hvflip = ov965x_set_hvflip;
204 dev->camera.set_exposure = ov965x_set_exposure;
205 dev->camera.set_auto_exposure = ov965x_set_autoexposure;
206 dev->camera.modes = ov965x_resolutions;
207 dev->camera.nmodes = ARRAY_SIZE(ov965x_resolutions);
208 dev->camera.fmts = ov965x_fmts;
209 dev->camera.nfmts = ARRAY_SIZE(ov965x_fmts);
210 return OV9650_SENSOR;
212 if (buf[1] == 0x56 || buf[1] == 0x57) {
213 return OV9655_SENSOR;
215 if (buf[1] == 0x28) {
216 soi968_initialize(dev);
217 dev->camera.set_exposure = soi968_set_exposure;
218 dev->camera.set_auto_exposure = ov965x_set_autoexposure;
219 dev->camera.modes = soi968_resolutions;
220 dev->camera.nmodes = ARRAY_SIZE(soi968_resolutions);
221 dev->camera.fmts = ov965x_fmts;
222 dev->camera.nfmts = ARRAY_SIZE(ov965x_fmts);
223 return SOI968_SENSOR;
225 } else {
226 UDIA_INFO("Failed on i2c read in ov965x probe\n");
228 return -EINVAL;
232 * @brief Initialize ov965x sensors
234 * @param dev Pointer to device structure
236 * @return 0 or negative error code
238 * This function applies the default settings from #ov965x_init to
239 * the ov965x sensor
242 int ov965x_initialize(struct usb_microdia *dev)
244 int ret, i;
245 __u8 value, reg;
247 for (i = 0; i < ARRAY_SIZE(ov965x_init); i++) {
248 reg = ov965x_init[i][0];
249 value = ov965x_init[i][1];
250 ret = sn9c20x_write_i2c_data(dev, 1, reg, &value);
251 if (ret < 0) {
252 UDIA_INFO("Sensor Init Error (%d). line %d\n", ret, i);
253 break;
257 return ret;
262 * @var soi968_init
263 * @brief Addresses and values for the initialization of SOI968 sensors
264 * @author GWater
267 static __u8 soi968_init[][2] = {
268 /* reset all registers */
269 {0x12, 0x80},
270 /* stop resetting */
271 {0x12, 0x00},
273 /* snapshot mode: off */
274 {0x0c, 0x00},
276 /* enable offset adjustment,
277 * full data range,
278 * analogue2digital control black-level calibration
280 {0x0f, 0x1f},
282 /* Clock: internal PLL on */
283 {0x11, 0x80},
286 /* Analoge Black-level calibration off , no anaolgue gain */
287 {0x38, 0x52},
289 /* reserved */
290 {0x1e, 0x00},
292 /* special system settings (voltage, analogue2digital, ...) */
293 {0x33, 0x08},
294 {0x35, 0x8c},
295 {0x36, 0x0c},
296 {0x37, 0x04},
298 /* next 7 are unknown/reserved */
299 {0x45, 0x04},
300 {0x47, 0xff},
301 {0x3e, 0x00},
302 {0x3f, 0x00},
303 {0x3b, 0x20},
304 {0x3a, 0x96},
305 {0x3d, 0x0a},
307 /* disable banding filter in dark environment,
308 * VSYNC is dropped when framerate is dropped,
309 * drop frmaes when exposure out of tolerance,
310 * unfreeze exposure and gain values
312 {0x14, 0x4e},
314 /* AEC, AGC, AWB disabled; fast AEC */
315 {0x13, 0x88},
317 /* output: VGA, master mode */
318 {0x12, 0x40},
320 /* set HSTART, HSTOP, VSTART and VSTOP */
321 {0x17, 0x13},
322 {0x18, 0x63},
323 {0x19, 0x01},
324 {0x1a, 0x79},
325 {0x32, 0x24}, /* LSB for all four */
327 /* this register contains the product ID, it is read-only
328 {0x1b, 0x00}, */
330 /* AWB update threshold,
331 * blue and red gain LSB
333 {0x03, 0x00},
335 /* CLock: internal PLL off */
336 {0x11, 0x40},
338 /* Line interval adjustment */
339 {0x2a, 0x10},
340 {0x2b, 0xe0},
342 /* AEC MSB */
343 {0x10, 0x32},
345 /* gain - default*/
346 {0x00, 0x00},
348 /* blue and red gain - default*/
349 {0x01, 0x80},
350 {0x02, 0x80}
354 * @brief Initialize SOI968 sensors
356 * @param dev Pointer to device structure
358 * @return 0 or negative error code
360 * @author GWater
362 * This function applies the default settings from #soi968_init to
363 * the SOI968 sensor
366 int soi968_initialize(struct usb_microdia *dev)
368 int ret, i;
369 __u8 value, reg;
371 for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
372 reg = soi968_init[i][0];
373 value = soi968_init[i][1];
374 ret = sn9c20x_write_i2c_data(dev, 1, reg, &value);
375 if (ret < 0) {
376 UDIA_INFO("Sensor Init Error (%d). line %d\n", ret, i);
377 break;
381 return ret;
386 * @brief Set hflip and vflip in ov965x sensors
388 * @param dev Pointer to device structure
390 * @returns 0 or negative error code
393 int ov965x_set_hvflip(struct usb_microdia *dev)
395 int ret;
396 __u8 value;
398 * Changing hstop value seems to be necessary to keep correct
399 * colors during a vflip. The values don't seem to make much
400 * sense since to keep the correct color value i'm setting hstop
401 * to the same value as hstart is set for.
403 __u8 hstop = 0xc5;
404 ret = sn9c20x_read_i2c_data(dev, 1, OV965X_CTL_MVFP, &value);
405 if (ret < 0)
406 return ret;
408 if (dev->vsettings.hflip)
409 value |= OV965X_MVFP_MIRROR;
410 else
411 value &= ~OV965X_MVFP_MIRROR;
413 if (dev->vsettings.vflip) {
414 hstop = 0x24;
415 value |= OV965X_MVFP_VFLIP;
416 } else {
417 value &= ~OV965X_MVFP_VFLIP;
420 ret = sn9c20x_write_i2c_data(dev, 1, OV965X_CTL_HSTOP, &hstop);
422 ret = sn9c20x_write_i2c_data(dev, 1, OV965X_CTL_MVFP, &value);
424 return ret;
428 * @brief Set exposure for ov965x sensors
430 * @param dev
432 * @returns 0 or negative error value
434 * The used registers do not show up the datasheets.
437 int ov965x_set_exposure(struct usb_microdia *dev)
439 int ret = 0;
440 __u8 v1 = (dev->vsettings.exposure >> 4) & 0xff;
441 __u8 v2 = dev->vsettings.exposure >> 12;
443 ret |= sn9c20x_write_i2c_data(dev, 1, 0x2d, &v1);
445 ret |= sn9c20x_write_i2c_data(dev, 1, 0x2e, &v2);
447 return ret;
451 * @brief Set autoexposure for ov96xx sensors
453 * @param dev
455 * @returns 0 or negative error value
457 * @author GWater
459 * For all OV965x and SOI968 sensors.
461 int ov965x_set_autoexposure(struct usb_microdia *dev)
463 __u8 buf[1];
464 int ret = 0;
466 /* Read current value of the I2C-register
467 * controlling AutoExposureControl:
469 ret = sn9c20x_read_i2c_data(dev, 1, 0x13, buf);
470 if (ret < 0) {
471 UDIA_ERROR("Error: setting of auto exposure failed: "
472 "error while reading from I2C-register 0x13");
473 return ret;
476 /* Determine new value for register 0x13: */
477 if (dev->vsettings.auto_exposure == 1) {
478 /* Enable automatic exposure: */
479 buf[0] |= 0x01;
480 } else if (dev->vsettings.auto_exposure == 0) {
481 /* Disable automatic exposure: */
482 buf[0] &= ~0x01;
483 } else
484 return -EINVAL;
486 /* Write new value to I2C-register 0x13: */
487 ret = sn9c20x_write_i2c_data(dev, 1, 0x13, buf);
488 if (ret < 0) {
489 UDIA_ERROR("Error: setting of auto exposure failed: "
490 "error while writing to I2C-register 0x13");
491 return ret;
493 return 0;
497 * @brief Set exposure for SOI968 sensors
499 * @param dev
501 * @returns 0 or negative error value
503 * @author GWater
505 * For SOI968 sensors.
507 int soi968_set_exposure(struct usb_microdia *dev)
509 int value, ret;
510 int exposure = dev->vsettings.exposure;
511 __u8 buf1, buf2, buf3;
513 /* Read current value of the I2C-register
514 * containing exposure LSB:
516 ret = sn9c20x_read_i2c_data(dev, 1, 0x04, &buf1);
517 if (ret < 0) {
518 UDIA_ERROR("Error: setting exposure failed: "
519 "error while reading from I2C-register 0x04");
520 return ret;
523 value = (exposure * 0x07ff / 0xffff) & 0x07ff;
524 buf2 = ((__u8) (value & 0x07)) | (buf1 & ~0x07);
525 buf3 = (__u8) (value >> 3) & 0xff;
527 /* Write new value to I2C-register 0x04: */
528 ret = sn9c20x_write_i2c_data(dev, 1, 0x04, &buf2);
529 if (ret < 0) {
530 UDIA_ERROR("Error: setting exposure failed: "
531 "error while writing to I2C-register 0x04");
532 return ret;
535 /* Write new value to I2C-register 0x10: */
536 ret = sn9c20x_write_i2c_data(dev, 1, 0x10, &buf3);
537 if (ret < 0) {
538 UDIA_ERROR("Error: setting exposure failed: "
539 "error while writing to I2C-register 0x10");
540 return ret;
543 return 0;