6 * @brief Common functions and data for the Micron MT9Vx11 sensors.
8 * @note Copyright (C) Comer352l
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
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>
34 int mt9v111_select_address_space(struct usb_microdia
*dev
, __u8 address_space
)
40 /* check if selection is valid: */
41 if ((address_space
!= MT9V111_ADDRESSSPACE_IFP
) && (address_space
!= MT9V111_ADDRESSSPACE_SENSOR
)) {
42 UDIA_INFO("Error: invalid register address space selection for sensor MT9V111/MI0360SOC !\n");
45 /* read address space slection register: */
48 while ((k
< 3) && (retI2C
!= 0)) {
49 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0x01, SN9C20X_I2C_2WIRE
, buf
);
50 if (retI2C
!= 0 && k
< 2)
55 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): read of reg0x01 (address space selection) failed !\n");
58 /* check current address space: */
59 if ((buf
[0] != 0x00) || (buf
[1] != address_space
)) {
62 while ((k
< 3) && (retI2C
!= 0)) {
63 /* switch address space: */
64 buf
[0] = 0x00; buf
[1] = address_space
;
65 retI2C
= sn9c20x_write_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0x01, SN9C20X_I2C_2WIRE
, buf
);
66 if (retI2C
!= 0 && k
< 2)
71 if (address_space
== MT9V111_ADDRESSSPACE_IFP
)
72 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to IFP address space failed !\n");
74 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to sensor core address space failed !\n");
82 int mt9vx11_sensor_probe(struct usb_microdia
*dev
)
89 /* *** Probe MT9V011/MI0360: */
90 /* read chip version: */
91 for (k
= 0; k
< 3; k
++) {
92 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V011_I2C_SLAVE_ADDRESS
, 2, 0xff, SN9C20X_I2C_2WIRE
, buf
);
94 if ((buf
[0] == 0x82) && (buf
[1] == 0x43)) {
95 UDIA_INFO("Detected sensor: MT9V011/MI0360 (chip version: 0x%02X%02X)\n", buf
[0], buf
[1]);
96 dev
->sensor_slave_address
= MT9V011_I2C_SLAVE_ADDRESS
;
99 UDIA_DEBUG("I2C-slave 0x5d returned invalid chip version: 0x%02X%02X\n", buf
[0], buf
[1]);
103 /* NOTE: DNT DigiMicro 1.3 (microscope camera):
104 * This device uses sensor MT9V111, but slave 0x5d is also successfully read.
105 * Registers 0x00, 0x36 and 0xff of slave 0x5d return chip version 0x0000.
108 /* *** Probe MT9V111/MI0360SOC: */
109 /* switch register address space: */
110 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
113 /* read chip version: */
114 for (k
= 0; k
< 3; k
++) {
115 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0xff, SN9C20X_I2C_2WIRE
, buf
);
117 if ((buf
[0] == 0x82) && (buf
[1] == 0x3a)) {
118 UDIA_INFO("Detected sensor: MT9V111/MI0360SOC (chip version: 0x%02X%02X)\n", buf
[0], buf
[1]);
119 dev
->sensor_slave_address
= MT9V111_I2C_SLAVE_ADDRESS
;
122 UDIA_DEBUG("I2C-slave 0x5c returned invalid chip version: 0x%02X%02X\n", buf
[0], buf
[1]);
127 /* FIXME: always switch back to last register address space */
129 UDIA_INFO("Error: sensor probe failed !\n");
130 dev
->sensor_slave_address
= 0;
135 int mt9v111_setup_autoexposure(struct usb_microdia
*dev
)
141 /* Check if sensor is MT9V111: */
142 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
144 /* Switch to IFP-register address space: */
145 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
147 return -11; /* -EAGAIN */
148 /* Set target luminance and tracking accuracy: */
149 buf
[0] = MT9V111_IFP_AE_TRACKINGACCURACY(12) /* 0-255 (default: 16) */
150 | MT9V111_IFP_AE_TARGETLUMINANCE(100); /* 0-255 (default: 100) */
151 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
152 MT9V111_IFPREG_AE_TARGETLUMCTL
, dev
->sensor_flags
, buf
);
153 /* Set speed and sensitivity: */
154 buf
[0] = MT9V111_IFP_AE_DELAY(4) /* 0-7 (fastest-slowest) (default: 4) */
155 | MT9V111_IFP_AE_SPEED(4) /* 0-7 (fastest-slowest) (default: 4) */
156 | MT9V111_IFP_AE_STEPSIZE_MEDIUM
;
157 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
158 MT9V111_IFPREG_AE_SPEEDSENSCTL
, dev
->sensor_flags
, buf
);
159 /* Horizontal boundaries of AE measurement window: */
160 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT(1020) /* 0-1020 (pixel) (default: 1020) */
161 | MT9V111_IFP_AE_WINBOUNDARY_LEFT(12); /* 0-1020 (pixel) (default: 12) */
162 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
163 MT9V111_IFPREG_AE_HWINBOUNDARY
, dev
->sensor_flags
, buf
);
164 /* Vertical boundaries of AE measurement window: */
165 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM(510) /* 0-510 (pixel) (default: 510) */
166 | MT9V111_IFP_AE_WINBOUNDARY_TOP(32); /* 0-510 (pixel) (default: 32) */
167 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
168 MT9V111_IFPREG_AE_VWINBOUNDARY
, dev
->sensor_flags
, buf
);
169 /* Horizontal boundaries of AE measurement window for back light compensation: */
170 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT_BLC(480) /* 0-1020 (pixel) (default: 480) */
171 | MT9V111_IFP_AE_WINBOUNDARY_LEFT_BLC(160); /* 0-1020 (pixel) (default: 160) */
172 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
173 MT9V111_IFPREG_AE_HWINBOUNDARY_BLC
, dev
->sensor_flags
, buf
);
174 /* Vertical boundaries of AE measurement window for back light compensation: */
175 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM_BLC(360) /* 0-510 (pixel) (default: 360) */
176 | MT9V111_IFP_AE_WINBOUNDARY_TOP_BLC(120); /* 0-510 (pixel) (default: 120) */
177 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
178 MT9V111_IFPREG_AE_VWINBOUNDARY_BLC
, dev
->sensor_flags
, buf
);
179 /* Set digital gains limit: */
180 buf
[0] = MT9V111_IFP_AE_MAXGAIN_POSTLS(64) /* 0-255 (default: 64) */
181 | MT9V111_IFP_AE_MAXGAIN_PRELS(16); /* 0-255 (default: 16) */
182 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
183 MT9V111_IFPREG_AE_DGAINSLIMITS
, dev
->sensor_flags
, buf
);
184 /* Read current value of IFP-register 0x06: */
185 ret2
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
186 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
188 /* Set new value for register 0x06: */
189 buf
[0] = (buf
[0] & 0xffe3) | MT9V111_IFP_AE_WIN_COMBINED
| MT9V111_IFP_OPMODE_BYPASSCOLORCORR
;
190 /* NOTE: BYPASS COLOR CORRECTION HAS TO BE SET FOR PERMANENT AE ! */
191 /* Write new value to IFP-register 0x06:*/
192 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
193 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
196 if (ret
< 0 || ret2
< 0) {
197 UDIA_INFO("One or more errors occured during setup of AE registers.\n");
204 int mt9v111_setup_autowhitebalance(struct usb_microdia
*dev
)
209 /* Check if sensor is MT9V111: */
210 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
212 /* Switch to IFP-register address space: */
213 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
215 return -11; /* -EAGAIN */
217 buf
[0] = MT9V111_IFP_AWB_ADDON_RED(0) /* 0-255 (default: 0) */
218 | MT9V111_IFP_AWB_ADDON_BLUE(0); /* 0-255 (default: 0) */
219 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
220 MT9V111_IFPREG_AWB_TINT
, dev
->sensor_flags
, buf
);
221 /* Set AWB speed and color saturation: */
222 buf
[0] = MT9V111_IFP_AWB_SATURATION_FULL
| MT9V111_IFP_AWB_AUTOSATLOWLIGHT_ENABLE
223 | MT9V111_IFP_AWB_DELAY(4) /* 0-7 (fastest-slowest) (default: 4) */
224 | MT9V111_IFP_AWB_SPEED(4); /* 0-7 (fastest-slowest) (default: 4) */
225 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
226 MT9V111_IFPREG_AWB_SPEEDCOLORSATCTL
, dev
->sensor_flags
, buf
);
227 /* Boundaries of AWB measurement window: */
228 buf
[0] = MT9V111_IFP_AWB_WINBOUNDARY_TOP(0) /* 0-480 (pixel) (default: 0) */
229 | MT9V111_IFP_AWB_WINBOUNDARY_BOTTOM(480) /* 0-480 (pixel) (default: 448) */
230 | MT9V111_IFP_AWB_WINBOUNDARY_LEFT(0) /* 0-960 (pixel) (default: 0) */
231 | MT9V111_IFP_AWB_WINBOUNDARY_RIGHT(640); /* 0-960 (pixel) (default: 640) */
232 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
233 MT9V111_IFPREG_AWB_WINBOUNDARY
, dev
->sensor_flags
, buf
);
236 UDIA_INFO("One or more errors occured during setup of AWB registers.\n");
243 int mt9vx11_set_exposure(struct usb_microdia
*dev
)
248 if (dev
->sensor_slave_address
== MT9V111_I2C_SLAVE_ADDRESS
)
249 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
252 return -11; /* -EAGAIN */
254 buf
[0] = (dev
->vsettings
.exposure
>> 12);
256 ret
|= sn9c20x_write_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x09, dev
->sensor_flags
, buf
);
257 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not used)
258 for MT9V111 sensor, because IFP controls this register if one of them
265 int mt9vx11_set_hvflip(struct usb_microdia
*dev
)
270 if ((dev
->vsettings
.hflip
> 1) || (dev
->vsettings
.hflip
< 0))
272 if ((dev
->vsettings
.vflip
> 1) || (dev
->vsettings
.vflip
< 0))
275 if (dev
->sensor_slave_address
== MT9V111_I2C_SLAVE_ADDRESS
)
276 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
278 return -11; /* -EAGAIN */
280 ret
= sn9c20x_read_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x20, dev
->sensor_flags
, buf
);
284 if (dev
->vsettings
.hflip
) {
285 buf
[0] |= 0x80; /* (MSB) set bit 15: read out from bottom to top (upside down) */
286 buf
[1] |= 0x80; /* (LSB) set bit 7: readout starting 1 row later */
288 buf
[0] &= 0x7f; /* (MSB) unset bit 15: normal readout */
289 buf
[1] &= 0x7f; /* (LSB) unset bit 7: normal readout */
291 if (dev
->vsettings
.vflip
) {
292 buf
[0] |= 0x40; /* (MSB) set bit 14: read out from right to left (mirrored) */
293 buf
[1] |= 0x20; /* (LSB) set bit 5: readout starting 1 column later */
295 buf
[0] &= 0xbf; /* (MSB) unset bit 14: normal readout */
296 buf
[1] &= 0xdf; /* (LSB) unset bit 5: normal readout */
299 ret
= sn9c20x_write_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x20, dev
->sensor_flags
, buf
);
304 int mt9v111_set_autoexposure(struct usb_microdia
*dev
)
309 /* Check if sensor is MT9V111: */
310 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
312 /* Switch to IFP-register address space: */
313 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
315 return -11; /* -EAGAIN */
316 /* Read current value of IFP-register 0x06: */
317 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
318 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
320 UDIA_ERROR("Error: setting of auto exposure failed: error while reading from IFP-register 0x06\n");
323 /* Set new value for register 0x06: */
324 if (dev
->vsettings
.auto_exposure
== 1) {
325 /* Enable automatic exposure: */
326 buf
[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE
;
327 } else if (dev
->vsettings
.auto_exposure
== 0) {
328 /* Disable automatic exposure: */
329 buf
[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE
;
332 /* Write new value to IFP-register 0x06: */
333 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
334 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
336 UDIA_ERROR("Error: setting of auto exposure failed: error while writing to IFP-register 0x06\n");
343 int mt9v111_set_autowhitebalance(struct usb_microdia
*dev
)
348 /* Check if sensor is MT9V111: */
349 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
351 /* Switch to IFP-register address space: */
352 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
354 return -11; /* -EAGAIN */
355 /* Read current value of IFP-register 0x06: */
356 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
357 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
359 UDIA_ERROR("Error: setting of auto whitebalance failed: error while reading from IFP-register 0x06\n");
362 /* Set new value for register 0x06: */
363 if (dev
->vsettings
.auto_whitebalance
== 1) {
364 /* Enable automatic exposure: */
365 buf
[0] |= MT9V111_IFP_OPMODE_AUTOWHITEBALANCE
;
366 } else if (dev
->vsettings
.auto_whitebalance
== 0) {
367 /* Disable automatic exposure: */
368 buf
[0] &= ~MT9V111_IFP_OPMODE_AUTOWHITEBALANCE
;
371 /* Write new value to IFP-register 0x06:*/
372 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
373 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
375 UDIA_ERROR("Error: setting of auto whitebalance failed: error while writing to IFP-register 0x06\n");
382 int mt9v111_set_autocorrections(struct usb_microdia
*dev
, int enable
)
387 /* Check if sensor is MT9V111: */
388 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
390 /* Switch to IFP-register address space: */
391 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
393 return -11; /* -EAGAIN */
394 /* Read current value of IFP-register 0x06: */
395 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
396 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
398 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while reading from IFP-register 0x06\n");
401 /* Set new value for register 0x06: */
403 buf
[0] |= MT9V111_IFP_OPMODE_APERTURECORR
| MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR
;
404 else if (enable
== 0)
405 buf
[0] &= ~(MT9V111_IFP_OPMODE_APERTURECORR
| MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR
);
408 /* Write new value to IFP-register 0x06: */
409 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
410 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
412 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while writing to IFP-register 0x06\n");