V4l2 controls now have a max range of 255.
[microdia.git] / sn9c20x-dev.c
blobfbb472d3dc5c3457c122836ba1d939d7363995bb
1 /**
2 * @file sn9c20x-dev.c
3 * @author Nicolas VIVIEN
4 * @date 2008-02-01
6 * @brief Device specific functions
8 * @note Copyright (C) Nicolas VIVIEN
10 * @par Licences
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
15 * any later version.
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/errno.h>
28 #include <linux/string.h>
29 #include <stdarg.h>
31 #include "sn9c20x.h"
32 #include "sn9c20x-bridge.h"
33 #include "omnivision.h"
34 #include "micron.h"
36 static struct sensor_info sensors[] = {
38 .id = OV9650_SENSOR,
39 .name = "OV9650",
40 .address = 0x30,
41 .initialize = ov_initialize,
42 .hstart = 0,
43 .vstart = 7,
44 .set_hvflip = ov965x_set_hvflip,
45 .set_exposure = ov965x_set_exposure,
46 .set_auto_gain = ov_set_autogain,
47 .flip_detect = ov965x_flip_detect,
48 .get_yavg = ov965x_get_yavg,
49 .min_yavg = 30,
50 .max_yavg = 60,
51 .min_stable_yavg = 32,
52 .max_stable_yavg = 58,
55 .id = OV9655_SENSOR,
56 .name = "OV9655",
57 .address = 0x30,
58 .initialize = ov_initialize,
59 .set_auto_exposure = ov_set_autoexposure,
60 .set_auto_gain = ov_set_autogain,
61 .hstart = 0,
62 .vstart = 7,
65 .id = SOI968_SENSOR,
66 .name = "SOI968",
67 .address = 0x30,
68 .initialize = ov_initialize,
69 .set_exposure = soi968_set_exposure,
70 .set_auto_exposure = ov_set_autoexposure,
71 .set_auto_gain = ov_set_autogain,
72 .set_gain = soi968_set_gain,
73 .button_detect = soi968_button_detect,
74 .hstart = 60,
75 .vstart = 11,
78 .id = OV7660_SENSOR,
79 .name = "OV7660",
80 .address = 0x21,
81 .initialize = ov_initialize,
82 .set_exposure = ov7660_set_exposure,
83 .set_auto_gain = ov_set_autogain,
84 .hstart = 1,
85 .vstart = 1,
86 .get_yavg = ov965x_get_yavg,
87 .min_yavg = 30,
88 .max_yavg = 60,
89 .min_stable_yavg = 32,
90 .max_stable_yavg = 58,
93 .id = OV7670_SENSOR,
94 .name = "OV7670",
95 .address = 0x21,
96 .initialize = ov_initialize,
97 .set_auto_exposure = ov_set_autoexposure,
98 .set_exposure = ov7660_set_exposure,
99 .set_auto_gain = ov_set_autogain,
100 .flip_detect = ov7670_flip_detect,
101 .hstart = 0,
102 .vstart = 1,
105 .id = MT9V111_SENSOR,
106 .name = "MT9V111",
107 .address = 0x5c,
108 .initialize = micron_initialize,
109 .set_hvflip = mt9v111_set_hvflip,
110 .set_exposure = mt9v111_set_exposure,
111 .set_auto_exposure = mt9v111_set_autoexposure,
112 .hstart = 2,
113 .vstart = 2,
116 .id = MT9M111_SENSOR,
117 .name = "MT9M111",
118 .address = 0x5d,
119 .initialize = micron_initialize,
120 .set_yuv422 = mt9m111_set_yuv422,
121 .set_bayer = mt9m111_set_raw,
122 .hstart = 6,
123 .vstart = 2,
126 .id = MT9V011_SENSOR,
127 .name = "MT9V011",
128 .address = 0x5d,
129 .initialize = micron_initialize,
130 .set_hvflip = mt9v011_set_hvflip,
131 .set_exposure = mt9v011_set_exposure,
132 .hstart = 2,
133 .vstart = 2,
136 .id = MT9M001_SENSOR,
137 .name = "MT9M001",
138 .address = 0x5d,
139 .initialize = micron_initialize,
140 .hstart = 2,
141 .vstart = 2,
144 .id = HV7131R_SENSOR,
145 .name = "HV7131R",
146 .address = 0x11,
147 .initialize = hv7131r_initialize,
148 .set_gain = hv7131r_set_gain,
149 .set_exposure = hv7131r_set_exposure,
150 .set_hvflip = hv7131r_set_hvflip,
151 .hstart = 0,
152 .vstart = 1,
156 static __u16 known_cams[][2] = {
157 {0x6240, MT9M001_SENSOR},
158 {0x6242, MT9M111_SENSOR},
159 {0x6248, OV9655_SENSOR},
160 {0x624e, SOI968_SENSOR},
161 {0x624f, OV9650_SENSOR},
162 {0x6251, OV9650_SENSOR},
163 {0x6253, OV9650_SENSOR},
164 {0x6260, OV7670_SENSOR},
165 {0x627b, OV7660_SENSOR},
166 {0x627c, HV7131R_SENSOR},
167 {0x627f, OV9650_SENSOR},
168 {0x6280, MT9M001_SENSOR},
169 {0x6282, MT9M111_SENSOR},
170 {0x6288, OV9655_SENSOR},
171 {0x628e, SOI968_SENSOR},
172 {0x628f, OV9650_SENSOR},
173 {0x62a0, OV7670_SENSOR},
174 {0x62b3, OV9655_SENSOR},
175 {0x62bb, OV7660_SENSOR},
176 {0x62bc, HV7131R_SENSOR},
177 /* The following PIDs have VIDs different from 0x0c45. */
178 {0x00f4, OV9650_SENSOR},
179 {0x013d, OV7660_SENSOR},
182 int dev_sn9c20x_assign_sensor(struct usb_sn9c20x *dev)
184 int i, j, ret;
185 __u16 pid = le16_to_cpu(dev->udev->descriptor.idProduct);
187 for (i = 0; i < ARRAY_SIZE(known_cams); i++) {
188 if (pid == known_cams[i][0]) {
189 for (j = 0; j < ARRAY_SIZE(sensors); j++) {
190 if (known_cams[i][1] == sensors[j].id) {
191 dev->camera.sensor = &(sensors[j]);
192 return 0;
198 if ((pid == 0x6270) || (pid == 0x62b0)) {
199 ret = micron_probe(dev);
200 for (j = 0; j < ARRAY_SIZE(sensors); j++) {
201 if (ret == sensors[j].id) {
202 dev->camera.sensor = &(sensors[j]);
203 return 0;
208 UDIA_ERROR("Could not assign sensor: "
209 "Please report to microdia@googlegroups.com .\n");
211 return -EINVAL;
214 int dev_sn9c20x_initialize_sensor(struct usb_sn9c20x *dev)
216 int ret = -ENODEV;
217 if (dev && dev->camera.sensor->initialize)
218 ret = dev->camera.sensor->initialize(dev);
219 return ret;
222 static struct sn9c20x_video_format default_fmts[] = {
224 .pix_fmt = V4L2_PIX_FMT_SBGGR8,
225 .desc = "Bayer 8bit (BGGR)",
226 .depth = 8,
227 .modes = {
228 {160, 120, SN9C20X_1_4_SCALE,
229 {0, 0, 640, 480},
230 {0, 0, 160, 120} },
231 {320, 240, SN9C20X_1_2_SCALE,
232 {0, 0, 640, 480},
233 {0, 0, 320, 240} },
234 {640, 480, SN9C20X_NO_SCALE,
235 {0, 0, 640, 480},
236 {0, 0, 640, 480} },
238 .set_format = sn9c20x_set_raw
241 .pix_fmt = V4L2_PIX_FMT_YUYV,
242 .desc = "YUV 4:2:2 (YUYV)",
243 .depth = 16,
244 .modes = {
245 {160, 120, SN9C20X_1_4_SCALE,
246 {0, 0, 1280, 480},
247 {0, 0, 320, 120} },
248 {320, 240, SN9C20X_1_2_SCALE,
249 {0, 0, 1280, 480},
250 {0, 0, 640, 240} },
251 {640, 480, SN9C20X_NO_SCALE,
252 {0, 0, 1280, 480},
253 {0, 0, 1280, 480} },
255 .set_format = NULL
258 .pix_fmt = V4L2_PIX_FMT_JPEG,
259 .desc = "JPEG (YUV 4:2:2)",
260 .depth = 16,
261 .modes = {
262 {160, 120, SN9C20X_1_4_SCALE,
263 {0, 0, 640, 480},
264 {0, 0, 160, 120} },
265 {320, 240, SN9C20X_1_2_SCALE,
266 {0, 0, 640, 480},
267 {0, 0, 320, 240} },
268 {640, 480, SN9C20X_NO_SCALE,
269 {0, 0, 640, 480},
270 {0, 0, 640, 480} },
272 .set_format = sn9c20x_set_jpeg
276 int dev_sn9c20x_assign_fmts(struct usb_sn9c20x *dev)
278 int i, j;
280 dev->camera.fmts = default_fmts;
281 dev->camera.nfmts = ARRAY_SIZE(default_fmts);
283 for (i = 0; i < ARRAY_SIZE(default_fmts); i++) {
284 if ((dev->camera.sensor->set_yuv422 != NULL) &&
285 (dev->camera.fmts[i].pix_fmt == V4L2_PIX_FMT_YUYV))
286 dev->camera.fmts[i].set_format =
287 dev->camera.sensor->set_yuv422;
288 if ((dev->camera.sensor->set_bayer != NULL) &&
289 (dev->camera.fmts[i].pix_fmt == V4L2_PIX_FMT_SBGGR8))
290 dev->camera.fmts[i].set_format =
291 dev->camera.sensor->set_bayer;
292 for (j = 0; j < N_MODES; j++) {
293 dev->camera.fmts[i].modes[j].hw_window[0] =
294 dev->camera.sensor->hstart;
295 dev->camera.fmts[i].modes[j].hw_window[1] =
296 dev->camera.sensor->vstart;
300 return 0;
304 * @brief Wrapper function to detect hardware states
306 * @param dev Pointer to device structure
308 * @returns 0
310 int dev_sn9c20x_call_constantly(struct usb_sn9c20x *dev)
313 /* Know to be broken, temporarely disabled */
314 /*dev_sn9c20x_flip_detection(dev);*/
315 dev_sn9c20x_button_detection(dev);
316 if (!dev->camera.sensor->set_auto_exposure &&
317 dev->camera.sensor->get_yavg &&
318 dev->vsettings.auto_exposure == V4L2_EXPOSURE_AUTO) {
319 dev_sn9c20x_perform_soft_ae(dev);
322 return 0;
326 * @brief Wrapper function to detect a flipped sensor
328 * @param dev Pointer to device structure
330 * @returns 0 or negative error value
333 int dev_sn9c20x_flip_detection(struct usb_sn9c20x *dev)
335 int ret = -ENODEV;
336 if (dev && dev->camera.sensor->flip_detect)
337 ret = dev->camera.sensor->flip_detect(dev);
338 return ret;
342 * @brief Wrapper function to detect a pushed button
344 * @param dev Pointer to device structure
346 * @returns 0 or negative error value
349 int dev_sn9c20x_button_detection(struct usb_sn9c20x *dev)
351 int ret = -ENODEV;
352 if (dev && dev->camera.sensor->button_detect)
353 ret = dev->camera.sensor->button_detect(dev);
354 return ret;
358 * @brief Wrapper function for device-specific initialization functions
360 * @param dev Pointer to device structure
361 * @param flags
363 * @returns 0 or negative error value
366 int dev_sn9c20x_initialize_device(struct usb_sn9c20x *dev, __u32 flags)
368 switch (flags >> 16) {
369 case (SN9C20X_BRIDGE >> 16):
370 UDIA_INFO("Detected SN9C20X Bridge\n");
371 return sn9c20x_initialize(dev);
372 default:
373 UDIA_ERROR("Unsupported bridge\n");
375 return -ENODEV;
379 * @brief Wrapper function for for enable video stream for specific bridge
381 * @param dev Pointer to device structure
382 * @param enable Contains the wanted state
384 * @returns 0
387 int dev_sn9c20x_enable_video(struct usb_sn9c20x *dev, int enable)
389 int ret = -ENODEV;
390 if (dev && dev->camera.enable_video)
391 ret = dev->camera.enable_video(dev, enable);
393 return ret;
398 * @brief Perform software autoexposure
400 * @param dev
402 * @returns 0 or negative error value
404 * @author Vasily Khoruzhick
406 int dev_sn9c20x_perform_soft_ae(struct usb_sn9c20x *dev)
408 int yavg;
409 static int old_yavg = -1, old_exp = -1;
410 int koef = 100, new_exp, i;
411 yavg = -1;
412 i = 10;
414 if (!dev->camera.sensor->set_exposure)
415 return -1;
416 if (!dev->camera.sensor->get_yavg)
417 return -1;
419 /* Trying to get average value of Y */
420 while ((yavg == -1) && (i--))
421 yavg = dev->camera.sensor->get_yavg(dev);
423 if (yavg == -1) {
424 /* Can't get YAVG - we have nothing to do */
425 return -1;
428 /* Image is too dark */
429 if (yavg < dev->camera.sensor->min_yavg) {
430 /* yavg can't be 0 - in that way new_exp == infinity */
431 if (yavg == 0)
432 yavg = 1;
433 if (old_yavg != -1) {
434 /* Previous correction was made to make image darker,
435 * but we made it too dark. Calculating our error
436 * and taking it into account.
437 * Usually error is 0.7 - 2, so we multiply it by 100
439 if (old_yavg > dev->camera.sensor->max_yavg)
440 koef = 100 * dev->camera.sensor->
441 min_stable_yavg / yavg;
442 else
443 koef = 100;
446 /* Calculating new exposure value. We assuming that
447 * exposure linearly depends on YAVG, but we taking
448 * our calculated error into account */
449 new_exp = (dev->camera.sensor->min_stable_yavg
450 * dev->vsettings.exposure * 100) / (yavg * koef);
452 /* Exposure can't be more than 0xff or less than 0x1 */
453 if (new_exp > 0xff)
454 new_exp = 0xff;
455 if (new_exp < 0x1)
456 new_exp = 1;
458 old_exp = dev->vsettings.exposure;
459 old_yavg = yavg;
461 /* Applying new exposure */
462 dev->vsettings.exposure = new_exp;
463 dev->camera.sensor->set_exposure(dev);
466 /* Image is too bright */
467 else if (yavg > dev->camera.sensor->max_yavg) {
468 /* yavg can't be 0 - in that way new_exp == infinity */
469 if (yavg == 0)
470 yavg = 1;
471 if (old_yavg != -1) {
473 /* Previous correction was made to make image brighter,
474 * but we made it too bright. Calculating our error
475 * and taking it into account.
476 * Usually error is 0.7 - 2, so we multiply it by 100
478 if (old_yavg < dev->camera.sensor->min_yavg)
479 koef = 100 * yavg / dev->camera.sensor
480 ->max_stable_yavg;
481 else
482 koef = 100;
485 /* Calculating new exposure value. We assuming that
486 * exposure linearly depends on YAVG, but we taking
487 * our calculated error into account */
488 new_exp = (koef * dev->camera.sensor->max_stable_yavg
489 * dev->vsettings.exposure) / (yavg * 100);
491 /* Exposure can't be more than 0xff or less than 0x1 */
492 if (new_exp > 0xff)
493 new_exp = 0xff;
494 if (new_exp < 0x1)
495 new_exp = 1;
497 old_exp = dev->vsettings.exposure;
498 old_yavg = yavg;
500 /* Applying new exposure */
501 dev->vsettings.exposure = new_exp;
502 dev->camera.sensor->set_exposure(dev);
505 return 0;