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