Some minor fixes
[microdia.git] / micron.c
blobe029798e513457e89bf44f4c38672f6861a24130
1 /**
2 * @file micron.c
3 * @author Comer352l
4 * @date 2008-04-25
6 * @brief Common functions and data for Micron 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 "micron.h"
32 struct micron_init {
33 __u8 address;
34 __u16 value;
37 static struct micron_init mt9v111_init[] = {
38 /* 0x0d: Color Correction Register 8 */
39 {0x0d, 0x0001},
40 /* 0x0d: Color Correction Register 8 */
41 {0x0d, 0x0000}, /* ??? */
42 /* 0x02: Color Correction Register 1 */
43 {0x01, 0x0001}, /* select IFP address space */
44 {0x02, 0x0016}, /* ??? */
45 /* 0x03: Color Correction Register 3 */
46 /* 0x04: Color Correction Register 4 */
47 {0x03, 0x01e1}, /* ??? */
48 {0x04, 0x0281}, /* ??? */
49 /* 0x05: Aperture Correction (Sharpening) */
50 /* 0x06: Operating Mode Control */
51 {0x05, 0x0004}, /* 100% sharpening,
52 no automatic sharpness reduction at low light: no sharpening */
53 {0x06, 0x0000}, /* stop AWB at the current values,
54 no on-the-fly defect correction, no auto exposure */
55 /* 0x07: IFP Soft Reset */
56 /* 0x08: Output Format Control */
57 {0x07, 0x3002}, /* reset */
58 {0x08, 0x0480}, /* bypass entire image processing,
59 raw 8+2 Bayer data output directly */
60 {0x01, 0x0004}, /* select sensor address space */
61 {0x02, 0x0016}, /* start with column 22 */
62 /* 0x03: Window Height */
63 /* 0x04: Window Width */
64 {0x03, 0x01e6}, /* 486 */
65 {0x04, 0x0286}, /* 646 */
66 /* 0x05: Horizontal Blanking */
67 /* 0x06: Vertical Blanking */
68 {0x05, 0x0004}, /* 4 columns (pixel clocks) */
69 {0x06, 0x0000}, /* 0 rows */
70 /* 0x07: Output Control */
71 /* 0x08: Row Start */
72 {0x07, 0x3002}, /* normal operation + chip enable*/
73 {0x08, 0x0008}, /* row 8 */
74 /* 0x0c: Shutter Delay */
75 /* 0x0d: Reset (Soft) */
76 {0x0c, 0x0000}, /* 0 master clocks */
77 {0x0d, 0x0000}, /* return to normal operation */
78 {0x0e, 0x0000},
79 {0x0f, 0x0000},
80 {0x10, 0x0000},
81 {0x11, 0x0000},
82 /* 0x12: 2X Zoom Col Start
83 => 0x1e bit 0 must be set to activate zoom */
84 /* 0x13: 2X Zoom Row Start
85 => 0x1e bit 0 must be set to activate zoom */
86 {0x12, 0x00b0}, /* column 176 */
87 {0x13, 0x007c}, /* row 124 */
88 {0x14, 0x0000},
89 {0x15, 0x0000},
90 {0x16, 0x0000},
91 {0x17, 0x0000},
92 {0x18, 0x0000},
93 {0x19, 0x0000},
94 {0x1a, 0x0000},
95 {0x1b, 0x0000},
96 {0x1c, 0x0000},
97 {0x1d, 0x0000},
98 {0x30, 0x0000},
99 {0x30, 0x0005},
100 {0x31, 0x0000},
101 /* 0x02: Column Start */
102 /* 0x03: Window Height */
103 {0x02, 0x0016}, /* coulmn 22 */
104 {0x03, 0x01e1}, /* 481 */
105 /* 0x04: Window Width */
106 /* 0x05: Horizontal Blanking */
107 {0x04, 0x0281}, /* 641 */
108 {0x05, 0x0004}, /* 4 columns (pixel clocks) */
109 /* 0x06: Vertical Blanking */
110 /* 0x07: Output Control */
111 {0x06, 0x0000}, /* 0 rows */
112 {0x07, 0x3002}, /* normal operation + chip enable */
113 /* 0x06: Vertical Blanking */
114 {0x06, 0x002d}, /* 45 rows */
115 /* 0x05: Horizontal Blanking */
116 {0x05, 0x0004}, /* 4 columns (pixel clocks) */
117 /* 0x09: Shutter Width */
118 {0x09, 0x0064}, /* integration of 100 rows */
119 /* 0x2b: Green 1 Gain */
120 /* 0x2c: Blue Gain */
121 {0x2b, 0x00a0}, /* 32*0.03125*2 = 2 */
122 {0x2c, 0x00a0}, /* 32*0.03125*2 = 2 */
123 /* 0x2d: Red Gain */
124 /* 0x2e: Green 2 Gain */
125 {0x2d, 0x00a0}, /* 32*0.03125*2 = 2 */
126 {0x2e, 0x00a0}, /* 32*0.03125*2 = 2 */
127 /* 0x02: Column Start */
128 /* 0x03: Window Hight */
129 {0x02, 0x0016}, /* coulmn 22 */
130 {0x03, 0x01e1}, /* 481 */
131 /* 0x04: Window Width */
132 /* 0x05: Horizontal Blanking */
133 {0x04, 0x0281}, /* 641 */
134 {0x05, 0x0004}, /* 4 columns (pixel clocks) */
135 /* 0x06: Vertical Blanking */
136 /* 0x07: Output Control */
137 {0x06, 0x002d}, /* 45 rows */
138 {0x07, 0x3002}, /* RESERVED options */
139 /* Writes to0x0e: UNDOCUMENTED */
140 {0x0e, 0x0008},
141 /* 0x06: Vertical Blanking */
142 {0x06, 0x002d}, /* 45 rows */
143 /* 0x05: Horizontal Blanking */
144 {0x05, 0x0004}, /* 4 columns (pixel clocks) */
147 static struct micron_init mt9v011_init[] = {
148 /* 0x07: Output Control */
149 {0x07, 0x0002}, /* chip enable, normal operation */
150 /* 0x0d: Soft Reset */
151 {0x0d, 0x0001}, /* reset */
152 /* 0x0d: Soft Reset */
153 {0x0d, 0x0000}, /* resume operation */
154 /* 0x01: Row start */
155 /* 0x02: Column Start */
156 {0x01, 0x0008}, /* start with row 8 */
157 {0x02, 0x0016}, /* start with column 22 */
158 /* 0x03: Window Height */
159 /* 0x04: Window Width */
160 {0x03, 0x01e1}, /* 481 */
161 {0x04, 0x0281}, /* 641 */
162 /* 0x05: Horizontal Blanking */
163 /* 0x06: Vertical Blanking */
164 {0x05, 0x0083}, /* 131 columns (pixel clocks) */
165 {0x06, 0x0006}, /* 6 rows */
166 /* 0x0d: Soft Reset */
167 {0x0d, 0x0002}, /* UNKNOWN */
168 /* 0x0a: Pixel Clock Speed */
169 /* 0x0b: Frame Restart */
170 {0x0a, 0x0000}, /* default */
171 {0x0b, 0x0000}, /* (has no effect/no restart) */
172 /* 0x0c: Shutter Delay */
173 /* 0x0d: Soft Reset */
174 {0x0c, 0x0000}, /* 0 master clocks */
175 {0x0d, 0x0000}, /* resume operation */
176 {0x0e, 0x0000},
177 {0x0f, 0x0000},
178 {0x10, 0x0000},
179 {0x11, 0x0000},
180 /* 0x12: 2X Zoom Column Start (from MT9V111 datasheet) */
181 /* 0x13: 2X Zoom Row Start (from MT9V111 datasheet) */
182 {0x12, 0x0000},
183 /* column 0 => bit0 of reg 0x1e must be set to activate zoom */
184 {0x13, 0x0000},
185 /* row 0 => bit0 of reg 0x1e must be set to activate zoom */
186 {0x14, 0x0000},
187 {0x15, 0x0000},
188 {0x16, 0x0000},
189 {0x17, 0x0000},
190 {0x18, 0x0000},
191 {0x19, 0x0000},
192 {0x1a, 0x0000},
193 {0x1b, 0x0000},
194 {0x1c, 0x0000},
195 {0x1d, 0x0000},
196 {0x32, 0x0000},
197 /* 0x20: Read Mode */
198 {0x20, 0x1101}, /* output all frames (including bad frames) */
199 {0x21, 0x0000},
200 {0x22, 0x0000},
201 {0x23, 0x0000},
202 {0x24, 0x0000},
203 {0x25, 0x0000},
204 {0x26, 0x0000},
205 {0x27, 0x0024},
206 {0x2f, 0xf7b0},
207 {0x30, 0x0005},
208 {0x31, 0x0000},
209 {0x32, 0x0000},
210 {0x33, 0x0000},
211 {0x34, 0x0100},
212 {0x3d, 0x068f},
213 {0x40, 0x01e0},
214 {0x41, 0x00d1},
215 {0x44, 0x0082},
216 {0x5a, 0x0000},
217 {0x5b, 0x0000},
218 {0x5c, 0x0000},
219 {0x5d, 0x0000},
220 {0x5e, 0x0000},
221 {0x5f, 0xa31d},
222 {0x62, 0x0611},
223 /* 0x0a: Pixel Clock Speed */
224 {0x0a, 0x0000}, /* default */
225 /* 0x06: Vertical Blanking */
226 {0x06, 0x0029}, /* 41 rows */
227 /* 0x05: Horizontal Blanking */
228 {0x05, 0x0009}, /* 9 columns (pixel clocks) */
229 /* 0x20: Read Mode */
230 {0x20, 0x1101}, /* output all frames (including bad ones) */
231 /* 0x20: Read Mode */
232 {0x20, 0x1101}, /* output all frames (including bad ones) */
233 /* 0x09: Shutter Width */
234 {0x09, 0x0064}, /* integration of 100 rows */
235 /* 0x07: Output Control */
236 {0x07, 0x0003},
237 /* dont update changes until bit0=0, chip enable, normal operation */
238 /* 0x2b: Green 1 Gain */
239 /* 0x2c: Blue Gain */
240 {0x2b, 0x0033}, /* 51*0.03125*1 = 1.59375 */
241 {0x2c, 0x00a0}, /* 32*0.03125*2 = 2 */
242 /* 0x2d: Red Gain */
243 /* 0x2e: Green 2 Gain */
244 {0x2d, 0x00a0}, /* 32*0.03125*2 = 2 */
245 {0x2e, 0x0033}, /* 51*0.03125*1 = 1.59375 */
246 /* 0x07: Output Control */
247 {0x07, 0x0002}, /* chip enable, normal operation */
248 /* 0x0a: Pixel Clock Speed */
249 {0x06, 0x0000}, /* default */
250 /* 0x06: Vertical Blanking */
251 {0x06, 0x0029}, /* 41 rows */
252 /* 0x05: Horizontal Blanking */
253 {0x05, 0x0009}, /* 9 columns (pixel clocks) */
256 static struct micron_init mt9m111_init[] = {
257 /* Reset sensor */
258 {0x0d, 0x0008},
259 {0x0d, 0x0009},
260 {0x0d, 0x0008},
261 /* Select Page map 0x01
262 * This means all new writes now have the address prefix 0x1.
263 * Example: 0x3a becomes 0x13a. */
264 {0xf0, 0x0001},
265 /** Select output format:
266 * - output raw bayer (8+2 bit)
267 * FIXME: There are nearly all YUV and RGB variants possible.
268 * Maybe we should use them.
270 {0x3a, 0x7300},
271 /* measure atoexposure through a mix of both possible windows */
272 {0x06, 0x308c},
273 /* Switch back to Page map 0x00 */
274 {0xf0, 0x0000},
275 /* The following set the resoutiona and window size.
276 * It's a bit more than SXGA.
277 * VSTART */
278 {0x01, 0x000e},
279 /* HSTART */
280 {0x02, 0x0014},
281 /* VSIZE */
282 {0x03, 0x03c4},
283 /* HSIZE */
284 {0x04, 0x0514},
285 /* Blanking */
286 {0xc8, 0x0003},
287 {0x0a, 0x0001},
288 {0x06, 0x0029},
289 {0x05, 0x0072},
290 {0x20, 0x0000},
291 {0x20, 0x0000},
292 /* Shutter width */
293 {0x09, 0x0190},
294 /* Protect settings */
295 {0x0d, 0x8008},
296 /* Green 1 gain */
297 {0x2b, 0x0188},
298 /* Blue gain */
299 {0x2c, 0x0188},
300 /* Red gain */
301 {0x2d, 0x0188},
302 /* Green 2 gain */
303 {0x2e, 0x0188},
304 /* Blanking (again?) */
305 {0x0a, 0x0001},
306 {0x06, 0x0029},
307 {0x05, 0x0072},
310 static struct micron_init mt9m001_init[] = {
311 {0x07, 0x0000}, {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000},
312 {0x01, 0x000e}, {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501},
313 {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002}, {0x09, 0x0000},
314 {0x0a, 0x0000}, {0x0b, 0x0000}, {0x0c, 0x0000}, {0x11, 0x0000},
315 {0x1e, 0x8000}, {0x20, 0x1105}, {0x2b, 0x0008}, {0x2c, 0x0010},
316 {0x2d, 0x0014}, {0x2e, 0x0008}, {0x5f, 0x8904}, {0x60, 0x0000},
317 {0x61, 0x0000}, {0x62, 0x0498}, {0x63, 0x0000}, {0x68, 0x0000},
318 {0x20, 0x111d}, {0x06, 0x00f2}, {0x05, 0x0013}, {0x20, 0x111d},
319 {0x20, 0x111d}, {0x07, 0x0003}, {0x2b, 0x0010}, {0x2c, 0x0010},
320 {0x2d, 0x0010}, {0x2e, 0x0010}, {0x07, 0x0002}, {0x07, 0x0003},
321 {0x2c, 0x001d}, {0x2d, 0x001d}, {0x07, 0x0002}, {0x06, 0x00f2},
322 {0x05, 0x0013}, {0x09, 0x0387}, {0x07, 0x0003}, {0x2b, 0x0028},
323 {0x2c, 0x003f}, {0x2d, 0x003f}, {0x2e, 0x0028}, {0x07, 0x0002},
324 {0x09, 0x04f1}, {0x07, 0x0003}, {0x2b, 0x0024}, {0x2c, 0x0039},
325 {0x2d, 0x0039}, {0x2e, 0x0024}, {0x07, 0x0002},
329 * @brief Initialize micron sensors
331 * @param dev Pointer to device structure
333 * @return 0 or negative error code
336 int micron_initialize(struct usb_microdia *dev)
338 int i;
339 int ret = 0;
340 __u16 value;
342 switch (dev->camera.sensor->id) {
343 case MT9V111_SENSOR:
344 mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
345 for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
346 ret = sn9c20x_write_i2c_data16(dev, 1,
347 mt9v111_init[i].address,
348 &(mt9v111_init[i].value));
349 if (ret < 0)
350 goto err;
352 mt9v111_setup_autoexposure(dev);
353 mt9v111_setup_autowhitebalance(dev);
354 mt9v111_set_autocorrections(dev, 1);
355 break;
356 case MT9M111_SENSOR:
357 value = 0x0000;
358 sn9c20x_write_i2c_data16(dev, 2, 0xf0, &value);
359 for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
360 ret = sn9c20x_write_i2c_data16(dev, 1,
361 mt9m111_init[i].address,
362 &(mt9m111_init[i].value));
363 if (ret < 0)
364 goto err;
366 break;
367 case MT9V011_SENSOR:
368 for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
369 ret = sn9c20x_write_i2c_data16(dev, 1,
370 mt9v011_init[i].address,
371 &(mt9v011_init[i].value));
372 if (ret < 0)
373 goto err;
375 break;
376 case MT9M001_SENSOR:
377 for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
378 ret = sn9c20x_write_i2c_data16(dev, 1,
379 mt9m001_init[i].address,
380 &(mt9m001_init[i].value));
381 if (ret < 0)
382 goto err;
384 break;
385 default:
386 return -EINVAL;
389 return ret;
391 err:
392 UDIA_ERROR("Sensor Init failed (%d)! - line %d\n", ret, i);
393 return ret;
396 int mt9v111_select_address_space(struct usb_microdia *dev, __u8 address_space)
398 __u8 buf[2];
399 int retI2C;
400 int k;
402 /* check if selection is valid: */
403 if ((address_space != MT9V111_ADDRESSSPACE_IFP) &&
404 (address_space != MT9V111_ADDRESSSPACE_SENSOR)) {
405 UDIA_WARNING("invalid register address space "
406 "selection for sensor MT9V111/MI0360SOC !\n");
407 return -1;
409 /* read address space slection register: */
410 k = 0;
411 retI2C = -1;
412 while ((k < 3) && (retI2C != 0)) {
413 retI2C = sn9c20x_read_i2c_data(dev, 2, 0x01, buf);
414 if (retI2C != 0 && k < 2)
415 udelay(1000);
416 k++;
418 if (retI2C != 0) {
419 UDIA_ERROR("MT9V111/MI0360SOC (I2C-slave 0x5c): "
420 "read of reg0x01 (address space selection) failed !\n");
421 return -1;
423 /* check current address space: */
424 if ((buf[0] != 0x00) || (buf[1] != address_space)) {
425 k = 0;
426 retI2C = -1;
427 while ((k < 3) && (retI2C != 0)) {
428 /* switch address space: */
429 buf[0] = 0x00; buf[1] = address_space;
430 retI2C = sn9c20x_write_i2c_data(dev, 2, 0x01, buf);
431 if (retI2C != 0 && k < 2)
432 udelay(1000);
433 k++;
435 if (retI2C != 0) {
436 if (address_space == MT9V111_ADDRESSSPACE_IFP)
437 UDIA_ERROR("MT9V111/MI0360SOC "
438 "(I2C-slave 0x5c): switch to IFP "
439 "address space failed !\n");
440 else
441 UDIA_ERROR("MT9V111/MI0360SOC "
442 "(I2C-slave 0x5c): switch to sensor "
443 "core address space failed !\n");
444 return -1;
447 return 0;
450 int micron_probe(struct usb_microdia *dev)
452 __u16 buf, old;
453 int ret;
454 static struct sensor_info dummy;
456 /* This is necessary to allow I2C/SCCB operation */
457 dev->camera.sensor = &dummy;
458 dev->camera.sensor->address = 0x5d;
459 sn9c20x_i2c_initialize(dev);
461 /* MT9V011 goes first */
462 ret = sn9c20x_read_i2c_data16(dev, 2, 0xff, &buf);
463 if ((ret == 0) || (buf == 0x8243))
464 return MT9V011_SENSOR;
466 /* MT9V111 has a different address */
467 dev->camera.sensor->address = 0x5c;
469 /* Store old address-space */
470 ret = sn9c20x_read_i2c_data16(dev, 2, 0x01, &old);
471 if (ret != 0)
472 return ret;
474 /* Select address-space: sensor */
475 buf = MT9V111_ADDRESSSPACE_SENSOR & 0x00ff;
476 ret = sn9c20x_write_i2c_data16(dev, 2, 0x01, &buf);
477 if (ret != 0)
478 return ret;
480 ret = sn9c20x_read_i2c_data16(dev, 2, 0xff, &buf);
481 if ((ret == 0) || (buf == 0x823a)) {
482 sn9c20x_write_i2c_data16(dev, 2, 0x01, &old);
483 return MT9V111_SENSOR;
486 return -EINVAL;
489 int mt9m111_set_raw(struct usb_microdia *dev)
491 __u16 buf;
493 /* select address-space: IFP */
494 buf = 0x0001;
495 sn9c20x_write_i2c_data16(dev, 1, 0xf0, &buf);
497 sn9c20x_read_i2c_data16(dev, 1, 0x3a, &buf);
498 buf = (buf & 0xfaff) + 0x0500;
499 sn9c20x_write_i2c_data16(dev, 1, 0x3a, &buf);
501 sn9c20x_read_i2c_data16(dev, 1, 0x9b, &buf);
502 buf = (buf & 0xfaff) + 0x0500;
503 sn9c20x_write_i2c_data16(dev, 1, 0x9b, &buf);
505 sn9c20x_set_raw(dev);
507 return 0;
510 int mt9m111_set_yuv422(struct usb_microdia *dev)
512 __u16 buf;
514 /* select address-space: IFP */
515 buf = 0x0001;
516 sn9c20x_write_i2c_data16(dev, 1, 0xf0, &buf);
518 sn9c20x_read_i2c_data16(dev, 1, 0x3a, &buf);
519 buf &= 0xfaff;
520 sn9c20x_write_i2c_data16(dev, 1, 0x3a, &buf);
522 sn9c20x_read_i2c_data16(dev, 1, 0x9b, &buf);
523 buf &= 0xfaff;
524 sn9c20x_write_i2c_data16(dev, 1, 0x9b, &buf);
526 sn9c20x_set_raw(dev);
528 return 0;
531 int mt9v111_setup_autoexposure(struct usb_microdia *dev)
533 __u16 buf[1];
534 int ret = 0;
535 int ret2 = 0;
537 /* Switch to IFP-register address space: */
538 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
539 if (ret < 0)
540 return -11; /* -EAGAIN */
541 /* Set target luminance and tracking accuracy: */
542 buf[0] = MT9V111_IFP_AE_TRACKINGACCURACY(12)
543 /* 0-255 (default: 16) */
544 | MT9V111_IFP_AE_TARGETLUMINANCE(100);
545 /* 0-255 (default: 100) */
546 ret = sn9c20x_write_i2c_data16(dev, 1,
547 MT9V111_IFPREG_AE_TARGETLUMCTL, buf);
548 /* Set speed and sensitivity: */
549 buf[0] = MT9V111_IFP_AE_DELAY(4)
550 /* 0-7 (fastest-slowest) (default: 4) */
551 | MT9V111_IFP_AE_SPEED(4)
552 /* 0-7 (fastest-slowest) (default: 4) */
553 | MT9V111_IFP_AE_STEPSIZE_MEDIUM;
554 ret += sn9c20x_write_i2c_data16(dev, 1,
555 MT9V111_IFPREG_AE_SPEEDSENSCTL, buf);
556 /* Horizontal boundaries of AE measurement window: */
557 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT(1020)
558 /* 0-1020 (pixel) (default: 1020) */
559 | MT9V111_IFP_AE_WINBOUNDARY_LEFT(12);
560 /* 0-1020 (pixel) (default: 12) */
561 ret += sn9c20x_write_i2c_data16(dev, 1,
562 MT9V111_IFPREG_AE_HWINBOUNDARY, buf);
563 /* Vertical boundaries of AE measurement window: */
564 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM(510)
565 /* 0-510 (pixel) (default: 510) */
566 | MT9V111_IFP_AE_WINBOUNDARY_TOP(32);
567 /* 0-510 (pixel) (default: 32) */
568 ret += sn9c20x_write_i2c_data16(dev, 1,
569 MT9V111_IFPREG_AE_VWINBOUNDARY, buf);
570 /* Horizontal boundaries of AE measurement window
571 * for back light compensation: */
572 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT_BLC(480)
573 /* 0-1020 (pixel) (default: 480) */
574 | MT9V111_IFP_AE_WINBOUNDARY_LEFT_BLC(160);
575 /* 0-1020 (pixel) (default: 160) */
576 ret += sn9c20x_write_i2c_data16(dev, 1,
577 MT9V111_IFPREG_AE_HWINBOUNDARY_BLC, buf);
578 /* Vertical boundaries of AE measurement window
579 * for back light compensation: */
580 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM_BLC(360)
581 /* 0-510 (pixel) (default: 360) */
582 | MT9V111_IFP_AE_WINBOUNDARY_TOP_BLC(120);
583 /* 0-510 (pixel) (default: 120) */
584 ret += sn9c20x_write_i2c_data16(dev, 1,
585 MT9V111_IFPREG_AE_VWINBOUNDARY_BLC, buf);
586 /* Set digital gains limit: */
587 buf[0] = MT9V111_IFP_AE_MAXGAIN_POSTLS(64)
588 /* 0-255 (default: 64) */
589 | MT9V111_IFP_AE_MAXGAIN_PRELS(16);
590 /* 0-255 (default: 16) */
591 ret += sn9c20x_write_i2c_data16(dev, 1,
592 MT9V111_IFPREG_AE_DGAINSLIMITS, buf);
593 /* Read current value of IFP-register 0x06: */
594 ret2 = sn9c20x_read_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
595 if (ret2 == 0) {
596 /* Set new value for register 0x06: */
597 buf[0] = (buf[0] & 0xffe3) | MT9V111_IFP_AE_WIN_COMBINED |
598 MT9V111_IFP_OPMODE_BYPASSCOLORCORR;
599 /* NOTE: BYPASS COLOR CORRECTION HAS
600 * TO BE SET FOR PERMANENT AE ! */
601 /* Write new value to IFP-register 0x06:*/
602 ret += sn9c20x_write_i2c_data16(dev, 1,
603 MT9V111_IFPREG_OPMODECTL, buf);
605 if (ret < 0 || ret2 < 0) {
606 UDIA_WARNING("One or more errors occured during "
607 "setup of AE registers.\n");
608 return -1;
610 return 0;
613 int mt9v111_setup_autowhitebalance(struct usb_microdia *dev)
615 __u16 buf[1];
616 int ret = 0;
618 /* Switch to IFP-register address space: */
619 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
620 if (ret < 0)
621 return -11; /* -EAGAIN */
622 /* Set AWB tint: */
623 buf[0] = MT9V111_IFP_AWB_ADDON_RED(0) /* 0-255 (default: 0) */
624 | MT9V111_IFP_AWB_ADDON_BLUE(0); /* 0-255 (default: 0) */
625 ret = sn9c20x_write_i2c_data16(dev, 1, MT9V111_IFPREG_AWB_TINT, buf);
626 /* Set AWB speed and color saturation: */
627 buf[0] = MT9V111_IFP_AWB_SATURATION_FULL
628 | MT9V111_IFP_AWB_AUTOSATLOWLIGHT_ENABLE
629 | MT9V111_IFP_AWB_DELAY(4)
630 /* 0-7 (fastest-slowest) (default: 4) */
631 | MT9V111_IFP_AWB_SPEED(4);
632 /* 0-7 (fastest-slowest) (default: 4) */
633 ret += sn9c20x_write_i2c_data16(dev, 1,
634 MT9V111_IFPREG_AWB_SPEEDCOLORSATCTL, buf);
635 /* Boundaries of AWB measurement window: */
636 buf[0] = MT9V111_IFP_AWB_WINBOUNDARY_TOP(0)
637 /* 0-480 (pixel) (default: 0) */
638 | MT9V111_IFP_AWB_WINBOUNDARY_BOTTOM(480)
639 /* 0-480 (pixel) (default: 448) */
640 | MT9V111_IFP_AWB_WINBOUNDARY_LEFT(0)
641 /* 0-960 (pixel) (default: 0) */
642 | MT9V111_IFP_AWB_WINBOUNDARY_RIGHT(640);
643 /* 0-960 (pixel) (default: 640) */
644 ret += sn9c20x_write_i2c_data16(dev, 1,
645 MT9V111_IFPREG_AWB_WINBOUNDARY, buf);
647 if (ret < 0) {
648 UDIA_WARNING("One or more errors occured during "
649 "setup of AWB registers.\n");
650 return -1;
652 return 0;
655 int mt9v011_set_exposure(struct usb_microdia *dev)
657 int ret = 0;
658 __u8 buf[2];
660 buf[0] = (dev->vsettings.exposure >> 12);
661 buf[1] = 0;
662 ret |= sn9c20x_write_i2c_data(dev, 2, 0x09, buf);
663 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not
664 * used) for MT9V111 sensor, because IFP controls this register if
665 * one of them is enabled. */
666 return ret;
669 int mt9v111_set_exposure(struct usb_microdia *dev)
671 int ret = 0;
672 __u8 buf[2];
675 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
677 if (ret < 0)
678 return -11; /* -EAGAIN */
680 buf[0] = (dev->vsettings.exposure >> 12);
681 buf[1] = 0;
682 ret |= sn9c20x_write_i2c_data(dev, 2, 0x09, buf);
683 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not
684 * used) for MT9V111 sensor, because IFP controls this register if
685 * one of them is enabled. */
686 return ret;
689 int mt9v011_set_hvflip(struct usb_microdia *dev)
691 int ret = 0;
692 __u8 buf[2];
694 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
695 return -EINVAL;
696 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
697 return -EINVAL;
699 ret = sn9c20x_read_i2c_data(dev, 2, 0x20, buf);
700 if (ret < 0)
701 return ret;
703 if (dev->vsettings.hflip) {
704 buf[0] |= 0x80;
705 /* (MSB) set bit 15: read out from
706 * bottom to top (upside down) */
707 buf[1] |= 0x80;
708 /* (LSB) set bit 7: readout starting 1 row later */
709 } else {
710 buf[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
711 buf[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
713 if (dev->vsettings.vflip) {
714 buf[0] |= 0x40;
715 /* (MSB) set bit 14: read out from right to left (mirrored) */
716 buf[1] |= 0x20;
717 /* (LSB) set bit 5: readout starting 1 column later */
718 } else {
719 buf[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
720 buf[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
723 ret = sn9c20x_write_i2c_data(dev, 2, 0x20, buf);
724 return ret;
727 int mt9v111_set_hvflip(struct usb_microdia *dev)
729 int ret = 0;
730 __u8 buf[2];
732 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
733 return -EINVAL;
734 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
735 return -EINVAL;
738 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
739 if (ret < 0)
740 return -11; /* -EAGAIN */
742 ret = sn9c20x_read_i2c_data(dev, 2, 0x20, buf);
743 if (ret < 0)
744 return ret;
746 if (dev->vsettings.hflip) {
747 buf[0] |= 0x80;
748 /* (MSB) set bit 15: read out from
749 * bottom to top (upside down) */
750 buf[1] |= 0x80;
751 /* (LSB) set bit 7: readout starting 1 row later */
752 } else {
753 buf[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
754 buf[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
756 if (dev->vsettings.vflip) {
757 buf[0] |= 0x40;
758 /* (MSB) set bit 14: read out from right to left (mirrored) */
759 buf[1] |= 0x20;
760 /* (LSB) set bit 5: readout starting 1 column later */
761 } else {
762 buf[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
763 buf[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
766 ret = sn9c20x_write_i2c_data(dev, 2, 0x20, buf);
767 return ret;
770 int mt9v111_set_autoexposure(struct usb_microdia *dev)
772 __u16 buf[1];
773 int ret = 0;
775 /* Switch to IFP-register address space: */
776 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
777 if (ret < 0)
778 return -11; /* -EAGAIN */
779 /* Read current value of IFP-register 0x06: */
780 ret = sn9c20x_read_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
781 if (ret < 0) {
782 UDIA_ERROR("Error: setting of auto exposure failed: "
783 "error while reading from IFP-register 0x06\n");
784 return ret;
787 /* Set new value for register 0x06: */
788 switch (dev->vsettings.auto_exposure) {
789 case V4L2_EXPOSURE_AUTO:
790 buf[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE;
791 break;
792 case V4L2_EXPOSURE_MANUAL:
793 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE;
794 break;
795 case V4L2_EXPOSURE_SHUTTER_PRIORITY:
796 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE;
797 break;
798 case V4L2_EXPOSURE_APERTURE_PRIORITY:
799 buf[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE;
800 break;
801 default:
802 return -EINVAL;
804 /* Write new value to IFP-register 0x06: */
805 ret = sn9c20x_write_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
806 if (ret < 0) {
807 UDIA_ERROR("Error: setting of auto exposure failed: "
808 "error while writing to IFP-register 0x06\n");
809 return ret;
811 return 0;
814 int mt9v111_set_autowhitebalance(struct usb_microdia *dev)
816 __u16 buf[1];
817 int ret = 0;
819 /* Switch to IFP-register address space: */
820 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
821 if (ret < 0)
822 return -11; /* -EAGAIN */
823 /* Read current value of IFP-register 0x06: */
824 ret = sn9c20x_read_i2c_data16(dev, 1,
825 MT9V111_IFPREG_OPMODECTL, buf);
826 if (ret < 0) {
827 UDIA_ERROR("Error: setting of auto whitebalance failed: "
828 "error while reading from IFP-register 0x06\n");
829 return ret;
831 /* Set new value for register 0x06: */
832 if (dev->vsettings.auto_whitebalance == 1) {
833 /* Enable automatic exposure: */
834 buf[0] |= MT9V111_IFP_OPMODE_AUTOWHITEBALANCE;
835 } else if (dev->vsettings.auto_whitebalance == 0) {
836 /* Disable automatic exposure: */
837 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOWHITEBALANCE;
838 } else
839 return -EINVAL;
840 /* Write new value to IFP-register 0x06:*/
841 ret = sn9c20x_write_i2c_data16(dev, 1,
842 MT9V111_IFPREG_OPMODECTL, buf);
843 if (ret < 0) {
844 UDIA_ERROR("Error: setting of auto whitebalance failed: "
845 "error while writing to IFP-register 0x06\n");
846 return ret;
848 return 0;
851 int mt9v111_set_autocorrections(struct usb_microdia *dev, int enable)
853 __u16 buf[1];
854 int ret = 0;
856 /* Switch to IFP-register address space: */
857 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
858 if (ret < 0)
859 return -11; /* -EAGAIN */
860 /* Read current value of IFP-register 0x06: */
861 ret = sn9c20x_read_i2c_data16(dev, 1,
862 MT9V111_IFPREG_OPMODECTL, buf);
863 if (ret < 0) {
864 UDIA_ERROR("Error: setting of sensor auto-correction functions "
865 "failed: error while reading from IFP-register 0x06\n");
866 return ret;
868 /* Set new value for register 0x06: */
869 if (enable == 1)
870 buf[0] |= MT9V111_IFP_OPMODE_APERTURECORR |
871 MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR;
872 else if (enable == 0)
873 buf[0] &= ~(MT9V111_IFP_OPMODE_APERTURECORR |
874 MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR);
875 else
876 return -EINVAL;
877 /* Write new value to IFP-register 0x06: */
878 ret = sn9c20x_write_i2c_data16(dev, 1,
879 MT9V111_IFPREG_OPMODECTL, buf);
880 if (ret < 0) {
881 UDIA_ERROR("Error: setting of sensor auto-correction functions "
882 "failed: error while writing to IFP-register 0x06\n");
883 return ret;
885 return 0;