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
))
44 UDIA_INFO("Error: invalid register address space selection for sensor MT9V111/MI0360SOC !\n");
47 // read address space slection register:
50 while ((k
<3) && (retI2C
!= 0))
52 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0x01, SN9C20X_I2C_2WIRE
, buf
);
53 if (retI2C
!=0 && k
<2) udelay(1000);
58 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): read of reg0x01 (address space selection) failed !\n");
61 // check current address space:
62 if ((buf
[0] != 0x00) || (buf
[1] != address_space
))
66 while ((k
<3) && (retI2C
!= 0))
68 // switch address space:
69 buf
[0] = 0x00; buf
[1] = address_space
;
70 retI2C
= sn9c20x_write_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0x01, SN9C20X_I2C_2WIRE
, buf
);
71 if (retI2C
!=0 && k
<2) udelay(1000);
76 if (address_space
== MT9V111_ADDRESSSPACE_IFP
)
77 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to IFP address space failed !\n");
79 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to sensor core address space failed !\n");
87 int mt9vx11_sensor_probe(struct usb_microdia
* dev
)
94 // *** Probe MT9V011/MI0360:
98 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V011_I2C_SLAVE_ADDRESS
, 2, 0xff, SN9C20X_I2C_2WIRE
, buf
);
101 if ((buf
[0] == 0x82) && (buf
[1] == 0x43))
103 UDIA_INFO("Detected sensor: MT9V011/MI0360 (chip version: 0x%02X%02X)\n", buf
[0], buf
[1]);
104 dev
->sensor_slave_address
= MT9V011_I2C_SLAVE_ADDRESS
;
108 UDIA_DEBUG("I2C-slave 0x5d returned invalid chip version: 0x%02X%02X\n", buf
[0], buf
[1]);
113 /* NOTE: DNT DigiMicro 1.3 (microscope camera):
114 * This device uses sensor MT9V111, but slave 0x5d is also successfully read.
115 * Registers 0x00, 0x36 and 0xff of slave 0x5d return chip version 0x0000.
118 // *** Probe MT9V111/MI0360SOC:
119 // switch register address space:
120 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
121 if (ret
!= 0) return -1;
122 // read chip version:
125 retI2C
= sn9c20x_read_i2c_data(dev
, MT9V111_I2C_SLAVE_ADDRESS
, 2, 0xff, SN9C20X_I2C_2WIRE
, buf
);
128 if ((buf
[0] == 0x82) && (buf
[1] == 0x3a))
130 UDIA_INFO("Detected sensor: MT9V111/MI0360SOC (chip version: 0x%02X%02X)\n", buf
[0], buf
[1]);
131 dev
->sensor_slave_address
= MT9V111_I2C_SLAVE_ADDRESS
;
135 UDIA_DEBUG("I2C-slave 0x5c returned invalid chip version: 0x%02X%02X\n", buf
[0], buf
[1]);
141 // FIXME: always switch back to last register address space
143 UDIA_INFO("Error: sensor probe failed !\n");
144 dev
->sensor_slave_address
= 0;
149 int mt9v111_setup_autoexposure(struct usb_microdia
*dev
)
155 // Check if sensor is MT9V111:
156 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
158 // Switch to IFP-register address space:
159 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
160 if (ret
< 0) return -11; // -EAGAIN
161 // Set target luminance and tracking accuracy:
162 buf
[0] = MT9V111_IFP_AE_TRACKINGACCURACY(12) // 0-255 (default: 16)
163 | MT9V111_IFP_AE_TARGETLUMINANCE(100); // 0-255 (default: 100)
164 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
165 MT9V111_IFPREG_AE_TARGETLUMCTL
, dev
->sensor_flags
, buf
);
166 // Set speed and sensitivity:
167 buf
[0] = MT9V111_IFP_AE_DELAY(4) // 0-7 (fastest-slowest) (default: 4)
168 | MT9V111_IFP_AE_SPEED(4) // 0-7 (fastest-slowest) (default: 4)
169 | MT9V111_IFP_AE_STEPSIZE_MEDIUM
;
170 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
171 MT9V111_IFPREG_AE_SPEEDSENSCTL
, dev
->sensor_flags
, buf
);
172 // Horizontal boundaries of AE measurement window:
173 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT(1020) // 0-1020 (pixel) (default: 1020)
174 | MT9V111_IFP_AE_WINBOUNDARY_LEFT(12); // 0-1020 (pixel) (default: 12)
175 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
176 MT9V111_IFPREG_AE_HWINBOUNDARY
, dev
->sensor_flags
, buf
);
177 // Vertical boundaries of AE measurement window:
178 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM(510) // 0-510 (pixel) (default: 510)
179 | MT9V111_IFP_AE_WINBOUNDARY_TOP(32); // 0-510 (pixel) (default: 32)
180 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
181 MT9V111_IFPREG_AE_VWINBOUNDARY
, dev
->sensor_flags
, buf
);
182 // Horizontal boundaries of AE measurement window for back light compensation:
183 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_RIGHT_BLC(480) // 0-1020 (pixel) (default: 480)
184 | MT9V111_IFP_AE_WINBOUNDARY_LEFT_BLC(160); // 0-1020 (pixel) (default: 160)
185 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
186 MT9V111_IFPREG_AE_HWINBOUNDARY_BLC
, dev
->sensor_flags
, buf
);
187 // Vertical boundaries of AE measurement window for back light compensation:
188 buf
[0] = MT9V111_IFP_AE_WINBOUNDARY_BOTTOM_BLC(360) // 0-510 (pixel) (default: 360)
189 | MT9V111_IFP_AE_WINBOUNDARY_TOP_BLC(120); // 0-510 (pixel) (default: 120)
190 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
191 MT9V111_IFPREG_AE_VWINBOUNDARY_BLC
, dev
->sensor_flags
, buf
);
192 // Set digital gains limit:
193 buf
[0] = MT9V111_IFP_AE_MAXGAIN_POSTLS(64) // 0-255 (default: 64)
194 | MT9V111_IFP_AE_MAXGAIN_PRELS(16); // 0-255 (default: 16)
195 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
196 MT9V111_IFPREG_AE_DGAINSLIMITS
, dev
->sensor_flags
, buf
);
197 // Read current value of IFP-register 0x06:
198 ret2
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
199 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
202 // Set new value for register 0x06:
203 buf
[0] = (buf
[0] & 0xffe3) | MT9V111_IFP_AE_WIN_COMBINED
| MT9V111_IFP_OPMODE_BYPASSCOLORCORR
;
204 // NOTE: BYPASS COLOR CORRECTION HAS TO BE SET FOR PERMANENT AE !
205 // Write new value to IFP-register 0x06:
206 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
207 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
210 if (ret
< 0 || ret2
< 0)
212 UDIA_INFO("One or more errors occured during setup of AE registers.");
219 int mt9v111_setup_autowhitebalance(struct usb_microdia
*dev
)
224 // Check if sensor is MT9V111:
225 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
227 // Switch to IFP-register address space:
228 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
229 if (ret
< 0) return -11; // -EAGAIN
231 buf
[0] = MT9V111_IFP_AWB_ADDON_RED(0) // 0-255 (default: 0)
232 | MT9V111_IFP_AWB_ADDON_BLUE(0); // 0-255 (default: 0)
233 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
234 MT9V111_IFPREG_AWB_TINT
, dev
->sensor_flags
, buf
);
235 // Set AWB speed and color saturation:
236 buf
[0] = MT9V111_IFP_AWB_SATURATION_FULL
| MT9V111_IFP_AWB_AUTOSATLOWLIGHT_ENABLE
237 | MT9V111_IFP_AWB_DELAY(4) // 0-7 (fastest-slowest) (default: 4)
238 | MT9V111_IFP_AWB_SPEED(4); // 0-7 (fastest-slowest) (default: 4)
239 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
240 MT9V111_IFPREG_AWB_SPEEDCOLORSATCTL
, dev
->sensor_flags
, buf
);
241 // Boundaries of AWB measurement window:
242 buf
[0] = MT9V111_IFP_AWB_WINBOUNDARY_TOP(0) // 0-480 (pixel) (default: 0)
243 | MT9V111_IFP_AWB_WINBOUNDARY_BOTTOM(480) // 0-480 (pixel) (default: 448)
244 | MT9V111_IFP_AWB_WINBOUNDARY_LEFT(0) // 0-960 (pixel) (default: 0)
245 | MT9V111_IFP_AWB_WINBOUNDARY_RIGHT(640); // 0-960 (pixel) (default: 640)
246 ret
+= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
247 MT9V111_IFPREG_AWB_WINBOUNDARY
, dev
->sensor_flags
, buf
);
251 UDIA_INFO("One or more errors occured during setup of AWB registers.");
258 int mt9vx11_set_exposure(struct usb_microdia
*dev
)
263 if (dev
->sensor_slave_address
== MT9V111_I2C_SLAVE_ADDRESS
)
264 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
266 if (ret
< 0) return -11; // -EAGAIN
268 buf
[0] = (dev
->vsettings
.exposure
>> 12);
270 ret
|= sn9c20x_write_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x09, dev
->sensor_flags
, buf
);
271 /* Maybe we have to disable AE/AWB/flicker avoidence (currently not used)
272 for MT9V111 sensor, because IFP controls this register if one of them
279 int mt9vx11_set_hvflip(struct usb_microdia
*dev
)
284 if ((dev
->vsettings
.hflip
> 1) || (dev
->vsettings
.hflip
< 0)) return -EINVAL
;
285 if ((dev
->vsettings
.vflip
> 1) || (dev
->vsettings
.vflip
< 0)) return -EINVAL
;
287 if (dev
->sensor_slave_address
== MT9V111_I2C_SLAVE_ADDRESS
)
288 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_SENSOR
);
289 if (ret
< 0) return -11; // -EAGAIN
291 ret
= sn9c20x_read_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x20, dev
->sensor_flags
, buf
);
292 if (ret
< 0) return ret
;
294 if (dev
->vsettings
.hflip
)
296 buf
[0] |= 0x80; // (MSB) set bit 15: read out from bottom to top (upside down)
297 buf
[1] |= 0x80; // (LSB) set bit 7: readout starting 1 row later
301 buf
[0] &= 0x7f; // (MSB) unset bit 15: normal readout
302 buf
[1] &= 0x7f; // (LSB) unset bit 7: normal readout
304 if (dev
->vsettings
.vflip
)
306 buf
[0] |= 0x40; // (MSB) set bit 14: read out from right to left (mirrored)
307 buf
[1] |= 0x20; // (LSB) set bit 5: readout starting 1 column later
311 buf
[0] &= 0xbf; // (MSB) unset bit 14: normal readout
312 buf
[1] &= 0xdf; // (LSB) unset bit 5: normal readout
315 ret
= sn9c20x_write_i2c_data(dev
, dev
->sensor_slave_address
, 2, 0x20, dev
->sensor_flags
, buf
);
320 int mt9v111_set_autoexposure(struct usb_microdia
*dev
)
325 // Check if sensor is MT9V111:
326 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
328 // Switch to IFP-register address space:
329 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
330 if (ret
< 0) return -11; // -EAGAIN
331 // Read current value of IFP-register 0x06:
332 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
333 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
336 UDIA_ERROR("Error: setting of auto exposure failed: error while reading from IFP-register 0x06");
339 // Set new value for register 0x06:
340 if (dev
->vsettings
.auto_exposure
== 1)
342 // Enable automatic exposure:
343 buf
[0] |= MT9V111_IFP_OPMODE_AUTOEXPOSURE
;
345 else if (dev
->vsettings
.auto_exposure
== 0)
347 // Disable automatic exposure:
348 buf
[0] &= ~MT9V111_IFP_OPMODE_AUTOEXPOSURE
;
352 // Write new value to IFP-register 0x06:
353 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
354 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
357 UDIA_ERROR("Error: setting of auto exposure failed: error while writing to IFP-register 0x06");
364 int mt9v111_set_autowhitebalance(struct usb_microdia
*dev
)
369 // Check if sensor is MT9V111:
370 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
372 // Switch to IFP-register address space:
373 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
374 if (ret
< 0) return -11; // -EAGAIN
375 // Read current value of IFP-register 0x06:
376 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
377 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
380 UDIA_ERROR("Error: setting of auto whitebalance failed: error while reading from IFP-register 0x06");
383 // Set new value for register 0x06:
384 if (dev
->vsettings
.auto_whitebalance
== 1)
386 // Enable automatic exposure:
387 buf
[0] |= MT9V111_IFP_OPMODE_AUTOWHITEBALANCE
;
389 else if (dev
->vsettings
.auto_whitebalance
== 0)
391 // Disable automatic exposure:
392 buf
[0] &= ~MT9V111_IFP_OPMODE_AUTOWHITEBALANCE
;
396 // Write new value to IFP-register 0x06:
397 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
398 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
401 UDIA_ERROR("Error: setting of auto whitebalance failed: error while writing to IFP-register 0x06");
408 int mt9v111_set_autocorrections(struct usb_microdia
*dev
, int enable
)
413 // Check if sensor is MT9V111:
414 if (dev
->sensor_slave_address
!= MT9V111_I2C_SLAVE_ADDRESS
)
416 // Switch to IFP-register address space:
417 ret
= mt9v111_select_address_space(dev
, MT9V111_ADDRESSSPACE_IFP
);
418 if (ret
< 0) return -11; // -EAGAIN
419 // Read current value of IFP-register 0x06:
420 ret
= sn9c20x_read_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
421 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
424 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while reading from IFP-register 0x06");
427 // Set new value for register 0x06:
430 buf
[0] |= MT9V111_IFP_OPMODE_APERTURECORR
| MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR
;
432 else if (enable
== 0)
434 buf
[0] &= ~(MT9V111_IFP_OPMODE_APERTURECORR
| MT9V111_IFP_OPMODE_ONTHEFLYDEFECTCORR
);
438 // Write new value to IFP-register 0x06:
439 ret
= sn9c20x_write_i2c_data16(dev
, dev
->sensor_slave_address
, 1,
440 MT9V111_IFPREG_OPMODECTL
, dev
->sensor_flags
, buf
);
443 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while writing to IFP-register 0x06");