diff options
Diffstat (limited to 'drivers/media/video/gspca/ov534.c')
-rw-r--r-- | drivers/media/video/gspca/ov534.c | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c new file mode 100644 index 000000000000..3bf15e401693 --- /dev/null +++ b/drivers/media/video/gspca/ov534.c | |||
@@ -0,0 +1,601 @@ | |||
1 | /* | ||
2 | * ov534/ov772x gspca driver | ||
3 | * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> | ||
4 | * Copyright (C) 2008 Jim Paris <jim@jtan.com> | ||
5 | * | ||
6 | * Based on a prototype written by Mark Ferrell <majortrips@gmail.com> | ||
7 | * USB protocol reverse engineered by Jim Paris <jim@jtan.com> | ||
8 | * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #define MODULE_NAME "ov534" | ||
26 | |||
27 | #include "gspca.h" | ||
28 | |||
29 | #define OV534_REG_ADDRESS 0xf1 /* ? */ | ||
30 | #define OV534_REG_SUBADDR 0xf2 | ||
31 | #define OV534_REG_WRITE 0xf3 | ||
32 | #define OV534_REG_READ 0xf4 | ||
33 | #define OV534_REG_OPERATION 0xf5 | ||
34 | #define OV534_REG_STATUS 0xf6 | ||
35 | |||
36 | #define OV534_OP_WRITE_3 0x37 | ||
37 | #define OV534_OP_WRITE_2 0x33 | ||
38 | #define OV534_OP_READ_2 0xf9 | ||
39 | |||
40 | #define CTRL_TIMEOUT 500 | ||
41 | |||
42 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | ||
43 | MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | |||
46 | /* specific webcam descriptor */ | ||
47 | struct sd { | ||
48 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
49 | __u32 last_fid; | ||
50 | __u32 last_pts; | ||
51 | int frame_rate; | ||
52 | }; | ||
53 | |||
54 | /* V4L2 controls supported by the driver */ | ||
55 | static struct ctrl sd_ctrls[] = { | ||
56 | }; | ||
57 | |||
58 | static const struct v4l2_pix_format vga_mode[] = { | ||
59 | {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, | ||
60 | .bytesperline = 640 * 2, | ||
61 | .sizeimage = 640 * 480 * 2, | ||
62 | .colorspace = V4L2_COLORSPACE_JPEG, | ||
63 | .priv = 0}, | ||
64 | }; | ||
65 | |||
66 | static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) | ||
67 | { | ||
68 | struct usb_device *udev = gspca_dev->dev; | ||
69 | int ret; | ||
70 | |||
71 | PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); | ||
72 | gspca_dev->usb_buf[0] = val; | ||
73 | ret = usb_control_msg(udev, | ||
74 | usb_sndctrlpipe(udev, 0), | ||
75 | 0x1, | ||
76 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
77 | 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); | ||
78 | if (ret < 0) | ||
79 | PDEBUG(D_ERR, "write failed"); | ||
80 | } | ||
81 | |||
82 | static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) | ||
83 | { | ||
84 | struct usb_device *udev = gspca_dev->dev; | ||
85 | int ret; | ||
86 | |||
87 | ret = usb_control_msg(udev, | ||
88 | usb_rcvctrlpipe(udev, 0), | ||
89 | 0x1, | ||
90 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
91 | 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); | ||
92 | PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); | ||
93 | if (ret < 0) | ||
94 | PDEBUG(D_ERR, "read failed"); | ||
95 | return gspca_dev->usb_buf[0]; | ||
96 | } | ||
97 | |||
98 | /* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. | ||
99 | * (direction and output)? */ | ||
100 | static void ov534_set_led(struct gspca_dev *gspca_dev, int status) | ||
101 | { | ||
102 | u8 data; | ||
103 | |||
104 | PDEBUG(D_CONF, "led status: %d", status); | ||
105 | |||
106 | data = ov534_reg_read(gspca_dev, 0x21); | ||
107 | data |= 0x80; | ||
108 | ov534_reg_write(gspca_dev, 0x21, data); | ||
109 | |||
110 | data = ov534_reg_read(gspca_dev, 0x23); | ||
111 | if (status) | ||
112 | data |= 0x80; | ||
113 | else | ||
114 | data &= ~(0x80); | ||
115 | |||
116 | ov534_reg_write(gspca_dev, 0x23, data); | ||
117 | } | ||
118 | |||
119 | static int sccb_check_status(struct gspca_dev *gspca_dev) | ||
120 | { | ||
121 | u8 data; | ||
122 | int i; | ||
123 | |||
124 | for (i = 0; i < 5; i++) { | ||
125 | data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); | ||
126 | |||
127 | switch (data) { | ||
128 | case 0x00: | ||
129 | return 1; | ||
130 | case 0x04: | ||
131 | return 0; | ||
132 | case 0x03: | ||
133 | break; | ||
134 | default: | ||
135 | PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", | ||
136 | data, i + 1); | ||
137 | } | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) | ||
143 | { | ||
144 | PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val); | ||
145 | ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); | ||
146 | ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); | ||
147 | ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); | ||
148 | |||
149 | if (!sccb_check_status(gspca_dev)) | ||
150 | PDEBUG(D_ERR, "sccb_reg_write failed"); | ||
151 | } | ||
152 | |||
153 | #ifdef GSPCA_DEBUG | ||
154 | static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) | ||
155 | { | ||
156 | ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); | ||
157 | ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); | ||
158 | if (!sccb_check_status(gspca_dev)) | ||
159 | PDEBUG(D_ERR, "sccb_reg_read failed 1"); | ||
160 | |||
161 | ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); | ||
162 | if (!sccb_check_status(gspca_dev)) | ||
163 | PDEBUG(D_ERR, "sccb_reg_read failed 2"); | ||
164 | |||
165 | return ov534_reg_read(gspca_dev, OV534_REG_READ); | ||
166 | } | ||
167 | #endif | ||
168 | |||
169 | static const __u8 ov534_reg_initdata[][2] = { | ||
170 | { 0xe7, 0x3a }, | ||
171 | |||
172 | { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */ | ||
173 | |||
174 | { 0xc2, 0x0c }, | ||
175 | { 0x88, 0xf8 }, | ||
176 | { 0xc3, 0x69 }, | ||
177 | { 0x89, 0xff }, | ||
178 | { 0x76, 0x03 }, | ||
179 | { 0x92, 0x01 }, | ||
180 | { 0x93, 0x18 }, | ||
181 | { 0x94, 0x10 }, | ||
182 | { 0x95, 0x10 }, | ||
183 | { 0xe2, 0x00 }, | ||
184 | { 0xe7, 0x3e }, | ||
185 | |||
186 | { 0x96, 0x00 }, | ||
187 | |||
188 | { 0x97, 0x20 }, | ||
189 | { 0x97, 0x20 }, | ||
190 | { 0x97, 0x20 }, | ||
191 | { 0x97, 0x0a }, | ||
192 | { 0x97, 0x3f }, | ||
193 | { 0x97, 0x4a }, | ||
194 | { 0x97, 0x20 }, | ||
195 | { 0x97, 0x15 }, | ||
196 | { 0x97, 0x0b }, | ||
197 | |||
198 | { 0x8e, 0x40 }, | ||
199 | { 0x1f, 0x81 }, | ||
200 | { 0x34, 0x05 }, | ||
201 | { 0xe3, 0x04 }, | ||
202 | { 0x88, 0x00 }, | ||
203 | { 0x89, 0x00 }, | ||
204 | { 0x76, 0x00 }, | ||
205 | { 0xe7, 0x2e }, | ||
206 | { 0x31, 0xf9 }, | ||
207 | { 0x25, 0x42 }, | ||
208 | { 0x21, 0xf0 }, | ||
209 | |||
210 | { 0x1c, 0x00 }, | ||
211 | { 0x1d, 0x40 }, | ||
212 | { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ | ||
213 | { 0x1d, 0x00 }, /* payload size */ | ||
214 | { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ | ||
215 | { 0x1d, 0x58 }, /* frame size */ | ||
216 | { 0x1d, 0x00 }, /* frame size */ | ||
217 | |||
218 | { 0x1c, 0x0a }, | ||
219 | { 0x1d, 0x08 }, /* turn on UVC header */ | ||
220 | { 0x1d, 0x0e }, /* .. */ | ||
221 | |||
222 | { 0x8d, 0x1c }, | ||
223 | { 0x8e, 0x80 }, | ||
224 | { 0xe5, 0x04 }, | ||
225 | |||
226 | { 0xc0, 0x50 }, | ||
227 | { 0xc1, 0x3c }, | ||
228 | { 0xc2, 0x0c }, | ||
229 | }; | ||
230 | |||
231 | static const __u8 ov772x_reg_initdata[][2] = { | ||
232 | { 0x12, 0x80 }, | ||
233 | { 0x11, 0x01 }, | ||
234 | |||
235 | { 0x3d, 0x03 }, | ||
236 | { 0x17, 0x26 }, | ||
237 | { 0x18, 0xa0 }, | ||
238 | { 0x19, 0x07 }, | ||
239 | { 0x1a, 0xf0 }, | ||
240 | { 0x32, 0x00 }, | ||
241 | { 0x29, 0xa0 }, | ||
242 | { 0x2c, 0xf0 }, | ||
243 | { 0x65, 0x20 }, | ||
244 | { 0x11, 0x01 }, | ||
245 | { 0x42, 0x7f }, | ||
246 | { 0x63, 0xe0 }, | ||
247 | { 0x64, 0xff }, | ||
248 | { 0x66, 0x00 }, | ||
249 | { 0x13, 0xf0 }, | ||
250 | { 0x0d, 0x41 }, | ||
251 | { 0x0f, 0xc5 }, | ||
252 | { 0x14, 0x11 }, | ||
253 | |||
254 | { 0x22, 0x7f }, | ||
255 | { 0x23, 0x03 }, | ||
256 | { 0x24, 0x40 }, | ||
257 | { 0x25, 0x30 }, | ||
258 | { 0x26, 0xa1 }, | ||
259 | { 0x2a, 0x00 }, | ||
260 | { 0x2b, 0x00 }, | ||
261 | { 0x6b, 0xaa }, | ||
262 | { 0x13, 0xff }, | ||
263 | |||
264 | { 0x90, 0x05 }, | ||
265 | { 0x91, 0x01 }, | ||
266 | { 0x92, 0x03 }, | ||
267 | { 0x93, 0x00 }, | ||
268 | { 0x94, 0x60 }, | ||
269 | { 0x95, 0x3c }, | ||
270 | { 0x96, 0x24 }, | ||
271 | { 0x97, 0x1e }, | ||
272 | { 0x98, 0x62 }, | ||
273 | { 0x99, 0x80 }, | ||
274 | { 0x9a, 0x1e }, | ||
275 | { 0x9b, 0x08 }, | ||
276 | { 0x9c, 0x20 }, | ||
277 | { 0x9e, 0x81 }, | ||
278 | |||
279 | { 0xa6, 0x04 }, | ||
280 | { 0x7e, 0x0c }, | ||
281 | { 0x7f, 0x16 }, | ||
282 | { 0x80, 0x2a }, | ||
283 | { 0x81, 0x4e }, | ||
284 | { 0x82, 0x61 }, | ||
285 | { 0x83, 0x6f }, | ||
286 | { 0x84, 0x7b }, | ||
287 | { 0x85, 0x86 }, | ||
288 | { 0x86, 0x8e }, | ||
289 | { 0x87, 0x97 }, | ||
290 | { 0x88, 0xa4 }, | ||
291 | { 0x89, 0xaf }, | ||
292 | { 0x8a, 0xc5 }, | ||
293 | { 0x8b, 0xd7 }, | ||
294 | { 0x8c, 0xe8 }, | ||
295 | { 0x8d, 0x20 }, | ||
296 | |||
297 | { 0x0c, 0x90 }, | ||
298 | |||
299 | { 0x2b, 0x00 }, | ||
300 | { 0x22, 0x7f }, | ||
301 | { 0x23, 0x03 }, | ||
302 | { 0x11, 0x01 }, | ||
303 | { 0x0c, 0xd0 }, | ||
304 | { 0x64, 0xff }, | ||
305 | { 0x0d, 0x41 }, | ||
306 | |||
307 | { 0x14, 0x41 }, | ||
308 | { 0x0e, 0xcd }, | ||
309 | { 0xac, 0xbf }, | ||
310 | { 0x8e, 0x00 }, | ||
311 | { 0x0c, 0xd0 } | ||
312 | }; | ||
313 | |||
314 | /* set framerate */ | ||
315 | static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) | ||
316 | { | ||
317 | struct sd *sd = (struct sd *) gspca_dev; | ||
318 | int fr = sd->frame_rate; | ||
319 | |||
320 | switch (fr) { | ||
321 | case 50: | ||
322 | sccb_reg_write(gspca_dev, 0x11, 0x01); | ||
323 | sccb_reg_write(gspca_dev, 0x0d, 0x41); | ||
324 | ov534_reg_write(gspca_dev, 0xe5, 0x02); | ||
325 | break; | ||
326 | case 40: | ||
327 | sccb_reg_write(gspca_dev, 0x11, 0x02); | ||
328 | sccb_reg_write(gspca_dev, 0x0d, 0xc1); | ||
329 | ov534_reg_write(gspca_dev, 0xe5, 0x04); | ||
330 | break; | ||
331 | /* case 30: */ | ||
332 | default: | ||
333 | fr = 30; | ||
334 | sccb_reg_write(gspca_dev, 0x11, 0x04); | ||
335 | sccb_reg_write(gspca_dev, 0x0d, 0x81); | ||
336 | ov534_reg_write(gspca_dev, 0xe5, 0x02); | ||
337 | break; | ||
338 | case 15: | ||
339 | sccb_reg_write(gspca_dev, 0x11, 0x03); | ||
340 | sccb_reg_write(gspca_dev, 0x0d, 0x41); | ||
341 | ov534_reg_write(gspca_dev, 0xe5, 0x04); | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | sd->frame_rate = fr; | ||
346 | PDEBUG(D_PROBE, "frame_rate: %d", fr); | ||
347 | } | ||
348 | |||
349 | /* setup method */ | ||
350 | static void ov534_setup(struct gspca_dev *gspca_dev) | ||
351 | { | ||
352 | int i; | ||
353 | |||
354 | /* Initialize bridge chip */ | ||
355 | for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++) | ||
356 | ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0], | ||
357 | ov534_reg_initdata[i][1]); | ||
358 | |||
359 | PDEBUG(D_PROBE, "sensor is ov%02x%02x", | ||
360 | sccb_reg_read(gspca_dev, 0x0a), | ||
361 | sccb_reg_read(gspca_dev, 0x0b)); | ||
362 | |||
363 | ov534_set_led(gspca_dev, 1); | ||
364 | |||
365 | /* Initialize sensor */ | ||
366 | for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++) | ||
367 | sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0], | ||
368 | ov772x_reg_initdata[i][1]); | ||
369 | |||
370 | ov534_reg_write(gspca_dev, 0xe0, 0x09); | ||
371 | ov534_set_led(gspca_dev, 0); | ||
372 | } | ||
373 | |||
374 | /* this function is called at probe time */ | ||
375 | static int sd_config(struct gspca_dev *gspca_dev, | ||
376 | const struct usb_device_id *id) | ||
377 | { | ||
378 | struct cam *cam; | ||
379 | |||
380 | cam = &gspca_dev->cam; | ||
381 | |||
382 | cam->epaddr = 0x01; | ||
383 | cam->cam_mode = vga_mode; | ||
384 | cam->nmodes = ARRAY_SIZE(vga_mode); | ||
385 | |||
386 | cam->bulk_size = 16384; | ||
387 | cam->bulk_nurbs = 2; | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /* this function is called at probe and resume time */ | ||
393 | static int sd_init(struct gspca_dev *gspca_dev) | ||
394 | { | ||
395 | ov534_setup(gspca_dev); | ||
396 | ov534_set_frame_rate(gspca_dev); | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int sd_start(struct gspca_dev *gspca_dev) | ||
402 | { | ||
403 | /* start streaming data */ | ||
404 | ov534_set_led(gspca_dev, 1); | ||
405 | ov534_reg_write(gspca_dev, 0xe0, 0x00); | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static void sd_stopN(struct gspca_dev *gspca_dev) | ||
411 | { | ||
412 | /* stop streaming data */ | ||
413 | ov534_reg_write(gspca_dev, 0xe0, 0x09); | ||
414 | ov534_set_led(gspca_dev, 0); | ||
415 | } | ||
416 | |||
417 | /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ | ||
418 | #define UVC_STREAM_EOH (1 << 7) | ||
419 | #define UVC_STREAM_ERR (1 << 6) | ||
420 | #define UVC_STREAM_STI (1 << 5) | ||
421 | #define UVC_STREAM_RES (1 << 4) | ||
422 | #define UVC_STREAM_SCR (1 << 3) | ||
423 | #define UVC_STREAM_PTS (1 << 2) | ||
424 | #define UVC_STREAM_EOF (1 << 1) | ||
425 | #define UVC_STREAM_FID (1 << 0) | ||
426 | |||
427 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, | ||
428 | __u8 *data, int len) | ||
429 | { | ||
430 | struct sd *sd = (struct sd *) gspca_dev; | ||
431 | __u32 this_pts; | ||
432 | int this_fid; | ||
433 | int remaining_len = len; | ||
434 | __u8 *next_data = data; | ||
435 | |||
436 | scan_next: | ||
437 | if (remaining_len <= 0) | ||
438 | return; | ||
439 | |||
440 | data = next_data; | ||
441 | len = min(remaining_len, 2048); | ||
442 | remaining_len -= len; | ||
443 | next_data += len; | ||
444 | |||
445 | /* Payloads are prefixed with a UVC-style header. We | ||
446 | consider a frame to start when the FID toggles, or the PTS | ||
447 | changes. A frame ends when EOF is set, and we've received | ||
448 | the correct number of bytes. */ | ||
449 | |||
450 | /* Verify UVC header. Header length is always 12 */ | ||
451 | if (data[0] != 12 || len < 12) { | ||
452 | PDEBUG(D_PACK, "bad header"); | ||
453 | goto discard; | ||
454 | } | ||
455 | |||
456 | /* Check errors */ | ||
457 | if (data[1] & UVC_STREAM_ERR) { | ||
458 | PDEBUG(D_PACK, "payload error"); | ||
459 | goto discard; | ||
460 | } | ||
461 | |||
462 | /* Extract PTS and FID */ | ||
463 | if (!(data[1] & UVC_STREAM_PTS)) { | ||
464 | PDEBUG(D_PACK, "PTS not present"); | ||
465 | goto discard; | ||
466 | } | ||
467 | this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2]; | ||
468 | this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; | ||
469 | |||
470 | /* If PTS or FID has changed, start a new frame. */ | ||
471 | if (this_pts != sd->last_pts || this_fid != sd->last_fid) { | ||
472 | gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); | ||
473 | sd->last_pts = this_pts; | ||
474 | sd->last_fid = this_fid; | ||
475 | } | ||
476 | |||
477 | /* Add the data from this payload */ | ||
478 | gspca_frame_add(gspca_dev, INTER_PACKET, frame, | ||
479 | data + 12, len - 12); | ||
480 | |||
481 | /* If this packet is marked as EOF, end the frame */ | ||
482 | if (data[1] & UVC_STREAM_EOF) { | ||
483 | sd->last_pts = 0; | ||
484 | |||
485 | if ((frame->data_end - frame->data) != | ||
486 | (gspca_dev->width * gspca_dev->height * 2)) { | ||
487 | PDEBUG(D_PACK, "short frame"); | ||
488 | goto discard; | ||
489 | } | ||
490 | |||
491 | gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0); | ||
492 | } | ||
493 | |||
494 | /* Done this payload */ | ||
495 | goto scan_next; | ||
496 | |||
497 | discard: | ||
498 | /* Discard data until a new frame starts. */ | ||
499 | gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); | ||
500 | goto scan_next; | ||
501 | } | ||
502 | |||
503 | /* get stream parameters (framerate) */ | ||
504 | static int sd_get_streamparm(struct gspca_dev *gspca_dev, | ||
505 | struct v4l2_streamparm *parm) | ||
506 | { | ||
507 | struct v4l2_captureparm *cp = &parm->parm.capture; | ||
508 | struct v4l2_fract *tpf = &cp->timeperframe; | ||
509 | struct sd *sd = (struct sd *) gspca_dev; | ||
510 | |||
511 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
512 | return -EINVAL; | ||
513 | |||
514 | cp->capability |= V4L2_CAP_TIMEPERFRAME; | ||
515 | tpf->numerator = 1; | ||
516 | tpf->denominator = sd->frame_rate; | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | /* set stream parameters (framerate) */ | ||
522 | static int sd_set_streamparm(struct gspca_dev *gspca_dev, | ||
523 | struct v4l2_streamparm *parm) | ||
524 | { | ||
525 | struct v4l2_captureparm *cp = &parm->parm.capture; | ||
526 | struct v4l2_fract *tpf = &cp->timeperframe; | ||
527 | struct sd *sd = (struct sd *) gspca_dev; | ||
528 | |||
529 | if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
530 | return -EINVAL; | ||
531 | |||
532 | /* Set requested framerate */ | ||
533 | sd->frame_rate = tpf->denominator / tpf->numerator; | ||
534 | ov534_set_frame_rate(gspca_dev); | ||
535 | |||
536 | /* Return the actual framerate */ | ||
537 | tpf->numerator = 1; | ||
538 | tpf->denominator = sd->frame_rate; | ||
539 | |||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | /* sub-driver description */ | ||
544 | static const struct sd_desc sd_desc = { | ||
545 | .name = MODULE_NAME, | ||
546 | .ctrls = sd_ctrls, | ||
547 | .nctrls = ARRAY_SIZE(sd_ctrls), | ||
548 | .config = sd_config, | ||
549 | .init = sd_init, | ||
550 | .start = sd_start, | ||
551 | .stopN = sd_stopN, | ||
552 | .pkt_scan = sd_pkt_scan, | ||
553 | .get_streamparm = sd_get_streamparm, | ||
554 | .set_streamparm = sd_set_streamparm, | ||
555 | }; | ||
556 | |||
557 | /* -- module initialisation -- */ | ||
558 | static const __devinitdata struct usb_device_id device_table[] = { | ||
559 | {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */ | ||
560 | {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */ | ||
561 | {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */ | ||
562 | {} | ||
563 | }; | ||
564 | |||
565 | MODULE_DEVICE_TABLE(usb, device_table); | ||
566 | |||
567 | /* -- device connect -- */ | ||
568 | static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) | ||
569 | { | ||
570 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | ||
571 | THIS_MODULE); | ||
572 | } | ||
573 | |||
574 | static struct usb_driver sd_driver = { | ||
575 | .name = MODULE_NAME, | ||
576 | .id_table = device_table, | ||
577 | .probe = sd_probe, | ||
578 | .disconnect = gspca_disconnect, | ||
579 | #ifdef CONFIG_PM | ||
580 | .suspend = gspca_suspend, | ||
581 | .resume = gspca_resume, | ||
582 | #endif | ||
583 | }; | ||
584 | |||
585 | /* -- module insert / remove -- */ | ||
586 | static int __init sd_mod_init(void) | ||
587 | { | ||
588 | if (usb_register(&sd_driver) < 0) | ||
589 | return -1; | ||
590 | PDEBUG(D_PROBE, "registered"); | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static void __exit sd_mod_exit(void) | ||
595 | { | ||
596 | usb_deregister(&sd_driver); | ||
597 | PDEBUG(D_PROBE, "deregistered"); | ||
598 | } | ||
599 | |||
600 | module_init(sd_mod_init); | ||
601 | module_exit(sd_mod_exit); | ||