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