Remove sn9c20x_set_exposure() and set the standard exposure back to 20%
[microdia.git] / micron.c
blobcc70fec7ce24e94e19307fdef9f7c22fd414d52e
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"
33 struct microdia_video_format mt9x0xx_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_format mt9m111_fmts[] = {
50 .pix_fmt = V4L2_PIX_FMT_SBGGR8,
51 .desc = "Bayer 8bit (BGGR)",
52 .depth = 8,
53 .set_format = mt9m111_set_raw
56 .pix_fmt = V4L2_PIX_FMT_YUYV,
57 .desc = "YUV 4:2:2",
58 .depth = 16,
59 .set_format = mt9m111_set_yuv422
62 .pix_fmt = V4L2_PIX_FMT_JPEG,
63 .desc = "JPEG (YUV 4:2:2)",
64 .depth = 16,
65 .set_format = sn9c20x_set_jpeg
69 struct microdia_video_format mt9v111_fmts[] = {
71 .pix_fmt = V4L2_PIX_FMT_SBGGR8,
72 .desc = "Bayer 8bit (BGGR)",
73 .depth = 8,
74 .set_format = sn9c20x_set_raw
77 .pix_fmt = V4L2_PIX_FMT_JPEG,
78 .desc = "JPEG (YUV 4:2:2)",
79 .depth = 16,
80 .set_format = sn9c20x_set_jpeg
84 struct microdia_video_resolution micron_resolutions[] = {
86 .width = 160,
87 .height = 120,
88 .scale = SN9C20X_1_4_SCALE,
89 .window = {2, 2, 640, 480}
92 .width = 320,
93 .height = 240,
94 .scale = SN9C20X_1_2_SCALE,
95 .window = {2, 2, 640, 480}
98 .width = 640,
99 .height = 480,
100 .scale = SN9C20X_NO_SCALE,
101 .window = {2, 2, 640, 480}
105 struct microdia_video_resolution mt9m111_resolutions[] = {
107 .width = 160,
108 .height = 120,
109 .scale = SN9C20X_1_4_SCALE,
110 .window = {6, 2, 640, 480}
113 .width = 320,
114 .height = 240,
115 .scale = SN9C20X_1_2_SCALE,
116 .window = {6, 2, 640, 480}
119 .width = 640,
120 .height = 480,
121 .scale = SN9C20X_NO_SCALE,
122 .window = {6, 2, 640, 480}
126 static __u8 mt9v111_init[][3] = {
127 /* 0x0d: Color Correction Register 8 */
128 {0x0d, 0x00, 0x01},
129 /* 0x0d: Color Correction Register 8 */
130 {0x0d, 0x00, 0x00}, /* ??? */
131 /* 0x02: Color Correction Register 1 */
132 {0x01, 0x00, 0x01}, /* select IFP address space */
133 {0x02, 0x00, 0x16}, /* ??? */
134 /* 0x03: Color Correction Register 3 */
135 /* 0x04: Color Correction Register 4 */
136 {0x03, 0x01, 0xe1}, /* ??? */
137 {0x04, 0x02, 0x81}, /* ??? */
138 /* 0x05: Aperture Correction (Sharpening) */
139 /* 0x06: Operating Mode Control */
140 {0x05, 0x00, 0x04}, /* 100% sharpening,
141 no automatic sharpness reduction at low light: no sharpening */
142 {0x06, 0x00, 0x00}, /* stop AWB at the current values,
143 no on-the-fly defect correction, no auto exposure */
144 /* 0x07: IFP Soft Reset */
145 /* 0x08: Output Format Control */
146 {0x07, 0x30, 0x02}, /* reset */
147 {0x08, 0x04, 0x80}, /* bypass entire image processing,
148 raw 8+2 Bayer data output directly */
149 {0x01, 0x00, 0x04}, /* select sensor address space */
150 {0x02, 0x00, 0x16}, /* start with column 22 */
151 /* 0x03: Window Height */
152 /* 0x04: Window Width */
153 {0x03, 0x01, 0xe6}, /* 486 */
154 {0x04, 0x02, 0x86}, /* 646 */
155 /* 0x05: Horizontal Blanking */
156 /* 0x06: Vertical Blanking */
157 {0x05, 0x00, 0x04}, /* 4 columns (pixel clocks) */
158 {0x06, 0x00, 0x00}, /* 0 rows */
159 /* 0x07: Output Control */
160 /* 0x08: Row Start */
161 {0x07, 0x30, 0x02}, /* normal operation + chip enable*/
162 {0x08, 0x00, 0x08}, /* row 8 */
163 /* 0x0c: Shutter Delay */
164 /* 0x0d: Reset (Soft) */
165 {0x0c, 0x00, 0x00}, /* 0 master clocks */
166 {0x0d, 0x00, 0x00}, /* return to normal operation */
167 {0x0e, 0x00, 0x00},
168 {0x0f, 0x00, 0x00},
169 {0x10, 0x00, 0x00},
170 {0x11, 0x00, 0x00},
171 /* 0x12: 2X Zoom Col Start
172 => 0x1e bit 0 must be set to activate zoom */
173 /* 0x13: 2X Zoom Row Start
174 => 0x1e bit 0 must be set to activate zoom */
175 {0x12, 0x00, 0xb0}, /* column 176 */
176 {0x13, 0x00, 0x7c}, /* row 124 */
177 {0x14, 0x00, 0x00},
178 {0x15, 0x00, 0x00},
179 {0x16, 0x00, 0x00},
180 {0x17, 0x00, 0x00},
181 {0x18, 0x00, 0x00},
182 {0x19, 0x00, 0x00},
183 {0x1a, 0x00, 0x00},
184 {0x1b, 0x00, 0x00},
185 {0x1c, 0x00, 0x00},
186 {0x1d, 0x00, 0x00},
187 {0x30, 0x00, 0x00},
188 {0x30, 0x00, 0x05},
189 {0x31, 0x00, 0x00},
190 /* 0x02: Column Start */
191 /* 0x03: Window Height */
192 {0x02, 0x00, 0x16}, /* coulmn 22 */
193 {0x03, 0x01, 0xe1}, /* 481 */
194 /* 0x04: Window Width */
195 /* 0x05: Horizontal Blanking */
196 {0x04, 0x02, 0x81}, /* 641 */
197 {0x05, 0x00, 0x04}, /* 4 columns (pixel clocks) */
198 /* 0x06: Vertical Blanking */
199 /* 0x07: Output Control */
200 {0x06, 0x00, 0x00}, /* 0 rows */
201 {0x07, 0x30, 0x02}, /* normal operation + chip enable */
202 /* 0x06: Vertical Blanking */
203 {0x06, 0x00, 0x2d}, /* 45 rows */
204 /* 0x05: Horizontal Blanking */
205 {0x05, 0x00, 0x04}, /* 4 columns (pixel clocks) */
206 /* 0x09: Shutter Width */
207 {0x09, 0x00, 0x64}, /* integration of 100 rows */
208 /* 0x2b: Green 1 Gain */
209 /* 0x2c: Blue Gain */
210 {0x2b, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
211 {0x2c, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
212 /* 0x2d: Red Gain */
213 /* 0x2e: Green 2 Gain */
214 {0x2d, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
215 {0x2e, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
216 /* 0x02: Column Start */
217 /* 0x03: Window Hight */
218 {0x02, 0x00, 0x16}, /* coulmn 22 */
219 {0x03, 0x01, 0xe1}, /* 481 */
220 /* 0x04: Window Width */
221 /* 0x05: Horizontal Blanking */
222 {0x04, 0x02, 0x81}, /* 641 */
223 {0x05, 0x00, 0x04}, /* 4 columns (pixel clocks) */
224 /* 0x06: Vertical Blanking */
225 /* 0x07: Output Control */
226 {0x06, 0x00, 0x2d}, /* 45 rows */
227 {0x07, 0x30, 0x02}, /* RESERVED options */
228 /* Writes to0x0e: UNDOCUMENTED */
229 {0x0e, 0x00, 0x08},
230 /* 0x06: Vertical Blanking */
231 {0x06, 0x00, 0x2d}, /* 45 rows */
232 /* 0x05: Horizontal Blanking */
233 {0x05, 0x00, 0x04}, /* 4 columns (pixel clocks) */
236 static __u8 mt9v011_init[][3] = {
237 /* 0x07: Output Control */
238 {0x07, 0x00, 0x02}, /* chip enable, normal operation */
239 /* 0x0d: Soft Reset */
240 {0x0d, 0x00, 0x01}, /* reset */
241 /* 0x0d: Soft Reset */
242 {0x0d, 0x00, 0x00}, /* resume operation */
243 /* 0x01: Row start */
244 /* 0x02: Column Start */
245 {0x01, 0x00, 0x08}, /* start with row 8 */
246 {0x02, 0x00, 0x16}, /* start with column 22 */
247 /* 0x03: Window Height */
248 /* 0x04: Window Width */
249 {0x03, 0x01, 0xe1}, /* 481 */
250 {0x04, 0x02, 0x81}, /* 641 */
251 /* 0x05: Horizontal Blanking */
252 /* 0x06: Vertical Blanking */
253 {0x05, 0x00, 0x83}, /* 131 columns (pixel clocks) */
254 {0x06, 0x00, 0x06}, /* 6 rows */
255 /* 0x0d: Soft Reset */
256 {0x0d, 0x00, 0x02}, /* UNKNOWN */
257 /* 0x0a: Pixel Clock Speed */
258 /* 0x0b: Frame Restart */
259 {0x0a, 0x00, 0x00}, /* default */
260 {0x0b, 0x00, 0x00}, /* (has no effect/no restart) */
261 /* 0x0c: Shutter Delay */
262 /* 0x0d: Soft Reset */
263 {0x0c, 0x00, 0x00}, /* 0 master clocks */
264 {0x0d, 0x00, 0x00}, /* resume operation */
265 {0x0e, 0x00, 0x00},
266 {0x0f, 0x00, 0x00},
267 {0x10, 0x00, 0x00},
268 {0x11, 0x00, 0x00},
269 /* 0x12: 2X Zoom Column Start (from MT9V111 datasheet) */
270 /* 0x13: 2X Zoom Row Start (from MT9V111 datasheet) */
271 {0x12, 0x00, 0x00},
272 /* column 0 => bit0 of reg 0x1e must be set to activate zoom */
273 {0x13, 0x00, 0x00},
274 /* row 0 => bit0 of reg 0x1e must be set to activate zoom */
275 {0x14, 0x00, 0x00},
276 {0x15, 0x00, 0x00},
277 {0x16, 0x00, 0x00},
278 {0x17, 0x00, 0x00},
279 {0x18, 0x00, 0x00},
280 {0x19, 0x00, 0x00},
281 {0x1a, 0x00, 0x00},
282 {0x1b, 0x00, 0x00},
283 {0x1c, 0x00, 0x00},
284 {0x1d, 0x00, 0x00},
285 {0x32, 0x00, 0x00},
286 /* 0x20: Read Mode */
287 {0x20, 0x11, 0x01}, /* output all frames (including bad frames) */
288 {0x21, 0x00, 0x00},
289 {0x22, 0x00, 0x00},
290 {0x23, 0x00, 0x00},
291 {0x24, 0x00, 0x00},
292 {0x25, 0x00, 0x00},
293 {0x26, 0x00, 0x00},
294 {0x27, 0x00, 0x24},
295 {0x2f, 0xf7, 0xb0},
296 {0x30, 0x00, 0x05},
297 {0x31, 0x00, 0x00},
298 {0x32, 0x00, 0x00},
299 {0x33, 0x00, 0x00},
300 {0x34, 0x01, 0x00},
301 {0x3d, 0x06, 0x8f},
302 {0x40, 0x01, 0xe0},
303 {0x41, 0x00, 0xd1},
304 {0x44, 0x00, 0x82},
305 {0x5a, 0x00, 0x00},
306 {0x5b, 0x00, 0x00},
307 {0x5c, 0x00, 0x00},
308 {0x5d, 0x00, 0x00},
309 {0x5e, 0x00, 0x00},
310 {0x5f, 0xa3, 0x1d},
311 {0x62, 0x06, 0x11},
312 /* 0x0a: Pixel Clock Speed */
313 {0x0a, 0x00, 0x00}, /* default */
314 /* 0x06: Vertical Blanking */
315 {0x06, 0x00, 0x29}, /* 41 rows */
316 /* 0x05: Horizontal Blanking */
317 {0x05, 0x00, 0x09}, /* 9 columns (pixel clocks) */
318 /* 0x20: Read Mode */
319 {0x20, 0x11, 0x01}, /* output all frames (including bad ones) */
320 /* 0x20: Read Mode */
321 {0x20, 0x11, 0x01}, /* output all frames (including bad ones) */
322 /* 0x09: Shutter Width */
323 {0x09, 0x00, 0x64}, /* integration of 100 rows */
324 /* 0x07: Output Control */
325 {0x07, 0x00, 0x03},
326 /* dont update changes until bit0=0, chip enable, normal operation */
327 /* 0x2b: Green 1 Gain */
328 /* 0x2c: Blue Gain */
329 {0x2b, 0x00, 0x33}, /* 51*0.03125*1 = 1.59375 */
330 {0x2c, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
331 /* 0x2d: Red Gain */
332 /* 0x2e: Green 2 Gain */
333 {0x2d, 0x00, 0xa0}, /* 32*0.03125*2 = 2 */
334 {0x2e, 0x00, 0x33}, /* 51*0.03125*1 = 1.59375 */
335 /* 0x07: Output Control */
336 {0x07, 0x00, 0x02}, /* chip enable, normal operation */
337 /* 0x0a: Pixel Clock Speed */
338 {0x06, 0x00, 0x00}, /* default */
339 /* 0x06: Vertical Blanking */
340 {0x06, 0x00, 0x29}, /* 41 rows */
341 /* 0x05: Horizontal Blanking */
342 {0x05, 0x00, 0x09}, /* 9 columns (pixel clocks) */
345 static __u8 mt9m111_init[][3] = {
346 /* Reset sensor */
347 {0x0d, 0x00, 0x08},
348 {0x0d, 0x00, 0x09},
349 {0x0d, 0x00, 0x08},
350 /* Select Page map 0x01
351 * This means all new writes now have the address prefix 0x1.
352 * Example: 0x3a becomes 0x13a. */
353 {0xf0, 0x00, 0x01},
354 /** Select output format:
355 * - output raw bayer (8+2 bit)
356 * FIXME: There are nearly all YUV and RGB variants possible.
357 * Maybe we should use them.
359 {0x3a, 0x73, 0x00},
360 /* measure atoexposure through a mix of both possible windows */
361 {0x06, 0x30, 0x8c},
362 /* Switch back to Page map 0x00 */
363 {0xf0, 0x00, 0x00},
364 /* The following set the resoutiona and window size.
365 * It's a bit more than SXGA.
366 * VSTART */
367 {0x01, 0x00, 0x0e},
368 /* HSTART */
369 {0x02, 0x00, 0x14},
370 /* VSIZE */
371 {0x03, 0x03, 0xc4},
372 /* HSIZE */
373 {0x04, 0x05, 0x14},
374 /* Blanking */
375 {0xc8, 0x00, 0x03},
376 {0x0a, 0x00, 0x01},
377 {0x06, 0x00, 0x29},
378 {0x05, 0x00, 0x72},
379 {0x20, 0x00, 0x00},
380 {0x20, 0x00, 0x00},
381 /* Shutter width */
382 {0x09, 0x01, 0x90},
383 /* Protect settings */
384 {0x0d, 0x80, 0x08},
385 /* Green 1 gain */
386 {0x2b, 0x01, 0x88},
387 /* Blue gain */
388 {0x2c, 0x01, 0x88},
389 /* Red gain */
390 {0x2d, 0x01, 0x88},
391 /* Green 2 gain */
392 {0x2e, 0x01, 0x88},
393 /* Blanking (again?) */
394 {0x0a, 0x00, 0x01},
395 {0x06, 0x00, 0x29},
396 {0x05, 0x00, 0x72},
399 static __u8 mt9m001_init[][3] = {
400 {0x07, 0x00, 0x00}, {0x07, 0x00, 0x02}, {0x0d, 0x00, 0x01},
401 {0x0d, 0x00, 0x00}, {0x01, 0x00, 0x0e}, {0x02, 0x00, 0x14},
402 {0x03, 0x03, 0xc1}, {0x04, 0x05, 0x01}, {0x05, 0x00, 0x83},
403 {0x06, 0x00, 0x06}, {0x0d, 0x00, 0x02}, {0x09, 0x00, 0x00},
404 {0x0a, 0x00, 0x00}, {0x0b, 0x00, 0x00}, {0x0c, 0x00, 0x00},
405 {0x11, 0x00, 0x00}, {0x1e, 0x80, 0x00}, {0x20, 0x11, 0x05},
406 {0x2b, 0x00, 0x08}, {0x2c, 0x00, 0x10}, {0x2d, 0x00, 0x14},
407 {0x2e, 0x00, 0x08}, {0x5f, 0x89, 0x04}, {0x60, 0x00, 0x00},
408 {0x61, 0x00, 0x00}, {0x62, 0x04, 0x98}, {0x63, 0x00, 0x00},
409 {0x68, 0x00, 0x00}, {0x20, 0x11, 0x1d}, {0x06, 0x00, 0xf2},
410 {0x05, 0x00, 0x13}, {0x20, 0x11, 0x1d}, {0x20, 0x11, 0x1d},
411 {0x07, 0x00, 0x03}, {0x2b, 0x00, 0x10}, {0x2c, 0x00, 0x10},
412 {0x2d, 0x00, 0x10}, {0x2e, 0x00, 0x10}, {0x07, 0x00, 0x02},
413 {0x07, 0x00, 0x03}, {0x2c, 0x00, 0x1d}, {0x2d, 0x00, 0x1d},
414 {0x07, 0x00, 0x02}, {0x06, 0x00, 0xf2}, {0x05, 0x00, 0x13},
415 {0x09, 0x03, 0x87}, {0x07, 0x00, 0x03}, {0x2b, 0x00, 0x28},
416 {0x2c, 0x00, 0x3f}, {0x2d, 0x00, 0x3f}, {0x2e, 0x00, 0x28},
417 {0x07, 0x00, 0x02}, {0x09, 0x04, 0xf1}, {0x07, 0x00, 0x03},
418 {0x2b, 0x00, 0x24}, {0x2c, 0x00, 0x39}, {0x2d, 0x00, 0x39},
419 {0x2e, 0x00, 0x24}, {0x07, 0x00, 0x02},
423 * @brief Initialize mt9v011 sensors
425 * @param dev Pointer to device structure
427 * @return 0 or negative error code
430 int mt9v011_initialize(struct usb_microdia *dev)
432 int i;
433 int ret = 0;
434 __u8 value[2], reg;
435 for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
436 reg = mt9v011_init[i][0];
437 value[0] = mt9v011_init[i][1];
438 value[1] = mt9v011_init[i][2];
439 ret = sn9c20x_write_i2c_data(dev, 2, reg, value);
440 if (ret < 0) {
441 UDIA_WARNING("Sensor Init Error (%d). "
442 "line %d\n", ret, i);
443 break;
447 return ret;
451 * @brief Initialize mt9v111 sensors
453 * @param dev Pointer to device structure
455 * @return 0 or negative error code
458 int mt9v111_initialize(struct usb_microdia *dev)
460 int i;
461 int ret = 0;
462 __u8 value[2], reg;
463 mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
465 for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
466 reg = mt9v111_init[i][0];
467 value[0] = mt9v111_init[i][1];
468 value[1] = mt9v111_init[i][2];
469 ret = sn9c20x_write_i2c_data(dev, 2, reg, value);
470 if (ret < 0) {
471 UDIA_WARNING("Sensor Init Error (%d). "
472 "line %d\n", ret, i);
473 break;
477 mt9v111_setup_autoexposure(dev);
478 mt9v111_setup_autowhitebalance(dev);
479 mt9v111_set_autocorrections(dev, 1);
481 return ret;
484 int mt9v011_probe(struct usb_microdia *dev)
486 __u8 buf[2];
487 int ret;
489 ret = sn9c20x_read_i2c_data(dev, 2, 0xff, buf);
490 if (ret == 0) {
491 if (buf[0] != 0x82)
492 return -EINVAL;
493 if (buf[1] == 0x43) {
494 mt9v011_initialize(dev);
495 dev->camera.set_hvflip = mt9v011_set_hvflip;
496 dev->camera.set_exposure = mt9v011_set_exposure;
497 dev->camera.modes = micron_resolutions;
498 dev->camera.nmodes = ARRAY_SIZE(micron_resolutions);
499 dev->camera.fmts = mt9x0xx_fmts;
500 dev->camera.nfmts = ARRAY_SIZE(mt9x0xx_fmts);
501 return MT9V011_SENSOR;
505 /** NOTE: DNT DigiMicro 1.3 (microscope camera):
506 * This device uses sensor MT9V111, but slave 0x5d is also successfully
507 * read. Registers 0x00, 0x36 and 0xff of slave 0x5d return chip version
508 * 0x0000. */
510 return -EINVAL;
513 int mt9v111_select_address_space(struct usb_microdia *dev, __u8 address_space)
515 __u8 buf[2];
516 int retI2C;
517 int k;
519 /* check if selection is valid: */
520 if ((address_space != MT9V111_ADDRESSSPACE_IFP) &&
521 (address_space != MT9V111_ADDRESSSPACE_SENSOR)) {
522 UDIA_WARNING("invalid register address space "
523 "selection for sensor MT9V111/MI0360SOC !\n");
524 return -1;
526 /* read address space slection register: */
527 k = 0;
528 retI2C = -1;
529 while ((k < 3) && (retI2C != 0)) {
530 retI2C = sn9c20x_read_i2c_data(dev, 2, 0x01, buf);
531 if (retI2C != 0 && k < 2)
532 udelay(1000);
533 k++;
535 if (retI2C != 0) {
536 UDIA_ERROR("MT9V111/MI0360SOC (I2C-slave 0x5c): "
537 "read of reg0x01 (address space selection) failed !\n");
538 return -1;
540 /* check current address space: */
541 if ((buf[0] != 0x00) || (buf[1] != address_space)) {
542 k = 0;
543 retI2C = -1;
544 while ((k < 3) && (retI2C != 0)) {
545 /* switch address space: */
546 buf[0] = 0x00; buf[1] = address_space;
547 retI2C = sn9c20x_write_i2c_data(dev, 2, 0x01, buf);
548 if (retI2C != 0 && k < 2)
549 udelay(1000);
550 k++;
552 if (retI2C != 0) {
553 if (address_space == MT9V111_ADDRESSSPACE_IFP)
554 UDIA_ERROR("MT9V111/MI0360SOC "
555 "(I2C-slave 0x5c): switch to IFP "
556 "address space failed !\n");
557 else
558 UDIA_ERROR("MT9V111/MI0360SOC "
559 "(I2C-slave 0x5c): switch to sensor "
560 "core address space failed !\n");
561 return -1;
564 return 0;
567 int mt9v111_probe(struct usb_microdia *dev)
569 __u8 buf[2];
570 int ret;
572 /* Select address-space: sensor */
573 buf[0] = 0x00; buf[1] = MT9V111_ADDRESSSPACE_SENSOR;
574 ret = sn9c20x_write_i2c_data(dev, 2, 0x01, buf);
575 if (ret != 0)
576 return ret;
578 ret = sn9c20x_read_i2c_data(dev, 2, 0xff, buf);
579 if (ret == 0) {
580 if (buf[0] != 0x82)
581 return -EINVAL;
582 if (buf[1] == 0x3a) {
583 mt9v111_initialize(dev);
584 dev->camera.set_hvflip = mt9v111_set_hvflip;
585 dev->camera.set_exposure = mt9v111_set_exposure;
586 dev->camera.set_auto_exposure = mt9v111_set_autoexposure;
587 dev->camera.modes = micron_resolutions;
588 dev->camera.nmodes = ARRAY_SIZE(micron_resolutions);
589 dev->camera.fmts = mt9v111_fmts;
590 dev->camera.nfmts = ARRAY_SIZE(mt9v111_fmts);
591 return MT9V111_SENSOR;
595 /** FIXME: always switch back to last register address space */
597 return -EINVAL;
601 * @brief Initialize mt9m111 sensors
603 * @param dev Pointer to device structure
605 * @return 0 or negative error code
608 int mt9m001_initialize(struct usb_microdia *dev)
610 int i;
611 int ret = 0;
612 __u8 value[2], reg;
614 for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
615 reg = mt9m001_init[i][0];
616 value[0] = mt9m001_init[i][1];
617 value[1] = mt9m001_init[i][2];
618 ret = sn9c20x_write_i2c_data(dev, 2, reg, value);
619 if (ret < 0) {
620 UDIA_WARNING("Sensor Init Error (%d). "
621 "line %d\n", ret, i);
622 break;
626 return ret;
629 int mt9m001_probe(struct usb_microdia *dev)
631 int ret;
632 __u8 buf[2];
633 ret = sn9c20x_read_i2c_data(dev, 2, 0x00, buf);
634 if (ret == 0) {
635 if (buf[0] != 0x84)
636 return -EINVAL;
637 if (buf[1] == 0x31) {
638 mt9m001_initialize(dev);
639 dev->camera.modes = micron_resolutions;
640 dev->camera.nmodes = ARRAY_SIZE(micron_resolutions);
641 dev->camera.fmts = mt9x0xx_fmts;
642 dev->camera.nfmts = ARRAY_SIZE(mt9x0xx_fmts);
643 return MT9M001_SENSOR;
647 return -EINVAL;
651 * @brief Initialize mt9m111 sensors
653 * @param dev Pointer to device structure
655 * @return 0 or negative error code
658 int mt9m111_initialize(struct usb_microdia *dev)
660 int i;
661 int ret = 0;
662 __u8 reg;
663 __u8 value[2] = {0x00, 0x00};
665 /* Select address-space: Sensor */
666 sn9c20x_write_i2c_data(dev, 2, 0xf0, value);
668 for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
669 reg = mt9m111_init[i][0];
670 value[0] = mt9m111_init[i][1];
671 value[1] = mt9m111_init[i][2];
672 ret = sn9c20x_write_i2c_data(dev, 2, reg, value);
673 if (ret < 0) {
674 UDIA_WARNING("Sensor Init Error (%d). "
675 "line %d\n", ret, i);
676 break;
680 return ret;
683 int mt9m111_probe(struct usb_microdia *dev)
685 int ret;
686 __u8 buf[2] = {0x00, 0x00};
688 /* Select address-space: Sensor */
689 sn9c20x_write_i2c_data(dev, 2, 0xf0, buf);
691 ret = sn9c20x_read_i2c_data(dev, 2, 0x00, buf);
692 if (ret == 0) {
693 if (buf[0] != 0x14)
694 return -EINVAL;
695 if (buf[1] == 0x3a) {
696 mt9m111_initialize(dev);
697 dev->camera.modes = mt9m111_resolutions;
698 dev->camera.nmodes = ARRAY_SIZE(mt9m111_resolutions);
699 dev->camera.fmts = mt9m111_fmts;
700 dev->camera.nfmts = ARRAY_SIZE(mt9m111_fmts);
701 return MT9M111_SENSOR;
705 return -EINVAL;
708 int mt9m111_set_raw(struct usb_microdia *dev)
710 __u16 buf;
712 /* select address-space: IFP */
713 buf = 0x0001;
714 sn9c20x_write_i2c_data16(dev, 1, 0xf0, &buf);
716 sn9c20x_read_i2c_data16(dev, 1, 0x3a, &buf);
717 buf = (buf & 0xfaff) + 0x0500;
718 sn9c20x_write_i2c_data16(dev, 1, 0x3a, &buf);
720 sn9c20x_read_i2c_data16(dev, 1, 0x9b, &buf);
721 buf = (buf & 0xfaff) + 0x0500;
722 sn9c20x_write_i2c_data16(dev, 1, 0x9b, &buf);
724 sn9c20x_set_raw(dev);
726 return 0;
729 int mt9m111_set_yuv422(struct usb_microdia *dev)
731 __u16 buf;
733 /* select address-space: IFP */
734 buf = 0x0001;
735 sn9c20x_write_i2c_data16(dev, 1, 0xf0, &buf);
737 sn9c20x_read_i2c_data16(dev, 1, 0x3a, &buf);
738 buf &= 0xfaff;
739 sn9c20x_write_i2c_data16(dev, 1, 0x3a, &buf);
741 sn9c20x_read_i2c_data16(dev, 1, 0x9b, &buf);
742 buf &= 0xfaff;
743 sn9c20x_write_i2c_data16(dev, 1, 0x9b, &buf);
745 sn9c20x_set_raw(dev);
747 return 0;
750 int mt9v111_setup_autoexposure(struct usb_microdia *dev)
752 __u16 buf[1];
753 int ret = 0;
754 int ret2 = 0;
756 /* Switch to IFP-register address space: */
757 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
758 if (ret < 0)
759 return -11; /* -EAGAIN */
760 /* Set target luminance and tracking accuracy: */
761 buf[0] = MT9V111_IFP_AE_TRACKINGACCURACY(12)
762 /* 0-255 (default: 16) */
763 | MT9V111_IFP_AE_TARGETLUMINANCE(100);
764 /* 0-255 (default: 100) */
765 ret = sn9c20x_write_i2c_data16(dev, 1,
766 MT9V111_IFPREG_AE_TARGETLUMCTL, buf);
767 /* Set speed and sensitivity: */
768 buf[0] = MT9V111_IFP_AE_DELAY(4)
769 /* 0-7 (fastest-slowest) (default: 4) */
770 | MT9V111_IFP_AE_SPEED(4)
771 /* 0-7 (fastest-slowest) (default: 4) */
772 | MT9V111_IFP_AE_STEPSIZE_MEDIUM;
773 ret += sn9c20x_write_i2c_data16(dev, 1,
774 MT9V111_IFPREG_AE_SPEEDSENSCTL, buf);
775 /* Horizontal boundaries of AE measurement window: */
776 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT(1020)
777 /* 0-1020 (pixel) (default: 1020) */
778 | MT9V111_IFP_AE_WINBOUNDARY_LEFT(12);
779 /* 0-1020 (pixel) (default: 12) */
780 ret += sn9c20x_write_i2c_data16(dev, 1,
781 MT9V111_IFPREG_AE_HWINBOUNDARY, buf);
782 /* Vertical boundaries of AE measurement window: */
783 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM(510)
784 /* 0-510 (pixel) (default: 510) */
785 | MT9V111_IFP_AE_WINBOUNDARY_TOP(32);
786 /* 0-510 (pixel) (default: 32) */
787 ret += sn9c20x_write_i2c_data16(dev, 1,
788 MT9V111_IFPREG_AE_VWINBOUNDARY, buf);
789 /* Horizontal boundaries of AE measurement window
790 * for back light compensation: */
791 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT_BLC(480)
792 /* 0-1020 (pixel) (default: 480) */
793 | MT9V111_IFP_AE_WINBOUNDARY_LEFT_BLC(160);
794 /* 0-1020 (pixel) (default: 160) */
795 ret += sn9c20x_write_i2c_data16(dev, 1,
796 MT9V111_IFPREG_AE_HWINBOUNDARY_BLC, buf);
797 /* Vertical boundaries of AE measurement window
798 * for back light compensation: */
799 buf[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM_BLC(360)
800 /* 0-510 (pixel) (default: 360) */
801 | MT9V111_IFP_AE_WINBOUNDARY_TOP_BLC(120);
802 /* 0-510 (pixel) (default: 120) */
803 ret += sn9c20x_write_i2c_data16(dev, 1,
804 MT9V111_IFPREG_AE_VWINBOUNDARY_BLC, buf);
805 /* Set digital gains limit: */
806 buf[0] = MT9V111_IFP_AE_MAXGAIN_POSTLS(64)
807 /* 0-255 (default: 64) */
808 | MT9V111_IFP_AE_MAXGAIN_PRELS(16);
809 /* 0-255 (default: 16) */
810 ret += sn9c20x_write_i2c_data16(dev, 1,
811 MT9V111_IFPREG_AE_DGAINSLIMITS, buf);
812 /* Read current value of IFP-register 0x06: */
813 ret2 = sn9c20x_read_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
814 if (ret2 == 0) {
815 /* Set new value for register 0x06: */
816 buf[0] = (buf[0] & 0xffe3) | MT9V111_IFP_AE_WIN_COMBINED |
817 MT9V111_IFP_OPMODE_BYPASSCOLORCORR;
818 /* NOTE: BYPASS COLOR CORRECTION HAS
819 * TO BE SET FOR PERMANENT AE ! */
820 /* Write new value to IFP-register 0x06:*/
821 ret += sn9c20x_write_i2c_data16(dev, 1,
822 MT9V111_IFPREG_OPMODECTL, buf);
824 if (ret < 0 || ret2 < 0) {
825 UDIA_WARNING("One or more errors occured during "
826 "setup of AE registers.\n");
827 return -1;
829 return 0;
832 int mt9v111_setup_autowhitebalance(struct usb_microdia *dev)
834 __u16 buf[1];
835 int ret = 0;
837 /* Switch to IFP-register address space: */
838 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
839 if (ret < 0)
840 return -11; /* -EAGAIN */
841 /* Set AWB tint: */
842 buf[0] = MT9V111_IFP_AWB_ADDON_RED(0) /* 0-255 (default: 0) */
843 | MT9V111_IFP_AWB_ADDON_BLUE(0); /* 0-255 (default: 0) */
844 ret = sn9c20x_write_i2c_data16(dev, 1, MT9V111_IFPREG_AWB_TINT, buf);
845 /* Set AWB speed and color saturation: */
846 buf[0] = MT9V111_IFP_AWB_SATURATION_FULL
847 | MT9V111_IFP_AWB_AUTOSATLOWLIGHT_ENABLE
848 | MT9V111_IFP_AWB_DELAY(4)
849 /* 0-7 (fastest-slowest) (default: 4) */
850 | MT9V111_IFP_AWB_SPEED(4);
851 /* 0-7 (fastest-slowest) (default: 4) */
852 ret += sn9c20x_write_i2c_data16(dev, 1,
853 MT9V111_IFPREG_AWB_SPEEDCOLORSATCTL, buf);
854 /* Boundaries of AWB measurement window: */
855 buf[0] = MT9V111_IFP_AWB_WINBOUNDARY_TOP(0)
856 /* 0-480 (pixel) (default: 0) */
857 | MT9V111_IFP_AWB_WINBOUNDARY_BOTTOM(480)
858 /* 0-480 (pixel) (default: 448) */
859 | MT9V111_IFP_AWB_WINBOUNDARY_LEFT(0)
860 /* 0-960 (pixel) (default: 0) */
861 | MT9V111_IFP_AWB_WINBOUNDARY_RIGHT(640);
862 /* 0-960 (pixel) (default: 640) */
863 ret += sn9c20x_write_i2c_data16(dev, 1,
864 MT9V111_IFPREG_AWB_WINBOUNDARY, buf);
866 if (ret < 0) {
867 UDIA_WARNING("One or more errors occured during "
868 "setup of AWB registers.\n");
869 return -1;
871 return 0;
874 int mt9v011_set_exposure(struct usb_microdia *dev)
876 int ret = 0;
877 __u8 buf[2];
879 buf[0] = (dev->vsettings.exposure >> 12);
880 buf[1] = 0;
881 ret |= sn9c20x_write_i2c_data(dev, 2, 0x09, buf);
882 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not
883 * used) for MT9V111 sensor, because IFP controls this register if
884 * one of them is enabled. */
885 return ret;
888 int mt9v111_set_exposure(struct usb_microdia *dev)
890 int ret = 0;
891 __u8 buf[2];
894 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
896 if (ret < 0)
897 return -11; /* -EAGAIN */
899 buf[0] = (dev->vsettings.exposure >> 12);
900 buf[1] = 0;
901 ret |= sn9c20x_write_i2c_data(dev, 2, 0x09, buf);
902 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not
903 * used) for MT9V111 sensor, because IFP controls this register if
904 * one of them is enabled. */
905 return ret;
908 int mt9v011_set_hvflip(struct usb_microdia *dev)
910 int ret = 0;
911 __u8 buf[2];
913 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
914 return -EINVAL;
915 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
916 return -EINVAL;
918 ret = sn9c20x_read_i2c_data(dev, 2, 0x20, buf);
919 if (ret < 0)
920 return ret;
922 if (dev->vsettings.hflip) {
923 buf[0] |= 0x80;
924 /* (MSB) set bit 15: read out from
925 * bottom to top (upside down) */
926 buf[1] |= 0x80;
927 /* (LSB) set bit 7: readout starting 1 row later */
928 } else {
929 buf[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
930 buf[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
932 if (dev->vsettings.vflip) {
933 buf[0] |= 0x40;
934 /* (MSB) set bit 14: read out from right to left (mirrored) */
935 buf[1] |= 0x20;
936 /* (LSB) set bit 5: readout starting 1 column later */
937 } else {
938 buf[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
939 buf[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
942 ret = sn9c20x_write_i2c_data(dev, 2, 0x20, buf);
943 return ret;
946 int mt9v111_set_hvflip(struct usb_microdia *dev)
948 int ret = 0;
949 __u8 buf[2];
951 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
952 return -EINVAL;
953 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
954 return -EINVAL;
957 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
958 if (ret < 0)
959 return -11; /* -EAGAIN */
961 ret = sn9c20x_read_i2c_data(dev, 2, 0x20, buf);
962 if (ret < 0)
963 return ret;
965 if (dev->vsettings.hflip) {
966 buf[0] |= 0x80;
967 /* (MSB) set bit 15: read out from
968 * bottom to top (upside down) */
969 buf[1] |= 0x80;
970 /* (LSB) set bit 7: readout starting 1 row later */
971 } else {
972 buf[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
973 buf[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
975 if (dev->vsettings.vflip) {
976 buf[0] |= 0x40;
977 /* (MSB) set bit 14: read out from right to left (mirrored) */
978 buf[1] |= 0x20;
979 /* (LSB) set bit 5: readout starting 1 column later */
980 } else {
981 buf[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
982 buf[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
985 ret = sn9c20x_write_i2c_data(dev, 2, 0x20, buf);
986 return ret;
989 int mt9v111_set_autoexposure(struct usb_microdia *dev)
991 __u16 buf[1];
992 int ret = 0;
994 /* Switch to IFP-register address space: */
995 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
996 if (ret < 0)
997 return -11; /* -EAGAIN */
998 /* Read current value of IFP-register 0x06: */
999 ret = sn9c20x_read_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
1000 if (ret < 0) {
1001 UDIA_ERROR("Error: setting of auto exposure failed: "
1002 "error while reading from IFP-register 0x06\n");
1003 return ret;
1005 /* Set new value for register 0x06: */
1006 if (dev->vsettings.auto_exposure == 1) {
1007 /* Enable automatic exposure: */
1008 buf[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE;
1009 } else if (dev->vsettings.auto_exposure == 0) {
1010 /* Disable automatic exposure: */
1011 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE;
1012 } else
1013 return -EINVAL;
1014 /* Write new value to IFP-register 0x06: */
1015 ret = sn9c20x_write_i2c_data16(dev, 1, MT9V111_IFPREG_OPMODECTL, buf);
1016 if (ret < 0) {
1017 UDIA_ERROR("Error: setting of auto exposure failed: "
1018 "error while writing to IFP-register 0x06\n");
1019 return ret;
1021 return 0;
1024 int mt9v111_set_autowhitebalance(struct usb_microdia *dev)
1026 __u16 buf[1];
1027 int ret = 0;
1029 /* Switch to IFP-register address space: */
1030 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
1031 if (ret < 0)
1032 return -11; /* -EAGAIN */
1033 /* Read current value of IFP-register 0x06: */
1034 ret = sn9c20x_read_i2c_data16(dev, 1,
1035 MT9V111_IFPREG_OPMODECTL, buf);
1036 if (ret < 0) {
1037 UDIA_ERROR("Error: setting of auto whitebalance failed: "
1038 "error while reading from IFP-register 0x06\n");
1039 return ret;
1041 /* Set new value for register 0x06: */
1042 if (dev->vsettings.auto_whitebalance == 1) {
1043 /* Enable automatic exposure: */
1044 buf[0] |= MT9V111_IFP_OPMODE_AUTOWHITEBALANCE;
1045 } else if (dev->vsettings.auto_whitebalance == 0) {
1046 /* Disable automatic exposure: */
1047 buf[0] &= ~MT9V111_IFP_OPMODE_AUTOWHITEBALANCE;
1048 } else
1049 return -EINVAL;
1050 /* Write new value to IFP-register 0x06:*/
1051 ret = sn9c20x_write_i2c_data16(dev, 1,
1052 MT9V111_IFPREG_OPMODECTL, buf);
1053 if (ret < 0) {
1054 UDIA_ERROR("Error: setting of auto whitebalance failed: "
1055 "error while writing to IFP-register 0x06\n");
1056 return ret;
1058 return 0;
1061 int mt9v111_set_autocorrections(struct usb_microdia *dev, int enable)
1063 __u16 buf[1];
1064 int ret = 0;
1066 /* Switch to IFP-register address space: */
1067 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
1068 if (ret < 0)
1069 return -11; /* -EAGAIN */
1070 /* Read current value of IFP-register 0x06: */
1071 ret = sn9c20x_read_i2c_data16(dev, 1,
1072 MT9V111_IFPREG_OPMODECTL, buf);
1073 if (ret < 0) {
1074 UDIA_ERROR("Error: setting of sensor auto-correction functions "
1075 "failed: error while reading from IFP-register 0x06\n");
1076 return ret;
1078 /* Set new value for register 0x06: */
1079 if (enable == 1)
1080 buf[0] |= MT9V111_IFP_OPMODE_APERTURECORR |
1081 MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR;
1082 else if (enable == 0)
1083 buf[0] &= ~(MT9V111_IFP_OPMODE_APERTURECORR |
1084 MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR);
1085 else
1086 return -EINVAL;
1087 /* Write new value to IFP-register 0x06: */
1088 ret = sn9c20x_write_i2c_data16(dev, 1,
1089 MT9V111_IFPREG_OPMODECTL, buf);
1090 if (ret < 0) {
1091 UDIA_ERROR("Error: setting of sensor auto-correction functions "
1092 "failed: error while writing to IFP-register 0x06\n");
1093 return ret;
1095 return 0;