linux/drivers/media/usb/pwc/pwc-ctrl.c
<<
>>
Prefs
   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 = -1, 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
 545