Fix warnings and errors when compiled under rt kernel
[microdia.git] / mt9vx11.c
blobb722431cc3c803e4be02c7bdfc1b471a0e4b4425
1 /**
2 * @file mt9vx11.c
3 * @author Comer352l
4 * @date 2008-04-25
5 * @version v0.0.0
7 * @brief Common functions and data for the Micron MT9Vx11 sensors.
9 * @note Copyright (C) Comer352l
11 * @par Licences
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
16 * any later version.
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>
29 #include "microdia.h"
30 #include "sn9c20x.h"
31 #include "mt9vx11.h"
35 int mt9v111_select_address_space(struct usb_microdia * dev, __u8 address_space)
37 __u8 buf[2];
38 int retI2C;
39 int k;
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");
45 return -1;
47 // read address space slection register:
48 k = 0;
49 retI2C = -1;
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);
54 k++;
56 if (retI2C != 0)
58 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): read of reg0x01 (address space selection) failed !\n");
59 return -1;
61 // check current address space:
62 if ((buf[0] != 0x00) || (buf[1] != address_space))
64 k = 0;
65 retI2C = -1;
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);
72 k++;
74 if (retI2C != 0)
76 if (address_space == MT9V111_ADDRESSSPACE_IFP)
77 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to IFP address space failed !\n");
78 else
79 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to sensor core address space failed !\n");
80 return -1;
83 return 0;
87 int mt9vx11_sensor_probe(struct usb_microdia * dev)
89 __u8 buf[2];
90 int ret;
91 int retI2C;
92 int k;
94 // *** Probe MT9V011/MI0360:
95 // read chip version:
96 for (k=0; k<3; k++)
98 retI2C = sn9c20x_read_i2c_data(dev, MT9V011_I2C_SLAVE_ADDRESS, 2, 0xff, SN9C20X_I2C_2WIRE, buf);
99 if (retI2C == 0)
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;
105 return 0;
107 else
108 UDIA_DEBUG("I2C-slave 0x5d returned invalid chip version: 0x%02X%02X\n", buf[0], buf[1]);
110 else
111 udelay(500);
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:
123 for (k=0; k<3; k++)
125 retI2C = sn9c20x_read_i2c_data(dev, MT9V111_I2C_SLAVE_ADDRESS, 2, 0xff, SN9C20X_I2C_2WIRE, buf);
126 if (retI2C == 0)
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;
132 return 0;
134 else
135 UDIA_DEBUG("I2C-slave 0x5c returned invalid chip version: 0x%02X%02X\n", buf[0], buf[1]);
137 else
138 udelay(500);
141 // FIXME: always switch back to last register address space
143 UDIA_INFO("Error: sensor probe failed !\n");
144 dev->sensor_slave_address = 0;
145 return -1;
149 int mt9v111_setup_autoexposure(struct usb_microdia *dev)
151 __u16 buf[1];
152 int ret = 0;
153 int ret2 = 0;
155 // Check if sensor is MT9V111:
156 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
157 return -ENODEV;
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);
200 if (ret2 == 0)
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.");
213 return -1;
215 return 0;
219 int mt9v111_setup_autowhitebalance(struct usb_microdia *dev)
221 __u16 buf[1];
222 int ret = 0;
224 // Check if sensor is MT9V111:
225 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
226 return -ENODEV;
227 // Switch to IFP-register address space:
228 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
229 if (ret < 0) return -11; // -EAGAIN
230 // Set AWB tint:
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);
249 if (ret < 0)
251 UDIA_INFO("One or more errors occured during setup of AWB registers.");
252 return -1;
254 return 0;
258 int mt9vx11_set_exposure(struct usb_microdia *dev)
260 int ret = 0;
261 __u8 buf[2];
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);
269 buf[1] = 0;
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
273 is enabled. */
275 return ret;
279 int mt9vx11_set_hvflip(struct usb_microdia *dev)
281 int ret = 0;
282 __u8 buf[2];
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
299 else
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
309 else
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);
316 return ret;
320 int mt9v111_set_autoexposure(struct usb_microdia *dev)
322 __u16 buf[1];
323 int ret = 0;
325 // Check if sensor is MT9V111:
326 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
327 return -ENODEV;
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);
334 if (ret < 0)
336 UDIA_ERROR("Error: setting of auto exposure failed: error while reading from IFP-register 0x06");
337 return ret;
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;
350 else
351 return -EINVAL;
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);
355 if (ret < 0)
357 UDIA_ERROR("Error: setting of auto exposure failed: error while writing to IFP-register 0x06");
358 return ret;
360 return 0;
364 int mt9v111_set_autowhitebalance(struct usb_microdia *dev)
366 __u16 buf[1];
367 int ret = 0;
369 // Check if sensor is MT9V111:
370 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
371 return -ENODEV;
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);
378 if (ret < 0)
380 UDIA_ERROR("Error: setting of auto whitebalance failed: error while reading from IFP-register 0x06");
381 return ret;
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;
394 else
395 return -EINVAL;
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);
399 if (ret < 0)
401 UDIA_ERROR("Error: setting of auto whitebalance failed: error while writing to IFP-register 0x06");
402 return ret;
404 return 0;
408 int mt9v111_set_autocorrections(struct usb_microdia *dev, int enable)
410 __u16 buf[1];
411 int ret = 0;
413 // Check if sensor is MT9V111:
414 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
415 return -ENODEV;
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);
422 if (ret < 0)
424 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while reading from IFP-register 0x06");
425 return ret;
427 // Set new value for register 0x06:
428 if (enable == 1)
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);
436 else
437 return -EINVAL;
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);
441 if (ret < 0)
443 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while writing to IFP-register 0x06");
444 return ret;
446 return 0;