Fixed "\n" errors in UDIA-messages.
[microdia.git] / mt9vx11.c
blobcdcd3813e071fa600565f5e1929df0994af5143a
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)) {
43 UDIA_INFO("Error: invalid register address space selection for sensor MT9V111/MI0360SOC !\n");
44 return -1;
46 /* read address space slection register: */
47 k = 0;
48 retI2C = -1;
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)
52 udelay(1000);
53 k++;
55 if (retI2C != 0) {
56 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): read of reg0x01 (address space selection) failed !\n");
57 return -1;
59 /* check current address space: */
60 if ((buf[0] != 0x00) || (buf[1] != address_space)) {
61 k = 0;
62 retI2C = -1;
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)
68 udelay(1000);
69 k++;
71 if (retI2C != 0) {
72 if (address_space == MT9V111_ADDRESSSPACE_IFP)
73 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to IFP address space failed !\n");
74 else
75 UDIA_INFO("Error: MT9V111/MI0360SOC (I2C-slave 0x5c): switch to sensor core address space failed !\n");
76 return -1;
79 return 0;
83 int mt9vx11_sensor_probe(struct usb_microdia *dev)
85 __u8 buf[2];
86 int ret;
87 int retI2C;
88 int k;
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);
94 if (retI2C == 0) {
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;
98 return 0;
99 } else
100 UDIA_DEBUG("I2C-slave 0x5d returned invalid chip version: 0x%02X%02X\n", buf[0], buf[1]);
101 } else
102 udelay(500);
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);
112 if (ret != 0)
113 return -1;
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);
117 if (retI2C == 0) {
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;
121 return 0;
122 } else
123 UDIA_DEBUG("I2C-slave 0x5c returned invalid chip version: 0x%02X%02X\n", buf[0], buf[1]);
124 } else
125 udelay(500);
128 /* FIXME: always switch back to last register address space */
130 UDIA_INFO("Error: sensor probe failed !\n");
131 dev->sensor_slave_address = 0;
132 return -1;
136 int mt9v111_setup_autoexposure(struct usb_microdia *dev)
138 __u16 buf[1];
139 int ret = 0;
140 int ret2 = 0;
142 /* Check if sensor is MT9V111: */
143 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
144 return -ENODEV;
145 /* Switch to IFP-register address space: */
146 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
147 if (ret < 0)
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);
188 if (ret2 == 0) {
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");
199 return -1;
201 return 0;
205 int mt9v111_setup_autowhitebalance(struct usb_microdia *dev)
207 __u16 buf[1];
208 int ret = 0;
210 /* Check if sensor is MT9V111: */
211 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
212 return -ENODEV;
213 /* Switch to IFP-register address space: */
214 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
215 if (ret < 0)
216 return -11; /* -EAGAIN */
217 /* Set AWB tint: */
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);
236 if (ret < 0) {
237 UDIA_INFO("One or more errors occured during setup of AWB registers.\n");
238 return -1;
240 return 0;
244 int mt9vx11_set_exposure(struct usb_microdia *dev)
246 int ret = 0;
247 __u8 buf[2];
249 if (dev->sensor_slave_address == MT9V111_I2C_SLAVE_ADDRESS)
250 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
252 if (ret < 0)
253 return -11; /* -EAGAIN */
255 buf[0] = (dev->vsettings.exposure >> 12);
256 buf[1] = 0;
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
260 is enabled. */
262 return ret;
266 int mt9vx11_set_hvflip(struct usb_microdia *dev)
268 int ret = 0;
269 __u8 buf[2];
271 if ((dev->vsettings.hflip > 1) || (dev->vsettings.hflip < 0))
272 return -EINVAL;
273 if ((dev->vsettings.vflip > 1) || (dev->vsettings.vflip < 0))
274 return -EINVAL;
276 if (dev->sensor_slave_address == MT9V111_I2C_SLAVE_ADDRESS)
277 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_SENSOR);
278 if (ret < 0)
279 return -11; /* -EAGAIN */
281 ret = sn9c20x_read_i2c_data(dev, dev->sensor_slave_address, 2, 0x20, dev->sensor_flags, buf);
282 if (ret < 0)
283 return ret;
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 */
288 } else {
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 */
295 } else {
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);
301 return ret;
305 int mt9v111_set_autoexposure(struct usb_microdia *dev)
307 __u16 buf[1];
308 int ret = 0;
310 /* Check if sensor is MT9V111: */
311 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
312 return -ENODEV;
313 /* Switch to IFP-register address space: */
314 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
315 if (ret < 0)
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);
320 if (ret < 0) {
321 UDIA_ERROR("Error: setting of auto exposure failed: error while reading from IFP-register 0x06\n");
322 return ret;
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;
331 } else
332 return -EINVAL;
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);
336 if (ret < 0) {
337 UDIA_ERROR("Error: setting of auto exposure failed: error while writing to IFP-register 0x06\n");
338 return ret;
340 return 0;
344 int mt9v111_set_autowhitebalance(struct usb_microdia *dev)
346 __u16 buf[1];
347 int ret = 0;
349 /* Check if sensor is MT9V111: */
350 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
351 return -ENODEV;
352 /* Switch to IFP-register address space: */
353 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
354 if (ret < 0)
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);
359 if (ret < 0) {
360 UDIA_ERROR("Error: setting of auto whitebalance failed: error while reading from IFP-register 0x06\n");
361 return ret;
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;
370 } else
371 return -EINVAL;
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);
375 if (ret < 0) {
376 UDIA_ERROR("Error: setting of auto whitebalance failed: error while writing to IFP-register 0x06\n");
377 return ret;
379 return 0;
383 int mt9v111_set_autocorrections(struct usb_microdia *dev, int enable)
385 __u16 buf[1];
386 int ret = 0;
388 /* Check if sensor is MT9V111: */
389 if (dev->sensor_slave_address != MT9V111_I2C_SLAVE_ADDRESS)
390 return -ENODEV;
391 /* Switch to IFP-register address space: */
392 ret = mt9v111_select_address_space(dev, MT9V111_ADDRESSSPACE_IFP);
393 if (ret < 0)
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);
398 if (ret < 0) {
399 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while reading from IFP-register 0x06\n");
400 return ret;
402 /* Set new value for register 0x06: */
403 if (enable == 1)
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);
407 else
408 return -EINVAL;
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);
412 if (ret < 0) {
413 UDIA_ERROR("Error: setting of sensor auto-correction functions failed: error while writing to IFP-register 0x06\n");
414 return ret;
416 return 0;