]>
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 | ||
676fdd85 HG |
61 | static int sd_s_ctrl(struct v4l2_ctrl *ctrl) |
62 | { | |
63 | struct gspca_dev *gspca_dev = | |
64 | container_of(ctrl->handler, struct gspca_dev, ctrl_handler); | |
65 | struct sd *sd = (struct sd *) gspca_dev; | |
4f7cb883 | 66 | |
676fdd85 HG |
67 | switch (ctrl->id) { |
68 | case V4L2_CID_BRIGHTNESS: | |
69 | sd->vcur.brightness = ctrl->val; | |
70 | break; | |
71 | case V4L2_CID_CONTRAST: | |
72 | sd->vcur.contrast = ctrl->val; | |
73 | break; | |
74 | case V4L2_CID_SATURATION: | |
75 | sd->vcur.saturation = ctrl->val; | |
76 | break; | |
77 | case V4L2_CID_HUE: | |
78 | sd->vcur.hue = ctrl->val; | |
79 | break; | |
80 | case V4L2_CID_GAMMA: | |
81 | sd->vcur.gamma = ctrl->val; | |
82 | break; | |
83 | case V4L2_CID_HFLIP: | |
84 | sd->vcur.mirror = ctrl->val; | |
85 | break; | |
86 | case V4L2_CID_VFLIP: | |
87 | sd->vcur.flip = ctrl->val; | |
88 | break; | |
89 | case V4L2_CID_POWER_LINE_FREQUENCY: | |
90 | sd->vcur.AC50Hz = ctrl->val; | |
91 | break; | |
92 | case V4L2_CID_WHITE_BALANCE_TEMPERATURE: | |
93 | sd->vcur.whitebal = ctrl->val; | |
94 | break; | |
95 | case V4L2_CID_SHARPNESS: | |
96 | sd->vcur.sharpness = ctrl->val; | |
97 | break; | |
98 | case V4L2_CID_BACKLIGHT_COMPENSATION: | |
99 | sd->vcur.backlight = ctrl->val; | |
100 | break; | |
101 | default: | |
102 | return -EINVAL; | |
4f7cb883 OL |
103 | } |
104 | ||
676fdd85 HG |
105 | if (gspca_dev->streaming) |
106 | sd->waitSet = 1; | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static const struct v4l2_ctrl_ops sd_ctrl_ops = { | |
112 | .s_ctrl = sd_s_ctrl, | |
113 | }; | |
114 | ||
115 | static int sd_init_controls(struct gspca_dev *gspca_dev) | |
4f7cb883 OL |
116 | { |
117 | struct sd *sd = (struct sd *) gspca_dev; | |
676fdd85 HG |
118 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; |
119 | ||
120 | gspca_dev->vdev.ctrl_handler = hdl; | |
121 | v4l2_ctrl_handler_init(hdl, 11); | |
122 | ||
123 | if (sd->vmax.brightness) | |
124 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BRIGHTNESS, | |
125 | 0, sd->vmax.brightness, 1, | |
126 | sd->vcur.brightness); | |
127 | ||
128 | if (sd->vmax.contrast) | |
129 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_CONTRAST, | |
130 | 0, sd->vmax.contrast, 1, | |
131 | sd->vcur.contrast); | |
132 | ||
133 | if (sd->vmax.saturation) | |
134 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SATURATION, | |
135 | 0, sd->vmax.saturation, 1, | |
136 | sd->vcur.saturation); | |
137 | ||
138 | if (sd->vmax.hue) | |
139 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HUE, | |
140 | 0, sd->vmax.hue, 1, sd->vcur.hue); | |
141 | ||
142 | if (sd->vmax.gamma) | |
143 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAMMA, | |
144 | 0, sd->vmax.gamma, 1, sd->vcur.gamma); | |
145 | ||
146 | if (sd->vmax.mirror) | |
147 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_HFLIP, | |
148 | 0, sd->vmax.mirror, 1, sd->vcur.mirror); | |
149 | ||
150 | if (sd->vmax.flip) | |
151 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_VFLIP, | |
152 | 0, sd->vmax.flip, 1, sd->vcur.flip); | |
153 | ||
154 | if (sd->vmax.AC50Hz) | |
155 | v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, | |
156 | V4L2_CID_POWER_LINE_FREQUENCY, | |
157 | sd->vmax.AC50Hz, 0, sd->vcur.AC50Hz); | |
158 | ||
159 | if (sd->vmax.whitebal) | |
160 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, | |
161 | V4L2_CID_WHITE_BALANCE_TEMPERATURE, | |
162 | 0, sd->vmax.whitebal, 1, sd->vcur.whitebal); | |
163 | ||
164 | if (sd->vmax.sharpness) | |
165 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_SHARPNESS, | |
166 | 0, sd->vmax.sharpness, 1, | |
167 | sd->vcur.sharpness); | |
168 | ||
169 | if (sd->vmax.backlight) | |
170 | v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, | |
171 | V4L2_CID_BACKLIGHT_COMPENSATION, | |
172 | 0, sd->vmax.backlight, 1, | |
173 | sd->vcur.backlight); | |
174 | ||
175 | if (hdl->error) { | |
176 | pr_err("Could not initialize controls\n"); | |
177 | return hdl->error; | |
178 | } | |
179 | ||
180 | return 0; | |
4f7cb883 OL |
181 | } |
182 | ||
183 | /*==================== sud-driver structure initialisation =================*/ | |
184 | ||
aabcdfb6 | 185 | static const struct sd_desc sd_desc_mi1320 = { |
4f7cb883 | 186 | .name = MODULE_NAME, |
4f7cb883 OL |
187 | .config = sd_config, |
188 | .init = sd_init, | |
676fdd85 | 189 | .init_controls = sd_init_controls, |
4f7cb883 OL |
190 | .isoc_init = sd_isoc_init, |
191 | .start = sd_start, | |
192 | .stop0 = sd_stop0, | |
193 | .pkt_scan = sd_pkt_scan, | |
194 | .dq_callback = sd_callback, | |
195 | }; | |
196 | ||
aabcdfb6 | 197 | static const struct sd_desc sd_desc_mi2020 = { |
4f7cb883 | 198 | .name = MODULE_NAME, |
4f7cb883 OL |
199 | .config = sd_config, |
200 | .init = sd_init, | |
676fdd85 | 201 | .init_controls = sd_init_controls, |
4f7cb883 OL |
202 | .isoc_init = sd_isoc_init, |
203 | .start = sd_start, | |
204 | .stop0 = sd_stop0, | |
205 | .pkt_scan = sd_pkt_scan, | |
206 | .dq_callback = sd_callback, | |
207 | }; | |
208 | ||
aabcdfb6 | 209 | static const struct sd_desc sd_desc_ov2640 = { |
4f7cb883 | 210 | .name = MODULE_NAME, |
4f7cb883 OL |
211 | .config = sd_config, |
212 | .init = sd_init, | |
676fdd85 | 213 | .init_controls = sd_init_controls, |
4f7cb883 OL |
214 | .isoc_init = sd_isoc_init, |
215 | .start = sd_start, | |
216 | .stop0 = sd_stop0, | |
217 | .pkt_scan = sd_pkt_scan, | |
218 | .dq_callback = sd_callback, | |
219 | }; | |
220 | ||
aabcdfb6 | 221 | static const struct sd_desc sd_desc_ov9655 = { |
4f7cb883 | 222 | .name = MODULE_NAME, |
4f7cb883 OL |
223 | .config = sd_config, |
224 | .init = sd_init, | |
676fdd85 | 225 | .init_controls = sd_init_controls, |
4f7cb883 OL |
226 | .isoc_init = sd_isoc_init, |
227 | .start = sd_start, | |
228 | .stop0 = sd_stop0, | |
229 | .pkt_scan = sd_pkt_scan, | |
230 | .dq_callback = sd_callback, | |
231 | }; | |
232 | ||
233 | /*=========================== sub-driver image sizes =======================*/ | |
234 | ||
235 | static struct v4l2_pix_format mi2020_mode[] = { | |
236 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
237 | .bytesperline = 640, | |
238 | .sizeimage = 640 * 480, | |
239 | .colorspace = V4L2_COLORSPACE_SRGB, | |
240 | .priv = 0 | |
241 | }, | |
bff7e839 | 242 | { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, |
4f7cb883 | 243 | .bytesperline = 800, |
bff7e839 | 244 | .sizeimage = 800 * 598, |
4f7cb883 OL |
245 | .colorspace = V4L2_COLORSPACE_SRGB, |
246 | .priv = 1 | |
247 | }, | |
248 | {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
249 | .bytesperline = 1280, | |
250 | .sizeimage = 1280 * 1024, | |
251 | .colorspace = V4L2_COLORSPACE_SRGB, | |
252 | .priv = 2 | |
253 | }, | |
bff7e839 | 254 | {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, |
4f7cb883 | 255 | .bytesperline = 1600, |
bff7e839 | 256 | .sizeimage = 1600 * 1198, |
4f7cb883 OL |
257 | .colorspace = V4L2_COLORSPACE_SRGB, |
258 | .priv = 3 | |
259 | }, | |
260 | }; | |
261 | ||
262 | static struct v4l2_pix_format ov2640_mode[] = { | |
263 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
264 | .bytesperline = 640, | |
265 | .sizeimage = 640 * 480, | |
266 | .colorspace = V4L2_COLORSPACE_SRGB, | |
267 | .priv = 0 | |
268 | }, | |
269 | { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
270 | .bytesperline = 800, | |
271 | .sizeimage = 800 * 600, | |
272 | .colorspace = V4L2_COLORSPACE_SRGB, | |
273 | .priv = 1 | |
274 | }, | |
275 | {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
276 | .bytesperline = 1280, | |
277 | .sizeimage = 1280 * 960, | |
278 | .colorspace = V4L2_COLORSPACE_SRGB, | |
279 | .priv = 2 | |
280 | }, | |
281 | {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
282 | .bytesperline = 1600, | |
283 | .sizeimage = 1600 * 1200, | |
284 | .colorspace = V4L2_COLORSPACE_SRGB, | |
285 | .priv = 3 | |
286 | }, | |
287 | }; | |
288 | ||
289 | static struct v4l2_pix_format mi1320_mode[] = { | |
290 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
291 | .bytesperline = 640, | |
292 | .sizeimage = 640 * 480, | |
293 | .colorspace = V4L2_COLORSPACE_SRGB, | |
294 | .priv = 0 | |
295 | }, | |
296 | { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
297 | .bytesperline = 800, | |
298 | .sizeimage = 800 * 600, | |
299 | .colorspace = V4L2_COLORSPACE_SRGB, | |
300 | .priv = 1 | |
301 | }, | |
302 | {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
303 | .bytesperline = 1280, | |
304 | .sizeimage = 1280 * 960, | |
305 | .colorspace = V4L2_COLORSPACE_SRGB, | |
306 | .priv = 2 | |
307 | }, | |
308 | }; | |
309 | ||
310 | static struct v4l2_pix_format ov9655_mode[] = { | |
311 | { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
312 | .bytesperline = 640, | |
313 | .sizeimage = 640 * 480, | |
314 | .colorspace = V4L2_COLORSPACE_SRGB, | |
315 | .priv = 0 | |
316 | }, | |
317 | {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, | |
318 | .bytesperline = 1280, | |
319 | .sizeimage = 1280 * 960, | |
320 | .colorspace = V4L2_COLORSPACE_SRGB, | |
321 | .priv = 1 | |
322 | }, | |
323 | }; | |
324 | ||
325 | /*========================= sud-driver functions ===========================*/ | |
326 | ||
327 | /* This function is called at probe time */ | |
328 | static int sd_config(struct gspca_dev *gspca_dev, | |
329 | const struct usb_device_id *id) | |
330 | { | |
331 | struct sd *sd = (struct sd *) gspca_dev; | |
332 | struct cam *cam; | |
83955556 | 333 | u16 vendor_id, product_id; |
4f7cb883 OL |
334 | |
335 | /* Get USB VendorID and ProductID */ | |
83955556 JFM |
336 | vendor_id = id->idVendor; |
337 | product_id = id->idProduct; | |
4f7cb883 OL |
338 | |
339 | sd->nbRightUp = 1; | |
340 | sd->nbIm = -1; | |
341 | ||
342 | sd->sensor = 0xff; | |
343 | if (strcmp(sensor, "MI1320") == 0) | |
344 | sd->sensor = ID_MI1320; | |
345 | else if (strcmp(sensor, "OV2640") == 0) | |
346 | sd->sensor = ID_OV2640; | |
347 | else if (strcmp(sensor, "OV9655") == 0) | |
348 | sd->sensor = ID_OV9655; | |
349 | else if (strcmp(sensor, "MI2020") == 0) | |
350 | sd->sensor = ID_MI2020; | |
4f7cb883 OL |
351 | |
352 | /* Get sensor and set the suitable init/start/../stop functions */ | |
353 | if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) | |
354 | return -1; | |
355 | ||
356 | cam = &gspca_dev->cam; | |
4f7cb883 OL |
357 | |
358 | switch (sd->sensor) { | |
359 | case ID_MI1320: | |
360 | gspca_dev->sd_desc = &sd_desc_mi1320; | |
361 | cam->cam_mode = mi1320_mode; | |
362 | cam->nmodes = ARRAY_SIZE(mi1320_mode); | |
363 | dev_init_settings = mi1320_init_settings; | |
364 | break; | |
365 | ||
366 | case ID_MI2020: | |
367 | gspca_dev->sd_desc = &sd_desc_mi2020; | |
368 | cam->cam_mode = mi2020_mode; | |
369 | cam->nmodes = ARRAY_SIZE(mi2020_mode); | |
370 | dev_init_settings = mi2020_init_settings; | |
371 | break; | |
372 | ||
4f7cb883 OL |
373 | case ID_OV2640: |
374 | gspca_dev->sd_desc = &sd_desc_ov2640; | |
375 | cam->cam_mode = ov2640_mode; | |
376 | cam->nmodes = ARRAY_SIZE(ov2640_mode); | |
377 | dev_init_settings = ov2640_init_settings; | |
378 | break; | |
379 | ||
380 | case ID_OV9655: | |
381 | gspca_dev->sd_desc = &sd_desc_ov9655; | |
382 | cam->cam_mode = ov9655_mode; | |
383 | cam->nmodes = ARRAY_SIZE(ov9655_mode); | |
384 | dev_init_settings = ov9655_init_settings; | |
385 | break; | |
386 | } | |
387 | ||
388 | dev_init_settings(gspca_dev); | |
389 | if (AC50Hz != 0xff) | |
390 | ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; | |
4f7cb883 OL |
391 | |
392 | return 0; | |
393 | } | |
394 | ||
395 | /* This function is called at probe time after sd_config */ | |
396 | static int sd_init(struct gspca_dev *gspca_dev) | |
397 | { | |
398 | struct sd *sd = (struct sd *) gspca_dev; | |
399 | ||
400 | return sd->dev_init_at_startup(gspca_dev); | |
401 | } | |
402 | ||
403 | /* This function is called before to choose the alt setting */ | |
404 | static int sd_isoc_init(struct gspca_dev *gspca_dev) | |
405 | { | |
406 | struct sd *sd = (struct sd *) gspca_dev; | |
407 | ||
408 | return sd->dev_configure_alt(gspca_dev); | |
409 | } | |
410 | ||
411 | /* This function is called to start the webcam */ | |
412 | static int sd_start(struct gspca_dev *gspca_dev) | |
413 | { | |
414 | struct sd *sd = (struct sd *) gspca_dev; | |
415 | ||
416 | return sd->dev_init_pre_alt(gspca_dev); | |
417 | } | |
418 | ||
419 | /* This function is called to stop the webcam */ | |
420 | static void sd_stop0(struct gspca_dev *gspca_dev) | |
421 | { | |
422 | struct sd *sd = (struct sd *) gspca_dev; | |
423 | ||
45432d41 HG |
424 | if (!sd->gspca_dev.present) |
425 | return; | |
426 | ||
4f7cb883 OL |
427 | return sd->dev_post_unset_alt(gspca_dev); |
428 | } | |
429 | ||
430 | /* This function is called when an image is being received */ | |
431 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | |
76dd272b | 432 | u8 *data, int len) |
4f7cb883 OL |
433 | { |
434 | struct sd *sd = (struct sd *) gspca_dev; | |
435 | static s32 nSkipped; | |
436 | ||
437 | s32 mode = (s32) gspca_dev->curr_mode; | |
438 | s32 nToSkip = | |
439 | sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); | |
440 | ||
f58c91ce | 441 | /* Test only against 0202h, so endianness does not matter */ |
4f7cb883 OL |
442 | switch (*(s16 *) data) { |
443 | case 0x0202: /* End of frame, start a new one */ | |
76dd272b | 444 | gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); |
4f7cb883 OL |
445 | nSkipped = 0; |
446 | if (sd->nbIm >= 0 && sd->nbIm < 10) | |
447 | sd->nbIm++; | |
76dd272b | 448 | gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); |
4f7cb883 OL |
449 | break; |
450 | ||
451 | default: | |
452 | data += 2; | |
453 | len -= 2; | |
454 | if (nSkipped + len <= nToSkip) | |
455 | nSkipped += len; | |
456 | else { | |
457 | if (nSkipped < nToSkip && nSkipped + len > nToSkip) { | |
458 | data += nToSkip - nSkipped; | |
459 | len -= nToSkip - nSkipped; | |
460 | nSkipped = nToSkip + 1; | |
461 | } | |
462 | gspca_frame_add(gspca_dev, | |
76dd272b | 463 | INTER_PACKET, data, len); |
4f7cb883 OL |
464 | } |
465 | break; | |
466 | } | |
467 | } | |
468 | ||
469 | /* This function is called when an image has been read */ | |
470 | /* This function is used to monitor webcam orientation */ | |
471 | static void sd_callback(struct gspca_dev *gspca_dev) | |
472 | { | |
473 | struct sd *sd = (struct sd *) gspca_dev; | |
474 | ||
475 | if (!_OV9655_) { | |
476 | u8 state; | |
477 | u8 upsideDown; | |
478 | ||
479 | /* Probe sensor orientation */ | |
480 | ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); | |
481 | ||
482 | /* C8/40 means upside-down (looking backwards) */ | |
483 | /* D8/50 means right-up (looking onwards) */ | |
484 | upsideDown = (state == 0xc8 || state == 0x40); | |
485 | ||
486 | if (upsideDown && sd->nbRightUp > -4) { | |
487 | if (sd->nbRightUp > 0) | |
488 | sd->nbRightUp = 0; | |
489 | if (sd->nbRightUp == -3) { | |
490 | sd->mirrorMask = 1; | |
491 | sd->waitSet = 1; | |
492 | } | |
493 | sd->nbRightUp--; | |
494 | } | |
495 | if (!upsideDown && sd->nbRightUp < 4) { | |
496 | if (sd->nbRightUp < 0) | |
497 | sd->nbRightUp = 0; | |
498 | if (sd->nbRightUp == 3) { | |
499 | sd->mirrorMask = 0; | |
500 | sd->waitSet = 1; | |
501 | } | |
502 | sd->nbRightUp++; | |
503 | } | |
504 | } | |
505 | ||
506 | if (sd->waitSet) | |
507 | sd->dev_camera_settings(gspca_dev); | |
508 | } | |
509 | ||
510 | /*=================== USB driver structure initialisation ==================*/ | |
511 | ||
95c967c1 | 512 | static const struct usb_device_id device_table[] = { |
4f7cb883 OL |
513 | {USB_DEVICE(0x05e3, 0x0503)}, |
514 | {USB_DEVICE(0x05e3, 0xf191)}, | |
515 | {} | |
516 | }; | |
517 | ||
518 | MODULE_DEVICE_TABLE(usb, device_table); | |
519 | ||
520 | static int sd_probe(struct usb_interface *intf, | |
521 | const struct usb_device_id *id) | |
522 | { | |
bd62bbcd | 523 | return gspca_dev_probe(intf, id, |
4f7cb883 | 524 | &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); |
4f7cb883 OL |
525 | } |
526 | ||
527 | static void sd_disconnect(struct usb_interface *intf) | |
528 | { | |
529 | gspca_disconnect(intf); | |
530 | } | |
531 | ||
532 | static struct usb_driver sd_driver = { | |
533 | .name = MODULE_NAME, | |
534 | .id_table = device_table, | |
535 | .probe = sd_probe, | |
536 | .disconnect = sd_disconnect, | |
537 | #ifdef CONFIG_PM | |
538 | .suspend = gspca_suspend, | |
539 | .resume = gspca_resume, | |
8bb58964 | 540 | .reset_resume = gspca_resume, |
4f7cb883 OL |
541 | #endif |
542 | }; | |
543 | ||
544 | /*====================== Init and Exit module functions ====================*/ | |
545 | ||
ecb3b2b3 | 546 | module_usb_driver(sd_driver); |
4f7cb883 OL |
547 | |
548 | /*==========================================================================*/ | |
549 | ||
550 | int gl860_RTx(struct gspca_dev *gspca_dev, | |
551 | unsigned char pref, u32 req, u16 val, u16 index, | |
552 | s32 len, void *pdata) | |
553 | { | |
554 | struct usb_device *udev = gspca_dev->dev; | |
555 | s32 r = 0; | |
556 | ||
557 | if (pref == 0x40) { /* Send */ | |
558 | if (len > 0) { | |
559 | memcpy(gspca_dev->usb_buf, pdata, len); | |
560 | r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
561 | req, pref, val, index, | |
562 | gspca_dev->usb_buf, | |
563 | len, 400 + 200 * (len > 1)); | |
564 | } else { | |
565 | r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), | |
566 | req, pref, val, index, NULL, len, 400); | |
567 | } | |
568 | } else { /* Receive */ | |
569 | if (len > 0) { | |
570 | r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | |
571 | req, pref, val, index, | |
572 | gspca_dev->usb_buf, | |
573 | len, 400 + 200 * (len > 1)); | |
574 | memcpy(pdata, gspca_dev->usb_buf, len); | |
575 | } else { | |
576 | r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | |
577 | req, pref, val, index, NULL, len, 400); | |
578 | } | |
579 | } | |
580 | ||
581 | if (r < 0) | |
d650fc30 JP |
582 | pr_err("ctrl transfer failed %4d [p%02x r%d v%04x i%04x len%d]\n", |
583 | r, pref, req, val, index, len); | |
4f7cb883 | 584 | else if (len > 1 && r < len) |
c93396e1 | 585 | PERR("short ctrl transfer %d/%d", r, len); |
4f7cb883 | 586 | |
ccbc3cf2 | 587 | msleep(1); |
4f7cb883 OL |
588 | |
589 | return r; | |
590 | } | |
591 | ||
592 | int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) | |
593 | { | |
594 | int n; | |
595 | ||
596 | for (n = 0; n < len; n++) { | |
597 | if (tbl[n].idx != 0xffff) | |
598 | ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, | |
599 | tbl[n].idx, 0, NULL); | |
600 | else if (tbl[n].val == 0xffff) | |
601 | break; | |
602 | else | |
603 | msleep(tbl[n].val); | |
604 | } | |
605 | return n; | |
606 | } | |
607 | ||
608 | int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, | |
609 | int len, int n) | |
610 | { | |
611 | while (++n < len) { | |
612 | if (tbl[n].idx != 0xffff) | |
613 | ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, | |
614 | 0, NULL); | |
615 | else if (tbl[n].val == 0xffff) | |
616 | break; | |
617 | else | |
618 | msleep(tbl[n].val); | |
619 | } | |
620 | return n; | |
621 | } | |
622 | ||
623 | void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) | |
624 | { | |
625 | int n; | |
626 | ||
627 | for (n = 0; n < len; n++) { | |
628 | if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) | |
629 | ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, | |
630 | 3, tbl[n].data); | |
631 | else | |
632 | msleep(tbl[n].idx); | |
633 | } | |
634 | } | |
635 | ||
636 | static int gl860_guess_sensor(struct gspca_dev *gspca_dev, | |
83955556 | 637 | u16 vendor_id, u16 product_id) |
4f7cb883 OL |
638 | { |
639 | struct sd *sd = (struct sd *) gspca_dev; | |
640 | u8 probe, nb26, nb96, nOV, ntry; | |
641 | ||
642 | if (product_id == 0xf191) | |
643 | sd->sensor = ID_MI1320; | |
644 | ||
645 | if (sd->sensor == 0xff) { | |
646 | ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); | |
647 | ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); | |
648 | ||
649 | ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); | |
650 | msleep(3); | |
651 | ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); | |
652 | msleep(3); | |
653 | ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); | |
654 | msleep(3); | |
655 | ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); | |
656 | msleep(3); | |
657 | ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); | |
658 | msleep(3); | |
659 | ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); | |
660 | msleep(3); | |
661 | ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); | |
662 | msleep(56); | |
663 | ||
216f05aa | 664 | PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); |
4f7cb883 OL |
665 | nOV = 0; |
666 | for (ntry = 0; ntry < 4; ntry++) { | |
667 | ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); | |
668 | msleep(3); | |
669 | ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); | |
670 | msleep(3); | |
671 | ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); | |
672 | msleep(10); | |
673 | ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); | |
216f05aa | 674 | PDEBUG(D_PROBE, "probe=0x%02x", probe); |
4f7cb883 OL |
675 | if (probe == 0xff) |
676 | nOV++; | |
677 | } | |
678 | ||
679 | if (nOV) { | |
216f05aa OL |
680 | PDEBUG(D_PROBE, "0xff -> OVXXXX"); |
681 | PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); | |
4f7cb883 OL |
682 | |
683 | nb26 = nb96 = 0; | |
684 | for (ntry = 0; ntry < 4; ntry++) { | |
685 | ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, | |
686 | 0, NULL); | |
687 | msleep(3); | |
688 | ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, | |
689 | 0, NULL); | |
690 | msleep(10); | |
216f05aa | 691 | |
4f7cb883 OL |
692 | /* Wait for 26(OV2640) or 96(OV9655) */ |
693 | ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, | |
694 | 1, &probe); | |
695 | ||
4f7cb883 | 696 | if (probe == 0x26 || probe == 0x40) { |
216f05aa OL |
697 | PDEBUG(D_PROBE, |
698 | "probe=0x%02x -> OV2640", | |
699 | probe); | |
4f7cb883 OL |
700 | sd->sensor = ID_OV2640; |
701 | nb26 += 4; | |
702 | break; | |
703 | } | |
704 | if (probe == 0x96 || probe == 0x55) { | |
216f05aa OL |
705 | PDEBUG(D_PROBE, |
706 | "probe=0x%02x -> OV9655", | |
707 | probe); | |
4f7cb883 OL |
708 | sd->sensor = ID_OV9655; |
709 | nb96 += 4; | |
710 | break; | |
711 | } | |
216f05aa OL |
712 | PDEBUG(D_PROBE, "probe=0x%02x", probe); |
713 | if (probe == 0x00) | |
714 | nb26++; | |
4f7cb883 OL |
715 | if (probe == 0xff) |
716 | nb96++; | |
717 | msleep(3); | |
718 | } | |
216f05aa | 719 | if (nb26 < 4 && nb96 < 4) |
4f7cb883 | 720 | return -1; |
216f05aa OL |
721 | } else { |
722 | PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); | |
4f7cb883 OL |
723 | sd->sensor = ID_MI2020; |
724 | } | |
725 | } | |
726 | ||
727 | if (_MI1320_) { | |
728 | PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); | |
729 | } else if (_MI2020_) { | |
730 | PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); | |
4f7cb883 OL |
731 | } else if (_OV9655_) { |
732 | PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); | |
733 | } else if (_OV2640_) { | |
734 | PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)"); | |
735 | } else { | |
736 | PDEBUG(D_PROBE, "***** Unknown sensor *****"); | |
737 | return -1; | |
738 | } | |
739 | ||
740 | return 0; | |
741 | } |