Commit | Line | Data |
---|---|---|
fbb4c6d2 AO |
1 | /* |
2 | * ov534/ov772x gspca driver | |
3 | * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> | |
4 | * | |
5 | * Based on a prototype written by Mark Ferrell <majortrips@gmail.com> | |
6 | * USB protocol reverse engineered by Jim Paris <jim@jtan.com> | |
7 | * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | #define MODULE_NAME "ov534" | |
25 | ||
26 | #include "gspca.h" | |
27 | ||
28 | #define OV534_REG_ADDRESS 0xf1 /* ? */ | |
29 | #define OV534_REG_SUBADDR 0xf2 | |
30 | #define OV534_REG_WRITE 0xf3 | |
31 | #define OV534_REG_READ 0xf4 | |
32 | #define OV534_REG_OPERATION 0xf5 | |
33 | #define OV534_REG_STATUS 0xf6 | |
34 | ||
35 | #define OV534_OP_WRITE_3 0x37 | |
36 | #define OV534_OP_WRITE_2 0x33 | |
37 | #define OV534_OP_READ_2 0xf9 | |
38 | ||
39 | #define CTRL_TIMEOUT 500 | |
40 | ||
41 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | |
42 | MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver"); | |
43 | MODULE_LICENSE("GPL"); | |
44 | ||
45 | /* global parameters */ | |
46 | static int frame_rate; | |
47 | ||
48 | /* specific webcam descriptor */ | |
49 | struct sd { | |
50 | struct gspca_dev gspca_dev; /* !! must be the first item */ | |
fbb4c6d2 AO |
51 | }; |
52 | ||
53 | /* V4L2 controls supported by the driver */ | |
54 | static struct ctrl sd_ctrls[] = { | |
55 | }; | |
56 | ||
57 | static struct v4l2_pix_format vga_mode[] = { | |
58 | {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, | |
59 | .bytesperline = 640 * 2, | |
60 | .sizeimage = 640 * 480 * 2, | |
61 | .colorspace = V4L2_COLORSPACE_JPEG, | |
62 | .priv = 0}, | |
63 | }; | |
64 | ||
9e1e7b06 | 65 | static void ov534_reg_write(struct usb_device *udev, u16 reg, u8 val) |
fbb4c6d2 | 66 | { |
9e1e7b06 | 67 | u8 data = val; |
fbb4c6d2 AO |
68 | int ret; |
69 | ||
9e1e7b06 | 70 | PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); |
fbb4c6d2 AO |
71 | ret = usb_control_msg(udev, |
72 | usb_sndctrlpipe(udev, 0), | |
73 | 0x1, | |
74 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
75 | 0x0, reg, &data, 1, CTRL_TIMEOUT); | |
76 | if (ret < 0) | |
77 | PDEBUG(D_ERR, "write failed"); | |
78 | } | |
79 | ||
9e1e7b06 | 80 | static u8 ov534_reg_read(struct usb_device *udev, u16 reg) |
fbb4c6d2 | 81 | { |
9e1e7b06 | 82 | u8 data; |
fbb4c6d2 AO |
83 | int ret; |
84 | ||
85 | ret = usb_control_msg(udev, | |
86 | usb_rcvctrlpipe(udev, 0), | |
87 | 0x1, | |
88 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
89 | 0x0, reg, &data, 1, CTRL_TIMEOUT); | |
9e1e7b06 | 90 | PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, data); |
fbb4c6d2 AO |
91 | if (ret < 0) |
92 | PDEBUG(D_ERR, "read failed"); | |
93 | return data; | |
94 | } | |
95 | ||
fbb4c6d2 AO |
96 | /* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. |
97 | * (direction and output)? */ | |
98 | static void ov534_set_led(struct usb_device *udev, int status) | |
99 | { | |
9e1e7b06 | 100 | u8 data; |
fbb4c6d2 AO |
101 | |
102 | PDEBUG(D_CONF, "led status: %d", status); | |
103 | ||
104 | data = ov534_reg_read(udev, 0x21); | |
105 | data |= 0x80; | |
106 | ov534_reg_write(udev, 0x21, data); | |
107 | ||
108 | data = ov534_reg_read(udev, 0x23); | |
109 | if (status) | |
110 | data |= 0x80; | |
111 | else | |
112 | data &= ~(0x80); | |
113 | ||
114 | ov534_reg_write(udev, 0x23, data); | |
115 | } | |
116 | ||
117 | static int sccb_check_status(struct usb_device *udev) | |
118 | { | |
9e1e7b06 | 119 | u8 data; |
fbb4c6d2 AO |
120 | int i; |
121 | ||
122 | for (i = 0; i < 5; i++) { | |
123 | data = ov534_reg_read(udev, OV534_REG_STATUS); | |
124 | ||
9e1e7b06 | 125 | switch (data) { |
fbb4c6d2 AO |
126 | case 0x00: |
127 | return 1; | |
128 | case 0x04: | |
129 | return 0; | |
130 | case 0x03: | |
131 | break; | |
132 | default: | |
133 | PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5\n", | |
134 | data, i + 1); | |
135 | } | |
136 | } | |
137 | return 0; | |
138 | } | |
139 | ||
9e1e7b06 | 140 | static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val) |
fbb4c6d2 | 141 | { |
9e1e7b06 | 142 | PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val); |
fbb4c6d2 AO |
143 | ov534_reg_write(udev, OV534_REG_SUBADDR, reg); |
144 | ov534_reg_write(udev, OV534_REG_WRITE, val); | |
145 | ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_3); | |
146 | ||
147 | if (!sccb_check_status(udev)) | |
148 | PDEBUG(D_ERR, "sccb_reg_write failed"); | |
149 | } | |
150 | ||
47dfd21f JP |
151 | static const __u8 ov534_reg_initdata[][2] = { |
152 | { 0xe7, 0x3a }, | |
153 | ||
154 | { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */ | |
155 | ||
156 | { 0xc2, 0x0c }, | |
157 | { 0x88, 0xf8 }, | |
158 | { 0xc3, 0x69 }, | |
159 | { 0x89, 0xff }, | |
160 | { 0x76, 0x03 }, | |
161 | { 0x92, 0x01 }, | |
162 | { 0x93, 0x18 }, | |
163 | { 0x94, 0x10 }, | |
164 | { 0x95, 0x10 }, | |
165 | { 0xe2, 0x00 }, | |
166 | { 0xe7, 0x3e }, | |
167 | ||
168 | { 0x1c, 0x0a }, | |
169 | { 0x1d, 0x22 }, | |
170 | { 0x1d, 0x06 }, | |
171 | ||
172 | { 0x96, 0x00 }, | |
173 | ||
174 | { 0x97, 0x20 }, | |
175 | { 0x97, 0x20 }, | |
176 | { 0x97, 0x20 }, | |
177 | { 0x97, 0x0a }, | |
178 | { 0x97, 0x3f }, | |
179 | { 0x97, 0x4a }, | |
180 | { 0x97, 0x20 }, | |
181 | { 0x97, 0x15 }, | |
182 | { 0x97, 0x0b }, | |
183 | ||
184 | { 0x8e, 0x40 }, | |
185 | { 0x1f, 0x81 }, | |
186 | { 0x34, 0x05 }, | |
187 | { 0xe3, 0x04 }, | |
188 | { 0x88, 0x00 }, | |
189 | { 0x89, 0x00 }, | |
190 | { 0x76, 0x00 }, | |
191 | { 0xe7, 0x2e }, | |
192 | { 0x31, 0xf9 }, | |
193 | { 0x25, 0x42 }, | |
194 | { 0x21, 0xf0 }, | |
195 | ||
196 | { 0x1c, 0x00 }, | |
197 | { 0x1d, 0x40 }, | |
198 | { 0x1d, 0x02 }, | |
199 | { 0x1d, 0x00 }, | |
200 | { 0x1d, 0x02 }, | |
201 | { 0x1d, 0x57 }, | |
202 | { 0x1d, 0xff }, | |
203 | ||
204 | { 0x8d, 0x1c }, | |
205 | { 0x8e, 0x80 }, | |
206 | { 0xe5, 0x04 }, | |
207 | ||
208 | { 0xc0, 0x50 }, | |
209 | { 0xc1, 0x3c }, | |
210 | { 0xc2, 0x0c }, | |
211 | }; | |
fbb4c6d2 | 212 | |
47dfd21f JP |
213 | static const __u8 ov772x_reg_initdata[][2] = { |
214 | { 0x12, 0x80 }, | |
215 | { 0x11, 0x01 }, | |
216 | ||
217 | { 0x3d, 0x03 }, | |
218 | { 0x17, 0x26 }, | |
219 | { 0x18, 0xa0 }, | |
220 | { 0x19, 0x07 }, | |
221 | { 0x1a, 0xf0 }, | |
222 | { 0x32, 0x00 }, | |
223 | { 0x29, 0xa0 }, | |
224 | { 0x2c, 0xf0 }, | |
225 | { 0x65, 0x20 }, | |
226 | { 0x11, 0x01 }, | |
227 | { 0x42, 0x7f }, | |
228 | { 0x63, 0xe0 }, | |
229 | { 0x64, 0xff }, | |
230 | { 0x66, 0x00 }, | |
231 | { 0x13, 0xf0 }, | |
232 | { 0x0d, 0x41 }, | |
233 | { 0x0f, 0xc5 }, | |
234 | { 0x14, 0x11 }, | |
235 | ||
236 | { 0x22, 0x7f }, | |
237 | { 0x23, 0x03 }, | |
238 | { 0x24, 0x40 }, | |
239 | { 0x25, 0x30 }, | |
240 | { 0x26, 0xa1 }, | |
241 | { 0x2a, 0x00 }, | |
242 | { 0x2b, 0x00 }, | |
243 | { 0x6b, 0xaa }, | |
244 | { 0x13, 0xff }, | |
245 | ||
246 | { 0x90, 0x05 }, | |
247 | { 0x91, 0x01 }, | |
248 | { 0x92, 0x03 }, | |
249 | { 0x93, 0x00 }, | |
250 | { 0x94, 0x60 }, | |
251 | { 0x95, 0x3c }, | |
252 | { 0x96, 0x24 }, | |
253 | { 0x97, 0x1e }, | |
254 | { 0x98, 0x62 }, | |
255 | { 0x99, 0x80 }, | |
256 | { 0x9a, 0x1e }, | |
257 | { 0x9b, 0x08 }, | |
258 | { 0x9c, 0x20 }, | |
259 | { 0x9e, 0x81 }, | |
260 | ||
261 | { 0xa6, 0x04 }, | |
262 | { 0x7e, 0x0c }, | |
263 | { 0x7f, 0x16 }, | |
264 | { 0x80, 0x2a }, | |
265 | { 0x81, 0x4e }, | |
266 | { 0x82, 0x61 }, | |
267 | { 0x83, 0x6f }, | |
268 | { 0x84, 0x7b }, | |
269 | { 0x85, 0x86 }, | |
270 | { 0x86, 0x8e }, | |
271 | { 0x87, 0x97 }, | |
272 | { 0x88, 0xa4 }, | |
273 | { 0x89, 0xaf }, | |
274 | { 0x8a, 0xc5 }, | |
275 | { 0x8b, 0xd7 }, | |
276 | { 0x8c, 0xe8 }, | |
277 | { 0x8d, 0x20 }, | |
278 | ||
279 | { 0x0c, 0x90 }, | |
280 | ||
281 | { 0x2b, 0x00 }, | |
282 | { 0x22, 0x7f }, | |
283 | { 0x23, 0x03 }, | |
284 | { 0x11, 0x01 }, | |
285 | { 0x0c, 0xd0 }, | |
286 | { 0x64, 0xff }, | |
287 | { 0x0d, 0x41 }, | |
288 | ||
289 | { 0x14, 0x41 }, | |
290 | { 0x0e, 0xcd }, | |
291 | { 0xac, 0xbf }, | |
292 | { 0x8e, 0x00 }, | |
293 | { 0x0c, 0xd0 } | |
294 | }; | |
fbb4c6d2 | 295 | |
fbb4c6d2 | 296 | |
47dfd21f JP |
297 | /* setup method */ |
298 | static void ov534_setup(struct usb_device *udev) | |
299 | { | |
300 | int i; | |
fbb4c6d2 | 301 | |
47dfd21f JP |
302 | /* Initialize bridge chip */ |
303 | for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++) | |
304 | ov534_reg_write(udev, ov534_reg_initdata[i][0], | |
305 | ov534_reg_initdata[i][1]); | |
fbb4c6d2 AO |
306 | |
307 | ov534_set_led(udev, 1); | |
308 | ||
47dfd21f JP |
309 | /* Initialize sensor */ |
310 | for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++) | |
311 | sccb_reg_write(udev, ov772x_reg_initdata[i][0], | |
312 | ov772x_reg_initdata[i][1]); | |
fbb4c6d2 AO |
313 | |
314 | ov534_reg_write(udev, 0xe0, 0x09); | |
315 | ov534_set_led(udev, 0); | |
316 | } | |
317 | ||
318 | /* this function is called at probe time */ | |
319 | static int sd_config(struct gspca_dev *gspca_dev, | |
320 | const struct usb_device_id *id) | |
321 | { | |
322 | struct cam *cam; | |
323 | ||
324 | cam = &gspca_dev->cam; | |
325 | ||
326 | cam->epaddr = 0x01; | |
327 | cam->cam_mode = vga_mode; | |
328 | cam->nmodes = ARRAY_SIZE(vga_mode); | |
329 | ||
442a43f5 AO |
330 | /* |
331 | * On some architectures we need contiguous memory for urb buffers, and | |
332 | * in low memory situation 'sizeimage' can be too much. | |
333 | * 16kiB chunks should be available even when we are low in memory. | |
334 | * TODO: CHECK this description: is the problem arch-dependent or more | |
335 | * general? | |
336 | */ | |
337 | cam->bulk_size = 16 * 1024; | |
fbb4c6d2 AO |
338 | cam->bulk_nurbs = 2; |
339 | ||
340 | PDEBUG(D_PROBE, "bulk_size = %d", cam->bulk_size); | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
345 | /* this function is called at probe and resume time */ | |
346 | static int sd_init(struct gspca_dev *gspca_dev) | |
347 | { | |
3adba442 | 348 | int fr; |
fbb4c6d2 | 349 | |
3adba442 | 350 | ov534_setup(gspca_dev->dev); |
fbb4c6d2 | 351 | |
3adba442 | 352 | fr = frame_rate; |
fbb4c6d2 | 353 | |
3adba442 | 354 | switch (fr) { |
fbb4c6d2 AO |
355 | case 50: |
356 | sccb_reg_write(gspca_dev->dev, 0x11, 0x01); | |
fbb4c6d2 | 357 | sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); |
47dfd21f | 358 | ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); |
fbb4c6d2 AO |
359 | break; |
360 | case 40: | |
361 | sccb_reg_write(gspca_dev->dev, 0x11, 0x02); | |
fbb4c6d2 | 362 | sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1); |
47dfd21f | 363 | ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); |
fbb4c6d2 | 364 | break; |
3adba442 | 365 | /* case 30: */ |
fbb4c6d2 | 366 | default: |
3adba442 | 367 | fr = 30; |
fbb4c6d2 | 368 | sccb_reg_write(gspca_dev->dev, 0x11, 0x04); |
fbb4c6d2 | 369 | sccb_reg_write(gspca_dev->dev, 0x0d, 0x81); |
47dfd21f | 370 | ov534_reg_write(gspca_dev->dev, 0xe5, 0x02); |
fbb4c6d2 AO |
371 | break; |
372 | case 15: | |
373 | sccb_reg_write(gspca_dev->dev, 0x11, 0x03); | |
fbb4c6d2 | 374 | sccb_reg_write(gspca_dev->dev, 0x0d, 0x41); |
47dfd21f | 375 | ov534_reg_write(gspca_dev->dev, 0xe5, 0x04); |
fbb4c6d2 | 376 | break; |
3adba442 AO |
377 | } |
378 | ||
379 | PDEBUG(D_PROBE, "frame_rate: %d", fr); | |
fbb4c6d2 AO |
380 | |
381 | return 0; | |
382 | } | |
383 | ||
384 | static int sd_start(struct gspca_dev *gspca_dev) | |
385 | { | |
442a43f5 | 386 | struct gspca_frame *frame; |
fbb4c6d2 AO |
387 | |
388 | /* start streaming data */ | |
389 | ov534_set_led(gspca_dev->dev, 1); | |
390 | ov534_reg_write(gspca_dev->dev, 0xe0, 0x00); | |
391 | ||
442a43f5 AO |
392 | frame = gspca_get_i_frame(gspca_dev); |
393 | if (frame == NULL) { | |
394 | PDEBUG(D_ERR, "NULL frame!"); | |
395 | return -1; | |
396 | } | |
397 | gspca_frame_add(gspca_dev, FIRST_PACKET, frame, gspca_dev->usb_buf, 0); | |
398 | ||
fbb4c6d2 AO |
399 | return 0; |
400 | } | |
401 | ||
402 | static void sd_stopN(struct gspca_dev *gspca_dev) | |
403 | { | |
404 | /* stop streaming data */ | |
405 | ov534_reg_write(gspca_dev->dev, 0xe0, 0x09); | |
406 | ov534_set_led(gspca_dev->dev, 0); | |
407 | } | |
408 | ||
409 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, | |
410 | __u8 *data, int len) | |
411 | { | |
412 | /* | |
413 | * The current camera setup doesn't stream the last pixel, so we set it | |
414 | * to a dummy value | |
415 | */ | |
442a43f5 AO |
416 | __u8 last[4] = { 0, 0, 0, 0 }; |
417 | int framesize = frame->v4l2_buf.length; | |
418 | ||
419 | PDEBUG(D_PACK, ""); | |
420 | PDEBUG(D_PACK, "** packet len = %d, framesize = %d", len, framesize); | |
421 | PDEBUG(D_PACK, "** frame->data_end - frame->data + len = %d", | |
422 | frame->data_end - frame->data + len); | |
423 | ||
424 | if (frame->data_end - frame->data + len == framesize - 4) { | |
425 | PDEBUG(D_PACK, " end of frame!"); | |
426 | gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); | |
427 | frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, last, 4); | |
428 | gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); | |
fbb4c6d2 | 429 | } else |
442a43f5 | 430 | gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); |
fbb4c6d2 AO |
431 | } |
432 | ||
433 | /* sub-driver description */ | |
434 | static const struct sd_desc sd_desc = { | |
435 | .name = MODULE_NAME, | |
436 | .ctrls = sd_ctrls, | |
437 | .nctrls = ARRAY_SIZE(sd_ctrls), | |
438 | .config = sd_config, | |
439 | .init = sd_init, | |
440 | .start = sd_start, | |
441 | .stopN = sd_stopN, | |
442 | .pkt_scan = sd_pkt_scan, | |
443 | }; | |
444 | ||
445 | /* -- module initialisation -- */ | |
446 | static const __devinitdata struct usb_device_id device_table[] = { | |
447 | {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */ | |
448 | {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */ | |
449 | {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */ | |
450 | {} | |
451 | }; | |
452 | ||
453 | MODULE_DEVICE_TABLE(usb, device_table); | |
454 | ||
455 | /* -- device connect -- */ | |
456 | static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) | |
457 | { | |
458 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | |
459 | THIS_MODULE); | |
460 | } | |
461 | ||
462 | static struct usb_driver sd_driver = { | |
463 | .name = MODULE_NAME, | |
464 | .id_table = device_table, | |
465 | .probe = sd_probe, | |
466 | .disconnect = gspca_disconnect, | |
467 | #ifdef CONFIG_PM | |
468 | .suspend = gspca_suspend, | |
469 | .resume = gspca_resume, | |
470 | #endif | |
471 | }; | |
472 | ||
473 | /* -- module insert / remove -- */ | |
474 | static int __init sd_mod_init(void) | |
475 | { | |
476 | if (usb_register(&sd_driver) < 0) | |
477 | return -1; | |
478 | PDEBUG(D_PROBE, "registered"); | |
479 | return 0; | |
480 | } | |
481 | ||
482 | static void __exit sd_mod_exit(void) | |
483 | { | |
484 | usb_deregister(&sd_driver); | |
485 | PDEBUG(D_PROBE, "deregistered"); | |
486 | } | |
487 | ||
488 | module_init(sd_mod_init); | |
489 | module_exit(sd_mod_exit); | |
490 | ||
491 | module_param(frame_rate, int, 0644); | |
492 | MODULE_PARM_DESC(frame_rate, "Frame rate (15, 30, 40, 50)"); |