Path: blob/master/drivers/media/video/gspca/sunplus.c
17531 views
/*1* Sunplus spca504(abc) spca533 spca536 library2* Copyright (C) 2005 Michel Xhaard [email protected]3*4* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License as published by8* the Free Software Foundation; either version 2 of the License, or9* any later version.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA19*/2021#define MODULE_NAME "sunplus"2223#include "gspca.h"24#include "jpeg.h"2526MODULE_AUTHOR("Michel Xhaard <[email protected]>");27MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver");28MODULE_LICENSE("GPL");2930/* specific webcam descriptor */31struct sd {32struct gspca_dev gspca_dev; /* !! must be the first item */3334s8 brightness;35u8 contrast;36u8 colors;37u8 autogain;38u8 quality;39#define QUALITY_MIN 7040#define QUALITY_MAX 9541#define QUALITY_DEF 854243u8 bridge;44#define BRIDGE_SPCA504 045#define BRIDGE_SPCA504B 146#define BRIDGE_SPCA504C 247#define BRIDGE_SPCA533 348#define BRIDGE_SPCA536 449u8 subtype;50#define AiptekMiniPenCam13 151#define LogitechClickSmart420 252#define LogitechClickSmart820 353#define MegapixV4 454#define MegaImageVI 55556u8 jpeg_hdr[JPEG_HDR_SZ];57};5859/* V4L2 controls supported by the driver */60static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);61static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);62static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);63static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);64static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);65static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);66static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);67static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);6869static const struct ctrl sd_ctrls[] = {70{71{72.id = V4L2_CID_BRIGHTNESS,73.type = V4L2_CTRL_TYPE_INTEGER,74.name = "Brightness",75.minimum = -128,76.maximum = 127,77.step = 1,78#define BRIGHTNESS_DEF 079.default_value = BRIGHTNESS_DEF,80},81.set = sd_setbrightness,82.get = sd_getbrightness,83},84{85{86.id = V4L2_CID_CONTRAST,87.type = V4L2_CTRL_TYPE_INTEGER,88.name = "Contrast",89.minimum = 0,90.maximum = 0xff,91.step = 1,92#define CONTRAST_DEF 0x2093.default_value = CONTRAST_DEF,94},95.set = sd_setcontrast,96.get = sd_getcontrast,97},98{99{100.id = V4L2_CID_SATURATION,101.type = V4L2_CTRL_TYPE_INTEGER,102.name = "Color",103.minimum = 0,104.maximum = 0xff,105.step = 1,106#define COLOR_DEF 0x1a107.default_value = COLOR_DEF,108},109.set = sd_setcolors,110.get = sd_getcolors,111},112{113{114.id = V4L2_CID_AUTOGAIN,115.type = V4L2_CTRL_TYPE_BOOLEAN,116.name = "Auto Gain",117.minimum = 0,118.maximum = 1,119.step = 1,120#define AUTOGAIN_DEF 1121.default_value = AUTOGAIN_DEF,122},123.set = sd_setautogain,124.get = sd_getautogain,125},126};127128static const struct v4l2_pix_format vga_mode[] = {129{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,130.bytesperline = 320,131.sizeimage = 320 * 240 * 3 / 8 + 590,132.colorspace = V4L2_COLORSPACE_JPEG,133.priv = 2},134{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,135.bytesperline = 640,136.sizeimage = 640 * 480 * 3 / 8 + 590,137.colorspace = V4L2_COLORSPACE_JPEG,138.priv = 1},139};140141static const struct v4l2_pix_format custom_mode[] = {142{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,143.bytesperline = 320,144.sizeimage = 320 * 240 * 3 / 8 + 590,145.colorspace = V4L2_COLORSPACE_JPEG,146.priv = 2},147{464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,148.bytesperline = 464,149.sizeimage = 464 * 480 * 3 / 8 + 590,150.colorspace = V4L2_COLORSPACE_JPEG,151.priv = 1},152};153154static const struct v4l2_pix_format vga_mode2[] = {155{176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,156.bytesperline = 176,157.sizeimage = 176 * 144 * 3 / 8 + 590,158.colorspace = V4L2_COLORSPACE_JPEG,159.priv = 4},160{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,161.bytesperline = 320,162.sizeimage = 320 * 240 * 3 / 8 + 590,163.colorspace = V4L2_COLORSPACE_JPEG,164.priv = 3},165{352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,166.bytesperline = 352,167.sizeimage = 352 * 288 * 3 / 8 + 590,168.colorspace = V4L2_COLORSPACE_JPEG,169.priv = 2},170{640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,171.bytesperline = 640,172.sizeimage = 640 * 480 * 3 / 8 + 590,173.colorspace = V4L2_COLORSPACE_JPEG,174.priv = 1},175};176177#define SPCA50X_OFFSET_DATA 10178#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3179#define SPCA504_PCCAM600_OFFSET_COMPRESS 4180#define SPCA504_PCCAM600_OFFSET_MODE 5181#define SPCA504_PCCAM600_OFFSET_DATA 14182/* Frame packet header offsets for the spca533 */183#define SPCA533_OFFSET_DATA 16184#define SPCA533_OFFSET_FRAMSEQ 15185/* Frame packet header offsets for the spca536 */186#define SPCA536_OFFSET_DATA 4187#define SPCA536_OFFSET_FRAMSEQ 1188189struct cmd {190u8 req;191u16 val;192u16 idx;193};194195/* Initialisation data for the Creative PC-CAM 600 */196static const struct cmd spca504_pccam600_init_data[] = {197/* {0xa0, 0x0000, 0x0503}, * capture mode */198{0x00, 0x0000, 0x2000},199{0x00, 0x0013, 0x2301},200{0x00, 0x0003, 0x2000},201{0x00, 0x0001, 0x21ac},202{0x00, 0x0001, 0x21a6},203{0x00, 0x0000, 0x21a7}, /* brightness */204{0x00, 0x0020, 0x21a8}, /* contrast */205{0x00, 0x0001, 0x21ac}, /* sat/hue */206{0x00, 0x0000, 0x21ad}, /* hue */207{0x00, 0x001a, 0x21ae}, /* saturation */208{0x00, 0x0002, 0x21a3}, /* gamma */209{0x30, 0x0154, 0x0008},210{0x30, 0x0004, 0x0006},211{0x30, 0x0258, 0x0009},212{0x30, 0x0004, 0x0000},213{0x30, 0x0093, 0x0004},214{0x30, 0x0066, 0x0005},215{0x00, 0x0000, 0x2000},216{0x00, 0x0013, 0x2301},217{0x00, 0x0003, 0x2000},218{0x00, 0x0013, 0x2301},219{0x00, 0x0003, 0x2000},220};221222/* Creative PC-CAM 600 specific open data, sent before using the223* generic initialisation data from spca504_open_data.224*/225static const struct cmd spca504_pccam600_open_data[] = {226{0x00, 0x0001, 0x2501},227{0x20, 0x0500, 0x0001}, /* snapshot mode */228{0x00, 0x0003, 0x2880},229{0x00, 0x0001, 0x2881},230};231232/* Initialisation data for the logitech clicksmart 420 */233static const struct cmd spca504A_clicksmart420_init_data[] = {234/* {0xa0, 0x0000, 0x0503}, * capture mode */235{0x00, 0x0000, 0x2000},236{0x00, 0x0013, 0x2301},237{0x00, 0x0003, 0x2000},238{0x00, 0x0001, 0x21ac},239{0x00, 0x0001, 0x21a6},240{0x00, 0x0000, 0x21a7}, /* brightness */241{0x00, 0x0020, 0x21a8}, /* contrast */242{0x00, 0x0001, 0x21ac}, /* sat/hue */243{0x00, 0x0000, 0x21ad}, /* hue */244{0x00, 0x001a, 0x21ae}, /* saturation */245{0x00, 0x0002, 0x21a3}, /* gamma */246{0x30, 0x0004, 0x000a},247{0xb0, 0x0001, 0x0000},248249250{0xa1, 0x0080, 0x0001},251{0x30, 0x0049, 0x0000},252{0x30, 0x0060, 0x0005},253{0x0c, 0x0004, 0x0000},254{0x00, 0x0000, 0x0000},255{0x00, 0x0000, 0x2000},256{0x00, 0x0013, 0x2301},257{0x00, 0x0003, 0x2000},258{0x00, 0x0000, 0x2000},259260};261262/* clicksmart 420 open data ? */263static const struct cmd spca504A_clicksmart420_open_data[] = {264{0x00, 0x0001, 0x2501},265{0x20, 0x0502, 0x0000},266{0x06, 0x0000, 0x0000},267{0x00, 0x0004, 0x2880},268{0x00, 0x0001, 0x2881},269270{0xa0, 0x0000, 0x0503},271};272273static const u8 qtable_creative_pccam[2][64] = {274{ /* Q-table Y-components */2750x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,2760x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,2770x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,2780x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,2790x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,2800x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,2810x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,2820x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e},283{ /* Q-table C-components */2840x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,2850x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,2860x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,2870x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,2880x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,2890x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,2900x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,2910x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}292};293294/* FIXME: This Q-table is identical to the Creative PC-CAM one,295* except for one byte. Possibly a typo?296* NWG: 18/05/2003.297*/298static const u8 qtable_spca504_default[2][64] = {299{ /* Q-table Y-components */3000x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12,3010x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11,3020x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11,3030x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13,3040x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17,3050x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c,3060x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e,3070x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e,308},309{ /* Q-table C-components */3100x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e,3110x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e,3120x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,3130x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,3140x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,3150x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,3160x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e,3170x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e}318};319320/* read <len> bytes to gspca_dev->usb_buf */321static void reg_r(struct gspca_dev *gspca_dev,322u8 req,323u16 index,324u16 len)325{326int ret;327328#ifdef GSPCA_DEBUG329if (len > USB_BUF_SZ) {330err("reg_r: buffer overflow");331return;332}333#endif334if (gspca_dev->usb_err < 0)335return;336ret = usb_control_msg(gspca_dev->dev,337usb_rcvctrlpipe(gspca_dev->dev, 0),338req,339USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,3400, /* value */341index,342len ? gspca_dev->usb_buf : NULL, len,343500);344if (ret < 0) {345err("reg_r err %d", ret);346gspca_dev->usb_err = ret;347}348}349350/* write one byte */351static void reg_w_1(struct gspca_dev *gspca_dev,352u8 req,353u16 value,354u16 index,355u16 byte)356{357int ret;358359if (gspca_dev->usb_err < 0)360return;361gspca_dev->usb_buf[0] = byte;362ret = usb_control_msg(gspca_dev->dev,363usb_sndctrlpipe(gspca_dev->dev, 0),364req,365USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,366value, index,367gspca_dev->usb_buf, 1,368500);369if (ret < 0) {370err("reg_w_1 err %d", ret);371gspca_dev->usb_err = ret;372}373}374375/* write req / index / value */376static void reg_w_riv(struct gspca_dev *gspca_dev,377u8 req, u16 index, u16 value)378{379struct usb_device *dev = gspca_dev->dev;380int ret;381382if (gspca_dev->usb_err < 0)383return;384ret = usb_control_msg(dev,385usb_sndctrlpipe(dev, 0),386req,387USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,388value, index, NULL, 0, 500);389if (ret < 0) {390err("reg_w_riv err %d", ret);391gspca_dev->usb_err = ret;392return;393}394PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x",395req, index, value);396}397398static void write_vector(struct gspca_dev *gspca_dev,399const struct cmd *data, int ncmds)400{401while (--ncmds >= 0) {402reg_w_riv(gspca_dev, data->req, data->idx, data->val);403data++;404}405}406407static void setup_qtable(struct gspca_dev *gspca_dev,408const u8 qtable[2][64])409{410int i;411412/* loop over y components */413for (i = 0; i < 64; i++)414reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);415416/* loop over c components */417for (i = 0; i < 64; i++)418reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]);419}420421static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,422u8 req, u16 idx, u16 val)423{424reg_w_riv(gspca_dev, req, idx, val);425reg_r(gspca_dev, 0x01, 0x0001, 1);426PDEBUG(D_FRAM, "before wait 0x%04x", gspca_dev->usb_buf[0]);427reg_w_riv(gspca_dev, req, idx, val);428429msleep(200);430reg_r(gspca_dev, 0x01, 0x0001, 1);431PDEBUG(D_FRAM, "after wait 0x%04x", gspca_dev->usb_buf[0]);432}433434#ifdef GSPCA_DEBUG435static void spca504_read_info(struct gspca_dev *gspca_dev)436{437int i;438u8 info[6];439440for (i = 0; i < 6; i++) {441reg_r(gspca_dev, 0, i, 1);442info[i] = gspca_dev->usb_buf[0];443}444PDEBUG(D_STREAM,445"Read info: %d %d %d %d %d %d."446" Should be 1,0,2,2,0,0",447info[0], info[1], info[2],448info[3], info[4], info[5]);449}450#endif451452static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,453u8 req,454u16 idx, u16 val, u8 endcode, u8 count)455{456u16 status;457458reg_w_riv(gspca_dev, req, idx, val);459reg_r(gspca_dev, 0x01, 0x0001, 1);460if (gspca_dev->usb_err < 0)461return;462PDEBUG(D_FRAM, "Status 0x%02x Need 0x%02x",463gspca_dev->usb_buf[0], endcode);464if (!count)465return;466count = 200;467while (--count > 0) {468msleep(10);469/* gsmart mini2 write a each wait setting 1 ms is enough */470/* reg_w_riv(gspca_dev, req, idx, val); */471reg_r(gspca_dev, 0x01, 0x0001, 1);472status = gspca_dev->usb_buf[0];473if (status == endcode) {474PDEBUG(D_FRAM, "status 0x%04x after wait %d",475status, 200 - count);476break;477}478}479}480481static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)482{483int count = 10;484485while (--count > 0) {486reg_r(gspca_dev, 0x21, 0, 1);487if ((gspca_dev->usb_buf[0] & 0x01) == 0)488break;489msleep(10);490}491}492493static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)494{495int count = 50;496497while (--count > 0) {498reg_r(gspca_dev, 0x21, 1, 1);499if (gspca_dev->usb_buf[0] != 0) {500reg_w_1(gspca_dev, 0x21, 0, 1, 0);501reg_r(gspca_dev, 0x21, 1, 1);502spca504B_PollingDataReady(gspca_dev);503break;504}505msleep(10);506}507}508509#ifdef GSPCA_DEBUG510static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)511{512u8 *data;513514data = gspca_dev->usb_buf;515reg_r(gspca_dev, 0x20, 0, 5);516PDEBUG(D_STREAM, "FirmWare: %d %d %d %d %d",517data[0], data[1], data[2], data[3], data[4]);518reg_r(gspca_dev, 0x23, 0, 64);519reg_r(gspca_dev, 0x23, 1, 64);520}521#endif522523static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)524{525struct sd *sd = (struct sd *) gspca_dev;526u8 Size;527528Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;529switch (sd->bridge) {530case BRIDGE_SPCA533:531reg_w_riv(gspca_dev, 0x31, 0, 0);532spca504B_WaitCmdStatus(gspca_dev);533spca504B_PollingDataReady(gspca_dev);534#ifdef GSPCA_DEBUG535spca50x_GetFirmware(gspca_dev);536#endif537reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */538reg_r(gspca_dev, 0x24, 8, 1);539540reg_w_1(gspca_dev, 0x25, 0, 4, Size);541reg_r(gspca_dev, 0x25, 4, 1); /* size */542spca504B_PollingDataReady(gspca_dev);543544/* Init the cam width height with some values get on init ? */545reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);546spca504B_WaitCmdStatus(gspca_dev);547spca504B_PollingDataReady(gspca_dev);548break;549default:550/* case BRIDGE_SPCA504B: */551/* case BRIDGE_SPCA536: */552reg_w_1(gspca_dev, 0x25, 0, 4, Size);553reg_r(gspca_dev, 0x25, 4, 1); /* size */554reg_w_1(gspca_dev, 0x27, 0, 0, 6);555reg_r(gspca_dev, 0x27, 0, 1); /* type */556spca504B_PollingDataReady(gspca_dev);557break;558case BRIDGE_SPCA504:559Size += 3;560if (sd->subtype == AiptekMiniPenCam13) {561/* spca504a aiptek */562spca504A_acknowledged_command(gspca_dev,5630x08, Size, 0,5640x80 | (Size & 0x0f), 1);565spca504A_acknowledged_command(gspca_dev,5661, 3, 0, 0x9f, 0);567} else {568spca504_acknowledged_command(gspca_dev, 0x08, Size, 0);569}570break;571case BRIDGE_SPCA504C:572/* capture mode */573reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);574reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));575break;576}577}578579static void spca504_wait_status(struct gspca_dev *gspca_dev)580{581int cnt;582583cnt = 256;584while (--cnt > 0) {585/* With this we get the status, when return 0 it's all ok */586reg_r(gspca_dev, 0x06, 0x00, 1);587if (gspca_dev->usb_buf[0] == 0)588return;589msleep(10);590}591}592593static void spca504B_setQtable(struct gspca_dev *gspca_dev)594{595reg_w_1(gspca_dev, 0x26, 0, 0, 3);596reg_r(gspca_dev, 0x26, 0, 1);597spca504B_PollingDataReady(gspca_dev);598}599600static void setbrightness(struct gspca_dev *gspca_dev)601{602struct sd *sd = (struct sd *) gspca_dev;603u16 reg;604605reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;606reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);607}608609static void setcontrast(struct gspca_dev *gspca_dev)610{611struct sd *sd = (struct sd *) gspca_dev;612u16 reg;613614reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;615reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);616}617618static void setcolors(struct gspca_dev *gspca_dev)619{620struct sd *sd = (struct sd *) gspca_dev;621u16 reg;622623reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;624reg_w_riv(gspca_dev, 0x00, reg, sd->colors);625}626627static void init_ctl_reg(struct gspca_dev *gspca_dev)628{629struct sd *sd = (struct sd *) gspca_dev;630int pollreg = 1;631632setbrightness(gspca_dev);633setcontrast(gspca_dev);634setcolors(gspca_dev);635636switch (sd->bridge) {637case BRIDGE_SPCA504:638case BRIDGE_SPCA504C:639pollreg = 0;640/* fall thru */641default:642/* case BRIDGE_SPCA533: */643/* case BRIDGE_SPCA504B: */644reg_w_riv(gspca_dev, 0, 0x21ad, 0x00); /* hue */645reg_w_riv(gspca_dev, 0, 0x21ac, 0x01); /* sat/hue */646reg_w_riv(gspca_dev, 0, 0x21a3, 0x00); /* gamma */647break;648case BRIDGE_SPCA536:649reg_w_riv(gspca_dev, 0, 0x20f5, 0x40);650reg_w_riv(gspca_dev, 0, 0x20f4, 0x01);651reg_w_riv(gspca_dev, 0, 0x2089, 0x00);652break;653}654if (pollreg)655spca504B_PollingDataReady(gspca_dev);656}657658/* this function is called at probe time */659static int sd_config(struct gspca_dev *gspca_dev,660const struct usb_device_id *id)661{662struct sd *sd = (struct sd *) gspca_dev;663struct cam *cam;664665cam = &gspca_dev->cam;666667sd->bridge = id->driver_info >> 8;668sd->subtype = id->driver_info;669670if (sd->subtype == AiptekMiniPenCam13) {671672/* try to get the firmware as some cam answer 2.0.1.2.2673* and should be a spca504b then overwrite that setting */674reg_r(gspca_dev, 0x20, 0, 1);675switch (gspca_dev->usb_buf[0]) {676case 1:677break; /* (right bridge/subtype) */678case 2:679sd->bridge = BRIDGE_SPCA504B;680sd->subtype = 0;681break;682default:683return -ENODEV;684}685}686687switch (sd->bridge) {688default:689/* case BRIDGE_SPCA504B: */690/* case BRIDGE_SPCA504: */691/* case BRIDGE_SPCA536: */692cam->cam_mode = vga_mode;693cam->nmodes = ARRAY_SIZE(vga_mode);694break;695case BRIDGE_SPCA533:696cam->cam_mode = custom_mode;697if (sd->subtype == MegaImageVI) /* 320x240 only */698cam->nmodes = ARRAY_SIZE(custom_mode) - 1;699else700cam->nmodes = ARRAY_SIZE(custom_mode);701break;702case BRIDGE_SPCA504C:703cam->cam_mode = vga_mode2;704cam->nmodes = ARRAY_SIZE(vga_mode2);705break;706}707sd->brightness = BRIGHTNESS_DEF;708sd->contrast = CONTRAST_DEF;709sd->colors = COLOR_DEF;710sd->autogain = AUTOGAIN_DEF;711sd->quality = QUALITY_DEF;712return 0;713}714715/* this function is called at probe and resume time */716static int sd_init(struct gspca_dev *gspca_dev)717{718struct sd *sd = (struct sd *) gspca_dev;719720switch (sd->bridge) {721case BRIDGE_SPCA504B:722reg_w_riv(gspca_dev, 0x1d, 0x00, 0);723reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01);724reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00);725reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00);726reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13);727reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00);728/* fall thru */729case BRIDGE_SPCA533:730spca504B_PollingDataReady(gspca_dev);731#ifdef GSPCA_DEBUG732spca50x_GetFirmware(gspca_dev);733#endif734break;735case BRIDGE_SPCA536:736#ifdef GSPCA_DEBUG737spca50x_GetFirmware(gspca_dev);738#endif739reg_r(gspca_dev, 0x00, 0x5002, 1);740reg_w_1(gspca_dev, 0x24, 0, 0, 0);741reg_r(gspca_dev, 0x24, 0, 1);742spca504B_PollingDataReady(gspca_dev);743reg_w_riv(gspca_dev, 0x34, 0, 0);744spca504B_WaitCmdStatus(gspca_dev);745break;746case BRIDGE_SPCA504C: /* pccam600 */747PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)");748reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000);749reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001); /* reset */750spca504_wait_status(gspca_dev);751if (sd->subtype == LogitechClickSmart420)752write_vector(gspca_dev,753spca504A_clicksmart420_open_data,754ARRAY_SIZE(spca504A_clicksmart420_open_data));755else756write_vector(gspca_dev, spca504_pccam600_open_data,757ARRAY_SIZE(spca504_pccam600_open_data));758setup_qtable(gspca_dev, qtable_creative_pccam);759break;760default:761/* case BRIDGE_SPCA504: */762PDEBUG(D_STREAM, "Opening SPCA504");763if (sd->subtype == AiptekMiniPenCam13) {764#ifdef GSPCA_DEBUG765spca504_read_info(gspca_dev);766#endif767768/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */769spca504A_acknowledged_command(gspca_dev, 0x24,7708, 3, 0x9e, 1);771/* Twice sequential need status 0xff->0x9e->0x9d */772spca504A_acknowledged_command(gspca_dev, 0x24,7738, 3, 0x9e, 0);774775spca504A_acknowledged_command(gspca_dev, 0x24,7760, 0, 0x9d, 1);777/******************************/778/* spca504a aiptek */779spca504A_acknowledged_command(gspca_dev, 0x08,7806, 0, 0x86, 1);781/* reg_write (dev, 0, 0x2000, 0); */782/* reg_write (dev, 0, 0x2883, 1); */783/* spca504A_acknowledged_command (gspca_dev, 0x08,7846, 0, 0x86, 1); */785/* spca504A_acknowledged_command (gspca_dev, 0x24,7860, 0, 0x9D, 1); */787reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);788/* L92 sno1t.txt */789reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);790spca504A_acknowledged_command(gspca_dev, 0x01,7910x0f, 0, 0xff, 0);792}793/* setup qtable */794reg_w_riv(gspca_dev, 0, 0x2000, 0);795reg_w_riv(gspca_dev, 0, 0x2883, 1);796setup_qtable(gspca_dev, qtable_spca504_default);797break;798}799return gspca_dev->usb_err;800}801802static int sd_start(struct gspca_dev *gspca_dev)803{804struct sd *sd = (struct sd *) gspca_dev;805int enable;806807/* create the JPEG header */808jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,8090x22); /* JPEG 411 */810jpeg_set_qual(sd->jpeg_hdr, sd->quality);811812if (sd->bridge == BRIDGE_SPCA504B)813spca504B_setQtable(gspca_dev);814spca504B_SetSizeType(gspca_dev);815switch (sd->bridge) {816default:817/* case BRIDGE_SPCA504B: */818/* case BRIDGE_SPCA533: */819/* case BRIDGE_SPCA536: */820switch (sd->subtype) {821case MegapixV4:822case LogitechClickSmart820:823case MegaImageVI:824reg_w_riv(gspca_dev, 0xf0, 0, 0);825spca504B_WaitCmdStatus(gspca_dev);826reg_r(gspca_dev, 0xf0, 4, 0);827spca504B_WaitCmdStatus(gspca_dev);828break;829default:830reg_w_riv(gspca_dev, 0x31, 0x0004, 0x00);831spca504B_WaitCmdStatus(gspca_dev);832spca504B_PollingDataReady(gspca_dev);833break;834}835break;836case BRIDGE_SPCA504:837if (sd->subtype == AiptekMiniPenCam13) {838#ifdef GSPCA_DEBUG839spca504_read_info(gspca_dev);840#endif841842/* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */843spca504A_acknowledged_command(gspca_dev, 0x24,8448, 3, 0x9e, 1);845/* Twice sequential need status 0xff->0x9e->0x9d */846spca504A_acknowledged_command(gspca_dev, 0x24,8478, 3, 0x9e, 0);848spca504A_acknowledged_command(gspca_dev, 0x24,8490, 0, 0x9d, 1);850} else {851spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);852#ifdef GSPCA_DEBUG853spca504_read_info(gspca_dev);854#endif855spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);856spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);857}858spca504B_SetSizeType(gspca_dev);859reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);860/* L92 sno1t.txt */861reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);862break;863case BRIDGE_SPCA504C:864if (sd->subtype == LogitechClickSmart420) {865write_vector(gspca_dev,866spca504A_clicksmart420_init_data,867ARRAY_SIZE(spca504A_clicksmart420_init_data));868} else {869write_vector(gspca_dev, spca504_pccam600_init_data,870ARRAY_SIZE(spca504_pccam600_init_data));871}872enable = (sd->autogain ? 0x04 : 0x01);873reg_w_riv(gspca_dev, 0x0c, 0x0000, enable);874/* auto exposure */875reg_w_riv(gspca_dev, 0xb0, 0x0000, enable);876/* auto whiteness */877878/* set default exposure compensation and whiteness balance */879reg_w_riv(gspca_dev, 0x30, 0x0001, 800); /* ~ 20 fps */880reg_w_riv(gspca_dev, 0x30, 0x0002, 1600);881spca504B_SetSizeType(gspca_dev);882break;883}884init_ctl_reg(gspca_dev);885return gspca_dev->usb_err;886}887888static void sd_stopN(struct gspca_dev *gspca_dev)889{890struct sd *sd = (struct sd *) gspca_dev;891892switch (sd->bridge) {893default:894/* case BRIDGE_SPCA533: */895/* case BRIDGE_SPCA536: */896/* case BRIDGE_SPCA504B: */897reg_w_riv(gspca_dev, 0x31, 0, 0);898spca504B_WaitCmdStatus(gspca_dev);899spca504B_PollingDataReady(gspca_dev);900break;901case BRIDGE_SPCA504:902case BRIDGE_SPCA504C:903reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000);904905if (sd->subtype == AiptekMiniPenCam13) {906/* spca504a aiptek */907/* spca504A_acknowledged_command(gspca_dev, 0x08,9086, 0, 0x86, 1); */909spca504A_acknowledged_command(gspca_dev, 0x24,9100x00, 0x00, 0x9d, 1);911spca504A_acknowledged_command(gspca_dev, 0x01,9120x0f, 0x00, 0xff, 1);913} else {914spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);915reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000);916}917break;918}919}920921static void sd_pkt_scan(struct gspca_dev *gspca_dev,922u8 *data, /* isoc packet */923int len) /* iso packet length */924{925struct sd *sd = (struct sd *) gspca_dev;926int i, sof = 0;927static u8 ffd9[] = {0xff, 0xd9};928929/* frames are jpeg 4.1.1 without 0xff escape */930switch (sd->bridge) {931case BRIDGE_SPCA533:932if (data[0] == 0xff) {933if (data[1] != 0x01) { /* drop packet */934/* gspca_dev->last_packet_type = DISCARD_PACKET; */935return;936}937sof = 1;938data += SPCA533_OFFSET_DATA;939len -= SPCA533_OFFSET_DATA;940} else {941data += 1;942len -= 1;943}944break;945case BRIDGE_SPCA536:946if (data[0] == 0xff) {947sof = 1;948data += SPCA536_OFFSET_DATA;949len -= SPCA536_OFFSET_DATA;950} else {951data += 2;952len -= 2;953}954break;955default:956/* case BRIDGE_SPCA504: */957/* case BRIDGE_SPCA504B: */958switch (data[0]) {959case 0xfe: /* start of frame */960sof = 1;961data += SPCA50X_OFFSET_DATA;962len -= SPCA50X_OFFSET_DATA;963break;964case 0xff: /* drop packet */965/* gspca_dev->last_packet_type = DISCARD_PACKET; */966return;967default:968data += 1;969len -= 1;970break;971}972break;973case BRIDGE_SPCA504C:974switch (data[0]) {975case 0xfe: /* start of frame */976sof = 1;977data += SPCA504_PCCAM600_OFFSET_DATA;978len -= SPCA504_PCCAM600_OFFSET_DATA;979break;980case 0xff: /* drop packet */981/* gspca_dev->last_packet_type = DISCARD_PACKET; */982return;983default:984data += 1;985len -= 1;986break;987}988break;989}990if (sof) { /* start of frame */991gspca_frame_add(gspca_dev, LAST_PACKET,992ffd9, 2);993994/* put the JPEG header in the new frame */995gspca_frame_add(gspca_dev, FIRST_PACKET,996sd->jpeg_hdr, JPEG_HDR_SZ);997}998999/* add 0x00 after 0xff */1000i = 0;1001do {1002if (data[i] == 0xff) {1003gspca_frame_add(gspca_dev, INTER_PACKET,1004data, i + 1);1005len -= i;1006data += i;1007*data = 0x00;1008i = 0;1009}1010i++;1011} while (i < len);1012gspca_frame_add(gspca_dev, INTER_PACKET, data, len);1013}10141015static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)1016{1017struct sd *sd = (struct sd *) gspca_dev;10181019sd->brightness = val;1020if (gspca_dev->streaming)1021setbrightness(gspca_dev);1022return gspca_dev->usb_err;1023}10241025static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)1026{1027struct sd *sd = (struct sd *) gspca_dev;10281029*val = sd->brightness;1030return 0;1031}10321033static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)1034{1035struct sd *sd = (struct sd *) gspca_dev;10361037sd->contrast = val;1038if (gspca_dev->streaming)1039setcontrast(gspca_dev);1040return gspca_dev->usb_err;1041}10421043static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)1044{1045struct sd *sd = (struct sd *) gspca_dev;10461047*val = sd->contrast;1048return 0;1049}10501051static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)1052{1053struct sd *sd = (struct sd *) gspca_dev;10541055sd->colors = val;1056if (gspca_dev->streaming)1057setcolors(gspca_dev);1058return gspca_dev->usb_err;1059}10601061static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)1062{1063struct sd *sd = (struct sd *) gspca_dev;10641065*val = sd->colors;1066return 0;1067}10681069static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)1070{1071struct sd *sd = (struct sd *) gspca_dev;10721073sd->autogain = val;1074return 0;1075}10761077static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)1078{1079struct sd *sd = (struct sd *) gspca_dev;10801081*val = sd->autogain;1082return 0;1083}10841085static int sd_set_jcomp(struct gspca_dev *gspca_dev,1086struct v4l2_jpegcompression *jcomp)1087{1088struct sd *sd = (struct sd *) gspca_dev;10891090if (jcomp->quality < QUALITY_MIN)1091sd->quality = QUALITY_MIN;1092else if (jcomp->quality > QUALITY_MAX)1093sd->quality = QUALITY_MAX;1094else1095sd->quality = jcomp->quality;1096if (gspca_dev->streaming)1097jpeg_set_qual(sd->jpeg_hdr, sd->quality);1098return gspca_dev->usb_err;1099}11001101static int sd_get_jcomp(struct gspca_dev *gspca_dev,1102struct v4l2_jpegcompression *jcomp)1103{1104struct sd *sd = (struct sd *) gspca_dev;11051106memset(jcomp, 0, sizeof *jcomp);1107jcomp->quality = sd->quality;1108jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT1109| V4L2_JPEG_MARKER_DQT;1110return 0;1111}11121113/* sub-driver description */1114static const struct sd_desc sd_desc = {1115.name = MODULE_NAME,1116.ctrls = sd_ctrls,1117.nctrls = ARRAY_SIZE(sd_ctrls),1118.config = sd_config,1119.init = sd_init,1120.start = sd_start,1121.stopN = sd_stopN,1122.pkt_scan = sd_pkt_scan,1123.get_jcomp = sd_get_jcomp,1124.set_jcomp = sd_set_jcomp,1125};11261127/* -- module initialisation -- */1128#define BS(bridge, subtype) \1129.driver_info = (BRIDGE_ ## bridge << 8) \1130| (subtype)1131static const struct usb_device_id device_table[] = {1132{USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)},1133{USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)},1134{USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)},1135{USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)},1136{USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)},1137{USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)},1138{USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)},1139{USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)},1140{USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)},1141{USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)},1142{USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)},1143{USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)},1144{USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)},1145{USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)},1146{USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)},1147{USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},1148{USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},1149{USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},1150{USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},1151{USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},1152{USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},1153{USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},1154{USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)},1155{USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)},1156{USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)},1157{USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)},1158{USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)},1159{USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)},1160{USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)},1161{USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)},1162{USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)},1163{USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)},1164{USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)},1165{USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)},1166{USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)},1167{USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)},1168{USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)},1169{USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)},1170{USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)},1171{USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)},1172{USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)},1173{USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)},1174{USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)},1175{USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)},1176{USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)},1177{USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)},1178{USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)},1179{USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)},1180{USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)},1181{USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)},1182{USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)},1183{USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)},1184{USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)},1185{USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)},1186{USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)},1187{USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)},1188{USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)},1189{USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)},1190{USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)},1191{}1192};1193MODULE_DEVICE_TABLE(usb, device_table);11941195/* -- device connect -- */1196static int sd_probe(struct usb_interface *intf,1197const struct usb_device_id *id)1198{1199return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),1200THIS_MODULE);1201}12021203static struct usb_driver sd_driver = {1204.name = MODULE_NAME,1205.id_table = device_table,1206.probe = sd_probe,1207.disconnect = gspca_disconnect,1208#ifdef CONFIG_PM1209.suspend = gspca_suspend,1210.resume = gspca_resume,1211#endif1212};12131214/* -- module insert / remove -- */1215static int __init sd_mod_init(void)1216{1217return usb_register(&sd_driver);1218}1219static void __exit sd_mod_exit(void)1220{1221usb_deregister(&sd_driver);1222}12231224module_init(sd_mod_init);1225module_exit(sd_mod_exit);122612271228