Remove wrong version tag from all files
[microdia.git] / ov965x.c
blob9ef224472270ce09989f8983cafc3d4989606145
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 static __u8 ov965x_init[][2] = {
32 {OV965X_CTL_COM7, OV965X_COM7_SCCB_RESET},
33 {OV965X_CTL_GAIN, 0x00},
34 {OV965X_CTL_BLUE, 0x78},
35 {OV965X_CTL_RED, 0x78},
36 {OV965X_CTL_VREF, OV965X_VREF_VSTOP_LOW3(0x06) |
37 OV965X_VREF_VSTART_LOW3(0x06)},
38 {OV965X_CTL_COM1, 0x03},
39 {OV965X_CTL_BAVE, 0x00}, /* default */
40 {OV965X_CTL_GEAVE, 0x00}, /* default */
41 {OV965X_CTL_RAVE, 0x00}, /* default */
42 {OV965X_CTL_COM2, OV965X_COM2_OUTPUT_DRIVE_CAP_2X},
43 {OV965X_CTL_COM3, 0x00},
44 {OV965X_CTL_COM4, 0x00},
45 {OV965X_CTL_COM5, OV965X_COM5_15FPS_48MHZ_RGB | 0x20},
46 {OV965X_CTL_COM6, OV965X_COM6_TIMING_RESET_ON_FMT_CHANGE | 0x50},
47 {OV965X_CTL_AECH, 0x7c},
48 {OV965X_CTL_CLKRC, OV965X_CLKRC_DBL_CLK_ENABLE},
49 {OV965X_CTL_COM7, OV965X_COM7_OUTPUT_VGA | OV965X_COM7_OUTPUT_RAW_RGB},
50 {OV965X_CTL_COM8, OV965X_COM8_FAST_AGC_AEC |
51 OV965X_COM8_AEC_STEP_SIZE_NOLIMIT |
52 OV965X_COM8_AGC_ENABLE |
53 OV965X_COM8_AEC_ENABLE |
54 OV965X_COM8_AWB_ENABLE},
55 {OV965X_CTL_COM9, OV965X_COM9_MAX_AGC_8X |
56 OV965X_COM9_RELAX_EXPOSURE_TIMING |
57 OV965X_COM9_DROP_VSYNC_ON_FRAME_DROP |
58 OV965X_COM9_DROP_FRAME_ON_BIG_AEC},
59 {OV965X_CTL_COM10, 0x00},
60 {0x16, 0x07}, /* reserved */
61 {OV965X_CTL_HSTART, 0x24},
62 {OV965X_CTL_HSTOP, 0xc5},
63 {OV965X_CTL_VSTRT, 0x00},
64 {OV965X_CTL_VSTOP, 0x3c},
65 {OV965X_CTL_PSHIFT, 0x00}, /* default */
66 {OV965X_CTL_MVFP, 0x04},
67 {OV965X_CTL_LAEC, 0x00}, /* reserved */
68 {OV965X_CTL_AEW, 0x78}, /* default */
69 {OV965X_CTL_AEB, 0x68}, /* default */
70 {OV965X_CTL_VPT, 0xd4}, /* default */
71 {OV965X_CTL_BBIAS, OV965X_BIAS_SUBTRACT}, /* default */
72 {OV965X_CTL_GbBIAS, OV965X_BIAS_SUBTRACT}, /* default */
73 {OV965X_CTL_Gr_COM, OV965X_Gr_COM_BYPASS_ANALOG_BLC |
74 OV965X_Gr_COM_BYPASS_REGULATOR},
75 {OV965X_CTL_EXHCH, 0x00}, /* default */
76 {OV965X_CTL_EXHCL, 0x00}, /* default */
77 {OV965X_CTL_RBIAS, OV965X_BIAS_SUBTRACT}, /* default */
78 {OV965X_CTL_ADVFL, 0x00}, /* default */
79 {OV965X_CTL_ADVFH, 0x00}, /* default */
80 {OV965X_CTL_YAVE, 0x00}, /* default */
81 {OV965X_CTL_HSYST, 0x08}, /* default */
82 {OV965X_CTL_HSYEN, 0x30}, /* default */
83 {OV965X_CTL_HREF, OV965X_HREF_EDGE_OFT_TO_DATA_OUT(2) |
84 OV965X_HREF_HSTOP_LOW3(0) |
85 OV965X_HREF_HSTART_LOW3(4)},
86 {OV965X_CTL_CHLF, 0xe2}, /* reserved */
87 {OV965X_CTL_ARBLM, 0xbf}, /* reserved */
88 {0x35, 0x81}, /* reserved */
89 {0x36, 0xf9}, /* reserved */
90 {OV965X_CTL_ADC, 0x00}, /* reserved */
91 {OV965X_CTL_ACOM, 0x93}, /* reserved */
92 {OV965X_CTL_OFON, 0x50},
93 {OV965X_CTL_TSLB, OV965X_TSLB_OUTPUT_SEQ_UYVY |
94 OV965X_TSLB_DIGITAL_BLC_ENABLE},
95 {OV965X_CTL_COM11, OV965X_COM11_MANUAL_BANDING_FILTER},
96 {OV965X_CTL_COM12, 0x73},
97 {OV965X_CTL_COM13, OV965X_COM13_ENABLE_COLOR_MATRIX |
98 OV965X_COM13_DELAY_Y_CHANNEL |
99 OV965X_COM13_OUTPUT_DELAY(1)},
100 {OV965X_CTL_COM14, OV965X_COM14_YUV_EDGE_ENHANCE |
101 OV965X_COM14_EDGE_ENHANCE_FACTOR_DBL | 0x0b},
102 {OV965X_CTL_EDGE, OV965X_EDGE_EDGE_ENHANCE_LOW4(8) |
103 OV965X_EDGE_EDGE_ENHANCE_FACTOR(8)},
104 {OV965X_CTL_COM15, OV965X_COM15_OUTPUT_RANGE_O0_TO_FF | 0x01},
105 {OV965X_CTL_COM16, 0x00},
106 {OV965X_CTL_COM17, 0x08},
107 {OV965X_CTL_MANU, 0x80}, /* default */
108 {OV965X_CTL_MANV, 0x80}, /* default */
109 {OV965X_CTL_HV, 0x40},
110 {OV965X_CTL_MBD, 0x00}, /* default */
111 {OV965X_CTL_DBLV, 0x0a}, /* reserved */
112 {OV965X_CTL_COM21, 0x06}, /* reserved */
113 {OV965X_CTL_COM22, 0x20},
114 {OV965X_CTL_COM23, 0x00}, /* default */
115 {OV965X_CTL_COM24, 0x00}, /* reserved */
116 {OV965X_CTL_DBLC1, 0xdf},
117 {OV965X_CTL_DM_LNL, 0x00}, /* default */
118 {OV965X_CTL_DM_LNH, 0x00}, /* default */
119 {0x94, 0x88}, /* reserved */
120 {0x95, 0x88}, /* reserved */
121 {0x96, 0x04}, /* reserved */
122 {OV965X_CTL_AECHM, 0x00},
123 {OV965X_CTL_COM26, 0x80}, /* reserved */
124 {0xa8, 0x80}, /* reserved */
125 {0xa9, 0xb8}, /* reserved */
126 {0xaa, 0x92}, /* reserved */
127 {0xab, 0x0a}, /* reserved */
130 int ov965x_initialize(struct usb_microdia *dev)
132 int ret, i;
133 __u8 value, reg;
135 for (i = 0; i < ARRAY_SIZE(ov965x_init); i++) {
136 reg = ov965x_init[i][0];
137 value = ov965x_init[i][1];
138 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address, 1,
139 reg, dev->sensor_flags, &value);
140 if (ret < 0) {
141 UDIA_INFO("Sensor Init Error (%d). line %d\n", ret, i);
142 break;
146 return ret;
150 static __u8 soi968_init[][2] = {
151 /* reset all registers */
152 {0x12, 0x80},
153 /* stop resetting */
154 {0x12, 0x00},
156 /* snapshot mode: off */
157 {0x0c, 0x00},
159 /* enable offset adjustment,
160 * full data range,
161 * analogue2digital control black-level calibration
163 {0x0f, 0x1f},
165 /* Clock: internal PLL on */
166 {0x11, 0x80},
169 /* Analoge Black-level calibration off , no anaolgue gain */
170 {0x38, 0x52},
172 /* reserved */
173 {0x1e, 0x00},
175 /* special system settings (voltage, analogue2digital, ...) */
176 {0x33, 0x08},
177 {0x35, 0x8c},
178 {0x36, 0x0c},
179 {0x37, 0x04},
181 /* next 7 are unknown/reserved */
182 {0x45, 0x04},
183 {0x47, 0xff},
184 {0x3e, 0x00},
185 {0x3f, 0x00},
186 {0x3b, 0x20},
187 {0x3a, 0x96},
188 {0x3d, 0x0a},
190 /* disable banding filter in dark environment,
191 * VSYNC is dropped when framerate is dropped,
192 * drop frmaes when exposure out of tolerance,
193 * unfreeze exposure and gain values
195 {0x14, 0x4e},
197 /* AEC, AGC, AWB disabled; fast AEC */
198 {0x13, 0x88},
200 /* output: VGA, master mode */
201 {0x12, 0x40},
203 /* set HSTART, HSTOP, VSTART and VSTOP */
204 {0x17, 0x13},
205 {0x18, 0x63},
206 {0x19, 0x01},
207 {0x1a, 0x79},
208 {0x32, 0x24}, /* LSB for all four */
210 /* this register contains the product ID, it is read-only
211 {0x1b, 0x00}, */
213 /* AWB update threshold,
214 * blue and red gain LSB
216 {0x03, 0x00},
218 /* CLock: internal PLL off */
219 {0x11, 0x40},
221 /* Line interval adjustment */
222 {0x2a, 0x10},
223 {0x2b, 0xe0},
225 /* AEC MSB */
226 {0x10, 0x32},
228 /* gain - default*/
229 {0x00, 0x00},
231 /* blue and red gain - default*/
232 {0x01, 0x80},
233 {0x02, 0x80}
236 int soi968_initialize(struct usb_microdia *dev)
238 int ret, i;
239 __u8 value, reg;
241 for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
242 reg = soi968_init[i][0];
243 value = soi968_init[i][1];
244 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address, 1,
245 reg, dev->sensor_flags, &value);
246 if (ret < 0) {
247 UDIA_INFO("Sensor Init Error (%d). line %d\n", ret, i);
248 break;
252 return ret;
256 int ov965x_set_hvflip(struct usb_microdia *dev)
258 int ret;
259 __u8 value;
261 * Changing hstop value seems to be necessary to keep correct
262 * colors during a vflip. The values don't seem to make much
263 * sense since to keep the correct color value i'm setting hstop
264 * to the same value as hstart is set for.
266 __u8 hstop = 0xc5;
267 ret = sn9c20x_read_i2c_data(dev, dev->sensor_slave_address, 1,
268 OV965X_CTL_MVFP, dev->sensor_flags, &value);
269 if (ret < 0)
270 return ret;
272 if (dev->vsettings.hflip)
273 value |= OV965X_MVFP_MIRROR;
274 else
275 value &= ~OV965X_MVFP_MIRROR;
277 if (dev->vsettings.vflip) {
278 hstop = 0x24;
279 value |= OV965X_MVFP_VFLIP;
280 } else {
281 value &= ~OV965X_MVFP_VFLIP;
284 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address, 1,
285 OV965X_CTL_HSTOP, dev->sensor_flags, &hstop);
287 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address, 1,
288 OV965X_CTL_MVFP, dev->sensor_flags, &value);
290 return ret;
293 int ov965x_set_exposure(struct usb_microdia *dev)
295 int ret = 0;
296 __u8 v1 = (dev->vsettings.exposure >> 4) & 0xff;
297 __u8 v2 = dev->vsettings.exposure >> 12;
299 ret |= sn9c20x_write_i2c_data(dev, dev->sensor_slave_address, 1, 0x2d,
300 dev->sensor_flags, &v1);
302 ret |= sn9c20x_write_i2c_data(dev, dev->sensor_slave_address, 1, 0x2e,
303 dev->sensor_flags, &v2);
305 return ret;
309 * @brief Set autoexposure for ov96xx sensors
311 * @param dev
313 * @returns 0 or negative error value
315 * @author GWater
317 * For all OV965x and SOI968 sensors.
319 int ov965x_set_autoexposure(struct usb_microdia *dev)
321 __u8 buf[1];
322 int ret = 0;
324 /* Read current value of the I2C-register
325 * controlling AutoExposureControl:
327 ret = sn9c20x_read_i2c_data(dev, dev->sensor_slave_address,
328 1, 0x13, dev->sensor_flags, buf);
329 if (ret < 0) {
330 UDIA_ERROR("Error: setting of auto exposure failed: "
331 "error while reading from I2C-register 0x13");
332 return ret;
335 /* Determine new value for register 0x13: */
336 if (dev->vsettings.auto_exposure == 1) {
337 /* Enable automatic exposure: */
338 buf[0] |= 0x01;
339 } else if (dev->vsettings.auto_exposure == 0) {
340 /* Disable automatic exposure: */
341 buf[0] &= ~0x01;
342 } else
343 return -EINVAL;
345 /* Write new value to I2C-register 0x13: */
346 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address,
347 1, 0x13, dev->sensor_flags, buf);
348 if (ret < 0) {
349 UDIA_ERROR("Error: setting of auto exposure failed: "
350 "error while writing to I2C-register 0x13");
351 return ret;
353 return 0;
357 * @brief Set exposure for SOI968 sensors
359 * @param dev
361 * @returns 0 or negative error value
363 * @author GWater
365 * For SOI968 sensors.
367 int soi968_set_exposure(struct usb_microdia *dev)
369 int value, ret;
370 int exposure = dev->vsettings.exposure;
371 __u8 buf1, buf2, buf3;
373 /* Read current value of the I2C-register
374 * containing exposure LSB:
376 ret = sn9c20x_read_i2c_data(dev, dev->sensor_slave_address,
377 1, 0x04, dev->sensor_flags, &buf1);
378 if (ret < 0) {
379 UDIA_ERROR("Error: setting exposure failed: "
380 "error while reading from I2C-register 0x04");
381 return ret;
384 value = (exposure * 0x07ff / 0xffff) & 0x07ff;
385 buf2 = ((__u8) (value & 0x07)) | (buf1 & ~0x07);
386 buf3 = (__u8) (value >> 3) & 0xff;
388 /* Write new value to I2C-register 0x04: */
389 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address,
390 1, 0x04, dev->sensor_flags, &buf2);
391 if (ret < 0) {
392 UDIA_ERROR("Error: setting exposure failed: "
393 "error while writing to I2C-register 0x04");
394 return ret;
397 /* Write new value to I2C-register 0x10: */
398 ret = sn9c20x_write_i2c_data(dev, dev->sensor_slave_address,
399 1, 0x10, dev->sensor_flags, &buf3);
400 if (ret < 0) {
401 UDIA_ERROR("Error: setting exposure failed: "
402 "error while writing to I2C-register 0x10");
403 return ret;
406 return 0;