]>
Commit | Line | Data |
---|---|---|
be9904bd OL |
1 | /* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip |
2 | * Subdriver core | |
4f7cb883 | 3 | * |
be9904bd | 4 | * 2009/09/24 Olivier Lorin <o.lorin@laposte.net> |
4f7cb883 OL |
5 | * GSPCA by Jean-Francois Moine <http://moinejf.free.fr> |
6 | * Thanks BUGabundo and Malmostoso for your amazing help! | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
d650fc30 JP |
21 | |
22 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
23 | ||
4f7cb883 OL |
24 | #include "gspca.h" |
25 | #include "gl860.h" | |
26 | ||
216f05aa OL |
27 | MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>"); |
28 | MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); | |
4f7cb883 OL |
29 | MODULE_LICENSE("GPL"); |
30 | ||
31 | /*======================== static function declarations ====================*/ | |
32 | ||
33 | static void (*dev_init_settings)(struct gspca_dev *gspca_dev); | |
34 | ||
35 | static int sd_config(struct gspca_dev *gspca_dev, | |
36 | const struct usb_device_id *id); | |
37 | static int sd_init(struct gspca_dev *gspca_dev); | |
38 | static int sd_isoc_init(struct gspca_dev *gspca_dev); | |
39 | static int sd_start(struct gspca_dev *gspca_dev); | |
40 | static void sd_stop0(struct gspca_dev *gspca_dev); | |
41 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |
76dd272b | 42 | u8 *data, int len); |
4f7cb883 OL |
43 | static void sd_callback(struct gspca_dev *gspca_dev); |
44 | ||
45 | static int gl860_guess_sensor(struct gspca_dev *gspca_dev, | |
83955556 | 46 | u16 vendor_id, u16 product_id); |
4f7cb883 OL |
47 | |
48 | /*============================ driver options ==============================*/ | |
49 | ||
50 | static s32 AC50Hz = 0xff; | |
51 | module_param(AC50Hz, int, 0644); | |
52 | MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); | |
53 | ||
54 | static char sensor[7]; | |
55 | module_param_string(sensor, sensor, sizeof(sensor), 0644); | |
56 | MODULE_PARM_DESC(sensor, | |
216f05aa | 57 | " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); |
4f7cb883 OL |
58 | |
59 | /*============================ webcam controls =============================*/ | |
60 | ||
61 | /* Functions to get and set a control value */ | |
62 | #define SD_SETGET(thename) \ | |
63 | static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ | |
64 | {\ | |
65 | struct sd *sd = (struct sd *) gspca_dev;\ | |
66 | \ | |
67 | sd->vcur.thename = val;\ | |
68 | if (gspca_dev->streaming)\ | |
f980f5d2 | 69 | sd->waitSet = 1;\ |
4f7cb883 OL |
70 | return 0;\ |
71 | } \ | |
72 | static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ | |
73 | {\ | |
74 | struct sd *sd = (struct sd *) gspca_dev;\ | |
75 | \ | |
76 | *val = sd->vcur.thename;\ | |
77 | return 0;\ | |
78 | } | |
79 | ||
80 | SD_SETGET(mirror) | |
81 | SD_SETGET(flip) | |
82 | SD_SETGET(AC50Hz) | |
83 | SD_SETGET(backlight) | |
84 | SD_SETGET(brightness) | |
85 | SD_SETGET(gamma) | |
86 | SD_SETGET(hue) | |
87 | SD_SETGET(saturation) | |
88 | SD_SETGET(sharpness) | |
89 | SD_SETGET(whitebal) | |
90 | SD_SETGET(contrast) | |
91 | ||
92 | #define GL860_NCTRLS 11 | |
93 | ||
94 | /* control table */ | |
95 | static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; | |
96 | static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; | |
4f7cb883 OL |
97 | static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; |
98 | static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; | |
99 | ||
100 | #define SET_MY_CTRL(theid, \ | |
101 | thetype, thelabel, thename) \ | |
102 | if (sd->vmax.thename != 0) {\ | |
103 | sd_ctrls[nCtrls].qctrl.id = theid;\ | |
104 | sd_ctrls[nCtrls].qctrl.type = thetype;\ | |
105 | strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\ | |
106 | sd_ctrls[nCtrls].qctrl.minimum = 0;\ | |
107 | sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\ | |
108 | sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\ | |
109 | sd_ctrls[nCtrls].qctrl.step = \ | |
110 | (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\ | |
111 | sd_ctrls[nCtrls].set = sd_set_##thename;\ | |
112 | sd_ctrls[nCtrls].get = sd_get_##thename;\ | |
113 | nCtrls++;\ | |
114 | } | |
115 | ||
116 | static int gl860_build_control_table(struct gspca_dev *gspca_dev) | |
117 | { | |
118 | struct sd *sd = (struct sd *) gspca_dev; | |
119 | struct ctrl *sd_ctrls; | |
120 | int nCtrls = 0; | |
121 | ||
122 | if (_MI1320_) | |
123 | sd_ctrls = sd_ctrls_mi1320; | |
124 | else if (_MI2020_) | |
125 | sd_ctrls = sd_ctrls_mi2020; | |
4f7cb883 OL |
126 | else if (_OV2640_) |
127 | sd_ctrls = sd_ctrls_ov2640; | |
128 | else if (_OV9655_) | |
129 | sd_ctrls = sd_ctrls_ov9655; | |
0030ec38 MCC |
130 | else |
131 | return 0; | |
4f7cb883 OL |
132 | |
133 | memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl)); | |
134 | ||
135 | SET_MY_CTRL(V4L2_CID_BRIGHTNESS, | |
136 | V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness) | |
137 | SET_MY_CTRL(V4L2_CID_SHARPNESS, | |
138 | V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness) | |
139 | SET_MY_CTRL(V4L2_CID_CONTRAST, | |
140 | V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast) | |
141 | SET_MY_CTRL(V4L2_CID_GAMMA, | |
142 | V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma) | |
143 | SET_MY_CTRL(V4L2_CID_HUE, | |
144 | V4L2_CTRL_TYPE_INTEGER, "Palette", hue) | |
145 | SET_MY_CTRL(V4L2_CID_SATURATION, | |
146 | V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation) | |
147 | SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE, | |
148 | V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal) | |
149 | SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION, | |
150 | V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight) | |
151 | ||
152 | SET_MY_CTRL(V4L2_CID_HFLIP, | |
153 | V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror) | |
154 | SET_MY_CTRL(V4L2_CID_VFLIP, | |
155 | V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) | |
156 | SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, | |
216f05aa | 157 | V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) |
4f7cb883 OL |
158 | |
159 | return nCtrls; | |
160 | } | |
161 | ||
162 | /*==================== sud-driver structure initialisation =================*/ | |
163 | ||
aabcdfb6 | 164 | static const struct sd_desc sd_desc_mi1320 = { |
4f7cb883 OL |
165 | .name = MODULE_NAME, |
166 | .ctrls = sd_ctrls_mi1320, | |
167 | .nctrls = GL860_NCTRLS, | |
168 | .config = sd_config, | |
169 | .init = sd_init, | |
170 | .isoc_init = sd_isoc_init, | |
171 | .start = sd_start, | |
172 | .stop0 = sd_stop0, | |
173 | .pkt_scan = sd_pkt_scan, | |
174 | .dq_callback = sd_callback, | |
175 | }; | |
176 | ||
aabcdfb6 | 177 | static const struct sd_desc sd_desc_mi2020 = { |
4f7cb883 OL |
178 | .name = MODULE_NAME, |
179 | .ctrls = sd_ctrls_mi2020, | |
180 | .nctrls = GL860_NCTRLS, | |
181 | .config = sd_config, | |
182 | .init = sd_init, | |
183 | .isoc_init = sd_isoc_init, | |
184 | .start = sd_start, | |
185 | .stop0 = sd_stop0, | |
186 | .pkt_scan = sd_pkt_scan, | |
187 | .dq_callback = sd_callback, | |
188 | }; | |
189 | ||
aabcdfb6 | 190 | static const struct sd_desc sd_desc_ov2640 = { |
4f7cb883 OL |
191 | .name = MODULE_NAME, |
192 | .ctrls = sd_ctrls_ov2640, | |
193 | .nctrls = GL860_NCTRLS, | |
194 | .config = sd_config, | |
195 | .init = sd_init, | |
196 | .isoc_init = sd_isoc_init, | |
197 | .start = sd_start, | |
198 | .stop0 = sd_stop0, | |
199 | .pkt_scan = sd_pkt_scan, | |
200 | .dq_callback = sd_callback, | |
201 | }; | |
202 | ||
aabcdfb6 | 203 | static const struct sd_desc sd_desc_ov9655 = { |
4f7cb883 OL |
204 | .name = MODULE_NAME, |
205 | .ctrls = sd_ctrls_ov9655, | |
206 | .nctrls = GL860_NCTRLS, | |
207 | .config = sd_config, | |
208 | .init = sd_init, | |
209 | .isoc_init = sd_isoc_init, | |
210 | .start = sd_start, | |
211 | .stop0 = sd_stop0, | |
212 | .pkt_scan = sd_pkt_scan, | |
213 | .dq_callback = sd_callback, | |
214 | }; | |
215 | ||
216 | /*=========================== sub-driver image sizes =======================*/ | |
217 | ||
218 | static struct v4l2_pix_format mi2020_mode[] = { | |
219 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
220 | .bytesperline = 640, | |
221 | .sizeimage = 640 * 480, | |
222 | .colorspace = V4L2_COLORSPACE_SRGB, | |
223 | .priv = 0 | |
224 | }, | |
bff7e839 | 225 | { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, |
4f7cb883 | 226 | .bytesperline = 800, |
bff7e839 | 227 | .sizeimage = 800 * 598, |
4f7cb883 OL |
228 | .colorspace = V4L2_COLORSPACE_SRGB, |
229 | .priv = 1 | |
230 | }, | |
231 | {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
232 | .bytesperline = 1280, | |
233 | .sizeimage = 1280 * 1024, | |
234 | .colorspace = V4L2_COLORSPACE_SRGB, | |
235 | .priv = 2 | |
236 | }, | |
bff7e839 | 237 | {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, |
4f7cb883 | 238 | .bytesperline = 1600, |
bff7e839 | 239 | .sizeimage = 1600 * 1198, |
4f7cb883 OL |
240 | .colorspace = V4L2_COLORSPACE_SRGB, |
241 | .priv = 3 | |
242 | }, | |
243 | }; | |
244 | ||
245 | static struct v4l2_pix_format ov2640_mode[] = { | |
246 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
247 | .bytesperline = 640, | |
248 | .sizeimage = 640 * 480, | |
249 | .colorspace = V4L2_COLORSPACE_SRGB, | |
250 | .priv = 0 | |
251 | }, | |
252 | { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
253 | .bytesperline = 800, | |
254 | .sizeimage = 800 * 600, | |
255 | .colorspace = V4L2_COLORSPACE_SRGB, | |
256 | .priv = 1 | |
257 | }, | |
258 | {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
259 | .bytesperline = 1280, | |
260 | .sizeimage = 1280 * 960, | |
261 | .colorspace = V4L2_COLORSPACE_SRGB, | |
262 | .priv = 2 | |
263 | }, | |
264 | {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
265 | .bytesperline = 1600, | |
266 | .sizeimage = 1600 * 1200, | |
267 | .colorspace = V4L2_COLORSPACE_SRGB, | |
268 | .priv = 3 | |
269 | }, | |
270 | }; | |
271 | ||
272 | static struct v4l2_pix_format mi1320_mode[] = { | |
273 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
274 | .bytesperline = 640, | |
275 | .sizeimage = 640 * 480, | |
276 | .colorspace = V4L2_COLORSPACE_SRGB, | |
277 | .priv = 0 | |
278 | }, | |
279 | { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
280 | .bytesperline = 800, | |
281 | .sizeimage = 800 * 600, | |
282 | .colorspace = V4L2_COLORSPACE_SRGB, | |
283 | .priv = 1 | |
284 | }, | |
285 | {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
286 | .bytesperline = 1280, | |
287 | .sizeimage = 1280 * 960, | |
288 | .colorspace = V4L2_COLORSPACE_SRGB, | |
289 | .priv = 2 | |
290 | }, | |
291 | }; | |
292 | ||
293 | static struct v4l2_pix_format ov9655_mode[] = { | |
294 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
295 | .bytesperline = 640, | |
296 | .sizeimage = 640 * 480, | |
297 | .colorspace = V4L2_COLORSPACE_SRGB, | |
298 | .priv = 0 | |
299 | }, | |
300 | {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
301 | .bytesperline = 1280, | |
302 | .sizeimage = 1280 * 960, | |
303 | .colorspace = V4L2_COLORSPACE_SRGB, | |
304 | .priv = 1 | |
305 | }, | |
306 | }; | |
307 | ||
308 | /*========================= sud-driver functions ===========================*/ | |
309 | ||
310 | /* This function is called at probe time */ | |
311 | static int sd_config(struct gspca_dev *gspca_dev, | |
312 | const struct usb_device_id *id) | |
313 | { | |
314 | struct sd *sd = (struct sd *) gspca_dev; | |
315 | struct cam *cam; | |
83955556 | 316 | u16 vendor_id, product_id; |
4f7cb883 OL |
317 | |
318 | /* Get USB VendorID and ProductID */ | |
83955556 JFM |
319 | vendor_id = id->idVendor; |
320 | product_id = id->idProduct; | |
4f7cb883 OL |
321 | |
322 | sd->nbRightUp = 1; | |
323 | sd->nbIm = -1; | |
324 | ||
325 | sd->sensor = 0xff; | |
326 | if (strcmp(sensor, "MI1320") == 0) | |
327 | sd->sensor = ID_MI1320; | |
328 | else if (strcmp(sensor, "OV2640") == 0) | |
329 | sd->sensor = ID_OV2640; | |
330 | else if (strcmp(sensor, "OV9655") == 0) | |
331 | sd->sensor = ID_OV9655; | |
332 | else if (strcmp(sensor, "MI2020") == 0) | |
333 | sd->sensor = ID_MI2020; | |
4f7cb883 OL |
334 | |
335 | /* Get sensor and set the suitable init/start/../stop functions */ | |
336 | if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) | |
337 | return -1; | |
338 | ||
339 | cam = &gspca_dev->cam; | |
4f7cb883 OL |
340 | |
341 | switch (sd->sensor) { | |
342 | case ID_MI1320: | |
343 | gspca_dev->sd_desc = &sd_desc_mi1320; | |
344 | cam->cam_mode = mi1320_mode; | |
345 | cam->nmodes = ARRAY_SIZE(mi1320_mode); | |
346 | dev_init_settings = mi1320_init_settings; | |
347 | break; | |
348 | ||
349 | case ID_MI2020: | |
350 | gspca_dev->sd_desc = &sd_desc_mi2020; | |
351 | cam->cam_mode = mi2020_mode; | |
352 | cam->nmodes = ARRAY_SIZE(mi2020_mode); | |
353 | dev_init_settings = mi2020_init_settings; | |
354 | break; | |
355 | ||
4f7cb883 OL |
356 | case ID_OV2640: |
357 | gspca_dev->sd_desc = &sd_desc_ov2640; | |
358 | cam->cam_mode = ov2640_mode; | |
359 | cam->nmodes = ARRAY_SIZE(ov2640_mode); | |
360 | dev_init_settings = ov2640_init_settings; | |
361 | break; | |
362 | ||
363 | case ID_OV9655: | |
364 | gspca_dev->sd_desc = &sd_desc_ov9655; | |
365 | cam->cam_mode = ov9655_mode; | |
366 | cam->nmodes = ARRAY_SIZE(ov9655_mode); | |
367 | dev_init_settings = ov9655_init_settings; | |
368 | break; | |
369 | } | |
370 | ||
371 | dev_init_settings(gspca_dev); | |
372 | if (AC50Hz != 0xff) | |
373 | ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; | |
374 | gl860_build_control_table(gspca_dev); | |
375 | ||
376 | return 0; | |
377 | } | |
378 | ||
379 | /* This function is called at probe time after sd_config */ | |
380 | static int sd_init(struct gspca_dev *gspca_dev) | |
381 | { | |
382 | struct sd *sd = (struct sd *) gspca_dev; | |
383 | ||
384 | return sd->dev_init_at_startup(gspca_dev); | |
385 | } | |
386 | ||
387 | /* This function is called before to choose the alt setting */ | |
388 | static int sd_isoc_init(struct gspca_dev *gspca_dev) | |
389 | { | |
390 | struct sd *sd = (struct sd *) gspca_dev; | |
391 | ||
392 | return sd->dev_configure_alt(gspca_dev); | |
393 | } | |
394 | ||
395 | /* This function is called to start the webcam */ | |
396 | static int sd_start(struct gspca_dev *gspca_dev) | |
397 | { | |
398 | struct sd *sd = (struct sd *) gspca_dev; | |
399 | ||
400 | return sd->dev_init_pre_alt(gspca_dev); | |
401 | } | |
402 | ||
403 | /* This function is called to stop the webcam */ | |
404 | static void sd_stop0(struct gspca_dev *gspca_dev) | |
405 | { | |
406 | struct sd *sd = (struct sd *) gspca_dev; | |
407 | ||
45432d41 HG |
408 | if (!sd->gspca_dev.present) |
409 | return; | |
410 | ||
4f7cb883 OL |
411 | return sd->dev_post_unset_alt(gspca_dev); |
412 | } | |
413 | ||
414 | /* This function is called when an image is being received */ | |
415 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |
76dd272b | 416 | u8 *data, int len) |
4f7cb883 OL |
417 | { |
418 | struct sd *sd = (struct sd *) gspca_dev; | |
419 | static s32 nSkipped; | |
420 | ||
421 | s32 mode = (s32) gspca_dev->curr_mode; | |
422 | s32 nToSkip = | |
423 | sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); | |
424 | ||
425 | /* Test only against 0202h, so endianess does not matter */ | |
426 | switch (*(s16 *) data) { | |
427 | case 0x0202: /* End of frame, start a new one */ | |
76dd272b | 428 | gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); |
4f7cb883 OL |
429 | nSkipped = 0; |
430 | if (sd->nbIm >= 0 && sd->nbIm < 10) | |
431 | sd->nbIm++; | |
76dd272b | 432 | gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); |
4f7cb883 OL |
433 | break; |
434 | ||
435 | default: | |
436 | data += 2; | |
437 | len -= 2; | |
438 | if (nSkipped + len <= nToSkip) | |
439 | nSkipped += len; | |
440 | else { | |
441 | if (nSkipped < nToSkip && nSkipped + len > nToSkip) { | |
442 | data += nToSkip - nSkipped; | |
443 | len -= nToSkip - nSkipped; | |
444 | nSkipped = nToSkip + 1; | |
445 | } | |
446 | gspca_frame_add(gspca_dev, | |
76dd272b | 447 | INTER_PACKET, data, len); |
4f7cb883 OL |
448 | } |
449 | break; | |
450 | } | |
451 | } | |
452 | ||
453 | /* This function is called when an image has been read */ | |
454 | /* This function is used to monitor webcam orientation */ | |
455 | static void sd_callback(struct gspca_dev *gspca_dev) | |
456 | { | |
457 | struct sd *sd = (struct sd *) gspca_dev; | |
458 | ||
459 | if (!_OV9655_) { | |
460 | u8 state; | |
461 | u8 upsideDown; | |
462 | ||
463 | /* Probe sensor orientation */ | |
464 | ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); | |
465 | ||
466 | /* C8/40 means upside-down (looking backwards) */ | |
467 | /* D8/50 means right-up (looking onwards) */ | |
468 | upsideDown = (state == 0xc8 || state == 0x40); | |
469 | ||
470 | if (upsideDown && sd->nbRightUp > -4) { | |
471 | if (sd->nbRightUp > 0) | |
472 | sd->nbRightUp = 0; | |
473 | if (sd->nbRightUp == -3) { | |
474 | sd->mirrorMask = 1; | |
475 | sd->waitSet = 1; | |
476 | } | |
477 | sd->nbRightUp--; | |
478 | } | |
479 | if (!upsideDown && sd->nbRightUp < 4) { | |
480 | if (sd->nbRightUp < 0) | |
481 | sd->nbRightUp = 0; | |
482 | if (sd->nbRightUp == 3) { | |
483 | sd->mirrorMask = 0; | |
484 | sd->waitSet = 1; | |
485 | } | |
486 | sd->nbRightUp++; | |
487 | } | |
488 | } | |
489 | ||
490 | if (sd->waitSet) | |
491 | sd->dev_camera_settings(gspca_dev); | |
492 | } | |
493 | ||
494 | /*=================== USB driver structure initialisation ==================*/ | |
495 | ||
95c967c1 | 496 | static const struct usb_device_id device_table[] = { |
4f7cb883 OL |
497 | {USB_DEVICE(0x05e3, 0x0503)}, |
498 | {USB_DEVICE(0x05e3, 0xf191)}, | |
499 | {} | |
500 | }; | |
501 | ||
502 | MODULE_DEVICE_TABLE(usb, device_table); | |
503 | ||
504 | static int sd_probe(struct usb_interface *intf, | |
505 | const struct usb_device_id *id) | |
506 | { | |
bd62bbcd | 507 | return gspca_dev_probe(intf, id, |
4f7cb883 | 508 | &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); |
4f7cb883 OL |
509 | } |
510 | ||
511 | static void sd_disconnect(struct usb_interface *intf) | |
512 | { | |
513 | gspca_disconnect(intf); | |
514 | } | |
515 | ||
516 | static struct usb_driver sd_driver = { | |
517 | .name = MODULE_NAME, | |
518 | .id_table = device_table, | |
519 | .probe = sd_probe, | |
520 | .disconnect = sd_disconnect, | |
521 | #ifdef CONFIG_PM | |
522 | .suspend = gspca_suspend, | |
523 | .resume = gspca_resume, | |
8bb58964 | 524 | .reset_resume = gspca_resume, |
4f7cb883 OL |
525 | #endif |
526 | }; | |
527 | ||
528 | /*====================== Init and Exit module functions ====================*/ | |
529 | ||
ecb3b2b3 | 530 | module_usb_driver(sd_driver); |
4f7cb883 OL |
531 | |
532 | /*==========================================================================*/ | |
533 | ||
534 | int gl860_RTx(struct gspca_dev *gspca_dev, | |
535 | unsigned char pref, u32 req, u16 val, u16 index, | |
536 | s32 len, void *pdata) | |
537 | { | |
538 | struct usb_device *udev = gspca_dev->dev; | |
539 | s32 r = 0; | |
540 | ||
541 | if (pref == 0x40) { /* Send */ | |
542 | if (len > 0) { | |
543 | memcpy(gspca_dev->usb_buf, pdata, len); | |
544 | r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
545 | req, pref, val, index, | |
546 | gspca_dev->usb_buf, | |
547 | len, 400 + 200 * (len > 1)); | |
548 | } else { | |
549 | r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
550 | req, pref, val, index, NULL, len, 400); | |
551 | } | |
552 | } else { /* Receive */ | |
553 | if (len > 0) { | |
554 | r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | |
555 | req, pref, val, index, | |
556 | gspca_dev->usb_buf, | |
557 | len, 400 + 200 * (len > 1)); | |
558 | memcpy(pdata, gspca_dev->usb_buf, len); | |
559 | } else { | |
560 | r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | |
561 | req, pref, val, index, NULL, len, 400); | |
562 | } | |
563 | } | |
564 | ||
565 | if (r < 0) | |
d650fc30 JP |
566 | pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", |
567 | r, pref, req, val, index, len); | |
4f7cb883 OL |
568 | else if (len > 1 && r < len) |
569 | PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); | |
570 | ||
ccbc3cf2 | 571 | msleep(1); |
4f7cb883 OL |
572 | |
573 | return r; | |
574 | } | |
575 | ||
576 | int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) | |
577 | { | |
578 | int n; | |
579 | ||
580 | for (n = 0; n < len; n++) { | |
581 | if (tbl[n].idx != 0xffff) | |
582 | ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, | |
583 | tbl[n].idx, 0, NULL); | |
584 | else if (tbl[n].val == 0xffff) | |
585 | break; | |
586 | else | |
587 | msleep(tbl[n].val); | |
588 | } | |
589 | return n; | |
590 | } | |
591 | ||
592 | int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, | |
593 | int len, int n) | |
594 | { | |
595 | while (++n < len) { | |
596 | if (tbl[n].idx != 0xffff) | |
597 | ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, | |
598 | 0, NULL); | |
599 | else if (tbl[n].val == 0xffff) | |
600 | break; | |
601 | else | |
602 | msleep(tbl[n].val); | |
603 | } | |
604 | return n; | |
605 | } | |
606 | ||
607 | void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) | |
608 | { | |
609 | int n; | |
610 | ||
611 | for (n = 0; n < len; n++) { | |
612 | if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) | |
613 | ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, | |
614 | 3, tbl[n].data); | |
615 | else | |
616 | msleep(tbl[n].idx); | |
617 | } | |
618 | } | |
619 | ||
620 | static int gl860_guess_sensor(struct gspca_dev *gspca_dev, | |
83955556 | 621 | u16 vendor_id, u16 product_id) |
4f7cb883 OL |
622 | { |
623 | struct sd *sd = (struct sd *) gspca_dev; | |
624 | u8 probe, nb26, nb96, nOV, ntry; | |
625 | ||
626 | if (product_id == 0xf191) | |
627 | sd->sensor = ID_MI1320; | |
628 | ||
629 | if (sd->sensor == 0xff) { | |
630 | ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); | |
631 | ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); | |
632 | ||
633 | ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); | |
634 | msleep(3); | |
635 | ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); | |
636 | msleep(3); | |
637 | ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); | |
638 | msleep(3); | |
639 | ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); | |
640 | msleep(3); | |
641 | ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); | |
642 | msleep(3); | |
643 | ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); | |
644 | msleep(3); | |
645 | ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); | |
646 | msleep(56); | |
647 | ||
216f05aa | 648 | PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); |
4f7cb883 OL |
649 | nOV = 0; |
650 | for (ntry = 0; ntry < 4; ntry++) { | |
651 | ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); | |
652 | msleep(3); | |
653 | ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); | |
654 | msleep(3); | |
655 | ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); | |
656 | msleep(10); | |
657 | ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); | |
216f05aa | 658 | PDEBUG(D_PROBE, "probe=0x%02x", probe); |
4f7cb883 OL |
659 | if (probe == 0xff) |
660 | nOV++; | |
661 | } | |
662 | ||
663 | if (nOV) { | |
216f05aa OL |
664 | PDEBUG(D_PROBE, "0xff -> OVXXXX"); |
665 | PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); | |
4f7cb883 OL |
666 | |
667 | nb26 = nb96 = 0; | |
668 | for (ntry = 0; ntry < 4; ntry++) { | |
669 | ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, | |
670 | 0, NULL); | |
671 | msleep(3); | |
672 | ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, | |
673 | 0, NULL); | |
674 | msleep(10); | |
216f05aa | 675 | |
4f7cb883 OL |
676 | /* Wait for 26(OV2640) or 96(OV9655) */ |
677 | ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, | |
678 | 1, &probe); | |
679 | ||
4f7cb883 | 680 | if (probe == 0x26 || probe == 0x40) { |
216f05aa OL |
681 | PDEBUG(D_PROBE, |
682 | "probe=0x%02x -> OV2640", | |
683 | probe); | |
4f7cb883 OL |
684 | sd->sensor = ID_OV2640; |
685 | nb26 += 4; | |
686 | break; | |
687 | } | |
688 | if (probe == 0x96 || probe == 0x55) { | |
216f05aa OL |
689 | PDEBUG(D_PROBE, |
690 | "probe=0x%02x -> OV9655", | |
691 | probe); | |
4f7cb883 OL |
692 | sd->sensor = ID_OV9655; |
693 | nb96 += 4; | |
694 | break; | |
695 | } | |
216f05aa OL |
696 | PDEBUG(D_PROBE, "probe=0x%02x", probe); |
697 | if (probe == 0x00) | |
698 | nb26++; | |
4f7cb883 OL |
699 | if (probe == 0xff) |
700 | nb96++; | |
701 | msleep(3); | |
702 | } | |
216f05aa | 703 | if (nb26 < 4 && nb96 < 4) |
4f7cb883 | 704 | return -1; |
216f05aa OL |
705 | } else { |
706 | PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); | |
4f7cb883 OL |
707 | sd->sensor = ID_MI2020; |
708 | } | |
709 | } | |
710 | ||
711 | if (_MI1320_) { | |
712 | PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); | |
713 | } else if (_MI2020_) { | |
714 | PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); | |
4f7cb883 OL |
715 | } else if (_OV9655_) { |
716 | PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); | |
717 | } else if (_OV2640_) { | |
718 | PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)"); | |
719 | } else { | |
720 | PDEBUG(D_PROBE, "***** Unknown sensor *****"); | |
721 | return -1; | |
722 | } | |
723 | ||
724 | return 0; | |
725 | } |