1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Driver for Philips webcam |
3 | Functions that send various control messages to the webcam, including |
4 | video modes. |
5 | (C) 1999-2003 Nemosoft Unv. |
6 | (C) 2004-2006 Luc Saillard (luc@saillard.org) |
7 | (C) 2011 Hans de Goede <hdegoede@redhat.com> |
8 | |
9 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx |
10 | driver and thus may have bugs that are not present in the original version. |
11 | Please send bug reports and support requests to <luc@saillard.org>. |
12 | |
13 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx |
14 | driver and thus may have bugs that are not present in the original version. |
15 | Please send bug reports and support requests to <luc@saillard.org>. |
16 | The decompression routines have been implemented by reverse-engineering the |
17 | Nemosoft binary pwcx module. Caveat emptor. |
18 | |
19 | */ |
20 | |
21 | /* |
22 | Changes |
23 | 2001/08/03 Alvarado Added methods for changing white balance and |
24 | red/green gains |
25 | */ |
26 | |
27 | /* Control functions for the cam; brightness, contrast, video mode, etc. */ |
28 | |
29 | #ifdef __KERNEL__ |
30 | #include <linux/uaccess.h> |
31 | #endif |
32 | #include <asm/errno.h> |
33 | |
34 | #include "pwc.h" |
35 | #include "pwc-kiara.h" |
36 | #include "pwc-timon.h" |
37 | #include "pwc-dec1.h" |
38 | #include "pwc-dec23.h" |
39 | |
40 | /* Selectors for status controls used only in this file */ |
41 | #define GET_STATUS_B00 0x0B00 |
42 | #define SENSOR_TYPE_FORMATTER1 0x0C00 |
43 | #define GET_STATUS_3000 0x3000 |
44 | #define READ_RAW_Y_MEAN_FORMATTER 0x3100 |
45 | #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 |
46 | #define MIRROR_IMAGE_FORMATTER 0x3300 |
47 | #define LED_FORMATTER 0x3400 |
48 | #define LOWLIGHT 0x3500 |
49 | #define GET_STATUS_3600 0x3600 |
50 | #define SENSOR_TYPE_FORMATTER2 0x3700 |
51 | #define GET_STATUS_3800 0x3800 |
52 | #define GET_STATUS_4000 0x4000 |
53 | #define GET_STATUS_4100 0x4100 /* Get */ |
54 | #define CTL_STATUS_4200 0x4200 /* [GS] 1 */ |
55 | |
56 | /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ |
57 | #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 |
58 | |
59 | static const char *size2name[PSZ_MAX] = |
60 | { |
61 | "subQCIF" , |
62 | "QSIF" , |
63 | "QCIF" , |
64 | "SIF" , |
65 | "CIF" , |
66 | "VGA" , |
67 | }; |
68 | |
69 | /********/ |
70 | |
71 | /* Entries for the Nala (645/646) camera; the Nala doesn't have compression |
72 | preferences, so you either get compressed or non-compressed streams. |
73 | |
74 | An alternate value of 0 means this mode is not available at all. |
75 | */ |
76 | |
77 | #define PWC_FPS_MAX_NALA 8 |
78 | |
79 | struct Nala_table_entry { |
80 | char alternate; /* USB alternate setting */ |
81 | int compressed; /* Compressed yes/no */ |
82 | |
83 | unsigned char mode[3]; /* precomputed mode table */ |
84 | }; |
85 | |
86 | static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; |
87 | |
88 | static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = |
89 | { |
90 | #include "pwc-nala.h" |
91 | }; |
92 | |
93 | /****************************************************************************/ |
94 | |
95 | static int recv_control_msg(struct pwc_device *pdev, |
96 | u8 request, u16 value, int recv_count) |
97 | { |
98 | int rc; |
99 | |
100 | rc = usb_control_msg(dev: pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), |
101 | request, |
102 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
103 | value, index: pdev->vcinterface, |
104 | data: pdev->ctrl_buf, size: recv_count, USB_CTRL_GET_TIMEOUT); |
105 | if (rc < 0) |
106 | PWC_ERROR("recv_control_msg error %d req %02x val %04x\n" , |
107 | rc, request, value); |
108 | return rc; |
109 | } |
110 | |
111 | static inline int send_video_command(struct pwc_device *pdev, |
112 | int index, const unsigned char *buf, int buflen) |
113 | { |
114 | int rc; |
115 | |
116 | memcpy(pdev->ctrl_buf, buf, buflen); |
117 | |
118 | rc = usb_control_msg(dev: pdev->udev, usb_sndctrlpipe(pdev->udev, 0), |
119 | SET_EP_STREAM_CTL, |
120 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
121 | VIDEO_OUTPUT_CONTROL_FORMATTER, index, |
122 | data: pdev->ctrl_buf, size: buflen, USB_CTRL_SET_TIMEOUT); |
123 | if (rc >= 0) |
124 | memcpy(pdev->cmd_buf, buf, buflen); |
125 | else |
126 | PWC_ERROR("send_video_command error %d\n" , rc); |
127 | |
128 | return rc; |
129 | } |
130 | |
131 | int send_control_msg(struct pwc_device *pdev, |
132 | u8 request, u16 value, void *buf, int buflen) |
133 | { |
134 | return usb_control_msg(dev: pdev->udev, usb_sndctrlpipe(pdev->udev, 0), |
135 | request, |
136 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
137 | value, index: pdev->vcinterface, |
138 | data: buf, size: buflen, USB_CTRL_SET_TIMEOUT); |
139 | } |
140 | |
141 | static int set_video_mode_Nala(struct pwc_device *pdev, int size, int pixfmt, |
142 | int frames, int *compression, int send_to_cam) |
143 | { |
144 | int fps, ret = 0; |
145 | struct Nala_table_entry *pEntry; |
146 | int frames2frames[31] = |
147 | { /* closest match of framerate */ |
148 | 0, 0, 0, 0, 4, /* 0-4 */ |
149 | 5, 5, 7, 7, 10, /* 5-9 */ |
150 | 10, 10, 12, 12, 15, /* 10-14 */ |
151 | 15, 15, 15, 20, 20, /* 15-19 */ |
152 | 20, 20, 20, 24, 24, /* 20-24 */ |
153 | 24, 24, 24, 24, 24, /* 25-29 */ |
154 | 24 /* 30 */ |
155 | }; |
156 | int frames2table[31] = |
157 | { 0, 0, 0, 0, 0, /* 0-4 */ |
158 | 1, 1, 1, 2, 2, /* 5-9 */ |
159 | 3, 3, 4, 4, 4, /* 10-14 */ |
160 | 5, 5, 5, 5, 5, /* 15-19 */ |
161 | 6, 6, 6, 6, 7, /* 20-24 */ |
162 | 7, 7, 7, 7, 7, /* 25-29 */ |
163 | 7 /* 30 */ |
164 | }; |
165 | |
166 | if (size < 0 || size > PSZ_CIF) |
167 | return -EINVAL; |
168 | if (frames < 4) |
169 | frames = 4; |
170 | else if (size > PSZ_QCIF && frames > 15) |
171 | frames = 15; |
172 | else if (frames > 25) |
173 | frames = 25; |
174 | frames = frames2frames[frames]; |
175 | fps = frames2table[frames]; |
176 | pEntry = &Nala_table[size][fps]; |
177 | if (pEntry->alternate == 0) |
178 | return -EINVAL; |
179 | |
180 | if (send_to_cam) |
181 | ret = send_video_command(pdev, index: pdev->vendpoint, |
182 | buf: pEntry->mode, buflen: 3); |
183 | if (ret < 0) |
184 | return ret; |
185 | |
186 | if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) |
187 | pwc_dec1_init(pdev, cmd: pEntry->mode); |
188 | |
189 | /* Set various parameters */ |
190 | pdev->pixfmt = pixfmt; |
191 | pdev->vframes = frames; |
192 | pdev->valternate = pEntry->alternate; |
193 | pdev->width = pwc_image_sizes[size][0]; |
194 | pdev->height = pwc_image_sizes[size][1]; |
195 | pdev->frame_size = (pdev->width * pdev->height * 3) / 2; |
196 | if (pEntry->compressed) { |
197 | if (pdev->release < 5) { /* 4 fold compression */ |
198 | pdev->vbandlength = 528; |
199 | pdev->frame_size /= 4; |
200 | } |
201 | else { |
202 | pdev->vbandlength = 704; |
203 | pdev->frame_size /= 3; |
204 | } |
205 | } |
206 | else |
207 | pdev->vbandlength = 0; |
208 | |
209 | /* Let pwc-if.c:isoc_init know we don't support higher compression */ |
210 | *compression = 3; |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | |
216 | static int set_video_mode_Timon(struct pwc_device *pdev, int size, int pixfmt, |
217 | int frames, int *compression, int send_to_cam) |
218 | { |
219 | const struct Timon_table_entry *pChoose; |
220 | int fps, ret = 0; |
221 | |
222 | if (size >= PSZ_MAX || *compression < 0 || *compression > 3) |
223 | return -EINVAL; |
224 | if (frames < 5) |
225 | frames = 5; |
226 | else if (size == PSZ_VGA && frames > 15) |
227 | frames = 15; |
228 | else if (frames > 30) |
229 | frames = 30; |
230 | fps = (frames / 5) - 1; |
231 | |
232 | /* Find a supported framerate with progressively higher compression */ |
233 | do { |
234 | pChoose = &Timon_table[size][fps][*compression]; |
235 | if (pChoose->alternate != 0) |
236 | break; |
237 | (*compression)++; |
238 | } while (*compression <= 3); |
239 | |
240 | if (pChoose->alternate == 0) |
241 | return -ENOENT; /* Not supported. */ |
242 | |
243 | if (send_to_cam) |
244 | ret = send_video_command(pdev, index: pdev->vendpoint, |
245 | buf: pChoose->mode, buflen: 13); |
246 | if (ret < 0) |
247 | return ret; |
248 | |
249 | if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) |
250 | pwc_dec23_init(pdev, cmd: pChoose->mode); |
251 | |
252 | /* Set various parameters */ |
253 | pdev->pixfmt = pixfmt; |
254 | pdev->vframes = (fps + 1) * 5; |
255 | pdev->valternate = pChoose->alternate; |
256 | pdev->width = pwc_image_sizes[size][0]; |
257 | pdev->height = pwc_image_sizes[size][1]; |
258 | pdev->vbandlength = pChoose->bandlength; |
259 | if (pChoose->bandlength > 0) |
260 | pdev->frame_size = (pChoose->bandlength * pdev->height) / 4; |
261 | else |
262 | pdev->frame_size = (pdev->width * pdev->height * 12) / 8; |
263 | return 0; |
264 | } |
265 | |
266 | |
267 | static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int pixfmt, |
268 | int frames, int *compression, int send_to_cam) |
269 | { |
270 | const struct Kiara_table_entry *pChoose; |
271 | int fps, ret = 0; |
272 | |
273 | if (size >= PSZ_MAX || *compression < 0 || *compression > 3) |
274 | return -EINVAL; |
275 | if (frames < 5) |
276 | frames = 5; |
277 | else if (size == PSZ_VGA && frames > 15) |
278 | frames = 15; |
279 | else if (frames > 30) |
280 | frames = 30; |
281 | fps = (frames / 5) - 1; |
282 | |
283 | /* Find a supported framerate with progressively higher compression */ |
284 | do { |
285 | pChoose = &Kiara_table[size][fps][*compression]; |
286 | if (pChoose->alternate != 0) |
287 | break; |
288 | (*compression)++; |
289 | } while (*compression <= 3); |
290 | |
291 | if (pChoose->alternate == 0) |
292 | return -ENOENT; /* Not supported. */ |
293 | |
294 | /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ |
295 | if (send_to_cam) |
296 | ret = send_video_command(pdev, index: 4, buf: pChoose->mode, buflen: 12); |
297 | if (ret < 0) |
298 | return ret; |
299 | |
300 | if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) |
301 | pwc_dec23_init(pdev, cmd: pChoose->mode); |
302 | |
303 | /* All set and go */ |
304 | pdev->pixfmt = pixfmt; |
305 | pdev->vframes = (fps + 1) * 5; |
306 | pdev->valternate = pChoose->alternate; |
307 | pdev->width = pwc_image_sizes[size][0]; |
308 | pdev->height = pwc_image_sizes[size][1]; |
309 | pdev->vbandlength = pChoose->bandlength; |
310 | if (pdev->vbandlength > 0) |
311 | pdev->frame_size = (pdev->vbandlength * pdev->height) / 4; |
312 | else |
313 | pdev->frame_size = (pdev->width * pdev->height * 12) / 8; |
314 | PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vbandlength=%d\n" , |
315 | pdev->frame_size, pdev->vframes, size, pdev->vbandlength); |
316 | return 0; |
317 | } |
318 | |
319 | int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, |
320 | int pixfmt, int frames, int *compression, int send_to_cam) |
321 | { |
322 | int ret, size; |
323 | |
324 | PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n" , |
325 | width, height, frames, pixfmt); |
326 | size = pwc_get_size(pdev, width, height); |
327 | PWC_TRACE("decode_size = %d.\n" , size); |
328 | |
329 | if (DEVICE_USE_CODEC1(pdev->type)) { |
330 | ret = set_video_mode_Nala(pdev, size, pixfmt, frames, |
331 | compression, send_to_cam); |
332 | } else if (DEVICE_USE_CODEC3(pdev->type)) { |
333 | ret = set_video_mode_Kiara(pdev, size, pixfmt, frames, |
334 | compression, send_to_cam); |
335 | } else { |
336 | ret = set_video_mode_Timon(pdev, size, pixfmt, frames, |
337 | compression, send_to_cam); |
338 | } |
339 | if (ret < 0) { |
340 | PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n" , size2name[size], frames, ret); |
341 | return ret; |
342 | } |
343 | pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; |
344 | PWC_DEBUG_SIZE("Set resolution to %dx%d\n" , pdev->width, pdev->height); |
345 | return 0; |
346 | } |
347 | |
348 | static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) |
349 | { |
350 | unsigned int i; |
351 | |
352 | for (i = 0; i < PWC_FPS_MAX_NALA; i++) { |
353 | if (Nala_table[size][i].alternate) { |
354 | if (index--==0) return Nala_fps_vector[i]; |
355 | } |
356 | } |
357 | return 0; |
358 | } |
359 | |
360 | static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) |
361 | { |
362 | unsigned int i; |
363 | |
364 | for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { |
365 | if (Kiara_table[size][i][3].alternate) { |
366 | if (index--==0) return Kiara_fps_vector[i]; |
367 | } |
368 | } |
369 | return 0; |
370 | } |
371 | |
372 | static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) |
373 | { |
374 | unsigned int i; |
375 | |
376 | for (i=0; i < PWC_FPS_MAX_TIMON; i++) { |
377 | if (Timon_table[size][i][3].alternate) { |
378 | if (index--==0) return Timon_fps_vector[i]; |
379 | } |
380 | } |
381 | return 0; |
382 | } |
383 | |
384 | unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) |
385 | { |
386 | unsigned int ret; |
387 | |
388 | if (DEVICE_USE_CODEC1(pdev->type)) { |
389 | ret = pwc_get_fps_Nala(pdev, index, size); |
390 | |
391 | } else if (DEVICE_USE_CODEC3(pdev->type)) { |
392 | ret = pwc_get_fps_Kiara(pdev, index, size); |
393 | |
394 | } else { |
395 | ret = pwc_get_fps_Timon(pdev, index, size); |
396 | } |
397 | |
398 | return ret; |
399 | } |
400 | |
401 | int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
402 | { |
403 | int ret; |
404 | |
405 | ret = recv_control_msg(pdev, request, value, recv_count: 1); |
406 | if (ret < 0) |
407 | return ret; |
408 | |
409 | *data = pdev->ctrl_buf[0]; |
410 | return 0; |
411 | } |
412 | |
413 | int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) |
414 | { |
415 | int ret; |
416 | |
417 | pdev->ctrl_buf[0] = data; |
418 | ret = send_control_msg(pdev, request, value, buf: pdev->ctrl_buf, buflen: 1); |
419 | if (ret < 0) |
420 | return ret; |
421 | |
422 | return 0; |
423 | } |
424 | |
425 | int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
426 | { |
427 | int ret; |
428 | |
429 | ret = recv_control_msg(pdev, request, value, recv_count: 1); |
430 | if (ret < 0) |
431 | return ret; |
432 | |
433 | *data = ((s8 *)pdev->ctrl_buf)[0]; |
434 | return 0; |
435 | } |
436 | |
437 | int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) |
438 | { |
439 | int ret; |
440 | |
441 | ret = recv_control_msg(pdev, request, value, recv_count: 2); |
442 | if (ret < 0) |
443 | return ret; |
444 | |
445 | *data = (pdev->ctrl_buf[1] << 8) | pdev->ctrl_buf[0]; |
446 | return 0; |
447 | } |
448 | |
449 | int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) |
450 | { |
451 | int ret; |
452 | |
453 | pdev->ctrl_buf[0] = data & 0xff; |
454 | pdev->ctrl_buf[1] = data >> 8; |
455 | ret = send_control_msg(pdev, request, value, buf: pdev->ctrl_buf, buflen: 2); |
456 | if (ret < 0) |
457 | return ret; |
458 | |
459 | return 0; |
460 | } |
461 | |
462 | int pwc_button_ctrl(struct pwc_device *pdev, u16 value) |
463 | { |
464 | int ret; |
465 | |
466 | ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, buflen: 0); |
467 | if (ret < 0) |
468 | return ret; |
469 | |
470 | return 0; |
471 | } |
472 | |
473 | /* POWER */ |
474 | void pwc_camera_power(struct pwc_device *pdev, int power) |
475 | { |
476 | int r; |
477 | |
478 | if (!pdev->power_save) |
479 | return; |
480 | |
481 | if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) |
482 | return; /* Not supported by Nala or Timon < release 6 */ |
483 | |
484 | if (power) |
485 | pdev->ctrl_buf[0] = 0x00; /* active */ |
486 | else |
487 | pdev->ctrl_buf[0] = 0xFF; /* power save */ |
488 | r = send_control_msg(pdev, SET_STATUS_CTL, |
489 | SET_POWER_SAVE_MODE_FORMATTER, buf: pdev->ctrl_buf, buflen: 1); |
490 | if (r < 0) |
491 | PWC_ERROR("Failed to power %s camera (%d)\n" , |
492 | power ? "on" : "off" , r); |
493 | } |
494 | |
495 | int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) |
496 | { |
497 | int r; |
498 | |
499 | if (pdev->type < 730) |
500 | return 0; |
501 | on_value /= 100; |
502 | off_value /= 100; |
503 | if (on_value < 0) |
504 | on_value = 0; |
505 | if (on_value > 0xff) |
506 | on_value = 0xff; |
507 | if (off_value < 0) |
508 | off_value = 0; |
509 | if (off_value > 0xff) |
510 | off_value = 0xff; |
511 | |
512 | pdev->ctrl_buf[0] = on_value; |
513 | pdev->ctrl_buf[1] = off_value; |
514 | |
515 | r = send_control_msg(pdev, |
516 | SET_STATUS_CTL, LED_FORMATTER, buf: pdev->ctrl_buf, buflen: 2); |
517 | if (r < 0) |
518 | PWC_ERROR("Failed to set LED on/off time (%d)\n" , r); |
519 | |
520 | return r; |
521 | } |
522 | |
523 | #ifdef CONFIG_USB_PWC_DEBUG |
524 | int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) |
525 | { |
526 | int ret, request; |
527 | |
528 | if (pdev->type < 675) |
529 | request = SENSOR_TYPE_FORMATTER1; |
530 | else if (pdev->type < 730) |
531 | return -1; /* The Vesta series doesn't have this call */ |
532 | else |
533 | request = SENSOR_TYPE_FORMATTER2; |
534 | |
535 | ret = recv_control_msg(pdev, GET_STATUS_CTL, value: request, recv_count: 1); |
536 | if (ret < 0) |
537 | return ret; |
538 | if (pdev->type < 675) |
539 | *sensor = pdev->ctrl_buf[0] | 0x100; |
540 | else |
541 | *sensor = pdev->ctrl_buf[0]; |
542 | return 0; |
543 | } |
544 | #endif |
545 | |