Doxygen fixes and missing nextlines in UDIA
[microdia.git] / mt9vx11.c
blobd94d2e01340e60d91fb82f2d8c2e1fa8b21bae5d
1 /**
2 * @file mt9vx11.c
3 * @author Comer352l
4 * @date 2008-04-25
6 * @brief Common functions and data for the Micron MT9Vx11 sensors.
8 * @note Copyright (C) Comer352l
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 <linux/delay.h>
28 #include "microdia.h"
29 #include "sn9c20x.h"
30 #include "mt9vx11.h"
33 struct microdia_video_format mt9vx11_fmts[] = {
35 .pix_fmt = V4L2_PIX_FMT_SBGGR8,
36 .desc = "Bayer 8bit (BGGR)",
37 .depth = 8,
38 .set_format = sn9c20x_set_raw
41 .pix_fmt = V4L2_PIX_FMT_JPEG,
42 .desc = "JPEG (YUV 4:2:2)",
43 .depth = 16,
44 .set_format = sn9c20x_set_jpeg
48 struct microdia_video_resolution mt9vx11_resolutions[] = {
50 .width = 160,
51 .height = 120,
52 .scale = SN9C20X_1_4_SCALE,
53 .window = {2, 2, 640, 480}
56 .width = 320,
57 .height = 240,
58 .scale = SN9C20X_1_2_SCALE,
59 .window = {2, 2, 640, 480}
62 .width = 640,
63 .height = 480,
64 .scale = SN9C20X_NO_SCALE,
65 .window = {2, 2, 640, 480}
69 static __u8 mt9v111_init[][3] = {
70 /* 0x0d: Color Correction Register 8 */
71 {0x0d ,0x00, 0x01},
72 /* 0x0d: Color Correction Register 8 */
73 {0x0d ,0x00, 0x00}, /* ??? */
74 /* 0x02: Color Correction Register 1 */
75 {0x01 ,0x00, 0x01}, /* select IFP address space */
76 {0x02 ,0x00, 0x16}, /* ??? */
77 /* 0x03: Color Correction Register 3 */
78 /* 0x04: Color Correction Register 4 */
79 {0x03 ,0x01, 0xe1}, /* ??? */
80 {0x04 ,0x02, 0x81}, /* ??? */
81 /* 0x05: Aperture Correction (Sharpening) */
82 /* 0x06: Operating Mode Control */
83 {0x05 ,0x00, 0x04}, /* 100% sharpening,
84 no automatic sharpness reduction at low light: no sharpening */
85 {0x06 ,0x00, 0x00}, /* stop AWB at the current values,
86 no on-the-fly defect correction, no auto exposure */
87 /* 0x07: IFP Soft Reset */
88 /* 0x08: Output Format Control */
89 {0x07 ,0x30, 0x02}, /* reset */
90 {0x08 ,0x04, 0x80}, /* bypass entire image processing,
91 raw 8+2 Bayer data output directly */
92 {0x01 ,0x00, 0x04}, /* select sensor address space */
93 {0x02 ,0x00, 0x16}, /* start with column 22 */
94 /* 0x03: Window Height */
95 /* 0x04: Window Width */
96 {0x03 ,0x01, 0xe6}, /* 486 */
97 {0x04 ,0x02, 0x86}, /* 646 */
98 /* 0x05: Horizontal Blanking */
99 /* 0x06: Vertical Blanking */
100 {0x05 ,0x00, 0x04}, /* 4 columns (pixel clocks) */
101 {0x06 ,0x00, 0x00}, /* 0 rows */
102 /* 0x07: Output Control */
103 /* 0x08: Row Start */
104 {0x07 ,0x30, 0x02}, /* normal operation + chip enable*/
105 {0x08 ,0x00, 0x08}, /* row 8 */
106 /* 0x0c: Shutter Delay */
107 /* 0x0d: Reset (Soft) */
108 {0x0c ,0x00, 0x00}, /* 0 master clocks */
109 {0x0d ,0x00, 0x00}, /* return to normal operation */
110 {0x0e ,0x00, 0x00},
111 {0x0f ,0x00, 0x00},
112 {0x10 ,0x00, 0x00},
113 {0x11 ,0x00, 0x00},
114 /* 0x12: 2X Zoom Col Start
115 => 0x1e bit 0 must be set to activate zoom */
116 /* 0x13: 2X Zoom Row Start
117 => 0x1e bit 0 must be set to activate zoom */
118 {0x12 ,0x00, 0xb0}, /* column 176 */
119 {0x13 ,0x00, 0x7c}, /* row 124 */
120 {0x14 ,0x00, 0x00},
121 {0x15 ,0x00, 0x00},
122 {0x16 ,0x00, 0x00},
123 {0x17 ,0x00, 0x00},
124 {0x18 ,0x00, 0x00},
125 {0x19 ,0x00, 0x00},
126 {0x1a ,0x00, 0x00},
127 {0x1b ,0x00, 0x00},
128 {0x1c ,0x00, 0x00},
129 {0x1d ,0x00, 0x00},
130 {0x30 ,0x00, 0x00},
131 {0x30 ,0x00, 0x05},
132 {0x31 ,0x00, 0x00},
133 /* 0x02: Column Start */
134 /* 0x03: Window Height */
135 {0x02 ,0x00, 0x16}, /* coulmn 22 */
136 {0x03 ,0x01, 0xe1}, /* 481 */
137 /* 0x04: Window Width */
138 /* 0x05: Horizontal Blanking */
139 {0x04 ,0x02, 0x81}, /* 641 */
140 {0x05 ,0x00, 0x04}, /* 4 columns (pixel clocks) */
141 /* 0x06: Vertical Blanking */
142 /* 0x07: Output Control */
143 {0x06 ,0x00, 0x00}, /* 0 rows */
144 {0x07 ,0x30, 0x02}, /* normal operation + chip enable */
145 /* 0x06: Vertical Blanking */
146 {0x06 ,0x00, 0x2d}, /* 45 rows */
147 /* 0x05: Horizontal Blanking */
148 {0x05 ,0x00, 0x04}, /* 4 columns (pixel clocks) */
149 /* 0x09: Shutter Width */
150 {0x09 ,0x00, 0x64}, /* integration of 100 rows */
151 /* 0x2b: Green 1 Gain */
152 /* 0x2c: Blue Gain */
153 {0x2b ,0x00, 0xa0}, /* 32*0.03125*2 = 2 */
154 {0x2c ,0x00, 0xa0}, /* 32*0.03125*2 = 2 */
155 /* 0x2d: Red Gain */
156 /* 0x2e: Green 2 Gain */
157 {0x2d ,0x00, 0xa0}, /* 32*0.03125*2 = 2 */
158 {0x2e ,0x00, 0xa0}, /* 32*0.03125*2 = 2 */
159 /* 0x02: Column Start */
160 /* 0x03: Window Hight */
161 {0x02 ,0x00, 0x16}, /* coulmn 22 */
162 {0x03 ,0x01, 0xe1}, /* 481 */
163 /* 0x04: Window Width */
164 /* 0x05: Horizontal Blanking */
165 {0x04 ,0x02, 0x81}, /* 641 */
166 {0x05 ,0x00, 0x04}, /* 4 columns (pixel clocks) */
167 /* 0x06: Vertical Blanking */
168 /* 0x07: Output Control */
169 {0x06 ,0x00, 0x2d}, /* 45 rows */
170 {0x07 ,0x30, 0x02}, /* RESERVED options */
171 /* Writes to0x0e: UNDOCUMENTED */
172 {0x0e ,0x00, 0x08},
173 /* 0x06: Vertical Blanking */
174 {0x06 ,0x00, 0x2d}, /* 45 rows */
175 /* 0x05: Horizontal Blanking */
176 {0x05 ,0x00, 0x04}, /* 4 columns (pixel clocks) */
179 static __u8 mt9v011_init[][3] = {
180 /* 0x07: Output Control */
181 {0x07, 0x00, 0x02}, /* chip enable, normal operation */
182 /* 0x0d: Soft Reset */
183 {0x0d, 0x00, 0x01}, /* reset */
184 /* 0x0d: Soft Reset */
185 {0x0d, 0x00, 0x00}, /* resume operation */
186 /* 0x01: Row start */
187 /* 0x02: Column Start */
188 {0x01, 0x00, 0x08}, /* start with row 8 */
189 {0x02, 0x00, 0x16}, /* start with column 22 */
190 /* 0x03: Window Height */
191 /* 0x04: Window Width */
192 {0x03, 0x01, 0xe1}, /* 481 */
193 {0x04, 0x02, 0x81}, /* 641 */
194 /* 0x05: Horizontal Blanking */
195 /* 0x06: Vertical Blanking */
196 {0x05, 0x00, 0x83}, /* 131 columns (pixel clocks) */
197 {0x06, 0x00, 0x06}, /* 6 rows */
198 /* 0x0d: Soft Reset */
199 {0x0d, 0x00, 0x02}, /* UNKNOWN */
200 /* 0x0a: Pixel Clock Speed */
201 /* 0x0b: Frame Restart */
202 {0x0a, 0x00, 0x00}, /* default */
203 {0x0b, 0x00, 0x00}, /* (has no effect/no restart) */
204 /* 0x0c: Shutter Delay */
205 /* 0x0d: Soft Reset */
206 {0x0c, 0x00, 0x00}, /* 0 master clocks */
207 {0x0d, 0x00, 0x00}, /* resume operation */
208 {0x0e, 0x00, 0x00},
209 {0x0f, 0x00, 0x00},
210 {0x10, 0x00, 0x00},
211 {0x11, 0x00, 0x00},
212 /* 0x12: 2X Zoom Column Start (from MT9V111 datasheet) */
213 /* 0x13: 2X Zoom Row Start (from MT9V111 datasheet) */
214 {0x12, 0x00, 0x00},
215 /* column 0 => bit0 of reg 0x1e must be set to activate zoom */
216 {0x13, 0x00, 0x00},
217 /* row 0 => bit0 of reg 0x1e must be set to activate zoom */
218 {0x14, 0x00, 0x00},
219 {0x15, 0x00, 0x00},
220 {0x16, 0x00, 0x00},
221 {0x17, 0x00, 0x00},
222 {0x18, 0x00, 0x00},
223 {0x19, 0x00, 0x00},
224 {0x1a, 0x00, 0x00},
225 {0x1b, 0x00, 0x00},
226 {0x1c, 0x00, 0x00},
227 {0x1d, 0x00, 0x00},
228 {0x32, 0x00, 0x00},
229 /* 0x20: Read Mode */
230 {0x20, 0x11, 0x01}, /* output all frames (including bad frames) */
231 {0x21, 0x00, 0x00},
232 {0x22, 0x00, 0x00},
233 {0x23, 0x00, 0x00},
234 {0x24, 0x00, 0x00},
235 {0x25, 0x00, 0x00},
236 {0x26, 0x00, 0x00},
237 {0x27, 0x00, 0x24},
238 {0x2f, 0xf7, 0xb0},
239 {0x30, 0x00, 0x05},
240 {0x31, 0x00, 0x00},
241 {0x32, 0x00, 0x00},
242 {0x33, 0x00, 0x00},
243 {0x34, 0x01, 0x00},
244 {0x3d, 0x06, 0x8f},
245 {0x40, 0x01, 0xe0},
246 {0x41, 0x00, 0xd1},
247 {0x44, 0x00, 0x82},
248 {0x5a, 0x00, 0x00},
249 {0x5b, 0x00, 0x00},
250 {0x5c, 0x00, 0x00},
251 {0x5d, 0x00, 0x00},
252 {0x5e, 0x00, 0x00},
253 {0x5f, 0xa3, 0x1d},
254 {0x62, 0x06, 0x11},
255 /* 0x0a: Pixel Clock Speed */
256 {0x0a, 0x00, 0x00}, /* default */
257 /* 0x06: Vertical Blanking */
258 {0x06, 0x00, 0x29}, /* 41 rows */
259 /* 0x05: Horizontal Blanking */
260 {0x05, 0x00, 0x09}, /* 9 columns (pixel clocks) */
261 /* 0x20: Read Mode */
262 {0x20, 0x11, 0x01}, /* output all frames (including bad ones) */
263 /* 0x20: Read Mode */
264 {0x20, 0x11, 0x01}, /* output all frames (including bad ones) */
265 /* 0x09: Shutter Width */
266 {0x09, 0x00, 0x64}, /* integration of 100 rows */
267 /* 0x07: Output Control */
268 {0x07, 0x00, 0x03},
269 /* dont update changes until bit0=0, chip enable, normal operation */
270 /* 0x2b: Green 1 Gain */
271 /* 0x2c: Blue Gain */
272 {0x2b, 0x00, 0x33}, /* 51*0.03125*1 = 1.59375 */
273 {0x2c, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
274 /* 0x2d: Red Gain */
275 /* 0x2e: Green 2 Gain */
276 {0x2d, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
277 {0x2e, 0x00, 0x33}, /* 51*0.03125*1 = 1.59375 */
278 /* 0x07: Output Control */
279 {0x07, 0x00, 0x02}, /* chip enable, normal operation */
280 /* 0x0a: Pixel Clock Speed */
281 {0x06, 0x00, 0x00}, /* default */
282 /* 0x06: Vertical Blanking */
283 {0x06, 0x00, 0x29}, /* 41 rows */
284 /* 0x05: Horizontal Blanking */
285 {0x05, 0x00, 0x09}, /* 9 columns (pixel clocks) */
289 * @brief Initialize mt9v011 sensors
291 * @param dev Pointer to device structure
293 * @return 0 or negative error code
296 int mt9v011_initialize(struct usb_microdia *dev)
298 int i;
299 int ret = 0;
300 __u8 value[2], reg;
301 dev->camera.sensor_slave_address = MT9V011_I2C_SLAVE_ADDRESS;
302 for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
303 reg = mt9v011_init[i][0];
304 value[0] = mt9v011_init[i][1];
305 value[1] = mt9v011_init[i][2];
306 ret = sn9c20x_write_i2c_data(dev, 2, reg, value);
307 if (ret < 0) {
308 UDIA_INFO("Sensor Init Error (%d). line %d\n", ret, i);
309 break;
313 return ret;
317 * @brief Initialize mt9v111 sensors
319 * @param dev Pointer to device structure
321 * @return 0 or negative error code
324 int mt9v111_initialize(struct usb_microdia *dev)
326 int i;
327 int ret = 0;
328 __u8 value[2], reg;
329 dev->camera.sensor_slave_address = MT9V111_I2C_SLAVE_ADDRESS;
330 mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
332 for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
333 reg = mt9v111_init[i][0];
334 value[0] = mt9v111_init[i][1];
335 value[1] = mt9v111_init[i][2];
336 ret = sn9c20x_write_i2c_data(dev, 2, reg, value);
337 if (ret < 0) {
338 UDIA_INFO("Sensor Init Error (%d). line %d\n", ret, i);
339 break;
343 mt9v111_setup_autoexposure(dev);
344 mt9v111_setup_autowhitebalance(dev);
345 mt9v111_set_autocorrections(dev, 1);
347 return ret;
350 int mt9v011_probe(struct usb_microdia *dev)
352 __u8 buf[2];
353 int ret;
355 ret = sn9c20x_read_i2c_data(dev, 2, 0xff, buf);
356 if (ret == 0) {
357 if (buf[0] != 0x82)
358 return -EINVAL;
359 if (buf[1] == 0x43) {
360 mt9v011_initialize(dev);
361 dev->camera.set_hvflip = mt9v011_set_hvflip;
362 dev->camera.set_exposure = mt9v011_set_exposure;
363 dev->camera.modes = mt9vx11_resolutions;
364 dev->camera.nmodes = ARRAY_SIZE(mt9vx11_resolutions);
365 dev->camera.fmts = mt9vx11_fmts;
366 dev->camera.nfmts = ARRAY_SIZE(mt9vx11_fmts);
367 return MT9V011_SENSOR;
369 } else
370 UDIA_INFO("Failed on i2c read in mt9v011_probe\n");
372 /** NOTE: DNT DigiMicro 1.3 (microscope camera):
373 * This device uses sensor MT9V111, but slave 0x5d is also successfully
374 * read. Registers 0x00, 0x36 and 0xff of slave 0x5d return chip version
375 * 0x0000. */
377 return -EINVAL;
380 int mt9v111_select_address_space(struct usb_microdia *dev, __u8 address_space)
382 __u8 buf[2];
383 int retI2C;
384 int k;
386 /* check if selection is valid: */
387 if ((address_space != MT9V111_ADDRESSSPACE_IFP) &&
388 (address_space != MT9V111_ADDRESSSPACE_SENSOR)) {
389 UDIA_INFO("Error: invalid register address space "
390 "selection for sensor MT9V111/MI0360SOC !\n");
391 return -1;
393 /* read address space slection register: */
394 k = 0;
395 retI2C = -1;
396 while ((k < 3) && (retI2C != 0)) {
397 retI2C = sn9c20x_read_i2c_data(dev, 2, 0x01, buf);
398 if (retI2C != 0 && k < 2)
399 udelay(1000);
400 k++;
402 if (retI2C != 0) {
403 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): "
404 "read of reg0x01 (address space selection) failed !\n");
405 return -1;
407 /* check current address space: */
408 if ((buf[0] != 0x00) || (buf[1] != address_space)) {
409 k = 0;
410 retI2C = -1;
411 while ((k < 3) && (retI2C != 0)) {
412 /* switch address space: */
413 buf[0] = 0x00; buf[1] = address_space;
414 retI2C = sn9c20x_write_i2c_data(dev, 2, 0x01, buf);
415 if (retI2C != 0 && k < 2)
416 udelay(1000);
417 k++;
419 if (retI2C != 0) {
420 if (address_space == MT9V111_ADDRESSSPACE_IFP)
421 UDIA_INFO("Error: MT9V111/MI0360SOC "
422 "(I2C-slave 0x5c): switch to IFP "
423 "address space failed !\n");
424 else
425 UDIA_INFO("Error: MT9V111/MI0360SOC "
426 "(I2C-slave 0x5c): switch to sensor "
427 "core address space failed !\n");
428 return -1;
431 return 0;
434 int mt9v111_probe(struct usb_microdia *dev)
436 __u8 buf[2];
437 int ret;
439 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
440 if (ret != 0)
441 return -1;
443 ret = sn9c20x_read_i2c_data(dev, 2, 0xff, buf);
444 if (ret == 0) {
445 if (buf[0] != 0x82)
446 return -EINVAL;
447 if (buf[1] == 0x3a) {
448 mt9v111_initialize(dev);
449 dev->camera.set_hvflip = mt9v111_set_hvflip;
450 dev->camera.set_exposure = mt9v111_set_exposure;
451 dev->camera.set_auto_exposure = mt9v111_set_autoexposure;
452 dev->camera.modes = mt9vx11_resolutions;
453 dev->camera.nmodes = ARRAY_SIZE(mt9vx11_resolutions);
454 dev->camera.fmts = mt9vx11_fmts;
455 dev->camera.nfmts = ARRAY_SIZE(mt9vx11_fmts);
456 return MT9V111_SENSOR;
458 } else
459 UDIA_INFO("Failed on i2c read in mt9v111_probe\n");
461 /** FIXME: always switch back to last register address space */
463 return -EINVAL;
466 int mt9v111_setup_autoexposure(struct usb_microdia *dev)
468 __u16 buf[1];
469 int ret = 0;
470 int ret2 = 0;
472 /* Switch to IFP-register address space: */
473 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
474 if (ret < 0)
475 return -11; /* -EAGAIN */
476 /* Set target luminance and tracking accuracy: */
477 buf[0] = MT9V111_IFP_AE_TRACKINGACCURACY(12)
478 /* 0-255 (default: 16) */
479 | MT9V111_IFP_AE_TARGETLUMINANCE(100);
480 /* 0-255 (default: 100) */
481 ret = sn9c20x_write_i2c_data16(dev, 1,
482 MT9V111_IFPREG_AE_TARGETLUMCTL, buf);
483 /* Set speed and sensitivity: */
484 buf[0] = MT9V111_IFP_AE_DELAY(4)
485 /* 0-7 (fastest-slowest) (default: 4) */
486 | MT9V111_IFP_AE_SPEED(4)
487 /* 0-7 (fastest-slowest) (default: 4) */
488 | MT9V111_IFP_AE_STEPSIZE_MEDIUM;
489 ret += sn9c20x_write_i2c_data16(dev, 1,
490 MT9V111_IFPREG_AE_SPEEDSENSCTL, buf);
491 /* Horizontal boundaries of AE measurement window: */
492 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT(1020)
493 /* 0-1020 (pixel) (default: 1020) */
494 | MT9V111_IFP_AE_WINBOUNDARY_LEFT(12);
495 /* 0-1020 (pixel) (default: 12) */
496 ret += sn9c20x_write_i2c_data16(dev, 1,
497 MT9V111_IFPREG_AE_HWINBOUNDARY, buf);
498 /* Vertical boundaries of AE measurement window: */
499 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM(510)
500 /* 0-510 (pixel) (default: 510) */
501 | MT9V111_IFP_AE_WINBOUNDARY_TOP(32);
502 /* 0-510 (pixel) (default: 32) */
503 ret += sn9c20x_write_i2c_data16(dev, 1,
504 MT9V111_IFPREG_AE_VWINBOUNDARY, buf);
505 /* Horizontal boundaries of AE measurement window
506 * for back light compensation: */
507 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT_BLC(480)
508 /* 0-1020 (pixel) (default: 480) */
509 | MT9V111_IFP_AE_WINBOUNDARY_LEFT_BLC(160);
510 /* 0-1020 (pixel) (default: 160) */
511 ret += sn9c20x_write_i2c_data16(dev, 1,
512 MT9V111_IFPREG_AE_HWINBOUNDARY_BLC, buf);
513 /* Vertical boundaries of AE measurement window
514 * for back light compensation: */
515 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM_BLC(360)
516 /* 0-510 (pixel) (default: 360) */
517 | MT9V111_IFP_AE_WINBOUNDARY_TOP_BLC(120);
518 /* 0-510 (pixel) (default: 120) */
519 ret += sn9c20x_write_i2c_data16(dev, 1,
520 MT9V111_IFPREG_AE_VWINBOUNDARY_BLC, buf);
521 /* Set digital gains limit: */
522 buf[0] = MT9V111_IFP_AE_MAXGAIN_POSTLS(64)
523 /* 0-255 (default: 64) */
524 | MT9V111_IFP_AE_MAXGAIN_PRELS(16);
525 /* 0-255 (default: 16) */
526 ret += sn9c20x_write_i2c_data16(dev, 1,
527 MT9V111_IFPREG_AE_DGAINSLIMITS, buf);
528 /* Read current value of IFP-register 0x06: */
529 ret2 = sn9c20x_read_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
530 if (ret2 == 0) {
531 /* Set new value for register 0x06: */
532 buf[0] = (buf[0] & 0xffe3) | MT9V111_IFP_AE_WIN_COMBINED |
533 MT9V111_IFP_OPMODE_BYPASSCOLORCORR;
534 /* NOTE: BYPASS COLOR CORRECTION HAS
535 * TO BE SET FOR PERMANENT AE ! */
536 /* Write new value to IFP-register 0x06:*/
537 ret += sn9c20x_write_i2c_data16(dev, 1,
538 MT9V111_IFPREG_OPMODECTL, buf);
540 if (ret < 0 || ret2 < 0) {
541 UDIA_INFO("One or more errors occured during "
542 "setup of AE registers.\n");
543 return -1;
545 return 0;
548 int mt9v111_setup_autowhitebalance(struct usb_microdia *dev)
550 __u16 buf[1];
551 int ret = 0;
553 /* Switch to IFP-register address space: */
554 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
555 if (ret < 0)
556 return -11; /* -EAGAIN */
557 /* Set AWB tint: */
558 buf[0] = MT9V111_IFP_AWB_ADDON_RED(0) /* 0-255 (default: 0) */
559 | MT9V111_IFP_AWB_ADDON_BLUE(0); /* 0-255 (default: 0) */
560 ret = sn9c20x_write_i2c_data16(dev, 1, MT9V111_IFPREG_AWB_TINT, buf);
561 /* Set AWB speed and color saturation: */
562 buf[0] = MT9V111_IFP_AWB_SATURATION_FULL
563 | MT9V111_IFP_AWB_AUTOSATLOWLIGHT_ENABLE
564 | MT9V111_IFP_AWB_DELAY(4)
565 /* 0-7 (fastest-slowest) (default: 4) */
566 | MT9V111_IFP_AWB_SPEED(4);
567 /* 0-7 (fastest-slowest) (default: 4) */
568 ret += sn9c20x_write_i2c_data16(dev, 1,
569 MT9V111_IFPREG_AWB_SPEEDCOLORSATCTL, buf);
570 /* Boundaries of AWB measurement window: */
571 buf[0] = MT9V111_IFP_AWB_WINBOUNDARY_TOP(0)
572 /* 0-480 (pixel) (default: 0) */
573 | MT9V111_IFP_AWB_WINBOUNDARY_BOTTOM(480)
574 /* 0-480 (pixel) (default: 448) */
575 | MT9V111_IFP_AWB_WINBOUNDARY_LEFT(0)
576 /* 0-960 (pixel) (default: 0) */
577 | MT9V111_IFP_AWB_WINBOUNDARY_RIGHT(640);
578 /* 0-960 (pixel) (default: 640) */
579 ret += sn9c20x_write_i2c_data16(dev, 1,
580 MT9V111_IFPREG_AWB_WINBOUNDARY, buf);
582 if (ret < 0) {
583 UDIA_INFO("One or more errors occured during "
584 "setup of AWB registers.\n");
585 return -1;
587 return 0;
590 int mt9v011_set_exposure(struct usb_microdia *dev)
592 int ret = 0;
593 __u8 buf[2];
595 buf[0] = (dev->vsettings.exposure >> 12);
596 buf[1] = 0;
597 ret |= sn9c20x_write_i2c_data(dev, 2, 0x09, buf);
598 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not
599 * used) for MT9V111 sensor, because IFP controls this register if
600 * one of them is enabled. */
601 return ret;
604 int mt9v111_set_exposure(struct usb_microdia *dev)
606 int ret = 0;
607 __u8 buf[2];
610 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
612 if (ret < 0)
613 return -11; /* -EAGAIN */
615 buf[0] = (dev->vsettings.exposure >> 12);
616 buf[1] = 0;
617 ret |= sn9c20x_write_i2c_data(dev, 2, 0x09, buf);
618 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not
619 * used) for MT9V111 sensor, because IFP controls this register if
620 * one of them is enabled. */
621 return ret;
624 int mt9v011_set_hvflip(struct usb_microdia *dev)
626 int ret = 0;
627 __u8 buf[2];
629 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
630 return -EINVAL;
631 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
632 return -EINVAL;
634 ret = sn9c20x_read_i2c_data(dev, 2, 0x20, buf);
635 if (ret < 0)
636 return ret;
638 if (dev->vsettings.hflip) {
639 buf[0] |= 0x80;
640 /* (MSB) set bit 15: read out from
641 * bottom to top (upside down) */
642 buf[1] |= 0x80;
643 /* (LSB) set bit 7: readout starting 1 row later */
644 } else {
645 buf[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
646 buf[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
648 if (dev->vsettings.vflip) {
649 buf[0] |= 0x40;
650 /* (MSB) set bit 14: read out from right to left (mirrored) */
651 buf[1] |= 0x20;
652 /* (LSB) set bit 5: readout starting 1 column later */
653 } else {
654 buf[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
655 buf[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
658 ret = sn9c20x_write_i2c_data(dev, 2, 0x20, buf);
659 return ret;
662 int mt9v111_set_hvflip(struct usb_microdia *dev)
664 int ret = 0;
665 __u8 buf[2];
667 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
668 return -EINVAL;
669 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
670 return -EINVAL;
673 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
674 if (ret < 0)
675 return -11; /* -EAGAIN */
677 ret = sn9c20x_read_i2c_data(dev, 2, 0x20, buf);
678 if (ret < 0)
679 return ret;
681 if (dev->vsettings.hflip) {
682 buf[0] |= 0x80;
683 /* (MSB) set bit 15: read out from
684 * bottom to top (upside down) */
685 buf[1] |= 0x80;
686 /* (LSB) set bit 7: readout starting 1 row later */
687 } else {
688 buf[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
689 buf[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
691 if (dev->vsettings.vflip) {
692 buf[0] |= 0x40;
693 /* (MSB) set bit 14: read out from right to left (mirrored) */
694 buf[1] |= 0x20;
695 /* (LSB) set bit 5: readout starting 1 column later */
696 } else {
697 buf[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
698 buf[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
701 ret = sn9c20x_write_i2c_data(dev, 2, 0x20, buf);
702 return ret;
705 int mt9v111_set_autoexposure(struct usb_microdia *dev)
707 __u16 buf[1];
708 int ret = 0;
710 /* Switch to IFP-register address space: */
711 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
712 if (ret < 0)
713 return -11; /* -EAGAIN */
714 /* Read current value of IFP-register 0x06: */
715 ret = sn9c20x_read_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
716 if (ret < 0) {
717 UDIA_ERROR("Error: setting of auto exposure failed: "
718 "error while reading from IFP-register 0x06\n");
719 return ret;
721 /* Set new value for register 0x06: */
722 if (dev->vsettings.auto_exposure == 1) {
723 /* Enable automatic exposure: */
724 buf[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE;
725 } else if (dev->vsettings.auto_exposure == 0) {
726 /* Disable automatic exposure: */
727 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE;
728 } else
729 return -EINVAL;
730 /* Write new value to IFP-register 0x06: */
731 ret = sn9c20x_write_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
732 if (ret < 0) {
733 UDIA_ERROR("Error: setting of auto exposure failed: "
734 "error while writing to IFP-register 0x06\n");
735 return ret;
737 return 0;
740 int mt9v111_set_autowhitebalance(struct usb_microdia *dev)
742 __u16 buf[1];
743 int ret = 0;
745 /* Switch to IFP-register address space: */
746 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
747 if (ret < 0)
748 return -11; /* -EAGAIN */
749 /* Read current value of IFP-register 0x06: */
750 ret = sn9c20x_read_i2c_data16(dev, 1,
751 MT9V111_IFPREG_OPMODECTL, buf);
752 if (ret < 0) {
753 UDIA_ERROR("Error: setting of auto whitebalance failed: "
754 "error while reading from IFP-register 0x06\n");
755 return ret;
757 /* Set new value for register 0x06: */
758 if (dev->vsettings.auto_whitebalance == 1) {
759 /* Enable automatic exposure: */
760 buf[0] |= MT9V111_IFP_OPMODE_AUTOWHITEBALANCE;
761 } else if (dev->vsettings.auto_whitebalance == 0) {
762 /* Disable automatic exposure: */
763 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOWHITEBALANCE;
764 } else
765 return -EINVAL;
766 /* Write new value to IFP-register 0x06:*/
767 ret = sn9c20x_write_i2c_data16(dev, 1,
768 MT9V111_IFPREG_OPMODECTL, buf);
769 if (ret < 0) {
770 UDIA_ERROR("Error: setting of auto whitebalance failed: "
771 "error while writing to IFP-register 0x06\n");
772 return ret;
774 return 0;
777 int mt9v111_set_autocorrections(struct usb_microdia *dev, int enable)
779 __u16 buf[1];
780 int ret = 0;
782 /* Switch to IFP-register address space: */
783 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
784 if (ret < 0)
785 return -11; /* -EAGAIN */
786 /* Read current value of IFP-register 0x06: */
787 ret = sn9c20x_read_i2c_data16(dev, 1,
788 MT9V111_IFPREG_OPMODECTL, buf);
789 if (ret < 0) {
790 UDIA_ERROR("Error: setting of sensor auto-correction functions "
791 "failed: error while reading from IFP-register 0x06\n");
792 return ret;
794 /* Set new value for register 0x06: */
795 if (enable == 1)
796 buf[0] |= MT9V111_IFP_OPMODE_APERTURECORR |
797 MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR;
798 else if (enable == 0)
799 buf[0] &= ~(MT9V111_IFP_OPMODE_APERTURECORR |
800 MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR);
801 else
802 return -EINVAL;
803 /* Write new value to IFP-register 0x06: */
804 ret = sn9c20x_write_i2c_data16(dev, 1,
805 MT9V111_IFPREG_OPMODECTL, buf);
806 if (ret < 0) {
807 UDIA_ERROR("Error: setting of sensor auto-correction functions "
808 "failed: error while writing to IFP-register 0x06\n");
809 return ret;
811 return 0;