3 * @author Nicolas VIVIEN
6 * @brief Device specific functions
8 * @note Copyright (C) Nicolas VIVIEN
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/errno.h>
28 #include <linux/string.h>
32 #include "sn9c20x-bridge.h"
33 #include "omnivision.h"
36 static struct sensor_info sensors
[] = {
41 .initialize
= ov_initialize
,
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
,
51 .min_stable_yavg
= 32,
52 .max_stable_yavg
= 58,
58 .initialize
= ov_initialize
,
59 .set_auto_exposure
= ov_set_autoexposure
,
60 .set_auto_gain
= ov_set_autogain
,
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
,
81 .initialize
= ov_initialize
,
82 .set_exposure
= ov7660_set_exposure
,
83 .set_auto_gain
= ov_set_autogain
,
86 .get_yavg
= ov965x_get_yavg
,
89 .min_stable_yavg
= 32,
90 .max_stable_yavg
= 58,
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
,
105 .id
= MT9V111_SENSOR
,
108 .initialize
= micron_initialize
,
109 .set_hvflip
= mt9v111_set_hvflip
,
110 .set_exposure
= mt9v111_set_exposure
,
111 .set_auto_exposure
= mt9v111_set_autoexposure
,
116 .id
= MT9M111_SENSOR
,
119 .initialize
= micron_initialize
,
120 .set_yuv422
= mt9m111_set_yuv422
,
121 .set_bayer
= mt9m111_set_raw
,
126 .id
= MT9V011_SENSOR
,
129 .initialize
= micron_initialize
,
130 .set_hvflip
= mt9v011_set_hvflip
,
131 .set_exposure
= mt9v011_set_exposure
,
136 .id
= MT9M001_SENSOR
,
139 .initialize
= micron_initialize
,
144 .id
= HV7131R_SENSOR
,
147 .initialize
= hv7131r_initialize
,
148 .set_gain
= hv7131r_set_gain
,
149 .set_exposure
= hv7131r_set_exposure
,
150 .set_hvflip
= hv7131r_set_hvflip
,
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
)
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
]);
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
]);
208 UDIA_ERROR("Could not assign sensor: "
209 "Please report to microdia@googlegroups.com .\n");
214 int dev_sn9c20x_initialize_sensor(struct usb_sn9c20x
*dev
)
217 if (dev
&& dev
->camera
.sensor
->initialize
)
218 ret
= dev
->camera
.sensor
->initialize(dev
);
222 static struct sn9c20x_video_format default_fmts
[] = {
224 .pix_fmt
= V4L2_PIX_FMT_SBGGR8
,
225 .desc
= "Bayer 8bit (BGGR)",
228 {160, 120, SN9C20X_1_4_SCALE
,
231 {320, 240, SN9C20X_1_2_SCALE
,
234 {640, 480, SN9C20X_NO_SCALE
,
238 .set_format
= sn9c20x_set_raw
241 .pix_fmt
= V4L2_PIX_FMT_YUYV
,
242 .desc
= "YUV 4:2:2 (YUYV)",
245 {160, 120, SN9C20X_1_4_SCALE
,
248 {320, 240, SN9C20X_1_2_SCALE
,
251 {640, 480, SN9C20X_NO_SCALE
,
258 .pix_fmt
= V4L2_PIX_FMT_JPEG
,
259 .desc
= "JPEG (YUV 4:2:2)",
262 {160, 120, SN9C20X_1_4_SCALE
,
265 {320, 240, SN9C20X_1_2_SCALE
,
268 {640, 480, SN9C20X_NO_SCALE
,
272 .set_format
= sn9c20x_set_jpeg
276 int dev_sn9c20x_assign_fmts(struct usb_sn9c20x
*dev
)
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
;
304 * @brief Wrapper function to detect hardware states
306 * @param dev Pointer to device structure
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
);
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
)
336 if (dev
&& dev
->camera
.sensor
->flip_detect
)
337 ret
= dev
->camera
.sensor
->flip_detect(dev
);
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
)
352 if (dev
&& dev
->camera
.sensor
->button_detect
)
353 ret
= dev
->camera
.sensor
->button_detect(dev
);
358 * @brief Wrapper function for device-specific initialization functions
360 * @param dev Pointer to device structure
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
);
373 UDIA_ERROR("Unsupported bridge\n");
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
387 int dev_sn9c20x_enable_video(struct usb_sn9c20x
*dev
, int enable
)
390 if (dev
&& dev
->camera
.enable_video
)
391 ret
= dev
->camera
.enable_video(dev
, enable
);
398 * @brief Perform software autoexposure
402 * @returns 0 or negative error value
404 * @author Vasily Khoruzhick
406 int dev_sn9c20x_perform_soft_ae(struct usb_sn9c20x
*dev
)
409 static int old_yavg
= -1, old_exp
= -1;
410 int koef
= 100, new_exp
, i
;
414 if (!dev
->camera
.sensor
->set_exposure
)
416 if (!dev
->camera
.sensor
->get_yavg
)
419 /* Trying to get average value of Y */
420 while ((yavg
== -1) && (i
--))
421 yavg
= dev
->camera
.sensor
->get_yavg(dev
);
424 /* Can't get YAVG - we have nothing to do */
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 */
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
;
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 */
458 old_exp
= dev
->vsettings
.exposure
;
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 */
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
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 */
497 old_exp
= dev
->vsettings
.exposure
;
500 /* Applying new exposure */
501 dev
->vsettings
.exposure
= new_exp
;
502 dev
->camera
.sensor
->set_exposure(dev
);