pwc-ctrl.c (13952B)
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 59static 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 79struct 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 86static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; 87 88static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = 89{ 90#include "pwc-nala.h" 91}; 92 93/****************************************************************************/ 94 95static 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(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), 101 request, 102 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 103 value, pdev->vcinterface, 104 pdev->ctrl_buf, 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 111static 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(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 pdev->ctrl_buf, 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 131int send_control_msg(struct pwc_device *pdev, 132 u8 request, u16 value, void *buf, int buflen) 133{ 134 return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), 135 request, 136 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 137 value, pdev->vcinterface, 138 buf, buflen, USB_CTRL_SET_TIMEOUT); 139} 140 141static 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, pdev->vendpoint, 182 pEntry->mode, 3); 183 if (ret < 0) 184 return ret; 185 186 if (pEntry->compressed && pixfmt == V4L2_PIX_FMT_YUV420) 187 pwc_dec1_init(pdev, 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 216static 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, pdev->vendpoint, 245 pChoose->mode, 13); 246 if (ret < 0) 247 return ret; 248 249 if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) 250 pwc_dec23_init(pdev, 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 267static 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, 4, pChoose->mode, 12); 297 if (ret < 0) 298 return ret; 299 300 if (pChoose->bandlength > 0 && pixfmt == V4L2_PIX_FMT_YUV420) 301 pwc_dec23_init(pdev, 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 319int 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 348static 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 360static 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 372static 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 384unsigned 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 401int 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, 1); 406 if (ret < 0) 407 return ret; 408 409 *data = pdev->ctrl_buf[0]; 410 return 0; 411} 412 413int 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, pdev->ctrl_buf, 1); 419 if (ret < 0) 420 return ret; 421 422 return 0; 423} 424 425int 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, 1); 430 if (ret < 0) 431 return ret; 432 433 *data = ((s8 *)pdev->ctrl_buf)[0]; 434 return 0; 435} 436 437int 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, 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 449int 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, pdev->ctrl_buf, 2); 456 if (ret < 0) 457 return ret; 458 459 return 0; 460} 461 462int 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, 0); 467 if (ret < 0) 468 return ret; 469 470 return 0; 471} 472 473/* POWER */ 474void 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, pdev->ctrl_buf, 1); 490 if (r < 0) 491 PWC_ERROR("Failed to power %s camera (%d)\n", 492 power ? "on" : "off", r); 493} 494 495int 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, pdev->ctrl_buf, 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 524int 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, request, 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