545 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* Driver for Philips webcam3 Functions that send various control messages to the webcam, including4 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 & pcwx10 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 & pcwx14 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 the17 Nemosoft binary pwcx module. Caveat emptor.18 19*/20 21/*22 Changes23 2001/08/03 Alvarado Added methods for changing white balance and24 red/green gains25 */26 27/* Control functions for the cam; brightness, contrast, video mode, etc. */28 29#ifdef __KERNEL__30#include <linux/uaccess.h>31#endif32#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 0x0B0042#define SENSOR_TYPE_FORMATTER1 0x0C0043#define GET_STATUS_3000 0x300044#define READ_RAW_Y_MEAN_FORMATTER 0x310045#define SET_POWER_SAVE_MODE_FORMATTER 0x320046#define MIRROR_IMAGE_FORMATTER 0x330047#define LED_FORMATTER 0x340048#define LOWLIGHT 0x350049#define GET_STATUS_3600 0x360050#define SENSOR_TYPE_FORMATTER2 0x370051#define GET_STATUS_3800 0x380052#define GET_STATUS_4000 0x400053#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 0x010058 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 compression72 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 878 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 else126 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 else207 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 else262 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 else313 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 else487 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_DEBUG524int 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 else533 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 else541 *sensor = pdev->ctrl_buf[0];542 return 0;543}544#endif545