7 * @brief Common functions and data for the Micron MT9Vx11 sensors.
9 * @note Copyright (C) Comer352l
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include <linux/delay.h>
35 int mt9v111_select_address_space(struct usb_microdia
*dev
, __u8 address_space
)
41 /* check if selection is valid: */
42 if ((address_space
!= MT9V111_ADDRESSSPACE_IFP
) && (address_space
!= MT9V111_ADDRESSSPACE_SENSOR
)) {
43 UDIA_INFO("Error: invalid register address space selection for sensor MT9V111/MI0360SOC !\n");
46 /* read address space slection register: */
49 while ((k
< 3) && (retI2C
!= 0)) {
50 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0x01, SN9C20X_I2C_2WIRE
, buf
);
51 if (retI2C
!= 0 && k
< 2)
56 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): read of reg0x01 (address space selection) failed !\n");
59 /* check current address space: */
60 if ((buf
[0] != 0x00) || (buf
[1] != address_space
)) {
63 while ((k
< 3) && (retI2C
!= 0)) {
64 /* switch address space: */
65 buf
[0] = 0x00; buf
[1] = address_space
;
66 retI2C
= sn9c20x_write_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0x01, SN9C20X_I2C_2WIRE
, buf
);
67 if (retI2C
!= 0 && k
< 2)
72 if (address_space
== MT9V111_ADDRESSSPACE_IFP
)
73 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to IFP address space failed !\n");
75 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to sensor core address space failed !\n");
83 int mt9vx11_sensor_probe(struct usb_microdia
*dev
)
90 /* *** Probe MT9V011/MI0360: */
91 /* read chip version: */
92 for (k
= 0; k
< 3; k
++) {
93 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V011_I2C_SLAVE_ADDRESS
, 2, 0xff, SN9C20X_I2C_2WIRE
, buf
);
95 if ((buf
[0] == 0x82) && (buf
[1] == 0x43)) {
96 UDIA_INFO("Detected sensor: MT9V011/MI0360 (chip version: 0x%02X%02X)\n", buf
[0], buf
[1]);
97 dev
->sensor_slave_address
= MT9V011_I2C_SLAVE_ADDRESS
;
100 UDIA_DEBUG("I2C-slave 0x5d returned invalid chip version: 0x%02X%02X\n", buf
[0], buf
[1]);
104 /* NOTE: DNT DigiMicro 1.3 (microscope camera):
105 * This device uses sensor MT9V111, but slave 0x5d is also successfully read.
106 * Registers 0x00, 0x36 and 0xff of slave 0x5d return chip version 0x0000.
109 /* *** Probe MT9V111/MI0360SOC: */
110 /* switch register address space: */
111 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
114 /* read chip version: */
115 for (k
= 0; k
< 3; k
++) {
116 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0xff, SN9C20X_I2C_2WIRE
, buf
);
118 if ((buf
[0] == 0x82) && (buf
[1] == 0x3a)) {
119 UDIA_INFO("Detected sensor: MT9V111/MI0360SOC (chip version: 0x%02X%02X)\n", buf
[0], buf
[1]);
120 dev
->sensor_slave_address
= MT9V111_I2C_SLAVE_ADDRESS
;
123 UDIA_DEBUG("I2C-slave 0x5c returned invalid chip version: 0x%02X%02X\n", buf
[0], buf
[1]);
128 /* FIXME: always switch back to last register address space */
130 UDIA_INFO("Error: sensor probe failed !\n");
131 dev
->sensor_slave_address
= 0;
136 int mt9v111_setup_autoexposure(struct usb_microdia
*dev
)
142 /* Check if sensor is MT9V111: */
143 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
145 /* Switch to IFP-register address space: */
146 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
148 return -11; /* -EAGAIN */
149 /* Set target luminance and tracking accuracy: */
150 buf
[0] = MT9V111_IFP_AE_TRACKINGACCURACY(12) /* 0-255 (default: 16) */
151 | MT9V111_IFP_AE_TARGETLUMINANCE(100); /* 0-255 (default: 100) */
152 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
153 MT9V111_IFPREG_AE_TARGETLUMCTL
, dev
->sensor_flags
, buf
);
154 /* Set speed and sensitivity: */
155 buf
[0] = MT9V111_IFP_AE_DELAY(4) /* 0-7 (fastest-slowest) (default: 4) */
156 | MT9V111_IFP_AE_SPEED(4) /* 0-7 (fastest-slowest) (default: 4) */
157 | MT9V111_IFP_AE_STEPSIZE_MEDIUM
;
158 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
159 MT9V111_IFPREG_AE_SPEEDSENSCTL
, dev
->sensor_flags
, buf
);
160 /* Horizontal boundaries of AE measurement window: */
161 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT(1020) /* 0-1020 (pixel) (default: 1020) */
162 | MT9V111_IFP_AE_WINBOUNDARY_LEFT(12); /* 0-1020 (pixel) (default: 12) */
163 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
164 MT9V111_IFPREG_AE_HWINBOUNDARY
, dev
->sensor_flags
, buf
);
165 /* Vertical boundaries of AE measurement window: */
166 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM(510) /* 0-510 (pixel) (default: 510) */
167 | MT9V111_IFP_AE_WINBOUNDARY_TOP(32); /* 0-510 (pixel) (default: 32) */
168 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
169 MT9V111_IFPREG_AE_VWINBOUNDARY
, dev
->sensor_flags
, buf
);
170 /* Horizontal boundaries of AE measurement window for back light compensation: */
171 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT_BLC(480) /* 0-1020 (pixel) (default: 480) */
172 | MT9V111_IFP_AE_WINBOUNDARY_LEFT_BLC(160); /* 0-1020 (pixel) (default: 160) */
173 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
174 MT9V111_IFPREG_AE_HWINBOUNDARY_BLC
, dev
->sensor_flags
, buf
);
175 /* Vertical boundaries of AE measurement window for back light compensation: */
176 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM_BLC(360) /* 0-510 (pixel) (default: 360) */
177 | MT9V111_IFP_AE_WINBOUNDARY_TOP_BLC(120); /* 0-510 (pixel) (default: 120) */
178 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
179 MT9V111_IFPREG_AE_VWINBOUNDARY_BLC
, dev
->sensor_flags
, buf
);
180 /* Set digital gains limit: */
181 buf
[0] = MT9V111_IFP_AE_MAXGAIN_POSTLS(64) /* 0-255 (default: 64) */
182 | MT9V111_IFP_AE_MAXGAIN_PRELS(16); /* 0-255 (default: 16) */
183 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
184 MT9V111_IFPREG_AE_DGAINSLIMITS
, dev
->sensor_flags
, buf
);
185 /* Read current value of IFP-register 0x06: */
186 ret2
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
187 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
189 /* Set new value for register 0x06: */
190 buf
[0] = (buf
[0] & 0xffe3) | MT9V111_IFP_AE_WIN_COMBINED
| MT9V111_IFP_OPMODE_BYPASSCOLORCORR
;
191 /* NOTE: BYPASS COLOR CORRECTION HAS TO BE SET FOR PERMANENT AE ! */
192 /* Write new value to IFP-register 0x06:*/
193 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
194 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
197 if (ret
< 0 || ret2
< 0) {
198 UDIA_INFO("One or more errors occured during setup of AE registers.\n");
205 int mt9v111_setup_autowhitebalance(struct usb_microdia
*dev
)
210 /* Check if sensor is MT9V111: */
211 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
213 /* Switch to IFP-register address space: */
214 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
216 return -11; /* -EAGAIN */
218 buf
[0] = MT9V111_IFP_AWB_ADDON_RED(0) /* 0-255 (default: 0) */
219 | MT9V111_IFP_AWB_ADDON_BLUE(0); /* 0-255 (default: 0) */
220 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
221 MT9V111_IFPREG_AWB_TINT
, dev
->sensor_flags
, buf
);
222 /* Set AWB speed and color saturation: */
223 buf
[0] = MT9V111_IFP_AWB_SATURATION_FULL
| MT9V111_IFP_AWB_AUTOSATLOWLIGHT_ENABLE
224 | MT9V111_IFP_AWB_DELAY(4) /* 0-7 (fastest-slowest) (default: 4) */
225 | MT9V111_IFP_AWB_SPEED(4); /* 0-7 (fastest-slowest) (default: 4) */
226 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
227 MT9V111_IFPREG_AWB_SPEEDCOLORSATCTL
, dev
->sensor_flags
, buf
);
228 /* Boundaries of AWB measurement window: */
229 buf
[0] = MT9V111_IFP_AWB_WINBOUNDARY_TOP(0) /* 0-480 (pixel) (default: 0) */
230 | MT9V111_IFP_AWB_WINBOUNDARY_BOTTOM(480) /* 0-480 (pixel) (default: 448) */
231 | MT9V111_IFP_AWB_WINBOUNDARY_LEFT(0) /* 0-960 (pixel) (default: 0) */
232 | MT9V111_IFP_AWB_WINBOUNDARY_RIGHT(640); /* 0-960 (pixel) (default: 640) */
233 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
234 MT9V111_IFPREG_AWB_WINBOUNDARY
, dev
->sensor_flags
, buf
);
237 UDIA_INFO("One or more errors occured during setup of AWB registers.\n");
244 int mt9vx11_set_exposure(struct usb_microdia
*dev
)
249 if (dev
->sensor_slave_address
== MT9V111_I2C_SLAVE_ADDRESS
)
250 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
253 return -11; /* -EAGAIN */
255 buf
[0] = (dev
->vsettings
.exposure
>> 12);
257 ret
|= sn9c20x_write_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x09, dev
->sensor_flags
, buf
);
258 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not used)
259 for MT9V111 sensor, because IFP controls this register if one of them
266 int mt9vx11_set_hvflip(struct usb_microdia
*dev
)
271 if ((dev
->vsettings
.hflip
> 1) || (dev
->vsettings
.hflip
< 0))
273 if ((dev
->vsettings
.vflip
> 1) || (dev
->vsettings
.vflip
< 0))
276 if (dev
->sensor_slave_address
== MT9V111_I2C_SLAVE_ADDRESS
)
277 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
279 return -11; /* -EAGAIN */
281 ret
= sn9c20x_read_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x20, dev
->sensor_flags
, buf
);
285 if (dev
->vsettings
.hflip
) {
286 buf
[0] |= 0x80; /* (MSB) set bit 15: read out from bottom to top (upside down) */
287 buf
[1] |= 0x80; /* (LSB) set bit 7: readout starting 1 row later */
289 buf
[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
290 buf
[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
292 if (dev
->vsettings
.vflip
) {
293 buf
[0] |= 0x40; /* (MSB) set bit 14: read out from right to left (mirrored) */
294 buf
[1] |= 0x20; /* (LSB) set bit 5: readout starting 1 column later */
296 buf
[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
297 buf
[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
300 ret
= sn9c20x_write_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x20, dev
->sensor_flags
, buf
);
305 int mt9v111_set_autoexposure(struct usb_microdia
*dev
)
310 /* Check if sensor is MT9V111: */
311 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
313 /* Switch to IFP-register address space: */
314 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
316 return -11; /* -EAGAIN */
317 /* Read current value of IFP-register 0x06: */
318 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
319 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
321 UDIA_ERROR("Error: setting of auto exposure failed: error while reading from IFP-register 0x06\n");
324 /* Set new value for register 0x06: */
325 if (dev
->vsettings
.auto_exposure
== 1) {
326 /* Enable automatic exposure: */
327 buf
[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE
;
328 } else if (dev
->vsettings
.auto_exposure
== 0) {
329 /* Disable automatic exposure: */
330 buf
[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE
;
333 /* Write new value to IFP-register 0x06: */
334 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
335 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
337 UDIA_ERROR("Error: setting of auto exposure failed: error while writing to IFP-register 0x06\n");
344 int mt9v111_set_autowhitebalance(struct usb_microdia
*dev
)
349 /* Check if sensor is MT9V111: */
350 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
352 /* Switch to IFP-register address space: */
353 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
355 return -11; /* -EAGAIN */
356 /* Read current value of IFP-register 0x06: */
357 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
358 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
360 UDIA_ERROR("Error: setting of auto whitebalance failed: error while reading from IFP-register 0x06\n");
363 /* Set new value for register 0x06: */
364 if (dev
->vsettings
.auto_whitebalance
== 1) {
365 /* Enable automatic exposure: */
366 buf
[0] |= MT9V111_IFP_OPMODE_AUTOWHITEBALANCE
;
367 } else if (dev
->vsettings
.auto_whitebalance
== 0) {
368 /* Disable automatic exposure: */
369 buf
[0] &= ~MT9V111_IFP_OPMODE_AUTOWHITEBALANCE
;
372 /* Write new value to IFP-register 0x06:*/
373 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
374 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
376 UDIA_ERROR("Error: setting of auto whitebalance failed: error while writing to IFP-register 0x06\n");
383 int mt9v111_set_autocorrections(struct usb_microdia
*dev
, int enable
)
388 /* Check if sensor is MT9V111: */
389 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
391 /* Switch to IFP-register address space: */
392 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
394 return -11; /* -EAGAIN */
395 /* Read current value of IFP-register 0x06: */
396 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
397 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
399 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while reading from IFP-register 0x06\n");
402 /* Set new value for register 0x06: */
404 buf
[0] |= MT9V111_IFP_OPMODE_APERTURECORR
| MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR
;
405 else if (enable
== 0)
406 buf
[0] &= ~(MT9V111_IFP_OPMODE_APERTURECORR
| MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR
);
409 /* Write new value to IFP-register 0x06: */
410 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
411 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
413 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while writing to IFP-register 0x06\n");