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 int (*sn_probes
[])(struct usb_sn9c20x
*dev
) = {
42 struct sn9c20x_video_mode sn9c20x_modes
[SN9C20X_N_MODES
] = {
46 .scale
= SN9C20X_1_4_SCALE
,
47 .window
= {65, 48, 1024, 768}
52 .scale
= SN9C20X_1_4_SCALE
,
53 .window
= {0, 0, 640, 480}
58 .scale
= SN9C20X_1_2_SCALE
,
59 .window
= {145, 96, 704, 576}
64 .scale
= SN9C20X_1_2_SCALE
,
65 .window
= {0, 0, 640, 480}
70 .scale
= SN9C20X_NO_SCALE
,
71 .window
= {145, 96, 704, 576}
76 .scale
= SN9C20X_NO_SCALE
,
77 .window
= {0, 0, 640, 480}
81 struct sn9c20x_video_format sn9c20x_fmts
[SN9C20X_N_FMTS
] = {
83 .pix_fmt
= V4L2_PIX_FMT_SN9C20X_I420
,
84 .desc
= "SN9C20X I420 (YUV 4:2:0)",
86 .set_format
= sn9c20x_set_yuv420
,
89 .pix_fmt
= V4L2_PIX_FMT_SBGGR8
,
90 .desc
= "Bayer 8bit (BGGR)",
92 .set_format
= sn9c20x_set_raw
,
96 .pix_fmt = V4L2_PIX_FMT_YUYV,
97 .desc = "YUYV (YUV 4:2:0)",
99 .set_format = sn9c20x_set_yuv422,
103 .pix_fmt
= V4L2_PIX_FMT_JPEG
,
104 .desc
= "JPEG (YUV 4:2:2)",
106 .set_format
= sn9c20x_set_jpeg
,
110 int sn9c20x_initialize_sensor(struct usb_sn9c20x
*dev
)
115 dev
->camera
.min_yavg
= 80;
116 dev
->camera
.max_yavg
= 130;
117 dev
->camera
.old_step
= 0;
118 dev
->camera
.older_step
= 0;
119 dev
->camera
.exposure_step
= 16;
121 /* Probe sensor first if sensor set to probe*/
122 if (dev
->camera
.sensor
== PROBE_SENSOR
) {
123 for (i
= 0; i
< ARRAY_SIZE(sn_probes
); i
++) {
124 ret
= sn_probes
[i
](dev
);
125 dev
->camera
.sensor
= ret
;
131 switch (dev
->camera
.sensor
) {
133 sn9c20x_write_i2c_array(dev
, soi968_init
, 0);
134 dev
->camera
.set_exposure
= soi968_set_exposure
;
135 dev
->camera
.set_auto_exposure
= soi968_set_autoexposure
;
136 dev
->camera
.set_gain
= soi968_set_gain
;
137 dev
->camera
.set_auto_gain
= ov_set_autogain
;
138 dev
->camera
.set_auto_whitebalance
= soi968_set_autowhitebalance
;
139 dev
->camera
.hstart
= 60;
140 dev
->camera
.vstart
= 11;
141 UDIA_INFO("Detected SOI968 Sensor.\n");
144 sn9c20x_write_i2c_array(dev
, ov9650_init
, 0);
145 dev
->camera
.hstart
= 1;
146 dev
->camera
.vstart
= 7;
147 dev
->camera
.set_hvflip
= ov965x_set_hvflip
;
148 dev
->camera
.set_exposure
= ov_set_exposure
;
149 dev
->camera
.set_auto_gain
= ov_set_autogain
;
150 dev
->camera
.set_gain
= ov9650_set_gain
;
151 dev
->camera
.flip_detect
= ov965x_flip_detect
;
152 UDIA_INFO("Detected OV9650 Sensor.\n");
155 sn9c20x_write_i2c_array(dev
, ov9655_init
, 0);
156 dev
->camera
.set_exposure
= ov_set_exposure
;
157 dev
->camera
.set_auto_gain
= ov_set_autogain
;
158 dev
->camera
.hstart
= 0;
159 dev
->camera
.vstart
= 7;
160 UDIA_INFO("Detected OV9655 Sensor.\n");
163 sn9c20x_write_i2c_array(dev
, ov7670_init
, 0);
164 dev
->camera
.set_exposure
= ov_set_exposure
;
165 dev
->camera
.set_auto_gain
= ov_set_autogain
;
166 dev
->camera
.flip_detect
= ov7670_flip_detect
;
167 dev
->camera
.hstart
= 0;
168 dev
->camera
.vstart
= 1;
169 UDIA_INFO("Detected OV7670 Sensor.\n");
172 sn9c20x_write_i2c_array(dev
, ov7660_init
, 0);
173 dev
->camera
.set_exposure
= ov_set_exposure
;
174 dev
->camera
.set_auto_gain
= ov_set_autogain
;
175 dev
->camera
.set_gain
= ov9650_set_gain
;
176 dev
->camera
.hstart
= 1;
177 dev
->camera
.vstart
= 1;
178 UDIA_INFO("Detected OV7660 Sensor.\n");
181 sn9c20x_write_i2c_array(dev
, mt9v111_init
, 1);
182 dev
->camera
.set_hvflip
= mt9v111_set_hvflip
;
183 dev
->camera
.set_exposure
= mt9v111_set_exposure
;
184 dev
->camera
.set_auto_exposure
= mt9v111_set_autoexposure
;
185 dev
->camera
.set_auto_whitebalance
= mt9v111_set_autowhitebalance
;
186 dev
->camera
.hstart
= 2;
187 dev
->camera
.vstart
= 2;
188 UDIA_INFO("Detected MT9V111 Sensor.\n");
191 sn9c20x_write_i2c_array(dev
, mt9v112_init
, 1);
192 dev
->camera
.set_hvflip
= mt9v112_set_hvflip
;
193 dev
->camera
.hstart
= 6;
194 dev
->camera
.vstart
= 2;
195 UDIA_INFO("Detected MT9V112 Sensor.\n");
198 sn9c20x_write_i2c_array(dev
, mt9m111_init
, 1);
199 dev
->camera
.set_exposure
= mt9m111_set_exposure
;
200 dev
->camera
.set_auto_exposure
= mt9m111_set_autoexposure
;
201 dev
->camera
.set_auto_whitebalance
= mt9m111_set_autowhitebalance
;
202 dev
->camera
.hstart
= 0;
203 dev
->camera
.vstart
= 2;
204 UDIA_INFO("Detected MT9M111 Sensor.\n");
207 sn9c20x_write_i2c_array(dev
, mt9v011_init
, 1);
208 dev
->camera
.set_hvflip
= mt9v011_set_hvflip
;
209 dev
->camera
.set_exposure
= mt9v011_set_exposure
;
210 dev
->camera
.hstart
= 2;
211 dev
->camera
.vstart
= 2;
212 UDIA_INFO("Detected MT9V011 Sensor.\n");
215 sn9c20x_write_i2c_array(dev
, mt9m001_init
, 1);
216 dev
->camera
.hstart
= 2;
217 dev
->camera
.vstart
= 2;
218 UDIA_INFO("Detected MT9M001 Sensor.\n");
221 dev
->camera
.i2c_flags
|= SN9C20X_I2C_400KHZ
;
222 sn9c20x_write_i2c_array(dev
, hv7131r_init
, 0);
223 dev
->camera
.set_hvflip
= hv7131r_set_hvflip
;
224 dev
->camera
.set_gain
= hv7131r_set_gain
;
225 dev
->camera
.set_exposure
= hv7131r_set_exposure
;
226 dev
->camera
.hstart
= 0;
227 dev
->camera
.vstart
= 1;
228 UDIA_INFO("Detected HV7131R Sensor.\n");
232 UDIA_INFO("Unsupported sensor.\n");
239 * @brief Wrapper function to detect hardware states
241 * @param dev Pointer to device structure
245 int dev_sn9c20x_call_constantly(struct usb_sn9c20x
*dev
)
248 /* Know to be broken, temporarely disabled */
249 /*dev_sn9c20x_flip_detection(dev);*/
250 if (!dev
->camera
.set_auto_exposure
&&
251 dev
->vsettings
.auto_exposure
) {
252 dev_sn9c20x_perform_soft_ae(dev
);
259 * @brief Wrapper function to detect a flipped sensor
261 * @param dev Pointer to device structure
263 * @returns 0 or negative error value
266 int dev_sn9c20x_flip_detection(struct usb_sn9c20x
*dev
)
269 if (dev
&& dev
->camera
.flip_detect
)
270 ret
= dev
->camera
.flip_detect(dev
);
275 * @brief Perform software autoexposure
279 * @returns 0 or negative error value
281 * @author Stefan Krastanov
283 int dev_sn9c20x_perform_soft_ae(struct usb_sn9c20x
*dev
)
288 if (!dev
->camera
.set_exposure
)
290 yavg
= atomic_read(&dev
->camera
.yavg
);
292 UDIA_DEBUG("Sensor YAVG: %d\n", yavg
);
294 /* Can't get YAVG - we have nothing to do */
298 /*some hardcoded values are present
299 *like those for maximal/minimal exposure
300 *and exposure steps*/
303 if (yavg
< dev
->camera
.min_yavg
) {
304 /*worst case - exposure is allready maximal*/
305 if (dev
->vsettings
.exposure
> 0xf5)
307 /*change exposure just a bit*/
308 new_exp
= dev
->vsettings
.exposure
+ dev
->camera
.exposure_step
;
314 dev
->vsettings
.exposure
= new_exp
;
315 dev
->camera
.set_exposure(dev
);
316 /*note the direction of the change*/
317 dev
->camera
.older_step
= dev
->camera
.old_step
;
318 dev
->camera
.old_step
= 1; /*it's going up*/
320 /*check if it's oscillating and take smaller step if it is*/
321 if (dev
->camera
.old_step
^ dev
->camera
.older_step
)
322 dev
->camera
.exposure_step
/= 2;
323 /* oscilating - divide the step by 2 */
325 dev
->camera
.exposure_step
+= 2;
326 /* same direction - step must be bigger */
329 if (yavg
> dev
->camera
.max_yavg
) {
330 /*worst case - exposure is allready minimal*/
331 if (dev
->vsettings
.exposure
< 0x10)
333 /*change exposure just a bit*/
334 new_exp
= dev
->vsettings
.exposure
- dev
->camera
.exposure_step
;
340 dev
->vsettings
.exposure
= new_exp
;
341 dev
->camera
.set_exposure(dev
);
342 /*note the direction of the change*/
343 dev
->camera
.older_step
= dev
->camera
.old_step
;
344 dev
->camera
.old_step
= 0; /*it's going down*/
346 /*check if it's oscillating and take smaller step if it is*/
347 if (dev
->camera
.old_step
^ dev
->camera
.older_step
)
348 dev
->camera
.exposure_step
/= 2;
349 /* oscilating - divide the step by 2 */
351 dev
->camera
.exposure_step
+= 2;
352 /* same direction - step must be bigger */